반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형

16.3. Sentiment Analysis: Using Convolutional Neural Networks — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

16.3. Sentiment Analysis: Using Convolutional Neural Networks — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

 

16.3. Sentiment Analysis: Using Convolutional Neural Networks

 

In Section 7, we investigated mechanisms for processing two-dimensional image data with two-dimensional CNNs, which were applied to local features such as adjacent pixels. Though originally designed for computer vision, CNNs are also widely used for natural language processing. Simply put, just think of any text sequence as a one-dimensional image. In this way, one-dimensional CNNs can process local features such as n-grams in text.

 

섹션 7에서는 인접 픽셀과 같은 로컬 특징에 적용된 2차원 CNN을 사용하여 2차원 이미지 데이터를 처리하는 메커니즘을 조사했습니다. CNN은 원래 컴퓨터 비전용으로 설계되었지만 자연어 처리에도 널리 사용됩니다. 간단히 말해서 텍스트 시퀀스를 1차원 이미지로 생각하면 됩니다. 이러한 방식으로 1차원 CNN은 텍스트의 n-gram과 같은 로컬 기능을 처리할 수 있습니다.

 

In this section, we will use the textCNN model to demonstrate how to design a CNN architecture for representing single text (Kim, 2014). Compared with Fig. 16.2.1 that uses an RNN architecture with GloVe pretraining for sentiment analysis, the only difference in Fig. 16.3.1 lies in the choice of the architecture.

 

이 섹션에서는 textCNN 모델을 사용하여 단일 텍스트를 표현하기 위한 CNN 아키텍처를 설계하는 방법을 보여줍니다(Kim, 2014). 감정 분석을 위해 GloVe 사전 훈련이 포함된 RNN 아키텍처를 사용하는 그림 16.2.1과 비교하면 그림 16.3.1의 유일한 차이점은 아키텍처 선택에 있습니다.

 

Fig. 16.3.1  This section feeds pretrained GloVe to a CNN-based architecture for sentiment analysis.

 

import torch
from torch import nn
from d2l import torch as d2l

batch_size = 64
train_iter, test_iter, vocab = d2l.load_data_imdb(batch_size)

 

이 코드는 IMDb 감정 분석 데이터셋을 로드하고, 데이터를 미니배치로 나누어서 데이터 로더(iterator)를 생성하는 작업을 수행하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 모듈 및 패키지 가져오기:
    • import torch: 파이토치 라이브러리를 가져옵니다.
    • from torch import nn: 파이토치의 nn 모듈에서 뉴럴 네트워크 관련 클래스와 함수를 가져옵니다.
    • from d2l import torch as d2l: d2l 패키지에서 torch 모듈을 가져오되, d2l을 접두어로 사용하여 모듈을 사용할 수 있도록 합니다.
  2. 미니배치 크기 설정:
    • batch_size = 64: 미니배치의 크기를 64로 설정합니다. 한 번에 처리하는 데이터 샘플의 개수입니다.
  3. 데이터 로딩:
    • train_iter, test_iter, vocab = d2l.load_data_imdb(batch_size): d2l 패키지의 load_data_imdb 함수를 사용하여 IMDb 감정 분석 데이터셋을 로드하고 데이터를 미니배치로 분할하여 훈련 및 테스트 데이터 이터레이터(train_iter, test_iter)와 어휘 사전(vocab)을 반환합니다.
    • train_iter: 훈련 데이터를 미니배치로 가져오는 데이터 이터레이터
    • test_iter: 테스트 데이터를 미니배치로 가져오는 데이터 이터레이터
    • vocab: 데이터에 사용되는 어휘 사전

즉, 이 코드는 IMDb 감정 분석 데이터셋을 로드하고 훈련 및 테스트 데이터를 미니배치로 나누어서 이후 모델 학습 및 평가에 사용할 수 있도록 데이터 로더를 생성하는 작업을 수행합니다.

 

 

16.3.1. One-Dimensional Convolutions

 

Before introducing the model, let’s see how a one-dimensional convolution works. Bear in mind that it is just a special case of a two-dimensional convolution based on the cross-correlation operation.

 

모델을 소개하기 전에 1차원 컨볼루션이 어떻게 작동하는지 살펴보겠습니다. 이는 상호 상관 연산을 기반으로 하는 2차원 컨볼루션의 특별한 경우일 뿐이라는 점을 명심하세요.

 

Fig. 16.3.2  One-dimensional cross-correlation operation. The shaded portions are the first output element as well as the input and kernel tensor elements used for the output computation: 0×1+1×2=2.  그림 16.3.2 1차원 상호 상관 연산. 음영 처리된 부분은 첫 번째 출력 요소이자 출력 계산에 사용되는 입력 및 커널 텐서 요소입니다.

 

As shown in Fig. 16.3.2, in the one-dimensional case, the convolution window slides from left to right across the input tensor. During sliding, the input subtensor (e.g., 0 and 1 in Fig. 16.3.2) contained in the convolution window at a certain position and the kernel tensor (e.g., 1 and 2 in Fig. 16.3.2) are multiplied elementwise. The sum of these multiplications gives the single scalar value (e.g., 0×1+1×2=2 in Fig. 16.3.2) at the corresponding position of the output tensor.

 

그림 16.3.2에서 볼 수 있듯이 1차원 경우 컨볼루션 창이 입력 텐서를 가로질러 왼쪽에서 오른쪽으로 미끄러집니다. 슬라이딩하는 동안 컨볼루션 윈도우의 특정 위치에 포함된 입력 서브텐서(예: 그림 16.3.2의 0과 1)와 커널 텐서(예: 그림 16.3.2의 1과 2)가 요소별로 곱해집니다. 이러한 곱셈의 합은 출력 텐서의 해당 위치에 단일 스칼라 값(예: 그림 16.3.2의 0×1+1×2=2)을 제공합니다.

 

We implement one-dimensional cross-correlation in the following corr1d function. Given an input tensor X and a kernel tensor K, it returns the output tensor Y.

 

다음 corr1d 함수에서 1차원 상호 상관을 구현합니다. 입력 텐서 X와 커널 텐서 K가 주어지면 출력 텐서 Y를 반환합니다.

 

def corr1d(X, K):
    w = K.shape[0]
    Y = torch.zeros((X.shape[0] - w + 1))
    for i in range(Y.shape[0]):
        Y[i] = (X[i: i + w] * K).sum()
    return Y

이 코드는 1차원의 합성곱 연산을 수행하는 함수를 정의하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 함수 정의:
    • corr1d(X, K) 함수는 입력 시퀀스 X와 커널 K를 받아 1차원 합성곱 연산을 수행하는 역할을 합니다.
  2. 커널 크기 확인:
    • w = K.shape[0]: 커널 K의 크기를 w로 지정합니다. 여기서 K의 크기는 커널의 길이를 의미합니다.
  3. 출력 텐서 초기화:
    • Y = torch.zeros((X.shape[0] - w + 1)): 출력 텐서 Y를 생성합니다. X의 길이에서 커널 크기를 뺀 길이만큼의 영행렬을 생성합니다.
  4. 합성곱 연산 수행:
    • for i in range(Y.shape[0]):: 출력 텐서의 각 원소를 계산하기 위한 루프를 수행합니다.
    • (X[i: i + w] * K).sum(): 입력 시퀀스 X에서 현재 위치 i부터 i + w까지의 부분 시퀀스와 커널 K 간의 원소별 곱셈을 수행한 후 그 합을 계산합니다. 이 결과는 Y의 i 위치에 저장됩니다.
  5. 결과 반환:
    • 계산된 출력 텐서 Y를 반환합니다. 이 텐서는 1차원 합성곱 연산 결과입니다.

즉, 이 코드는 주어진 입력 시퀀스와 커널을 사용하여 1차원 합성곱 연산을 수행하는 함수를 정의합니다. 이 함수는 시퀀스 데이터에서 패턴을 찾거나 특성을 추출하는 데 사용될 수 있습니다.

 

We can construct the input tensor X and the kernel tensor K from Fig. 16.3.2 to validate the output of the above one-dimensional cross-correlation implementation.

 

위의 1차원 상호 상관 구현의 출력을 검증하기 위해 그림 16.3.2의 입력 텐서 X와 커널 텐서 K를 구성할 수 있습니다.

 

X, K = torch.tensor([0, 1, 2, 3, 4, 5, 6]), torch.tensor([1, 2])
corr1d(X, K)

이 코드는 주어진 입력 시퀀스와 커널을 사용하여 1차원 합성곱 연산을 수행하는 corr1d 함수를 호출하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 텐서 생성:
    • X는 입력 시퀀스를 나타내는 텐서로, [0, 1, 2, 3, 4, 5, 6]로 초기화됩니다.
    • K는 커널을 나타내는 텐서로, [1, 2]로 초기화됩니다.
  2. 함수 호출:
    • corr1d(X, K) 함수를 호출하여 주어진 입력 시퀀스 X와 커널 K를 사용하여 1차원 합성곱 연산을 수행합니다.
  3. 합성곱 연산 결과 반환:
    • 함수는 주어진 입력 시퀀스 X에 대해 커널 K를 사용하여 1차원 합성곱 연산을 수행한 결과인 출력 텐서를 반환합니다.

결과적으로, 코드는 주어진 입력 시퀀스 X와 커널 K를 사용하여 1차원 합성곱 연산을 수행한 결과를 계산하고 출력합니다. 이를 통해 시퀀스 데이터에서 패턴을 추출하거나 특성을 찾는 데 사용되는 연산을 수행합니다.

tensor([ 2.,  5.,  8., 11., 14., 17.])

 

For any one-dimensional input with multiple channels, the convolution kernel needs to have the same number of input channels. Then for each channel, perform a cross-correlation operation on the one-dimensional tensor of the input and the one-dimensional tensor of the convolution kernel, summing the results over all the channels to produce the one-dimensional output tensor. Fig. 16.3.3 shows a one-dimensional cross-correlation operation with 3 input channels.

 

여러 채널이 있는 1차원 입력의 경우 컨볼루션 커널의 입력 채널 수가 동일해야 합니다. 그런 다음 각 채널에 대해 입력의 1차원 텐서와 컨볼루션 커널의 1차원 텐서에 대해 상호 상관 연산을 수행하고 모든 채널에 대한 결과를 합산하여 1차원 출력 텐서를 생성합니다. 그림 16.3.3은 3개의 입력 채널을 사용한 1차원 상호 상관 연산을 보여줍니다.

 

Fig. 16.3.3  One-dimensional cross-correlation operation with 3 input channels. The shaded portions are the first output element as well as the input and kernel tensor elements used for the output computation: 0×1+1×2+1×3+2×4+2×(−1)+3×(−3)=2. 그림 16.3.3 3개의 입력 채널을 사용한 1차원 상호 상관 연산. 음영 처리된 부분은 첫 번째 출력 요소이자 출력 계산에 사용되는 입력 및 커널 텐서 요소입니다. 0×1+1×2+1×3+2×4+2×(−1)+3×(− 3)=2.

 

We can implement the one-dimensional cross-correlation operation for multiple input channels and validate the results in Fig. 16.3.3.

 

우리는 여러 입력 채널에 대한 1차원 상호 상관 연산을 구현하고 그림 16.3.3의 결과를 검증할 수 있습니다.

 

def corr1d_multi_in(X, K):
    # First, iterate through the 0th dimension (channel dimension) of `X` and
    # `K`. Then, add them together
    return sum(corr1d(x, k) for x, k in zip(X, K))

X = torch.tensor([[0, 1, 2, 3, 4, 5, 6],
              [1, 2, 3, 4, 5, 6, 7],
              [2, 3, 4, 5, 6, 7, 8]])
K = torch.tensor([[1, 2], [3, 4], [-1, -3]])
corr1d_multi_in(X, K)

이 코드는 다중 입력 채널을 가진 입력과 커널을 사용하여 다중 입력 채널에 대한 1차원 합성곱 연산을 수행하는 함수를 정의하고 호출하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 함수 정의:
    • corr1d_multi_in(X, K) 함수는 다중 입력 채널을 가진 입력 X와 커널 K를 받아 각 입력 채널에 대한 1차원 합성곱 연산을 수행한 결과를 합산하여 반환하는 역할을 합니다.
    • 함수 내부에서는 zip(X, K)를 사용하여 입력 채널과 커널을 하나씩 묶어서 연산합니다.
  2. 입력과 커널 텐서 생성:
    • X는 다중 입력 채널을 가진 입력을 나타내는 텐서입니다.
    • K는 다중 커널을 나타내는 텐서입니다.
  3. 함수 호출:
    • corr1d_multi_in(X, K) 함수를 호출하여 주어진 다중 입력 채널을 가진 입력 X와 다중 커널 K를 사용하여 다중 입력 채널에 대한 1차원 합성곱 연산을 수행합니다.
  4. 합성곱 연산 결과 합산:
    • 함수는 zip(X, K)를 통해 각 입력 채널과 커널을 묶어서 corr1d 함수로 1차원 합성곱 연산을 수행한 결과를 합산합니다.

결과적으로, 코드는 주어진 다중 입력 채널과 커널을 사용하여 각 입력 채널에 대한 1차원 합성곱 연산을 수행한 결과를 합산하여 반환합니다. 이는 다중 채널의 입력 데이터에 대해 필터 연산을 수행하는 데 사용될 수 있습니다.

tensor([ 2.,  8., 14., 20., 26., 32.])

 

Note that multi-input-channel one-dimensional cross-correlations are equivalent to single-input-channel two-dimensional cross-correlations. To illustrate, an equivalent form of the multi-input-channel one-dimensional cross-correlation in Fig. 16.3.3 is the single-input-channel two-dimensional cross-correlation in Fig. 16.3.4, where the height of the convolution kernel has to be the same as that of the input tensor.

 

다중 입력 채널 1차원 상호 상관은 단일 입력 채널 2차원 상호 상관과 동일합니다. 설명하기 위해 그림 16.3.3의 다중 입력 채널 1차원 상호 상관의 등가 형식은 그림 16.3.4의 단일 입력 채널 2차원 상호 상관입니다. 컨볼루션 커널은 입력 텐서의 커널과 동일해야 합니다.

 

Fig. 16.3.4  Two-dimensional cross-correlation operation with a single input channel. The shaded portions are the first output element as well as the input and kernel tensor elements used for the output computation: 2×(−1)+3×(−3)+1×3+2×4+0×1+1×2=2. 그림 16.3.4 단일 입력 채널을 사용한 2차원 상호 상관 연산. 음영 처리된 부분은 첫 번째 출력 요소이자 출력 계산에 사용되는 입력 및 커널 텐서 요소입니다. 2×(−1)+3×(−3)+1×3+2×4+0×1+1 ×2=2.

 

Both the outputs in Fig. 16.3.2 and Fig. 16.3.3 have only one channel. Same as two-dimensional convolutions with multiple output channels described in Section 7.4.2, we can also specify multiple output channels for one-dimensional convolutions.

 

그림 16.3.2와 그림 16.3.3의 출력에는 모두 채널이 하나만 있습니다. 7.4.2절에 설명된 여러 출력 채널을 사용하는 2차원 컨볼루션과 마찬가지로 1차원 컨볼루션에 대해 여러 출력 채널을 지정할 수도 있습니다.

 

16.3.2. Max-Over-Time Pooling

 

Similarly, we can use pooling to extract the highest value from sequence representations as the most important feature across time steps. The max-over-time pooling used in textCNN works like the one-dimensional global max-pooling (Collobert et al., 2011). For a multi-channel input where each channel stores values at different time steps, the output at each channel is the maximum value for that channel. Note that the max-over-time pooling allows different numbers of time steps at different channels.

 

마찬가지로 풀링을 사용하여 시퀀스 표현에서 가장 높은 값을 시간 단계에 걸쳐 가장 중요한 특징으로 추출할 수 있습니다. textCNN에서 사용되는 최대 시간 풀링은 1차원 전역 최대 풀링과 유사하게 작동합니다(Collobert et al., 2011). 각 채널이 서로 다른 시간 간격으로 값을 저장하는 다중 채널 입력의 경우 각 채널의 출력은 해당 채널의 최대값입니다. 시간에 따른 최대 풀링은 서로 다른 채널에서 서로 다른 수의 시간 단계를 허용한다는 점에 유의하십시오.

 

Max-Over-Time Pooling 이란?

 

Max-over-time pooling is a type of pooling operation commonly used in natural language processing (NLP) and text analysis tasks, especially in the context of convolutional neural networks (CNNs) for text classification. The purpose of max-over-time pooling is to extract the most important information or features from a sequence of data (such as a sentence or text document) while preserving their sequential order.

 

맥스 오버 타임 풀링(Max-over-time pooling)은 주로 자연어 처리(NLP) 및 텍스트 분석 작업에서 사용되는 풀링 연산 중 하나로, 특히 텍스트 분류를 위한 컨볼루션 신경망(CNN)의 맥락에서 널리 사용됩니다. 맥스 오버 타임 풀링의 목적은 데이터 시퀀스(예: 문장 또는 텍스트 문서)에서 가장 중요한 정보나 특징을 추출하면서 그들의 순차적인 순서를 보존하는 것입니다.

 

Here's how max-over-time pooling works:

 

맥스 오버 타임 풀링의 작동 방식은 다음과 같습니다.

 

  1. Input: You have a sequence of data, typically represented as a matrix where each row corresponds to a token or word embedding, and the columns represent features of each token.

    입력: 데이터 시퀀스를 가지고 있으며, 일반적으로 각 행이 토큰 또는 단어 임베딩에 해당하고 열은 각 토큰의 특징을 나타내는 행렬로 표현됩니다.

  2. Pooling Operation: Max-over-time pooling takes the maximum value along a specific dimension of the input matrix. In text analysis, this dimension is often the sequence length or time steps. Essentially, for each feature (column), it selects the maximum value across all tokens in the sequence for that feature.

    풀링 연산: 맥스 오버 타임 풀링은 입력 행렬의 특정 차원을 따라 최댓값을 선택합니다. 텍스트 분석에서 이 차원은 일반적으로 시퀀스 길이 또는 시간 단계입니다. 본질적으로 각 특징(열)에 대해 시퀀스 전체에서 해당 특징에 대한 모든 토큰에서 최댓값을 선택합니다.

  3. Result: The result of max-over-time pooling is a fixed-size vector where each element represents the maximum value of a feature across the entire sequence. This vector is sometimes referred to as a "pooled feature" vector.

    결과: 맥스 오버 타임 풀링의 결과는 고정 크기의 벡터입니다. 각 요소는 입력 시퀀스 전체에서 해당 특징에 대한 최댓값을 나타냅니다. 이 벡터는 종종 "풀링된 특징" 벡터로 불립니다.

The intuition behind max-over-time pooling is that it captures the most important information or the "key feature" from each feature dimension of the input sequence. In text classification tasks, this can be particularly useful because it helps identify the most relevant words or phrases in a sentence for making a classification decision.

 

맥스 오버 타임 풀링의 아이디어는 입력 시퀀스의 각 특징에서 가장 중요한 정보 또는 "주요 특징"을 포착하는 것입니다. 텍스트 분류 작업에서 이는 특히 유용합니다. 왜냐하면 이를 통해 분류 결정을 내리기 위해 문장 내에서 가장 관련성 높은 단어나 구를 식별할 수 있기 때문입니다.

 

Max-over-time pooling is often used in combination with convolutional neural networks (CNNs) for text classification tasks. After applying a series of convolutional filters to the input text, max-over-time pooling is used to select the most important features from the resulting feature maps, which are then fed into a classifier for making predictions.

 

맥스 오버 타임 풀링은 종종 텍스트 분류 작업을 위한 컨볼루션 신경망(CNN)과 결합하여 사용됩니다. 입력 텍스트에 일련의 컨볼루션 필터를 적용한 후, 맥스 오버 타임 풀링을 사용하여 결과 특징 맵에서 가장 중요한 특징을 선택하고, 그런 다음 예측을 위한 분류기에 공급합니다.

 

In summary, max-over-time pooling is a pooling technique used in NLP and text analysis to capture the most salient features from a sequence of data, such as a sentence, while preserving the sequence's order. It is particularly useful in text classification tasks to identify key information for making predictions.

 

요약하면, 맥스 오버 타임 풀링은 NLP 및 텍스트 분석에서 데이터 시퀀스(예: 문장)에서 가장 중요한 특징을 추출하고 순차적인 순서를 유지하는 풀링 기술 중 하나입니다. 텍스트 분류 작업에서 키 정보를 식별하는 데 유용하며, 입력 데이터 시퀀스에서 예측을 위한 핵심 정보를 파악하는 데 도움이 됩니다.

 

 

One-dimensional global max pooling 이란?

 

"One-dimensional global max pooling" refers to a specific type of pooling operation applied to one-dimensional data, typically used in deep learning models, particularly for tasks involving sequences such as text or time series data.

 

일차원 글로벌 맥스 풀링"은 주로 시퀀스와 관련된 딥러닝 모델에서 사용되는 일련의 데이터에 적용되는 풀링 작업 중 하나를 가리킵니다. 이것은 일차원 데이터에서 사용되며 주로 텍스트 또는 시계열 데이터와 같은 순차 데이터 작업에 사용됩니다.

 

Here's what it means:

 

이게 무엇을 의미하는지 살펴보겠습니다.

 

  • One-dimensional Data: This refers to data organized in a single sequence, often represented as a vector or a sequence of values along a single dimension. Examples include a sequence of words in a sentence or a time series of sensor readings.

    일차원 데이터: 이것은 하나의 시퀀스로 구성된 데이터를 나타냅니다. 일반적으로 벡터 또는 단일 차원을 따라 값의 시퀀스로 표현됩니다. 예로는 문장의 단어 시퀀스 또는 센서 데이터의 시계열이 있습니다.

  • Global Max Pooling: Pooling is an operation used to reduce the dimensionality of data while retaining essential information. Global max pooling, in particular, involves taking the maximum value across the entire sequence.

    글로벌 맥스 풀링: 풀링은 데이터의 차원을 줄이는 연산으로, 중요한 정보를 유지하면서 데이터의 차원을 줄입니다. 특히 글로벌 맥스 풀링은 전체 시퀀스에서 최대값을 선택하는 것을 의미합니다.

In the context of one-dimensional data, one-dimensional global max pooling entails finding the maximum value from the entire sequence. Here's how it works:

 

일차원 데이터의 맥스 풀링은 전체 시퀀스에서 최대값을 찾는 것을 포함합니다. 작동 방식은 다음과 같습니다.

 

  1. Input: You have a one-dimensional sequence of data.

    입력: 일차원 데이터 시퀀스가 있습니다.

  2. Pooling Operation: The pooling operation scans through the entire sequence and selects the maximum value.

    풀링 연산: 풀링 연산은 전체 시퀀스를 스캔하고 최대값을 선택합니다.

  3. Output: The output of this operation is a single value, which is the maximum value found in the entire sequence.

    출력: 이 작업의 결과는 전체 시퀀스에서 찾은 최대값으로, 시퀀스의 가장 중요한 특징을 나타냅니다.

One-dimensional global max pooling is commonly used in neural networks, especially in convolutional neural networks (CNNs) applied to text or time series data. It helps capture the most important or salient feature from the sequence, which can be useful for various tasks like sentiment analysis, text classification, or anomaly detection in time series data.

일차원 글로벌 맥스 풀링은 특히 텍스트 또는 시계열 데이터에 적용되는 합성곱 신경망(CNN)에서 일반적으로 사용됩니다. 이것은 시퀀스에서 가장 중요한 또는 주요한 특징을 포착하여 해당 문장이나 데이터의 대표적인 값을 얻는 데 도움이 됩니다. 이 값은 감정 분석, 텍스트 분류 또는 시계열 데이터에서 이상 탐지와 같은 다양한 작업에 유용하게 사용됩니다.

For example, in text classification, you might have a sequence of word embeddings for a sentence, and applying one-dimensional global max pooling would give you a single value representing the most important feature in that sentence, which can be used as an input for further layers in the neural network.

 

예를 들어 텍스트 분류에서는 문장에 대한 일련의 단어 임베딩이 있을 수 있습니다. 1차원 전역 최대 풀링을 적용하면 해당 문장에서 가장 중요한 특징을 나타내는 단일 값이 제공되며, 이는 신경망의 추가 레이어에 대한 입력으로 사용될 수 있습니다.

 

 

16.3.3. The textCNN Model

Using the one-dimensional convolution and max-over-time pooling, the textCNN model takes individual pretrained token representations as input, then obtains and transforms sequence representations for the downstream application.

 

1차원 컨볼루션 및 최대 시간 풀링을 사용하여 textCNN 모델은 사전 훈련된 개별 토큰 표현을 입력으로 가져온 다음 다운스트림 애플리케이션에 대한 시퀀스 표현을 얻고 변환합니다.

 

For a single text sequence with n tokens represented by d-dimensional vectors, the width, height, and number of channels of the input tensor are n, 1, and d, respectively. The textCNN model transforms the input into the output as follows:

 

d차원 벡터로 표현되는 n개의 토큰이 있는 단일 텍스트 시퀀스의 경우 입력 텐서의 너비, 높이, 채널 수는 각각 n, 1, d입니다. textCNN 모델은 다음과 같이 입력을 출력으로 변환합니다.

 

  1. Define multiple one-dimensional convolution kernels and perform convolution operations separately on the inputs. Convolution kernels with different widths may capture local features among different numbers of adjacent tokens.

    여러 개의 1차원 컨볼루션 커널을 정의하고 입력에 대해 개별적으로 컨볼루션 작업을 수행합니다. 폭이 서로 다른 컨볼루션 커널은 서로 다른 수의 인접한 토큰 중에서 로컬 기능을 캡처할 수 있습니다.

  2. Perform max-over-time pooling on all the output channels, and then concatenate all the scalar pooling outputs as a vector.

    모든 출력 채널에 대해 최대 시간 풀링을 수행한 다음 모든 스칼라 풀링 출력을 벡터로 연결합니다.

  3. Transform the concatenated vector into the output categories using the fully connected layer. Dropout can be used for reducing overfitting.

    완전 연결 레이어를 사용하여 연결된 벡터를 출력 범주로 변환합니다. Dropout은 Overfitting을 줄이기 위해 사용될 수 있습니다.

 

Fig. 16.3.5  The model architecture of textCNN.

 

Fig. 16.3.5 illustrates the model architecture of textCNN with a concrete example. The input is a sentence with 11 tokens, where each token is represented by a 6-dimensional vectors. So we have a 6-channel input with width 11. Define two one-dimensional convolution kernels of widths 2 and 4, with 4 and 5 output channels, respectively. They produce 4 output channels with width 11−2+1=10 and 5 output channels with width 11−4+1=8. Despite different widths of these 9 channels, the max-over-time pooling gives a concatenated 9-dimensional vector, which is finally transformed into a 2-dimensional output vector for binary sentiment predictions.

 

그림 16.3.5는 구체적인 예를 들어 textCNN의 모델 아키텍처를 보여줍니다. 입력은 11개의 토큰이 포함된 문장이며, 각 토큰은 6차원 벡터로 표시됩니다. 따라서 너비가 11인 6채널 입력이 있습니다. 각각 4개와 5개의 출력 채널을 사용하여 너비가 2와 4인 두 개의 1차원 컨볼루션 커널을 정의합니다. 너비가 11−2+1=10인 4개의 출력 채널과 너비가 11−4+1=8인 5개의 출력 채널을 생성합니다. 이러한 9개 채널의 너비가 다름에도 불구하고 시간별 최대 풀링은 연결된 9차원 벡터를 제공하며 이는 최종적으로 이진 감정 예측을 위한 2차원 출력 벡터로 변환됩니다.

 

textCNN이란?

 

TextCNN은 텍스트 분류 및 텍스트 기반 작업을 위한 컨볼루션 신경망 (Convolutional Neural Network) 아키텍처 중 하나입니다. 주로 텍스트 문제에 적합한 모델 구조로, 텍스트 분류, 감정 분석, 스팸 탐지, 텍스트 유사성 평가 등 다양한 자연어 처리 (NLP) 작업에 사용됩니다. 아래에서 TextCNN의 주요 특징과 작동 방식을 설명하겠습니다.

 

주요 특징:

 

  1. 합성곱 레이어 사용: TextCNN은 합성곱 레이어를 사용하여 텍스트의 지역적인 특징을 추출합니다. 이 합성곱 레이어는 일반적으로 텍스트에서의 n-그램 (n-grams)을 감지하는 데 사용됩니다. 예를 들어, 1-gram 합성곱은 단어 수준의 특징을, 2-gram 합성곱은 이웃한 단어 쌍의 특징을 추출합니다.

  2. 풀링 레이어 사용: TextCNN은 풀링 레이어를 사용하여 추출된 특징을 요약합니다. 일반적으로 최대 풀링 (max-pooling)이 사용되며, 각 합성곱 필터가 생성한 특징 중에서 가장 중요한 정보만을 선택합니다. 이것은 텍스트의 중요한 부분을 강조하는 데 도움이 됩니다.

  3. 다중 크기의 필터: TextCNN은 서로 다른 크기의 여러 합성곱 필터를 사용합니다. 이로 인해 모델은 다양한 크기의 텍스트 구조를 캡처할 수 있으며, 단어 수준과 구문 수준의 특징을 동시에 학습할 수 있습니다.

  4. 단어 임베딩: 텍스트 데이터를 처리하기 위해 사전 훈련된 단어 임베딩 (Word Embedding)을 사용하는 것이 일반적입니다. 이를 통해 단어를 고정 차원의 벡터로 표현하고, 이러한 임베딩을 모델 입력으로 사용합니다.

  5. 전역 맥스 풀링: 마지막으로, 전역 맥스 풀링을 사용하여 합성곱 레이어의 출력을 하나의 벡터로 요약합니다. 이는 텍스트 길이와 상관없이 일정한 출력 크기를 갖게 해주며, 텍스트 분류 작업을 위해 주로 사용됩니다.

작동 방식:

  1. 입력 텍스트는 단어 임베딩으로 변환됩니다.
  2. 서로 다른 크기의 합성곱 필터가 입력 텍스트를 합성곱 연산합니다. 이것은 입력에서 서로 다른 크기의 특징을 추출합니다.
  3. 합성곱 결과를 최대 풀링 레이어를 통해 요약하고, 각 필터가 생성한 가장 중요한 특징만 남깁니다.
  4. 다양한 크기의 필터에서 얻은 특징을 하나로 연결합니다.
  5. 전역 맥스 풀링을 사용하여 이러한 특징을 최종적으로 하나의 벡터로 압축합니다.
  6. 압축된 벡터를 완전 연결 레이어를 통해 최종 예측 결과로 변환합니다.

TextCNN은 간단하면서도 강력한 텍스트 분류 모델 중 하나로, 컴퓨터 비전에서의 CNN과 유사한 구조를 텍스트 처리에 적용한 예입니다. 이 모델은 텍스트 데이터의 특징 추출과 분류를 위해 효과적이며, 자연어 처리 작업에 널리 사용됩니다.

 

16.3.3.1. Defining the Model

 

We implement the textCNN model in the following class. Compared with the bidirectional RNN model in Section 16.2, besides replacing recurrent layers with convolutional layers, we also use two embedding layers: one with trainable weights and the other with fixed weights.

 

다음 클래스에서 textCNN 모델을 구현합니다. 섹션 16.2의 양방향 RNN 모델과 비교하여 순환 레이어를 컨볼루셔널 레이어로 바꾸는 것 외에도 두 개의 임베딩 레이어를 사용합니다. 하나는 훈련 가능한 가중치가 있고 다른 하나는 고정 가중치가 있습니다.

 

class TextCNN(nn.Module):
    def __init__(self, vocab_size, embed_size, kernel_sizes, num_channels,
                 **kwargs):
        super(TextCNN, self).__init__(**kwargs)
        self.embedding = nn.Embedding(vocab_size, embed_size)
        # The embedding layer not to be trained
        self.constant_embedding = nn.Embedding(vocab_size, embed_size)
        self.dropout = nn.Dropout(0.5)
        self.decoder = nn.Linear(sum(num_channels), 2)
        # The max-over-time pooling layer has no parameters, so this instance
        # can be shared
        self.pool = nn.AdaptiveAvgPool1d(1)
        self.relu = nn.ReLU()
        # Create multiple one-dimensional convolutional layers
        self.convs = nn.ModuleList()
        for c, k in zip(num_channels, kernel_sizes):
            self.convs.append(nn.Conv1d(2 * embed_size, c, k))

    def forward(self, inputs):
        # Concatenate two embedding layer outputs with shape (batch size, no.
        # of tokens, token vector dimension) along vectors
        embeddings = torch.cat((
            self.embedding(inputs), self.constant_embedding(inputs)), dim=2)
        # Per the input format of one-dimensional convolutional layers,
        # rearrange the tensor so that the second dimension stores channels
        embeddings = embeddings.permute(0, 2, 1)
        # For each one-dimensional convolutional layer, after max-over-time
        # pooling, a tensor of shape (batch size, no. of channels, 1) is
        # obtained. Remove the last dimension and concatenate along channels
        encoding = torch.cat([
            torch.squeeze(self.relu(self.pool(conv(embeddings))), dim=-1)
            for conv in self.convs], dim=1)
        outputs = self.decoder(self.dropout(encoding))
        return outputs

이 코드는 텍스트 분류를 위한 CNN(Convolutional Neural Network) 모델을 정의하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 클래스 정의:
    • TextCNN(nn.Module) 클래스는 텍스트 분류를 위한 CNN 모델을 정의합니다.
    • vocab_size: 어휘 사전의 크기
    • embed_size: 임베딩 차원 크기
    • kernel_sizes: 커널 크기 리스트
    • num_channels: 각 커널 크기에 해당하는 채널 수 리스트
  2. 초기화 메서드:
    • __init__ 메서드에서 모델의 구조를 정의합니다.
    • 임베딩 레이어, 고정된 임베딩 레이어, 드롭아웃, 디코더(FC 레이어), 풀링 레이어, 활성화 함수 레이어, 1D 컨볼루션 레이어들을 생성합니다.
  3. 순전파 메서드:
    • forward 메서드에서 모델의 순전파 연산을 정의합니다.
    • 입력 데이터에 대한 임베딩을 생성하고, 두 개의 임베딩 레이어 결과를 연결(concatenate)합니다.
    • 1D 컨볼루션 레이어들을 순회하며 연산을 수행하고, 풀링과 활성화 함수를 적용한 후 연산 결과를 연결합니다.
    • 최종적으로 드롭아웃과 FC 레이어를 통해 최종 출력을 계산합니다.

즉, 이 코드는 텍스트 분류를 위한 CNN 모델을 정의하는데 사용되며, 임베딩, 컨볼루션, 풀링, 드롭아웃 등의 다양한 레이어들이 조합되어 텍스트 데이터의 특성을 추출하고 분류하는 데 활용됩니다.

 

 

Let’s create a textCNN instance. It has 3 convolutional layers with kernel widths of 3, 4, and 5, all with 100 output channels.

 

textCNN 인스턴스를 만들어 보겠습니다. 커널 너비가 3, 4, 5이고 모두 100개의 출력 채널을 갖는 3개의 컨벌루션 레이어가 있습니다.

 

embed_size, kernel_sizes, nums_channels = 100, [3, 4, 5], [100, 100, 100]
devices = d2l.try_all_gpus()
net = TextCNN(len(vocab), embed_size, kernel_sizes, nums_channels)

def init_weights(module):
    if type(module) in (nn.Linear, nn.Conv1d):
        nn.init.xavier_uniform_(module.weight)

net.apply(init_weights);

