###前提・実現したいこと
doc2vecを用いて
VirtualBoxのcentOS上で、python3.6を使ってdoc2vecを動かしています。
実現したいプログラムは
・類似する単語の検索機能
です。
参考にしたプログラムはこちらのサイトにあったものです。
https://qiita.com/akira_/items/f9bb46cad6834da32367
文書を学習するプログラムと検索するプログラムは分かれていて、
学習するプログラムに問題が発生しました。
与えたプログラムは電子メールをプレーンテキストにしたもので、
def trim_doc(doc):
で不要な部分を削除してから与えました。
実際に書いたコードがこちらです。
import os import sys import MeCab import collections from gensim import models from gensim.models.doc2vec import LabeledSentence INPUT_DOC_DIR = './mail' OUTPUT_MODEL = 'doc2vec.model' PASSING_PRECISION = 93 #すべてのファイルのリストを取得 def get_all_files(directory): for root, dirs, files in os.walk(directory): for file in files: yield os.path.join(root, file) #ファイルから文章を返す def read_document(path): with open(path, 'r', errors='ignore') as f: return f.read() #不要な部分の削除 def trim_doc(doc): lines = doc.splitlines() valid_lines = [] is_valid = False horizontal_rule_cnt = 0 break_cnt = 0 for line in lines: if horizontal_rule_cnt < 2 and 'Reply-to:' in line: horizontal_rule_cnt += 1 is_valid = horizontal_rule_cnt == 2 continue if not(is_valid): continue break_cnt = 0 valid_lines.append(line) return ''.join(valid_lines) #文章から単語に分割して返す def split_into_words(doc, name=''): mecab = MeCab.Tagger("-Ochasen") valid_doc = trim_doc(doc) lines = mecab.parse(doc).splitlines() words = [] for line in lines: chunks = line.split("\t") if len(chunks) > 3 and (chunks[3].startswith('動詞') or chunks[3].startswith('形容詞') or (chunks[3].startswith('名詞') and not chunks[3].startswith('名詞-数'))): words.append(chunks[0]) return LabeledSentence(words=words, tags=[name]) #ファイルから単語のリストを取得 def corpus_to_sentences(corpus): docs=[read_document(x) for x in corpus] for idx, (doc, name) in enumerate(zip(docs, corpus)): sys.stdout.write('\r前処理{}/{}'.format(idx, len(corpus))) yield split_into_words(doc, name) #学習 def train(sentences): model = models.Doc2Vec(alpha=0.0015, sample=1e-4, min_count=1, workers=4) model.build_vocab(sentences) for x in range(30): print(x) model.train(sentences, total_examples=model.corpus_count, epochs=model.iter) ranks = [] for doc_id in range(100): inferred_vector = model.infer_vector(sentences[doc_id].words) sims = model.docvecs.most_similar([inferred_vector], topn=len(model.docvecs)) rank = [docid for docid, sim in sims].index(sentences[doc_id].tags[0]) ranks.append(rank) print(collections.Counter(ranks)) if collections.Counter(ranks)[0] >= PASSING_PRECISION: break return model if __name__ == '__main__': corpus = list(get_all_files(INPUT_DOC_DIR)) sentences = list(corpus_to_sentences(corpus)) print() model = train(sentences) model.save(OUTPUT_MODEL)
下の部分で学習と評価を行っています。
for x in range(30): print(x) model.train(sentences, total_examples=model.corpus_count, epochs=model.iter) ranks = [] for doc_id in range(100): inferred_vector = model.infer_vector(sentences[doc_id].words) sims = model.docvecs.most_similar([inferred_vector], topn=len(model.docvecs)) rank = [docid for docid, sim in sims].index(sentences[doc_id].tags[0]) ranks.append(rank) print(collections.Counter(ranks)) if collections.Counter(ranks)[0] >= PASSING_PRECISION: break
評価は、学習した文章のうち100個で類似の文章を検索し、最も類似度の高い文章が自分自身だった回数で行います。
評価の出力結果は以下の通りです。
$python3.6 program_v1.py 0 Counter({0: 36, 2: 8, 1: 5, 3: 3, 62: 2, 8: 2, 3721: 1, 2457: 1, 296: 1,(以下略)}) 1 Counter({0: 21, 1: 5, 2: 4, 9: 3, 16: 2, 3: 2, 879: 1, 3665: 1, 3423: 1,(以下略)}) 2 Counter({0: 5, 1: 2, 7: 2, 3904: 2, 3163: 2, 213: 2, 975: 1, 201: 1, 296: 1,(以下略)}) 3 Counter({1: 2, 3891: 2, 3941: 2, 5: 2, 2880: 2, 0: 2, 2057: 1, 1166: 1, 3399: 1,(以下略)}) 以下略
2行目の0,4行目の1 などの数字は学習回数で、
Counterはの:の左は文書、右は最も類似度の高い文書が自分自身だった回数を表しています。
###発生している問題
学習を繰り返すたびに自分自身を示す回数が減ってしまうのは何が原因なのでしょうか。
また、初めてプログラムを実行したとき、評価で自分自信だった回数は最大36回でした。
その後、作成されたモデルを削除して、再びプログラムを実行したところ、評価で自分自身だった回数は多くても4回くらいです。
なぜ、モデルを作り直したはずなのに、最初に実行した時と似た結果にならないのでしょうか。
よろしくお願いします。
あなたの回答
tips
プレビュー