Data Science/ML&AI

[NLP] Word Embedding with Lookup table - nn.Embedding()

so.py 2021. 6. 3. 00:25

파이토치에서는 임베딩 벡터를 사용하는 방법이 크게 두 가지가 있다. 임베딩 층 (embedding layer)를 만들어 훈련 데이터로부터 처음부터 임베딩 벡터를 학습하는 방법미리 사전에 훈련된 임베딩 벡터(pre-trained word embedding)들을 가져와 사용하는 방법이다.

우선 전자의 방법부터 알아보겟다.

임베딩 층의 입력으로 사용하기 위해서 입력 시퀀스의 각 단어들은 모두 정수 인코딩이 되어있어야 한다.

  • 어떤 단어 -> 단어에 부여된 고유한 정수값 -> 임베딩 층 통과 -> 밀집 벡터

임베딩 층은 입력 정수에 대해 밀집 벡터로 매핑하고, 이 밀집 벡터는 인공 신경망의 학습 과정에서 가중치가 학습되는 것과 같은 방식으로 훈련된다. 훈련 과정에서 단어는 모델이 풀고자하는 작업에 맞는 값으로 업데이트 된다. 이 밀집 벡터를 임베딩 벡터라고 한다.

 

Screen Shot 2021-06-02 at 11 53 13 PM

특정 단어와 맵핑되는 정수를 인덱스로 가지고 있는 테이블로부터 임베딩 벡터 값을 가져오는 곳은 룩업 테이블이다. 이 테이블은 단어 집합의 크기만큼의 행을 가지고 있으므로, 모든 단어는 고유한 임베딩 벡터를 가진다.

파이토치는 단어를 정수 인덱스로 바꾸고 원-핫 벡터로 한번 더 바꾸고나서 임베딩 층의 입력으로 사용하는 것이 아니라, 단어를 정수 인덱스로만 바꾼채로 임베딩 층의 입력으로 사용해도 룩업 테이블 된 결과인 임베딩 벡터를 리턴한다.

Logic w/o nn.Embedding()

nn.Embedding()을 사용하지 않고, 임베딩의 로직을 코드로 이해해보겠다.

1. 임의의 문장으로부터 단어 집합을 만들고, 각 단어에 정수를 부여한다.

train_data = 'you need to know how to code'
word_set = set(train_data.split()) # 중복을 제거한 단어들의 집합인 단어 집합 생성.
vocab = {word: i+2 for i, word in enumerate(word_set)}  # 단어 집합의 각 단어에 고유한 정수 맵핑.
vocab['<unk>'] = 0
vocab['<pad>'] = 1
print(vocab)

>>> {'code': 2, 'you': 3, 'know': 4, 'to': 5, 'need': 6, 'how': 7, '<unk>': 0, '<pad>': 1}

 

2. 단어 집합의 크기를 행으로 가지는 임베딩 테이블을 구현한다. 단, 여기서 임베딩 벡터의 차원은 3으로 정한다.

# 단어 집합의 크기만큼의 행을 가지는 테이블 생성.
# len(vocab) X 3
embedding_table = torch.FloatTensor([
                               [ 0.0,  0.0,  0.0],
                               [ 0.0,  0.0,  0.0],
                               [ 0.2,  0.9,  0.3],
                               [ 0.1,  0.5,  0.7],
                               [ 0.2,  0.1,  0.8],
                               [ 0.4,  0.1,  0.1],
                               [ 0.1,  0.8,  0.9],
                               [ 0.6,  0.1,  0.1]])

 

3. 임의의 문장에 대해 룩업 테이블을 통해 임베딩 벡터들을 업데이트 시켜준다.

# 임의의 샘플 문장
sample = 'you need to run'.split()
idxes=[]
# 각 단어를 정수로 변환
for word in sample:
  try:
    idxes.append(vocab[word])
  except KeyError: # 단어 집합에 없는 단어일 경우 <unk>로 대체된다.
    idxes.append(vocab['<unk>'])
idxes = torch.LongTensor(idxes)

# 룩업 테이블
lookup_result = embedding_table[idxes, :] # 각 정수를 인덱스로 임베딩 테이블에서 값을 가져온다.
print(lookup_result)

# 순서대로 you, need, to, run 단어에 대한 벡터값이 생성됐다.
>>> tensor([[0.1000, 0.5000, 0.7000],
        [0.1000, 0.8000, 0.9000],
        [0.4000, 0.1000, 0.1000],
        [0.0000, 0.0000, 0.0000]])