사전 훈련된 단어 임베딩 사용하기
- 훈련데이터가 부족하다면 작업에 맞는 단어 임베딩을 학습할 수 없음
- 대신 미리 계산된 임베딩 공간에서 임베딩 벡터를 로드할 수 있음 ( pre-trained 불러오기)
- 단어 임베딩은 일반적으로 단어 출현 통계를 사용하여 계산됨
<Word2vec 알고리즘>
- word2vec의 차원은 성별처럼 구체적인 의미가 있는 속성을 잡아냄
- 케라스의 Embedding층을 위해 내려 받을 수 있는 미리 계산된 단어 임베딩 데이터베이스가 여럿있음
- 그 중 하나가 word2vec 임
<GloVe 알고리즘>
- 인기 있는 또 다른 하나는 GloVe(Global Vectors for Word Representation) 임
- 이 기법은 단어의 동시 출현(co-occurrence)12 통계를 기록한 행렬을 분해하는 기법을 사용함
- 데이터에서 가져온 수백만 개의 영어 토큰에 대해서 임베딩을 미리 계산해놓음
glove 를 예시로 코드를 구현
0. 라이브러리 불러오기
import os
import numpy as np
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout, Bidirectional
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
1. GloVe 임베딩 로드
https://nlp.stanford.edu/projects/glove/
> 해당 링크 내의 glove.6B.zip 파일을 다운받아 사용했다.
# 임베딩 로드
glove_dir= '/content/glove.6B/'
embeddings_index = {}
f = open(os.path.join(glove_dir, 'glove.6B.100d.txt'))
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
embeddings_index[word] = coefs
f.close
print(len(embeddings_index),'개의 단어벡터')
# 400000 개의 단어벡터
> 100d.txt는 각 단어당 100차원으로 임베딩 된 값들이 들어있다.
> embeddings_index= { 'the': [0.2342, 0.5454, 0.4352, 4.4846, 3.4174, ... ], ...}
위와 같이 단어를 key로 두고 100차원의 임베딩 값을 value로 하는 embeddings_index 딕셔너리를 만든다.
2. IMDB 데이터 셋 로드 및 전처리
앞서 사용했던 imdb 데이터 셋을 불러와 모델 학습의 데이터 셋으로 사용
max_words= 10000
maxlen= 100
(x_train, y_train),(X_test, y_test) = imdb.load_data(num_words=max_words)
x_train = pad_sequences(x_train, maxlen=maxlen)
X_test = pad_sequences(X_test, maxlen=maxlen)
> imdb datasets은 리뷰 문장마다 인덱스의 모음으로 되어있는 상태이다.
즉, 토큰화와 정수 인코딩 이라는 텍스트 전처리가 끝난 상태라고 할 수 있다.
x_train
"""array([[1415, 33, 6, ..., 19, 178, 32],
[ 163, 11, 3215, ..., 16, 145, 95],
[1301, 4, 1873, ..., 7, 129, 113],
...,
[ 11, 6, 4065, ..., 4, 3586, 2],
[ 100, 2198, 8, ..., 12, 9, 23],
[ 78, 1099, 17, ..., 204, 131, 9]], dtype=int32)
"""
> x_train을 출력해보면 이런 형태를 띄고 있음을 확인할 수 있다.
> 여기서 갑자기 헷갈렸던 부분!
glove 데이터는 일반적 단어들을 이미 임베딩한 상태의 데이터이고,
imdb 데이터는 우리가 예측하고자 하는 특정 주제의 데이터라고 보면 된다.
3. IMDB 데이터셋의 단어 인덱스를 GloVe 임베딩에 매핑
word_index= imdb.get_word_index() # 최다 빈도 순으로 인덱스 부여
embedding_dim =100
embedding_matrix= np.zeros((max_words, embedding_dim))
for word, i in word_index.items():
if i < max_words: # 상위 max_words개의 단어에 들어가는 단어이고
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None: # 기존 glove에서 정의했던 단어의 벡터라면
embedding_matrix[i] = embedding_vector # 새로 zero로 초기화 했던 행렬에 추가
> imdb 셋에서 word_index를 가져오고, glove 데이터셋과 imdb 데이터 셋이 겹치는 것을 가져오도록 한다.
이말인 즉슨, imdb의 빈도 상위 10000개(= max_words)의 단어 중 glove에도 포함되어있는 단어라면 embedding_matrix라는 초기화 시킨 2차원 배열에 넣는다.
> 따라서, imdb의 상위 단어 중 glove에서 임베딩벡터값이 정의된 단어라면, 가져온다!
> 여기서 드는 의문, 그럼 10,000개가 모두 채워지지 않을 수도 있겠는데..?
※ Correct! 약 200개 정도 비어있는 것으로 확인..!
4. 모델 정의
model= Sequential()
model.add(Embedding(max_words, embedding_dim, weights= [embedding_matrix],
input_length= maxlen, trainable= False)) # 임베딩 벡터 갱신 x
model.add (Bidirectional(LSTM(64, return_sequences= True)))
model.add(Dropout(0.5))
model.add (Bidirectional(LSTM(64)))
model.add(Dense(32, activation= 'relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation= 'sigmoid'))
model.summary()
- Embedding ( )
- weights : 각 단어의 인덱스에 해당하는 벡터값들의 행렬
- trainable : 모델학습 과정에서 임베딩 가중치를 업데이트 할 것인지 (False : 미리 학습된 임베딩을 고정된 상태로 사용)
> 여기서 생겼던 의문!
Embedding함수는 단어 인덱스를 임베딩 벡터로 매핑하는 역할을 하는데,
위와 같은 경우에는 이미 벡터화가 되어있는 GloVe 임베딩을 사용하였기 때문에
embedding함수를 사용하면서 굳이 trainable 인자를 False로 하는 이유가 있을까..?
Embedding 레이어를 사용하면 임베딩 벡터를 다른 레이어와 쉽게 통합할 수 있습니다. 딥러닝 모델은 일반적으로 여러 레이어로 구성되므로, 임베딩 레이어를 사용하면 임베딩 벡터를 모델 내에서 다른 레이어와 자연스럽게 연결할 수 있습니다. - G선생 왈
> Embedding함수가 아니고 Embedding레이어라고 생각하면 맞는 말인듯!
5. 모델 컴파일
model.compile(optimizer= Adam(learning_rate= 0.0001), loss= 'binary_crossentropy', metrics= ['accuracy'])
- optimizer= 'adam' 의 경우는 기본설정된 adam 옵티마이저를 사용하지만,
Adam( )객체를 사용하는 경우에는 하이퍼파라미터를 변경할 수 있어 세밀한 옵티마이저 설정이 가능하다.
6. 모델 학습
history= model.fit(x_train, y_train, epochs= 10, batch_size= 128, validation_data= (X_test, y_test))
7. 정확도와 손실 그래프 그리기
acc= history.history['accuracy']
val_acc= history.history['val_accuracy']
loss= history.history['loss']
val_loss= history.history['val_loss']
epochs= range(1, len(acc)+1)
plt.figure(figsize= (12, 5))
# 정확도 그래프
plt.subplot(1, 2, 1)
plt.plot(epochs, acc, 'bo', label= 'Training acc')
plt.plot(epochs, val_acc, 'b', label= 'Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
# 손실 그래프
plt.subplot(1, 2, 2)
plt.plot(epochs, loss, 'bo', label= 'Training loss')
plt.plot(epochs, val_loss, 'b', label= 'Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
8. 모델을 사용하여 예측
test_texts = [
"I hated this movie. It was terrible and the acting was horrible.",
"This was the worst film I have ever seen. Not worth the time.",
"I loved this movie. It was fantastic and the acting was great.",
"This was the best film I have seen in a long time. Totally worth it.",
"I had high hopes for this movie, but it was a complete letdown. The storyline was incredibly dull, and the pacing was excruciatingly slow. The characters lacked depth, making it hard to care about their fates. Overall, a waste of time.",
"This film was a disaster from start to finish. The dialogue was cringe-worthy, and the special effects looked outdated. I couldn't believe how poorly executed the entire production was. Definitely one to avoid.",
"What an amazing film! The plot was deeply engaging, keeping me on the edge of my seat throughout. The actors delivered powerful performances that brought the story to life. This movie is a must-watch for everyone.",
"I was thoroughly impressed by this film. The cinematography was stunning, capturing the beauty of every scene. The story was heartwarming and inspiring, and the music perfectly complemented the mood. An outstanding cinematic experience."
]
# 새로운 텍스트 데이터에 대해 토큰화 및 패딩 적용
tokenizer= Tokenizer(num_words= max_words)
tokenizer.fit_on_texts(test_texts)
test_sequences= tokenizer.texts_to_sequences(test_texts)
test_data= pad_sequences(test_sequences, maxlen= maxlen)
# 새로운 텍스트 데이터에 대해 예측
predictions= model.predict(test_data)
# 예측 결과 출력
for i, text in enumerate(test_texts):
print('text: ',text)
print('predicted sentiment: ', 'positive' if predictions[i] >0.5 else 'negative')
print('prediction score: ', predictions[i][0])
> 여기서 또 하나의 의문!
테스트시에는 새로운 토크나이저를 정의하여 fit_on_texts를 하지 않는다. 이유는 기존에 학습했던 토크나이저를 테스트 데이터에도 그대로 적용해야 학습에서 사용했던 방식으로 동일하게 매핑되기 때문이다.
그래서,, 엄... 만약 테스트시 학습할때 사용했던 tokenizer가 살아있다면, 그 토크나이저를 불러와 사용할 것 !
혹은
위와 같이 학습시에 Tokenizer를 사용하지 않았다면, 테스트 텍스트를 토크나이징 하여 embedding_matrix와 매핑 시켜야 할 듯하다..
후 그럼 주먹구구 코드 살짝쿵 짜야 할거 같은데,,
임베딩 벡터를 사용한 경우 정확도가 떨어지는 이유
사실 테스트 결과..
text: I hated this movie. It was terrible and the acting was horrible.
predicted sentiment: positive
prediction score: 0.6064338
text: This was the worst film I have ever seen. Not worth the time.
predicted sentiment: positive
prediction score: 0.5905847
text: I loved this movie. It was fantastic and the acting was great.
predicted sentiment: positive
prediction score: 0.676383
text: This was the best film I have seen in a long time. Totally worth it.
predicted sentiment: positive
prediction score: 0.623778
text: I had high hopes for this movie, but it was a complete letdown. The storyline was incredibly dull, and the pacing was excruciatingly slow. The characters lacked depth, making it hard to care about their fates. Overall, a waste of time.
predicted sentiment: positive
prediction score: 0.81819546
text: This film was a disaster from start to finish. The dialogue was cringe-worthy, and the special effects looked outdated. I couldn't believe how poorly executed the entire production was. Definitely one to avoid.
predicted sentiment: positive
prediction score: 0.6840857
text: What an amazing film! The plot was deeply engaging, keeping me on the edge of my seat throughout. The actors delivered powerful performances that brought the story to life. This movie is a must-watch for everyone.
predicted sentiment: positive
prediction score: 0.86795545
text: I was thoroughly impressed by this film. The cinematography was stunning, capturing the beauty of every scene. The story was heartwarming and inspiring, and the music perfectly complemented the mood. An outstanding cinematic experience.
predicted sentiment: positive
prediction score: 0.7537438
> 모든 문장이 긍정으로 나왔다..
내가 생각하는 문제점이 두가지 있는데,
- 테스트 데이터 사용시 토크나이저 문제
- embedding_matrix 의 공벡터 문제
<이론적으로 임베딩 벡터를 사용한 경우 정확도가 떨어지는 경우>
- 사전 훈련된 GloVe임베딩 벡터가 IMDB 데이터셋의 특징을 완벽하게 담아내지 못하기 때문→ GloVe는 일반적인 단어의 모음, IMDB는 영화 리뷰에 대한 텍스트
→ 특정 도메인에서의 특징을 반영하지 못함 - 고정된 임베딩
→ GloVe 임베딩을 학습되지 않도록 고정하면, 모델이 IMDB 데이터셋의 특성에 맞게 임베딩을 조정할 수 없음
→ Embedding 레이어에서 Trainable 인자를 True로 했다면 특성에 맞게 조절될 뿐만 아니라, 0으로 되어있던 벡터값도 유효한 벡터값으로 모델에 입력되었을듯!
'Natural Language Processing' 카테고리의 다른 글
[Goorm] 딥리닝을 이용한 자연어 처리 5 (LSTM 이용) (0) | 2024.07.06 |
---|---|
[Goorm] 딥러닝을 이용한 자연어 처리 4 (RNN & LSTM) (4) | 2024.07.05 |
[Goorm] 딥러닝을 이용한 자연어 처리 2 (IMDB 이용) (0) | 2024.07.05 |
[Goorm] 딥러닝을 이용한 자연어 처리 1 (토큰화 & 임베딩) (0) | 2024.06.23 |
[ChatGPT] 프롬프트 이용하여 시스템 만들기 (0) | 2023.08.04 |