質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

0回答

2585閲覧

doc2vec 実行を行うたびに精度が悪くなってしまいます

shut_student

総合スコア10

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/02/04 01:53

編集2019/02/04 13:38

###前提・実現したいこと
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回くらいです。
なぜ、モデルを作り直したはずなのに、最初に実行した時と似た結果にならないのでしょうか。

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

quickquip

2019/02/04 02:08 編集

精度ってなんですか? 1つ目のプログラムとはなんですか? 2つ目のプログラムとはなんですか? どういうデータを分類しているんですか? 結果と言っているこの出力はどう見るんですか? タスクは「文書の類似度を出す」なんですか? この出力結果からはクラス分類問題に見えるんですが違いますか? だとしてどういう数字を出して精度としているんですか?
shut_student

2019/02/04 02:14

うまく説明できないのですが、2つ目に参考にしたサイトのプログラム from gensim.models.doc2vec import Doc2Vec from gensim.models.doc2vec import TaggedDocument training_docs = [] sent1 = TaggedDocument(words=['どーも', '谷口', 'です', 'よーし', 'やってく', 'ぞ'], tags=['d1']) sent2 = TaggedDocument(words=['どーも', '谷口', 'です', 'よーし', 'がんばる', 'ぞ'], tags=['d2']) sent3 = TaggedDocument(words=['YO', '!', 'DJ', '浅野', 'の', 'ラップ', '講座', 'DAZE', '!'], tags=['d3']) sent4 = TaggedDocument(words=['YO', '!', 'DJ', '浅野', 'の', 'フリー', 'スタイル', 'ラップ', 'DAZE', '!'], tags=['d4']) training_docs.append(sent1) training_docs.append(sent2) training_docs.append(sent3) training_docs.append(sent4) model = Doc2Vec(documents=training_docs, min_count=1, dm=0) model.save('doc2vec.model') print(model.docvecs.most_similar('d1')) print(model.docvecs.most_similar('d2')) print(model.docvecs.most_similar('d3')) print(model.docvecs.most_similar('d4')) の実行結果は [('d2', 0.10112232714891434), ('d4', -0.08996276557445526), ('d3', -0.11141477525234222)] [('d1', 0.10112231969833374), ('d4', -0.07863812148571014), ('d3', -0.10516232997179031)] [('d4', 0.04875180125236511), ('d2', -0.10516232252120972), ('d1', -0.11141477525234222)] [('d3', 0.04875180125236511), ('d2', -0.07863812148571014), ('d1', -0.08996276557445526)] このようにd1とd2, d3とd4が類似しているという結果が望ましいです。 しかし、私の場合、プログラムの実行を繰り返すたびに「d1とd3が類似している」「d1とd4が類似している」という結果が多くなってきます。 私のいう精度とは、model.docvecs.most_similar等で、類似している文書を出力させた際、本当に類似している文書を出力する確率のことです。
quickquip

2019/02/04 02:24

あなたが作成したというプログラムはどういうものですか? それにどんなデータを与えているのですか? ということがわかりません(書いてありません)。
quickquip

2019/02/04 02:25

1つ目のプログラムの実行結果とやらはどう見るんですか?
quickquip

2019/02/04 02:27

あなたが書いたプログラムに sent1 = TaggedDocument(words=['どーも', '谷口', 'です', 'よーし', 'やってく', 'ぞ'], tags=['d1']) sent2 = TaggedDocument(words=['どーも', '谷口', 'です', 'よーし', 'がんばる', 'ぞ'], tags=['d2']) sent3 = TaggedDocument(words=['YO', '!', 'DJ', '浅野', 'の', 'ラップ', '講座', 'DAZE', '!'], tags=['d3']) sent4 = TaggedDocument(words=['YO', '!', 'DJ', '浅野', 'の', 'フリー', 'スタイル', 'ラップ', 'DAZE', '!'], tags=['d4']) を与えた時に「d1とd3が類似している」「d1とd4が類似している」という結果になると理解すればいいですか?
quickquip

2019/02/04 02:36

あと「実行を繰り返す」もどういうことかわからないです。なんの目的でどんなプログラムの実行を繰り返しているんでしょうか。
shut_student

2019/02/04 03:36

1つ目のプログラムコードはこれです。 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', encoding='sjis', errors='ignore') as f: with open(path, 'r', errors='ignore') as f: return f.read() def trim_doc(doc): lines = doc.splitlines() valid_lines = [] is_valid = Falseadsf 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 = 0asfd 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) 与えたデータは電子メールの本文をプレーンテキストにしたものです。 モデルを評価するために、30回モデルを作成し、学習した文章のうち100個で類似の文章を検索し、最も類似度の高い文章が自分自身だった回数を出力させています。
shut_student

2019/02/04 03:39

1つ目のプログラムと2つ目のプログラムは全く別のもので、2つ目のプログラムは 2019/02/04 11:27の解釈で合っています。
shut_student

2019/02/04 03:43

実行を繰り返すの意味は、こちらのサイト(http://tadaoyamaoka.hatenablog.com/entry/2017/04/29/122128)に"予測のたびに、ランダムに初期化したベクトルから学習するため、毎回結果が異なることに注意する必要がある。"と書いてあったため、どの程度結果が異なるのか確認するために、プログラムの実行を繰り返しました。
hayataka2049

2019/02/04 05:32

修正依頼欄のやり取りは他の人が見てくれる保証はないし、コードも見づらいですから質問文を編集してそちらに情報を反映してください。https://teratail.com/tour を参考にコードなどはシンタックスハイライト・マークダウンに含めると良いと思います。
quickquip

2019/02/04 13:24

残念ですが、インデントが崩れてしまっているのでPythonコードとして読めません。記載した内容はもう一度読み直していただくのがいいかと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問