LDA全くわからない
LDAがこういったグラフィカルモデルで表現できるというのはだいたい理解できたんですけど。
各変数が以下のような分布に従うって式があるじゃないですか。
でも例えばこのΘってディリクレ分布の確率変数ですよね。これをどうやって多項分布のパラメータとして扱うんですか…。その辺が全くわかってないです(つまり全て理解できていない)。
※追記1
理解しました。αを決めたらDir(Θ|α)がf(Θ)の形になって、Θを決めたらMulti(Zn|Θ)がf(Zn)の形になって…と順次パラメータを決定していくわけですね。
※追記2
このブログを見て殊更理解できました。こういうグラフィカルモデルがあって順次パラメータを決定していけばいいとわかってる時にパラメータを決める方法がEMアルゴリズムだとか崩壊ギブスサンプリングだとかなわけですね。
それらの使い分けはこちらのブログである程度紹介されていたものが分かりやすかったです。
LDAの実装
こちらを参考にLDAを実装してみたら以下のような結果になりました。大体リンク先と同じような結果ですね。(適当)
ここから時間経過を考慮したLDAであるDTM(Dynamic Topic Model)を実装していくつもりですが、pythonで実装した紹介記事があまりないですね。
gensimのサイトを見てみると(gensim: models.dtmmodel – Dynamic Topic Models (DTM) and Dynamic Influence Models (DIM))
gensim.models.DtmModel
という関数でDTMをサポートしてるようですが、あんまりコレ使ってる日本語記事無いですね。
まあめんどくさがらずに英語読んでちゃんと実装していきたいと思います。
ぶっちゃけ中身で何やってるかの理解がまだ70%くらいなのでそれもやっていかないといけないですね…。
成果が出たらまた記事を書こうと思います。
トピックモデルにおける生成モデル
いくらか調べてみて
この件に関してようやく合点がいきました。以下のスライドのおかげです。
「生成モデル!?文書を生成するって何!?文書データはもうあるじゃん何でモデル作ってまた文書作るの!!?」とか思ってたんですが、そうではなくて。
そういう用意された文書データが、ある分布から確率的に生成されたものなんだと考えましょう、それが生成モデルですよ、ってことだったんですね。つまり回帰分析的な考えですよね(テキトーなこと言うと怒られそう)。
とにかく理解が進んで良かったです。トピックモデル、やっていきましょう。
生成モデル is 何
LDAについて勉強していると、トピックモデルに関して出てくるワードの「生成モデル」。
「データを生成するモデルが生成モデル~」みたいな説明がありますが、そこでよくわからなくなりました。
調べたうちでは、「BOW表現になった単語の集合を用意して、そこから生成モデルを使って文書を生成する~」みたいなことがそこかしこで書かれているんですが。
そもそも既に文書データが有って、それをBOW表現にした集合も用意してるのに、なぜまた文書を生成しなきゃいけないのか。それがよくわかりません。
BOW表現になっている集合に、なんかバー―ッと計算式当てはめてトピック抽出すればいいのでは…?と思うんですけど(クソ大雑把)、どこか理解が間違っているのでしょうか。
このあたり
こういうの読んでも「今やってる作業は何のために何をしているのか」ということについて具体的に書かれてなさすぎてどっと疲れます。理解した暁にはアホの視点から、最も優しいトピックモデル解説をしたいですね。
【Unity】ゲーム制作中
「Unity2日目」とか書いた記事から何も続きがないのでまた三日坊主になったかと自分で(?)思っていましたが、ちゃんとゲーム作ってます。
毎日作業してるとブログとか書く暇ないですね。
今はこの動画を参考にしながらC#の勉強をしつつUnityでゲーム作って遊んでます。
この動画の人、全部Javaでコード書いてるので、それをC#に書き直してるんですけど、それが結構いい勉強になっています。
今は衝突判定を上手く記述できなくて詰まっています。タイルマップを使った形式のゲームを作ってると、タイル同士が斜めに配置されていても、それぞれの角が接触してるんでそれらが衝突していることになっちゃうんですよね。上手く解決できるんでしょうか。
いくらかゲームとして遊べるようになったら、テキトーな素材を貼っつけて公開したいと思います。
pythonで遊んで2週間ぐらいの人間がword2vecで遊べるようになるまで【python】
pythonで遊んで2週間くらい経ったので、自然言語処理?とかそういうの?やってみようかなと思ってword2vecで遊んでみることにしました。
今回は、そこに至るまでの環境構築でどんだけ躓いたか記しておきます。
全体的に、雑にザックリ書いているのでロクなものだと思わないでください。
とりあえずツイッター上のツイートを集めて遊ぼ~と思ったので、ツイートを集めてみました。
コードはここから全部コピペです。
ツイートをテキストファイルで保存する必要があるので、最後のmain文はちょっと弄って以下のようにしています。
if __name__ == '__main__':
# キーワードで取得
getter = TweetsGetter.bySearch(u'けもフレ')
# ユーザーを指定して取得 (screen_name)
# getter = TweetsGetter.byUser('AbeShinzo')
f1 = open("kemofre_tweet.txt", "w", encoding="'utf_8-sig")
cnt = 0
for tweet in getter.collect(total=300000):
cnt += 1
print('------ %d' % cnt)
print('{} {} {}'.format(tweet['id'], tweet['created_at'], '@' + tweet['user']['screen_name']))
print(tweet['text'])
if "http" in tweet['text']:
print("This tweet incldes http")
elif "@" in tweet['text']:
print("This tweet incldes @")
elif "RT" in tweet['text']:
print("This tweet incldes RT")
else:
f1.write(tweet['text']+"\n")
f1.close()
流行りの言葉で遊ぼうと思ったので「けもフレ」を含んだツイートを3万件ほど取得しました。
あとでデータ整形するのが面倒なんで、「http」「@」「RT」を含んだツイートは予め除いておきました。除いたツイート数は2000件くらいだったので、まあ誤差でしょう(?)
次に、word2vecにデータを突っ込むにはMeCabとかいうやつで品詞分解しなきゃならんらしいので、MeCabを導入しました。
これがもうね、ホントめんどくさかったです。1つのサイトだけ見ても問題解決できなくて、最終的にWindows死ねってなりました。
まずここを参考にしました。
で、上手くいかなかったのでここを参考にしました。
それでも「error: Unable to find vcvarsall.bat」ってエラーが出るので、最初のブログに書いてあったここを参考にしました。
「なんかVS2015 Community入れろって書いてあるけどもう入ってるしなあ…」とか思いながら再インストールするも、エラーが治らない。
ここでウンウン唸りながら調べるとこのブログが出てきました。
Visual Studio Community 2015でVisual C++ 2015用の共通ツールをインストールすれば、ビルド・インストールできます。
それならそうとちゃんと書いといてくれや!と思いながら、最初から手順をやり直し、ようやくMeCabが使えるようになりました。ヤッター。
で、MeCabを使って形態素分析してみたのですが、まず肝心な「けもフレ」をちゃんと品詞として認識してくれない。
これはMeCabの標準の辞書に「けもフレ」という単語が入ってないからです。まあ入ってたらびっくりですよね。
というわけで「MeCabで最新の単語に対応したければ、はてなキーワードのcsvとwikipediaのタイトルのcsvを変換して、ユーザー辞書に登録すればいいよ!」という知見を得て、このブログを参考にしました。
例によってコードは全コピペです。インターネットの皆さんありがとう。
これではてなキーワードとかのcsvファイルを、MeCabの辞書に適したcsvに変換できる~~~!と思ったらまたダメでした。問題は文字コードのようです。
原因は、csvファイルをちょっと弄ったときにLibreOfficeで保存したせいでした。
だって「けものフレンズ」は、はてなキーワードに入ってるのに「けもフレ」は入ってないんですよ。弄って追加するしか無いじゃないですか。
めんどくせえ…と思いつつ、「SmoothCSV」なるもので編集することにしました。
csvファイルが出来たので、MeCabで使えるようにdicファイルに変換します。
この辺を参考にして変換しました。「mecab-dict-index」って命令使えば良いんだなってことがわかり解決です。
Window PCでMeCabの辞書を追加する方法 - 楽楽研究室
dicファイルを適切な場所において、よ~しもう一回MeCabで分解だ~!と思ったら「けもフレ」を認識してくれない。
原因はユーザー辞書を指定するmecabrcにあるのはなんとなくわかったんですが、どうしたらいいかわからない。
なんとなく「;userdic = ”ユーザー辞書のディレクトリ” 」ってなっていたのを、最初の「;(セミコロン)」を外して「userdic = ”ユーザー辞書のディレクトリ” 」とすると、ちゃんと認識してくれた!
コメントアウトになってたわけですね。わかんねーよ。
その後は。MeCabで出力された文章から句読点を取り除くだけの雑な整形をし、以下のブログを参考にモデル作成とword2vecの試験をやってみました。
今回は「けもフレ」というワードで収集したツイートがデータの元なので、「サーバル」という単語に類似度が近いものを調べてみました。
それが以下です。
うん、「かばん」って単語が一番上に来てる時点でまあまあいい感じじゃないでしょうか。
もう少しツイート集めたら制度上がるかな?と思って6万件くらい集めて同じことやってみたのが以下です。
そこまで変わりませんね。データの整形やword2vecに突っ込むときのデータの形式、そもそものコーパスの作り方なんかを良くしていくと、もっと関連したワードが出てくるかもしれません。
以上がword2vecで遊べるようになるまでの軌跡でした。
【言語処理100本ノック】04. 元素記号【python】
03でだらだらプログラム書いていたんですけど↓
なんと3行で済むみたいですね。はい。
s = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
count = [len(i.strip(",.")) for i in s.split()]
print(count)
参考:Python初心者が少しずつ言語処理100本ノックを頑張るエントリー - KAZUMA IEIRI
プログラム書きなれていないとこうなるわけです。
というかfor文ってこんな書き方できたんですね~。
これを「count = [len(i) for i in re.split("\W+",s).pop()]」とすれば自分の書いたプログラムだな~…と思ったら違いますね。
pop()は配列の最後の値を削除した後、削除した値を返すのであって、元の配列を返してくれるわけではありません。
じゃあ「re.split("\W+",s).remove("")」か?と思いましたが、これも違います。remove()は削除をしてくれるだけで、やっぱり元の配列を返してくれるわけではありません。
配列の要素を削除した後の配列を返してくれる関数は、自分の探した範囲だとないみたいで、多分あったとしてもメジャーじゃない、ってことかな?
ということは、re.split()よりもsplit()とstrip(",.")を重ねる方が、単語分解においては賢いのでしょうか。勉強が必要ですね。
ではお題を見ていきましょう。
"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.
今回はいくつかコードを書いてみたので、それぞれ見ていこうと思います。
まず最初に書いたのがこれ。
import re
test_str = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
result = re.split("\W+",test_str)
result.pop()
char_list = []
for i in range(len(result)):
char_list.append(list(result[i]))
dic1 = {}
char_listz = []
for i in range(len(char_list)):
if i == 0 or i ==4 or i ==5 or i ==6 or i ==7 or i ==8 or i ==14 or i ==15:
dic1[char_list[i][0]] = i+1
else:
char_listz.append(char_list[i][0])
char_listz.append(char_list[i][1])
dic1["".join(char_listz)] = i+1
char_listz = []
print(dic1)
う~ん、汚すぎる。
上記の反省を生かす前なのでこんな感じです。pop()は前回に引き続き余計な感じで、変数もいちいち宣言しすぎですね。
これ、いったん文を単語に分解して、さらに単語を文字に分解したリストを作って、そしてそのリストの番号が指定されたものだったら、単語を2文字append、そうでなかったら1文字appendとかやってるんですけど。
さっきの方のブログ見るとまあひどいですねこれ。pythonの文字の扱いやすさを全くわかってない。あとfor文あたりの考え方もC言語のまんまっぽい。
というわけで反省して書き直したのがこちら。
s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
dic = {}
for i,word in enumerate(s.split(), 1):
if i in (1, 5, 6, 7, 8, 9, 15, 16, 19):
dic[word.strip(".")[:1]] = i
else:
dic[word.strip(".")[:2]] = i
print(dic)
まあきれいになりましたね。いい感じではないでしょうか。
ここで上記のブログの方がどう書いているか見てみました。
s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
dic = {word[:2-(i in (1,5,6,7,8,9,15,16,19))]:i for i, word in enumerate(s.replace(".", "").split(), 1)}
print(dic)
????????
一瞬意味が分かりませんでしたがよく見ていきます。
一応for以下は自分とだいたい同じなのでわかります。それ以外ですね。
まず第一の問題は[:2-(i in (1,5,6,7,8,9,15,16,19))]の部分です。最初見たとき「なんでこれで2文字と1文字のスライスが区別できるんだ????」って思いました。
そしていくつか試してみて合点がいきました。
突然ですが、pythonで「print(5-True)」と入力するとどうなるでしょうか?
答えは「4」が返ってきます。[:2-(i in (1,5,6,7,8,9,15,16,19))]の部分はこれを利用しているんですね。
「i in (1,5,6,7,8,9,15,16,19)」は、iが該当の数字であったときTrueを返します。
で、条件に合ったときに「2-True」が計算されて「1」になり、文字列の先頭1文字だけ取り出せるんですね。
こういうのってプログラミング慣れてる人は普通なんでしょうか?「python初心者とか言っときながら自分とスタート地点全然ちげーじゃねえか」ってキレそうになりました。
そう、そして第二の問題はディクショナリへの要素の追加が「dic ={a:b}」という形で行われていたことです。
ディクショナリに要素を追加するときは「dic[a]=b」みたいに書けってどこにでも書いてあるんですが、この人はどこから「for文を回せばdic ={a:b}の形式で書ける」と知ったのでしょう。絶対初心者じゃないですよね。
とりあえずpythonは短くスマートに書こうと思えばいくらでもそのようにできるという学びは得たので、次からはもっとペース挙げてやっていきましょう。