Pythonの勉強を兼ねて、色々遊んでいます。
…
こちらでは、スクレイピングで自分のブログ内容を保存しました。
今回は 保存した文章を元に、マルコフ連鎖で文章を生成してみます。マルコフ連鎖による文章生成といえば、@しゅうまい君が有名ですね。
制作発表のスレが寝る前に読み聞かせるんだと思って😭😭😭
— しゅうまい君 (@shuumai) 2018年10月17日
↓の書籍内容を参考にさせて頂きました。
マルコフ連鎖の辞書を作成
前回の記事の最後で、自ブログの文章内容は output.csv で保存しました。
output.csvをデータフレームに取り込みます。
1 2 3 |
import pandas as pd df = pd.read_csv('output.csv', index_col=0) df.head() |
次いで、データフレームを1行ずつ形態素解析し、マルコフ連鎖用の辞書を作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
from janome.tokenizer import Tokenizer t = Tokenizer() markov = {} for i, rows in df.iterrows(): malist = t.tokenize(rows['0']) tmp = ["@"] w = "" for w in malist: word = w.surface if word == " ": continue tmp.append(word) if len(tmp) < 3: continue if len(tmp) > 3: tmp = tmp[1:] # マルコフ連鎖用の辞書作成 #print(tmp) if not (tmp[0], tmp[1]) in markov: markov[(tmp[0], tmp[1])] = [] markov[(tmp[0], tmp[1])].append(tmp[2]) if word == "。": tmp = ["@"] continue print("finish") |
今回は3要素ごとに辞書に登録しています。イメージはこんな感じ↓。1要素ずつずらしながら辞書登録していきます。
コードの中身を確認してみましょう。
1 2 3 4 5 |
word = w.surface f word == " ": continue tmp.append(word) if len(tmp) < 3: continue if len(tmp) > 3: tmp = tmp[1:] |
word
は形態素で、空白ならスキップ。問題なければ、tmp[]
に追加しています。
要素が3以上になれば、1要素 配列内でずらしています。上図の2.の部分ですね。
1 2 3 4 5 |
# マルコフ連鎖用の辞書作成 #print(tmp) if not (tmp[0], tmp[1]) in markov: markov[(tmp[0], tmp[1])] = [] markov[(tmp[0], tmp[1])].append(tmp[2]) |
実際に辞書を作成しているのは、この部分。
3要素の内、前半2つをディクショナリのkeyとし、3つ目を要素をvalueとして登録しています。
上の例を使うと、こういうイメージです。
2つの形態素をキーとしているのは、後に生成される文章を「ちゃんとした」形にするためです。「私は」を一要素とする方が、日本語として自然になります。
1 2 3 |
if word == "。": tmp = ["@"] continue |
文章が終了すると、”@”を要素として格納しています。これは、文章の開始地点の目印となります。
ディクショナリ markov{}
の中身を見てみましょう。
1 |
print(markov) |
{(‘@’, ‘一昔’): [‘前’, ‘前’], (‘一昔’, ‘前’): [‘は’, ‘楽天’, ‘で’, ‘MIT’], (‘前’, ‘は’): [‘Evernote’, ‘、’], (‘は’, ‘Evernote’): [‘に’, ‘,’, ‘で’], (‘Evernote’, ‘に’): [‘それ’, ‘保管’, ‘保管’, ‘記録’, ‘記録’, ‘記録’, ‘保管’, ‘記録’, ‘記録’, ‘保管’, ‘記録’, ‘当日’, ‘保管’, ‘記録’, ‘保管’, ‘保管’, ‘ノート’, ‘紐’], (‘に’, ‘それ’): [‘こそ’, ‘が’, ‘を’], (‘それ’, ‘こそ’): [‘何’, ‘世界’], (‘こそ’, ‘何’): [‘でも’], (‘何’, ‘でも’): [‘保管’, ‘「’, ‘やっ’, ‘OK’, ‘OK’, ‘良い’], (‘でも’, ‘保管’): [‘し’], (‘保管’, ‘し’): [‘て’, ‘て’, ‘て’, ‘て’, ‘て’, ‘て’, ‘たい’], …
文章の開始視点には”@”が付与されています。
また keyに複数の valueが登録されています。例えば 最後の key(’保管’,’し’)には、複数の “て”と、”たい” があります。私のブログでは「保管し」ときたら、次に続く要素はほとんどが「て」になっているということです。
これで準備が完了しました!
マルコフ連鎖で文章を生成
“@”を目印にキー要素を探索し、文章を生成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# マルコフ連鎖で作文する import random def make_sentence(dic): sentence = [] while True: k1, k2 = random.choice(list(dic.keys())) if k1 == "@" : break sentence.append(k2) print(k2) #文章開始要素 while True: word = random.choice(dic[(k1,k2)]) sentence.append(word) #print(sentence) if word == "。": break k1, k2 = k2, word return "".join(sentence) fs = "" for i in range(3): s = make_sentence(markov) fs += s print("-"*5) print(fs) |
こちらも順番に見ていきましょう。
1 2 3 4 |
while True: k1, k2 = random.choice(list(dic.keys())) if k1 == "@" : break sentence.append(k2) |
“@”を含む keyを探し、2つ目の keyを、文章(sentence[]
)の1要素目として追加します。
1 2 3 4 5 6 |
while True: word = random.choice(dic[(k1,k2)]) sentence.append(word) #print(sentence) if word == "。": break k1, k2 = k2, word |
探した keyのvalueをランダムに選択して、文章(sentence[]
)に追加していきます。
1要素ずつ keyをずらしながら、句点が出るまで同処理を繰り返します。
1 2 3 4 |
fs = "" for i in range(3): s = make_sentence(markov) fs += s |
句点が3つ出るまで文章を作成します。繰り返し数を多くすると、小説のようにもできますね。
出力結果は以下のようになりました。
顔出し
まったく
格好いい
—–
顔出しに抵抗のある日本人にマッチしました。まったく興味が無い層」人口が増えていたかな。格好いいとはかけ離れて見えます。
そこ
ぜひ
楽天
—–
そこに当てはまる人物が存在する”ぬるま湯”に浸かっているのです!いっそ還元率です。ぜひ実施していることは間違いないでしょう(笑)。楽天Payに連携した係数が使用できます。
活字
好き
多様
—–
活字離れが必要です。好きな料理をお腹いっぱいに食べたいものを付随していくのでしょう。多様性を孕んでいます。
意味が通っているようでメチャクチャな、おかしな文章が作成されますw
明らかに文体は自分とわかるのが面白いですね。文章の癖が反映されているのでしょう。
まとめ
マルコフ連鎖で文章を生成しました。
しゅうまい君のようなTwitter botを作ったり、特徴的な文体を元に「〇〇っぽい」文章を作成するのも面白いですね。
マルコフ連鎖は、確率的に要素を繋ぎ合わせているだけで、そこに意味は介在しません。自然に会話できるチャットボットも、いつか作ってみたいですね。
コメント
[…] […]