2020년 1월 27일 월요일

doc2vec 예제 분석


doc2vec 예제 분석


오래전부터 doc2vec 예제를 좀 찾아봤습니다. 적당한 예제도 없고 자세한 설명이 부족한것 같아서 정리해 봅니다.


준비

기본 예제는 아래 링크에서 출발합니다.
python3 로 시작 합니다.

https://medium.com/@mishra.thedeepak/doc2vec-simple-implementation-example-df2afbbfbad5


시작하기전에 import 에러나는 것은 없는지 확인해봅시다.
#Import all the dependencies
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
from nltk.tokenize import word_tokenize


만약 아래와 같은 에러가 난다면 nltk를 설치해 줍니다.

python doc2vectut.py
Traceback (most recent call last):
  File "doc2vectut.py", line 3, in <module>
    from nltk.tokenize import word_tokenize
ModuleNotFoundError: No module named 'nltk'

https://stackoverflow.com/questions/40361488/importerror-no-module-named-nltk-tokenize-nltk-is-not-a-package

pip install nltk


입력 데이터


학습 모델의 기본은 입력 데이터 형태부터 파악 하는것입니다. 여기 train을 위해서 입력하는 데이터를 가공 하는 소스 입니다. word2vec에서는 list의 list형태로 데이터를 넣었다면, 여기에서는 words=['i', 'love', 'machine', 'learning', '.', 'its', 'awesome', '.'], tags=['0'] 이런 형태의 데이터의 list들을 만들어서 입력으로 사용합니다.
여기에서 tags는 label이 되기를 원하는 값=목표값으로 (분류를 원하는 값) 입니다. 예를 들어보자면 영화 평점 리뷰 게시판이 있고 댓글과 평점이 있을때 학습을 하는경우 댓글 내용이 words가 되고 평점은 tags가 됩니다. 학습이 되고 나면 댓글 (words)내용을 가지고 (tags)평점이 얼마나 될지 예측이 가능하게 됩니다.
평점이 높을수록 좋은 의미를 지니므로 이것은 영화뿐 아니라 다른곳에서 사용가능한 데이터에도 응용할 수 있습니다. 영화 평점은 이런면에서 좋은 학습 데이터가 되게 됩니다.
여기 비슷하게 처리한 내용이 있네요, 한번 읽어보는것도 좋을것 같습니다.
http://hero4earth.com/blog/projects/2018/01/21/naver_movie_review/
Let’s prepare data for training our doc2vec model
data = ["I love machine learning. Its awesome.",
        "I love coding in python",
        "I love building chatbots",
        "they chat amagingly well"]

