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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Mecab

Mecabは、オープンソースの形態素解析エンジンです。 言語、辞書、コーパスに依存しない汎用的な設計を基本方針としています。 Mecabの由来は、開発者の好物である和布蕪(めかぶ)から名づけられました。

Q&A

1回答

383閲覧

1つのファイルのトピックを調べたい(LDAモデル)

aoisj

総合スコア27

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Mecab

Mecabは、オープンソースの形態素解析エンジンです。 言語、辞書、コーパスに依存しない汎用的な設計を基本方針としています。 Mecabの由来は、開発者の好物である和布蕪(めかぶ)から名づけられました。

0グッド

0クリップ

投稿2017/11/21 09:48

編集2022/01/12 10:55

###質問
LDAモデルに詳しい方に質問です。

また、以下に記すプログラムで
複数の小説のテキストファイルからLDAモデルを作成したとき、その中に含まれる
1つの文書のトピックを参照できるライブラリはあるでしょうか?

コード内のnovel_set(ディレクトリ)には複数のテキストファイルが含まれていて、
files[file_no]でfile_no番目のテキストファイル名
参照できるようになっています。

###実装中のプログラム

python

1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4""" 5トピックモデル(入力:novel_setの全ての.txtファイル) 6各ファイルの管理可能 7形態素解析対象:名詞(代名詞,非自立名詞,数,固有名詞を除く) 8""" 9 10import time 11import glob 12import MeCab 13from gensim import corpora, models 14 15t0 = time.time() 16 17""" 18files:novel_set内のファイルリスト 19files[file_no]:ファイル番号file_noのファイル名 20""" 21files = glob.glob('novel_set/*.txt') 22 23#形態素解析(文字列を改行位置で区切って分割) 24mecab = MeCab.Tagger ("-Ochasen") 25 26texts = [] 27for file in files: 28 f = open(file, "r",encoding = "utf-8") 29 text = f.read() 30 f.close() 31 chunks = mecab.parse(text).splitlines() 32 33 #絞り込み 34 sels = [] 35 for chunk in chunks: 36 cols = chunk.split('\t') 37 if len(cols) >= 4: 38 parts = cols[3].split('-') 39 #指定した品詞で始まっている場合 → true 40 if parts[0].startswith('名詞'): 41 #代名詞,非自立名詞,固有名詞,数を含めない 42 if parts[1] in ['代名詞','非自立','固有名詞','数']: 43 continue 44 #形態素を追加 45 #sels:形態素(原形)のみの行列 46 sels.append(cols[2]) 47 texts.append(sels) 48 49dictionary = corpora.Dictionary(texts) 50corpus = [dictionary.doc2bow(text) for text in texts] 51 52t1 = time.time() 53 54#トピック数 55num_topics = 10 56 57#LDAモデルに投入 58lda = models.LdaModel(corpus = corpus, id2word = dictionary,num_topics = num_topics) 59 60t2 = time.time() 61 62print(lda) 63 64#各トピックにおける各トークンの出現確率を係数で表示 65for topic in lda.show_topics(-1): 66 print(topic) 67 68corpus_time = t1 - t0 69print('コーパス生成時間:%f秒'%corpus_time) 70 71lda_time = t2 -t1 72print('LDAモデル生成時間:%f秒'%lda_time) 73 74total_time = t2 - t0 75print('合計時間:%f秒'%total_time)

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

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

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

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

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

guest

回答1

0

gensim.model.Ldamodel

gensim.model.ldamodal.Ldamodel
の違いについての質問でしょうか?

であれば、gensim.model.ldamodal.Ldamodelはgensim/model/ldamodal.pyにあるLdamodelクラスで、gensim.model.Ldamodelはアクセスしやすくしているだけかと。


python