이 코드는 TextCNN 모델을 초기화하고 가중치를 초기화하는 작업을 수행하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 변수 설정:
    • embed_size: 임베딩 차원 크기를 100으로 설정합니다.
    • kernel_sizes: 사용할 커널 크기 리스트로 [3, 4, 5]로 설정합니다.
    • nums_channels: 각 커널 크기에 해당하는 채널 수 리스트로 [100, 100, 100]으로 설정합니다.
  2. GPU 디바이스 설정:
    • devices = d2l.try_all_gpus(): 가능한 모든 GPU 디바이스를 가져옵니다.
  3. TextCNN 모델 생성:
    • net = TextCNN(len(vocab), embed_size, kernel_sizes, nums_channels): TextCNN 클래스를 사용하여 텍스트 분류를 위한 CNN 모델을 생성합니다. 어휘 사전의 크기, 임베딩 차원 크기, 커널 크기 리스트, 채널 수 리스트를 인자로 제공합니다.
  4. 초기화 함수 정의:
    • init_weights(module) 함수는 모듈의 가중치를 초기화하는 역할을 합니다.
    • 만약 모듈이 nn.Linear 또는 nn.Conv1d 타입이라면, 해당 모듈의 가중치를 Xavier 초기화를 사용하여 초기화합니다.
  5. 가중치 초기화:
    • net.apply(init_weights);: 모델의 가중치를 초기화하는 함수 init_weights를 적용합니다. 이를 통해 모델 내의 선형 레이어와 1D 컨볼루션 레이어의 가중치가 초기화됩니다.

결과적으로, 이 코드는 TextCNN 모델을 초기화하고 Xavier 초기화를 사용하여 모델 내의 선형 레이어와 1D 컨볼루션 레이어의 가중치를 초기화합니다. 이를 통해 모델이 효과적으로 학습될 수 있도록 준비 단계를 수행합니다.

 

16.3.3.2. Loading Pretrained Word Vectors

 

Same as Section 16.2, we load pretrained 100-dimensional GloVe embeddings as the initialized token representations. These token representations (embedding weights) will be trained in embedding and fixed in constant_embedding.

 

섹션 16.2와 동일하게 사전 훈련된 100차원 GloVe 임베딩을 초기화된 토큰 표현으로 로드합니다. 이러한 토큰 표현(임베딩 가중치)은 임베딩에 대해 학습되고 Constant_embedding에서 수정됩니다.

 

glove_embedding = d2l.TokenEmbedding('glove.6b.100d')
embeds = glove_embedding[vocab.idx_to_token]
net.embedding.weight.data.copy_(embeds)
net.constant_embedding.weight.data.copy_(embeds)
net.constant_embedding.weight.requires_grad = False

이 코드는 사전 훈련된 임베딩 벡터를 사용하여 모델의 임베딩 레이어 가중치를 초기화하고, 고정된 임베딩 레이어의 학습을 방지하는 작업을 수행하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 사전 훈련된 임베딩 벡터 로드:
    • glove_embedding = d2l.TokenEmbedding('glove.6b.100d'): 'glove.6b.100d' 데이터셋의 사전 훈련된 임베딩 벡터를 가져옵니다. 이 벡터들은 단어에 대한 의미론적 표현을 포함하고 있습니다.
  2. 어휘에 해당하는 임베딩 벡터 추출:
    • embeds = glove_embedding[vocab.idx_to_token]: 어휘 사전에 있는 단어들에 대응하는 임베딩 벡터를 glove_embedding에서 추출합니다. idx_to_token 메서드는 어휘 사전의 인덱스에 해당하는 단어를 반환합니다.
  3. 임베딩 레이어 가중치 초기화:
    • net.embedding.weight.data.copy_(embeds): 모델의 임베딩 레이어의 가중치를 사전 훈련된 임베딩 벡터 embeds로 초기화합니다.
  4. 고정된 임베딩 레이어 가중치 초기화 및 학습 방지:
    • net.constant_embedding.weight.data.copy_(embeds): 모델의 고정된 임베딩 레이어의 가중치도 사전 훈련된 임베딩 벡터 embeds로 초기화합니다.
    • net.constant_embedding.weight.requires_grad = False: 고정된 임베딩 레이어의 가중치에 대한 기울기 계산을 비활성화하여 해당 가중치가 학습되지 않도록 합니다.

결과적으로, 이 코드는 모델의 임베딩 레이어와 고정된 임베딩 레이어에 대해 사전 훈련된 임베딩 벡터를 초기화하고, 고정된 임베딩 레이어의 학습을 방지하여 모델이 사전 훈련된 의미론적 정보를 활용하면서도 특정 임베딩 가중치는 업데이트되지 않도록 설정합니다.

 

16.3.3.3. Training and Evaluating the Model

 

Now we can train the textCNN model for sentiment analysis.

 

이제 감정 분석을 위해 textCNN 모델을 훈련할 수 있습니다.

 

lr, num_epochs = 0.001, 5
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction="none")
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)

이 코드는 TextCNN 모델을 학습하는 과정을 구성하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 학습률 및 에폭 설정:
    • lr, num_epochs = 0.001, 5: 학습률을 0.001로 설정하고, 학습할 에폭 수를 5로 설정합니다.
  2. 옵티마이저 설정:
    • trainer = torch.optim.Adam(net.parameters(), lr=lr): Adam 옵티마이저를 생성하고, 모델의 파라미터들을 최적화할 대상으로 지정합니다. 학습률은 위에서 설정한 값인 0.001로 설정됩니다.
  3. 손실 함수 설정:
    • loss = nn.CrossEntropyLoss(reduction="none"): 교차 엔트로피 손실 함수를 생성하고, 각 샘플에 대한 손실 값을 개별적으로 계산하기 위해 reduction을 "none"으로 설정합니다.
  4. 모델 학습:
    • d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices): d2l 라이브러리의 train_ch13 함수를 사용하여 모델을 학습합니다. 학습 데이터인 train_iter, 검증 데이터인 test_iter, 손실 함수 loss, 옵티마이저 trainer, 에폭 수 num_epochs, GPU 디바이스 devices 등을 인자로 제공합니다.

결과적으로, 이 코드는 지정한 학습률과 에폭 수로 TextCNN 모델을 학습시키고, 손실 함수와 옵티마이저를 사용하여 모델을 최적화합니다. d2l 라이브러리의 학습 함수를 사용하여 학습 과정을 수행하며, 학습 데이터와 검증 데이터의 손실 및 성능을 모니터링하면서 모델이 훈련됩니다.

loss 0.066, train acc 0.979, test acc 0.868
4354.2 examples/sec on [device(type='cuda', index=0), device(type='cuda', index=1)]

 

Below we use the trained model to predict the sentiment for two simple sentences.

 

아래에서는 훈련된 모델을 사용하여 두 개의 간단한 문장에 대한 감정을 예측합니다.

 

d2l.predict_sentiment(net, vocab, 'this movie is so great')

이 코드는 훈련된 TextCNN 모델을 사용하여 주어진 텍스트에 대한 감성 분석 결과를 예측하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 함수 호출:
    • d2l.predict_sentiment(net, vocab, 'this movie is so great'): d2l 라이브러리의 predict_sentiment 함수를 호출합니다. 이 함수는 훈련된 모델 net, 어휘 사전 vocab, 그리고 예측하고자 하는 텍스트 'this movie is so great'를 인자로 제공하여 해당 텍스트의 감성을 예측합니다.
  2. 예측 결과 반환:
    • 함수는 주어진 텍스트에 대한 감성 예측을 수행하고, 예측 결과를 반환합니다. 결과로 'positive'나 'negative'와 같은 감성 분석 결과를 출력합니다.

즉, 이 코드는 훈련된 TextCNN 모델을 사용하여 주어진 텍스트의 감성을 예측하는 작업을 수행하고 그 결과를 출력합니다. 'this movie is so great'와 같은 문장을 입력으로 주면 해당 문장의 감성 분석 결과를 예측하여 출력합니다.

'positive'

d2l.predict_sentiment(net, vocab, 'this movie is so bad')
'negative'

이건 내가 바라는대로 답이 나오지 않음.

 

16.3.4. Summary

  • One-dimensional CNNs can process local features such as n-grams in text.

    1차원 CNN은 텍스트의 n-gram과 같은 로컬 기능을 처리할 수 있습니다.

  • Multi-input-channel one-dimensional cross-correlations are equivalent to single-input-channel two-dimensional cross-correlations.

    다중 입력 채널 1차원 상호 상관은 단일 입력 채널 2차원 상호 상관과 동일합니다.

  • The max-over-time pooling allows different numbers of time steps at different channels.

    최대 시간별 풀링은 서로 다른 채널에서 서로 다른 수의 시간 단계를 허용합니다.

  • The textCNN model transforms individual token representations into downstream application outputs using one-dimensional convolutional layers and max-over-time pooling layers.

    textCNN 모델은 1차원 컨벌루션 레이어와 시간별 최대 풀링 레이어를 사용하여 개별 토큰 표현을 다운스트림 애플리케이션 출력으로 변환합니다.

 

16.3.5. Exercises

  1. Tune hyperparameters and compare the two architectures for sentiment analysis in Section 16.2 and in this section, such as in classification accuracy and computational efficiency.
  2. Can you further improve the classification accuracy of the model by using the methods introduced in the exercises of Section 16.2?
  3. Add positional encoding in the input representations. Does it improve the classification accuracy?

 

 

 

반응형


반응형

16.2. Sentiment Analysis: Using Recurrent Neural Networks — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

16.2. Sentiment Analysis: Using Recurrent Neural Networks — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

 

16.2. Sentiment Analysis: Using Recurrent Neural Networks

 

Like word similarity and analogy tasks, we can also apply pretrained word vectors to sentiment analysis. Since the IMDb review dataset in Section 16.1 is not very big, using text representations that were pretrained on large-scale corpora may reduce overfitting of the model. As a specific example illustrated in Fig. 16.2.1, we will represent each token using the pretrained GloVe model, and feed these token representations into a multilayer bidirectional RNN to obtain the text sequence representation, which will be transformed into sentiment analysis outputs (Maas et al., 2011). For the same downstream application, we will consider a different architectural choice later.

 

단어 유사성 및 유추 작업과 마찬가지로 사전 훈련된 단어 벡터를 감정 분석에 적용할 수도 있습니다. 섹션 16.1의 IMDb 검토 데이터 세트는 그다지 크지 않기 때문에 대규모 말뭉치에 대해 사전 훈련된 텍스트 표현을 사용하면 모델의 과적합을 줄일 수 있습니다. 그림 16.2.1에 설명된 구체적인 예와 같이 사전 훈련된 GloVe 모델을 사용하여 각 토큰을 표현하고 이러한 토큰 표현을 다층 양방향 RNN에 공급하여 감정 분석 출력으로 변환될 텍스트 시퀀스 표현을 얻습니다(Maas 등, 2011). 동일한 다운스트림 애플리케이션에 대해 나중에 다른 아키텍처 선택을 고려할 것입니다.

 

Fig. 16.2.1  This section feeds pretrained GloVe to an RNN-based architecture for sentiment analysis.

import torch
from torch import nn
from d2l import torch as d2l

batch_size = 64
train_iter, test_iter, vocab = d2l.load_data_imdb(batch_size)

 

16.2.1. Representing Single Text with RNNs

In text classifications tasks, such as sentiment analysis, a varying-length text sequence will be transformed into fixed-length categories. In the following BiRNN class, while each token of a text sequence gets its individual pretrained GloVe representation via the embedding layer (self.embedding), the entire sequence is encoded by a bidirectional RNN (self.encoder). More concretely, the hidden states (at the last layer) of the bidirectional LSTM at both the initial and final time steps are concatenated as the representation of the text sequence. This single text representation is then transformed into output categories by a fully connected layer (self.decoder) with two outputs (“positive” and “negative”).

 

감정 분석과 같은 텍스트 분류 작업에서는 다양한 길이의 텍스트 시퀀스가 고정 길이 범주로 변환됩니다. 다음 BiRNN 클래스에서 텍스트 시퀀스의 각 토큰은 임베딩 레이어(self.embedding)를 통해 사전 훈련된 개별 GloVe 표현을 가져오지만 전체 시퀀스는 양방향 RNN(self.encoder)으로 인코딩됩니다. 보다 구체적으로, 초기 및 최종 시간 단계 모두에서 양방향 LSTM의 숨겨진 상태(마지막 레이어)는 텍스트 시퀀스의 표현으로 연결됩니다. 이 단일 텍스트 표현은 두 개의 출력("양수" 및 "음수")이 있는 완전히 연결된 레이어(self.decoder)에 의해 출력 범주로 변환됩니다.

 

LSTM 이란?

 

LSTM stands for "Long Short-Term Memory," which is a type of recurrent neural network (RNN) architecture designed to handle sequences and patterns in data. RNNs are a class of artificial neural networks that are well-suited for tasks involving sequential data, such as time series, natural language processing, and more.

 

LSTM은 "Long Short-Term Memory"의 약자로, 데이터의 시퀀스와 패턴을 다루기 위해 고안된 순환 신경망(RNN) 아키텍처입니다. RNN은 시계열 데이터, 자연어 처리 등과 같은 순차적인 데이터를 다루는 데 적합한 인공 신경망의 한 유형입니다.

 

The key challenge LSTM addresses is the vanishing gradient problem, which can occur when training traditional RNNs. The vanishing gradient problem makes it difficult for RNNs to capture long-range dependencies in sequential data. LSTM networks overcome this limitation by introducing specialized memory cells and gating mechanisms that allow them to learn and store information for longer periods of time.

 

LSTM이 다루는 주요 문제는 기존의 RNN에서 나타나는 사라지는 그래디언트 문제(vanishing gradient problem)입니다. 사라지는 그래디언트 문제는 전통적인 RNN이 순차 데이터의 장기적인 의존성을 잡아내기 어렵게 만듭니다. LSTM 네트워크는 특수한 메모리 셀과 게이팅 메커니즘을 도입하여 더 오래된 시간 동안 정보를 학습하고 저장할 수 있도록 합니다.

 

LSTM networks consist of different components that work together to process sequences of data:

 

LSTM 네트워크는 데이터 시퀀스를 처리하기 위해 함께 동작하는 여러 구성 요소로 구성됩니다:

 

  1. Cell State: This is the "memory" of the LSTM. Information can be added to or removed from the cell state using various gates.

    셀 상태(Cell State): 이것은 LSTM의 "메모리"입니다. 다양한 게이트를 사용하여 셀 상태에 정보를 추가하거나 제거할 수 있습니다.

  2. Input Gate: Determines what information from the current input should be added to the cell state.

    입력 게이트(Input Gate): 현재 입력에서 어떤 정보를 셀 상태에 추가해야 할지 결정합니다.

  3. Forget Gate: Decides what information from the cell state should be removed or forgotten.

    망각 게이트(Forget Gate): 셀 상태에서 어떤 정보를 제거하거나 잊어야 할지 결정합니다.

  4. Output Gate: Determines what information from the cell state should be output based on the current input and the past memory.

    출력 게이트(Output Gate): 현재 입력과 이전 메모리를 기반으로 셀 상태에서 어떤 정보를 출력해야 할지 결정합니다.

LSTM networks excel in capturing and maintaining dependencies in sequences, making them highly effective for tasks like language modeling, machine translation, sentiment analysis, speech recognition, and more. They are capable of learning both short-term and long-term dependencies, which makes them particularly suitable for handling sequences with varying time lags between important events.

LSTM 네트워크는 시퀀스의 의존성을 캡처하고 유지하는 데 능숙하며, 언어 모델링, 기계 번역, 감성 분석, 음성 인식 등과 같은 작업에서 뛰어난 성과를 내놓습니다. LSTM은 단기적인 의존성과 장기적인 의존성을 모두 학습할 수 있어, 중요한 이벤트 사이의 다양한 시간 간격을 가진 시퀀스를 처리하는 데 특히 적합합니다.

In summary, LSTM (Long Short-Term Memory) is a type of recurrent neural network architecture that addresses the vanishing gradient problem and excels in modeling sequential data by utilizing memory cells and gating mechanisms.

요약하면, LSTM(Long Short-Term Memory)은 사라지는 그래디언트 문제를 해결하고 메모리 셀과 게이팅 메커니즘을 활용하여 순차적인 데이터를 모델링하는 순환 신경망 아키텍처입니다.

 

**아래 코드가 핵심임

class BiRNN(nn.Module):
    def __init__(self, vocab_size, embed_size, num_hiddens,
                 num_layers, **kwargs):
        super(BiRNN, self).__init__(**kwargs)
        self.embedding = nn.Embedding(vocab_size, embed_size)
        # Set `bidirectional` to True to get a bidirectional RNN
        self.encoder = nn.LSTM(embed_size, num_hiddens, num_layers=num_layers,
                                bidirectional=True)
        self.decoder = nn.Linear(4 * num_hiddens, 2)

    def forward(self, inputs):
        # The shape of `inputs` is (batch size, no. of time steps). Because
        # LSTM requires its input's first dimension to be the temporal
        # dimension, the input is transposed before obtaining token
        # representations. The output shape is (no. of time steps, batch size,
        # word vector dimension)
        embeddings = self.embedding(inputs.T)
        self.encoder.flatten_parameters()
        # Returns hidden states of the last hidden layer at different time
        # steps. The shape of `outputs` is (no. of time steps, batch size,
        # 2 * no. of hidden units)
        outputs, _ = self.encoder(embeddings)
        # Concatenate the hidden states at the initial and final time steps as
        # the input of the fully connected layer. Its shape is (batch size,
        # 4 * no. of hidden units)
        encoding = torch.cat((outputs[0], outputs[-1]), dim=1)
        outs = self.decoder(encoding)
        return outs

이 코드는 양방향 LSTM 네트워크를 정의하는 파이토치 모듈(nn.Module)입니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. 클래스 정의와 초기화(__init__ 메서드):
    • BiRNN 클래스는 nn.Module 클래스를 상속하여 정의됩니다.
    • 클래스의 생성자(__init__)에서는 네트워크의 구조와 파라미터를 설정합니다.
    • vocab_size, embed_size, num_hiddens, num_layers 등의 인자를 받아 네트워크를 초기화합니다.
  2. 네트워크 구조 설정:
    • nn.Embedding 층을 사용하여 임베딩을 정의합니다. vocab_size와 embed_size는 어휘 사전 크기와 임베딩 차원을 의미합니다.
    • nn.LSTM 층을 사용하여 양방향 LSTM을 정의합니다. num_hiddens는 은닉 유닛의 개수, num_layers는 LSTM 층의 층 수를 의미합니다.
    • nn.Linear 층을 사용하여 완전 연결층을 정의합니다. 입력 차원은 4 * num_hiddens로 설정되며, 출력 차원은 2로 설정됩니다.
  3. forward 메서드:
    • 입력 데이터 inputs를 받아 모델을 통해 결과를 반환하는 forward 메서드를 정의합니다.
    • 입력 데이터를 임베딩하고 양방향 LSTM에 통과시키는 작업을 수행합니다.
    • 초기와 최종 시간 단계에서의 은닉 상태를 연결한 결과를 완전 연결층에 통과시켜 최종 출력을 얻습니다.

이 코드는 양방향 LSTM 네트워크를 정의하고 입력 데이터를 처리하여 출력을 생성하는 기능을 가진 파이토치 모듈을 생성합니다.

 

 

Let’s construct a bidirectional RNN with two hidden layers to represent single text for sentiment analysis.

 

감정 분석을 위한 단일 텍스트를 나타내기 위해 두 개의 숨겨진 레이어가 있는 양방향 RNN을 구성해 보겠습니다.

 

embed_size, num_hiddens, num_layers, devices = 100, 100, 2, d2l.try_all_gpus()
net = BiRNN(len(vocab), embed_size, num_hiddens, num_layers)

def init_weights(module):
    if type(module) == nn.Linear:
        nn.init.xavier_uniform_(module.weight)
    if type(module) == nn.LSTM:
        for param in module._flat_weights_names:
            if "weight" in param:
                nn.init.xavier_uniform_(module._parameters[param])
net.apply(init_weights);

이 코드는 모델의 가중치를 초기화하는 과정을 수행하는 파이토치 코드입니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. 하이퍼파라미터 설정:
    • embed_size, num_hiddens, num_layers는 모델의 임베딩 차원, 은닉 유닛 수, LSTM 층 수를 나타내는 값입니다.
    • d2l.try_all_gpus()는 사용 가능한 모든 GPU 디바이스를 가져옵니다.
  2. 모델 생성:
    • BiRNN 클래스를 사용하여 net 모델을 생성합니다.
    • len(vocab)는 어휘 사전의 크기를 나타내며, embed_size, num_hiddens, num_layers는 앞서 설정한 하이퍼파라미터입니다.
  3. 가중치 초기화 함수 정의:
    • init_weights 함수는 모델의 가중치를 초기화하는 역할을 합니다.
    • nn.Linear 타입의 층의 가중치를 세팅하는 부분과 nn.LSTM 타입의 층의 가중치를 세팅하는 부분으로 구성됩니다.
  4. 가중치 초기화 적용:
    • net.apply(init_weights)는 net 모델에 init_weights 함수를 적용하여 가중치 초기화를 수행합니다.

즉, 이 코드는 먼저 모델을 정의하고, 그 모델의 가중치를 초기화하는 함수를 정의한 후, 해당 함수를 모델에 적용하여 모델의 가중치를 초기화하는 작업을 수행합니다. 초기화된 가중치는 모델 학습 전에 모델 파라미터를 초기화하는데 사용됩니다.

 

https://pytorch.org/cppdocs/api/function_namespacetorch_1_1nn_1_1init_1ace282f75916a862c9678343dfd4d5ffe.html

 

Function torch::nn::init::xavier_uniform_ — PyTorch main documentation

Shortcuts

pytorch.org

 

[Pytorch] 파이토치 가중치 초기화 방법(Xavier, He) (tistory.com)

 

[Pytorch] 파이토치 가중치 초기화 방법(Xavier, He)

Python torch Weight Initialization 파이토치에서 Xavier, He 등의 가중치 초기화를 각 layer 별로 혹은 모델 전체에 대하여 진행하는 방법을 간략하게 요약해보도록 하겠습니다. 우선, 다음과 같이 fc1, fc2, fc3

jimmy-ai.tistory.com

16.2.2. Loading Pretrained Word Vectors

 

Below we load the pretrained 100-dimensional (needs to be consistent with embed_size) GloVe embeddings for tokens in the vocabulary.

 

아래에서는 어휘의 토큰에 대해 사전 훈련된 100차원(embed_size와 일치해야 함) GloVe 임베딩을 로드합니다.

 

glove_embedding = d2l.TokenEmbedding('glove.6b.100d')

이 코드는 GloVe(차원 크기: 100) 사전 훈련된 임베딩을 불러오는 작업을 수행합니다. 코드의 작동 방식을 설명하겠습니다.

  1. d2l.TokenEmbedding('glove.6b.100d'):
    • d2l.TokenEmbedding은 토큰 임베딩을 다루는 도구입니다.
    • 'glove.6b.100d'는 GloVe 임베딩의 파일 이름을 나타냅니다.
    • 이 파일 이름은 6억 개의 토큰으로 구성된 GloVe 데이터셋을 사용하며, 각 임베딩은 100차원으로 표현됩니다.

이 코드는 GloVe 사전 훈련된 임베딩을 불러와 glove_embedding 객체에 저장하는 작업을 수행합니다. 이 임베딩은 텍스트 데이터를 임베딩 벡터로 변환하는 데 사용될 수 있습니다.

 

Print the shape of the vectors for all the tokens in the vocabulary.

 

어휘의 모든 토큰에 대한 벡터의 모양을 인쇄합니다.

 

embeds = glove_embedding[vocab.idx_to_token]
embeds.shape

이 코드는 어휘의 토큰에 해당하는 GloVe 임베딩을 가져오고 해당 임베딩의 형태(shape)를 확인하는 작업을 수행합니다. 코드의 작동 방식을 설명하겠습니다.

  1. glove_embedding[vocab.idx_to_token]:
    • vocab.idx_to_token은 어휘 사전의 인덱스를 토큰으로 매핑한 딕셔너리입니다.
    • glove_embedding은 GloVe 임베딩 객체를 나타내며, [vocab.idx_to_token]은 어휘의 각 토큰에 해당하는 GloVe 임베딩을 선택합니다.
    • 이 작업을 통해 어휘 내 모든 토큰의 GloVe 임베딩을 가져옵니다.
  2. embeds.shape:
    • embeds는 선택된 GloVe 임베딩들로 이루어진 텐서입니다.
    • .shape는 해당 텐서의 형태(shape)를 확인하는 연산입니다.
    • embeds.shape는 GloVe 임베딩 텐서의 형태를 출력합니다.

즉, 이 코드는 어휘 내의 각 토큰에 해당하는 GloVe 임베딩을 가져와 텐서 embeds에 저장하고, 이 텐서의 형태를 출력하여 임베딩 차원 정보를 확인하는 작업을 수행합니다.

torch.Size([49346, 100])

We use these pretrained word vectors to represent tokens in the reviews and will not update these vectors during training.

 

우리는 사전 훈련된 단어 벡터를 사용하여 리뷰에서 토큰을 나타내며 훈련 중에는 이러한 벡터를 업데이트하지 않습니다.

 

net.embedding.weight.data.copy_(embeds)
net.embedding.weight.requires_grad = False

이 코드는 뉴럴 네트워크의 임베딩 층에 미리 훈련된 임베딩 값을 복사하고, 해당 임베딩 가중치의 역전파를 막는 작업을 수행합니다. 코드의 작동 방식을 설명하겠습니다.

  1. net.embedding.weight.data.copy_(embeds):
    • net은 이미 정의된 뉴럴 네트워크 모델을 가리킵니다.
    • embedding.weight는 뉴럴 네트워크 내의 임베딩 층의 가중치를 나타냅니다.
    • .data는 가중치 텐서 자체를 나타냅니다.
    • .copy_(embeds)는 embeds 텐서의 값을 가중치 텐서로 복사합니다.
    • 이 작업을 통해 미리 훈련된 임베딩 값을 모델의 임베딩 가중치에 복사합니다.
  2. net.embedding.weight.requires_grad = False:
    • requires_grad는 파라미터의 역전파 여부를 제어하는 플래그입니다.
    • net.embedding.weight.requires_grad를 False로 설정하여 해당 임베딩 가중치의 역전파를 막습니다.
    • 이는 미리 훈련된 임베딩 값을 고정하고 역전파 시 가중치 업데이트를 방지하는 데 사용됩니다.

즉, 이 코드는 뉴럴 네트워크 모델의 임베딩 층에 미리 훈련된 임베딩 값을 복사하고, 해당 가중치의 역전파를 막아 가중치가 고정되도록 설정하는 작업을 수행합니다. 이는 사전 훈련된 임베딩을 사용하여 모델을 초기화하고, 이후 임베딩 가중치를 업데이트하지 않는 용도로 사용될 수 있습니다.

 

16.2.3. Training and Evaluating the Model

 

Now we can train the bidirectional RNN for sentiment analysis.

 

이제 감정 분석을 위해 양방향 RNN을 훈련할 수 있습니다.

 

lr, num_epochs = 0.01, 5
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction="none")
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)

이 코드는 모델의 학습을 설정하고 학습을 실행하는 작업을 수행하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 학습 관련 하이퍼파라미터 설정:
    • lr은 학습률(learning rate)을 나타냅니다.
    • num_epochs는 학습 에포크(epoch)의 수를 나타냅니다.
  2. 옵티마이저 설정:
    • torch.optim.Adam은 Adam 옵티마이저를 생성하는 역할을 합니다.
    • net.parameters()는 모델 내의 학습 가능한 파라미터들을 반환합니다.
    • lr=lr로 학습률을 설정하여 옵티마이저를 초기화합니다.
  3. 손실 함수 설정:
    • nn.CrossEntropyLoss(reduction="none")은 교차 엔트로피 손실 함수를 생성합니다.
    • reduction="none"은 손실 값을 개별 데이터 포인트별로 계산하도록 설정합니다.
  4. 학습 실행:
    • d2l.train_ch13()은 모델을 학습하는 함수입니다. 해당 함수에 학습에 필요한 정보를 전달하여 학습을 실행합니다.
    • net: 학습할 모델
    • train_iter: 훈련 데이터 이터레이터
    • test_iter: 테스트 데이터 이터레이터
    • loss: 사용할 손실 함수
    • trainer: 사용할 옵티마이저
    • num_epochs: 학습 에포크 수
    • devices: 사용할 디바이스(GPU) 정보

즉, 이 코드는 하이퍼파라미터, 옵티마이저, 손실 함수를 설정하고 지정된 에포크 수 동안 모델을 학습하는 작업을 수행합니다. d2l.train_ch13() 함수는 실제로 학습을 진행하고 훈련 및 검증 데이터에 대한 손실과 정확도를 반환합니다.

loss 0.277, train acc 0.884, test acc 0.861
2608.4 examples/sec on [device(type='cuda', index=0), device(type='cuda', index=1)]

 

We define the following function to predict the sentiment of a text sequence using the trained model net.

 

훈련된 모델 네트를 사용하여 텍스트 시퀀스의 감정을 예측하기 위해 다음 함수를 정의합니다.

 

#@save
def predict_sentiment(net, vocab, sequence):
    """Predict the sentiment of a text sequence."""
    sequence = torch.tensor(vocab[sequence.split()], device=d2l.try_gpu())
    label = torch.argmax(net(sequence.reshape(1, -1)), dim=1)
    return 'positive' if label == 1 else 'negative'

이 코드는 텍스트 시퀀스의 감정(긍정 또는 부정)을 예측하는 함수를 정의하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 함수 정의:
    • predict_sentiment 함수는 뉴럴 네트워크 모델, 어휘 사전, 텍스트 시퀀스를 입력으로 받아 감정을 예측하는 역할을 합니다.
  2. 텍스트 시퀀스 전처리:
    • vocab[sequence.split()]는 입력된 텍스트 시퀀스를 공백으로 분할하여 어휘 사전의 토큰 인덱스들로 변환합니다.
    • torch.tensor로 변환하고, device=d2l.try_gpu()로 텐서를 GPU로 전송합니다.
  3. 감정 예측:
    • net(sequence.reshape(1, -1))는 모델에 텍스트 시퀀스를 전달하여 예측 결과를 얻습니다.
    • torch.argmax로 가장 높은 예측 확률을 가진 클래스의 인덱스를 선택합니다.
    • label은 예측된 레이블을 나타내는 텐서입니다.
  4. 반환:
    • label == 1인 경우 'positive', 그렇지 않은 경우 'negative'로 감정을 예측합니다.
    • 이 예측 결과를 문자열로 반환합니다.

즉, 이 코드는 뉴럴 네트워크 모델과 어휘 사전을 사용하여 입력된 텍스트 시퀀스의 감정을 예측하고, 결과를 문자열로 반환하는 함수를 정의합니다

 

https://pytorch.org/docs/stable/generated/torch.argmax.html

 

torch.argmax — PyTorch 2.0 documentation

Shortcuts

pytorch.org

http://nozzi-study.tistory.com/34

 

[Pytorch 함수] torch.max( ) & torch.argmax( )

torch.max : 최댓값을 출력 dim=0이면 dim=1이면 뭐를 기준으로 어떻게 더한다...이런게 나는 헷갈려서 터득한 방법이 있다 ! torch.sum과 마찬가지로 인덱스를 이용하여 구하는 방법 예를 들어, 아래와

nozzi-study.tistory.com

http://paintycode.tistory.com/25

 

[파이토치] torch.argmax 함수

torch.argmax torch.argmax(input) → LongTensor torch.argmax(input, dim, keepdim=False) → LongTensor 이 함수는 input tensor에 있는 모든 element들 중에서 가장 큰 값을 가지는 공간의 인덱스 번호를 반환하는 함수이다. a =

paintycode.tistory.com

Finally, let’s use the trained model to predict the sentiment for two simple sentences.

 

마지막으로 훈련된 모델을 사용하여 두 개의 간단한 문장에 대한 감정을 예측해 보겠습니다.

 

predict_sentiment(net, vocab, 'this movie is so great')

이 코드는 뉴럴 네트워크 모델을 사용하여 주어진 텍스트 시퀀스의 감정(긍정 또는 부정)을 예측하는 작업을 수행하는 파이토치 코드입니다. 코드의 작동 방식을 설명하겠습니다.

  1. 함수 호출:
    • predict_sentiment(net, vocab, 'this movie is so great')는 predict_sentiment 함수를 호출합니다.
    • 함수에 뉴럴 네트워크 모델 net, 어휘 사전 vocab, 그리고 예측하고자 하는 텍스트 시퀀스 'this movie is so great'를 입력으로 제공합니다.
  2. 텍스트 시퀀스 전처리:
    • 입력된 텍스트 시퀀스 'this movie is so great'는 함수 내부에서 어휘 사전을 사용하여 토큰 인덱스로 변환됩니다.
  3. 감정 예측:
    • 변환된 토큰 인덱스를 뉴럴 네트워크 모델에 전달하여 감정을 예측합니다.
    • 예측된 결과를 바탕으로 긍정 또는 부정으로 감정을 분류합니다.
  4. 반환:
    • 예측된 감정 결과를 문자열로 반환합니다.

즉, 이 코드는 주어진 텍스트 시퀀스에 대해 뉴럴 네트워크 모델을 사용하여 감정(긍정 또는 부정)을 예측하고, 해당 결과를 출력합니다. 이 경우 'this movie is so great'라는 문장이 어떤 감정을 나타내는지 예측한 결과를 출력합니다.

'positive'

 

predict_sentiment(net, vocab, 'this movie is so bad')
'negative'

 

16.2.4. Summary

  • Pretrained word vectors can represent individual tokens in a text sequence.

    사전 훈련된 단어 벡터는 텍스트 시퀀스의 개별 토큰을 나타낼 수 있습니다.
  • Bidirectional RNNs can represent a text sequence, such as via the concatenation of its hidden states at the initial and final time steps. This single text representation can be transformed into categories using a fully connected layer.

    양방향 RNN은 초기 및 최종 시간 단계에서 숨겨진 상태를 연결하는 등의 방법으로 텍스트 시퀀스를 represent 할  있습니다. 이 단일 텍스트 표현은 완전히 연결된 레이어를 사용하여 카테고리로 변환될 수 있습니다.

 

16.2.5. Exercises

  1. Increase the number of epochs. Can you improve the training and testing accuracies? How about tuning other hyperparameters?
  2. Use larger pretrained word vectors, such as 300-dimensional GloVe embeddings. Does it improve classification accuracy?
  3. Can we improve the classification accuracy by using the spaCy tokenization? You need to install spaCy (pip install spacy) and install the English package (python -m spacy download en). In the code, first, import spaCy (import spacy). Then, load the spaCy English package (spacy_en = spacy.load('en')). Finally, define the function def tokenizer(text): return [tok.text for tok in spacy_en.tokenizer(text)] and replace the original tokenizer function. Note the different forms of phrase tokens in GloVe and spaCy. For example, the phrase token “new york” takes the form of “new-york” in GloVe and the form of “new york” after the spaCy tokenization.
반응형


반응형

16.1. Sentiment Analysis and the Dataset — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

16.1. Sentiment Analysis and the Dataset — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

 

16.1. Sentiment Analysis and the Dataset

 

With the proliferation of online social media and review platforms, a plethora of opinionated data has been logged, bearing great potential for supporting decision making processes. Sentiment analysis studies people’s sentiments in their produced text, such as product reviews, blog comments, and forum discussions. It enjoys wide applications to fields as diverse as politics (e.g., analysis of public sentiments towards policies), finance (e.g., analysis of sentiments of the market), and marketing (e.g., product research and brand management).

 

온라인 소셜 미디어와 리뷰 플랫폼이 확산되면서 수많은 의견이 있는 데이터가 기록되어 의사 결정 프로세스를 지원할 수 있는 큰 잠재력을 갖게 되었습니다. 감정 분석은 제품 리뷰, 블로그 댓글, 포럼 토론 등 생성된 텍스트에서 사람들의 감정을 연구합니다. 이는 정치(예: 정책에 대한 대중 정서 분석), 금융(예: 시장 정서 분석), 마케팅(예: 제품 연구 및 브랜드 관리) 등 다양한 분야에 폭넓게 적용됩니다.

 

Since sentiments can be categorized as discrete polarities or scales (e.g., positive and negative), we can consider sentiment analysis as a text classification task, which transforms a varying-length text sequence into a fixed-length text category. In this chapter, we will use Stanford’s large movie review dataset for sentiment analysis. It consists of a training set and a testing set, either containing 25000 movie reviews downloaded from IMDb. In both datasets, there are equal number of “positive” and “negative” labels, indicating different sentiment polarities.

 

