[NLP] word2vec 사용해보기
Python 3.7 환경에서 word2vec을 사용해볼 것이다.
'자연어 처리와 컴퓨터 언어학'이라는 책을 참고했다.
우선 gensim 이 설치되어 있지 않으므로 cmd 창을 열어 > pip install gensim 을 입력해 설치해준다.
그런 다음 jupyter를 실행해 from gensim.models import word2vec 와 같이 word2vec 모델을 임포트한다.
※ word2vec.Word2vec 클래스의 파라미터 모아 보기
sg : 알고리즘을 지정한다. 0(기본값)이면 CBOW, 1이면 skip-gram
size : 벡터의 차원 수
window : 현재 단어와 예측 단어 간의 거리
alpha : 초기 학습률
seed : 임의 숫자를 생성할 때 사용하는 값
min_count : 빈도수가 이 값보다 낮으면 그 단어를 제거한다
max_vocab_size : 단어집 생성 시 메모리 크기를 제한하고, 좀 더 특별한 단어가 존재하면 빈도수가 낮은 단어를 단어집에서 삭제한다
sample : 높은 빈도수의 단어를 지정하는 값
workers : 모델 학습 시 멀티 작업자 스레드 지정
hs : 0(기본값) 또는 음수이면 음수 샘플링 사용, 1이면 소프트맥스 사용
negative : 기본값은 5. 보통 5~20으로 사용하며 얼마나 많은 잡음 단어를 고려할 것인지에 따라 값을 지정함. 양수이면 음수 샘플링이 사용되며 0이면 사용되지 않는다
cbow_mean : CBOW가 사용될 때만 적용. 0이면 context 워드 벡터의 합 사용, 1이면 평균을 사용
hashfxn : 가중값을 임의로 지정하는 해시 함수
iter : 말뭉치에 대한 반복 횟수(epoch), 기본값은 5
trim_rule : 단어를 정리하는 규칙을 지정
sorted_vocab : 1(기본값)이면 단어집을 빈도수의 내림차순으로 정렬함
batch_words : 예제에 배치를 수행할 때 작업 스레드에 전달하기 위한 단어의 수. 기본 값은 10,000
일단 처음 사용해보는 것이므로 정석대로 text8 파일에 대해 적용해보자.
http://mattmahoney.net/dc/text8.zip
위 파일을 다운받고 압축을 풀어줘야 한다. 그런 다음 나는 파일을 현재 jupyter 작업 디렉토리로 옮겨줬다.
그리고 jupyter에 다음과 같이 입력한다.
sentences = word2vec.Text8Corpus('text8')
model = word2vec.Word2Vec(sentences, size=200, hs=1)
두 번째 문장이 실행되는데 시간이 좀 걸렸다. hs가 1이면 소프트맥스 함수를 사용한다는 뜻이다.
실행되고 나면 print(model) 를 입력해 출력 결과를 확인해본다.
출력 결과>>
Word2Vec(vocab=71290, size=200, alpha=0.025)
그 다음 word2vec에서 가장 유명한 예제인 King - Man + Woman = Queen 을 테스트해보자. 다음과 같이 입력한다.
model.wv.most_similar(positive=['woman', 'king'], negative=['man'], topn=1)
출력 결과>>
[('queen', 0.5559418201446533)]
model.wv.most_similar_cosmul(positive=['woman', 'king'], negative=['man'])
출력 결과>>
[('queen', 0.865217387676239), ('throne', 0.8521602153778076), ('empress', 0.8407161235809326), ('jadwiga', 0.8317084908485413), ('princess', 0.8298579454421997), ('prince', 0.825510561466217), ('jeroboam', 0.8236377239227295), ('monarch', 0.8119550943374634), ('sigismund', 0.8110775947570801), ('aragon', 0.8102884888648987)]
신기하다 !!! ≥∇≤
most_similar 함수를 사용하면 정확도가 높은 하나의 단어만 출력되고,
most_similar_cosmul 함수를 사용하면 정확도가 높은 단어가 10개 출력되나보다.
단어의 벡터 표현을 찾으려면 다음과 같이 입력한다.
model.wv['queen']
출력 결과>>
array([ 5.14554977e-01, 5.36465108e-01, -1.14925766e+00, 4.73865509e-01, -1.93510807e+00, -1.61862838e+00, -1.17729127e+00, 8.70164931e-01, -1.54654849e+00, -1.86901057e+00, 7.21027851e-01, -3.43961060e-01, 6.42343640e-01, -4.49818343e-01, 1.64962813e-01, 2.14892492e-01, -9.65537012e-01, -1.04106021e+00, -2.20626450e+00, 4.46278155e-01, 2.21676439e-01, -4.17919159e-01, 4.85151798e-01, -1.97554731e+00, -1.49084258e+00, -8.16119194e-01, -9.57898319e-01, 7.71050155e-02, … ],dtype=float32)
위에서 size=200으로 설정했기 때문에 총 200차원의 배열(값이 200개)을 볼 수 있다.
이와 같이 학습한 모델을 저장하려면 다음과 같이 입력한다.
model.save("모델 이름")
저장한 모델을 불러오려면 다음과 같이 입력한다.
model = word2vec.Word2Vec.load("모델 이름")
메모리 공간을 해제하려면 다음과 같이 입력한다.
word_vectors = model.wv
del model
이제 개인적인 한글 텍스트 파일에 word2vec을 적용해보려 한다.
word2vec.Text8Corpus()의 매개변수를 텍스트파일 명에 .txt를 붙여서 바꿔주기만 하면 위에서 한 것과 동일하다.
여기서 2가지 에러가 발생했다.
에러 1) 인코딩 에러
위에서 실습했던 text8 파일은 내부가 모두 영어로 되어 있어 오류가 발생하지 않았지만 이번에 사용하는 텍스트 파일은 모두 한글로 되어 있어 인코딩 에러가 발생할 거라고 예상하고 있었다.
그래서 구글링을 해본 결과 Python 3버전에서는 한글 파일이 왠만하면 다 호환되며, 내 경우에는 한글이 문제가 아니라 텍스트 파일 자체의 인코딩 방식이 문제였다.
텍스트 파일을 메모장으로 연 후 다른 이름으로 저장을 눌렀더니 인코딩 방식이 당연히 UTF-8일 줄 알았는데 ANSI로 되어 있었다. 그래서 UTF-8로 변경하여 저장했더니 인코딩 에러가 해결되었다!
에러 2) "word ' ' not in vocabulary"
model.wv.most_similar(positive=['하늘'], negative=['구름'], topn=1) 이런 식으로 most_similar 함수를 실행해보려고 했는데 계속해서 "word '하늘' not in vocabulary" 이러한 에러가 발생했다.
'하늘', '구름' 둘 다 텍스트 파일에 존재하는 단어인데 왜 이런 에러가 나는 것일까?
처음에는 한국어 단어의 split 문제인줄 알았지만 for row in sentences: print(row) 를 실행해보니 문제가 없었다.
그래서 구글링을 해봤더니 대부분의 사람들이 리스트 형식의 문제였지만 나의 경우는 이것도 아니었다.
결국 알아낸 원인은 내가 사용한 테스트파일에 해당 단어의 빈도 수가 너무 적어서 그런 것이었다!
개인적인 텍스트 파일이라 전체 단어의 수가 적은 편이다 보니 model이 해당 단어를 충분히 학습하지 못해 이러한 에러가 발생하는 것 같았다.
그래서 텍스트 파일에서 자주 출현하는 단어를 positive, negative 속성에 넣어주었더니 에러가 해결되었다.
위에서 사용했던 text8 파일은 단어집이 커서 왠만한 단어를 넣어도 다 결과가 나온다. 하지만 단어집이 작은 파일을 사용할 경우 주의해야 할 점은 positive, negative에 넣을 단어가 원래 파일에 등장하는 단어여야 하고, 그 빈도 수가 커야한다는 것이다.