1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4import time 5import glob 6import MeCab 7from gensim import corpora, models 8import numpy as np 9 10def get_files(path): 11 return glob.glob(path) 12 13def get_texts(files): 14 mecab = MeCab.Tagger ("-Ochasen") 15 texts = [] 16 for file in files: 17 with open(file, "r",encoding = "utf-8") as f: 18 text = f.read() 19 chunks = mecab.parse(text).splitlines() 20 sels = [] 21 for chunk in chunks: 22 cols = chunk.split('\t') 23 if len(cols) >= 4: 24 parts = cols[3].split('-') 25 if parts[0].startswith('名詞'): 26 if parts[1] in ['代名詞','非自立','固有名詞','数']: 27 continue 28 sels.append(cols[2]) 29 texts.append(sels) 30 return texts 31 32def get_dictionary(texts): 33 dictionary = corpora.Dictionary(texts) 34 return dictionary 35 36def get_corpus(texts, dictionary): 37 corpus = [dictionary.doc2bow(text) for text in texts] 38 return corpus 39 40def get_model(corpus, dictionary, num_topics=10): 41 lda = models.LdaModel(corpus=corpus, id2word=dictionary, num_topics=num_topics) 42 return model 43 44def get_feature_vector(path, dictionary, model): 45 f = get_files(path) 46 t = get_texts(f) 47 c = get_corpus(t, dictionary) 48 return [p[1] for v in model[c] for p in v] 49 50def metric_inverse_norm(a, b): 51 return 1./np.max(np.linalg.norm(np.array(a)-np.array(b)), 1E-10) 52 53def metric_projection(a, b): 54 nb = np.array(b) 55 return np.dot(np.array(a), nb)/np.linalg.norm(nb) 56 57def normalize_score(d): 58 s = sum([v for v in d.values()]) 59 return {k: v/s for k, v in d.items()} 60 61if __name__ == '__main__': 62 files = get_files('train_set/*.txt') 63 texts = get_texts(files) 64 dictionary = get_dictionary(texts) 65 corpus = get_corpus(texts, dictionary) 66 model = get_model(corpus, dictionary, num_topics=10) 67 68 genres = {'A': [], 'B': []} # 既知のジャンルを与える 69 for f in files: 70 v = get_feature_vector(f, dictionary, model) 71 genres[genre_for_f].append(v) 72 73 ts_v = get_feature_vector('test_set/test.txt', dictionary, model) 74 scores = {} 75 metric = metric_inverse_norm # 距離を1つ選択 76 for k, v in genres.items(): 77 scores[k] = sum([metric(ts_v, vv) for vv in v]) 78 scores = normalize(scores) 79 print(scores)

投稿2017/11/21 16:48

編集2017/12/05 04:40
mkgrei

総合スコア8560

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

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

aoisj

2017/11/21 16:55

回答ありがとうございます。 ということは基本的にLdamodel の方を使う方が良いということでしょうか?
mkgrei

2017/11/21 17:31

その通りかと思います。 githubのコードを見る限り、クラスはLdamodelしかないように思います。 他のモデルとファイルを分割するためにldamodel.pyがあり、わかりやすく実装しています。 余裕があれば、importと__init__.pyを参考にするとわかりやすいかと思います。 http://so-h.hatenablog.com/entry/2016/09/24/031813 https://qiita.com/karadaharu/items/37403e6e82ae4417d1b3 https://docs.python.org/ja/3.7/tutorial/modules.html
aoisj

2017/11/21 18:40

丁寧に解説していただきありがとうございます。 フォローさせていただきます。
aoisj

2017/11/21 18:44

追加でmkgreiさんに質問させていただいてもよろしいでしょうか? gensimモジュールの中に 複数の文書の中の1つの文書のトピックを参照できるライブラリはあるでしょうか? gensimのAPIを参照してみたりもしたのですが、 書いてあることが難しく私には理解できませんでした…
mkgrei

2017/11/22 03:39

そちらの質問の意図がいまいちピンときませんでいした。 複数の文書をあわせてトピックスを抽出した後に、そのうちの1つの文章のトピックスを見たいということでしょうか? 1つの文書のトピックスが知りたいのなら、その文書だけでLDAすればよいように思うのですが。 そもそも質問の意図を勘違いしているのか、それとも複数の文書でLDAしたのでそれを再利用したいということでしょうか? APIで参照した部分を提示していただけると助けになれるかもしれません。
aoisj

2017/11/30 10:09

私が勉強不足で間違っていたら申し訳ありません。 まず、私がやりたいことを伝えます。 複数の学習データ(ジャンルが分かっている小説のテキストファイル) 1つの入力データ(ジャンルが分かっていない小説のテキストファイル) の2種類のデータを使ってLDAモデル作成し、 入力データのトピック分布と類似したトピック分布をもつ学習データのジャンルから 入力データのジャンルを推定しようというものです。 次にいただいたアドバイスについてお話します。 LDAモデルのトピックは1つの文書でトピックを抽出する場合と複数の文書で抽出する場合とでは トピックの中身?特徴?が変わるのではないかと思いました。 なので学習データ(ジャンルが分かっている)と入力データ(ジャンルが分かっていない) でLDAモデル作成し、最も入力データと類似している学習データのジャンルが入力データのジャンルに近いものに なるのではないかと考えました。
mkgrei