감정은 별개의 극성 또는 척도(예: 긍정적 및 부정적)로 분류될 수 있으므로 감정 분석을 다양한 길이의 텍스트 시퀀스를 고정 길이의 텍스트 범주로 변환하는 텍스트 분류 작업으로 간주할 수 있습니다. 이 장에서는 감정 분석을 위해 Stanford의 대규모 영화 리뷰 데이터 세트를 사용합니다. 이는 IMDb에서 다운로드한 25,000개의 영화 리뷰를 포함하는 훈련 세트와 테스트 세트로 구성됩니다. 두 데이터세트 모두 동일한 수의 "긍정적" 레이블과 "부정적" 레이블이 있어 서로 다른 감정 극성을 나타냅니다.

 

import os
import torch
from torch import nn
from d2l import torch as d2l

 

16.1.1. Reading the Dataset

First, download and extract this IMDb review dataset in the path ../data/aclImdb.

 

먼저 ../data/aclImdb 경로에서 이 IMDb 검토 데이터 세트를 다운로드하고 추출합니다.

 

#@save
d2l.DATA_HUB['aclImdb'] = (d2l.DATA_URL + 'aclImdb_v1.tar.gz',
                          '01ada507287d82875905620988597833ad4e0903')

data_dir = d2l.download_extract('aclImdb', 'aclImdb')

이 코드는 파이썬 프로그램으로, 주로 딥 러닝과 관련된 작업을 수행하는 데 사용됩니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. import 문을 통해 필요한 모듈을 가져옵니다:
    • os: 운영 체제 관련 기능을 사용할 수 있는 모듈입니다.
    • torch: 파이토치 딥 러닝 라이브러리입니다.
    • nn (torch의 하위 모듈인 torch.nn): 신경망 관련 기능을 포함한 모듈입니다.
    • d2l: "Dive into Deep Learning" 책의 예제 및 유틸리티 함수를 담고 있는 사용자 지정 라이브러리입니다. torch as d2l로 불러왔으므로 이후에 d2l을 사용하여 파이토치 관련 작업을 수행할 수 있습니다.
  2. d2l.DATA_HUB에 새 데이터 세트를 추가합니다:
    • d2l.DATA_HUB['aclImdb']에는 데이터 세트의 URL과 해시 값이 튜플로 저장되어 있습니다. 이 정보는 데이터를 다운로드하고 압축을 해제할 때 사용됩니다. 여기서는 IMDB 영화 리뷰 데이터 세트에 대한 정보가 저장되었습니다.
  3. data_dir 변수를 생성합니다:
    • d2l.download_extract() 함수를 사용하여 데이터 세트를 다운로드하고 압축을 해제한 후, 압축 해제된 데이터가 저장될 디렉터리 경로를 data_dir 변수에 저장합니다. 이때 'aclImdb'는 데이터 세트의 이름이며, 두 번째 'aclImdb'는 데이터가 압축 해제될 디렉터리 이름입니다.

즉, 이 코드는 IMDB 영화 리뷰 데이터 세트를 다운로드하고 압축을 해제하여 data_dir 디렉터리에 저장하는 작업을 수행합니다. 이 데이터는 후속 작업에서 자연어 처리나 감정 분석과 같은 NLP 작업에 활용될 수 있습니다.

 

aclImdb 압축 해제 후 볼 수 있는 폴더 구조.

 

 

Next, read the training and test datasets. Each example is a review and its label: 1 for “positive” and 0 for “negative”.

 

다음으로 훈련 및 테스트 데이터 세트를 읽습니다. 각 예는 리뷰이며 해당 라벨은 '긍정적'인 경우 1이고 '부정적인'인 경우 0입니다.

 

#@save
def read_imdb(data_dir, is_train):
    """Read the IMDb review dataset text sequences and labels."""
    data, labels = [], []
    for label in ('pos', 'neg'):
        folder_name = os.path.join(data_dir, 'train' if is_train else 'test',
                                   label)
        for file in os.listdir(folder_name):
            with open(os.path.join(folder_name, file), 'rb') as f:
                review = f.read().decode('utf-8').replace('\n', '')
                data.append(review)
                labels.append(1 if label == 'pos' else 0)
    return data, labels

train_data = read_imdb(data_dir, is_train=True)
print('# trainings:', len(train_data[0]))
for x, y in zip(train_data[0][:3], train_data[1][:3]):
    print('label:', y, 'review:', x[:60])

 

이 코드는 IMDb 영화 리뷰 데이터셋의 텍스트 시퀀스와 레이블을 읽어오는 함수를 정의하고, 이를 활용하여 데이터를 읽고 출력하는 작업을 수행합니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. read_imdb(data_dir, is_train) 함수를 정의합니다:
    • 이 함수는 IMDb 영화 리뷰 데이터셋의 텍스트 시퀀스와 레이블을 읽어옵니다.
    • data_dir은 데이터가 저장된 디렉터리 경로이며, is_train은 훈련 데이터인지 여부를 나타냅니다.
  2. 데이터를 읽어오는 과정:
    • 먼저 긍정('pos')과 부정('neg') 리뷰에 대한 각각의 레이블을 순회합니다.
    • folder_name은 데이터가 저장된 디렉터리 경로를 나타냅니다. is_train 값에 따라 'train' 또는 'test' 디렉터리에서 데이터를 읽습니다.
    • 각 레이블 폴더 내의 파일을 순회하며 파일을 열고 내용을 읽어옵니다. 이 때 파일 내용을 UTF-8로 디코딩하고 개행 문자를 제거하여 리뷰 텍스트를 얻습니다.
    • 읽어온 리뷰와 해당 레이블(긍정인 경우 1, 부정인 경우 0)을 data와 labels 리스트에 추가합니다.
  3. train_data에 데이터를 읽어옵니다:
    • read_imdb() 함수를 사용하여 훈련 데이터를 읽어옵니다. data_dir는 앞서 정의한 데이터 디렉터리 경로이며, is_train=True로 설정하여 훈련 데이터를 읽습니다.
  4. 데이터 출력:
    • train_data의 길이를 출력하여 훈련 데이터의 총 개수를 보여줍니다.
    • 첫 번째 3개의 데이터 샘플에 대해서 레이블과 리뷰 일부를 출력합니다.

이 코드는 IMDb 영화 리뷰 데이터셋을 읽고, 데이터와 레이블을 추출하여 이를 활용할 수 있도록 하는 작업을 수행합니다.

 

# trainings: 25000
label: 1 review: Zentropa has much in common with The Third Man, another noir
label: 1 review: Zentropa is the most original movie I've seen in years. If y
label: 1 review: Lars Von Trier is never backward in trying out new technique

16.1.2. Preprocessing the Dataset

Treating each word as a token and filtering out words that appear less than 5 times, we create a vocabulary out of the training dataset.

 

각 단어를 토큰으로 처리하고 5번 미만으로 나타나는 단어를 필터링하여 훈련 데이터 세트에서 어휘를 생성합니다.

 

train_tokens = d2l.tokenize(train_data[0], token='word')
vocab = d2l.Vocab(train_tokens, min_freq=5, reserved_tokens=['<pad>'])

이 코드는 IMDb 영화 리뷰 텍스트 데이터를 토큰화하고 어휘 사전을 생성하는 작업을 수행합니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. train_data[0]에 있는 영화 리뷰 데이터를 토큰화합니다:
    • d2l.tokenize() 함수를 사용하여 영화 리뷰 텍스트를 토큰화합니다. train_data[0]에는 훈련 데이터의 리뷰 텍스트가 저장되어 있습니다.
    • token='word'는 단어 단위로 텍스트를 토큰화하라는 의미입니다.
  2. 어휘 사전을 생성합니다:
    • d2l.Vocab() 함수를 사용하여 어휘 사전을 생성합니다. 어휘 사전은 텍스트 데이터에서 사용되는 단어들을 숫자로 매핑하여 표현하는데 사용됩니다.
    • train_tokens는 토큰화된 훈련 데이터의 리스트입니다.
    • min_freq=5는 최소 빈도를 나타내며, 이 값보다 적게 등장한 단어는 어휘 사전에 포함되지 않습니다.
    • reserved_tokens=['<pad>']는 특정 단어를 어휘 사전에 예약하고 추가하는 역할을 합니다. 여기서는 <pad>라는 토큰을 예약하여 패딩을 위한 토큰으로 사용하고 있습니다.

즉, 이 코드는 훈련 데이터의 리뷰 텍스트를 단어 단위로 토큰화하고, 이 토큰들을 바탕으로 어휘 사전을 생성하는 작업을 수행합니다. 이렇게 생성된 어휘 사전은 텍스트 데이터를 숫자로 변환하여 모델이 이해하고 처리할 수 있도록 돕습니다.

 

After tokenization, let’s plot the histogram of review lengths in tokens.

 

토큰화 후에 리뷰 길이의 히스토그램을 토큰 단위로 그려보겠습니다.

 

d2l.set_figsize()
d2l.plt.xlabel('# tokens per review')
d2l.plt.ylabel('count')
d2l.plt.hist([len(line) for line in train_tokens], bins=range(0, 1000, 50));

이 코드는 영화 리뷰 데이터의 토큰 개수 분포를 히스토그램으로 시각화하는 작업을 수행합니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. d2l.set_figsize():
    • 그래프의 크기를 설정하는 함수입니다. 시각화 결과의 크기를 조절하는 역할을 합니다.
  2. d2l.plt.xlabel('# tokens per review'):
    • x축에 레이블을 설정하는 함수로, 그래프의 x축에 "리뷰 당 토큰 수"를 나타내는 레이블을 추가합니다.
  3. d2l.plt.ylabel('count'):
    • y축에 레이블을 설정하는 함수로, 그래프의 y축에 "개수"를 나타내는 레이블을 추가합니다.
  4. d2l.plt.hist([len(line) for line in train_tokens], bins=range(0, 1000, 50));:
    • 히스토그램을 그리는 함수로, train_tokens 리스트 내의 각 리뷰의 토큰 개수에 대한 히스토그램을 생성합니다.
    • len(line) for line in train_tokens는 train_tokens 리스트 내 각 리뷰의 토큰 개수를 나타냅니다.
    • bins=range(0, 1000, 50)는 히스토그램의 구간(bin)을 나타내며, 0부터 1000까지 50 단위로 나눈 범위를 사용합니다.

이 코드는 훈련 데이터 내의 각 리뷰에 대한 토큰 개수를 히스토그램으로 시각화하여, 리뷰의 길이 분포를 확인할 수 있도록 돕습니다. 이를 통해 리뷰 텍스트의 길이 특성을 이해하고 모델을 설계하는 데 도움을 줄 수 있습니다.

As we expected, the reviews have varying lengths. To process a minibatch of such reviews at each time, we set the length of each review to 500 with truncation and padding, which is similar to the preprocessing step for the machine translation dataset in Section 10.5.

 

예상한 대로 리뷰의 길이는 다양합니다. 매번 이러한 리뷰의 미니 배치를 처리하기 위해 섹션 10.5의 기계 번역 데이터 세트에 대한 전처리 단계와 유사하게 잘림 및 패딩을 사용하여 각 리뷰의 길이를 500으로 설정했습니다.

 

num_steps = 500  # sequence length
train_features = torch.tensor([d2l.truncate_pad(
    vocab[line], num_steps, vocab['<pad>']) for line in train_tokens])
print(train_features.shape)

이 코드는 토큰화된 영화 리뷰 데이터를 처리하여 시퀀스 길이를 맞추고 패딩을 적용하는 작업을 수행합니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. num_steps = 500:
    • 시퀀스 길이를 나타내는 변수로, 각 리뷰의 토큰 수를 최대 500개로 제한하고자 합니다.
  2. train_features = torch.tensor([...]):
    • 리스트 컴프리헨션을 사용하여 각 리뷰의 토큰을 처리하고 시퀀스 길이를 조절하는 작업을 수행합니다.
    • vocab[line]은 토큰화된 리뷰를 어휘 사전을 이용해 숫자로 변환한 결과입니다.
    • d2l.truncate_pad(...) 함수는 토큰 시퀀스를 주어진 시퀀스 길이로 자르거나 패딩을 적용하여 길이를 맞추는 역할을 합니다.
    • vocab['<pad>']는 패딩 토큰 '<pad>'의 숫자 표현을 나타냅니다.
  3. print(train_features.shape):
    • 변환된 데이터의 크기를 출력합니다. train_features는 토큰 시퀀스를 처리하여 시퀀스 길이를 맞추고 패딩을 적용한 결과로, 이 텐서의 형태를 출력합니다.

즉, 이 코드는 토큰화된 영화 리뷰 데이터를 어휘 사전을 활용하여 숫자 시퀀스로 변환하고, 시퀀스 길이를 맞추기 위해 패딩을 적용한 후, 그 결과의 형태를 출력하는 작업을 수행합니다. 이는 모델 학습을 위해 데이터를 준비하는 단계 중 하나입니다.

torch.Size([25000, 500])

print(train_features[:1])
tensor([[ 9590, 45841, 45793, 35404, 48194, 17311, 33838, 20000, 45544, 35301,
         35404, 23749, 18774, 29385, 32497, 23653, 28550, 15892, 32497, 30257,
         32554, 22945, 37421, 36449, 45586, 38202, 32497, 25457, 29744, 32207,
         48797, 49280, 47063, 36449, 45586, 35433, 15815, 19877, 41287,  9681,
         49252, 36145, 36705, 46106, 35259, 33408, 38345, 31265, 45650, 45793,
         38920, 32477, 45586, 35404, 28546, 49258,  9590, 45841, 18139, 45586,
         17647, 31580, 45586, 35404, 48482, 37015,  7401, 21639, 30257, 45610,
         28999, 35143, 18388, 45586, 19192, 19076, 45586, 16121,  8000,  7965,
         48194, 33163, 45932, 45586, 48616, 45743,  9590, 45841, 45586, 41374,
         48194, 17311, 45119, 37028, 45586, 46215, 31580, 36633, 37402, 18785,
         20982, 45564, 32165, 25523, 10115,  6597, 32452, 40859, 31580, 45556,
         18254, 32511, 42133, 31580, 45586, 29038, 32497, 39855, 34135, 49242,
         41831, 45586, 38736,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,
          3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680,  3680]])

 

 

16.1.3. Creating Data Iterators

 

Now we can create data iterators. At each iteration, a minibatch of examples are returned.

이제 데이터 반복자를 만들 수 있습니다. 각 반복마다 예제의 미니배치가 반환됩니다.
train_iter = d2l.load_array((train_features, torch.tensor(train_data[1])), 64)

for X, y in train_iter:
    print('X:', X.shape, ', y:', y.shape)
    break
print('# batches:', len(train_iter))

이 코드는 데이터를 미니배치로 나누어 반복적으로 사용할 수 있도록 데이터 로더를 생성하는 작업을 수행합니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. train_iter = d2l.load_array((train_features, torch.tensor(train_data[1])), 64):
    • d2l.load_array(...) 함수를 사용하여 데이터를 로딩하고 미니배치로 나누는 데이터 로더를 생성합니다.
    • (train_features, torch.tensor(train_data[1]))는 입력 데이터와 해당 데이터의 레이블을 나타내는 튜플입니다.
    • 64는 미니배치 크기를 나타내며, 데이터를 64개의 샘플로 나누어 미니배치를 생성합니다.
  2. for X, y in train_iter::
    • train_iter를 반복하여 미니배치 데이터를 순회합니다.
    • 각 미니배치에서 X는 입력 데이터, y는 해당 데이터의 레이블을 나타냅니다.
  3. print('X:', X.shape, ', y:', y.shape):
    • 각 미니배치의 입력 데이터 X와 레이블 y의 크기를 출력합니다.
    • X.shape는 X의 형태(shape)를 나타내며, y.shape는 y의 형태를 나타냅니다.
  4. break:
    • 첫 번째 미니배치만 출력한 후 반복문을 종료합니다.
  5. print('# batches:', len(train_iter)):
    • 생성된 미니배치의 총 개수를 출력합니다.
    • len(train_iter)는 train_iter의 미니배치 개수를 나타냅니다.

즉, 이 코드는 데이터를 미니배치로 나누어주는 데이터 로더를 생성하고, 첫 번째 미니배치의 입력 데이터와 레이블을 출력하며, 생성된 미니배치의 총 개수를 출력하는 작업을 수행합니다. 이는 모델 학습 시 데이터를 효율적으로 처리하기 위한 단계 중 하나입니다.

X: torch.Size([64, 500]) , y: torch.Size([64])
# batches: 391

16.1.4. Putting It All Together

 

Last, we wrap up the above steps into the load_data_imdb function. It returns training and test data iterators and the vocabulary of the IMDb review dataset.

 

마지막으로 위의 단계를 load_data_imdb 함수로 마무리합니다. 훈련 및 테스트 데이터 반복자와 IMDb 검토 데이터 세트의 어휘를 반환합니다.

 

#@save
def load_data_imdb(batch_size, num_steps=500):
    """Return data iterators and the vocabulary of the IMDb review dataset."""
    data_dir = d2l.download_extract('aclImdb', 'aclImdb')
    train_data = read_imdb(data_dir, True)
    test_data = read_imdb(data_dir, False)
    train_tokens = d2l.tokenize(train_data[0], token='word')
    test_tokens = d2l.tokenize(test_data[0], token='word')
    vocab = d2l.Vocab(train_tokens, min_freq=5)
    train_features = torch.tensor([d2l.truncate_pad(
        vocab[line], num_steps, vocab['<pad>']) for line in train_tokens])
    test_features = torch.tensor([d2l.truncate_pad(
        vocab[line], num_steps, vocab['<pad>']) for line in test_tokens])
    train_iter = d2l.load_array((train_features, torch.tensor(train_data[1])),
                                batch_size)
    test_iter = d2l.load_array((test_features, torch.tensor(test_data[1])),
                               batch_size,
                               is_train=False)
    return train_iter, test_iter, vocab

이 코드는 IMDb 영화 리뷰 데이터셋을 처리하고 데이터 이터레이터와 어휘 사전을 생성하여 반환하는 함수를 정의합니다. 코드의 작동 방식을 단계별로 설명하겠습니다.

  1. 함수 시그니처 설명:
    • load_data_imdb(batch_size, num_steps=500): IMDb 리뷰 데이터셋의 데이터 이터레이터와 어휘 사전을 반환하는 함수입니다.
    • batch_size: 미니배치 크기를 나타내는 인자입니다.
    • num_steps: 시퀀스 길이를 나타내는 인자로, 기본값은 500입니다.
  2. 데이터 로딩 및 전처리:
    • 데이터를 다운로드하고 압축을 해제하여 데이터 디렉터리를 얻습니다.
    • read_imdb() 함수를 사용하여 훈련 데이터와 테스트 데이터를 읽어옵니다.
    • 각 데이터의 텍스트를 토큰화하여 토큰 시퀀스로 변환합니다.
  3. 어휘 사전 생성:
    • 훈련 데이터의 토큰을 이용하여 어휘 사전 vocab을 생성합니다. 단어의 최소 빈도는 min_freq=5로 설정됩니다.
  4. 시퀀스 길이 조절 및 패딩 적용:
    • 훈련 데이터와 테스트 데이터의 토큰 시퀀스를 시퀀스 길이로 자르거나 패딩을 적용하여 길이를 맞춥니다.
  5. 데이터 이터레이터 생성:
    • 훈련 데이터와 해당 레이블을 이용하여 훈련 데이터 이터레이터 train_iter를 생성합니다.
    • 테스트 데이터와 해당 레이블을 이용하여 테스트 데이터 이터레이터 test_iter를 생성합니다.
    • is_train=False로 설정하여 테스트 데이터를 로딩하는 것을 나타냅니다.
  6. 반환:
    • 생성된 훈련 데이터 이터레이터, 테스트 데이터 이터레이터, 그리고 어휘 사전을 반환합니다.

이 코드는 IMDb 리뷰 데이터셋을 처리하고 데이터 이터레이터를 생성하여 모델 학습에 활용할 수 있도록 하는 작업을 수행합니다.

 

16.1.5. Summary

  • Sentiment analysis studies people’s sentiments in their produced text, which is considered as a text classification problem that transforms a varying-length text sequence into a fixed-length text category.
  • 감정 분석은 생산된 텍스트에서 사람들의 감정을 연구하는데, 이는 다양한 길이의 텍스트 시퀀스를 고정 길이의 텍스트 범주로 변환하는 텍스트 분류 문제로 간주됩니다.
  • After preprocessing, we can load Stanford’s large movie review dataset (IMDb review dataset) into data iterators with a vocabulary.
  • 전처리 후에 Stanford의 대규모 영화 리뷰 데이터 세트(IMDb 리뷰 데이터 세트)를 어휘가 있는 데이터 반복기에 로드할 수 있습니다.

 

16.1.6. Exercises

  1. What hyperparameters in this section can we modify to accelerate training sentiment analysis models?
  2. Can you implement a function to load the dataset of Amazon reviews into data iterators and labels for sentiment analysis?

 

 

 

반응형


반응형

16. Natural Language Processing: Applications — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

16. Natural Language Processing: Applications — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

 

16. Natural Language Processing: Applications

We have seen how to represent tokens in text sequences and train their representations in Section 15. Such pretrained text representations can be fed to various models for different downstream natural language processing tasks.

 

우리는 섹션 15에서 텍스트 시퀀스에서 토큰을 표현하고 그 표현을 훈련하는 방법을 살펴보았습니다. 이러한 사전 훈련된 텍스트 표현은 다양한 다운스트림 자연어 처리 작업을 위한 다양한 모델에 공급될 수 있습니다.

 

In fact, earlier chapters have already discussed some natural language processing applications without pretraining, just for explaining deep learning architectures. For instance, in Section 9, we have relied on RNNs to design language models to generate novella-like text. In Section 10 and Section 11, we have also designed models based on RNNs and attention mechanisms for machine translation.

 

실제로 이전 장에서는 단지 딥러닝 아키텍처를 설명하기 위해 사전 훈련 없이 일부 자연어 처리 응용 프로그램을 이미 논의했습니다. 예를 들어 섹션 9에서는 RNN을 사용하여 소설 같은 텍스트를 생성하는 언어 모델을 설계했습니다. 섹션 10과 섹션 11에서는 RNN과 기계 번역을 위한 attention 메커니즘을 기반으로 모델을 설계했습니다.

 

However, this book does not intend to cover all such applications in a comprehensive manner. Instead, our focus is on how to apply (deep) representation learning of languages to addressing natural language processing problems. Given pretrained text representations, this chapter will explore two popular and representative downstream natural language processing tasks: sentiment analysis and natural language inference, which analyze single text and relationships of text pairs, respectively.

 

그러나 이 책에서는 그러한 모든 응용 프로그램을 포괄적으로 다루지는 않습니다. 대신, 우리는 자연어 처리 문제를 해결하기 위해 언어의 (심층) 표현 학습 representation learning 을 적용하는 방법에 중점을 둡니다. 미리 훈련된 텍스트 표현이 주어지면 이 장에서는 인기 있고 대표적인 두 가지 다운스트림 자연어 처리 작업, 즉 단일 텍스트와 텍스트 쌍의 관계를 각각 분석하는 감정 분석과 자연어 추론을 살펴봅니다.

 

Fig. 16.1&nbsp; Pretrained text representations can be fed to various deep learning architectures for different downstream natural language processing applications. This chapter focuses on how to design models for different downstream natural language processing applications.&nbsp;그림 16.1 사전 훈련된 텍스트 표현은 다양한 다운스트림 자연어 처리 애플리케이션을 위한 다양한 딥러닝 아키텍처에 공급될 수 있습니다. 이 장에서는 다양한 다운스트림 자연어 처리 애플리케이션을 위한 모델을 설계하는 방법에 중점을 둡니다.

 

As depicted in Fig. 16.1, this chapter focuses on describing the basic ideas of designing natural language processing models using different types of deep learning architectures, such as MLPs, CNNs, RNNs, and attention. Though it is possible to combine any pretrained text representations with any architecture for either application in Fig. 16.1, we select a few representative combinations. Specifically, we will explore popular architectures based on RNNs and CNNs for sentiment analysis. For natural language inference, we choose attention and MLPs to demonstrate how to analyze text pairs. In the end, we introduce how to fine-tune a pretrained BERT model for a wide range of natural language processing applications, such as on a sequence level (single text classification and text pair classification) and a token level (text tagging and question answering). As a concrete empirical case, we will fine-tune BERT for natural language inference.

 

그림 16.1에 설명된 것처럼 이 장에서는 MLP, CNN, RNN 및 Attention과 같은 다양한 유형의 딥러닝 아키텍처를 사용하여 자연어 처리 모델을 설계하는 기본 아이디어를 설명하는 데 중점을 둡니다. 그림 16.1의 각 응용 프로그램에 대해 사전 훈련된 텍스트 표현을 모든 아키텍처와 결합하는 것이 가능하지만 몇 가지 대표적인 조합을 선택합니다. 특히, 감정 분석을 위해 RNN 및 CNN을 기반으로 하는 인기 있는 아키텍처를 살펴보겠습니다. 자연어 추론의 경우 어텐션과 MLP를 선택하여 텍스트 쌍을 분석하는 방법을 보여줍니다. 마지막에는 시퀀스 수준(단일 텍스트 분류 및 텍스트 쌍 분류) 및 토큰 수준(텍스트 태깅 및 질문 답변)과 같은 광범위한 자연어 처리 애플리케이션에 대해 사전 훈련된 BERT 모델을 미세 조정하는 방법을 소개합니다. ). 구체적인 실증 사례로 자연어 추론을 위해 BERT를 미세 조정해 보겠습니다.

 

As we have introduced in Section 15.8, BERT requires minimal architecture changes for a wide range of natural language processing applications. However, this benefit comes at the cost of fine-tuning a huge number of BERT parameters for the downstream applications. When space or time is limited, those crafted models based on MLPs, CNNs, RNNs, and attention are more feasible. In the following, we start by the sentiment analysis application and illustrate the model design based on RNNs and CNNs, respectively.

 

섹션 15.8에서 소개한 것처럼 BERT는 광범위한 자연어 처리 애플리케이션에 대해 최소한의 아키텍처 변경이 필요합니다. 그러나 이러한 이점은 다운스트림 애플리케이션에 대해 수많은 BERT 매개변수를 미세 조정하는 비용으로 발생합니다. 공간이나 시간이 제한되어 있는 경우 MLP, CNN, RNN 및 Attention을 기반으로 제작된 모델이 더 실현 가능합니다. 다음에서는 감정 분석 애플리케이션으로 시작하여 각각 RNN과 CNN을 기반으로 한 모델 설계를 설명합니다.

 

 

 

 

 

 

 

 

반응형


반응형

15.10. Pretraining BERT — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

15.10. Pretraining BERT — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

 

15.10. Pretraining BERT

 

With the BERT model implemented in Section 15.8 and the pretraining examples generated from the WikiText-2 dataset in Section 15.9, we will pretrain BERT on the WikiText-2 dataset in this section.

 

섹션 15.8에서 구현된 BERT 모델과 섹션 15.9의 WikiText-2 데이터 세트에서 생성된 사전 학습 예제를 사용하여 이 섹션에서는 WikiText-2 데이터 세트에 대해 BERT를 사전 학습할 것입니다.

 

import torch
from torch import nn
from d2l import torch as d2l

 

To start, we load the WikiText-2 dataset as minibatches of pretraining examples for masked language modeling and next sentence prediction. The batch size is 512 and the maximum length of a BERT input sequence is 64. Note that in the original BERT model, the maximum length is 512.

 

시작하려면 WikiText-2 데이터 세트를 마스크된 언어 모델링 및 다음 문장 예측을 위한 사전 학습 예제의 미니 배치로 로드합니다. 배치 크기는 512이고 BERT 입력 시퀀스의 최대 길이는 64입니다. 원래 BERT 모델에서 최대 길이는 512입니다.

 

batch_size, max_len = 512, 64
train_iter, vocab = d2l.load_data_wiki(batch_size, max_len)

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. batch_size와 max_len 설정: 미니 배치 크기와 최대 시퀀스 길이를 설정합니다.
  2. 데이터 로드 및 데이터로더 생성: d2l.load_data_wiki 함수를 호출하여 WikiText-2 데이터셋을 로드하고, 데이터로더를 생성합니다.

이렇게 생성된 train_iter는 미니 배치 데이터를 반복적으로 제공하며, vocab은 데이터셋에 등장하는 단어들의 어휘 사전을 나타냅니다. 이렇게 생성된 데이터로더와 어휘 사전은 BERT 모델 학습을 위해 사용될 수 있습니다.

15.10.1. Pretraining BERT

The original BERT has two versions of different model sizes (Devlin et al., 2018). The base model (BERTBASE) uses 12 layers (Transformer encoder blocks) with 768 hidden units (hidden size) and 12 self-attention heads. The large model (BERTLARGE) uses 24 layers with 1024 hidden units and 16 self-attention heads. Notably, the former has 110 million parameters while the latter has 340 million parameters. For demonstration with ease, we define a small BERT, using 2 layers, 128 hidden units, and 2 self-attention heads.

 

원래 BERT에는 서로 다른 모델 크기의 두 가지 버전이 있습니다(Devlin et al., 2018). 기본 모델(BERTBASE)은 768개의 숨겨진 유닛(숨겨진 크기)과 12개의 self-attention 헤드가 있는 12개의 레이어(변환기 인코더 블록)를 사용합니다. 대형 모델(BERTLARGE)은 1024개의 숨겨진 유닛과 16개의 self-attention 헤드가 포함된 24개의 레이어를 사용합니다. 특히 전자에는 1억 1천만 개의 매개변수가 있고 후자에는 3억 4천만 개의 매개변수가 있습니다. 쉽게 시연하기 위해 2개의 레이어, 128개의 숨겨진 유닛, 2개의 self-attention 헤드를 사용하여 작은 BERT를 정의합니다.

 

net = d2l.BERTModel(len(vocab), num_hiddens=128,
                    ffn_num_hiddens=256, num_heads=2, num_blks=2, dropout=0.2)
devices = d2l.try_all_gpus()
loss = nn.CrossEntropyLoss()

위의 코드에서 이루어지는 작업은 다음과 같습니다:

BERT 모델 생성: d2l.BERTModel 클래스를 이용하여 BERT 모델을 생성합니다. 인자로는 어휘 사전 크기 len(vocab)와 모델의 히든 차원 수 num_hiddens, 피드포워드 신경망의 히든 차원 수 ffn_num_hiddens, 어텐션 헤드 수 num_heads, 블록 수 num_blks, 드롭아웃 비율 dropout 등이 설정됩니다.

 

디바이스 할당: d2l.try_all_gpus() 함수를 호출하여 가능한 GPU 장치들 중에서 사용 가능한 장치들의 리스트를 가져옵니다. 이렇게 얻은 장치 리스트는 모델 학습을 GPU에서 병렬적으로 처리하기 위해 사용될 수 있습니다.

 

손실 함수 설정: 분류 작업을 위한 크로스 엔트로피 손실 함수 nn.CrossEntropyLoss()를 생성합니다. 이 함수는 모델의 출력과 실제 타겟 간의 손실을 계산합니다.

 

이렇게 생성된 BERT 모델과 관련된 요소들은 이후 학습과 평가를 수행하는 단계에서 사용됩니다.

 

Before defining the training loop, we define a helper function _get_batch_loss_bert. Given the shard of training examples, this function computes the loss for both the masked language modeling and next sentence prediction tasks. Note that the final loss of BERT pretraining is just the sum of both the masked language modeling loss and the next sentence prediction loss.

 

훈련 루프를 정의하기 전에 도우미 함수 _get_batch_loss_bert를 정의합니다. 훈련 예제의 조각이 주어지면 이 함수는 마스크된 언어 모델링과 다음 문장 예측 작업 모두에 대한 손실을 계산합니다. BERT 사전 훈련의 최종 손실은 마스크된 언어 모델링 손실과 다음 문장 예측 손실의 합일 뿐입니다.

 

#@save
def _get_batch_loss_bert(net, loss, vocab_size, tokens_X,
                         segments_X, valid_lens_x,
                         pred_positions_X, mlm_weights_X,
                         mlm_Y, nsp_y):
    # Forward pass
    _, mlm_Y_hat, nsp_Y_hat = net(tokens_X, segments_X,
                                  valid_lens_x.reshape(-1),
                                  pred_positions_X)
    # Compute masked language model loss
    mlm_l = loss(mlm_Y_hat.reshape(-1, vocab_size), mlm_Y.reshape(-1)) *\
    mlm_weights_X.reshape(-1, 1)
    mlm_l = mlm_l.sum() / (mlm_weights_X.sum() + 1e-8)
    # Compute next sentence prediction loss
    nsp_l = loss(nsp_Y_hat, nsp_y)
    l = mlm_l + nsp_l
    return mlm_l, nsp_l, l

위의 함수는 아래 작업을 수행합니다:

  1. Forward Pass: 입력 데이터인 tokens_X, segments_X, valid_lens_x, pred_positions_X를 BERT 모델 net에 전달하여 순전파를 수행합니다. 이 과정에서 얻어진 출력은 mlm_Y_hat (마스킹된 언어 모델의 예측)와 nsp_Y_hat (다음 문장 예측의 예측)입니다.
  2. 마스킹된 언어 모델 손실 계산: mlm_Y_hat와 실제 레이블 mlm_Y 간의 손실을 계산합니다. 이 손실은 마스킹된 토큰들에 대해서만 계산되며, 마스킹된 토큰의 가중치 mlm_weights_X를 고려하여 계산된 다음, 이를 마스킹된 토큰의 총 가중치 합으로 나누어 정규화합니다.
  3. 다음 문장 예측 손실 계산: nsp_Y_hat와 실제 레이블 nsp_y 간의 손실을 계산합니다.
  4. 총 손실 계산: 마스킹된 언어 모델 손실과 다음 문장 예측 손실을 더한 총 손실을 계산합니다.

즉, 이 함수는 하나의 미니배치 데이터에 대해 BERT 모델의 마스킹된 언어 모델 손실과 다음 문장 예측 손실을 계산하고, 이들 손실을 합산한 총 손실을 반환합니다. 이 손실 값은 학습 단계에서 최적화(optimizer)를 위해 사용됩니다

 

Invoking the two aforementioned helper functions, the following train_bert function defines the procedure to pretrain BERT (net) on the WikiText-2 (train_iter) dataset. Training BERT can take very long. Instead of specifying the number of epochs for training as in the train_ch13 function (see Section 14.1), the input num_steps of the following function specifies the number of iteration steps for training.

 

앞서 언급한 두 도우미 함수를 호출하는 다음 train_bert 함수는 WikiText-2(train_iter) 데이터 세트에서 BERT(net)를 사전 훈련하는 절차를 정의합니다. BERT 훈련은 매우 오랜 시간이 걸릴 수 있습니다. train_ch13 함수(14.1절 참조)에서처럼 훈련을 위한 에포크 수를 지정하는 대신, 다음 함수의 입력 num_steps는 훈련을 위한 반복 단계 수를 지정합니다.

 

def train_bert(train_iter, net, loss, vocab_size, devices, num_steps):
    net(*next(iter(train_iter))[:4])
    net = nn.DataParallel(net, device_ids=devices).to(devices[0])
    trainer = torch.optim.Adam(net.parameters(), lr=0.01)
    step, timer = 0, d2l.Timer()
    animator = d2l.Animator(xlabel='step', ylabel='loss',
                            xlim=[1, num_steps], legend=['mlm', 'nsp'])
    # Sum of masked language modeling losses, sum of next sentence prediction
    # losses, no. of sentence pairs, count
    metric = d2l.Accumulator(4)
    num_steps_reached = False
    while step < num_steps and not num_steps_reached:
        for tokens_X, segments_X, valid_lens_x, pred_positions_X,\
            mlm_weights_X, mlm_Y, nsp_y in train_iter:
            tokens_X = tokens_X.to(devices[0])
            segments_X = segments_X.to(devices[0])
            valid_lens_x = valid_lens_x.to(devices[0])
            pred_positions_X = pred_positions_X.to(devices[0])
            mlm_weights_X = mlm_weights_X.to(devices[0])
            mlm_Y, nsp_y = mlm_Y.to(devices[0]), nsp_y.to(devices[0])
            trainer.zero_grad()
            timer.start()
            mlm_l, nsp_l, l = _get_batch_loss_bert(
                net, loss, vocab_size, tokens_X, segments_X, valid_lens_x,
                pred_positions_X, mlm_weights_X, mlm_Y, nsp_y)
            l.backward()
            trainer.step()
            metric.add(mlm_l, nsp_l, tokens_X.shape[0], 1)
            timer.stop()
            animator.add(step + 1,
                         (metric[0] / metric[3], metric[1] / metric[3]))
            step += 1
            if step == num_steps:
                num_steps_reached = True
                break

    print(f'MLM loss {metric[0] / metric[3]:.3f}, '
          f'NSP loss {metric[1] / metric[3]:.3f}')
    print(f'{metric[2] / timer.sum():.1f} sentence pairs/sec on '
          f'{str(devices)}')

 

