케라스 Word2Vec Skipgram, CBOW 구현 - YouTube
전처리
sklearn.datasets.fetch_20newsgroups — scikit-learn 1.0.2 documentation
참고! 뉴스에는 헤더 / 푸터 / 인용구가 들어가는데, 이런 부분을 제외하고자 한다면 remove에 튜플로 넣어서 제외 가능.
# 데이터 & 전처리
from sklearn.datasets import fetch_20newsgroups
dataset = fetch_20newsgroups(shuffle=True, random_state=0, remove=('headers', 'footers', 'quotes'))
documents = dataset.data
print(len(documents))
documents[0] # 이메일주소, 개행문자, 숫자가 있으니 전처리 해주자!
import re
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
nltk.download('stopwords')
nltk.download('punkt')
def clean_text(d): # 문자열만 추출
pattern = r'[^a-zA-Z\s]'
text = re.sub(pattern, '', d)
return text
def clean_stopword(d):
stop_words = stopwords.words('english')
return ' '.join([w.lower() for w in d.split() if w.lower() not in stop_words and len(w) > 3])
def tokenize(d):
return word_tokenize(d)
import pandas as pd
news_df = pd.DataFrame({'article':documents})
len(news_df) # 11314
news_df.replace('', float("NaN"), inplace=True)
news_df.dropna(inplace=True)
len(news_df) # 11096
news_df['article'] = news_df['article'].apply(clean_text)
news_df['article'] = news_df['article'].apply(clean_stopword)
news_df
news_df = news_df.reset_index()
del news_df['index']
news_df
tokenized_news = news_df['article'].apply(tokenize)
tokenized_news =tokenized_news.to_list()
import numpy as np
drop_news = [index for index, sentence in enumerate(tokenized_news) if len(sentence) <= 1] # 짧은 문장 인덱스리스트
news_texts = np.delete(tokenized_news, drop_news, axis = 0) # 인덱스리스트에 있는 경우 지워라
print(len(news_texts)) # 10939
# 단어 사전 만들기
from tensorflow.keras.preprocessing.text import Tokenizer
news_2000 = news_texts[:2000]
tokenizer = Tokenizer()
tokenizer.fit_on_texts(news_2000)
idx2word = {value:key for key, value in tokenizer.word_index.items()}
sequences = tokenizer.texts_to_sequences(news_2000)
vocab_size = len(tokenizer.word_index) + 1
print(vocab_size) # 31365
Skipgram
Skipgram 전처리
- 네거티브 샘플링(Negative Sampling)
- Word2Vec은 출력층이 내놓는 값에 소프트맥스 함수를 적용해 확률값으로 변환한 후 이를 정답과 비교해 역전파(backpropagation)
- 소프트맥스를 적용하려면 분모에 해당하는 값, 즉 중심단어와 나머지 모든 단어의 내적을 한 뒤, 이를 다시 exp 계산을 하는데 전체 단어가 많을 경우 엄청난 계산량 발생
- 네거티브 샘플링은 소프트맥스 확률을 구할 때 전체 단어를 대상으로 구하지 않고, 일부 단어만 뽑아서 계산을 하는 방식
- 네거티브 샘플링 동작은 사용자가 지정한 윈도우 사이즈 내에 등장하지 않는 단어(negative sample)를 5~20개 정도 뽑고, 이를 정답단어와 합쳐 전체 단어처럼 소프트맥스 확률을 계산하여 파라미터 업데이트
from tensorflow.keras.preprocessing.sequence import skipgrams
skip_grams = [skipgrams(sample, vocabulary_size=vocab_size, window_size=10) for sample in sequences[:10]] # 10개 단어 샘플!
pairs, labels = skip_grams[0][0], skip_grams[0][1]
for i in range(5):
print("{:s}({:d}), {:s}({:d}) -> {:d}".format(
idx2word[pairs[i][0]], pairs[i][0],
idx2word[pairs[i][1]], pairs[i][1],
labels[i]))
# held(912), quote(939) -> 1
# charismatic(8463), moteccinfops(22899) -> 0
# generally(913), fuer(15246) -> 0
# christ(416), opposed(1480) -> 1
# majority(1188), bosch(10313) -> 0
print(len(skip_grams), len(pairs), len(labels)) # 10 5020 5020
skip_grams = [skipgrams(sample, vocabulary_size=vocab_size, window_size=10) for sample in sequences]
# skipgram 모델 구성
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Embedding, Reshape, Activation, Input, Dot
from tensorflow.keras.utils import plot_model
embed_size = 50
def word2vec():
target_inputs = Input(shape=(1,), dtype='int32')
target_embedding = Embedding(vocab_size, embed_size)(target_inputs)
context_inputs = Input(shape=(1,), dtype='int32')
context_embedding = Embedding(vocab_size, embed_size)(context_inputs)
dot_product = Dot(axes=2)([target_embedding, context_embedding])
dot_product = Reshape((1,), input_shape=(1,1))(dot_product)
output = Activation('sigmoid')(dot_product)
model = Model(inputs=[target_inputs, context_inputs], outputs=output)
model.compile(loss='binary_crossentropy', optimizer='adam')
return model
skip-gram 모델 구성
model = word2vec()
model.summary()
plot_model(model, show_shapes=True, show_layer_names=True)
for epoch in range(1,11):
loss = 0
for _, elem in enumerate(skip_grams):
first_elem = np.array(list(zip(*elem[0]))[0], dtype='int32')
second_elem = np.array(list(zip(*elem[0]))[1], dtype='int32')
labels = np.array(elem[1], dtype='int32')
X = [first_elem, second_elem] # 학습데이터
Y = labels # 정답
loss += model.train_on_batch(X,Y) # 한번의 epoch에 여러 번 트레이닝
print('Epoch : ', epoch, 'Loss : ', loss)
성능이.. 별로 안좋네용...
import gensim
f = open('skipgram.txt', 'w')
f.write('{} {}\n'.format(vocab_size-1, embed_size))
vectors = model.get_weights()[0]
for word, i in tokenizer.word_index.items():
f.write('{} {}\n'.format(word, ' '.join(map(str, list(vectors[i,:])))))
f.close()
skipgram = gensim.models.KeyedVectors.load_word2vec_format('skipgram.txt', binary=False)
CBOW
CBOW 전처리
skipgram 전처리했던 것 cbow로 변환시켜서 사용할게용
# CBOW
def skipgram2cbow(skipgrams):
cbows = []
flag = 0
for n in skip_grams:
temp1 = []
for t in n :
if flag == 0:
temp1.append(t)
else:
flag = 1
tmp2 = []
for x in t:
temp2.append([x[1], x[0]])
temp1.append(temp2)
cbows.append(temp1)
return cbows
cbows = skipgram2cbow(skip_grams)
pairs, labels = cbows[0][0], cbows[0][1]
for i in range(5):
print('{:s}({:d}), {:s}({:d}) -> {:d}'.format(
idx2word[pairs[i][0]], pairs[i][0],
idx2word[pairs[i][1]], pairs[i][1],
labels[i]))
print(len(cbows), len(pairs), len(labels)) # 2000 5020 5020
skip-gram : 타겟 단어로 문맥 맞추기
cbow : 문맥 가지고 타겟 단어 맞추기
-> cbow가 skip-gram 보다 훨씬 간단한 문제. 따라서 skip-gram보다 훨씬 더 많이 학습시켜도 시간은 비슷할듯
CBOW 모델 구성
model = word2vec()
model.summary()
plot_model(model, show_shapes=True, show_layer_names=True)
for epoch in range(1,11):
loss = 0
for _, elem in enumerate(cbows):
first_elem = np.array(list(zip(*elem[0]))[0], dtype='int32')
second_elem = np.array(list(zip(*elem[0]))[1], dtype='int32')
labels = np.array(elem[1], dtype='int32')
X = [first_elem, second_elem] # 학습데이터
Y = labels # 정답
loss += model.train_on_batch(X,Y) # 한번의 epoch에 여러 번 트레이닝
print('Epoch : ', epoch, 'Loss : ', loss)
import gensim
f = open('cbow.txt', 'w')
f.write('{} {}\n'.format(vocab_size-1, embed_size))
vectors = model.get_weights()[0]
for word, i in tokenizer.word_index.items():
f.write('{} {}\n'.format(word, ' '.join(map(str, list(vectors[i,:])))))
f.close()
cbow = gensim.models.KeyedVectors.load_word2vec_format('cbow.txt', binary=False)
'Computer > ML·DL·NLP' 카테고리의 다른 글
[이수안컴퓨터연구소] 감정 분석 (0) | 2022.01.31 |
---|---|
[이수안컴퓨터연구소] CNN 스팸 메일 분류 (0) | 2022.01.31 |
[이수안컴퓨터연구소] 합성곱 신경망 Convolution Neural Network (0) | 2022.01.11 |
[이수안컴퓨터연구소] 순환 신경망 Recurrent Neural Network (0) | 2022.01.10 |
[이수안컴퓨터연구소] 임베딩 Embedding (0) | 2022.01.08 |
댓글