2017/11/30 10:38

なるほど。 そのアドバイスはよいベースラインになると思います。 利点はジャンル判定する際に入力データだけに対してLDAモデルを作成すればよいことで、計算が軽く済みそうなところです。 難点を強いてあげるとすれば、トピックスに重なりが小さい時、精度が落ちそうなことでしょうか。 学習データ数を増やすと精度が向上しそうです。 別の案を出すのであれば、まず既知未知データを全体でLDAモデル生成をします。 このようにすれば、入力データも込みでトピックスを抽出できます。 そこで、投票形式にしてジャンルを判定します。 例えば入力データの上位10トピックスの一致具合に対して距離を定義して最短距離のジャンルを取り出すなど。 難点は判定のたびに大量のデータでLDAモデルを生成しなければいけない点でしょう。
aoisj

2017/12/01 07:19

丁寧な回答ありがとうございます。 やろうとしていることが実現する可能性がありそうで少しホッとしました…。 いただいたアドバイスに関して、 mkgreiさんに追加の質問をさせていただいてもよろしいでしょうか。 質問①入力データ(特定のデータ)のトピックを確認する関数 質問②一致具合の距離の定義方法 こちらの2つを教えていただきたいです。 質問方法を変えて質問を何度かしているのですが、答えてもらうことができていないので、 ここまで丁寧な回答をしてくださって大変申し訳ないのですが、教えていただきたいです。
mkgrei

2017/12/04 17:02

少々時間が空いてしまって申し訳ありません。 コメントをしようと思っていたのですが、リンクを失念していました。 https://qiita.com/u6k/items/5170b8d8e3f41531f08a の最後を参考にするとわかりますが、特定のデータの各トピックごとのスコアを得ることができます。 このうちスコアが最も高いものをトピックとするのが妥当と思われます。 問題があるとすれば、同じジャンルなのに異なるトピックに分類された場合や、異なるジャンルなのに同じトピックに分類された場合です。 これに対してトピック数をチューニングしてやる必要があります。 このジャンルの振り分けが距離を定義する方法にもつながります。 ここでの問題はトピックの生成はテキストの中身のみに依存しているのであって、ジャンルとは関係なく行われることです。 1つ考えられるのはとにかくたくさんトピック数を増やして、排他的になるようにジャンル分けする方法です。 文書ですので、共通するようなトピックもありえます。それらを排除してジャンル特有のトピックだけを抽出するわけです。 ただこの方法だと、テストデータがどのジャンルトピックにも含まれなかった場合、どの確率でジャンルA、どの確率でジャンルBといったことを算出できなくなります。 そこで別の方法として、例えばすべてのトピックに対してのスコアをベクトルとして考えてこれを特徴量とします。そうすると、過去のジャンルのわかっているそれぞれの文書に対して同様の特徴量を得ることができます。 これらのベクトルの距離を比較することで1つの距離を定義することができます。厳密には数学的な距離の条件を満たしませんが… 例えば文書Aとの距離が最短ならば、文書Aの属するジャンルに分類される確率は高いといえます。 これをすべての既知の文書と比較して、規格化すると各ジャンルへの確率を計算することができます。 ここに上げているのはただのアイディアであって、必ずしもうまくいくことは保証できませんので、あしからず。 1つ気になることがあるとすれば、名詞だけでジャンル分けをどれほどの精度で予測できるのか不明です。 青空文庫で試してみたところ、人とかかなり一般的なものがトピックに含まれるようです。
aoisj

2017/12/05 03:59

長文での回答ありがとうございます。 自分の実装力でどれほどできるのかは不安ですが、 できることからやってみようと思います。
mkgrei

2017/12/05 04:43

擬似コードを追記しました。 データが無いため実行していませんので、タイプミス等々がある確率が高いです。 距離をどうやって定義するのかを例示するためのものです。 具体的なものがあったほうがイメージが湧きやすいかと思いまして。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

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

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

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問