위의 함수는 아래 작업을 수행합니다:

  1. 모델 초기화: 먼저 net에 한 미니배치 데이터를 전달하여 모델을 초기화합니다. 이는 BERT 모델을 GPU에 병렬로 배치하기 위한 사전작업입니다.
  2. 모델 병렬화 및 옵티마이저 설정: 모델을 병렬화하여 다중 GPU에서 학습할 수 있도록 준비하고, Adam 옵티마이저를 설정합니다.
  3. 학습 루프: 학습 루프는 주어진 num_steps만큼 반복하여 BERT 모델을 학습합니다. 미니배치 데이터를 하나씩 가져와 GPU로 이동시킨 후, _get_batch_loss_bert 함수를 호출하여 마스킹된 언어 모델 손실과 다음 문장 예측 손실을 계산합니다. 그런 다음 손실을 역전파하고 옵티마이저를 통해 모델의 파라미터를 업데이트합니다.
  4. 손실 및 성능 측정: _get_batch_loss_bert 함수에서 계산한 마스킹된 언어 모델 손실과 다음 문장 예측 손실을 누적하여 기록하고, 성능 지표를 계산합니다. 이후 학습의 진행 상황을 애니메이션으로 표시합니다.
  5. 학습 결과 출력: 학습이 완료되면 마스킹된 언어 모델 손실과 다음 문장 예측 손실을 출력하며, 학습 속도를 표시합니다.

즉, 이 함수는 BERT 모델을 주어진 데이터로 학습시키는 데 사용되며, 모델의 학습 손실 및 성능 지표를 출력합니다.

 

모델 초기화와 병렬화

net(*next(iter(train_iter))[:4])
net = nn.DataParallel(net, device_ids=devices).to(devices[0])

 

첫 번째 줄에서는 미니배치 데이터의 첫 번째 요소를 가져와 모델에 전달하여 초기화합니다. 이는 모델을 GPU에 병렬로 배치하기 위해 필요한 작업입니다.

 

두 번째 줄에서는 nn.DataParallel을 사용하여 모델을 다중 GPU로 병렬화합니다. device_ids 매개변수를 통해 사용할 GPU를 선택하고, .to(devices[0])를 사용하여 모델을 첫 번째 GPU로 이동시킵니다. 이렇게 함으로써 모델을 다중 GPU에서 병렬로 학습할 수 있도록 준비합니다.

 

옵티마이저 설정

trainer = torch.optim.Adam(net.parameters(), lr=0.01)

Adam 옵티마이저를 설정합니다. net.parameters()를 통해 모델의 파라미터들을 가져와 옵티마이저에 전달하고, lr 매개변수를 통해 학습률을 설정합니다.

 

학습 루프

for tokens_X, segments_X, valid_lens_x, pred_positions_X,\
    mlm_weights_X, mlm_Y, nsp_y in train_iter:
    # ...

학습 루프에서는 주어진 train_iter 데이터로 BERT 모델을 학습합니다. 데이터를 하나씩 가져와서 다음 작업을 수행합니다:

  1. 데이터를 GPU로 이동시킵니다.
  2. 옵티마이저의 그래디언트를 초기화합니다.
  3. _get_batch_loss_bert 함수를 호출하여 마스킹된 언어 모델 손실과 다음 문장 예측 손실을 계산합니다.
  4. 역전파를 수행하고 옵티마이저를 통해 모델 파라미터를 업데이트합니다.
  5. 손실과 성능 지표를 기록합니다.

손실 및 성능 측정

mlm_l, nsp_l, l = _get_batch_loss_bert(
    net, loss, vocab_size, tokens_X, segments_X, valid_lens_x,
    pred_positions_X, mlm_weights_X, mlm_Y, nsp_y)
metric.add(mlm_l, nsp_l, tokens_X.shape[0], 1)

_get_batch_loss_bert 함수를 통해 계산된 마스킹된 언어 모델 손실과 다음 문장 예측 손실을 기록하고, 해당 미니배치의 문장 쌍 수와 학습한 미니배치 수를 누적합니다. 이 정보는 학습 과정에서 손실과 성능 지표를 계산하고 모니터링하는 데 사용됩니다.

 

print(f'MLM loss {metric[0] / metric[3]:.3f}, '
      f'NSP loss {metric[1] / metric[3]:.3f}')
print(f'{metric[2] / timer.sum():.1f} sentence pairs/sec on '
      f'{str(devices)}')

 

