[NLP] 단어부터 문장까지 GloVe Embedding 하기 / Clustering 까지
워드 임베딩 방법론 중 하나인 GloVe에 대해서 직접 임베딩하는 과정을 알아보겠습니다. 단어 단위로 임베딩하고 문장 단위의 임베딩으로 바꾼 후 clustering 까지 하는 과정을 담았습니다.
제가 실제로 해커톤과 프로젝트에 이용한 방법들을 기준으로 합니다.
저는 주로 한국어 문장을 단어(토큰) 별로 피쳐로 바꿔서 이용하기 위해서 GloVe로 임베딩 하는 과정을 사용했습니다.
(한국어 데이터들을 사용했습니다.)
GloVe 모델 자체에 대한 자세한 설명은 아래 블로그 글을 참고해주세요.
저는 GloVe가 Word2Vec 과 같은 워드 임베딩 방법론 중에 통계 정보 를 추가한 방법론이라고 설명하고 넘어가겠습니다.
* colab 환경 이용
1. GloVe 모델 불러오기
# colab 환경에 glove를 설치해줍니다.
! pip install glove_python
# import 해서 불러옵니다.
from glove import Corpus, Glove
이 때 vocab(corpus, 단어사전) 을 만들어주는 Corpus도 불러와줍니다.
2. Corpus 만들기
# corpus 생성
corpus = Corpus()
corpus.fit(sent_token, window=20)
corpus를 위와 같이 만들어줍니다. 이 때 sent_token을 우리가 가지고 있는 토큰 데이터를 넣어주시면 됩니다.
단 형식은 문장(sentence)이 token 화 된 모습이어야합니다.
안녕하세요. 지니티토리입니다. -> [ [안녕, 하세요], [지니티토리, 입니다] ]
corpus 라이브러리는 이 형식이 아니면 에러가 납니다.
문장 길이가 길고, 양 옆으로 넓은 범위까지 보기 위해서 window를 조금 큰 크기인 20으로 설정 해줬습니다.
3. GloVe 학습시키기
# model
glove = Glove(no_components=128, learning_rate=0.01) # 0.05
%time glove.fit(corpus.matrix, epochs=50, no_threads=4, verbose=False) # Wall time: 8min 32s
glove.add_dictionary(corpus.dictionary)
# save
glove.save(DATA_DIR + '/glove_w20_epoch50.model')
위와 같은 파라미터 설정으로 모델을 학습시켰습니다. 100이상의 차원인 128차원, learning rate 0.01 을 설정해줬습니다.
이는 단어가 128차원으로 임베딩된 벡터로 나온다는 것을 설정해준 것입니다.
glove.fit 에는 위에서 만들어준 corpus를 matrix 형태로 넣어주게 됩니다. epoch을 50으로 학습시켰습니다.
학습 시간을 알아보기 위해서 %time을 앞에 넣어줬고 생략 가능합니다. 저는 약 6만개의 한국어 문장에 대해서 8분 50초가 걸렸습니다.
혹시 모르기 때문에 단어사전에 corpus를 추가하고 얼른 저장도 해주었습니다. 다른 모델과 비교하기 위해 모델 이름에 window 사이즈와 epoch을 넣어주고, .model을 붙였습니다.
4. GloVe 모델 불러오고 word_dictionary 만들기
# load glove
glove_model = Glove.load(DATA_DIR + '/glove_w20_epoch50.model')
학습시킨 모델을 불러오는 것은 아주 간단합니다. Glove.load 를 통해서 불러올 수 있습니다.
이제 불러온 모델을 가지고 이 모델이 한국어 단어들에 대해서 어떤 임베딩 벡터를 내보내는지 보고 싶습니다.
이때 단순히 내장된 glove.word_vectors를 사용하거나, 단어사전을 불러오면 큰 차원, 큰 데이터 양 때문에 에러가 나기 일수였습니다.
따라서 다음과 같은 코드(프로세스)를 통해서 word dictionary를 따로 만들고 pickle 저장해서 필요할 때나,
처음 보는 데이터에 대해서 워드 임베딩을 할 때 (인퍼러스 시) 등에 사용했습니다.
# word dict 생성
word_dict = {}
for word in glove_model.dictionary.keys():
word_dict[word] = glove_model.word_vectors[glove_model.dictionary[word]]
print('[Success !] Lengh of word dict... : ', len(word_dict))
# save word_dict
with open(DATA_DIR + '/glove_word_dict_128.pickle', 'wb') as f:
pickle.dump(word_dict, f)
print('[Success !] Save word dict!...')
제가 word2vec 워드 임베딩이나, glove 모델 등에 자주 사용하는 과정인데, 더 좋은 방법이 있으시면 사용하시면 됩니다.
5. 임베딩된 단어 벡터 확인하기
위의 과정을 모두 거치면 word_dict 딕셔너리에서 우리가 가진 한글 단어를 어떻게 임베딩한지 간단하게 볼 수 있습니다.
우울증이라는 단어에 대해서 위의 128차원으로 임베딩 되었습니다.
6. test 데이터 워드 임베딩
test 데이터, inference 시의 데이터에 대해서 워드 임베딩 사전을 만들 때는 np.zeros를 이용합니다.
train 데이터로 만들었던 word_dict 를 불러와서 진행합니다.
# word dict : train데이터의 임베딩 사전
total_word_dict = {}
cnt = 0
for word in test_tokens:
if word in word_dict.keys():
total_word_dict[word] = word_dict[word]
else:
word_dict[word] = np.zeros((128))
cnt += 1 # 처음 본 단어 갯수 세기
print('no train word -> 0....', cnt)
print('token -> word embedding....!',len(unique_tokens))
train데이터에 없는 처음 본 단어 들에 대해서는 128차원의 0 벡터로 임베딩 되었습니다.
6. 활용 : 워드 임베딩된 벡터로 문장 단위 임베딩
이제 각 128차원으로 임베딩된 단어로 이루어진 단어사전을 가지고 있습니다.
이를 활용해서 문장 단위의 임베딩을 진행해보겠습니다. 문장을 128차원으로 임베딩 하는 것으로, 주로 text classification 이나 clustering에 사용합니다. (같은 axis를 기준으로 벡터 값을 average 해버리기 때문입니다.)
def sent2vec_glove(tokens, embedding_dim=128):
'''문장 token 리스트를 받아서 임베딩 시킨다.'''
size = len(tokens)
matrix = np.zeros((size, embedding_dim))
word_table = word_dict # glove word_dict
for i, token in enumerate(tokens):
vector = np.array([
word_table[t] for t in token
if t in word_table
])
if vector.size != 0:
final_vector = np.mean(vector, axis=0)
matrix[i] = final_vector
return matrix
위의 코드는 전체 문장을 토큰화된 리스트들로 받아서 한번에 임베딩된 matrix로 만들어버립니다.
[
[안녕, 하세요], [지니티토리, 입니다], [자연어,처리, 많이, 어렵습니다]
]
와 같은 형식입니다.
GloVe 모델로 학습시켜서 임베딩된 문장을 K-means 알고리즘으로 clustering 도 진행해봤습니다.
시각화는 T-SNE로 진행했습니다.
# sklearn
from sklearn.cluster import KMeans
from sklearn.manifold import TSNE
# 시각화
import seaborn as sns
import matplotlib.pyplot as plt
# 문장 임베딩
sentence_glove = sent2vec_glove(sentence_tokens)
sentence_glove.shape
# clustering
k = 20
kmeans = KMeans(n_clusters=k, random_state=2021)
y_pred = kmeans.fit_predict(sentence_glove)
# tsne
tsne = TSNE(verbose=1, perplexity=100, random_state=2021) # perplexity : 유사정도
X_embedded = tsne.fit_transform(sentence_glove)
print('Embedding shape 확인', X_embedded.shape)
# 시각화
sns.set(rc={'figure.figsize':(20,20)})
# colors
palette = sns.hls_palette(10, l=.4, s=.9)
# plot
sns.scatterplot(X_embedded[:,0], X_embedded[:,1], hue=y_pred,
legend='full',palette=palette) # kmeans로 예측
plt.title('t-SNE with KMeans Labels and Glove Embedding')
plt.savefig(BASE_DIR + "/t-sne_question_glove_embedding.png")
plt.show()
(TSNE가 조금 시간이 걸리기 때문에 진행 상황을 보기 위해서 verbose=1로 설정했습니다. 설정 안하셔도 됩니다.)
결과는 위와 같습니다. average 했음에도 불구하고 문장을 glove로 임베딩 한 후 clustering을 하면 생각보다 꽤 잘나옵니다.
K-Means 알고리즘이기 때문에 추가로 최적의 k를 찾는 코드도 덧붙여 놓겠습니다.
# 최적의 K 찾기 : 군집 갯수 k 찾기
from sklearn import metrics
from scipy.spatial.distance import cdist
# K=50 개의 클러스터에 대해서 시각화
distortions = []
K = range(2, 50)
tqdm.pandas()
for k in K:
k_means = KMeans(n_clusters=k, random_state=42).fit(sentence_glove)
k_means.fit(sentence_glove)
distortions.append(sum(np.min(cdist(sentence_glove, k_means.cluster_centers_, 'euclidean'), axis=1)) / sentence_glove.shape[0])
print('Found distortion for {} clusters'.format(k))
# Visualization
X_line = [K[0], K[-1]]
Y_line = [distortions[0], distortions[-1]]
# Plot the elbow
plt.plot(K, distortions, 'b-')
plt.plot(X_line, Y_line, 'r')
plt.xlabel('k')
plt.ylabel('Distortion')
plt.title('Optimal K')
plt.show()
distortion 값을 기준으로 찾으며 시각화 한 결과는 아래와 같습니다. 시각화 결과를 보고 컷오프를 잘 설정해주시면 됩니다.
지금까지 GloVe를 이용한 자연어처리 방법을 간단하게 담아봤습니다. 궁금점이나 틀린 부분은 댓글로 남겨주세요!
'🤖 Today-I-Learned ] > Deep Learning' 카테고리의 다른 글
Apple paper 모음 (1) | 2021.03.04 |
---|---|
CNN 의 특징 3가지 (0) | 2021.02.25 |
[coursera NLP] week1. Neural Machine Translation (0) | 2021.02.18 |
[NLP] 딥러닝을 이용한 자연어 처리 입문 - Text Classification Questions 정리 (0) | 2020.11.24 |
[논문 리뷰] Deep Residual Learning for Image Recognition - ResNet(1) (2) | 2020.03.31 |