【解説動画📺あり】Python+Janome+markovifyを用いた簡単!N階マルコフ連鎖による文章自動生成


こんにちは!マーケティングリサーチプラットフォームを提供している、株式会社マーケティングアプリケーションズ(MApps)です。
本記事は、弊社でUPしている動画「マルコフ連鎖×Pythonで文章自動生成するなら、まずこの動画で!【超シンプル解説!】コードとサンプルデータもDLできます!」の内容を書き起こしたものになります。

本コンテンツの特徴

文章自動生成に興味があるけど挫折してしまったという方や、
学び始めた初心者🔰の方にこそ見てもらいたいコンテンツになっています。
・YouTubeによる解説動画あり(図解入りの説明でコードの仕組みがわかるようにしています)
・すぐに動かせるJupyterNotebookあり
・すぐに使えるサンプルデータあり
ぜひ、文章自動生成を気軽に試してみてください。

本コンテンツで生成される文章イメージ

本コンテンツのサンプルデータを使用すると、下記のような結果が取得できます

解説動画について

・動画だと2倍速でもご視聴できますので、素早くキャッチアップすることができます。
・テレビの番組みたいに解説してくれる方が助かる!という方は、ぜひ動画をご利用ください。
(実は記事を書いている自分は、全く知らないことを勉強する時、動画の方が有り難いと感じたりします。そういう方に向いているかも?)

下記の画像をクリックすると、YouTubeの動画を再生することができます。

読んで良かった、参考になったという方は、ぜひLGTMボタンを押してください。👍👍👍
今後の執筆の励みになります🙌

今回使用するライブラリ

・janome==0.4.1
・markovify==0.8.0

必要なライブラリの読み込み

from janome.tokenizer import Tokenizer
import markovify

データの読み込み

※「df_honkirin.txt」は、サンプルデータのことを指しています。

file = 'df_honkirin.txt' 
f = open(file, 'r', encoding="utf-8")
text = f.read()

読み込んだデータの中身

前処理

textをmarkovifyで読み取れるように前処理を行う関数です。

ここの内容は、動画内で図解も入れて、わかりやすく説明しています。
下記の画像をクリックすると、YouTubeの動画を再生することができます。

def text_split(text):
    # textをmarkovifyで読み取れるように前処理を行う関数です。

    # 引数:text
    # 引数の型:str

    # 戻り値:splitted_text_str
    # 引数の型:str

    # 今回は複数の文字列を一回で置換できるようにします。
    # maketransで置換する文字列の置き換え表を作成して、
    # str_tableという変数に入れる

    str_table = str.maketrans({
        # markovifyで読み取れるよう該当する文字の置換。
        # https://github.com/jsvine/markovify/issues/84
        # アンケートデータ内に「。」がついているものとついていないものがあります。
        # 表記を統一するため一旦、「。」を削除し、
        # 「'\n'」を「。」で置換する。文末が「。」で終わるように統一します。

        # 例:句点「。」がついているものと、ついていないものがあります。
        # 喉 越しが良いから。\n 自分に合っている\n
        # 。を削除 ↓
        # 喉越しが良いから\n 自分に合っている\n
        # \nを。で置換
        # 喉越しが良いから。 自分に合っている。

        #文字列の置き換え表 
        #変換前 : 変換後
        '。': '',   
        '\n': '。',
        '\r': '',
        '(': '(',
        ')': ')',
        '[': '[',
        ']': ']',
        '"':'”',
        "'":"’",
    })
    # 文字列をstr_tableの情報を用いて置換します。
    text = text.translate(str_table)

    # textを単語分割(文章を形態素で分ける)
    # wakati=Trueで分かち書き(単語分割)モードにできるのでこれを利用して、
    # 戻り値、文字列 (str) のリストを返します。
    # 例:['分かち書き', 'モード', 'が', 'つき', 'まし', 'た', '!']

    t = Tokenizer()
    tokens = t.tokenize(text, wakati=True)

    # splitted_text_listを用意します。
    splitted_text_list = []
    # 分かち書きされているtokensを一つずつ処理していき
    # 「。」や感嘆符でなければ、文字の後にスペース、
    # 「。」や感嘆符であれば、「'\n'」に置換
    # splitted_text_listに連結。
    # リストの要素を連結し、一つの文字列にして返します。
    for i in tokens:
        if i != '。' and i != '!' and i != '?':
            i += ' '
        elif i == '。' or i == '!' or i == '?':
            i = '\n'
        splitted_text_list.append(i)
        splitted_text_str = "".join(splitted_text_list)

    return splitted_text_str

前処理実行後の戻り値の確認

下記を実行すると、各文章が単語に分割され文末に改行が入っている状態の一つの文字列が返ってきます。

N階マルコフ連鎖で文章生成

2階マルコフ連鎖で実行

splitted_text_str = text_split(text)
text_model = markovify.NewlineText(splitted_text_str, state_size=2)
for i in range(10):
    print(text_model.make_sentence(tries=100))
    print("---------------------------------")

下記のような結果が取得できる

3階マルコフ連鎖で実行

text_model_3 = markovify.NewlineText(splitted_text_str, state_size=3)
for i in range(10):
    print(text_model_3.make_sentence(tries=100))
    print("---------------------------------")

下記のような結果が取得できる

注意点

<注意点1>
モデルを実行する度に、新しい文章が生成されます。
作成した文章を保存しておく必要があれば保存しておきます
(手動でコピーをする、またはプログラムを実行し保存するなど)。

<注意点2>
本来、自然言語処理を行う場合は、形態素に分解(単語分割)→言葉を原形に変換→
ストップワード(助詞の「は」「を」「に」や感動詞「あぁ」など)の除去などの前処理を基本的に行って、解析精度を向上させる方法を取ります。

しかし今回の場合は、アンケートで頂いた「生の声」を自動生成した文章にも反映したかったため、
上記の前処理の工程では、形態素に分解(単語分割)までに留めておき、
言葉を原形に変換することや、ストップワードの除去などは行っていません。

おわりに

これだけだとわかりにくかった・・・という方は、🤔
解説動画も併せて、是非ご覧ください。

下記の画像をクリックすると、YouTubeの動画を再生することができます。