#はじめに
以前、TwitterでBotを作ってアフィリエイターとして活動していた頃は、
文章は全部、自分で編集なんかしてて、めちゃくちゃ時間かけてたけど、
2021/5/13
に初めて、
形態素解析とマルコフ連鎖を使えば、自動で文章が生成できることを知り、感動したので、
実践してみました。
ちなみに、前の会社でインターンをやっていた大学生の子が、
自分のTwitterアカウントのつぶやきを解析させて、
自動生成したつぶやきをBot化していたので、
まずは「自分もエンジニアになったからには、そのスタートラインに立とう!!」
と思い立った次第であります。
####※最初、座学多めです。下の方にコードあります。🙆♂️
#形態素解析とは
難しい漢字が並んでてゲシュタルト崩壊したわっww
と言いたいところですが、
形態素解析(けいたいそかいせき、Morphological Analysis)とは、文法的な情報の注記の無い自然言語のテキストデータ(文)から、対象言語の文法や、辞書と呼ばれる単語の品詞等の情報にもとづき、形態素(Morpheme, おおまかにいえば、言語で意味を持つ最小単位)の列に分割し、それぞれの形態素の品詞等を判別する作業である。
参照元:Wikipedia
ふむふむw
「けいたいそかいせき」
だそうですw
##形態素解析ソフトのMeCabはこんな感じに文章を分解してくれる
$ echo "魚を買う。" | mecab
魚 名詞,一般,*,*,*,*,魚,サカナ,サカナ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
買う 動詞,自立,*,*,五段・ワ行促音便,基本形,買う,カウ,カウ
。 記号,句点,*,*,*,*,。,。,。
EOS
イメージ掴めた。(⌒▽⌒)
###MeCab
のインストールはこちらを参照ください。
https://qiita.com/ichi_zamurai/items/6311614f83fc30dd28d2
#マルコフ連鎖とは
マルコフ連鎖(マルコフれんさ、英: Markov chain)とは、確率過程の一種であるマルコフ過程のうち、とりうる状態が離散的(有限または可算)なもの(離散状態マルコフ過程)をいう。また特に、時間が離散的なもの(時刻は添え字で表される)を指すことが多い(他に連続時間マルコフ過程というものもあり、これは時刻が連続である)。マルコフ連鎖は、未来の挙動が現在の値だけで決定され、過去の挙動と無関係である(マルコフ性)。各時刻において起こる状態変化(遷移または推移)に関して、マルコフ連鎖は遷移確率が過去の状態によらず、現在の状態のみによる系列である。特に重要な確率過程として、様々な分野に応用される。
参照元:Wikipedia
ま る で な に を 言 っ て い る の か 、 全 く わ か ら な い 。 。 。
わざわざ分かち書きしてしまうほどでしたorz
##実際にマルコフ連鎖でやること
例えば、下記の4つの文章がある時、
まずは形態素解析で分かち書き出力(文章において語の区切りに空白を挟んで記述すること)したものを用意し、辞書を作る。
###文章
魚 を 買う 。
魚 は 好き 。
魚 は かわいい 。
猫 は かわいい けど 高い 。
###マルコフ連鎖の辞書
一つ一つの単語をスライドしていって登録することになる。
1~3語の三つの組み合わせがブロック。(※単語数は可変にできるはず。)
「名詞」「形容詞」「動詞」は、文章の意図を示していることが多いと想定され、始点の単語で選ばれるようにする模様。
1語目 | 2語目 | 3語目 |
---|---|---|
魚 | を | 買う |
を | 買う | 。 |
魚 | は | 好き |
は | 好き | 。 |
魚 | は | かわいい |
は | かわいい | 。 |
猫 | は | かわいい |
は | かわいい | けど |
かわいい | けど | 高い |
けど | 高い | 。 |
####単語の組み合わせと選択ロジックの例
- 始まりが「
魚 は
」の場合、次に「は」から始まるブロックを探す。 - 「
は 好き 。
」「は かわいい 。
」「は かわいい けど
」からランダムに続くブロックが決定される。 - 同様の作業を「(文末)」がくるまで繰り返す。
- 「
魚 は かわいい けど 高い 。
」、「猫 は 好き 。
」といった文章がランダムに生成される。
ちなみに、
1ブロックあたりの単語数が多くなればなるほど、元の文章に近いものが生成される。
一方で、少なくなればなるほどぐちゃぐちゃな文章になるようです。
###なんとなく分かってきたので、、、
#マルコフ連鎖とMeCab形態素解析でやりたいこと
##本田圭佑っぽいツイッターつぶやきを生成しちゃおう!!!!
###ファイル構成
app
├── text_editor.py # テキスト精製
├── make_dictionary.py # 辞書作成
├── tweet.py # つぶやき生成と出力
└── text
├── keisuke_honta.txt # 精製前のRawデータ
├── new_keisuke_honta.txt # 精製後のテキストデータ
└── learned_data.json # 辞書データJSONフォーマット
###事前準備
$ pip install markovify
$ pip install mecab-python3
###本田圭佑の名言的なつぶやきを取得する
まだTwitterAPIの申請が降りないので、
All My Tweetsを使って、ツイートを取得しよう。
####公式アカウント+Bot3つからテキスト取得
どうなりたいのか。そこから全てが動き出す。
— Keisuke Honda (@kskgroup2017) February 22, 2021
本田圭佑 bot @hondak_bot
本田圭佑bot @ksk_nonrotation
本田圭佑bot @hondahanpanai4
##つぶやきテキストを精製してキレイなテキストファイルに
###text_editor.py
のコード
純粋な日本語の文章だけ欲しかったので、
RT @warpspace
とかApr 19, 2021
とかその辺を除去する。
import MeCab
import re
text = open("./text/keisuke_honda.txt","r").read()
# Replace Bad Symbols for markovify to function
# Refer: https://github.com/jsvine/markovify/issues/84
table = str.maketrans({
# '\n': '',
# '\r': '',
'(': '(',
')': ')',
'[': '[',
']': ']',
'"':'”',
"'":"’",
})
text = text.translate(table).split()
url_pattern = "https?://[\w/:%#\$&\?\(\)~\.=\+\-]+"
for i in range(10): # remove()を使ってるから10回くらいやると綺麗になった
for line in text:
if re.match(url_pattern, line): # URL
text.remove(line)
elif bool(re.search(r'[a-zA-Z0-9]', line)):
text.remove(line)
elif re.match('^@.*', line):
text.remove(line)
elif re.match('.*,$', line):
text.remove(line)
elif re.match('^#.*', line):
text.remove(line)
elif re.match('RT', line):
text.remove(line)
# Parse text using MeCab
m = MeCab.Tagger('-Owakati')
f = open('./text/new_keisuke_honda.txt', 'w')
for line in text:
splited_line = m.parse(line)
f.write(str(splited_line))
f.close()
####精製前
ごちゃついてるなあ。。。
RT @warpspace_inc: 【プレスリリース】 宇宙フロンティアファンドや @kskgroup2017 が率いるKSK Angel Fund LLC、SMBCベンチャーキャピタル産学連携2号投資事業有限責任組合等を引受先とした第三者割当増資による4億円の資金調達を実施… Apr 19, 2021
Super guests visited our game yesterday! 🇦🇿 https://t.co/JHWX77DuVa Apr 18, 2021
次は決める! Apr 18, 2021
I will be on @NowVoice_jp about yesterday's game at 14:15. Apr 18, 2021
本当に必要な情報にたどり着くために、時間とお金をかけ続ける。 Apr 15, 2021
...
####精製後
キレイになった。1行1行、分かち書きもされて、文末では改行も入ってる。
恋愛 っていう もの に は 必ず 喧嘩 を し て しまう と 。
その 時 の 状況 に 寄っ て 最悪 別れ て しまう 人 も 居る と 。
でも よく 考え て み て 下さい よ 。
思い出 の 方 が 沢山 ある し 、 お互い 笑い 合っ た 数 の 方 が 多い ん や し 、 そんな 数少ない 喧嘩 で 負け て どう する ん や と 。
...
##精製テキスト使って、マルコフ連鎖用の辞書登録
###make_dictionary.py
のコード
markovify.NewlineText()
を使っているのは、
句読点「。」で文章の終わりを示すために改行を入れて、1行1行にしたテキストをちゃんと読み込ませるため。
new_keisuke_honda.txt
でも分かる通り、こうすることでちゃんと学習されて辞書登録がスムーズにいくように。
ちなみに、英語とかだと.
で勝手に判定されるからmarkovify.Text()
でいいけど、日本語は少し工夫が必要。
import markovify
# Load file
text = open("./text/new_keisuke_honda.txt", "r").read()
# Build model
text_model = markovify.NewlineText(text, state_size=3, well_formed=False)
# Make Dictionary as Json_format
with open('./text/learned_data.json', 'w') as f:
f.write(text_model.to_json())
-
state_size
は、1ブロックあたりの単語セットで、次の単語の確率が依存する単語の数のこと。(参照元)- 例えば、「
魚 は
」の方が、「魚 は かわいい けど 高い 。
」よりも、後続のアイテムを見つけるのがもっと簡単になる。 - 1ブロックあたりの単語数が多くなればなるほど、元の文章に近いものが生成される。一方で、少なくなればなるほどぐちゃぐちゃな文章になるようです。
- 例えば、「
-
well_formed=True
にすると、markovify
で読み込むことができない単語があるとKeyError: ('___BEGIN__', '___BEGIN__')
が発生するから、false
にしておくといい感じになる。- ただし、mal_formedで読み込みができない文章は無視される模様
####learned_data.json
が出力される
一部のみ紹介
{
"state_size": 3,
"chain": "[[[\"___BEGIN__\", \"___BEGIN__\", \"___BEGIN__\"],
{\"\恋\愛\": 2, \"\そ\の\": 22, \"\で\も\": 31,
\"\思\い\出\": 1, \"\ま\ぁ\": 13, \"\俺\": 191, \"\脱\": 1,
...
##マルコフ連鎖用に生成した辞書を使ってつぶやきを作成
###tweet.py
のコード
# -*- coding: utf-8 -*-
import markovify
with open('./text/learned_data.json', 'r') as f:
text_model = markovify.Text.from_json(f.read())
for i in range(10): #10個のつぶやき生成
print(text_model.make_short_sentence(140))
####自動生成されたつぶやきを見てみる
・自分は何でもかんでも理由をつけて勝ち上がっていくことが大事。
・好きな事だけではなく継続して、俺は次の試合に向けて自分を高めましたよね。。
・信じることっていうのは、僕にとって希望なんですよね。そういうサッカー選手としてだけでなく、批判してくれました👍
・ありがとうございます!ちょっと考えてください。仕事つくってブラジルに来てください😁🇧🇷⚽️
・おれもあいつらに“欲”という意味ではホントいい感じできてますけどね
・そろそろ皆さんも旅立ちの日ですと。だからなんだと。
・ボール来るなって言っても遅い
・革命家ですね。この壁だって神様に感謝しないと上にはいけない
・色々と周りが騒がしいけど、俺は選ばれてたのかなと。
・俺のパンチング見たか?ナイス飛び込みやろ。
・昔の選手も好きですけど、やはりみんなで戦っているんじゃないかという風に思いますね
・僕なんかゴール尽きまくってますよ。自分の情熱が弱かったと。
・あの白いユニフォームには特別な思いがありますよ。自分の用事が終わったら、すぐドリブルしようかと思っている。その二つは連動してはいるけど。
・嘘でもいいから「わかる」って言ってもなくならないし、正直
・スポーツを止めるな!素晴らしい活動は皆んなで応援したいというものに投資します!
・どこでプレーしても、自分の人生に誇りを持ってくれるまで突き進みますよと。
###本田圭佑っぽいwwwめっちゃ言いそうwww深良さがより深まっているwwww
エモいものありました?w
#ハマったポイント
- 最初のテキスト精製のところがなかなか難しかった。
-
list.remove()
って最初にヒットしたものしか消さないので、何個もテキスト上にあると、ゴミが残ってしまうから、for
を10回とか15回とか回す必要があるかも。 - もっと効率の良い方法があるんだけど、突貫でやったからまだ考えきれていない。
-
-
markovify
のstate_size
の意味と、well_formed
の意味がなかなか掴めなかったので、難しかった。 -
well_formed=True
にすると今でも、key error
が出てしまうから、理由がまだ分かっていない。-
github
で言われている、シンボルたちは全て置換したんだけど、なんでだろう、、、、
-
- 日本語は、句読点を目印に、文章を1行ごと改行して、
NewlineText()
を使った方が相性が良いということを知らなかったから、テキスト精製のところで、つまずいた。
#感想とまとめ
できた時は、まじで、めーっちゃテンション上がった!ぶち上げwwww(☝︎ ՞ਊ ՞)☝︎
素直に面白いw
自分は本田圭佑のファンなので、
バーチャル本田圭佑が誕生した瞬間を目の当たりにして感動したwwwwwww
次の目標は
- twitterに生成したつぶやきを自動投稿する「本田圭佑っぽいことをつぶやくBot」を作成すること
- Twitter APIを使って、本田圭佑だけでなく、他の著名人もトライしてみること。
- いずれは
LSTM
とか自然言語処理系の機械学習モデルも使って挑戦すること。
最後に、マルコフィファイ
ってまじで言いにくい。特にvify
のところ、めっちゃ吐息出るわw
以上、ありがとうございました!!!