딥러닝을 이용한 자연어 처리 입문 교재를 바탕으로 작성되었습니다.
누렁이 = [0.2, 1.5, -3.1, ..., 5.1, 3.9] => 벡터의 차원이 128
밥을 먹는다
에서 ‘밥’과 ‘먹는다’는 비슷한 문맥에서 등장하므로 유사한 벡터값을 갖는다.The fat cat sat on the mat
V x M
행렬이 된다.M x V
행렬이 된다.The fat cat sat on the mat
: 중심단어 cat, window 범위 2 x → yThe fat cat sat on the mat
: 중심단어 cat, window 범위 2 x → ycat + The → 1
cat + fat → 1
cat + sat → 1
cat + on → 1
cat + tissue → 0
cat + computer → 0
…
아버지가 방에 들어가신다.
아버지 가방에 들어가신다.
방에 아버지의 가방이 있다.
윈도우 크기가 1이라고 할 때,
아버지 | 방 | 가방 | 들어가신다 | 있다 | |
---|---|---|---|---|---|
아버지 | 0 | 2 | 2 | 0 | 0 |
방 | 2 | 0 | 0 | 1 | 0 |
가방 | 2 | 0 | 0 | 1 | 1 |
들어가신다 | 0 | 1 | 1 | 0 | 0 |
있다 | 0 | 0 | 1 | 0 | 0 |
P(k|i) = ( i행 k열의 값) / (i행 값의 합계)
P(아버지|가방) = ( 2 / 4 ) = 0.5
P(있다|가방) = ( 1/ 4 ) = 0.25
P(아버지|가방)
/ P(있다|가방)
= 2 라는 값을 얻을 수 있다. 이를 이용해 loss를 도출한다.n=5일 때 apple은 <ap, app, ppl, ple, le>
가 될 수 있다. 여기서 꺽쇠는 시작과 끝을 의미한다.
여기에 기존 단어 앞뒤로 <, >를 붙인 토큰을 특별 토큰으로 벡터화 한다. <apple>
# n=5일 때 apple에 대한 토큰 벡터화
<ap, app, ppl, ple, le>, <apple>
# n = 3 ~ 6인 경우
<ap, app, ppl, ppl, le>, <app, appl, pple, ple>, <appl, pple>, ..., <apple>
penpineappleapplepen
이라는 학습하지 않은 데이터가 존재할 때, apple
이라는 서브 단어가 있었다면 penpineappleapplepen
의 벡터 역시 얻을 수 있다.Embedding()
을 제공한다.# 케라스 임베딩 층 구현
vocab_size = 20000 # input의 차원 (원-핫 벡터 - 단어 집합 크기)
output_dim = 128 # ouput의 차원 (임베딩 후 임베딩 벡터의 차원)
input_length = 500 # 입력 시퀀스의 길이
v = Embedding(input_dim=vocab_size,
output_dim=output_dim,
input_length=input_length)
# 1. 문장과 레이블 만들기
sentences = ['nice great best amazing', 'stop lies', 'pitiful nerd', 'excellent work', 'supreme quality', 'bad', 'highly respectable']
y_train = np.array([1, 0, 0, 1, 1, 0, 1])
# 2. 케라스 토크나이저를 통해 단어 집합을 만들기
tokenizer = Tokenizer()
tokenizer.fit_on_texts(sentences)
vocab_size = len(tokenizer.word_index) + 1 # 패딩을 고려해서 +1
print('단어 집합의 크기:', vocab_size)
>>> 16
# 3. 정수 인코딩
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]]
# 4. 전체 문장 중 최대길이로 패딩
x_train = pad_sequences(x_encoded, max(len(length) for length in x_encoded), padding='post')
print('패딩 결과: \n', 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]]
"""
# 5. 모델링
from tensorflow.keras.backend import clear_session
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dense, Flatten
output_dim = 4
# 1. 세션 클리어
clear_session()
# 2. 모델 선언
model = Sequential()
# 3. 모델 층 쌓기
model.add( Embedding(input_dim=vocab_size,
output_dim=output_dim,
input_length=max_len) )
model.add( Flatten() )
model.add( Dense( 1, activation='sigmoid') ) # 이진분류
# 4. 컴파일
model.compile(loss='binary_crossentropy',
metrics=['accuracy'],
optimizer='adam')
# 요약
model.summary()
"""
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, 4, 4) 64
flatten (Flatten) (None, 16) 0
dense (Dense) (None, 1) 17
=================================================================
Total params: 81
Trainable params: 81
Non-trainable params: 0
_________________________________________________________________
"""
# 6. 학습
model.fit(x_train, y_train, epochs=100, verbose=1)
💡 GloVe 다운로드 링크 : http://nlp.stanford.edu/data/glove.6B.zip
# 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()
# 2. 여러 파일 중 GloVe.6B.100d 임베딩 벡터 불러오기
embedding_dict = dict()
f = open('glove.6B.100d.txt', encoding="utf8")
for line in f:
word_vector = line.split()
word = word_vector[0] # 0번째 열이 단어
# 100개의 값을 가지는 array로 변환
word_vector_arr = np.asarray(word_vector[1:], dtype='float32') # 1번째 이후 값이 임베딩 벡터
embedding_dict[word] = word_vector_arr
f.close()
print('임베딩 벡터 수 :', len(embedding_dict))
>>>
# 3. 예제 단어 확인
print('단어 great의 벡터 값 \n', embedding_dict['great'])
"""
단어 great의 벡터 값
[-0.013786 0.38216 0.53236 0.15261 -0.29694 -0.20558
... 중략 ...
-0.69183 -1.0426 0.28855 0.63056 ]
"""
print('단어 supreme의 벡터 값 \n', embedding_dict['supreme']
"""
단어 supreme의 벡터 값
[ 0.85622 -0.30057 0.98826 0.42044 0.54632 0.18236
... 중략 ...
0.31196 -0.54905 0.18415 0.017116 ]
"""
# 4. 임베딩 벡터 행렬 선언 및 맵핑
embedding_matrix = np.zeros((vocab_size, 100))
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
# 5. 맵핑된 행렬을 통해 학습 (임베딩 학습 X)
model.add( Embedding(input_dim=vocab_size,
output_dim=output_dim,
weights=[embedding_matrix], # 사용할 워드 임베딩 초기값
input_length=max_len,
trainable=False) # Embedding 벡터를 학습하지 않음 (가져온 것 그대로 사용)
)
입력값의 임베딩 층
첫번째 히든레이어
두번째 히든레이어
이러한 층이 순방향, 역방향에 각각 하나씩 존재한다고 할 수 있다.
# 필요 라이브러리 로딩
import urllib.request
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests
import re
from PIL import Image
from nltk.tokenize import RegexpTokenizer
import nltk
from gensim.models import Word2Vec, KeyedVectors
from nltk.corpus import stopwords
from sklearn.metrics.pairwise import cosine_similarity
nltk.download('stopwords')
# 데이터 다운로드 및 확인
urllib.request.urlretrieve("https://raw.githubusercontent.com/ukairia777/tensorflow-nlp-tutorial/main/09.%20Word%20Embedding/dataset/data.csv", filename="data.csv")
df = pd.read_csv("data.csv")
# 필요한 정보인 Desc (줄거리) 열에 대한 전처리
def _removeNonAscii(s):
return "".join(i for i in s if ord(i)<128) # 아스키코드에 해당하는 문자만 추출하고, 나머지 제거
def make_lower_case(text):
return text.lower() # 모든 대문자를 소문자로
def remove_stop_words(text):
text = text.split()
stops = set(stopwords.words("english")) # 불용어 집합 불러오기
text = [w for w in text if not w in stops] # 불용어가 아닌 단어만 저장
text = " ".join(text) # 띄어쓰기로 구분한 문자열로 출력
return text
def remove_html(text):
html_pattern = re.compile('<.*?>') # html 태그를 전부 제거
return html_pattern.sub(r'', text)
def remove_punctuation(text):
tokenizer = RegexpTokenizer(r'[a-zA-Z]+') # 마침표 등 기호 및 특수문자 제거 (영어 알파벤만 남김)
text = tokenizer.tokenize(text)
text = " ".join(text)
return text
df['cleaned'] = df['Desc'].apply(_removeNonAscii)
df['cleaned'] = df.cleaned.apply(make_lower_case)
df['cleaned'] = df.cleaned.apply(remove_stop_words)
df['cleaned'] = df.cleaned.apply(remove_punctuation)
df['cleaned'] = df.cleaned.apply(remove_html)
# nan 확인 (교재에는 하나가 존재하는 걸로 나왔는데 여기서는 없다.)
df['cleaned'].isna().sum()
>>> 0
#사전 훈련된 GloVe 로드
import gzip
import zipfile
# urllib.request.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")
for line in f:
word_vector = line.split()
word = word_vector[0] # 0번째 열이 단어
# 100개의 값을 가지는 array로 변환
word_vector_arr = np.asarray(word_vector[1:], dtype='float32') # 1번째 이후 값이 임베딩 벡터
embedding_dict[word] = word_vector_arr
f.close()
print('임베딩 벡터 수 :', len(embedding_dict))
def get_document_vectors(document_list):
document_embedding_list = []
# 각 문서에 대해서
for line in document_list:
doc2vec = None
count = 0
for word in line.split():
if word in embedding_dict:
count += 1
# 해당 문서에 있는 모든 단어들의 벡터값을 더한다.
if doc2vec is None:
doc2vec = embedding_dict[word]
else:
doc2vec = doc2vec + embedding_dict[word]
if doc2vec is not None:
# 단어 벡터를 모두 더한 벡터의 값을 문서 길이로 나눠준다.
doc2vec = doc2vec / count
document_embedding_list.append(doc2vec)
# 각 문서에 대한 문서 벡터 리스트를 리턴
return document_embedding_list
# 유사도 매트릭스 구하기
cosine_similarities = cosine_similarity(document_embedding_list, document_embedding_list)
def recommendations(title):
books = df[['title', 'image_link']]
# 책의 제목을 입력하면 해당 제목의 인덱스를 리턴받아 idx에 저장.
indices = pd.Series(df.index, index = df['title']).drop_duplicates()
idx = indices[title]
# 입력된 책과 줄거리(document embedding)가 유사한 책 5개 선정.
sim_scores = list(enumerate(cosine_similarities[idx]))
sim_scores = sorted(sim_scores, key = lambda x: x[1], reverse = True) # 유사도가 높은 순으로 인덱스 정렬
sim_scores = sim_scores[1:6] # 상위 5개 추출 (0번째는 자기 자신이므로)
# 가장 유사한 책 5권의 인덱스
book_indices = [i[0] for i in sim_scores]
# 전체 데이터프레임에서 해당 인덱스의 행만 추출. 5개의 행을 가진다.
recommend = books.iloc[book_indices].reset_index(drop=True)
fig = plt.figure(figsize=(20, 30))
# 데이터프레임으로부터 순차적으로 이미지를 출력
for index, row in recommend.iterrows():
response = requests.get(row['image_link'])
img = Image.open(io.BytesIO(response.content))
fig.add_subplot(1, 5, index + 1)
plt.imshow(img)
plt.title(row['title'])