학습이 완료되면 마스킹된 언어 모델 손실과 다음 문장 예측 손실을 출력하며, 학습 속도를 표시합니다. metric[0]은 마스킹된 언어 모델 손실의 누적값, metric[1]은 다음 문장 예측 손실의 누적값, metric[2]는 학습한 문장 쌍의 수, `metric[3]

 

 

We can plot both the masked language modeling loss and the next sentence prediction loss during BERT pretraining.

 

BERT 사전 훈련 중 마스크된 언어 모델링 손실과 다음 문장 예측 손실을 모두 플롯할 수 있습니다.

 

train_bert(train_iter, net, loss, len(vocab), devices, 50)

위의 출력은 학습 결과를 보여줍니다.

  • MLM loss 5.885: 마스킹된 언어 모델 손실의 평균 값입니다.
  • NSP loss 0.760: 다음 문장 예측 손실의 평균 값입니다.
  • 4413.2 sentence pairs/sec: 초당 처리된 문장 쌍의 수를 나타냅니다.
  • on [device(type='cuda', index=0), device(type='cuda', index=1)]: 학습에 사용된 GPU 장치입니다. [device(type='cuda', index=0), device(type='cuda', index=1)]은 두 개의 GPU를 사용한다는 것을 나타냅니다.

이 출력을 통해 BERT 모델의 학습 과정과 손실 값, 학습 속도 등을 파악할 수 있습니다.

MLM loss 5.885, NSP loss 0.760
4413.2 sentence pairs/sec on [device(type='cuda', index=0), device(type='cuda', index=1)]

 

15.10.2. Representing Text with BERT

 

After pretraining BERT, we can use it to represent single text, text pairs, or any token in them. The following function returns the BERT (net) representations for all tokens in tokens_a and tokens_b.

 

BERT를 사전 훈련한 후 이를 사용하여 단일 텍스트, 텍스트 쌍 또는 그 안에 있는 모든 토큰을 나타낼 수 있습니다. 다음 함수는 tokens_a 및 tokens_b의 모든 토큰에 대한 BERT(net) 표현을 반환합니다.

 

def get_bert_encoding(net, tokens_a, tokens_b=None):
    tokens, segments = d2l.get_tokens_and_segments(tokens_a, tokens_b)
    token_ids = torch.tensor(vocab[tokens], device=devices[0]).unsqueeze(0)
    segments = torch.tensor(segments, device=devices[0]).unsqueeze(0)
    valid_len = torch.tensor(len(tokens), device=devices[0]).unsqueeze(0)
    encoded_X, _, _ = net(token_ids, segments, valid_len)
    return encoded_X

위의 코드는 주어진 입력 문장 또는 문장 쌍을 BERT 모델을 통해 인코딩하는 함수를 정의합니다. 각 매개변수의 역할을 살펴보겠습니다.

  • net: BERT 모델 인스턴스입니다.
  • tokens_a: 첫 번째 문장에 해당하는 토큰 리스트입니다.
  • tokens_b: 두 번째 문장에 해당하는 토큰 리스트입니다. (옵션)

이 함수는 주어진 문장 또는 문장 쌍을 BERT 모델을 통해 인코딩하여 반환합니다. 구체적으로 다음 단계를 따릅니다.

  1. d2l.get_tokens_and_segments(tokens_a, tokens_b) 함수를 사용하여 입력 토큰과 세그먼트 정보를 가져옵니다.
  2. vocab[tokens]를 사용하여 토큰들을 정수 ID로 변환하고, 그 결과를 torch.tensor로 변환하고 unsqueeze(0)으로 차원을 추가하여 텐서를 생성합니다. 이 텐서는 BERT 모델에 입력됩니다.
  3. token_ids와 세그먼트 정보, 그리고 문장 길이 정보(valid_len)를 사용하여 BERT 모델에 인코딩을 요청합니다.

 

Consider the sentence “a crane is flying”. Recall the input representation of BERT as discussed in Section 15.8.4. After inserting special tokens “<cls>” (used for classification) and “<sep>” (used for separation), the BERT input sequence has a length of six. Since zero is the index of the “<cls>” token, encoded_text[:, 0, :] is the BERT representation of the entire input sentence. To evaluate the polysemy token “crane”, we also print out the first three elements of the BERT representation of the token.

 

a crane is flying”라는 문장을 생각해 보세요. 섹션 15.8.4에서 논의된 BERT의 입력 표현을 상기해보세요. 특수 토큰 “<cls>”(분류에 사용) 및 “<sep>”(분리에 사용)을 삽입한 후 BERT 입력 시퀀스의 길이는 6입니다. 0은 "<cls>" 토큰의 인덱스이므로 Encoded_text[:, 0, :]는 전체 입력 문장의 BERT 표현입니다. 다의어 토큰 "crane"을 평가하기 위해 토큰의 BERT 표현의 처음 세 요소도 인쇄합니다.

 

tokens_a = ['a', 'crane', 'is', 'flying']
encoded_text = get_bert_encoding(net, tokens_a)
# Tokens: '<cls>', 'a', 'crane', 'is', 'flying', '<sep>'
encoded_text_cls = encoded_text[:, 0, :]
encoded_text_crane = encoded_text[:, 2, :]
encoded_text.shape, encoded_text_cls.shape, encoded_text_crane[0][:3]

위의 코드는 BERT 모델을 사용하여 입력 토큰 시퀀스를 인코딩한 결과를 추출하는 과정을 보여줍니다. 코드의 각 부분을 살펴보겠습니다.

  • tokens_a: 인코딩하려는 입력 토큰 시퀀스입니다.
  • encoded_text: get_bert_encoding 함수를 사용하여 입력 토큰 시퀀스를 BERT 모델을 통해 인코딩한 결과입니다.

위의 코드는 다음과 같은 작업을 수행합니다:

  1. get_bert_encoding 함수를 호출하여 입력 토큰 시퀀스 tokens_a를 BERT 모델을 통해 인코딩한 결과를 encoded_text로 받습니다.
  2. encoded_text는 3차원 텐서입니다. 첫 번째 차원은 배치 차원, 두 번째 차원은 토큰 시퀀스의 위치 (토큰의 위치), 세 번째 차원은 BERT 모델의 히든 상태의 차원입니다.
  3. encoded_text_cls는 인코딩된 텍스트의 첫 번째 토큰인 <cls>의 히든 상태를 추출합니다.
  4. encoded_text_crane은 인코딩된 텍스트에서 "crane" 토큰의 히든 상태를 추출합니다.
  5. 마지막으로, encoded_text의 모양, encoded_text_cls의 모양, 그리고 encoded_text_crane의 처음 세 원소를 출력합니다.

이 코드를 실행하면 입력 토큰 시퀀스를 BERT 모델을 통해 인코딩한 결과와 해당 결과의 특정 위치에서 추출한 히든 상태를 확인할 수 있습니다

(torch.Size([1, 6, 128]),
 torch.Size([1, 128]),
 tensor([0.8414, 1.4830, 0.8226], device='cuda:0', grad_fn=<SliceBackward0>))

 

Now consider a sentence pair “a crane driver came” and “he just left”. Similarly, encoded_pair[:, 0, :] is the encoded result of the entire sentence pair from the pretrained BERT. Note that the first three elements of the polysemy token “crane” are different from those when the context is different. This supports that BERT representations are context-sensitive.

 

이제 "a crane driver came"와 "he just left"라는 문장 쌍을 생각해 보세요. 마찬가지로, Encoded_pair[:, 0, :]는 사전 훈련된 BERT의 전체 문장 쌍의 인코딩된 결과입니다. 다의어 토큰 "크레인"의 처음 세 요소는 문맥이 다를 때의 요소와 다릅니다. 이는 BERT 표현이 상황에 따라 달라지는 것을 지원합니다.

 

tokens_a, tokens_b = ['a', 'crane', 'driver', 'came'], ['he', 'just', 'left']
encoded_pair = get_bert_encoding(net, tokens_a, tokens_b)
# Tokens: '<cls>', 'a', 'crane', 'driver', 'came', '<sep>', 'he', 'just',
# 'left', '<sep>'
encoded_pair_cls = encoded_pair[:, 0, :]
encoded_pair_crane = encoded_pair[:, 2, :]
encoded_pair.shape, encoded_pair_cls.shape, encoded_pair_crane[0][:3]

위의 코드는 두 개의 입력 시퀀스를 BERT 모델을 통해 함께 인코딩하는 과정을 나타냅니다. 코드의 내용을 자세히 살펴보겠습니다.

  • tokens_a: 첫 번째 입력 시퀀스로 사용되는 토큰들의 리스트입니다.
  • tokens_b: 두 번째 입력 시퀀스로 사용되는 토큰들의 리스트입니다.
  • encoded_pair: get_bert_encoding 함수를 호출하여 두 개의 입력 시퀀스를 BERT 모델을 통해 인코딩한 결과입니다.

위의 코드는 다음과 같은 작업을 수행합니다:

  1. get_bert_encoding 함수를 호출하여 두 개의 입력 시퀀스 tokens_a와 tokens_b를 BERT 모델을 통해 함께 인코딩한 결과를 encoded_pair로 받습니다.
  2. encoded_pair는 3차원 텐서입니다. 첫 번째 차원은 배치 차원, 두 번째 차원은 토큰 시퀀스의 위치 (토큰의 위치), 세 번째 차원은 BERT 모델의 히든 상태의 차원입니다.
  3. encoded_pair_cls는 인코딩된 페어의 첫 번째 토큰인 <cls>의 히든 상태를 추출합니다.
  4. encoded_pair_crane은 인코딩된 페어에서 "crane" 토큰의 히든 상태를 추출합니다.
  5. 마지막으로, encoded_pair의 모양, encoded_pair_cls의 모양, 그리고 encoded_pair_crane의 처음 세 원소를 출력합니다.

이 코드를 실행하면 두 개의 입력 시퀀스를 BERT 모델을 통해 함께 인코딩한 결과와 해당 결과의 특정 위치에서 추출한 히든 상태를 확인할 수 있습니다

(torch.Size([1, 10, 128]),
 torch.Size([1, 128]),
 tensor([0.0430, 1.6132, 0.0437], device='cuda:0', grad_fn=<SliceBackward0>))

 

In Section 16, we will fine-tune a pretrained BERT model for downstream natural language processing applications.

 

섹션 16에서는 다운스트림 자연어 처리 애플리케이션을 위해 사전 훈련된 BERT 모델을 미세 조정합니다.

 

15.10.3. Summary

  • The original BERT has two versions, where the base model has 110 million parameters and the large model has 340 million parameters.

    원래 BERT에는 두 가지 버전이 있습니다. 기본 모델에는 1억 1천만 개의 매개변수가 있고 대형 모델에는 3억 4천만 개의 매개변수가 있습니다.

  • After pretraining BERT, we can use it to represent single text, text pairs, or any token in them.

    BERT를 사전 훈련한 후 이를 사용하여 단일 텍스트, 텍스트 쌍 또는 그 안에 있는 모든 토큰을 나타낼 수 있습니다.

  • In the experiment, the same token has different BERT representation when their contexts are different. This supports that BERT representations are context-sensitive.

    실험에서 동일한 토큰은 컨텍스트가 다를 때 BERT 표현이 다릅니다. 이는 BERT 표현이 상황에 따라 달라지는 것을 지원합니다.

 

15.10.4. Exercises

  1. In the experiment, we can see that the masked language modeling loss is significantly higher than the next sentence prediction loss. Why?
  2. Set the maximum length of a BERT input sequence to be 512 (same as the original BERT model). Use the configurations of the original BERT model such as BERTLARGE. Do you encounter any error when running this section? Why?

 

 

 

 

 

 

 

반응형


반응형

15.9. The Dataset for Pretraining BERT — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

15.9. The Dataset for Pretraining BERT — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

15.9. The Dataset for Pretraining BERT

 

To pretrain the BERT model as implemented in Section 15.8, we need to generate the dataset in the ideal format to facilitate the two pretraining tasks: masked language modeling and next sentence prediction. On the one hand, the original BERT model is pretrained on the concatenation of two huge corpora BookCorpus and English Wikipedia (see Section 15.8.5), making it hard to run for most readers of this book. On the other hand, the off-the-shelf pretrained BERT model may not fit for applications from specific domains like medicine. Thus, it is getting popular to pretrain BERT on a customized dataset. To facilitate the demonstration of BERT pretraining, we use a smaller corpus WikiText-2 (Merity et al., 2016).

 

섹션 15.8에 구현된 BERT 모델을 사전 훈련하려면 마스크된 언어 모델링과 다음 문장 예측이라는 두 가지 사전 훈련 작업을 용이하게 하기 위해 이상적인 형식으로 데이터 세트를 생성해야 합니다. 한편으로, 원래 BERT 모델은 두 개의 거대한 말뭉치인 BookCorpus와 English Wikipedia(섹션 15.8.5 참조)의 연결에 대해 사전 훈련되어 있어 이 책의 대부분의 독자가 실행하기 어렵습니다. 반면, 상용 사전 훈련된 BERT 모델은 의학과 같은 특정 영역의 애플리케이션에는 적합하지 않을 수 있습니다. 따라서 맞춤형 데이터 세트에 대해 BERT를 사전 훈련하는 것이 인기를 얻고 있습니다. BERT 사전 훈련의 시연을 용이하게 하기 위해 우리는 더 작은 말뭉치인 WikiText-2(Merity et al., 2016)를 사용합니다.

 

Comparing with the PTB dataset used for pretraining word2vec in Section 15.3, WikiText-2 (i) retains the original punctuation, making it suitable for next sentence prediction; (ii) retains the original case and numbers; (iii) is over twice larger.

 

15.3. The Dataset for Pretraining Word Embeddings — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

섹션 15.3의 word2vec 사전 훈련에 사용된 PTB 데이터 세트와 비교하면 WikiText-2 (i)는 원래 구두점을 유지하여 다음 문장 예측에 적합합니다. (ii) 원래 대소문자와 번호를 유지합니다. (iii) 2배 이상 크다.

 

import os
import random
import torch
from d2l import torch as d2l

In the WikiText-2 dataset, each line represents a paragraph where space is inserted between any punctuation and its preceding token. Paragraphs with at least two sentences are retained. To split sentences, we only use the period as the delimiter for simplicity. We leave discussions of more complex sentence splitting techniques in the exercises at the end of this section.

 

WikiText-2 데이터세트에서 각 줄은 구두점과 선행 토큰 사이에 공백이 삽입된 단락을 나타냅니다. 문장이 두 개 이상인 단락은 유지됩니다. 문장을 분할하려면 단순화를 위해 마침표만 구분 기호로 사용합니다. 이 섹션 끝 부분의 연습에서 더 복잡한 문장 분할 기술에 대한 논의를 남깁니다.

 

#@save
d2l.DATA_HUB['wikitext-2'] = (
    'https://s3.amazonaws.com/research.metamind.io/wikitext/'
    'wikitext-2-v1.zip', '3c914d17d80b1459be871a5039ac23e752a53cbe')

#@save
def _read_wiki(data_dir):
    file_name = os.path.join(data_dir, 'wiki.train.tokens')
    with open(file_name, 'r') as f:
        lines = f.readlines()
    # Uppercase letters are converted to lowercase ones
    paragraphs = [line.strip().lower().split(' . ')
                  for line in lines if len(line.split(' . ')) >= 2]
    random.shuffle(paragraphs)
    return paragraphs

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. d2l.DATA_HUB['wikitext-2'] 정의: 데이터셋을 다운로드하기 위한 URL과 체크섬을 정의합니다. 이 정보는 데이터를 다운로드하고 추출하는 데 사용됩니다.
  2. _read_wiki 함수 정의: 이 함수는 Wikitext-2 데이터셋을 읽어와 처리하는 역할을 합니다. data_dir 매개변수는 데이터셋이 저장되어 있는 디렉토리 경로를 나타냅니다.
  3. 데이터 읽기 및 전처리: 함수 내부에서는 데이터 파일을 열어 각 줄을 읽어들이고 처리합니다. 각 줄을 소문자로 변환하고 ' . ' (마침표+공백)을 구분자로 사용하여 문단을 분할합니다. 문단의 길이가 2 이상인 경우에만 선택하도록 하여 문장이 적어도 두 개 이상 있는 문단들을 선택합니다.
  4. 데이터 섞기: 문단들을 무작위로 섞어 데이터의 랜덤성을 높입니다.

이러한 작업을 통해 _read_wiki 함수는 Wikitext-2 데이터셋을 읽어와 전처리한 후 문단들을 반환합니다. 이 데이터는 텍스트 처리 태스크에서 사용될 수 있습니다.

 

15.9.1. Defining Helper Functions for Pretraining Tasks

In the following, we begin by implementing helper functions for the two BERT pretraining tasks: next sentence prediction and masked language modeling. These helper functions will be invoked later when transforming the raw text corpus into the dataset of the ideal format to pretrain BERT.

 

다음에서는 다음 문장 예측과 마스크된 언어 모델링이라는 두 가지 BERT 사전 학습 작업에 대한 도우미 기능을 구현하는 것으로 시작합니다. 이러한 도우미 함수는 나중에 BERT를 사전 훈련하기 위해 원시 텍스트 코퍼스를 이상적인 형식의 데이터 세트로 변환할 때 호출됩니다.

 

15.9.1.1. Generating the Next Sentence Prediction Task

According to descriptions of Section 15.8.5.2, the _get_next_sentence function generates a training example for the binary classification task.

 

섹션 15.8.5.2의 설명에 따라 _get_next_sentence 함수는 이진 분류 작업에 대한 훈련 예제를 생성합니다.

 

#@save
def _get_next_sentence(sentence, next_sentence, paragraphs):
    if random.random() < 0.5:
        is_next = True
    else:
        # `paragraphs` is a list of lists of lists
        next_sentence = random.choice(random.choice(paragraphs))
        is_next = False
    return sentence, next_sentence, is_next

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. sentence, next_sentence, paragraphs 매개변수: 함수에는 세 개의 매개변수가 전달됩니다.
    • sentence: 현재 문장입니다.
    • next_sentence: 다음 문장입니다.
    • paragraphs: 이중 리스트 형태의 데이터셋으로, 문단들을 포함한 구조입니다.
  2. NSP 타겟 생성: 먼저 0.5의 확률로 is_next 라는 변수를 True로 설정합니다. 이는 현재 문장과 다음 문장이 연속된 문장인 경우를 의미합니다. 그렇지 않은 경우에는 (0.5 확률로) 다른 무작위 문단에서 문장을 선택하여 next_sentence를 업데이트하고 is_next를 False로 설정합니다. 이 경우 현재 문장과 다음 문장은 연속되지 않습니다.
  3. 결과 반환: 최종적으로 생성된 sentence, next_sentence, is_next를 반환합니다. 이 정보는 NSP 작업의 학습 데이터를 구성하거나 평가하는 데 사용됩니다.

NSP 작업은 BERT 모델의 학습에서 사용되며, 주어진 문장과 다음 문장이 실제로 연속되는지 여부를 판단하는 과제입니다. 이를 통해 모델은 문맥을 이해하고 문장 간의 관계를 파악할 수 있도록 학습됩니다

 

 

The following function generates training examples for next sentence prediction from the input paragraph by invoking the _get_next_sentence function. Here paragraph is a list of sentences, where each sentence is a list of tokens. The argument max_len specifies the maximum length of a BERT input sequence during pretraining.

 

다음 함수는 _get_next_sentence 함수를 호출하여 입력 단락에서 다음 문장 예측을 위한 훈련 예제를 생성합니다. 여기 단락은 문장 목록이며, 각 문장은 토큰 목록입니다. max_len 인수는 사전 학습 중 BERT 입력 시퀀스의 최대 길이를 지정합니다.

 

#@save
def _get_nsp_data_from_paragraph(paragraph, paragraphs, vocab, max_len):
    nsp_data_from_paragraph = []
    for i in range(len(paragraph) - 1):
        tokens_a, tokens_b, is_next = _get_next_sentence(
            paragraph[i], paragraph[i + 1], paragraphs)
        # Consider 1 '<cls>' token and 2 '<sep>' tokens
        if len(tokens_a) + len(tokens_b) + 3 > max_len:
            continue
        tokens, segments = d2l.get_tokens_and_segments(tokens_a, tokens_b)
        nsp_data_from_paragraph.append((tokens, segments, is_next))
    return nsp_data_from_paragraph

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. paragraph, paragraphs, vocab, max_len 매개변수: 함수에는 네 가지 매개변수가 전달됩니다.
    • paragraph: 현재 문단을 나타내는 리스트입니다.
    • paragraphs: 전체 데이터셋의 모든 문단들을 포함한 구조입니다.
    • vocab: 어휘 사전입니다.
    • max_len: 토큰의 최대 길이입니다.
  2. NSP 데이터 생성: 현재 문단 내에서 인접한 두 문장을 선택하여 NSP 작업에 사용되는 데이터를 생성합니다. 이를 위해 _get_next_sentence 함수를 사용하여 현재 문장과 다음 문장, 그리고 두 문장이 연속되는지 여부(is_next)를 얻어옵니다.
  3. 길이 조건 확인: 생성된 두 문장의 토큰들의 길이와 추가된 특수 토큰('<cls>' 및 '<sep>')을 고려하여 문장의 길이가 max_len을 초과하지 않는지 확인합니다. 만약 초과하는 경우 건너뛰고 다음 인덱스의 문장을 고려합니다.
  4. 토큰 및 세그먼트 생성: _get_tokens_and_segments 함수를 사용하여 두 문장의 토큰과 세그먼트를 생성합니다. 이 정보는 BERT 입력으로 사용됩니다.
  5. 결과 반환: 생성된 NSP 데이터인 (tokens, segments, is_next)를 리스트에 추가하여 반환합니다. 이 정보는 NSP 작업의 학습 데이터를 구성하는 데 사용됩니다.

NSP 작업은 BERT 모델의 학습에서 문장 간의 관계를 학습하는 데 사용되며, 문장이 실제로 연속되는지 여부를 판단하는 태스크입니다.

 

15.9.1.2. Generating the Masked Language Modeling Task

In order to generate training examples for the masked language modeling task from a BERT input sequence, we define the following _replace_mlm_tokens function. In its inputs, tokens is a list of tokens representing a BERT input sequence, candidate_pred_positions is a list of token indices of the BERT input sequence excluding those of special tokens (special tokens are not predicted in the masked language modeling task), and num_mlm_preds indicates the number of predictions (recall 15% random tokens to predict). Following the definition of the masked language modeling task in Section 15.8.5.1, at each prediction position, the input may be replaced by a special “<mask>” token or a random token, or remain unchanged. In the end, the function returns the input tokens after possible replacement, the token indices where predictions take place and labels for these predictions.

 

BERT 입력 시퀀스에서 마스크된 언어 모델링 작업에 대한 훈련 예제를 생성하기 위해 다음 _replace_mlm_tokens 함수를 정의합니다. 입력에서 tokens는 BERT 입력 시퀀스를 나타내는 토큰 목록이고, Candidate_pred_positions는 특수 토큰을 제외한 BERT 입력 시퀀스의 토큰 인덱스 목록이며(특수 토큰은 마스크된 언어 모델링 작업에서 예측되지 않음) num_mlm_preds는 예측 수(예측을 위해 15% 무작위 토큰을 회수) 섹션 15.8.5.1의 마스크된 언어 모델링 작업 정의에 따라 각 예측 위치에서 입력은 특수 "<mask>" 토큰 또는 무작위 토큰으로 대체되거나 변경되지 않은 상태로 유지될 수 있습니다. 결국 함수는 가능한 교체 후 입력 토큰, 예측이 발생하는 토큰 인덱스 및 이러한 예측에 대한 레이블을 반환합니다.

 

#@save
def _replace_mlm_tokens(tokens, candidate_pred_positions, num_mlm_preds,
                        vocab):
    # For the input of a masked language model, make a new copy of tokens and
    # replace some of them by '<mask>' or random tokens
    mlm_input_tokens = [token for token in tokens]
    pred_positions_and_labels = []
    # Shuffle for getting 15% random tokens for prediction in the masked
    # language modeling task
    random.shuffle(candidate_pred_positions)
    for mlm_pred_position in candidate_pred_positions:
        if len(pred_positions_and_labels) >= num_mlm_preds:
            break
        masked_token = None
        # 80% of the time: replace the word with the '<mask>' token
        if random.random() < 0.8:
            masked_token = '<mask>'
        else:
            # 10% of the time: keep the word unchanged
            if random.random() < 0.5:
                masked_token = tokens[mlm_pred_position]
            # 10% of the time: replace the word with a random word
            else:
                masked_token = random.choice(vocab.idx_to_token)
        mlm_input_tokens[mlm_pred_position] = masked_token
        pred_positions_and_labels.append(
            (mlm_pred_position, tokens[mlm_pred_position]))
    return mlm_input_tokens, pred_positions_and_labels

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. tokens, candidate_pred_positions, num_mlm_preds, vocab 매개변수: 함수에는 네 가지 매개변수가 전달됩니다.
    • tokens: 입력 문장의 토큰들을 나타내는 리스트입니다.
    • candidate_pred_positions: 예측될 토큰 위치의 후보 목록입니다.
    • num_mlm_preds: MLM 작업에서 예측할 토큰의 개수입니다.
    • vocab: 어휘 사전입니다.
  2. MLM 데이터 생성: 입력 문장의 토큰들 중에서 일부를 '<mask>' 토큰이나 랜덤한 토큰으로 교체하여 MLM 작업에 사용되는 데이터를 생성합니다.
  3. 교체 비율 선택: 랜덤한 확률에 따라 토큰을 교체할지 여부를 결정합니다. 대부분의 경우에는 '<mask>' 토큰으로 교체하며, 일부 경우에는 토큰을 변경하지 않거나 랜덤한 토큰으로 교체합니다.
  4. 교체 및 레이블 생성: mlm_input_tokens 리스트에서 선택된 위치의 토큰을 교체하고, 교체된 토큰 위치와 원래 토큰 값을 pred_positions_and_labels에 저장합니다.
  5. 결과 반환: 교체된 토큰들을 포함한 mlm_input_tokens 리스트와 예측된 토큰 위치와 레이블을 저장한 pred_positions_and_labels 리스트를 반환합니다. 이 정보는 MLM 작업의 학습 데이터를 구성하는 데 사용됩니다.

MLM 작업은 BERT 모델의 학습에서 토큰의 일부를 가리고 해당 토큰을 예측하는 태스크로, 모델이 언어적인 패턴을 학습하고 문맥을 이해하는데 도움이 됩니다.

 

 

By invoking the aforementioned _replace_mlm_tokens function, the following function takes a BERT input sequence (tokens) as an input and returns indices of the input tokens (after possible token replacement as described in Section 15.8.5.1), the token indices where predictions take place, and label indices for these predictions.

 

앞서 언급한 _replace_mlm_tokens 함수를 호출함으로써 다음 함수는 BERT 입력 시퀀스(토큰)를 입력으로 사용하고 입력 토큰의 인덱스(섹션 15.8.5.1에 설명된 대로 가능한 토큰 교체 후), 예측이 발생하는 토큰 인덱스를 반환합니다. 이러한 예측에 대한 라벨 인덱스.

 

#@save
def _get_mlm_data_from_tokens(tokens, vocab):
    candidate_pred_positions = []
    # `tokens` is a list of strings
    for i, token in enumerate(tokens):
        # Special tokens are not predicted in the masked language modeling
        # task
        if token in ['<cls>', '<sep>']:
            continue
        candidate_pred_positions.append(i)
    # 15% of random tokens are predicted in the masked language modeling task
    num_mlm_preds = max(1, round(len(tokens) * 0.15))
    mlm_input_tokens, pred_positions_and_labels = _replace_mlm_tokens(
        tokens, candidate_pred_positions, num_mlm_preds, vocab)
    pred_positions_and_labels = sorted(pred_positions_and_labels,
                                       key=lambda x: x[0])
    pred_positions = [v[0] for v in pred_positions_and_labels]
    mlm_pred_labels = [v[1] for v in pred_positions_and_labels]
    return vocab[mlm_input_tokens], pred_positions, vocab[mlm_pred_labels]

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. tokens, vocab 매개변수: 함수에는 두 가지 매개변수가 전달됩니다.
    • tokens: 입력 문장의 토큰들을 나타내는 리스트입니다.
    • vocab: 어휘 사전입니다.
  2. 후보 예측 위치 식별: 입력 문장에서 '<cls>'와 '<sep>' 토큰을 제외한 토큰들의 위치를 후보 예측 위치로 식별합니다.
  3. 예측할 토큰 개수 결정: 입력 문장 길이의 15%에 해당하는 토큰을 예측합니다.
  4. MLM 데이터 생성: _replace_mlm_tokens 함수를 사용하여 입력 문장에서 일부 토큰을 '<mask>' 토큰이나 랜덤한 토큰으로 교체하여 MLM 작업에 사용되는 데이터를 생성합니다.
  5. 토큰과 위치 정보 반환: MLM 작업에 필요한 토큰들과 예측 위치 정보를 반환합니다. 이 정보는 BERT 모델의 학습 데이터로 사용됩니다.

MLM 작업에서는 입력 문장의 토큰 중에서 일부를 가리고 해당 토큰을 예측하는 태스크로, 모델이 문맥을 이해하고 언어적인 패턴을 학습하는 데 도움이 됩니다.

15.9.2. Transforming Text into the Pretraining Dataset

Now we are almost ready to customize a Dataset class for pretraining BERT. Before that, we still need to define a helper function _pad_bert_inputs to append the special “<pad>” tokens to the inputs. Its argument examples contain the outputs from the helper functions _get_nsp_data_from_paragraph and _get_mlm_data_from_tokens for the two pretraining tasks.

 

이제 BERT 사전 훈련을 위해 Dataset 클래스를 사용자 정의할 준비가 거의 완료되었습니다. 그 전에 특수 "<pad>" 토큰을 입력에 추가하려면 도우미 함수 _pad_bert_inputs를 정의해야 합니다. 인수 예제에는 두 가지 사전 학습 작업에 대한 도우미 함수 _get_nsp_data_from_paragraph 및 _get_mlm_data_from_tokens의 출력이 포함되어 있습니다.

 

#@save
def _pad_bert_inputs(examples, max_len, vocab):
    max_num_mlm_preds = round(max_len * 0.15)
    all_token_ids, all_segments, valid_lens,  = [], [], []
    all_pred_positions, all_mlm_weights, all_mlm_labels = [], [], []
    nsp_labels = []
    for (token_ids, pred_positions, mlm_pred_label_ids, segments,
         is_next) in examples:
        all_token_ids.append(torch.tensor(token_ids + [vocab['<pad>']] * (
            max_len - len(token_ids)), dtype=torch.long))
        all_segments.append(torch.tensor(segments + [0] * (
            max_len - len(segments)), dtype=torch.long))
        # `valid_lens` excludes count of '<pad>' tokens
        valid_lens.append(torch.tensor(len(token_ids), dtype=torch.float32))
        all_pred_positions.append(torch.tensor(pred_positions + [0] * (
            max_num_mlm_preds - len(pred_positions)), dtype=torch.long))
        # Predictions of padded tokens will be filtered out in the loss via
        # multiplication of 0 weights
        all_mlm_weights.append(
            torch.tensor([1.0] * len(mlm_pred_label_ids) + [0.0] * (
                max_num_mlm_preds - len(pred_positions)),
                dtype=torch.float32))
        all_mlm_labels.append(torch.tensor(mlm_pred_label_ids + [0] * (
            max_num_mlm_preds - len(mlm_pred_label_ids)), dtype=torch.long))
        nsp_labels.append(torch.tensor(is_next, dtype=torch.long))
    return (all_token_ids, all_segments, valid_lens, all_pred_positions,
            all_mlm_weights, all_mlm_labels, nsp_labels)

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. examples 입력: 함수에는 입력 데이터 예시인 examples가 전달됩니다. 각 예시는 (token_ids, pred_positions, mlm_pred_label_ids, segments, is_next) 형태의 튜플로 구성되어 있습니다.
  2. BERT 입력 데이터 생성: 입력 데이터를 모델의 입력 형식에 맞게 생성합니다. 이 과정에서 패딩이 진행됩니다.
  3. 입력 데이터 반환: 모든 생성된 데이터를 리스트 형태로 반환합니다. 반환되는 데이터는 (all_token_ids, all_segments, valid_lens, all_pred_positions, all_mlm_weights, all_mlm_labels, nsp_labels) 형식입니다.

BERT 모델은 다양한 입력 데이터를 필요로 합니다. 이 함수는 이러한 입력 데이터를 정제하고 필요한 형식으로 변환하여 모델에 공급할 준비를 하는 역할을 수행합니다

 

 

Putting the helper functions for generating training examples of the two pretraining tasks, and the helper function for padding inputs together, we customize the following _WikiTextDataset class as the WikiText-2 dataset for pretraining BERT. By implementing the __getitem__function, we can arbitrarily access the pretraining (masked language modeling and next sentence prediction) examples generated from a pair of sentences from the WikiText-2 corpus.

 

두 가지 사전 훈련 작업의 훈련 예제를 생성하기 위한 도우미 함수와 입력 패딩을 위한 도우미 함수를 함께 배치하여 BERT 사전 훈련을 위한 WikiText-2 데이터 세트로 다음 _WikiTextDataset 클래스를 사용자 정의합니다. __getitem__function을 구현함으로써 WikiText-2 코퍼스의 문장 쌍에서 생성된 사전 훈련(마스킹된 언어 모델링 및 다음 문장 예측) 예제에 임의로 액세스할 수 있습니다.

 

The original BERT model uses WordPiece embeddings whose vocabulary size is 30000 (Wu et al., 2016). The tokenization method of WordPiece is a slight modification of the original byte pair encoding algorithm in Section 15.6.2. For simplicity, we use the d2l.tokenize function for tokenization. Infrequent tokens that appear less than five times are filtered out.

 

원래 BERT 모델은 어휘 크기가 30000인 WordPiece 임베딩을 사용합니다(Wu et al., 2016). WordPiece의 토큰화 방법은 섹션 15.6.2의 원래 바이트 쌍 인코딩 알고리즘을 약간 수정한 것입니다. 단순화를 위해 토큰화에 d2l.tokenize 함수를 사용합니다. 5회 미만으로 나타나는 빈도가 낮은 토큰은 필터링됩니다.

 

#@save
class _WikiTextDataset(torch.utils.data.Dataset):
    def __init__(self, paragraphs, max_len):
        # Input `paragraphs[i]` is a list of sentence strings representing a
        # paragraph; while output `paragraphs[i]` is a list of sentences
        # representing a paragraph, where each sentence is a list of tokens
        paragraphs = [d2l.tokenize(
            paragraph, token='word') for paragraph in paragraphs]
        sentences = [sentence for paragraph in paragraphs
                     for sentence in paragraph]
        self.vocab = d2l.Vocab(sentences, min_freq=5, reserved_tokens=[
            '<pad>', '<mask>', '<cls>', '<sep>'])
        # Get data for the next sentence prediction task
        examples = []
        for paragraph in paragraphs:
            examples.extend(_get_nsp_data_from_paragraph(
                paragraph, paragraphs, self.vocab, max_len))
        # Get data for the masked language model task
        examples = [(_get_mlm_data_from_tokens(tokens, self.vocab)
                      + (segments, is_next))
                     for tokens, segments, is_next in examples]
        # Pad inputs
        (self.all_token_ids, self.all_segments, self.valid_lens,
         self.all_pred_positions, self.all_mlm_weights,
         self.all_mlm_labels, self.nsp_labels) = _pad_bert_inputs(
            examples, max_len, self.vocab)

    def __getitem__(self, idx):
        return (self.all_token_ids[idx], self.all_segments[idx],
                self.valid_lens[idx], self.all_pred_positions[idx],
                self.all_mlm_weights[idx], self.all_mlm_labels[idx],
                self.nsp_labels[idx])

    def __len__(self):
        return len(self.all_token_ids)

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. 텍스트 전처리: 입력으로 주어진 paragraphs 리스트의 각 문단을 토큰화하여 BERT 모델에 필요한 형태로 변환합니다.
  2. 어휘 생성: 위에서 전처리한 문단들을 바탕으로 어휘를 생성합니다. BERT 모델에서 사용할 토큰들과 토큰의 인덱스를 매핑하는 역할을 합니다.
  3. NSP 및 MLM 데이터 생성: 생성된 어휘와 문단 데이터를 활용하여 다음 문장 예측(NSP) 및 마스킹된 언어 모델(MLM) 학습을 위한 데이터를 생성합니다.
  4. 입력 데이터 패딩: 생성된 데이터를 BERT 모델의 입력 형식에 맞게 패딩합니다.
  5. __getitem__ 메서드: 인덱스를 입력으로 받아 해당 인덱스의 데이터를 반환합니다. BERT 모델의 입력으로 사용될 데이터를 반환합니다.
  6. __len__ 메서드: 데이터셋의 크기를 반환합니다.

이 클래스는 BERT 모델 학습을 위한 데이터셋을 생성하는데 필요한 모든 과정을 수행하며, 학습 데이터를 모델에 공급할 준비를 하게 됩니다

 

 

By using the _read_wiki function and the _WikiTextDataset class, we define the following load_data_wiki to download and WikiText-2 dataset and generate pretraining examples from it.

 

_read_wiki 함수와 _WikiTextDataset 클래스를 사용하여 다음 load_data_wiki를 정의하여 WikiText-2 데이터 세트를 다운로드하고 여기에서 사전 훈련 예제를 생성합니다.

 

#@save
def load_data_wiki(batch_size, max_len):
    """Load the WikiText-2 dataset."""
    num_workers = d2l.get_dataloader_workers()
    data_dir = d2l.download_extract('wikitext-2', 'wikitext-2')
    paragraphs = _read_wiki(data_dir)
    train_set = _WikiTextDataset(paragraphs, max_len)
    train_iter = torch.utils.data.DataLoader(train_set, batch_size,
                                        shuffle=True, num_workers=num_workers)
    return train_iter, train_set.vocab

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. 데이터 디렉토리 설정: WikiText-2 데이터셋을 다운로드하고 압축을 해제한 후, 데이터 디렉토리를 가져옵니다.
  2. WikiText-2 데이터 읽기: _read_wiki 함수를 사용하여 WikiText-2 데이터셋을 읽어와서 전처리합니다.
  3. 데이터셋 생성: 전처리된 문단 데이터를 기반으로 _WikiTextDataset 클래스를 사용하여 데이터셋을 생성합니다.
  4. 데이터로더 생성: 생성된 데이터셋을 PyTorch의 DataLoader로 변환하여 모델 학습에 사용할 수 있는 형태로 준비합니다. 배치 사이즈와 데이터 로더의 워커 수도 설정합니다.
  5. 반환: 생성된 데이터로더와 어휘(vocabulary)를 반환합니다. 데이터로더는 학습에 사용되며, 어휘는 BERT 모델의 입력을 처리하기 위해 필요한 정보를 담고 있습니다.

이 함수를 호출하면 WikiText-2 데이터셋을 효율적으로 학습에 활용할 수 있는 형태로 변환하여 반환해줍니다.

 

 

Setting the batch size to 512 and the maximum length of a BERT input sequence to be 64, we print out the shapes of a minibatch of BERT pretraining examples. Note that in each BERT input sequence, 10 (64×0.15) positions are predicted for the masked language modeling task.

 

배치 크기를 512로 설정하고 BERT 입력 시퀀스의 최대 길이를 64로 설정하면 BERT 사전 학습 예제의 미니 배치 모양을 인쇄합니다. 각 BERT 입력 시퀀스에서 마스크된 언어 모델링 작업에 대해 10(64×0.15) 위치가 예측됩니다.

 

batch_size, max_len = 512, 64
train_iter, vocab = load_data_wiki(batch_size, max_len)

for (tokens_X, segments_X, valid_lens_x, pred_positions_X, mlm_weights_X,
     mlm_Y, nsp_y) in train_iter:
    print(tokens_X.shape, segments_X.shape, valid_lens_x.shape,
          pred_positions_X.shape, mlm_weights_X.shape, mlm_Y.shape,
          nsp_y.shape)
    break

위의 코드에서 이루어지는 작업은 다음과 같습니다:

  1. batch_size와 max_len 설정: 미니 배치 크기와 최대 시퀀스 길이를 설정합니다.
  2. 데이터 로드 및 데이터로더 생성: load_data_wiki 함수를 호출하여 WikiText-2 데이터셋을 로드하고, 데이터로더를 생성합니다.
  3. 미니 배치 순회: train_iter 데이터로더에서 미니 배치를 순회합니다.
  4. 미니 배치 구성 요소 확인: 각 미니 배치에 포함된 요소들의 형태(shape)를 확인합니다. 이는 데이터의 구성을 이해하고 문제 없이 모델에 입력할 수 있는지 확인하기 위한 과정입니다.

위의 코드는 하나의 미니 배치만 확인하기 위해 break문을 사용하여 첫 번째 미니 배치만 순회하고 종료합니다. 출력된 형태들은 미니 배치에 포함된 다양한 요소들의 차원(shape)을 나타내며, 각각의 형태는 데이터의 차원을 나타냅니다

Downloading ../data/wikitext-2-v1.zip from https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-2-v1.zip...
torch.Size([512, 64]) torch.Size([512, 64]) torch.Size([512]) torch.Size([512, 10]) torch.Size([512, 10]) torch.Size([512, 10]) torch.Size([512])

 

In the end, let’s take a look at the vocabulary size. Even after filtering out infrequent tokens, it is still over twice larger than that of the PTB dataset.

 

마지막으로 어휘량을 살펴보겠습니다. 자주 발생하지 않는 토큰을 필터링한 후에도 여전히 PTB 데이터세트보다 2배 이상 큽니다.

 

len(vocab)
20256

 

15.9.3. Summary

  • Comparing with the PTB dataset, the WikiText-2 dateset retains the original punctuation, case and numbers, and is over twice larger.

    PTB 데이터 세트와 비교할 때 WikiText-2 날짜 세트는 원래 구두점, 대소문자 및 숫자를 유지하며 두 배 이상 더 큽니다.

  • We can arbitrarily access the pretraining (masked language modeling and next sentence prediction) examples generated from a pair of sentences from the WikiText-2 corpus.

    WikiText-2 코퍼스의 문장 쌍에서 생성된 사전 훈련(마스크된 언어 모델링 및 다음 문장 예측) 예제에 임의로 액세스할 수 있습니다.

 

15.9.4. Exercises

  1. For simplicity, the period is used as the only delimiter for splitting sentences. Try other sentence splitting techniques, such as the spaCy and NLTK. Take NLTK as an example. You need to install NLTK first: pip install nltk. In the code, first import nltk. Then, download the Punkt sentence tokenizer: nltk.download('punkt'). To split sentences such as sentences = 'This is great ! Why not ?', invoking nltk.tokenize.sent_tokenize(sentences) will return a list of two sentence strings: ['This is great !', 'Why not ?'].
  2. What is the vocabulary size if we do not filter out any infrequent token?
반응형


반응형

15.8. Bidirectional Encoder Representations from Transformers (BERT) — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

15.8. Bidirectional Encoder Representations from Transformers (BERT) — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

 

15.8. Bidirectional Encoder Representations from Transformers (BERT)

 

BERT 란?

 

BERT (Bidirectional Encoder Representations from Transformers)는 자연어 처리(NLP) 분야에서 가장 혁신적인 모델 중 하나로, 2018년에 고안되었습니다. BERT는 사전 훈련된 언어 모델로, 언어의 다양한 태스크에 대한 성능을 크게 향상시켰습니다. 이 모델은 Transformers 아키텍처를 기반으로 하며, 양방향 언어 모델링을 통해 문맥을 고려한 텍스트 임베딩을 생성합니다.

 

BERT의 주요 특징과 개념을 설명해보겠습니다:

 

  1. 사전 훈련과 미세 조정: BERT는 "사전 훈련(pre-training)"과 "미세 조정(fine-tuning)" 두 단계로 모델을 구축합니다. 사전 훈련 단계에서는 대량의 텍스트 데이터로 모델을 미리 학습시킵니다. 그리고 이후 미세 조정 단계에서는 특정 NLP 태스크에 대해 작은 양의 태스크 관련 데이터로 모델을 미세 조정하여 해당 태스크에 적합한 정보를 학습합니다.

  2. 양방향 언어 모델링: BERT는 양방향 언어 모델링을 수행합니다. 이는 문장 내 모든 단어를 좌우 방향으로 모두 고려하여 임베딩을 생성하는 것을 의미합니다. 이는 기존의 단방향 모델보다 훨씬 풍부한 문맥 정보를 반영할 수 있도록 도와줍니다.

  3. 사전 훈련 태스크: BERT는 사전 훈련을 위해 "마스크드 언어 모델링"과 "다음 문장 예측"이라는 두 가지 태스크를 활용합니다. "마스크드 언어 모델링"에서는 문장 내에서 임의의 단어를 가리고 그 단어를 모델이 추론하도록 합니다. "다음 문장 예측"에서는 두 문장의 관계를 판단하여 한 문장이 다른 문장의 뒤에 올 확률을 예측합니다.

  4. Transfer Learning: BERT는 사전 훈련을 통해 언어의 일반적인 특징을 학습하고, 이를 다양한 NLP 태스크에 전이 학습(transfer learning)하여 활용합니다. BERT를 미세 조정하여 특정 태스크에 맞게 튜닝하면, 해당 태스크에서 높은 성능을 달성할 수 있습니다.

  5. Transformer 아키텍처: BERT는 Transformer 아키텍처의 여러 레이어를 쌓아 만들어진 모델입니다. Self-Attention 메커니즘을 활용하여 문장 내 단어 간의 상관 관계를 모델링하며, 여러 개의 인코더 레이어로 구성되어 문맥을 잘 파악하는 텍스트 임베딩을 생성합니다.

BERT는 자연어 처리에서 다양한 태스크에 적용할 수 있는 범용적인 임베딩을 제공하며, 전통적인 NLP 모델보다 훨씬 뛰어난 성능을 보입니다. 그 결과, BERT는 자연어 이해와 생성 과제에서 혁신적인 역할을 하였고, 이후로도 다양한 NLP 모델의 기반이 되는 중요한 아키텍처 중 하나로 자리 잡았습니다.

 

We have introduced several word embedding models for natural language understanding. After pretraining, the output can be thought of as a matrix where each row is a vector that represents a word of a predefined vocabulary. In fact, these word embedding models are all context-independent. Let’s begin by illustrating this property.

 

우리는 자연어 이해를 위한 여러 단어 임베딩 모델을 도입했습니다. 사전 훈련 후 출력은 각 행이 사전 정의된 어휘의 단어를 나타내는 벡터인 행렬로 간주될 수 있습니다. 실제로 이러한 단어 임베딩 모델은 모두 상황에 독립적입니다. 이 속성을 설명하는 것부터 시작하겠습니다.

 

15.8.1. From Context-Independent to Context-Sensitive

Recall the experiments in Section 15.4 and Section 15.7. For instance, word2vec and GloVe both assign the same pretrained vector to the same word regardless of the context of the word (if any). Formally, a context-independent representation of any token x is a function f(x) that only takes x as its input. Given the abundance of polysemy and complex semantics in natural languages, context-independent representations have obvious limitations. For instance, the word “crane” in contexts “a crane is flying” and “a crane driver came” has completely different meanings; thus, the same word may be assigned different representations depending on contexts.

 

15.4절과 15.7절의 실험을 떠올려 보세요. 예를 들어, word2vec과 GloVe는 모두 단어의 컨텍스트(있는 경우)에 관계없이 동일한 사전 훈련된 벡터를 동일한 단어에 할당합니다. 공식적으로 토큰 x의 컨텍스트 독립적 표현은 x만 입력으로 사용하는 함수 f(x)입니다. 자연어의 풍부한 다의어와 복잡한 의미를 고려할 때 문맥 독립적 표현에는 분명한 한계가 있습니다. 예를 들어, "a crane is flying"와 "a crane driver came"라는 맥락에서 "crane"이라는 단어는 완전히 다른 의미를 갖습니다. 따라서 동일한 단어에도 상황에 따라 다른 표현이 할당될 수 있습니다.

 

This motivates the development of context-sensitive word representations, where representations of words depend on their contexts. Hence, a context-sensitive representation of token x is a function f(x,c(x)) depending on both x and its context c(x). Popular context-sensitive representations include TagLM (language-model-augmented sequence tagger) (Peters et al., 2017), CoVe (Context Vectors) (McCann et al., 2017), and ELMo (Embeddings from Language Models) (Peters et al., 2018).

 

이는 단어 표현이 문맥에 따라 달라지는 문맥 인식 단어 표현의 개발에 동기를 부여합니다. 따라서 토큰 x의 상황에 맞는 표현은 x와 해당 상황 c(x)에 모두 의존하는 함수 f(x,c(x))입니다. 널리 사용되는 상황 인식 표현에는 TagLM(언어 모델 증강 시퀀스 태거)(Peters et al., 2017), CoVe(컨텍스트 벡터)(McCann et al., 2017) 및 ELMo(Embeddings from Language Models)(Peters et al. al., 2018) 등이 있습니.

 

For example, by taking the entire sequence as input, ELMo is a function that assigns a representation to each word from the input sequence. Specifically, ELMo combines all the intermediate layer representations from pretrained bidirectional LSTM as the output representation. Then the ELMo representation will be added to a downstream task’s existing supervised model as additional features, such as by concatenating ELMo representation and the original representation (e.g., GloVe) of tokens in the existing model. On the one hand, all the weights in the pretrained bidirectional LSTM model are frozen after ELMo representations are added. On the other hand, the existing supervised model is specifically customized for a given task. Leveraging different best models for different tasks at that time, adding ELMo improved the state of the art across six natural language processing tasks: sentiment analysis, natural language inference, semantic role labeling, coreference resolution, named entity recognition, and question answering.

 

예를 들어 ELMo는 전체 시퀀스를 입력으로 사용하여 입력 시퀀스의 각 단어에 표현을 할당하는 함수입니다. 특히 ELMo는 사전 훈련된 양방향 LSTM의 모든 중간 계층 표현을 출력 표현으로 결합합니다. 그런 다음 ELMo 표현은 ELMo 표현과 기존 모델의 토큰 원래 표현(예: GloVe)을 연결하는 등의 추가 기능으로 다운스트림 작업의 기존 감독 모델에 추가됩니다. 한편, 사전 훈련된 양방향 LSTM 모델의 모든 가중치는 ELMo 표현이 추가된 후에 고정됩니다. 반면, 기존 지도 모델은 특정 작업에 맞게 특별히 맞춤화되었습니다. 당시 다양한 작업에 대해 다양한 최상의 모델을 활용하고 ELMo를 추가하여 감정 분석, 자연어 추론, 의미론적 역할 레이블 지정, 상호 참조 해결, 명명된 엔터티 인식 및 질문 응답 등 6가지 자연어 처리 작업 전반에 걸쳐 최신 기술을 향상시켰습니다.

 

ELMo란?

 

ELMo (Embeddings from Language Models)는 사전 훈련된 언어 모델을 활용하여 단어 임베딩을 생성하는 기술입니다. ELMo는 2018년에 제안된 자연어 처리 기법 중 하나로, 언어 모델을 사용하여 단어의 의미를 잘 포착하고 문맥을 고려한 풍부한 단어 임베딩을 만들어냅니다.

 

ELMo는 다음과 같은 특징을 가지고 있습니다:

 

  1. 사전 훈련된 언어 모델 사용: ELMo는 언어 모델을 사전 훈련하여 단어의 의미와 문맥 정보를 학습합니다. 이 때, 양방향 LSTM (Bidirectional Long Short-Term Memory)을 사용하여 단어의 좌우 문맥을 모두 고려합니다.
  2. 문맥 정보 반영: ELMo는 단어의 임베딩을 생성할 때 해당 단어가 나타나는 문맥 정보를 모두 고려합니다. 이렇게 함으로써 단어의 다의성을 해소하고 문맥을 풍부하게 반영한 임베딩을 얻을 수 있습니다.
  3. 계층적 특성 추출: ELMo는 다양한 언어적 특징을 반영하기 위해 여러 계층의 언어 모델을 사용하여 임베딩을 생성합니다. 이는 각 계층의 모델이 단어의 다양한 언어적 특성을 포착하도록 도와줍니다.
  4. 사전 훈련 및 파인 튜닝: ELMo는 먼저 대규모 텍스트 데이터로 사전 훈련된 언어 모델을 생성한 다음, 특정 자연어 처리 작업에 맞게 파인 튜닝하여 사용합니다. 이로써 작업에 특화된 품질 높은 임베딩을 얻을 수 있습니다.

ELMo는 문맥을 고려한 임베딩을 통해 다양한 자연어 처리 작업에서 성능 향상을 이룰 수 있습니다. 특히 ELMo 임베딩은 감정 분석, 질문 응답, 기계 번역, 개체명 인식 등 다양한 자연어 처리 작업에 활용되며, 사전 훈련된 모델의 높은 품질과 다양한 언어 특징을 반영하는 장점을 가지고 있습니다.

 

15.8.2. From Task-Specific to Task-Agnostic

Although ELMo has significantly improved solutions to a diverse set of natural language processing tasks, each solution still hinges on a task-specific architecture. However, it is practically non-trivial to craft a specific architecture for every natural language processing task. The GPT (Generative Pre-Training) model represents an effort in designing a general task-agnostic model for context-sensitive representations (Radford et al., 2018). Built on a Transformer decoder, GPT pretrains a language model that will be used to represent text sequences. When applying GPT to a downstream task, the output of the language model will be fed into an added linear output layer to predict the label of the task. In sharp contrast to ELMo that freezes parameters of the pretrained model, GPT fine-tunes all the parameters in the pretrained Transformer decoder during supervised learning of the downstream task. GPT was evaluated on twelve tasks of natural language inference, question answering, sentence similarity, and classification, and improved the state of the art in nine of them with minimal changes to the model architecture.

 

ELMo는 다양한 자연어 처리 작업에 대한 솔루션을 크게 개선했지만 각 솔루션은 여전히 작업별 아키텍처에 달려 있습니다. 그러나 모든 자연어 처리 작업에 대해 특정 아키텍처를 제작하는 것은 사실상 쉽지 않습니다. GPT(Generative Pre-Training) 모델은 상황에 맞는 표현을 위한 일반적인 작업 독립적 모델을 설계하려는 노력을 나타냅니다(Radford et al., 2018). Transformer 디코더를 기반으로 구축된 GPT는 텍스트 시퀀스를 나타내는 데 사용될 언어 모델을 사전 학습합니다. 다운스트림 작업에 GPT를 적용하면 언어 모델의 출력이 추가된 선형 출력 레이어에 공급되어 작업의 레이블을 예측합니다. 사전 학습된 모델의 매개변수를 고정하는 ELMo와는 대조적으로 GPT는 다운스트림 작업의 지도 학습 중에 사전 학습된 Transformer 디코더의 모든 매개변수를 미세 조정합니다. GPT는 자연어 추론, 질의 응답, 문장 유사성, 분류 등 12가지 과제에 대해 평가했으며, 모델 아키텍처를 최소한으로 변경하면서 9가지 항목을 최신 수준으로 개선했습니다.

 

However, due to the autoregressive nature of language models, GPT only looks forward (left-to-right). In contexts “i went to the bank to deposit cash” and “i went to the bank to sit down”, as “bank” is sensitive to the context to its left, GPT will return the same representation for “bank”, though it has different meanings.

 

그러나 언어 모델의 자동 회귀 특성으로 인해 GPT는 앞(왼쪽에서 오른쪽)만 봅니다. "i went to the bank to deposit cash" 및 "i went to the bank to sit down"라는 맥락에서 "bank"은 왼쪽의 상황에 민감하므로 GPT는 '은행'에 대해 동일한 표현을 반환하지만 의미는 다릅니다.

 

15.8.3. BERT: Combining the Best of Both Worlds

 

As we have seen, ELMo encodes context bidirectionally but uses task-specific architectures; while GPT is task-agnostic but encodes context left-to-right. Combining the best of both worlds, BERT (Bidirectional Encoder Representations from Transformers) encodes context bidirectionally and requires minimal architecture changes for a wide range of natural language processing tasks (Devlin et al., 2018). Using a pretrained Transformer encoder, BERT is able to represent any token based on its bidirectional context. During supervised learning of downstream tasks, BERT is similar to GPT in two aspects. First, BERT representations will be fed into an added output layer, with minimal changes to the model architecture depending on nature of tasks, such as predicting for every token vs. predicting for the entire sequence. Second, all the parameters of the pretrained Transformer encoder are fine-tuned, while the additional output layer will be trained from scratch. Fig. 15.8.1 depicts the differences among ELMo, GPT, and BERT.

 

앞서 살펴본 것처럼 ELMo는 컨텍스트를 양방향으로 인코딩하지만 작업별 아키텍처를 사용합니다. GPT는 작업에 구애받지 않지만 컨텍스트를 왼쪽에서 오른쪽으로 인코딩합니다. 두 가지 장점을 결합한 BERT(BiDirectional Encoder Representations from Transformers)는 컨텍스트를 양방향으로 인코딩하고 광범위한 자연어 처리 작업에 대해 최소한의 아키텍처 변경이 필요합니다(Devlin et al., 2018). 사전 훈련된 Transformer 인코더를 사용하여 BERT는 양방향 컨텍스트를 기반으로 모든 토큰을 나타낼 수 있습니다. 다운스트림 작업에 대한 지도 학습 중에 BERT는 두 가지 측면에서 GPT와 유사합니다. 첫째, BERT 표현은 모든 토큰에 대한 예측과 전체 시퀀스에 대한 예측과 같은 작업의 성격에 따라 모델 아키텍처를 최소한으로 변경하여 추가된 출력 레이어에 공급됩니다. 둘째, 사전 훈련된 Transformer 인코더의 모든 매개변수가 미세 조정되는 반면, 추가 출력 레이어는 처음부터 훈련됩니다. 그림 15.8.1은 ELMo, GPT, BERT의 차이점을 보여줍니다.

 

Fig. 15.8.1&nbsp; A comparison of ELMo, GPT, and BERT.

 

BERT further improved the state of the art on eleven natural language processing tasks under broad categories of (i) single text classification (e.g., sentiment analysis), (ii) text pair classification (e.g., natural language inference), (iii) question answering, (iv) text tagging (e.g., named entity recognition). All proposed in 2018, from context-sensitive ELMo to task-agnostic GPT and BERT, conceptually simple yet empirically powerful pretraining of deep representations for natural languages have revolutionized solutions to various natural language processing tasks.

 

BERT는 (i) 단일 텍스트 분류(예: 감정 분석), (ii) 텍스트 쌍 분류(예: 자연어 추론), (iii) 질문 응답의 광범위한 범주에서 11가지 자연어 처리 작업에 대한 최신 기술을 더욱 개선했습니다. , (iv) 텍스트 태깅(예: 명명된 개체 인식). context-sensitive ELMo부터 task-agnostic GPT 및 BERT에 이르기까지 2018년에 제안된 모든 것, 개념적으로 단순하지만 경험적으로 강력한 자연어에 대한 심층 표현 사전 학습은 다양한 자연어 처리 작업에 대한 솔루션에 혁명을 일으켰습니다.

 

In the rest of this chapter, we will dive into the pretraining of BERT. When natural language processing applications are explained in Section 16, we will illustrate fine-tuning of BERT for downstream applications.

 

이 장의 나머지 부분에서는 BERT의 사전 훈련에 대해 살펴보겠습니다. 섹션 16에서 자연어 처리 애플리케이션을 설명할 때 다운스트림 애플리케이션을 위한 BERT의 미세 조정을 설명합니다.

 

import torch
from torch import nn
from d2l import torch as d2l

 

15.8.4. Input Representation

 

In natural language processing, some tasks (e.g., sentiment analysis) take single text as input, while in some other tasks (e.g., natural language inference), the input is a pair of text sequences. The BERT input sequence unambiguously represents both single text and text pairs. In the former, the BERT input sequence is the concatenation of the special classification token “<cls>”, tokens of a text sequence, and the special separation token “<sep>”. In the latter, the BERT input sequence is the concatenation of “<cls>”, tokens of the first text sequence, “<sep>”, tokens of the second text sequence, and “<sep>”. We will consistently distinguish the terminology “BERT input sequence” from other types of “sequences”. For instance, one BERT input sequence may include either one text sequence or two text sequences.

 

자연어 처리에서 일부 작업(예: 감정 분석)은 단일 텍스트를 입력으로 사용하는 반면, 일부 다른 작업(예: 자연어 추론)에서는 입력이 텍스트 시퀀스 쌍입니다. BERT 입력 시퀀스는 단일 텍스트와 텍스트 쌍을 모두 명확하게 나타냅니다. 전자의 경우 BERT 입력 시퀀스는 특수 분류 토큰 “<cls>”, 텍스트 시퀀스 토큰 및 특수 분리 토큰 “<sep>”의 연결입니다. 후자의 경우, BERT 입력 시퀀스는 첫 번째 텍스트 시퀀스의 토큰인 "<cls>", 두 번째 텍스트 시퀀스의 토큰인 "<sep>" 및 "<sep>"의 연결입니다. 우리는 "BERT 입력 시퀀스"라는 용어를 다른 유형의 "시퀀스"와 일관되게 구별할 것입니다. 예를 들어, 하나의 BERT 입력 시퀀스에는 하나의 텍스트 시퀀스 또는 두 개의 텍스트 시퀀스가 포함될 수 있습니다.

 

To distinguish text pairs, the learned segment embeddings eA and eB are added to the token embeddings of the first sequence and the second sequence, respectively. For single text inputs, only eA is used.

 

텍스트 쌍을 구별하기 위해 학습된 세그먼트 임베딩 eA 및 eB가 각각 첫 번째 시퀀스와 두 번째 시퀀스의 토큰 임베딩에 추가됩니다. 단일 텍스트 입력의 경우 eA만 사용됩니다.

 

The following get_tokens_and_segments takes either one sentence or two sentences as input, then returns tokens of the BERT input sequence and their corresponding segment IDs.

 

다음 get_tokens_and_segments는 한 문장 또는 두 문장을 입력으로 사용한 다음 BERT 입력 시퀀스의 토큰과 해당 세그먼트 ID를 반환합니다.

 

#@save
def get_tokens_and_segments(tokens_a, tokens_b=None):
    """Get tokens of the BERT input sequence and their segment IDs."""
    tokens = ['<cls>'] + tokens_a + ['<sep>']
    # 0 and 1 are marking segment A and B, respectively
    segments = [0] * (len(tokens_a) + 2)
    if tokens_b is not None:
        tokens += tokens_b + ['<sep>']
        segments += [1] * (len(tokens_b) + 1)
    return tokens, segments

이 함수는 BERT 모델의 입력으로 사용되는 토큰과 세그먼트 ID를 생성하는 과정을 수행합니다. BERT의 입력 형식은 [CLS] - 토큰 A - [SEP] - 토큰 B - [SEP]으로 구성되며, 이때 세그먼트 A는 토큰 A에 대한 세그먼트 ID(0), 세그먼트 B는 토큰 B에 대한 세그먼트 ID(1)를 나타냅니다.

  1. tokens_a: 첫 번째 시퀀스의 토큰 리스트입니다.
  2. tokens_b: 두 번째 시퀀스의 토큰 리스트(선택 사항)입니다.

함수 내용:

  • tokens: BERT 입력 시퀀스에 해당하는 토큰 리스트를 구성합니다. 시퀀스의 처음은 [CLS] 토큰으로 시작하고, 첫 번째 시퀀스의 토큰들을 이어붙인 후 [SEP] 토큰을 추가합니다. 두 번째 시퀀스가 제공되면 해당 시퀀스의 토큰들을 이어붙이고 다시 [SEP] 토큰을 추가합니다.
  • segments: 각 토큰의 세그먼트 ID를 나타내는 리스트를 구성합니다. 세그먼트 A는 토큰 A에 대한 것이므로 길이는 tokens_a의 길이 + 2입니다. 만약 두 번째 시퀀스가 제공되면 해당 시퀀스에 대한 세그먼트 ID를 추가합니다.

이 함수를 통해 토큰과 세그먼트 ID를 생성하여 BERT 입력 형식에 맞게 구성할 수 있습니다.

 

BERT chooses the Transformer encoder as its bidirectional architecture. Common in the Transformer encoder, positional embeddings are added at every position of the BERT input sequence. However, different from the original Transformer encoder, BERT uses learnable positional embeddings. To sum up, Fig. 15.8.2 shows that the embeddings of the BERT input sequence are the sum of the token embeddings, segment embeddings, and positional embeddings.

 

15.8. Bidirectional Encoder Representations from Transformers (BERT) — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

BERT는 양방향 아키텍처로 Transformer 인코더를 선택합니다. Transformer 인코더에서 일반적으로 위치 임베딩은 BERT 입력 시퀀스의 모든 위치에 추가됩니다. 그러나 원래 Transformer 인코더와 달리 BERT는 학습 가능한 위치 임베딩을 사용합니다. 요약하면 그림 15.8.2는 BERT 입력 시퀀스의 임베딩이 토큰 임베딩, 세그먼트 임베딩 및 위치 임베딩의 합임을 보여줍니다.

 

Fig. 15.8.2&nbsp; The embeddings of the BERT input sequence are the sum of the token embeddings, segment embeddings, and positional embeddings.

 

The following BERTEncoder class is similar to the TransformerEncoder class as implemented in Section 11.7. Different from TransformerEncoder, BERTEncoder uses segment embeddings and learnable positional embeddings.

 

다음 BERTEncoder 클래스는 섹션 11.7에 구현된 TransformerEncoder 클래스와 유사합니다. TransformerEncoder와 달리 BERTEncoder는 세그먼트 임베딩과 학습 가능한 위치 임베딩을 사용합니다.

 

#@save
class BERTEncoder(nn.Module):
    """BERT encoder."""
    def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens, num_heads,
                 num_blks, dropout, max_len=1000, **kwargs):
        super(BERTEncoder, self).__init__(**kwargs)
        self.token_embedding = nn.Embedding(vocab_size, num_hiddens)
        self.segment_embedding = nn.Embedding(2, num_hiddens)
        self.blks = nn.Sequential()
        for i in range(num_blks):
            self.blks.add_module(f"{i}", d2l.TransformerEncoderBlock(
                num_hiddens, ffn_num_hiddens, num_heads, dropout, True))
        # In BERT, positional embeddings are learnable, thus we create a
        # parameter of positional embeddings that are long enough
        self.pos_embedding = nn.Parameter(torch.randn(1, max_len,
                                                      num_hiddens))

    def forward(self, tokens, segments, valid_lens):
        # Shape of `X` remains unchanged in the following code snippet:
        # (batch size, max sequence length, `num_hiddens`)
        X = self.token_embedding(tokens) + self.segment_embedding(segments)
        X = X + self.pos_embedding[:, :X.shape[1], :]
        for blk in self.blks:
            X = blk(X, valid_lens)
        return X

이 클래스는 BERT 모델의 인코더 부분을 정의합니다. BERT 모델은 토큰 임베딩, 세그먼트 임베딩, 여러 개의 변환기 블록 및 위치 임베딩을 포함하고 있습니다.

  • vocab_size: 토큰의 종류 수입니다.
  • num_hiddens: 임베딩 차원 및 히든 레이어의 차원입니다.
  • ffn_num_hiddens: Feed-Forward 신경망의 히든 레이어 차원입니다.
  • num_heads: Multi-Head Attention의 헤드 수입니다.
  • num_blks: 변환기 블록의 개수입니다.
  • dropout: 드롭아웃 비율입니다.
  • max_len: 최대 시퀀스 길이입니다.
  • kwargs: 추가 매개변수입니다.

이 클래스는 다음과 같은 요소들로 구성됩니다:

  • token_embedding: 토큰 임베딩 레이어입니다. 입력 토큰을 임베딩 벡터로 변환합니다.
  • segment_embedding: 세그먼트 임베딩 레이어입니다. 입력 세그먼트 정보를 임베딩 벡터로 변환합니다.
  • blks: 여러 개의 변환기 블록을 포함하는 순차적인 레이어입니다.
  • pos_embedding: 위치 임베딩 매개변수로서, BERT에서 위치 임베딩은 학습 가능합니다.

forward 메서드에서는 다음과 같은 작업을 수행합니다:

  1. token_embedding과 segment_embedding을 사용하여 토큰과 세그먼트 임베딩을 생성합니다.
  2. 위치 임베딩을 추가합니다.
  3. blks에 있는 각 변환기 블록을 차례로 통과시킵니다.
  4. 최종 인코딩 결과를 반환합니다.

이를 통해 BERTEncoder 클래스는 BERT의 인코더 부분을 구현하며, 입력 토큰과 세그먼트를 받아서 인코딩된 벡터를 반환합니다.

 

 

Suppose that the vocabulary size is 10000. To demonstrate forward inference of BERTEncoder, let’s create an instance of it and initialize its parameters.

 

어휘 크기가 10000이라고 가정합니다. BERTEncoder의 순방향 추론을 시연하기 위해 인스턴스를 만들고 해당 매개변수를 초기화하겠습니다.

 

vocab_size, num_hiddens, ffn_num_hiddens, num_heads = 10000, 768, 1024, 4
ffn_num_input, num_blks, dropout = 768, 2, 0.2
encoder = BERTEncoder(vocab_size, num_hiddens, ffn_num_hiddens, num_heads,
                      num_blks, dropout)

위의 코드에서 다음과 같은 변수들을 설정하고 있습니다:

  • vocab_size: 어휘 크기로, 모델에서 다루는 토큰의 종류 수입니다.
  • num_hiddens: 임베딩 차원 및 각 변환기 블록의 출력 차원입니다.
  • ffn_num_hiddens: Feed-Forward 신경망의 히든 레이어 차원입니다.
  • num_heads: Multi-Head Attention의 헤드 수입니다.
  • ffn_num_input: Feed-Forward 신경망의 입력 차원으로, 일반적으로 num_hiddens와 동일합니다.
  • num_blks: 변환기 블록의 개수입니다.
  • dropout: 드롭아웃 비율입니다.

그리고 BERTEncoder 클래스의 인스턴스인 encoder를 생성합니다. 이를 통해 BERT 인코더 모델을 정의하고 인코딩 작업을 수행할 수 있게 됩니다. encoder 객체는 위에서 설정한 매개변수들을 바탕으로 BERT 인코더를 생성한 것입니다.

 

We define tokens to be 2 BERT input sequences of length 8, where each token is an index of the vocabulary. The forward inference of BERTEncoder with the input tokens returns the encoded result where each token is represented by a vector whose length is predefined by the hyperparameter num_hiddens. This hyperparameter is usually referred to as the hidden size (number of hidden units) of the Transformer encoder.

 

우리는 토큰을 길이 8의 2개의 BERT 입력 시퀀스로 정의합니다. 여기서 각 토큰은 어휘의 인덱스입니다. 입력 토큰을 사용한 BERTEncoder의 순방향 추론은 각 토큰이 하이퍼파라미터 num_hiddens에 의해 길이가 미리 정의된 벡터로 표현되는 인코딩된 결과를 반환합니다. 이 하이퍼파라미터는 일반적으로 Transformer 인코더의 숨겨진 크기(숨겨진 단위 수)라고 합니다.

 

tokens = torch.randint(0, vocab_size, (2, 8))
segments = torch.tensor([[0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1]])
encoded_X = encoder(tokens, segments, None)
encoded_X.shape

위 코드에서 다음과 같은 작업을 수행하고 있습니다:

  1. tokens와 segments 생성: 임의의 정수로 구성된 tokens와 세그먼트 정보를 생성합니다. tokens는 (2, 8) 모양의 텐서로, 2개의 시퀀스 각각에 8개의 토큰이 포함되어 있습니다. segments는 (2, 8) 모양의 텐서로, 세그먼트 정보를 나타내는데 0은 첫 번째 세그먼트를, 1은 두 번째 세그먼트를 나타냅니다.
  2. 인코딩: encoder 객체를 사용하여 tokens와 segments를 인코딩한 결과를 계산합니다. 이를 encoded_X에 저장합니다. 이 과정은 BERT 인코더의 forward 연산과정을 의미합니다. 입력 토큰과 세그먼트 정보를 통해 인코딩된 특성 행렬을 얻게 됩니다.
  3. 결과 확인: encoded_X의 형태(shape)를 출력하여 인코딩된 결과의 텐서 모양을 확인합니다. 출력된 형태는 인코딩된 특성 행렬의 모양을 나타냅니다.

결과적으로 encoded_X는 인코딩된 특성 행렬로, 첫 번째 차원은 시퀀스의 개수, 두 번째 차원은 시퀀스의 길이, 세 번째 차원은 임베딩 차원(num_hiddens)으로 구성됩니다. 따라서 encoded_X.shape의 결과는 (2, 8, 768)이 됩니다.

torch.Size([2, 8, 768])

 

15.8.5. Pretraining Tasks

 

The forward inference of BERTEncoder gives the BERT representation of each token of the input text and the inserted special tokens “<cls>” and “<seq>”. Next, we will use these representations to compute the loss function for pretraining BERT. The pretraining is composed of the following two tasks: masked language modeling and next sentence prediction.

 

BERTEncoder의 순방향 추론은 입력 텍스트의 각 토큰과 삽입된 특수 토큰 "<cls>" 및 "<seq>"에 대한 BERT representation을 제공합니다. 다음으로 이러한 representations을 사용하여 BERT 사전 학습을 위한 손실 함수를 계산합니다. 사전 훈련은 마스크된 언어 모델링과 다음 문장 예측이라는 두 가지 작업으로 구성됩니다.

 

15.8.5.1. Masked Language Modeling

As illustrated in Section 9.3, a language model predicts a token using the context on its left. To encode context bidirectionally for representing each token, BERT randomly masks tokens and uses tokens from the bidirectional context to predict the masked tokens in a self-supervised fashion. This task is referred to as a masked language model.

 

섹션 9.3에 설명된 대로 언어 모델은 왼쪽의 컨텍스트를 사용하여 토큰을 예측합니다. 각 토큰을 표현하기 위해 컨텍스트를 양방향으로 인코딩하기 위해 BERT는 토큰을 무작위로 마스킹하고 양방향 컨텍스트의 토큰을 사용하여 자체 감독 방식으로 마스킹된 토큰을 예측합니다. 이 작업을 마스크된 언어 모델이라고 합니다.

 

In this pretraining task, 15% of tokens will be selected at random as the masked tokens for prediction. To predict a masked token without cheating by using the label, one straightforward approach is to always replace it with a special “<mask>” token in the BERT input sequence. However, the artificial special token “<mask>” will never appear in fine-tuning. To avoid such a mismatch between pretraining and fine-tuning, if a token is masked for prediction (e.g., “great” is selected to be masked and predicted in “this movie is great”), in the input it will be replaced with:

 

이 사전 훈련 작업에서는 토큰의 15%가 예측을 위한 마스크된 토큰으로 무작위로 선택됩니다. 레이블을 사용하여 부정 행위 없이 마스킹된 토큰을 예측하기 위한 한 가지 간단한 접근 방식은 항상 BERT 입력 시퀀스에서 특수 "<mask>" 토큰으로 바꾸는 것입니다. 그러나 인공 특수 토큰 “<mask>”는 미세 조정에서는 절대 나타나지 않습니다. 사전 훈련과 미세 조정 사이의 불일치를 피하기 위해 토큰이 예측을 위해 마스크된 경우(예: "이 영화는 훌륭합니다"에서 "훌륭함"이 마스크되고 예측되도록 선택됨) 입력에서 다음으로 대체됩니다.

 

  • a special “<mask>” token for 80% of the time (e.g., “this movie is great” becomes “this movie is <mask>”);

    80%의 시간 동안 특수 "<마스크>" 토큰(예: "이 영화는 훌륭합니다"는 "이 영화는 <마스크>입니다"가 됨)

  • a random token for 10% of the time (e.g., “this movie is great” becomes “this movie is drink”);

    10%의 시간에 대한 무작위 토큰(예: "이 영화는 훌륭해요"는 "이 영화는 술입니다"가 됩니다)

  • the unchanged label token for 10% of the time (e.g., “this movie is great” becomes “this movie is great”).

    10%의 시간 동안 변경되지 않은 레이블 토큰(예: "이 영화는 훌륭합니다"는 "이 영화는 훌륭합니다"가 됨)

 

Note that for 10% of 15% time a random token is inserted. This occasional noise encourages BERT to be less biased towards the masked token (especially when the label token remains unchanged) in its bidirectional context encoding.

 

15% 시간 중 10% 동안 무작위 토큰이 삽입된다는 점에 유의하세요. 이러한 간헐적인 노이즈는 BERT가 양방향 컨텍스트 인코딩에서 마스크된 토큰(특히 레이블 토큰이 변경되지 않은 상태로 유지되는 경우)에 덜 편향되도록 장려합니다.

 

We implement the following MaskLM class to predict masked tokens in the masked language model task of BERT pretraining. The prediction uses a one-hidden-layer MLP (self.mlp). In forward inference, it takes two inputs: the encoded result of BERTEncoder and the token positions for prediction. The output is the prediction results at these positions.

 

BERT 사전 학습의 마스크된 언어 모델 작업에서 마스크된 토큰을 예측하기 위해 다음 MaskLM 클래스를 구현합니다. 예측은 단일 숨겨진 레이어 MLP(self.mlp)를 사용합니다. 순방향 추론에서는 BERTEncoder의 인코딩된 결과와 예측을 위한 토큰 위치라는 두 가지 입력을 사용합니다. 출력은 이러한 위치에서의 예측 결과입니다.

 

#@save
class MaskLM(nn.Module):
    """The masked language model task of BERT."""
    def __init__(self, vocab_size, num_hiddens, **kwargs):
        super(MaskLM, self).__init__(**kwargs)
        self.mlp = nn.Sequential(nn.LazyLinear(num_hiddens),
                                 nn.ReLU(),
                                 nn.LayerNorm(num_hiddens),
                                 nn.LazyLinear(vocab_size))

    def forward(self, X, pred_positions):
        num_pred_positions = pred_positions.shape[1]
        pred_positions = pred_positions.reshape(-1)
        batch_size = X.shape[0]
        batch_idx = torch.arange(0, batch_size)
        # Suppose that `batch_size` = 2, `num_pred_positions` = 3, then
        # `batch_idx` is `torch.tensor([0, 0, 0, 1, 1, 1])`
        batch_idx = torch.repeat_interleave(batch_idx, num_pred_positions)
        masked_X = X[batch_idx, pred_positions]
        masked_X = masked_X.reshape((batch_size, num_pred_positions, -1))
        mlm_Y_hat = self.mlp(masked_X)
        return mlm_Y_hat

위의 코드에서 수행되는 작업은 다음과 같습니다:

  1. 초기화 메서드(__init__): MaskLM 클래스의 초기화 메서드에서는 MLM 작업을 위한 신경망 구조를 정의합니다. num_hiddens는 은닉 유닛의 수, vocab_size는 어휘 크기를 나타냅니다.
  2. Forward 메서드(forward): 이 메서드는 BERT의 마스킹된 언어 모델 작업을 수행합니다. 입력으로 주어진 X는 BERT 인코더의 출력입니다. pred_positions는 마스킹된 위치를 나타내는 텐서로, 예측해야 할 위치에 대한 정보를 가지고 있습니다.
    • pred_positions의 형태(shape)를 조정하여 마스크된 위치 정보를 준비합니다.
    • 마스크된 위치에 해당하는 특성만 추출하여 masked_X를 생성합니다.
    • masked_X를 MLP 모델(self.mlp)에 통과시켜 마스킹된 언어 모델의 예측값을 계산합니다.

즉, 이 모듈은 인코더의 출력을 받아서 마스크된 위치의 특성을 추출하고, 이를 통해 마스킹된 언어 모델 작업을 수행하여 단어 예측을 수행합니다.

 

To demonstrate the forward inference of MaskLM, we create its instance mlm and initialize it. Recall that encoded_X from the forward inference of BERTEncoder represents 2 BERT input sequences. We define mlm_positions as the 3 indices to predict in either BERT input sequence of encoded_X. The forward inference of mlm returns prediction results mlm_Y_hat at all the masked positions mlm_positions of encoded_X. For each prediction, the size of the result is equal to the vocabulary size.

 

MaskLM의 순방향 추론을 보여주기 위해 인스턴스 mlm을 생성하고 초기화합니다. BERTEncoder의 순방향 추론에서 나온 Encoded_X는 2개의 BERT 입력 시퀀스를 나타냅니다. 우리는 mlm_positions를 Encoded_X의 BERT 입력 시퀀스에서 예측할 3개의 인덱스로 정의합니다. mlm의 순방향 추론은 Encoded_X의 모든 마스크 위치 mlm_positions에서 예측 결과 mlm_Y_hat를 반환합니다. 각 예측에 대해 결과의 크기는 어휘 크기와 같습니다.

 

mlm = MaskLM(vocab_size, num_hiddens)
mlm_positions = torch.tensor([[1, 5, 2], [6, 1, 5]])
mlm_Y_hat = mlm(encoded_X, mlm_positions)
mlm_Y_hat.shape

위의 코드에서 수행되는 작업은 다음과 같습니다:

  1. MaskLM 모델 생성: MaskLM 클래스로부터 MLM 모델을 생성합니다. vocab_size는 어휘 크기, num_hiddens는 은닉 유닛의 수입니다.
  2. 마스크된 위치 정보 생성: mlm_positions 텐서는 마스킹된 위치 정보를 나타내며, 이 정보를 통해 모델은 어떤 위치의 단어를 예측할지 결정합니다.
  3. MLM 모델 적용: 생성한 mlm 모델에 인코딩된 입력 encoded_X와 마스크된 위치 정보 mlm_positions를 전달하여 마스킹된 언어 모델 작업을 수행합니다. 모델은 이 위치에서 어떤 단어가 들어갈지 예측한 결과를 반환합니다.
  4. 결과 확인: mlm_Y_hat은 마스킹된 언어 모델의 예측 결과입니다. 이 결과의 형태(shape)를 확인하면 마스크된 위치의 단어 예측에 대한 확률값 분포를 확인할 수 있습니다.

즉, 위의 코드는 MaskLM 모델을 사용하여 BERT의 마스킹된 언어 모델 작업을 수행하고, 예측 결과의 형태(shape)를 출력하는 예제입니다

torch.Size([6])

 

15.8.5.2. Next Sentence Prediction

 

Although masked language modeling is able to encode bidirectional context for representing words, it does not explicitly model the logical relationship between text pairs. To help understand the relationship between two text sequences, BERT considers a binary classification task, next sentence prediction, in its pretraining. When generating sentence pairs for pretraining, for half of the time they are indeed consecutive sentences with the label “True”; while for the other half of the time the second sentence is randomly sampled from the corpus with the label “False”.

 

마스킹된 언어 모델링은 단어를 표현하기 위해 양방향 컨텍스트를 인코딩할 수 있지만 텍스트 쌍 간의 논리적 관계를 명시적으로 모델링하지는 않습니다. 두 텍스트 시퀀스 간의 관계를 이해하는 데 도움을 주기 위해 BERT는 사전 학습에서 이진 분류 작업, 다음 문장 예측을 고려합니다. 사전 학습을 위해 문장 쌍을 생성할 때 절반의 시간 동안 실제로는 "True"라는 레이블이 붙은 연속 문장입니다. 나머지 절반 동안은 "False"라는 라벨이 붙은 코퍼스에서 두 번째 문장이 무작위로 샘플링됩니다.

 

The following NextSentencePred class uses a one-hidden-layer MLP to predict whether the second sentence is the next sentence of the first in the BERT input sequence. Due to self-attention in the Transformer encoder, the BERT representation of the special token “<cls>” encodes both the two sentences from the input. Hence, the output layer (self.output) of the MLP classifier takes X as input, where X is the output of the MLP hidden layer whose input is the encoded “<cls>” token.

 

다음 NextSentencePred 클래스는 단일 숨겨진 레이어 MLP를 사용하여 두 번째 문장이 BERT 입력 시퀀스에서 첫 번째 문장의 다음 문장인지 예측합니다. Transformer 인코더의 self-attention으로 인해 특수 토큰 “<cls>”의 BERT 표현은 입력의 두 문장을 모두 인코딩합니다. 따라서 MLP 분류기의 출력 계층(self.output)은 X를 입력으로 사용합니다. 여기서 X는 입력이 인코딩된 "<cls>" 토큰인 MLP 숨겨진 계층의 출력입니다.

 

#@save
class NextSentencePred(nn.Module):
    """The next sentence prediction task of BERT."""
    def __init__(self, **kwargs):
        super(NextSentencePred, self).__init__(**kwargs)
        self.output = nn.LazyLinear(2)

    def forward(self, X):
        # `X` shape: (batch size, `num_hiddens`)
        return self.output(X)

위의 코드에서 수행되는 작업은 다음과 같습니다:

  1. NextSentencePred 모듈 생성: NextSentencePred 클래스로부터 다음 문장 예측 작업을 수행하는 모듈을 생성합니다.
  2. 모듈 구성: 모듈 내부에는 출력을 생성하는 레이어인 nn.LazyLinear이 정의되어 있습니다. nn.LazyLinear(2)는 출력 레이어를 생성하며, 이 레이어는 2개의 출력 유닛을 가집니다. 이것은 다음 문장이 관련성이 있을지 없을지에 대한 이진 예측을 수행하기 위한 레이어입니다.
  3. Forward 연산: forward 함수는 주어진 입력 X를 받아 출력을 계산합니다. X의 형태는 (배치 크기, num_hiddens)입니다. 입력 X를 출력 레이어에 전달하여 다음 문장 예측 작업을 수행하고 결과를 반환합니다.

위의 코드는 BERT의 다음 문장 예측 작업을 위한 모듈인 NextSentencePred를 정의하는 예제입니다.

 

We can see that the forward inference of an NextSentencePred instance returns binary predictions for each BERT input sequence.

 

NextSentencePred 인스턴스의 순방향 추론이 각 BERT 입력 시퀀스에 대해 이진 예측을 반환하는 것을 볼 수 있습니다.

 

# PyTorch by default will not flatten the tensor as seen in mxnet where, if
# flatten=True, all but the first axis of input data are collapsed together
encoded_X = torch.flatten(encoded_X, start_dim=1)
# input_shape for NSP: (batch size, `num_hiddens`)
nsp = NextSentencePred()
nsp_Y_hat = nsp(encoded_X)
nsp_Y_hat.shape

위의 코드에서 수행되는 작업은 다음과 같습니다:

  1. encoded_X 펼치기: torch.flatten 함수를 사용하여 encoded_X 텐서를 펼치는 작업을 수행합니다. encoded_X는 이전 단계에서 인코딩된 BERT 출력 텐서로, 차원을 펼쳐서 2차원으로 만듭니다. start_dim=1 인자는 펼치는 시작 차원을 지정합니다.
  2. NextSentencePred 모듈 생성: 다음 문장 예측 작업을 위한 NextSentencePred 모듈을 생성합니다.
  3. Forward 연산: 생성된 NextSentencePred 모듈에 펼쳐진 encoded_X를 입력으로 전달하여 다음 문장 예측 작업을 수행하고, 결과 nsp_Y_hat을 얻습니다.
  4. 결과 확인: nsp_Y_hat의 형태(shape)를 확인하여 다음 문장 예측 작업의 결과를 파악합니다. 결과 형태는 (batch size, 2)입니다. 두 개의 출력 유닛은 각각 두 가지 다른 클래스(다음 문장이 관련성이 있을 경우 1, 없을 경우 0)에 대한 확률 예측을 나타냅니다.

위의 코드는 BERT의 다음 문장 예측 작업을 수행하는 예제로, 인코딩된 텍스트 데이터를 입력으로 하여 다음 문장이 관련성이 있을지 없을지를 예측하는 모듈을 생성하고 결과를 확인하는 과정을 보여줍니다

torch.Size([2, 2])

The cross-entropy loss of the 2 binary classifications can also be computed.

 

2개의 이진 분류의 교차 엔트로피 손실도 계산할 수 있습니다.

 

nsp_y = torch.tensor([0, 1])
nsp_l = loss(nsp_Y_hat, nsp_y)
nsp_l.shape

위의 코드에서 수행되는 작업은 다음과 같습니다:

  1. nsp_y 정의: 다음 문장 예측 작업에서 실제 정답 레이블을 나타내는 텐서 nsp_y를 정의합니다. 이 예제에서는 두 개의 미니배치 샘플에 대한 정답 레이블을 나타냅니다. 첫 번째 샘플은 관련성이 없는 경우(0), 두 번째 샘플은 관련성이 있는 경우(1)를 나타냅니다.
  2. 손실 계산: 앞서 정의한 nsp_Y_hat 텐서와 nsp_y 정답 레이블을 사용하여 다음 문장 예측 작업의 손실을 계산합니다. 이를 위해 사용하는 손실 함수인 loss는 코드에는 나타나지 않았지만, BERT의 다음 문장 예측 작업에서는 일반적으로 교차 엔트로피(Cross Entropy) 손실이 사용됩니다.
  3. 손실 형태 확인: nsp_l의 형태(shape)를 확인하여 계산된 손실의 형태를 파악합니다. 이 경우 nsp_l은 스칼라값입니다. 미니배치 내 각 샘플에 대한 손실을 합한 결과를 나타냅니다.

위의 코드는 BERT의 다음 문장 예측 작업에서 손실을 계산하는 과정을 보여주는 예제입니다.

torch.Size([2])

 

It is noteworthy that all the labels in both the aforementioned pretraining tasks can be trivially obtained from the pretraining corpus without manual labeling effort. The original BERT has been pretrained on the concatenation of BookCorpus (Zhu et al., 2015) and English Wikipedia. These two text corpora are huge: they have 800 million words and 2.5 billion words, respectively.

 

앞서 언급한 사전 훈련 작업의 모든 레이블은 수동 레이블 지정 작업 없이 사전 훈련 코퍼스에서 쉽게 얻을 수 있다는 점은 주목할 만합니다. 원래 BERT는 BookCorpus(Zhu et al., 2015)와 English Wikipedia의 연결을 통해 사전 학습되었습니다. 이 두 개의 텍스트 말뭉치에는 각각 8억 단어와 25억 단어가 있습니다.

 

15.8.6. Putting It All Together

When pretraining BERT, the final loss function is a linear combination of both the loss functions for masked language modeling and next sentence prediction. Now we can define the BERTModel class by instantiating the three classes BERTEncoder, MaskLM, and NextSentencePred. The forward inference returns the encoded BERT representations encoded_X, predictions of masked language modeling mlm_Y_hat, and next sentence predictions nsp_Y_hat.

 

BERT를 사전 훈련할 때 최종 손실 함수는 마스크된 언어 모델링과 다음 문장 예측을 위한 손실 함수의 선형 조합입니다. 이제 BERTEncoder, MaskLM 및 NextSentencePred 세 클래스를 인스턴스화하여 BERTModel 클래스를 정의할 수 있습니다. 순방향 추론은 인코딩된 BERT 표현 encode_X, 마스크된 언어 모델링 mlm_Y_hat의 예측 및 다음 문장 예측 nsp_Y_hat을 반환합니다.

 

#@save
class BERTModel(nn.Module):
    """The BERT model."""
    def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens,
                 num_heads, num_blks, dropout, max_len=1000):
        super(BERTModel, self).__init__()
        self.encoder = BERTEncoder(vocab_size, num_hiddens, ffn_num_hiddens,
                                   num_heads, num_blks, dropout,
                                   max_len=max_len)
        self.hidden = nn.Sequential(nn.LazyLinear(num_hiddens),
                                    nn.Tanh())
        self.mlm = MaskLM(vocab_size, num_hiddens)
        self.nsp = NextSentencePred()

    def forward(self, tokens, segments, valid_lens=None, pred_positions=None):
        encoded_X = self.encoder(tokens, segments, valid_lens)
        if pred_positions is not None:
            mlm_Y_hat = self.mlm(encoded_X, pred_positions)
        else:
            mlm_Y_hat = None
        # The hidden layer of the MLP classifier for next sentence prediction.
        # 0 is the index of the '<cls>' token
        nsp_Y_hat = self.nsp(self.hidden(encoded_X[:, 0, :]))
        return encoded_X, mlm_Y_hat, nsp_Y_hat

위의 코드에서 수행되는 작업은 다음과 같습니다:

  1. nsp_y 정의: 다음 문장 예측 작업에서 실제 정답 레이블을 나타내는 텐서 nsp_y를 정의합니다. 이 예제에서는 두 개의 미니배치 샘플에 대한 정답 레이블을 나타냅니다. 첫 번째 샘플은 관련성이 없는 경우(0), 두 번째 샘플은 관련성이 있는 경우(1)를 나타냅니다.
  2. 손실 계산: 앞서 정의한 nsp_Y_hat 텐서와 nsp_y 정답 레이블을 사용하여 다음 문장 예측 작업의 손실을 계산합니다. 이를 위해 사용하는 손실 함수인 loss는 코드에는 나타나지 않았지만, BERT의 다음 문장 예측 작업에서는 일반적으로 교차 엔트로피(Cross Entropy) 손실이 사용됩니다.
  3. 손실 형태 확인: nsp_l의 형태(shape)를 확인하여 계산된 손실의 형태를 파악합니다. 이 경우 nsp_l은 스칼라값입니다. 미니배치 내 각 샘플에 대한 손실을 합한 결과를 나타냅니다.

위의 코드는 BERT의 다음 문장 예측 작업에서 손실을 계산하는 과정을 보여주는 예제입니다.

15.8.7. Summary

  • Word embedding models such as word2vec and GloVe are context-independent. They assign the same pretrained vector to the same word regardless of the context of the word (if any). It is hard for them to handle well polysemy or complex semantics in natural languages.

    word2vec 및 GloVe와 같은 단어 임베딩 모델은 상황에 독립적입니다. 단어의 맥락(있는 경우)에 관계없이 동일한 사전 훈련된 벡터를 동일한 단어에 할당합니다. 자연어에서는 다의어나 복잡한 의미를 잘 다루기가 어렵습니다.

  • For context-sensitive word representations such as ELMo and GPT, representations of words depend on their contexts.

    ELMo 및 GPT와 같은 상황에 맞는 단어 표현의 경우 단어 표현은 해당 상황에 따라 달라집니다.

  • ELMo encodes context bidirectionally but uses task-specific architectures (however, it is practically non-trivial to craft a specific architecture for every natural language processing task); while GPT is task-agnostic but encodes context left-to-right.

    ELMo는 컨텍스트를 양방향으로 인코딩하지만 작업별 아키텍처를 사용합니다(그러나 모든 자연어 처리 작업에 대해 특정 아키텍처를 만드는 것은 사실상 쉽지 않습니다). GPT는 작업에 구애받지 않지만 컨텍스트를 왼쪽에서 오른쪽으로 인코딩합니다.

  • BERT combines the best of both worlds: it encodes context bidirectionally and requires minimal architecture changes for a wide range of natural language processing tasks.

    BERT는 두 가지 장점을 결합합니다. 즉, 컨텍스트를 양방향으로 인코딩하고 광범위한 자연어 처리 작업에 대해 최소한의 아키텍처 변경이 필요합니다.

  • The embeddings of the BERT input sequence are the sum of the token embeddings, segment embeddings, and positional embeddings.

    BERT 입력 시퀀스의 임베딩은 토큰 임베딩, 세그먼트 임베딩 및 위치 임베딩의 합계입니다.

  • Pretraining BERT is composed of two tasks: masked language modeling and next sentence prediction. The former is able to encode bidirectional context for representing words, while the latter explicitly models the logical relationship between text pairs.

    사전 훈련 BERT는 마스크된 언어 모델링과 다음 문장 예측이라는 두 가지 작업으로 구성됩니다. 전자는 단어를 표현하기 위해 양방향 컨텍스트를 인코딩할 수 있는 반면, 후자는 텍스트 쌍 간의 논리적 관계를 명시적으로 모델링합니다.

https://www.youtube.com/live/QCOT5D7Pa7s?si=BfY_2QMLWpFqvm9T 

15.8.8. Exercises

  1. All other things being equal, will a masked language model require more or fewer pretraining steps to converge than a left-to-right language model? Why?
  2. In the original implementation of BERT, the positionwise feed-forward network in BERTEncoder (via d2l.TransformerEncoderBlock) and the fully connected layer in MaskLM both use the Gaussian error linear unit (GELU) (Hendrycks and Gimpel, 2016) as the activation function. Research into the difference between GELU and ReLU.

 

반응형


반응형

15.7. Word Similarity and Analogy — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

15.7. Word Similarity and Analogy — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

15.7. Word Similarity and Analogy

 

In Section 15.4, we trained a word2vec model on a small dataset, and applied it to find semantically similar words for an input word. In practice, word vectors that are pretrained on large corpora can be applied to downstream natural language processing tasks, which will be covered later in Section 16. To demonstrate semantics of pretrained word vectors from large corpora in a straightforward way, let’s apply them in the word similarity and analogy tasks.

 

섹션 15.4에서는 작은 데이터 세트에 대해 word2vec 모델을 훈련하고 이를 적용하여 입력 단어에 대해 의미상 유사한 단어를 찾았습니다. 실제로 큰 말뭉치에서 사전 훈련된 단어 벡터는 다운스트림 자연어 처리 작업에 적용될 수 있으며 이에 대해서는 섹션 16에서 나중에 다룰 것입니다. 큰 말뭉치에서 사전 훈련된 단어 벡터의 의미를 간단한 방법으로 보여주기 위해 이것들을 유사성 및 비유 작업이라는 단어에 적용해 보겠습니다.

 

import os
import torch
from torch import nn
from d2l import torch as d2l

 

15.7.1. Loading Pretrained Word Vectors

Below lists pretrained GloVe embeddings of dimension 50, 100, and 300, which can be downloaded from the GloVe website. The pretrained fastText embeddings are available in multiple languages. Here we consider one English version (300-dimensional “wiki.en”) that can be downloaded from the fastText website.

 

아래에는 GloVe 웹사이트에서 다운로드할 수 있는 차원 50, 100, 300의 사전 훈련된 GloVe 임베딩이 나열되어 있습니다. 사전 훈련된 fastText 임베딩은 여러 언어로 제공됩니다. 여기에서는 fastText 웹사이트에서 다운로드할 수 있는 하나의 영어 버전(300차원 “wiki.en”)을 고려합니다.

 

#@save
d2l.DATA_HUB['glove.6b.50d'] = (d2l.DATA_URL + 'glove.6B.50d.zip',
                                '0b8703943ccdb6eb788e6f091b8946e82231bc4d')

#@save
d2l.DATA_HUB['glove.6b.100d'] = (d2l.DATA_URL + 'glove.6B.100d.zip',
                                 'cd43bfb07e44e6f27cbcc7bc9ae3d80284fdaf5a')

#@save
d2l.DATA_HUB['glove.42b.300d'] = (d2l.DATA_URL + 'glove.42B.300d.zip',
                                  'b5116e234e9eb9076672cfeabf5469f3eec904fa')

#@save
d2l.DATA_HUB['wiki.en'] = (d2l.DATA_URL + 'wiki.en.zip',
                           'c1816da3821ae9f43899be655002f6c723e91b88')

이 코드는 다양한 단어 임베딩 데이터셋에 대한 정보를 d2l.DATA_HUB 딕셔너리에 저장하는 역할을 합니다. 이 데이터셋들은 GloVe(글로브)와 위키백과 데이터의 영어 언어 버전입니다.

  1. d2l.DATA_HUB['glove.6b.50d']:
    • 'glove.6b.50d' 데이터셋의 정보를 저장합니다.
    • 데이터의 다운로드 경로와 해시값이 튜플로 저장됩니다.
  2. d2l.DATA_HUB['glove.6b.100d']:
    • 'glove.6b.100d' 데이터셋의 정보를 저장합니다.
  3. d2l.DATA_HUB['glove.42b.300d']:
    • 'glove.42b.300d' 데이터셋의 정보를 저장합니다.
  4. d2l.DATA_HUB['wiki.en']:
    • 'wiki.en' 데이터셋의 정보를 저장합니다.

이 코드는 데이터셋의 이름과 다운로드 경로, 해시값을 d2l.DATA_HUB 딕셔너리에 저장하여 데이터를 관리하는 데 사용됩니다.

 

 

To load these pretrained GloVe and fastText embeddings, we define the following TokenEmbedding class.

 

사전 학습된 GloVe 및 fastText 임베딩을 로드하기 위해 다음 TokenEmbedding 클래스를 정의합니다.

 

#@save
class TokenEmbedding:
    """Token Embedding."""
    def __init__(self, embedding_name):
        self.idx_to_token, self.idx_to_vec = self._load_embedding(
            embedding_name)
        self.unknown_idx = 0
        self.token_to_idx = {token: idx for idx, token in
                             enumerate(self.idx_to_token)}

    def _load_embedding(self, embedding_name):
        idx_to_token, idx_to_vec = ['<unk>'], []
        data_dir = d2l.download_extract(embedding_name)
        # GloVe website: https://nlp.stanford.edu/projects/glove/
        # fastText website: https://fasttext.cc/
        with open(os.path.join(data_dir, 'vec.txt'), 'r') as f:
            for line in f:
                elems = line.rstrip().split(' ')
                token, elems = elems[0], [float(elem) for elem in elems[1:]]
                # Skip header information, such as the top row in fastText
                if len(elems) > 1:
                    idx_to_token.append(token)
                    idx_to_vec.append(elems)
        idx_to_vec = [[0] * len(idx_to_vec[0])] + idx_to_vec
        return idx_to_token, torch.tensor(idx_to_vec)

    def __getitem__(self, tokens):
        indices = [self.token_to_idx.get(token, self.unknown_idx)
                   for token in tokens]
        vecs = self.idx_to_vec[torch.tensor(indices)]
        return vecs

    def __len__(self):
        return len(self.idx_to_token)

이 코드는 토큰 임베딩(Token Embedding)을 관리하는 TokenEmbedding 클래스를 정의합니다.

  1. class TokenEmbedding::
    • TokenEmbedding 클래스를 정의합니다.
  2. def __init__(self, embedding_name)::
    • 클래스의 초기화 함수입니다. embedding_name을 받아 해당 이름의 임베딩 데이터를 로드하고 초기화합니다.
    • idx_to_token, idx_to_vec, unknown_idx, token_to_idx 등의 속성을 초기화합니다.
  3. def _load_embedding(self, embedding_name)::
    • 임베딩 데이터를 로드하는 내부 함수입니다.
    • 주어진 embedding_name을 이용하여 해당 임베딩 데이터를 다운로드하고 읽어들입니다.
    • 임베딩 데이터의 내용을 idx_to_token, idx_to_vec 형태로 저장합니다.
  4. def __getitem__(self, tokens)::
    • 특정 토큰들에 대한 임베딩 벡터를 반환하는 함수입니다.
    • 토큰들을 인덱스로 변환한 후, 해당 인덱스에 해당하는 임베딩 벡터를 반환합니다.
  5. def __len__(self)::
    • 토큰의 개수를 반환하는 함수입니다.

이 클래스는 주어진 임베딩 데이터 이름에 따라 토큰 임베딩을 생성하고 관리하는 역할을 합니다.

 

Below we load the 50-dimensional GloVe embeddings (pretrained on a Wikipedia subset). When creating the TokenEmbedding instance, the specified embedding file has to be downloaded if it was not yet.

 

아래에서는 50차원 GloVe 임베딩(Wikipedia 하위 집합에서 사전 훈련됨)을 로드합니다. TokenEmbedding 인스턴스를 생성할 때 지정된 임베딩 파일이 아직 다운로드되지 않은 경우 다운로드해야 합니다.

 

glove_6b50d = TokenEmbedding('glove.6b.50d')

이 코드는 TokenEmbedding 클래스를 사용하여 'glove.6b.50d' 임베딩 데이터를 로드하고, glove_6b50d 객체를 생성하는 과정을 나타내고 있습니다.

  1. glove_6b50d = TokenEmbedding('glove.6b.50d'):
    • 'glove.6b.50d'라는 임베딩 데이터셋 이름을 가지고 TokenEmbedding 클래스의 객체 glove_6b50d를 생성합니다.
    • 이 객체는 'glove.6b.50d' 데이터셋의 토큰 임베딩을 관리하는 역할을 합니다.

이 코드는 'glove.6b.50d' 데이터셋의 토큰 임베딩을 TokenEmbedding 클래스를 사용하여 로드하고, 해당 임베딩을 관리하기 위한 객체를 생성하는 과정을 나타냅니다

Downloading ../data/glove.6B.50d.zip from http://d2l-data.s3-accelerate.amazonaws.com/glove.6B.50d.zip...

 

Output the vocabulary size. The vocabulary contains 400000 words (tokens) and a special unknown token.

 

어휘 크기를 출력합니다. 어휘에는 400,000개의 단어(토큰)와 특별한 알려지지 않은 토큰이 포함되어 있습니다.

 

len(glove_6b50d)
400001

 

We can get the index of a word in the vocabulary, and vice versa.

 

어휘에서 단어의 색인을 얻을 수 있고 그 반대의 경우도 마찬가지입니다.

 

glove_6b50d.token_to_idx['beautiful'], glove_6b50d.idx_to_token[3367]

이 코드는 'glove.6b.50d' 임베딩 데이터셋에서 'beautiful' 토큰의 인덱스와 인덱스 3367에 해당하는 토큰을 조회하는 과정을 나타내고 있습니다.

  1. glove_6b50d.token_to_idx['beautiful']:
    • 'beautiful' 토큰의 인덱스를 glove_6b50d 객체의 token_to_idx 속성을 사용하여 조회합니다.
    • 이 결과는 해당 토큰의 임베딩 벡터를 찾을 때 사용될 인덱스입니다.
  2. glove_6b50d.idx_to_token[3367]:
    • 인덱스 3367에 해당하는 토큰을 glove_6b50d 객체의 idx_to_token 속성을 사용하여 조회합니다.
    • 이 결과는 해당 인덱스가 어떤 토큰을 나타내는지를 알려줍니다.

이 코드는 'glove.6b.50d' 임베딩 데이터셋에서 'beautiful' 토큰의 인덱스를 조회하고, 인덱스 3367에 해당하는 토큰을 찾는 과정을 나타냅니다.

(3367, 'beautiful')

 

15.7.2. Applying Pretrained Word Vectors

Using the loaded GloVe vectors, we will demonstrate their semantics by applying them in the following word similarity and analogy tasks.

 

로드된 GloVe 벡터를 사용하여 다음 단어 유사성 및 유추 작업에 적용하여 의미를 보여줍니다.

 

15.7.2.1. Word Similarity

Similar to Section 15.4.3, in order to find semantically similar words for an input word based on cosine similarities between word vectors, we implement the following knn (k-nearest neighbors) function.

 

15.4.3절과 마찬가지로 단어 벡터 간의 코사인 유사성을 기반으로 입력 단어에 대해 의미상 유사한 단어를 찾기 위해 다음 knn(k-최근접 이웃) 함수를 구현합니다.

 

def knn(W, x, k):
    # Add 1e-9 for numerical stability
    cos = torch.mv(W, x.reshape(-1,)) / (
        torch.sqrt(torch.sum(W * W, axis=1) + 1e-9) *
        torch.sqrt((x * x).sum()))
    _, topk = torch.topk(cos, k=k)
    return topk, [cos[int(i)] for i in topk]

이 코드는 K 최근접 이웃 (K Nearest Neighbors, KNN) 알고리즘을 구현한 함수를 정의합니다.

  1. def knn(W, x, k)::
    • knn 함수를 정의합니다. 함수는 임베딩 벡터들의 유사도를 계산하고, 가장 유사한 상위 k개의 인덱스와 유사도를 반환합니다.
    • 함수는 세 개의 인자를 받습니다:
      • W: 임베딩 벡터들을 포함한 행렬입니다.
      • x: 주어진 임베딩 벡터입니다.
      • k: 찾고자 하는 최근접 이웃의 개수입니다.
  2. cos = torch.mv(W, x.reshape(-1,)) / (...):
    • 주어진 임베딩 벡터 x와 모든 임베딩 벡터들을 행렬 W와 내적하여 코사인 유사도를 계산합니다.
    • 수치 안정성을 위해 분모에 작은 값 1e-9를 더해줍니다.
  3. _, topk = torch.topk(cos, k=k):
    • 계산한 코사인 유사도 cos에서 가장 큰 k개의 값과 해당 값의 인덱스를 반환합니다.
  4. return topk, [cos[int(i)] for i in topk]:
    • 상위 k개의 인덱스와 해당 인덱스에 대한 코사인 유사도 값을 반환합니다.

이 함수는 주어진 임베딩 벡터와 행렬에서 K 최근접 이웃 알고리즘을 사용하여 가장 유사한 상위 k개의 임베딩 벡터 인덱스와 그에 해당하는 코사인 유사도 값을 찾아 반환합니다.

 

 

Then, we search for similar words using the pretrained word vectors from the TokenEmbedding instance embed.

 

그런 다음 TokenEmbedding 인스턴스 삽입에서 사전 학습된 단어 벡터를 사용하여 유사한 단어를 검색합니다.

 

def get_similar_tokens(query_token, k, embed):
    topk, cos = knn(embed.idx_to_vec, embed[[query_token]], k + 1)
    for i, c in zip(topk[1:], cos[1:]):  # Exclude the input word
        print(f'cosine sim={float(c):.3f}: {embed.idx_to_token[int(i)]}')

이 코드는 주어진 토큰에 대해 유사한 상위 k개의 토큰을 검색하여 출력하는 함수를 정의합니다.

  1. def get_similar_tokens(query_token, k, embed)::
    • get_similar_tokens 함수를 정의합니다. 함수는 주어진 토큰에 대해 유사한 상위 k개의 토큰을 검색하여 출력합니다.
    • 함수는 세 개의 인자를 받습니다:
      • query_token: 유사한 토큰을 검색하고자 하는 입력 토큰입니다.
      • k: 반환할 유사한 토큰의 개수입니다.
      • embed: 임베딩을 관리하는 TokenEmbedding 객체입니다.
  2. topk, cos = knn(embed.idx_to_vec, embed[[query_token]], k + 1):
    • 주어진 입력 토큰에 대한 임베딩 벡터를 embed에서 조회하고, K 최근접 이웃 알고리즘인 knn 함수를 사용하여 유사한 상위 k개의 인덱스와 코사인 유사도 값을 가져옵니다.
    • k + 1을 사용하여 입력 토큰 자체를 제외하고 유사한 토큰을 가져옵니다.
  3. for i, c in zip(topk[1:], cos[1:])::
    • 유사한 토큰의 인덱스와 해당 토큰에 대한 코사인 유사도 값을 반복적으로 순회합니다.
    • topk[1:]와 cos[1:]를 사용하여 입력 토큰을 제외하고 순회합니다.
  4. print(f'cosine sim={float(c):.3f}: {embed.idx_to_token[int(i)]}'):
    • 각 유사한 토큰에 대해 코사인 유사도 값을 출력합니다.
    • float(c)를 사용하여 코사인 유사도 값을 소수점 세 자리로 출력하고, 해당 인덱스에 대응하는 토큰을 embed.idx_to_token을 사용하여 출력합니다.

이 함수는 주어진 입력 토큰에 대해 유사한 상위 k개의 토큰과 해당 토큰에 대한 코사인 유사도 값을 출력합니다.

 

 

The vocabulary of the pretrained word vectors in glove_6b50d contains 400000 words and a special unknown token. Excluding the input word and unknown token, among this vocabulary let’s find three most semantically similar words to word “chip”.

 

Glove_6b50d의 사전 훈련된 단어 벡터의 어휘에는 400,000개의 단어와 특별한 알려지지 않은 토큰이 포함되어 있습니다. 입력 단어와 알 수 없는 토큰을 제외하고 이 어휘 중에서 "chip"이라는 단어와 의미상 가장 유사한 단어 3개를 찾아보겠습니다.

 

get_similar_tokens('chip', 3, glove_6b50d)

이 코드는 'glove.6b.50d' 임베딩 데이터셋을 사용하여 'chip' 토큰과 유사한 상위 3개의 토큰을 검색하여 출력하는 과정을 나타내고 있습니다.

  1. get_similar_tokens('chip', 3, glove_6b50d):
    • get_similar_tokens 함수를 호출하여 'chip' 토큰과 유사한 상위 3개의 토큰을 검색하고 출력합니다.
    • 'chip'은 검색하고자 하는 입력 토큰이며, 3은 반환할 유사한 토큰의 개수입니다.
    • glove_6b50d는 'glove.6b.50d' 임베딩 데이터셋을 나타내는 TokenEmbedding 객체입니다.

함수는 'chip' 토큰과 유사한 상위 3개의 토큰을 검색하고, 각 토큰에 대한 코사인 유사도 값을 출력합니다

cosine sim=0.856: chips
cosine sim=0.749: intel
cosine sim=0.749: electronics

 

Below outputs similar words to “baby” and “beautiful”.

 

아래에서는 "baby" 및 "beautiful"과 유사한 단어가 출력됩니다.

 

get_similar_tokens('baby', 3, glove_6b50d)
cosine sim=0.839: babies
cosine sim=0.800: boy
cosine sim=0.792: girl

 

get_similar_tokens('beautiful', 3, glove_6b50d)
cosine sim=0.921: lovely
cosine sim=0.893: gorgeous
cosine sim=0.830: wonderful

 

15.7.2.2. Word Analogy

Besides finding similar words, we can also apply word vectors to word analogy tasks. For example, “man”:“woman”::“son”:“daughter” is the form of a word analogy: “man” is to “woman” as “son” is to “daughter”. Specifically, the word analogy completion task can be defined as: for a word analogy a:b::c:d, given the first three words a, b and c, find d. Denote the vector of word w by vec(w). To complete the analogy, we will find the word whose vector is most similar to the result of vec(c)+vec(b)−vec(a).

 

유사한 단어를 찾는 것 외에도 단어 유추 작업에 단어 벡터를 적용할 수도 있습니다. 예를 들어, “man”:“woman”::“son”:“daughter”는 단어 유추의 형태입니다. “man”은 “woman”을 의미하고 “son”은 “daughter”를 의미합니다. 구체적으로 단어 유추 완료 작업은 다음과 같이 정의할 수 있습니다. 단어 유추 a:b::c:d에 대해 처음 세 단어 a, b 및 c가 주어지면 d를 찾습니다. 단어 w의 벡터를 vec(w)로 나타냅니다. 유추를 완성하기 위해 벡터가 vec(c)+vec(b)−vec(a)의 결과와 가장 유사한 단어를 찾습니다.

 

def get_analogy(token_a, token_b, token_c, embed):
    vecs = embed[[token_a, token_b, token_c]]
    x = vecs[1] - vecs[0] + vecs[2]
    topk, cos = knn(embed.idx_to_vec, x, 1)
    return embed.idx_to_token[int(topk[0])]  # Remove unknown words

이 코드는 임베딩을 사용하여 단어 간의 유추(Analogy)를 수행하는 함수를 정의하고 있습니다.

  1. def get_analogy(token_a, token_b, token_c, embed)::
    • get_analogy 함수를 정의합니다. 함수는 주어진 세 개의 토큰 token_a, token_b, token_c를 이용하여 유추 결과를 반환합니다.
    • 함수는 네 개의 인자를 받습니다:
      • token_a, token_b, token_c: 유추를 위한 입력 토큰들입니다.
      • embed: 임베딩을 관리하는 TokenEmbedding 객체입니다.
  2. vecs = embed[[token_a, token_b, token_c]]:
    • 주어진 입력 토큰들의 임베딩 벡터들을 embed에서 조회합니다.
    • token_a, token_b, token_c에 대한 임베딩 벡터들이 vecs에 저장됩니다.
  3. x = vecs[1] - vecs[0] + vecs[2]:
    • 세 개의 임베딩 벡터를 이용하여 유추 벡터 x를 계산합니다.
    • vecs[1] - vecs[0] + vecs[2]를 통해 유추 벡터를 구합니다.
  4. topk, cos = knn(embed.idx_to_vec, x, 1):
    • 주어진 유추 벡터 x와 모든 임베딩 벡터 사이의 코사인 유사도를 계산하여 가장 유사한 토큰의 인덱스를 찾습니다.
    • knn 함수를 사용하여 가장 유사한 토큰의 인덱스와 해당 토큰과의 코사인 유사도 값을 얻습니다.
  5. return embed.idx_to_token[int(topk[0])] # Remove unknown words:
    • 유사한 토큰의 인덱스를 이용하여 해당 토큰을 찾아 반환합니다.
    • embed.idx_to_token을 사용하여 인덱스에 대응하는 토큰을 찾아 반환하고, [int(topk[0])]을 사용하여 가장 유사한 토큰의 인덱스를 가져옵니다.
    • 반환 시 '[UNK]'와 같은 미지 토큰은 제외합니다.

이 함수는 입력 토큰 token_a, token_b, token_c를 사용하여 유추한 결과를 반환합니다

 

 

Let’s verify the “male-female” analogy using the loaded word vectors.

 

로드된 단어 벡터를 사용하여 "남성-여성" 비유를 검증해 보겠습니다.

 

get_analogy('man', 'woman', 'son', glove_6b50d)

이 코드는 'glove.6b.50d' 임베딩 데이터셋을 사용하여 'man'과 'woman' 간의 관계를 이용하여 'son'과 유추한 결과를 출력하는 과정을 나타내고 있습니다.

  1. get_analogy('man', 'woman', 'son', glove_6b50d):
    • get_analogy 함수를 호출하여 'man'과 'woman' 간의 관계를 이용하여 'son'과 유추한 결과를 출력합니다.
    • 'man'은 첫 번째 입력 토큰, 'woman'은 두 번째 입력 토큰, 'son'은 세 번째 입력 토큰입니다.
    • glove_6b50d는 'glove.6b.50d' 임베딩 데이터셋을 나타내는 TokenEmbedding 객체입니다.

함수는 'man'과 'woman' 간의 관계를 이용하여 'son'과 유추한 결과를 출력합니다. 결과는 'man'에서 'woman'을 빼고 'son'을 더한 결과에 가장 유사한 단어를 찾아 반환합니다

'daughter'

 

Below completes a “capital-country” analogy: “beijing”:“china”::“tokyo”:“japan”. This demonstrates semantics in the pretrained word vectors.

 

아래는 "수도-국가" 비유를 완성합니다: "베이징":"중국"::"도쿄":"일본". 이는 사전 학습된 단어 벡터의 의미를 보여줍니다.

 

get_analogy('beijing', 'china', 'tokyo', glove_6b50d)
'japan'

 

For the “adjective-superlative adjective” analogy such as “bad”:“worst”::“big”:“biggest”, we can see that the pretrained word vectors may capture the syntactic information.

 

"bad":"worst"::"big":"biggest"와 같은 "형용사-최상급" 비유의 경우 사전 훈련된 단어 벡터가 구문 정보를 캡처할 수 있음을 알 수 있습니다.

 

get_analogy('bad', 'worst', 'big', glove_6b50d)
'biggest'

To show the captured notion of past tense in the pretrained word vectors, we can test the syntax using the “present tense-past tense” analogy: “do”:“did”::“go”:“went”.

 

미리 훈련된 단어 벡터에서 캡처된 과거 시제 개념을 표시하기 위해 "현재 시제-과거 시제" 비유("do":"did"::"go":"went")를 사용하여 구문을 테스트할 수 있습니다.

 

get_analogy('do', 'did', 'go', glove_6b50d)
'went'

 

15.7.3. Summary

  • In practice, word vectors that are pretrained on large corpora can be applied to downstream natural language processing tasks.

    실제로 대규모 말뭉치에 대해 사전 훈련된 단어 벡터는 다운스트림 자연어 처리 작업에 적용될 수 있습니다.

  • Pretrained word vectors can be applied to the word similarity and analogy tasks.

    사전 훈련된 단어 벡터는 단어 유사성 및 유추 작업에 적용될 수 있습니다.

15.7.4. Exercises

  1. Test the fastText results using TokenEmbedding('wiki.en').
  2. When the vocabulary is extremely large, how can we find similar words or complete a word analogy faster?

 

 

 

 

반응형


반응형

15.6. Subword Embedding — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

15.6. Subword Embedding — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

 

15.6. Subword Embedding

 

In English, words such as “helps”, “helped”, and “helping” are inflected forms of the same word “help”. The relationship between “dog” and “dogs” is the same as that between “cat” and “cats”, and the relationship between “boy” and “boyfriend” is the same as that between “girl” and “girlfriend”. In other languages such as French and Spanish, many verbs have over 40 inflected forms, while in Finnish, a noun may have up to 15 cases. In linguistics, morphology studies word formation and word relationships. However, the internal structure of words was neither explored in word2vec nor in GloVe.

 

영어에서 "helps", "helped" 및 "helping"과 같은 단어는 동일한 단어 "help"의 변형 형태입니다. 'dog'와 'dogs'의 관계는 'cat'와 'cats'의 관계와 같고, 'boy'과 'boyfriend'의 관계는 'girl'와 'girlfriend'의 관계와 같습니다. 프랑스어와 스페인어와 같은 다른 언어에서는 많은 동사에 40개 이상의 변화형이 있는 반면, 핀란드어에서는 명사가 최대 15개의 경우를 가질 수 있습니다. 언어학에서 형태론은 단어 형성과 단어 관계를 연구합니다. 그러나 word2vec이나 GloVe에서는 단어의 내부 구조가 탐색되지 않았습니다.

 

Recall how words are represented in word2vec. In both the skip-gram model and the continuous bag-of-words model, different inflected forms of the same word are directly represented by different vectors without shared parameters. To use morphological information, the fastText model proposed a subword embedding approach, where a subword is a character n-gram (Bojanowski et al., 2017). Instead of learning word-level vector representations, fastText can be considered as the subword-level skip-gram, where each center word is represented by the sum of its subword vectors.

 

word2vec에서 단어가 어떻게 표현되는지 기억해 보세요. 스킵그램 모델과 continuous  bag-of-words 모델 모두에서 동일한 단어의 다양한 활용 형태는 공유 매개변수 없이 다양한 벡터로 직접 표현됩니다. 형태학적 정보를 사용하기 위해 fastText 모델은 하위 단어가 문자 n-gram인 하위 단어 임베딩 접근 방식을 제안했습니다(Bojanowski et al., 2017). 단어 수준 벡터 표현을 학습하는 대신 fastText는 하위 단어 수준 건너뛰기 그램으로 간주될 수 있습니다. 여기서 각 중심 단어는 해당 하위 단어 벡터의 합으로 표시됩니다.

 

Let’s illustrate how to obtain subwords for each center word in fastText using the word “where”. First, add special characters “<” and “>” at the beginning and end of the word to distinguish prefixes and suffixes from other subwords. Then, extract character n-grams from the word. For example, when n=3, we obtain all subwords of length 3: “<wh”, “whe”, “her”, “ere”, “re>”, and the special subword “<where>”.

 

"where"라는 단어를 사용하여 fastText의 각 중심 단어에 대한 하위 단어를 얻는 방법을 살펴보겠습니다. 먼저, 접두어와 접미어를 다른 하위 단어와 구별하기 위해 단어의 시작과 끝 부분에 특수 문자 “<” “>”를 추가합니다. 그런 다음 단어에서 문자 n-그램을 추출합니다. 예를 들어 n=3이면 길이가 3인 모든 하위 단어인 "<wh", "whe", "her", "ere", "re>" 및 특수 하위 단어 "<where>"를 얻습니다.

 

In fastText, for any word w, denote by gw the union of all its subwords of length between 3 and 6 and its special subword. The vocabulary is the union of the subwords of all words. Letting zg be the vector of subword g in the dictionary, the vector vw for word was a center word in the skip-gram model is the sum of its subword vectors:

 

fastText에서 임의의 단어 w에 대해 길이가 3에서 6 사이인 모든 하위 단어와 특수 하위 단어의 결합을 gw로 표시합니다. 어휘는 모든 단어의 하위 단어의 결합입니다. zg를 사전에 있는 하위 단어 g의 벡터로 두면 단어에 대한 벡터 vw는 스킵 그램 모델의 중심 단어였으며 해당 하위 단어 벡터의 합입니다.

 

The rest of fastText is the same as the skip-gram model. Compared with the skip-gram model, the vocabulary in fastText is larger, resulting in more model parameters. Besides, to calculate the representation of a word, all its subword vectors have to be summed, leading to higher computational complexity. However, thanks to shared parameters from subwords among words with similar structures, rare words and even out-of-vocabulary words may obtain better vector representations in fastText.

 

fastText의 나머지 부분은 Skip-gram 모델과 동일합니다. Skip-gram 모델과 비교하면 fastText의 어휘량이 더 많아 모델 매개변수가 더 많아집니다. 게다가, 단어의 표현을 계산하려면 모든 하위 단어 벡터를 합산해야 하므로 계산 복잡도가 높아집니다. 그러나 유사한 구조를 가진 단어들 사이에서 하위 단어의 공유 매개변수 덕분에 희귀한 단어와 심지어 어휘에 포함되지 않은 단어도 fastText에서 더 나은 벡터 표현을 얻을 수 있습니다.

 

fastText란?

 

fastText is a popular library and approach in natural language processing (NLP) that focuses on word embeddings and text classification. Developed by Facebook's AI Research (FAIR) team, fastText extends the concept of word embeddings introduced by Word2Vec and provides a more efficient and powerful way of representing words in a continuous vector space.

 

fastText는 자연어 처리(NLP)에서 널리 사용되는 라이브러리 및 접근 방식으로, 단어 임베딩과 텍스트 분류에 중점을 둡니다. 페이스북의 인공지능 연구(Facebook AI Research, FAIR) 팀에서 개발한 fastText는 Word2Vec에 소개된 단어 임베딩 개념을 확장하여 단어를 연속 벡터 공간에 효과적으로 표현하는 방법을 제공하며 더 효율적이고 강력한 접근 방식을 제공합니다.

 

The key features of fastText include:

 

fastText의 주요 특징은 다음과 같습니다:

 

  1. Subword Embeddings: Unlike traditional word embeddings, which treat words as atomic units, fastText breaks words into smaller subword units called "character n-grams." This allows it to capture morphological and semantic information from subword components, making it particularly effective for handling out-of-vocabulary words and morphologically rich languages.

    서브워드 임베딩: 전통적인 단어 임베딩과 달리 fastText는 단어를 더 작은 "문자 n-그램"이라 불리는 서브워드 단위로 분해합니다. 이를 통해 형태소 및 의미 정보를 서브워드 구성 요소에서 캡처할 수 있어, 어휘에 없는 단어나 형태론적으로 풍부한 언어를 처리하는 데 효과적입니다.

  2. Efficiency: fastText is designed to be highly efficient both in terms of training time and memory usage. Its subword-based approach reduces the vocabulary size and allows it to handle rare or unseen words effectively.

    효율성: fastText는 훈련 시간과 메모리 사용량 면에서 높은 효율성을 가지도록 설계되었습니다. 서브워드 기반 접근 방식을 사용하여 어휘 크기를 줄이고 희귀한 단어나 처음 보는 단어를 효과적으로 다룰 수 있습니다.

  3. Text Classification: In addition to word embeddings, fastText also excels at text classification tasks. It can classify documents, sentences, or shorter text snippets into predefined categories or labels. This makes it suitable for tasks like sentiment analysis, topic classification, and more.

    Text 분: 단어 임베딩 외에도 fastText는 텍스트 분류 작업에도 탁월합니다. 문서, 문장 또는 짧은 텍스트 조각을 미리 정의된 카테고리나 레이블로 분류할 수 있습니다. 따라서 감정 분석, 주제 분류 등과 같은 작업에 적합합니다..

  4. Pretrained Models: fastText provides pre-trained word vectors that can be readily used for various downstream tasks. These pre-trained vectors capture semantic information about words and can be fine-tuned or incorporated into other models.

    미리 훈련된 모델: fastText는 미리 훈련된 단어 벡터를 제공하여 다양한 하위 작업에 쉽게 활용할 수 있습니다. 이러한 미리 훈련된 벡터는 단어에 관한 의미 정보를 캡처하며 다른 모델에 미세 조정하거나 통합할 수 있습니다.

  5. Hierarchical Softmax and Negative Sampling: fastText employs techniques like hierarchical softmax and negative sampling to speed up training and make it feasible to train on large datasets.

    계층화된 소프트맥스와 네거티브 샘플링: fastText는 계층화된 소프트맥스와 네거티브 샘플링과 같은 기술을 사용하여 훈련 속도를 높이고 대규모 데이터셋에서 훈련할 수 있게 합니다.

  6. Open-Source: fastText is open-source software, which means researchers and developers can freely access its code, use pre-trained models, and even modify the library to suit their needs.

    오픈 소스: fastText는 오픈 소스 소프트웨어로, 연구원과 개발자는 코드에 자유롭게 접근하고, 미리 훈련된 모델을 사용하며, 라이브러리를 수정하여 필요에 맞게 사용할 수 있습니다.

Overall, fastText's unique subword embeddings and its focus on efficiency make it a valuable tool in the field of NLP, especially for tasks involving limited training data, morphologically complex languages, and text classification.

총론적으로 fastText의 독특한 서브워드 임베딩과 효율성에 중점을 두어서, 특히 훈련 데이터가 제한적인 경우, 형태론적으로 복잡한 언어, 텍스트 분류와 관련된 작업에서 가치 있는 도구로 사용됩니다.

15.6.2. Byte Pair Encoding

 

In fastText, all the extracted subwords have to be of the specified lengths, such as 3 to 6, thus the vocabulary size cannot be predefined. To allow for variable-length subwords in a fixed-size vocabulary, we can apply a compression algorithm called byte pair encoding (BPE) to extract subwords (Sennrich et al., 2015).

 

fastText에서는 추출된 모든 하위 단어가 3~6과 같이 지정된 길이여야 하므로 어휘 크기를 미리 정의할 수 없습니다. 고정 크기 어휘에서 가변 길이 하위 단어를 허용하기 위해 BPE(바이트 쌍 인코딩)라는 압축 알고리즘을 적용하여 하위 단어를 추출할 수 있습니다(Sennrich et al., 2015).

 

Byte pair encoding performs a statistical analysis of the training dataset to discover common symbols within a word, such as consecutive characters of arbitrary length. Starting from symbols of length 1, byte pair encoding iteratively merges the most frequent pair of consecutive symbols to produce new longer symbols. Note that for efficiency, pairs crossing word boundaries are not considered. In the end, we can use such symbols as subwords to segment words. Byte pair encoding and its variants has been used for input representations in popular natural language processing pretraining models such as GPT-2 (Radford et al., 2019) and RoBERTa (Liu et al., 2019). In the following, we will illustrate how byte pair encoding works.

 

바이트 쌍 인코딩은 훈련 데이터 세트의 통계 분석을 수행하여 임의 길이의 연속 문자와 같은 단어 내의 공통 기호를 찾습니다. 길이 1의 기호부터 시작하여 바이트 쌍 인코딩은 가장 빈번한 연속 기호 쌍을 반복적으로 병합하여 새로운 더 긴 기호를 생성합니다. 효율성을 위해 단어 경계를 넘는 쌍은 고려되지 않습니다. 결국 우리는 하위 단어와 같은 기호를 사용하여 단어를 분할할 수 있습니다. 바이트 쌍 인코딩과 그 변형은 GPT-2(Radford et al., 2019) 및 RoBERTa(Liu et al., 2019)와 같은 인기 있는 자연어 처리 사전 학습 모델의 입력 표현에 사용되었습니다. 다음에서는 바이트 쌍 인코딩이 작동하는 방식을 설명합니다.

 

First, we initialize the vocabulary of symbols as all the English lowercase characters, a special end-of-word symbol '_', and a special unknown symbol '[UNK]'.

 

먼저 기호의 어휘를 모두 영어 소문자, 특수 단어 끝 기호 '_', 특수 미지 기호 '[UNK]'로 초기화합니다.

 

import collections

symbols = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
           'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
           '_', '[UNK]']

이 코드는 다양한 기호를 포함하는 리스트인 symbols를 정의하는 부분입니다.

  1. import collections:
    • 파이썬 내장 모듈인 collections를 가져옵니다. 이 모듈은 데이터 컨테이너를 위한 유용한 클래스와 함수를 제공합니다.
  2. symbols = ['a', 'b', 'c', ..., '[UNK]']:
    • symbols라는 리스트를 정의하고 초기화합니다.
    • 이 리스트에는 알파벳 소문자 'a'부터 'z'까지의 문자, 언더스코어 '_', 그리고 '[UNK]'라는 특수 기호가 포함되어 있습니다.
    • '[UNK]'는 "unknown"을 의미하며, 텍스트에서 잘못된 문자 등을 대체하는 용도로 사용될 수 있습니다.

이 코드는 다양한 문자와 특수 기호를 포함하는 symbols 리스트를 정의하고 초기화하는 역할을 합니다.

 

Since we do not consider symbol pairs that cross boundaries of words, we only need a dictionary raw_token_freqs that maps words to their frequencies (number of occurrences) in a dataset. Note that the special symbol '_' is appended to each word so that we can easily recover a word sequence (e.g., “a taller man”) from a sequence of output symbols ( e.g., “a_ tall er_ man”). Since we start the merging process from a vocabulary of only single characters and special symbols, space is inserted between every pair of consecutive characters within each word (keys of the dictionary token_freqs). In other words, space is the delimiter between symbols within a word.

 

단어의 경계를 넘는 기호 쌍을 고려하지 않기 때문에 단어를 데이터 세트의 빈도(발생 횟수)에 매핑하는 사전 raw_token_freqs만 필요합니다. 특수 기호 '_'가 각 단어에 추가되어 출력 기호 시퀀스(예: "a_ Taller_ Man")에서 단어 시퀀스(예: "a Taller Man")를 쉽게 복구할 수 있습니다. 단일 문자와 특수 기호로만 구성된 어휘에서 병합 프로세스를 시작하므로 각 단어 내의 모든 연속 문자 쌍(사전 token_freqs의 키) 사이에 공백이 삽입됩니다. 즉, 공백은 단어 내 기호 사이의 구분 기호입니다.

 

raw_token_freqs = {'fast_': 4, 'faster_': 3, 'tall_': 5, 'taller_': 4}
token_freqs = {}
for token, freq in raw_token_freqs.items():
    token_freqs[' '.join(list(token))] = raw_token_freqs[token]
token_freqs

이 코드는 원시 토큰 빈도 정보를 가공하여 토큰을 수정하고, 새로운 형식으로 빈도 정보를 저장하는 과정을 나타내고 있습니다.

  1. raw_token_freqs = {'fast_': 4, 'faster_': 3, 'tall_': 5, 'taller_': 4}:
    • raw_token_freqs라는 딕셔너리를 정의하고 초기화합니다. 각 토큰과 해당 토큰의 빈도를 나타냅니다.
  2. token_freqs = {}:
    • 빈 딕셔너리 token_freqs를 초기화합니다. 수정된 토큰과 빈도 정보를 저장할 예정입니다.
  3. for token, freq in raw_token_freqs.items()::
    • raw_token_freqs 딕셔너리의 각 토큰과 빈도에 대해 반복합니다.
  4. token_freqs[' '.join(list(token))] = raw_token_freqs[token]:
    • 해당 토큰을 공백으로 분리하여 리스트로 만든 다음, 다시 공백을 이어붙여 하나의 문자열로 만듭니다.
    • 수정된 토큰을 token_freqs 딕셔너리의 키로 설정하고, 해당 토큰의 빈도를 raw_token_freqs 딕셔너리에서 가져와 값으로 설정합니다.
  5. token_freqs:
    • 수정된 토큰과 해당 토큰의 빈도로 이루어진 token_freqs 딕셔너리를 출력합니다.

이 코드는 원시 토큰의 빈도 정보를 가공하여 토큰을 수정하고, 수정된 토큰과 빈도 정보를 새로운 형식으로 저장하는 과정을 보여줍니다.

{'f a s t _': 4, 'f a s t e r _': 3, 't a l l _': 5, 't a l l e r _': 4}

We define the following get_max_freq_pair function that returns the most frequent pair of consecutive symbols within a word, where words come from keys of the input dictionary token_freqs.

 

단어 내에서 가장 빈번한 연속 기호 쌍을 반환하는 다음 get_max_freq_pair 함수를 정의합니다. 여기서 단어는 입력 사전 token_freqs의 키에서 나옵니다.

 

def get_max_freq_pair(token_freqs):
    pairs = collections.defaultdict(int)
    for token, freq in token_freqs.items():
        symbols = token.split()
        for i in range(len(symbols) - 1):
            # Key of `pairs` is a tuple of two consecutive symbols
            pairs[symbols[i], symbols[i + 1]] += freq
    return max(pairs, key=pairs.get)  # Key of `pairs` with the max value

이 코드는 주어진 토큰 빈도 정보에서 두 연속된 심볼의 페어 중 빈도가 가장 높은 페어를 찾아 반환하는 함수를 정의하고 있습니다.

  1. def get_max_freq_pair(token_freqs)::
    • token_freqs라는 인자를 받는 함수 get_max_freq_pair를 정의합니다.
    • token_freqs는 토큰과 해당 토큰의 빈도 정보가 저장된 딕셔너리입니다.
  2. pairs = collections.defaultdict(int):
    • collections.defaultdict 객체인 pairs를 생성합니다. 이 객체는 기본값으로 정수 0을 갖습니다.
    • 이 객체는 연속된 심볼 페어와 그 빈도를 저장할 용도로 사용됩니다.
  3. for token, freq in token_freqs.items()::
    • token_freqs 딕셔너리의 각 토큰과 빈도에 대해 반복합니다.
  4. symbols = token.split():
    • 현재 토큰을 공백을 기준으로 분리하여 symbols라는 리스트로 만듭니다.
  5. for i in range(len(symbols) - 1)::
    • symbols 리스트의 길이에서 1을 뺀 범위 내에서 반복합니다.
  6. pairs[symbols[i], symbols[i + 1]] += freq:
    • pairs 딕셔너리에 페어의 키인 (symbols[i], symbols[i + 1])에 빈도 freq를 더합니다.
  7. return max(pairs, key=pairs.get):
    • pairs 딕셔너리에서 값이 가장 큰 키를 반환합니다. 즉, 빈도가 가장 높은 페어의 키를 반환합니다.

이 함수는 주어진 토큰 빈도 정보에서 빈도가 가장 높은 연속된 심볼 페어를 찾아 반환하는 기능을 수행합니다.

 

As a greedy approach based on frequency of consecutive symbols, byte pair encoding will use the following merge_symbols function to merge the most frequent pair of consecutive symbols to produce new symbols.

 

연속 기호의 빈도를 기반으로 한 탐욕적 접근 방식으로 바이트 쌍 인코딩은 다음 merge_symbols 함수를 사용하여 가장 빈번한 연속 기호 쌍을 병합하여 새 기호를 생성합니다.

 

def merge_symbols(max_freq_pair, token_freqs, symbols):
    symbols.append(''.join(max_freq_pair))
    new_token_freqs = dict()
    for token, freq in token_freqs.items():
        new_token = token.replace(' '.join(max_freq_pair),
                                  ''.join(max_freq_pair))
        new_token_freqs[new_token] = token_freqs[token]
    return new_token_freqs

이 코드는 가장 높은 빈도를 갖는 연속된 심볼 페어를 병합하고, 토큰 빈도 정보를 업데이트하는 함수를 나타내고 있습니다.

  1. def merge_symbols(max_freq_pair, token_freqs, symbols)::
    • max_freq_pair, token_freqs, symbols라는 세 개의 인자를 받는 함수 merge_symbols를 정의합니다.
    • max_freq_pair는 가장 높은 빈도를 갖는 연속된 심볼 페어를 나타내는 튜플입니다.
    • token_freqs는 토큰과 해당 토큰의 빈도 정보가 저장된 딕셔너리입니다.
    • symbols는 기존 심볼을 저장한 리스트입니다.
  2. symbols.append(''.join(max_freq_pair)):
    • max_freq_pair 튜플의 심볼을 이어붙여서 하나의 문자열로 만든 다음, symbols 리스트에 추가합니다.
    • 이렇게 만들어진 문자열은 더 이상 나누어지지 않는 단일 심볼로써 추가됩니다.
  3. new_token_freqs = dict():
    • 빈 딕셔너리 new_token_freqs를 초기화합니다. 업데이트된 토큰 빈도 정보를 저장할 예정입니다.
  4. for token, freq in token_freqs.items()::
    • token_freqs 딕셔너리의 각 토큰과 빈도에 대해 반복합니다.
  5. new_token = token.replace(' '.join(max_freq_pair), ''.join(max_freq_pair)):
    • 현재 토큰에서 max_freq_pair 튜플의 심볼을 하나의 문자열로 바꾸어 새로운 토큰을 생성합니다.
    • 이렇게 생성된 새로운 토큰은 병합된 연속된 심볼을 포함하도록 됩니다.
  6. new_token_freqs[new_token] = token_freqs[token]:
    • 업데이트된 토큰과 해당 토큰의 빈도 정보를 new_token_freqs 딕셔너리에 추가합니다.
  7. return new_token_freqs:
    • 업데이트된 토큰 빈도 정보가 저장된 new_token_freqs 딕셔너리를 반환합니다.

이 함수는 가장 높은 빈도를 갖는 연속된 심볼 페어를 병합하고, 해당 페어가 병합된 토큰 빈도 정보를 업데이트하는 기능을 수행합니다.

 

 

Now we iteratively perform the byte pair encoding algorithm over the keys of the dictionary token_freqs. In the first iteration, the most frequent pair of consecutive symbols are 't' and 'a', thus byte pair encoding merges them to produce a new symbol 'ta'. In the second iteration, byte pair encoding continues to merge 'ta' and 'l' to result in another new symbol 'tal'.

 

이제 사전 token_freqs의 키에 대해 바이트 쌍 인코딩 알고리즘을 반복적으로 수행합니다. 첫 번째 반복에서 가장 빈번한 연속 기호 쌍은 't'와 'a'이므로 바이트 쌍 인코딩은 이를 병합하여 새로운 기호 'ta'를 생성합니다. 두 번째 반복에서는 바이트 쌍 인코딩이 계속해서 'ta'와 'l'을 병합하여 또 다른 새로운 기호 'tal'을 생성합니다.

 

num_merges = 10
for i in range(num_merges):
    max_freq_pair = get_max_freq_pair(token_freqs)
    token_freqs = merge_symbols(max_freq_pair, token_freqs, symbols)
    print(f'merge #{i + 1}:', max_freq_pair)

 

merge #1: ('t', 'a')
merge #2: ('ta', 'l')
merge #3: ('tal', 'l')
merge #4: ('f', 'a')
merge #5: ('fa', 's')
merge #6: ('fas', 't')
merge #7: ('e', 'r')
merge #8: ('er', '_')
merge #9: ('tall', '_')
merge #10: ('fast', '_')

이 코드는 주어진 빈도 정보와 심볼을 사용하여 심볼을 반복적으로 병합하는 과정을 나타내고 있습니다.

  1. num_merges = 10:
    • num_merges 변수에 10을 할당합니다. 이 변수는 병합을 반복할 횟수를 나타냅니다.
  2. for i in range(num_merges)::
    • 0부터 num_merges - 1까지의 범위에서 반복합니다.
  3. max_freq_pair = get_max_freq_pair(token_freqs):
    • get_max_freq_pair 함수를 사용하여 현재 가장 높은 빈도를 갖는 연속된 심볼 페어를 가져옵니다.
    • 이를 max_freq_pair 변수에 할당합니다.
  4. token_freqs = merge_symbols(max_freq_pair, token_freqs, symbols):
    • merge_symbols 함수를 사용하여 심볼을 병합하고, 토큰 빈도 정보를 업데이트합니다.
    • 업데이트된 토큰 빈도 정보를 token_freqs 변수에 할당합니다.
  5. print(f'merge #{i + 1}:', max_freq_pair):
    • 현재 반복 횟수와 병합된 심볼 페어를 출력합니다.
    • 출력 형식은 "merge #1: (심볼1, 심볼2)"와 같이 됩니다.

이 코드는 주어진 빈도 정보와 심볼을 사용하여 심볼을 반복적으로 병합하고, 각 반복마다 병합된 심볼 페어를 출력하는 과정을 나타내고 있습니다.

 

After 10 iterations of byte pair encoding, we can see that list symbols now contains 10 more symbols that are iteratively merged from other symbols.

 

바이트 쌍 인코딩을 10번 반복한 후 이제 목록 기호에 다른 기호에서 반복적으로 병합된 기호가 10개 더 포함되어 있음을 확인할 수 있습니다.

 

print(symbols)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '[UNK]', 'ta', 'tal', 'tall', 'fa', 'fas', 'fast', 'er', 'er_', 'tall_', 'fast_']

 

For the same dataset specified in the keys of the dictionary raw_token_freqs, each word in the dataset is now segmented by subwords “fast_”, “fast”, “er_”, “tall_”, and “tall” as a result of the byte pair encoding algorithm. For instance, words “faster_” and “taller_” are segmented as “fast er_” and “tall er_”, respectively.

 

raw_token_freqs 사전의 키에 지정된 동일한 데이터 세트의 경우 이제 데이터 세트의 각 단어는 바이트 쌍의 결과로 하위 단어 "fast_", "fast", "er_", "tall_" 및 "tall"로 분할됩니다. 인코딩 알고리즘. 예를 들어, "faster_" 및 "taller_"라는 단어는 각각 "fast er_" 및 "tall er_"로 분할됩니다.

 

print(list(token_freqs.keys()))
['fast_', 'fast er_', 'tall_', 'tall er_']

Note that the result of byte pair encoding depends on the dataset being used. We can also use the subwords learned from one dataset to segment words of another dataset. As a greedy approach, the following segment_BPE function tries to break words into the longest possible subwords from the input argument symbols.

 

바이트 쌍 인코딩의 결과는 사용되는 데이터세트에 따라 달라집니다. 또한 한 데이터세트에서 학습된 하위 단어를 사용하여 다른 데이터세트의 단어를 분할할 수도 있습니다. 탐욕적인 접근 방식으로, 다음 세그먼트_BPE 함수는 입력 인수 기호에서 단어를 가능한 가장 긴 하위 단어로 나누려고 시도합니다.

 

def segment_BPE(tokens, symbols):
    outputs = []
    for token in tokens:
        start, end = 0, len(token)
        cur_output = []
        # Segment token with the longest possible subwords from symbols
        while start < len(token) and start < end:
            if token[start: end] in symbols:
                cur_output.append(token[start: end])
                start = end
                end = len(token)
            else:
                end -= 1
        if start < len(token):
            cur_output.append('[UNK]')
        outputs.append(' '.join(cur_output))
    return outputs

이 코드는 주어진 토큰과 심볼을 사용하여 BPE (Byte-Pair Encoding) 알고리즘을 통해 토큰을 하위 단어로 분할하는 함수를 나타내고 있습니다.

  1. def segment_BPE(tokens, symbols)::
    • tokens와 symbols라는 두 개의 인자를 받는 함수 segment_BPE를 정의합니다.
    • tokens는 분할할 토큰들이 저장된 리스트입니다.
    • symbols는 BPE 알고리즘에서 사용할 심볼들을 저장한 리스트입니다.
  2. outputs = []:
    • 빈 리스트 outputs를 초기화합니다. 분할된 결과를 저장할 예정입니다.
  3. for token in tokens::
    • tokens 리스트의 각 토큰에 대해 반복합니다.
  4. start, end = 0, len(token):
    • start와 end를 초기화합니다. 이 변수들은 토큰의 하위 단어 분할을 위한 인덱스를 나타냅니다.
  5. cur_output = []:
    • 빈 리스트 cur_output을 초기화합니다. 현재 토큰의 분할 결과를 저장할 예정입니다.
  6. while start < len(token) and start < end::
    • start가 토큰의 길이보다 작고, start가 end보다 작은 동안 반복합니다.
    • 토큰의 하위 단어 분할을 수행합니다.
  7. if token[start: end] in symbols::
    • symbols 리스트에 현재 부분 토큰이 존재하면 (가장 긴 하위 단어라면):
    • cur_output 리스트에 현재 부분 토큰을 추가하고, start를 end로 설정합니다.
    • 그리고 end를 토큰의 길이로 설정하여 더 이상 하위 단어 분할을 시도하지 않도록 합니다.
  8. else::
    • 그렇지 않으면 (현재 부분 토큰이 symbols에 없으면):
    • end를 하나 줄여 다음으로 더 짧은 부분 토큰을 검사하도록 합니다.
  9. if start < len(token)::
    • 만약 start가 토큰의 길이보다 작다면 (아직 분할되지 않은 부분이 남았다면):
    • '[UNK]'라는 특수 토큰을 cur_output 리스트에 추가합니다.
  10. outputs.append(' '.join(cur_output)):
    • 현재 토큰의 분할 결과인 cur_output 리스트를 공백으로 연결하여 하나의 문자열로 만들고, 이를 outputs 리스트에 추가합니다.
  11. return outputs:
    • 분할된 결과가 저장된 outputs 리스트를 반환합니다.

이 함수는 BPE 알고리즘을 사용하여 주어진 토큰들을 하위 단어로 분할하는 기능을 수행합니다.

 

In the following, we use the subwords in list symbols, which is learned from the aforementioned dataset, to segment tokens that represent another dataset.

 

다음에서는 앞서 언급한 데이터 세트에서 학습한 목록 기호의 하위 단어를 사용하여 다른 데이터 세트를 나타내는 토큰을 분할합니다.

 

tokens = ['tallest_', 'fatter_']
print(segment_BPE(tokens, symbols))

이 코드는 주어진 토큰들을 BPE 알고리즘을 이용하여 하위 단어로 분할하는 함수를 호출하고, 그 결과를 출력하는 과정을 나타내고 있습니다.

  1. tokens = ['tallest_', 'fatter_']:
    • tokens 리스트에 두 개의 토큰인 'tallest_'와 'fatter_'를 저장합니다.
  2. print(segment_BPE(tokens, symbols)):
    • segment_BPE 함수를 호출하여 주어진 토큰들을 하위 단어로 분할합니다.
    • symbols 리스트는 앞서 정의된 심볼 리스트입니다.
    • 분할된 결과를 출력합니다.

이 코드는 주어진 토큰들을 BPE 알고리즘을 이용하여 하위 단어로 분할한 후, 그 결과를 출력합니다.

['tall e s t _', 'fa t t er_']

 

Byte Pair Encoding (BPE) 란?

 

바이트 페어 인코딩(Byte Pair Encoding 또는 BPE)은 자연어 처리에서 텍스트 압축 및 토큰화에 사용되는 기술입니다. 이 기술은 텍스트 데이터를 작은 단위로 쪼개어 단어 및 서브워드의 어휘를 구축하는 방법을 의미합니다.

 

BPE의 주요 아이디어는 빈도 기반으로 텍스트 데이터 내에서 가장 자주 나타나는 바이트 또는 문자열 단위를 식별하고 이를 하나의 토큰으로 결합하여 새로운 어휘를 생성하는 것입니다. 이렇게 하면 텍스트 데이터 내에서 자주 사용되는 단어와 단어 구성 요소를 캡처하면서 어휘 크기를 줄일 수 있습니다.

 

BPE 알고리즘의 작동 방식은 다음과 같습니다:

 

  1. 빈도 계산: 텍스트 데이터 내에서 모든 바이트 또는 문자열 단위의 빈도를 계산합니다.
  2. 가장 빈도가 높은 바이트 결합 식별: 가장 자주 나타나는 바이트나 문자열의 쌍을 식별합니다. 이러한 바이트 쌍은 어휘 내의 새로운 토큰으로 결합됩니다.
  3. 토큰 결합: 가장 빈도가 높은 바이트 쌍을 하나의 토큰으로 결합하여 어휘를 업데이트합니다. 이 단계에서 중복되는 바이트 쌍을 찾아 처리합니다.
  4. 반복: 토큰 결합 단계를 여러 번 반복하여 텍스트 데이터의 빈도 기반 어휘를 구축합니다. 이렇게 하면 단어와 서브워드의 어휘가 생성됩니다.

 

BPE를 사용하면 희소성을 줄이고 미등록 단어에 대한 처리를 향상시킬 수 있습니다. 또한, 어휘 크기를 줄이는 효과를 가져와 모델 학습 및 토큰화 속도를 향상시킬 수 있습니다. 이 기술은 특히 기계 번역, 텍스트 생성, 감정 분석 등 다양한 자연어 처리 작업에서 유용하게 활용됩니다.

15.6.3. Summary

 

  • The fastText model proposes a subword embedding approach. Based on the skip-gram model in word2vec, it represents a center word as the sum of its subword vectors.

    fastText 모델은 하위 단어 임베딩 접근 방식을 제안합니다. word2vec의 스킵 그램 모델을 기반으로 중앙 단어를 하위 단어 벡터의 합으로 나타냅니다.

  • Byte pair encoding performs a statistical analysis of the training dataset to discover common symbols within a word. As a greedy approach, byte pair encoding iteratively merges the most frequent pair of consecutive symbols.

    바이트 쌍 인코딩은 훈련 데이터 세트의 통계 분석을 수행하여 단어 내의 공통 기호를 찾습니다. 욕심 많은 접근 방식으로 바이트 쌍 인코딩은 가장 빈번한 연속 기호 쌍을 반복적으로 병합합니다.

  • Subword embedding may improve the quality of representations of rare words and out-of-dictionary words.

    하위 단어 임베딩은 희귀 단어 및 사전에 없는 단어 표현의 품질을 향상시킬 수 있습니다.

 

15.6.4. Exercises

  1. As an example, there are about 3×10**8 possible 6-grams in English. What is the issue when there are too many subwords? How to address the issue? Hint: refer to the end of Section 3.2 of the fastText paper (Bojanowski et al., 2017).
  2. How to design a subword embedding model based on the continuous bag-of-words model?
  3. To get a vocabulary of size m, how many merging operations are needed when the initial symbol vocabulary size is n?
  4. How to extend the idea of byte pair encoding to extract phrases?
반응형


반응형

15.5. Word Embedding with Global Vectors (GloVe) — Dive into Deep Learning 1.0.3 documentation (d2l.ai)

 

15.5. Word Embedding with Global Vectors (GloVe) — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

 

15.5. Word Embedding with Global Vectors (GloVe)

 

Word-word co-occurrences within context windows may carry rich semantic information. For example, in a large corpus word “solid” is more likely to co-occur with “ice” than “steam”, but word “gas” probably co-occurs with “steam” more frequently than “ice”. Besides, global corpus statistics of such co-occurrences can be precomputed: this can lead to more efficient training. To leverage statistical information in the entire corpus for word embedding, let’s first revisit the skip-gram model in Section 15.1.3, but interpreting it using global corpus statistics such as co-occurrence counts.

 

15.1. Word Embedding (word2vec) — Dive into Deep Learning 1.0.3 documentation

 

d2l.ai

컨텍스트 창 내에서 단어-단어 동시 발생 Word-word co-occurrences 은 풍부한 의미 정보를 전달할 수 있습니다. 예를 들어, 대규모 코퍼스에서 "solid"라는 단어는 "steam"보다 "ice"와 함께 나타날 가능성이 더 높지만 "gas"라는 단어는 "ice"보다 "steam"과 더 자주 함께 나타날 가능성이 높습니다. 게다가, 그러한 동시 발생에 대한 글로벌 코퍼스 통계가 미리 계산될 수 있습니다. 이는 보다 효율적인 훈련으로 이어질 수 있습니다. 단어 임베딩을 위해 전체 코퍼스의 통계 정보를 활용하기 위해 먼저 섹션 15.1.3의 스킵 그램 모델을 다시 방문하되 동시 발생 횟수와 같은 전역 코퍼스 통계를 사용하여 해석해 보겠습니다.

 

15.5.1. Skip-Gram with Global Corpus Statistics

 

Denoting by qij the conditional probability P(wj∣wi) of word wj given word wi in the skip-gram model, we have this formula where for any index i vectors vi and ui represent word wi as the center word and context word, respectively, and V={0,1,…,|V|−1} is the index set of the vocabulary.

 

 

스킵-그램 모델에서 단어 wi가 주어졌을 때 단어 wj의 조건부 확률 P(wj∣wi)를 qij로 표시하면 다음 공식을 얻을 수 있습니다. 여기서 모든 인덱스 i에 대해 벡터 vi와 ui는 단어 wi를 각각 중심 단어와 문맥 단어로 나타냅니다. , 그리고 V={0,1,…,|V|−1}은 어휘의 인덱스 세트입니다.

 

Consider word wi that may occur multiple times in the corpus. In the entire corpus, all the context words wherever wi is taken as their center word form a multiset Ci of word indices that allows for multiple instances of the same element. For any element, its number of instances is called its multiplicity. To illustrate with an example, suppose that word wi occurs twice in the corpus and indices of the context words that take wi as their center word in the two context windows are k,j,m,k and k,l,k,j. Thus, multiset Ci={j,j,k,k,k,k,l,m}, where multiplicities of elements j,k,l,m are 2, 4, 1, 1, respectively.

 

말뭉치에서 여러 번 나타날 수 있는 단어 wi를 생각해 보세요. 전체 코퍼스에서 wi가 중심 단어로 사용되는 모든 문맥 단어는 동일한 요소의 여러 인스턴스를 허용하는 단어 인덱스의 다중 집합 Ci를 형성합니다. 모든 요소에 대해 인스턴스 수를 다중성이라고 합니다. 예를 들어 설명하자면, 단어 wi가 말뭉치에 두 번 나타나고 두 문맥 창에서 wi를 중심 단어로 하는 문맥 단어의 인덱스가 k,j,m,k 및 k,l,k,j라고 가정합니다. 따라서 다중 집합 Ci={j,j,k,k,k,k,l,m}이며, 여기서 요소 j,k,l,m의 다중도는 각각 2, 4, 1, 1입니다.

 

Now let’s denote the multiplicity of element j in multiset Ci as xij. This is the global co-occurrence count of word wj (as the context word) and word wi (as the center word) in the same context window in the entire corpus. Using such global corpus statistics, the loss function of the skip-gram model is equivalent to this fomular,

 

이제 다중집합 Ci에서 요소 j의 다중도를 xij로 표시해 보겠습니다. 이는 전체 말뭉치에서 동일한 컨텍스트 창에 있는 단어 wj(문맥 단어)와 단어 wi(중앙 단어)의 전역 동시 발생 횟수입니다. 이러한 전역 코퍼스 통계를 사용하면 스킵그램 모델의 손실 함수는 다음 공식과 동일합니다.

 

We further denote by xi the number of all the context words in the context windows where wi occurs as their center word, which is equivalent to |Ci|. Letting pij be the conditional probability xij/ki for generating context word wj given center word wi, (15.5.2) can be rewritten as

 

또한 wi가 중심 단어로 나타나는 컨텍스트 창의 모든 컨텍스트 단어 수를 xi로 표시하며 이는 |Ci|와 동일합니다. pij를 중심 단어 wi가 주어졌을 때 문맥 단어 wj를 생성하기 위한 조건부 확률 xij/ki라고 하면 (15.5.2)는 다음 공식으로 다시 작성할 수 있습니다.

 

In (15.5.3), −∑j∈vpij**log qij calculates the cross-entropy of the conditional distribution pij of global corpus statistics and the conditional distribution qij of model predictions. This loss is also weighted by xi as explained above. Minimizing the loss function in (15.5.3) will allow the predicted conditional distribution to get close to the conditional distribution from the global corpus statistics.

 

(15.5.3)에서 −∑j∈vpij**log qij는 전역 코퍼스 통계의 조건부 분포 pij와 모델 예측의 조건부 분포 qij의 교차 엔트로피를 계산합니다. 이 손실은 위에서 설명한 대로 xi에 의해 가중치가 부여됩니다. (15.5.3)에서 손실함수를 최소화하면 예측된 조건부 분포가 전역 코퍼스 통계의 조건부 분포에 가까워질 수 있습니다.

 

Though being commonly used for measuring the distance between probability distributions, the cross-entropy loss function may not be a good choice here. On the one hand, as we mentioned in Section 15.2, the cost of properly normalizing qij results in the sum over the entire vocabulary, which can be computationally expensive. On the other hand, a large number of rare events from a large corpus are often modeled by the cross-entropy loss to be assigned with too much weight.

 

확률 분포 사이의 거리를 측정하는 데 일반적으로 사용되지만 교차 엔트로피 손실 함수는 여기서는 좋은 선택이 아닐 수 있습니다. 한편으로, 섹션 15.2에서 언급했듯이 qij를 적절하게 정규화하는 비용은 전체 어휘에 대한 합계를 산출하므로 계산 비용이 많이 들 수 있습니다. 반면, 대규모 코퍼스에서 발생하는 다수의 희귀 이벤트는 교차 엔트로피 손실로 모델링되어 너무 많은 가중치가 할당되는 경우가 많습니다.

 

15.5.2. The GloVe Model

 

In view of this, the GloVe model makes three changes to the skip-gram model based on squared loss (Pennington et al., 2014):

 

이를 고려하여 GloVe 모델은 손실 제곱을 기반으로 하는 스킵 그램 모델에 세 가지 변경 사항을 적용합니다(Pennington et al., 2014).

 

Putting all things together, training GloVe is to minimize the following loss function:

 

모든 것을 종합하면 GloVe 교육은 다음 손실 함수를 최소화하는 것입니다.

 

 

For the weight function, a suggested choice is: ℎ(x)=(x/c)**α (e.g α=0.75) if x<c (e.g., c=100); otherwise ℎ(x)=1. In this case, because ℎ(0)=0, the squared loss term for any xij=0 can be omitted for computational efficiency. For example, when using minibatch stochastic gradient descent for training, at each iteration we randomly sample a minibatch of non-zero xij to calculate gradients and update the model parameters. Note that these non-zero xij are precomputed global corpus statistics; thus, the model is called GloVe for Global Vectors.

 

가중치 함수의 경우 제안되는 선택은 다음과 같습니다. x<c(예: c=100)인 경우 ℎ(x)=(x/c)**α(예: α=0.75); 그렇지 않으면 ℎ(x)=1입니다. 이 경우 ℎ(0)=0이므로 xij=0에 대한 제곱 손실 항은 계산 효율성을 위해 생략될 수 있습니다. 예를 들어, 훈련을 위해 미니배치 확률적 경사하강법을 사용할 때 각 반복마다 0이 아닌 xij의 미니배치를 무작위로 샘플링하여 경사를 계산하고 모델 매개변수를 업데이트합니다. 0이 아닌 xij는 미리 계산된 글로벌 코퍼스 통계입니다. 따라서 이 모델은 전역 벡터용 GloVe라고 합니다.

 

It should be emphasized that if word wi appears in the context window of word wj, then vice versa. Therefore, xij=xji. Unlike word2vec that fits the asymmetric conditional probability pij, GloVe fits the symmetric logxij. Therefore, the center word vector and the context word vector of any word are mathematically equivalent in the GloVe model. However in practice, owing to different initialization values, the same word may still get different values in these two vectors after training: GloVe sums them up as the output vector.

 

wj라는 단어의 컨텍스트 창에 wi라는 단어가 나타나면 그 반대의 경우도 마찬가지라는 점을 강조해야 합니다. 따라서 xij=xji입니다. 비대칭 조건부 확률 pij에 맞는 word2vec와 달리 GloVe는 대칭 logxij에 적합합니다. 따라서 모든 단어의 중심 단어 벡터와 문맥 단어 벡터는 GloVe 모델에서 수학적으로 동일합니다. 그러나 실제로는 초기화 값이 다르기 때문에 동일한 단어가 훈련 후에도 두 벡터에서 서로 다른 값을 얻을 수 있습니다. GloVe는 이를 출력 벡터로 합산합니다.

 

15.5.3. Interpreting GloVe from the Ratio of Co-occurrence Probabilities

We can also interpret the GloVe model from another perspective. Using the same notation in Section 15.5.1, let pij =def P(wj|wi)  be the conditional probability of generating the context word wj given wi as the center word in the corpus. tab_glove lists several co-occurrence probabilities given words “ice” and “steam” and their ratios based on statistics from a large corpus.

 

GloVe 모델을 다른 관점에서 해석할 수도 있습니다. 섹션 15.5.1의 동일한 표기법을 사용하여, pij =def P(wj|wi)를 말뭉치의 중심 단어로 wi가 주어진 문맥 단어 wj를 생성하는 조건부 확률로 둡니다. tab_glove는 "ice"와 "steam"이라는 단어가 주어진 여러 동시 발생 확률과 대규모 코퍼스의 통계를 기반으로 한 비율을 나열합니다.

 

:Word-word co-occurrence probabilities and their ratios from a large corpus (adapted from Table 1 in Pennington et al. (2014))

 

: 대규모 자료의 단어-단어 동시 발생 확률 및 그 비율(Pennington et al.(2014)의 표 1에서 채택)

 

Table 15.5.1 label:tab_glove

 

 

We can observe the following from tab_glove:

 

tab_glove에서 다음을 관찰할 수 있습니다.

  • For a word wk that is related to “ice” but unrelated to “steam”, such as wk=solid, we expect a larger ratio of co-occurence probabilities, such as 8.9.
  • wk=solid와 같이 “ice”와 관련이 있지만 “steam”과 관련이 없는 단어 wk의 경우 8.9와 같이 더 큰 동시 발생 확률 비율이 예상됩니다.
  • For a word wk that is related to “steam” but unrelated to “ice”, such as wk=gas, we expect a smaller ratio of co-occurence probabilities, such as 0.085.
  • wk=gas와 같이 "증기"와 관련이 있지만 "얼음"과 관련이 없는 단어 wk의 경우 0.085와 같이 더 작은 동시 발생 확률 비율이 예상됩니다.
  • For a word wk that is related to both “ice” and “steam”, such as wk=water, we expect a ratio of co-occurence probabilities that is close to 1, such as 1.36.
  • wk=물과 같이 "얼음"과 "증기" 모두와 관련된 단어 wk의 경우 1.36과 같이 1에 가까운 동시 발생 확률 비율을 예상합니다.
  • For a word wk that is unrelated to both “ice” and “steam”, such as wk=fashion, we expect a ratio of co-occurence probabilities that is close to 1, such as 0.96.
  • wk=fashion과 같이 "ice"와 "steam" 모두와 관련이 없는 단어 wk의 경우 0.96과 같이 1에 가까운 동시 발생 확률 비율을 기대합니다.

It can be seen that the ratio of co-occurrence probabilities can intuitively express the relationship between words. Thus, we can design a function of three word vectors to fit this ratio. For the ratio of co-occurrence probabilities pij/pik with wi being the center word and wj and wk being the context words, we want to fit this ratio using some function f:

 

동시발생 확률의 비율을 통해 단어 간의 관계를 직관적으로 표현할 수 있음을 알 수 있다. 따라서 우리는 이 비율에 맞게 세 단어 벡터의 함수를 설계할 수 있습니다. wi가 중심 단어이고 wj와 wk가 문맥 단어인 동시 발생 확률 pij/pik의 비율에 대해 우리는 일부 함수 f를 사용하여 이 비율을 맞추고 싶습니다.

 

Among many possible designs for f, we only pick a reasonable choice in the following. Since the ratio of co-occurrence probabilities is a scalar, we require that f be a scalar function, such as f(uj,uk,vi)=f((ui−uk)**⊤vi). Switching word indices j and k in (15.5.5), it must hold that f(x)f(−x)=1, so one possibility is f(x)=exp⁡(x), i.e.,

 

f에 대해 가능한 많은 디자인 중에서 우리는 다음 중에서 합리적인 선택만을 선택합니다. 동시 발생 확률의 비율은 스칼라이므로 f는 f(uj,uk,vi)=f((ui−uk)**⊤vi)와 같은 스칼라 함수여야 합니다. (15.5.5)에서 단어 인덱스 j와 k를 전환하면 f(x)f(−x)=1을 유지해야 하므로 한 가지 가능성은 f(x)=exp⁡(x)입니다. 즉,

 

Now let’s pick exp⁡(uj**⊤ vi)≈αpij, where α is a constant. Since pij=xij/xi, after taking the logarithm on both sides we get uj**⊤ vi≈log α+log xij − log xi. We may use additional bias terms to fit −log α + log xi, such as the center word bias bi and the context word bias cj:

 

이제 α가 상수인 exp⁡(uj**⊤ vi)⁡αpij를 선택해 보겠습니다. pij=xij/xi이므로, 양쪽에 로그를 취한 후 uj**⊤ vi≒log α+log xij − log xi를 얻습니다. 중심 단어 바이어스 bi 및 문맥 단어 바이어스 cj와 같이 −log α + log xi를 맞추기 위해 추가 바이어스 항을 사용할 수 있습니다.

 

Measuring the squared error of (15.5.7) with weights, the GloVe loss function in (15.5.4) is obtained.

 

가중치를 사용하여 (15.5.7)의 제곱 오차를 측정하면 (15.5.4)의 GloVe 손실 함수가 얻어집니다.

 

GloVe Model 이란?

 

The GloVe model, short for Global Vectors for Word Representation, is an unsupervised learning algorithm designed to create word embeddings – numerical representations of words – from large text corpora. These word embeddings capture semantic relationships between words and are used in various natural language processing (NLP) tasks.

 

GloVe 모델은 Global Vectors for Word Representation의 약자로, 대용량 텍스트 말뭉치에서 단어 임베딩 – 단어의 숫자 표현 – 을 생성하기 위한 비지도 학습 알고리즘입니다. 이러한 단어 임베딩은 단어 간 의미 관계를 포착하며 다양한 자연어 처리 (NLP) 작업에서 사용됩니다.

 

The key idea behind the GloVe model is to factorize the word co-occurrence matrix, which represents how often words appear together in a given context window. By analyzing these co-occurrence statistics, GloVe learns to embed words in a continuous vector space where similar words are closer to each other, and relationships between words are preserved.

 

GloVe 모델의 핵심 아이디어는 word co-occurrence matrix을 분해하는 것입니다. 이 행렬은 주어진 문맥 창 내에서 단어가 얼마나 자주 함께 나타나는지를 나타냅니다. 이러한 공기 발생 통계를 분석하여 GloVe는 유사한 단어가 서로 가까이 위치하고 단어 간 관계가 보존되는 연속 벡터 공간에 단어를 임베딩하는 방법을 학습합니다.

 

Here's a brief overview of how the GloVe model works:

 

다음은 GloVe 모델의 작동 방식에 대한 간략한 개요입니다:

 

  1. Construct the Co-occurrence Matrix: Create a co-occurrence matrix that counts how often each word appears in the context of other words within a specified window.

    공기 발생 행렬 생성: 지정된 창 내에서 각 단어가 다른 단어와 함께 나타나는 빈도를 계산하는 공기 발생 행렬을 생성합니다.

  2. Initialize Word Vectors: Initialize word vectors randomly for each word.

    단어 벡터 초기화: 각 단어에 대해 단어 벡터를 무작위로 초기화합니다.

  3. Define the GloVe Objective Function: Define an objective function that measures the difference between the dot product of word vectors and the logarithm of the co-occurrence probabilities. The aim is to minimize the difference.

    GloVe 목적 함수 정의: 단어 벡터의 내적과 공기 발생 확률의 로그 차이를 측정하는 목적 함수를 정의합니다. 목표는 차이를 최소화하는 것입니다.

  4. Optimize the Objective Function: Use an optimization algorithm (usually stochastic gradient descent) to minimize the objective function. During this optimization, the word vectors are updated to better capture the co-occurrence patterns.

    목적 함수 최적화: 최적화 알고리즘 (일반적으로 확률적 경사 하강법)을 사용하여 목적 함수를 최소화합니다. 이 최적화 과정에서 단어 벡터는 공기 발생 패턴을 더 잘 포착하기 위해 업데이트됩니다.

  5. Extract Word Embeddings: Once training is complete, the learned word vectors serve as the word embeddings that capture semantic information about words.

    단어 임베딩 추출: 훈련이 완료되면 학습된 단어 벡터는 의미 정보를 포착하는 단어 임베딩으로 사용됩니다.

GloVe embeddings have gained popularity due to their ability to capture meaningful semantic relationships between words, even without requiring extensive training data. They excel in capturing both syntactic (grammatical) and semantic (meaning-based) relationships, making them useful for a wide range of NLP tasks, such as text classification, sentiment analysis, machine translation, and more.

GloVe 임베딩은 광범위한 의미 관계를 캡처하는 능력으로 인해 풍부한 의미 정보를 기대할 수 없는 환경에서도 의미 있는 의미 관계를 포착하여 인기를 얻었습니다. 구문적 (문법적) 및 의미적 (의미 기반) 관계를 모두 잘 포착하여 텍스트 분류, 감성 분석, 기계 번역 등 다양한 NLP 작업에 유용합니다.

The GloVe model is an important advancement in the field of word embeddings and has contributed significantly to improving the quality of word representations used in various NLP applications.

GloVe 모델은 단어 임베딩 분야에서 중요한 진전으로, 다양한 NLP 응용 프로그램에서 사용되는 단어 표현의 품질을 크게 향상시키는 데 기여한 중요한 역할을 하고 있습니다.

 

15.5.4. Summary

  • The skip-gram model can be interpreted using global corpus statistics such as word-word co-occurrence counts.

    스킵그램 모델은 단어-단어 동시 발생 횟수와 같은 전역 코퍼스 통계를 사용하여 해석할 수 있습니다.

  • The cross-entropy loss may not be a good choice for measuring the difference of two probability distributions, especially for a large corpus. GloVe uses squared loss to fit precomputed global corpus statistics.

    교차 엔트로피 손실은 특히 대규모 자료의 경우 두 확률 분포의 차이를 측정하는 데 좋은 선택이 아닐 수 있습니다. GloVe는 미리 계산된 글로벌 코퍼스 통계를 맞추기 위해 제곱 손실을 사용합니다.

  • The center word vector and the context word vector are mathematically equivalent for any word in GloVe.

    중심 단어 벡터와 문맥 단어 벡터는 GloVe의 모든 단어에 대해 수학적으로 동일합니다.

  • GloVe can be interpreted from the ratio of word-word co-occurrence probabilities.

    GloVe는 단어-단어 동시 출현 확률의 비율로 해석할 수 있습니다.

 

15.5.5. Exercises

  1. If words wi and wj co-occur in the same context window, how can we use their distance in the text sequence to redesign the method for calculating the conditional probability pij? Hint: see Section 4.2 of the GloVe paper (Pennington et al., 2014).
  2. For any word, are its center word bias and context word bias mathematically equivalent in GloVe? Why?

 

 

 

 

 

 

 

 

 

 

반응형