2. 임베딩
2.1 임베딩이란?
- 자연어를 숫자나 벡터형태로 변환하는 것을 임베딩이라고 한다.
- 즉, 임베딩은 단어나 문장을 수치화해서 벡터공간으로 표현하는 과정을 의미한다.
임베딩기법
- 문장임베딩 : 여러개의 문장에서 각각의 문장을 벡터로 표현하는 기법
- 문장의 흐름을 벡터화하기 때문에 문맥적의미응 가지는 장점이 있다.
- 하지만, 문장을 임베딩하기 위해서는 많은 문장이 필요하며 학습비용이 많이 든다.
- 상용시스템에서 많이 사용된다. (단어임베딩에 비해 품질이 우수하다.)
- 단어임베딩 : 개별 단어를 벤터로 표현하는 기법
- 동음의의어에 대한 구분을 하지 않는다.
- 그렇기 때문에 의미가 다르더라도 단어형태가 같다면 동일한 벡터값으로 표현하는 단점이 있다.
- 하지만, 문장임베딩에 비해 학습이 간단하여 실무에 많이 사용된다.
2.2 단어임베딩
- 단어 임베딩은 말뭉치에서 각각의 단어를 벡터로 변환하는 기법을 의미
- 참고 : https://wikidocs.net/33520
2.2.1 원핫인코딩(One-HoEncoding)
- 단어를 숫자벡터로 변환하는 가장 기본적인 방법
- 단 한 개의 값만 1이고 나머지는 0인 희소행렬
- 원핫인코딩의 결과를 원핫벡터라고 하고 희소벡터(sparse vector), 희소행렬이라고 한다.t
# 원핫인코딩
import numpy as np
from konlpy.tag import Komoran
Komoran = Komoran()
text = '오늘 날씨는 구름이 많아요'
# 명사만 추출
nouns = Komoran.nouns(text)
print(nouns)
# 단어사전구축 및 단어별 인덱스를 부여
dics = {}
for word in nouns:
if word not in dics.keys():
dics[word] = len(dics)
print(dics)
# 단어벡터화 - 원핫인코딩
nb_classes = len(dics)
targets = list(dics.values())
print(nb_classes, targets)
['오늘', '날씨', '구름']
{'오늘': 0, '날씨': 1, '구름': 2}
3 [0, 1, 2]
한국어 텍스트를 원핫인코딩으로 벡터화하는 과정
from konlpy.tag import Komoran: Konlpy 라이브러리에서 Komoran 클래스를 가져온다. 이를 통해 한국어 텍스트의 형태소 분석을 수행할 수 있다.
Komoran = Komoran(): Komoran 객체를 생성한다.
nouns = Komoran.nouns(text): 입력 텍스트에서 명사만 추출하여 nouns에 저장한다.
dics = {}: 빈 딕셔너리 dics를 생성한다. 이 딕셔너리는 단어와 해당하는 인덱스를 저장한다.
for word in nouns: 추출된 명사 리스트를 반복한다.
if word not in dics.keys(): 현재 명사가 딕셔너리에 없는지 확인한다.
dics[word] = len(dics): 명사가 딕셔너리에 없다면, 그 명사를 딕셔너리에 추가하고, 해당하는 인덱스를 딕셔너리의 길이로 설정한다.
nb_classes = len(dics): 딕셔너리의 길이를 통해 클래스의 개수를 구한다. 이는 원핫인코딩을 할 때 벡터의 차원으로 사용된다.
targets = list(dics.values()): 딕셔너리의 값들을 리스트로 변환하여 targets에 저장한다. 이는 나중에 단어를 원핫인코딩으로 변환할 때 사용된다.
# np.eye() : 원핫인코딩을 만들어주는 함수
one_hot_targets = np.eye(nb_classes)[targets]
print(one_hot_targets)
# 결과
# [1.0.0.]이란 값이 입력이 되면 '오늘'이라고 인식한다.
# 원핫코딩의 메모리낭비와 예산의 복집도가 커지는 단점이 있다.
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
one_hot_targets = np.eye(nb_classes)[targets]: np.eye() 함수를 사용하여 원핫인코딩을 생성한다. 이 함수는 단위 행렬(identity matrix)을 생성하는데, 이때 단위 행렬의 크기는 nb_classes로 지정된다. 그런 다음 [targets]에 해당하는 인덱스 위치에 1을 할당하고 나머지 위치는 0으로 채워진 배열을 반환한다.
2.2.2 희소표현과 분산표현
- 희소표현은 희소벡터로 표현되는 방식, 분산표현은 단어간의 유사성을 표현하는 방법
- 분산표현은 데이터손실을 최소화 하면서 벡터차원을 압축하는 효과가 있다.
- 분산표현을 원하는 차원에 데이터를 최대한 밀집시켜서 밀집표현이라고 한다.
- 참고 : http://bigdata.dongguk.ac.kr/lectures/TextMining/_book/분산표현.html
2.2.2 Word2Vec
- 신경망기반의 단어임베딩의 대표적인 알고리즘이다.
- Word2Vec은 2013년 구글에서 발표, 가장 많이 사용하는 모델이다.
- Word2Vec 모델은 CBOW Continuos Bag Of Words와 skip-gram 2가지 모델로 제안되었다. -CBOW는 주변 단어를 이용해 타깃단어를 예측하는 모델
- skip-gram은 CBOW와 반대로 하나의 타깃단어를 이용해 주변단어를 예측하는 모델
- Word2Vec은 단어를 밀집벡터로 표현하고 학습을 통해서 유사한 단어를 가까운 벡터공간에 위치시킨다
- Word2Vec은 오픈소스라이브러리 Gensim패키지를 사용하고 한국어말뭉치는 네이버영화리뷰데이터를 사용 1.gensim : pip install gensim
- 네이버영화리뷰데이터(Naver Sentiment Movie Corpus, NSMC) -최신버전 : http://github.com/e9t/nsmc
!pip show gensim
from gensim.models import Word2Vec
from konlpy.tag import Komoran
import time
# 네이버영화리뷰데이터읽기
def read_review_data(filename):
with open(filename, 'r', encoding='utf-8') as f:
data = [line.split('\t') for line in f.read().splitlines()]
dat = data[1:] # header부분을 제거
return data
위 코드는 Word2Vec 모델을 사용하여 한국어 텍스트를 임베딩하기 위해 필요한 데이터를 읽어오는 함수를 정의한다.
from gensim.models import Word2Vec: Gensim 라이브러리에서 Word2Vec 모델을 가져온다. Word2Vec은 단어 임베딩을 학습하기 위한 모델이다.
from konlpy.tag import Komoran: Konlpy 라이브러리에서 Komoran 클래스를 가져온다. 를 통해 한국어 텍스트를 형태소 분석할 수 있다.
import time: 시간 관련 모듈인 time을 임포트한다. 이는 코드 실행 시간을 측정하기 위해 사용된다.
def read_review_data(filename): read_review_data라는 함수를 정의한다. 이 함수는 리뷰 데이터를 읽어오는 역할을한다. 매개변수로 파일 이름(filename)을 받는다.
with open(filename, 'r', encoding='utf-8') as f: 입력된 파일을 utf-8 인코딩으로 열고, 파일 핸들을 f에 할당한다.
data = [line.split('\t') for line in f.read().splitlines()]: 파일을 줄 단위로 읽고, 탭('\t')을 기준으로 분리하여 리스트 형태로 data에 저장한다.
dat = data[1:]: 데이터의 첫 번째 줄은 헤더이므로 이를 제외하고 나머지 데이터를 dat에 저장한다.
# 학습시간측정 - 시작시간
start = time.time()
# 1. 파일읽기
review_data = read_review_data('./data/chatbot/ratings.txt')
print(f'1) 말뭉치데이터 읽기 소요시간 = {time.time()-start}, 데이터처리건수 = {len(review_data)}')
# 2. 문장단위로 명사만 추출 - 학습을 위한 입력데이터로 사용
komoran = Komoran()
docs = [komoran.nouns(sentence[1]) for sentence in review_data]
print(f'2) 형태소에서 명사만 추출 소요시간 = {time.time()-start}, 데이터처리건수 = {len(docs)}')
1) 말뭉치데이터 읽기 소요시간 = 0.4326174259185791, 데이터처리건수 = 200001
2) 형태소에서 명사만 추출 소요시간 = 99.83483386039734, 데이터처리건수 = 200001
# Word2Vec모델학습
# sentences : Word2Vec모델 학습에 필요한 문장 데이터
# vector_size : 단어 임베딩 벡터차원 크기
# window : 주변 단어의 우니도우의 크기
# hs : 1(모델학습에 softmax 사용), 0(negative옵션값이 0이 아닐때 음수 샘플링을 사용)
# min_count : 단어 최소 빈도수 제한(설정된 빈도수이하의 단어들은 학습하지 않음)
# sg : 0(CBOW모델), 1(skip-gram)
start = time.time()
model = Word2Vec(sentences=docs, vector_size=200, window=4, min_count=2, sg=1)
print(f'3) Word2Vec 학습 소요시간 = {time.time()-start}')
3) Word2Vec 학습 소요시간 = 7.537257671356201
# 4. 모델저장
start = time.time()
model.save('./data/chatbot/nvmc.model')
print(f'4) Word2Vec 모델 저장 소요시간 = {time.time()-start}')
3) Word2Vec 모델 저장 소요시간 = 0.04495596885681152
# 5. 결과 - 학습된 말뭉치의 갯수, 말뭉치(코퍼스)내의 전체 단어 갯수
print(f'5) 학습된 말뭉치의 갯수 = {model.corpus_count}')
print(f'6) 말뭉치내의 전체 단어의 갯수 = {model.corpus_total_words}')
5) 학습된 말뭉치의 갯수 = 200001
6) 말뭉치내의 전체 단어의 갯수 = 1076896
# 6. 학습된 모델을 이용한 단어의 유사성
# 1) 학습된 모델을 로딩
model = Word2Vec.load('./data/chatbot/nvmc.model')
# 2) 사랑이라는 단어로 생성한 단어의 임베딩된 벡터
print(f"1) 사랑 : {model.wv['사랑']}, \n 2) 사랑단어의 갯수 = {len(model.wv['사랑'])}")
1) 사랑 : [-0.00880693 -0.49992582 0.5562715 0.11021315 -0.03624455 0.02194167...]
2) 사랑단어의 갯수 = 200
# 3) 단어의 유사도 : model.similarity()함수
print(f"일요일과 월요일의 유사도 = {model.wv.similarity(w1='일요일', w2='월요일')}")
print(f"일요일과 사랑의 유사도 = {model.wv.similarity(w1='일요일', w2='사랑')}")
print(f"황정민과 배우의 유사도 = {model.wv.similarity(w1='황정민', w2='배우')}")
print(f"안성기과 사랑의 유사도 = {model.wv.similarity(w1='안성기', w2='사랑')}")
print(f"정우성과 사랑의 유사도 = {model.wv.similarity(w1='정우성', w2='사랑')}")
print(f"대기업과 삼성의 유사도 = {model.wv.similarity(w1='대기업', w2='삼성'):.3f}")
print(f"일요일과 삼성의 유사도 = {model.wv.similarity(w1='일요일', w2='삼성'):.3f}")
일요일과 월요일의 유사도 = 0.9147074222564697
일요일과 사랑의 유사도 = 0.43263447284698486
황정민과 배우의 유사도 = 0.6902545690536499
안성기과 사랑의 유사도 = 0.3471217155456543
정우성과 사랑의 유사도 = 0.4066528081893921
대기업과 삼성의 유사도 = 0.879
일요일과 삼성의 유사도 = 0.658
# 4) most_similar(str, topn) - 가장 유사한 단어를 추출
# topn = 5 : 5개까지 유사한 단어갯수
print(model.wv.most_similar("안성기", topn=5))
print(model.wv.most_similar("시리즈", topn=5))
print(model.wv.most_similar("사랑", topn=5))
[('지진희', 0.9468665719032288), ('한석규', 0.9443938136100769), ('박신양', 0.9417400360107422), ('능청', 0.9405069351196289), ('설경구', 0.9403910040855408)]
[('엑스맨', 0.8057305216789246), ('포터', 0.8022477626800537), ('반지의 제왕', 0.785901665687561), ('스타워즈', 0.7816969156265259), ('미이라', 0.777804434299469)]
[('사랑은', 0.7140669226646423), ('이별', 0.707787275314331), ('치유', 0.6925687193870544), ('사랑이란', 0.6879589557647705), ('사랑으로', 0.6795760989189148)]
# 시각화
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 한글처리
plt.rcParams['font.family'] = 'D2Coding'
plt.rcParams['axes.unicode_minus'] = False
print(f"안성기와 배우의 유사도 = {model.wv.similarity(w1='안성기', w2='배우')}")
print(f"안성기와 사랑의 유사도 = {model.wv.similarity(w1='안성기', w2='사랑')}")
print(f"사랑과 사랑의 유사도 = {model.wv.similarity(w1='사랑', w2='사랑')}")
print(f"사랑과 이별의 유사도 = {model.wv.similarity(w1='사랑', w2='이별')}")
안성기와 배우의 유사도 = 0.7129108309745789
안성기와 사랑의 유사도 = 0.3471217155456543
사랑과 사랑의 유사도 = 0.9999999403953552
사랑과 이별의 유사도 = 0.7077872157096863
plt.plot(model.wv['사랑'])
plt.show()
# 결과분석
# 사랑이란 단어는 모든 차원에 골고루 분포되어 있는 것을 알 수가 있다.
# 즉, 사랑이란 단어는 모든 벡터와 관계가 있음을 유추할 수가 있다.
# 안성기라는 단어는 라는 단어와의 유사도가 70%, 사랑이란단어와의 유사도는 37%의 유사도가 있다.
# 유사도가 1.0에 가까울수록 두 단어는 동일의미이거나 문법적관계가 있을 확률이 높다.
plt.plot(model.wv['안성기'])
plt.show()
'Python' 카테고리의 다른 글
[Python]챗봇_04_딥러닝모델(keras) (1) | 2024.04.26 |
---|---|
[Python]챗봇_03_텍스트유사도 (0) | 2024.04.25 |
[Python]챗봇_01_토크나이징 (1) | 2024.04.25 |
[Python]데이터베이스_DataBase (0) | 2024.04.24 |
[Python]정규식_Regula_Expressions (0) | 2024.04.24 |