tagged_data = [TaggedDocument(words=word_tokenize(_d.lower()), tags=[str(i)]) for i, _d in enumerate(data

위 코드를 실행했을때 nltk 아래와 같은 오류가 난다면 데이터를 다운로드 받도록 합니다.

LookupError:
**********************************************************************
  Resource  [93mpunkt [0m not found.
  Please use the NLTK Downloader to obtain the resource:

   [31m>>> import nltk
  >>> nltk.download('punkt')
   [0m
  For more information see: https://www.nltk.org/data.html

  Attempted to load  [93mtokenizers/punkt/english.pickle [0m

  Searched in:
    - 'C:\\Users\\USER/nltk_data'
    - 'C:\\Users\\USER\\AppData\\Local\\Programs\\Python\\Python37\\nltk_data'
    - 'C:\\Users\\USER\\AppData\\Local\\Programs\\Python\\Python37\\share\\nltk_data'
    - 'C:\\Users\\USER\\AppData\\Local\\Programs\\Python\\Python37\\lib\\nltk_data'
    - 'C:\\Users\\USER\\AppData\\Roaming\\nltk_data'
    - 'C:\\nltk_data'
    - 'D:\\nltk_data'
    - 'E:\\nltk_data'
    - ''
**********************************************************************


nltk 데이터 다운로드 받기

http://www.nltk.org/nltk_data/

Punkt Tokenizer Models

python 실행 후 아래와 같이 실행합니다.
>>> import nltk
>>> nltk.download('punkt')


지금까지 소스와 실행한 결과입니다.

#Import all the dependencies
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
from nltk.tokenize import word_tokenize

data = ["I love machine learning. Its awesome.",
        "I love coding in python",
        "I love building chatbots",
        "they chat amagingly well"]

tagged_data = [TaggedDocument(words=word_tokenize(_d.lower()), tags=[str(i)]) for i, _d in enumerate(data)]

print(tagged_data)

실행 결과
C:\Users\USER\Documents\python\doc2vec>python doc2vectut.py
[TaggedDocument(words=['i', 'love', 'machine', 'learning', '.', 'its', 'awesome', '.'], tags=['0']), TaggedDocument(words=['i', 'love', 'coding', 'in', 'python'], tags=['1']), TaggedDocument(words=['i', 'love', 'building', 'chatbots'], tags=['2']), TaggedDocument(words=['they', 'chat', 'amagingly', 'well'], tags=['3'])]


코드가 복잡한데 좀 더 쉽게 풀어서 구현한 코드는 아래와 같습니다.

tagged_data = []

for i, _d in enumerate(data):
 tagged_data.append(TaggedDocument(words=word_tokenize(_d.lower()), tags=[str(i)]))

python enumerate 는 index와 data를 분리하게 됩니다. 아래 링크 참조
https://dpdpwl.tistory.com/43


학습시키기

학습하는 데이터의 parameter는 데이터들마다 다르기 때문에 값을 바꿔가면서 이것저것 변경해봐야 합니다. 이것을 hyper parameter라고 하는데 여기 원본 문서를 참고해 가면서 의미를 파악 하시면 됩니다.
https://radimrehurek.com/gensim/models/doc2vec.html

학습의 기본은 epoch과 learning rate입니다. 여기 예제에서 max_epochs로 나온 부분이 학습을 얼마나 할것인지 결정합니다. 전체 데이터를 이용해서 1번 학습하는것을 1 epoch라고합니다.
여기 예제에서는 model.alpha -= 0.0002 를 이용하여 learning rate를 조절 하고 있습니다. 학습을 거듭할수록 error값이 줄어드는데 learning rate가 올라가면 발진할 수 있으므로 학습 할수록 작아지도록 예제를 만들어 놓은것으로 판단됩니다.
max_epochs = 100
vec_size = 20
alpha = 0.025

model = Doc2Vec(size=vec_size,
                alpha=alpha, 
                min_alpha=0.00025,
                min_count=1,
                dm =1)
  
model.build_vocab(tagged_data)

for epoch in range(max_epochs):
    print('iteration {0}'.format(epoch))
    model.train(tagged_data,
                total_examples=model.corpus_count,
                epochs=model.iter)
    # decrease the learning rate
    model.alpha -= 0.0002
    # fix the learning rate, no decay
    model.min_alpha = model.alpha

model.save("d2v.model")
print("Model Saved")

지금까지 소스외에 model save / load하는 소스까지 하여 실행 시켜 봤습니다.

전체 소스
#Import all the dependencies
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
from nltk.tokenize import word_tokenize

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

data = ["I love machine learning. Its awesome.",
        "I love coding in python",
        "I love building chatbots",
        "they chat amagingly well"]

tagged_data = []

for i, _d in enumerate(data):
 tagged_data.append(TaggedDocument(words=word_tokenize(_d.lower()), tags=[str(i)]))

print(tagged_data)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

max_epochs = 100
vec_size = 20
alpha = 0.025

model = Doc2Vec(size=vec_size,
                alpha=alpha, 
                min_alpha=0.00025,
                min_count=1,
                dm =1)
  
model.build_vocab(tagged_data)

for epoch in range(max_epochs):
    print('iteration {0}'.format(epoch))
    model.train(tagged_data,
                total_examples=model.corpus_count,
                epochs=model.iter)
    # decrease the learning rate
    model.alpha -= 0.0002
    # fix the learning rate, no decay
    model.min_alpha = model.alpha

model.save("d2v.model")
print("Model Saved")

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


from gensim.models.doc2vec import Doc2Vec

model= Doc2Vec.load("d2v.model")
#to find the vector of a document which is not in training data
test_data = word_tokenize("I love chatbots".lower())
v1 = model.infer_vector(test_data)
print("V1_infer", v1)

# to find most similar doc using tags
similar_doc = model.docvecs.most_similar('1')
print(similar_doc)

결과
C:\Users\USER\Documents\python\doc2vec>python doc2vectut.py
[TaggedDocument(words=['i', 'love', 'machine', 'learning', '.', 'its', 'awesome', '.'], tags=['0']), TaggedDocument(words=['i', 'love', 'coding', 'in', 'python'], tags=['1']), TaggedDocument(words=['i', 'love', 'building', 'chatbots'], tags=['2']), TaggedDocument(words=['they', 'chat', 'amagingly', 'well'], tags=['3'])]
C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\site-packages\gensim\models\doc2vec.py:574: UserWarning: The parameter `size` is deprecated, will be removed in 4.0.0, use `vector_size` instead.
  warnings.warn("The parameter `size` is deprecated, will be removed in 4.0.0, use `vector_size` instead.")
iteration 0
doc2vectut.py:37: DeprecationWarning: Call to deprecated `iter` (Attribute will be removed in 4.0.0, use self.epochs instead).
  epochs=model.iter)
iteration 1
iteration 2
iteration 3
iteration 4
iteration 5
iteration 6
iteration 7
iteration 8
iteration 9
iteration 10
iteration 11
iteration 12
iteration 13
iteration 14
iteration 15
iteration 16
iteration 17
iteration 18
iteration 19
iteration 20
iteration 21
iteration 22
iteration 23
iteration 24
iteration 25
iteration 26
iteration 27
iteration 28
iteration 29
iteration 30
iteration 31
iteration 32
iteration 33
iteration 34
iteration 35
iteration 36
iteration 37
iteration 38
iteration 39
iteration 40
iteration 41
iteration 42
iteration 43
iteration 44
iteration 45
iteration 46
iteration 47
iteration 48
iteration 49
iteration 50
iteration 51
iteration 52
iteration 53
iteration 54
iteration 55
iteration 56
iteration 57
iteration 58
iteration 59
iteration 60
iteration 61
iteration 62
iteration 63
iteration 64
iteration 65
iteration 66
iteration 67
iteration 68
iteration 69
iteration 70
iteration 71
iteration 72
iteration 73
iteration 74
iteration 75
iteration 76
iteration 77
iteration 78
iteration 79
iteration 80
iteration 81
iteration 82
iteration 83
iteration 84
iteration 85
iteration 86
iteration 87
iteration 88
iteration 89
iteration 90
iteration 91
iteration 92
iteration 93
iteration 94
iteration 95
iteration 96
iteration 97
iteration 98
iteration 99
Model Saved
V1_infer [-0.0109304   0.01453446 -0.02796343  0.01905919  0.0186462   0.01566897
  0.01792961  0.00705164  0.01424597  0.00248461  0.02653115  0.02868732
 -0.01172108  0.00585754 -0.01540187 -0.01015471  0.0146137   0.00021682
  0.01906102 -0.01555774]
[('0', 0.9923108816146851), ('3', 0.9911640882492065), ('2', 0.9911146759986877)]

소스 분석


test_data = word_tokenize("I love chatbots".lower())
v1 = model.infer_vector(test_data)
위 코드는 test_data를 토큰화 해서 vector를 만드는 과정입니다.
위 예제에서는 결과로서 아래와 같은 vector가 출력되었습니다. 위에서 vector size를 20으로 했기 때문에 20개가 되는것 입니다. 크기가 클수록 더 정확해질 수 있으나 더 많은 메모리와 더많은 데이터로 더 많은 훈련이 필요합니다.
 [-0.0109304   0.01453446 -0.02796343  0.01905919  0.0186462   0.01566897
  0.01792961  0.00705164  0.01424597  0.00248461  0.02653115  0.02868732
 -0.01172108  0.00585754 -0.01540187 -0.01015471  0.0146137   0.00021682
  0.01906102 -0.01555774]

similar_doc = model.docvecs.most_similar('1')
이 예제는 "1" 번 문장과 가장 비슷한 문장이 어떤것인지 나타납니다. 결과를 보자면 "0"이 0.9923% 수준으로 제일 근접 합니다. "0" 번 문장인데 전혀 비슷하지 않지만....
[('0', 0.9923108816146851), ('3', 0.9911640882492065), ('2', 0.9911146759986877)]

문제점

여러번 실행시키면 아래와 같이 값이 다릅니다. 이때 결과가 "2" 두번째 문장인 경우도 나옵니다.

Model Saved
V1_infer [-0.02121443 -0.00848201 -0.00763245  0.00745193  0.00415829 -0.01217643
  0.01441886  0.01548216 -0.00765178 -0.02253398 -0.0081254  -0.00448609
 -0.0034246  -0.01841096  0.02063655 -0.01921302  0.01506925  0.0162458
  0.01329079 -0.0103074 ]
[('2', 0.9906354546546936), ('0', 0.9863607883453369), ('3', 0.9810261726379395)]


Model Saved
V1_infer [ 0.00368929  0.00579063 -0.00694244 -0.01026715 -0.00822515  0.01120392
 -0.00292197  0.00318307 -0.00821392  0.003614   -0.02230442 -0.01828359
  0.01888582  0.01448112  0.00215786  0.00533434  0.01083726 -0.00696347
 -0.00343927 -0.02374181]
[('0', 0.9902483224868774), ('3', 0.9879346489906311), ('2', 0.9857644438743591)]

앞으로 해결할 주제

위 문제점은 어디에서 잘못되었을까?
원하는 문장이 있을때 어떤 문장과 비슷한가?







댓글 없음:

댓글 쓰기