딥러닝을 이용한 자연어처리 입문 #11 8) 사전 훈련된 워드 임베딩 ~ 09) 사전 훈련된 워드 임베딩 (Pre-Trained Word Embedding) 사용하기
08) 사전 훈련된 워드 임베딩 (Pre-trained Word Embedding)
1. 케라스 임베딩 층 (Keras Embedding Layer)
- 케라스 : 훈련 데이터의 단어들에 대해 워드 임베딩을 수행하는 도구 Embedding() 제공
- Embedding() : 인공 신경망 구조 관점에서 임베딩 층 구현
1) 임베딩 층은 룩업 테이블이다.
어떤 단어 → 단어에 부여된 고유한 정수값 → 임베딩 층 통과 → 밀집 벡터
ex) 영단어 : great

- 임베딩 차원 : 4로 설정
- great은 정수 인코딩 과정에서 1,918의 정수로 인코딩 되었고, 그에 따라 단어 집합의 크기만큼의 행을 가지는 테이블에서 인덱스 1,918번에 위치한 행을 great의 임베딩 벡터로 사용
- 임베딩 벡터 : 모델의 입력
- 역전파 과정에서 단어 great의 임베딩 벡터값이 학습됨
1) 케라스의 임베딩 층 구현 코드
vocab_size = 20000 # 텍스트 데이터의 전체 단어 집합의 크기
output_dim = 128 # 워드 임베딩 후의 임베딩 벡터의 차원
input_length = 500 # 입력 시퀀스의 길이
v = Embedding(vocab_size, output_dim, input_length=input_length)
갖고 있는 각 샘플의 길이가 500개이면, input_length는 500이다.
- Embedding() : 2D 정수 텐서 입력 받음
- 각 샘플은 정수 인코딩이 된 결과의 정수 시퀀스
- Embedding() : 워드 임베딩 작업 수행 후 3D 실수 텐서 리턴
2) 임베딩 층 사용하기
- 문장의 긍/부정 판단하는 감정 분류 모델 생성
import numpy as np
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
sentences = ['nice great best amazing', 'stop lies', 'pitiful nerd', 'excellent work', 'supreme quality', 'bad', 'highly respectable'] # 문장 생성
y_train = [1, 0, 0, 1, 1, 0, 1] # 긍정 : 1, 부정 : 0
tokenizer = Tokenizer()
tokenizer.fit_on_texts(sentences)
vocab_size = len(tokenizer.word_index) + 1 # 패딩을 고려하여 +1
print('단어 집합 :',vocab_size)
# 케라스의 Tokenizer()사용하여 단어 집합 생성 후 크기 확인
>>> 단어 집합 : 16
# 각 문장에 대해 정수 인코딩 수행
X_encoded = tokenizer.texts_to_sequences(sentences)
print('정수 인코딩 결과 :',X_encoded)
>>> 정수 인코딩 결과 : [[1, 2, 3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13], [14, 15]]
max_len = max(len(l) for l in X_encoded) # 가장 길이가 긴 문장의 길이 구함
print('최대 길이 :',max_len)
>>> 최대 길이 : 4
X_train = pad_sequences(X_encoded, maxlen=max_len, padding='post') # 최대 길이로 모든 샘플에 대해서 패딩 진행
y_train = np.array(y_train)
print('패딩 결과 :')
print(X_train)
>>> 패딩 결과 :
[[ 1 2 3 4]
[ 5 6 0 0]
[ 7 8 0 0]
[ 9 10 0 0]
[11 12 0 0]
[13 0 0 0]
[14 15 0 0]]
- 훈련 데이터에 대한 전처리 완료
- 이진 분류 모델 설계
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding, Flatten
embedding_dim = 4
model = Sequential()
model.add(Embedding(vocab_size, embedding_dim, input_length=max_len))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
model.fit(X_train, y_train, epochs=100, verbose=2)
- 출력층 : 1개의 뉴런 배치, 활성화 함수 : 시그모이드 함수
- 손실 함수 : binary_crossentropy
- epoch : 100으로 학습
09) 사전 훈련된 워드 임베딩 (Pre-Trained Word Embedding) 사용하기
- 사용에 따라 훈련 데이터로 이미 학습되어져 있는 임베딩 벡터들을 사용 하는 것이 성능 개선에 좋음
- 사전 훈련된 GloVe와 Word2Vec 임베딩을 사용해서 모델 훈련 실습 진행
# 훈련 데이터는 앞서 사용했던 데이터에 동일한 전처리까지 진행된 상태라고 가정
print(X_train)
>>>
[[ 1 2 3 4]
[ 5 6 0 0]
[ 7 8 0 0]
[ 9 10 0 0]
[11 12 0 0]
[13 0 0 0]
[14 15 0 0]]
print(y_train)
>>> [1, 0, 0, 1, 1, 0, 1]
1) 사전 훈련된 GloVe 사용하기
# 파일 다운로드
from urllib.request import urlretrieve, urlopen
import gzip
import zipfile
urlretrieve("<http://nlp.stanford.edu/data/glove.6B.zip>", filename="glove.6B.zip")
zf = zipfile.ZipFile('glove.6B.zip')
zf.extractall()
zf.close()
embedding_dict = dict()
f = open('glove.6B.100d.txt', encoding="utf8") # glove.6B.100d.txt에 있는 모든 임베딩 벡터 불러옴
for line in f:
word_vector = line.split()
word = word_vector[0]
word_vector_arr = np.asarray(word_vector[1:], dtype='float32')
embedding_dict[word] = word_vector_arr
f.close()
print('%s개의 Embedding vector가 있습니다.' % len(embedding_dict))
>>> 400000개의 Embedding vector가 있습니다.
- 총 40만개의 임베딩 벡터 존재 확인
print(embedding_dict['respectable']) # 임베딩 벡터값 출력
print('벡터의 차원 수 :',len(embedding_dict['respectable'])) # 크기 출력
>>> [-0.049773 0.19903 0.10585 ... 중략 ... -0.032502 0.38025 ]
벡터의 차원 수 : 100
# 풀고자 하는 문제의 단어 집합 크기의 행과 100개의 열을 가지는 행렬 생성
embedding_matrix = np.zeros((vocab_size, 100)) # 행렬 값 전부 0으로 채움 -> 행렬에 사전 훈련된 임베딩 값 넣어줌
print('임베딩 행렬의 크기(shape) :',np.shape(embedding_matrix)
>>> 임베딩 행렬의 크기(shape) : (16, 100)
print(tokenizer.word_index.items()) # 기존 데이터의 각 단어와 맵핑된 정수값 확인
>>> dict_items([('nice', 1), ('great', 2), ('best', 3), ('amazing', 4), ('stop', 5), ('lies', 6), ('pitiful', 7), ('nerd', 8), ('excellent', 9), ('work', 10), ('supreme', 11), ('quality', 12), ('bad', 13), ('highly', 14), ('respectable', 15)])
- 단어 great에 맵핑된 정수 : 2
print('단어 great의 맵핑된 정수 :',tokenizer.word_index['great']
>>> 단어 great의 맵핑된 정수 : 2
print(embedding_dict['great']) # 사전 훈련된 GloVe에서 great의 벡터값 확인
>>> [-0.013786 0.38216 0.53236 0.15261 -0.29694 -0.20558
.. 중략 ...
-0.69183 -1.0426 0.28855 0.63056 ]
# 단어 집합의 모든 단어에 대해서 사전 훈련된 GloVe의 임베딩 벡터들을 맵핑한 후 great의 벡터값이 의도한 인덱스의 위치에 삽입되었는지 확인
for word, index in tokenizer.word_index.items():
# 단어와 맵핑되는 사전 훈련된 임베딩 벡터값
vector_value = embedding_dict.get(word)
if vector_value is not None:
embedding_matrix[index] = vector_value
embedding_matrix[2]
>>> array([-0.013786 , 0.38216001, 0.53236002, 0.15261 , -0.29694 ,
... 중략 ...
-0.39346001, -0.69182998, -1.04260004, 0.28854999, 0.63055998])
- 사전에 훈련된 GloVe에서의 great의 벡터값과 일치
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding, Flatten
output_dim = 100
model = Sequential()
e = Embedding(vocab_size, output_dim, weights=[embedding_matrix], input_length=max_len, trainable=False)
model.add(e)
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
model.fit(X_train, y_train, epochs=100, verbose=2)
- Embedding layer에 embedding_matrix를 초기값으로 설정
- 사전 훈련된 워드 임베딩을 100차원의 값인 것으로 사용하고 있기 때문에 임베딩 층의 output_dim의 인자값으로 100을 주어야 함
- 사전 훈련된 워드 임베딩을 그대로 사용할 경우 추가 훈련을 하지 않는다는 의미에서 trainable 인자값을 False로 선택
2) 사전 훈련된 Word2Vec 사용하기
# 구글의 사전 훈련된 Word2Vec 모델 로드
import gensim
urlretrieve("<https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz>", \\
filename="GoogleNews-vectors-negative300.bin.gz")
word2vec_model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin.gz', binary=True)
print('모델의 크기(shape) :',word2vec_model.vectors.shape) # 모델의 크기 확인
>>> 모델의 크기(shape) : (3000000, 300)
- 300의 차원을 가진 Word2Vec 벡터 3,000,000개 존재
# 모든 값이 0으로 채워진 임베딩 행렬 생성
# 풀고자 하는 문제의 단어 집합 크기의 행과 300개의 열을 가진 행렬 생성
embedding_matrix = np.zeros((vocab_size, 300))
print('임베딩 행렬의 크기(shape) :',np.shape(embedding_matrix)
>>> 임베딩 행렬의 크기(shape) : (16, 300)
def get_vector(word):
if word in word2vec_model:
return word2vec_model[word]
else:
return None
- get_vector() 함수 생성
- word2vec_model : 특정 단어 입력하면 해당 단어의 임베딩 벡터 리턴
- 만약, word2vec_model에 특정 단어의 임베딩 벡터가 없다면 None 리턴
# 단어 집합으로부터 단어를 1개씩 호출하여 word2vec_model에 해당하는 단어의 임베딩 벡터값이 존재하는지 확인
for word, index in tokenizer.word_index.items():
# 단어와 맵핑되는 사전 훈련된 임베딩 벡터값
vector_value = get_vector(word)
if vector_value is not None: # 만약 None이 아니면 존재한다는 뜻
embedding_matrix[index] = vector_value # 임베딩 행렬에 해당 단어의 인덱스 위치의 행에 임베딩 벡터의 값 저장
- 현재 풀고자 하는 문제의 16개의 단어와 맵핑되는 임베딩 행렬 완성
- 제대로 맵핑이 되었는지 확인
print(word2vec_model['nice']) # nice 의 임베딩 벡터값 확인
>>>
[ 0.15820312 0.10595703 -0.18945312 0.38671875 0.08349609 -0.26757812
0.08349609 0.11328125 -0.10400391 0.17871094 -0.12353516 -0.22265625
... 중략 ...
-0.16894531 -0.08642578 -0.08544922 0.18945312 -0.14648438 0.13476562
-0.04077148 0.03271484 0.08935547 -0.26757812 0.00836182 -0.21386719]
print('단어 nice의 맵핑된 정수 :', tokenizer.word_index['nice']) # nice의 맵핑된 정수 확인
>>> 단어 nice의 맵핑된 정수 : 1
print(embedding_matrix[1]) # 1이므로 embedding_matrix[1]에는 nice의 임베딩 벡터값이 있어야 함
>>>
[ 0.15820312 0.10595703 -0.18945312 0.38671875 0.08349609 -0.26757812
0.08349609 0.11328125 -0.10400391 0.17871094 -0.12353516 -0.22265625
... 중략 ...
-0.16894531 -0.08642578 -0.08544922 0.18945312 -0.14648438 0.13476562
-0.04077148 0.03271484 0.08935547 -0.26757812 0.00836182 -0.21386719]
- word2vec_model 의 값과 동일
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding, Flatten, Input
model = Sequential()
model.add(Input(shape=(max_len,), dtype='int32'))
e = Embedding(vocab_size, 300, weights=[embedding_matrix], input_length=max_len, trainable=False)
model.add(e)
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
model.fit(X_train, y_train, epochs=100, verbose=2)
- Embedding()에 사전 훈련된 embedding_matrix를 입력으로 넣고 모델 학습
'공부하는 습관을 들이자 > Deep Learning (NLP,LLM)' 카테고리의 다른 글
딥러닝을 이용한 자연어처리 입문 #12 (0) | 2024.01.03 |
---|---|
[딥러닝 자연어처리] 10. (1) 워드 임베딩 ~ 7) 자모 단위 한국어 fast text 학습하기 (0) | 2024.01.01 |
[딥러닝 자연어처리] 9-3. (08) 케라스의 SimpleRNN과 LSTM 이해하기- (11) 문자 단위 RNN (0) | 2023.12.28 |
[딥러닝 자연어처리] 9-2. (05) 양방향 순환 신경망 - (07) 게이트 순환 유닛 (0) | 2023.12.26 |
[딥러닝 자연어처리] 9-1. (01) 순환 신경망 (Recurrent Neural Network) (0) | 2023.12.25 |