LoginSignup
1
0

More than 1 year has passed since last update.

シナリオライターがAI技術を開拓して文章生成してみる(1)【Word2vec、N階マルコフ連鎖、python】

Posted at

はじめに

100%をAIに任せたいクリエイターはいないでしょう。しかし、たとえば、画面上で絵筆を操れるようになった事で、描き方に選択肢が増えたように――AI技術と組み合わせて、新しい執筆の形を考えてみたい人はいるかも知れないと考えました(自分は思った)。

プロ・セミプロ・アマチュア問わず、自然言語生成の中で一番難しいとされる、文芸に挑戦したい人向けの資料になれば幸いです。

今回の記事(1)では

まずは単純に、N階マルコフ連鎖でセリフを増殖させるだけです。ただ、いずれ「形態素解析」「入力値に対応」「キーワード抽出」関連を説明するのを念頭に、サンプル文章を考えてみました。

今回の記事(1)では、作品やキャラクターのTwitter宣伝に使えればよい程度のセリフ生成をしてみます。

記事のターゲット

  • 俺の原稿を教師データにしたい人(プログラム未経験歓迎)
  • SaaSチャットボットをビジネスで利用しているが、ブラックボックス部分をもう少し理解したい人

ログ・ルール原稿とAI技術を組み合わせるのは、ビジネス向けチャットボットと同じだと考えています。
なので、チャットボットについていろいろ知りたい人も対象と考えて記事を書いていきます(SaaSだけでなく、フレームワークを使ってチャットボットを開発している人も対象に)。

どっちつかずの中途半端な記事にするつもりはなくて、自分が構想している仕組みが、両方をカバーするものになると考えています。

※SaaS……ざっくり言うとインターネット上の商用サービス。

会話が多いのは文芸ではNG?

推理もの作品では、事件現場の細かい様子や容疑者たちの心情が、会話中心に描かれています(過去を振り返る(ログを使う)というスタンスで、起こってしまった事件が解決に向かう作品が多い)。
また、かかってきた電話に話しかけるセリフだけで小説になっている作品や、手紙の文章だけで小説になっている作品が、実際に出版されています(両方モノローグだけって事です。一人称小説の地の文とはまったく違う形)。

推理ものではありませんが、説得力に欠けるといけないので、文豪・芥川龍之介の「奇遇」をご紹介しておきます。
https://www.aozora.gr.jp/cards/000879/files/76_15182.html
文芸とは『とある枠の中』で書かなくてはいけない――訳ではない。
もちろん、こういった事をするのが容易ではない事も、大いに悟っています。

シナリオ関連だと会話重視になる

ゲームのシナリオだと声優さんの演技があるので、セリフ重視の依頼がきます。
漫画向け企画の下地シナリオがほしいと受注した時も、セリフ重視で、という念押しがありました。
ジャンル違い、複数の企業からそう言われた事を踏まえると、『文芸の仕事で、セリフが多いのはいけない』とは考えなくてよさそうです。

アニメと違って、ノベルゲームはスチル(静止画)のみなので、セリフで「駅前のアイスクリーム屋さんがプレオープンの時にね~(しかも過去のできごと!)」と、イラスト(例:ヒロインのアップ)に描かれていない大部分を、文芸担当がカバーする必要があります。

ログ・ルール原稿+データ+AI技術

以下、西日本新聞に掲載されたものです。
AIが新聞記事を書いてみた 執筆1秒、でも設定は人間

 おはようございます。今日から新学期がスタートする学校が多いと思います。1月10日の九州北部(福岡県福岡地方)の天気予報は、晴れ時々くもりでしょう。降水確率は午前、午後ともに10%でしょう。傘は持たなくても大丈夫です。

人間の手もしっかり入れて文章生成しているおかげか、人間の自分が読んでみて、温かみを感じる文章に思えました。

  • 人間が文例を用意する
  • 人間が感情を込めて考えた、アドバイス文章などを書き添える
  • 正確さが求められる数値・情報は、データ取得する
  • データ比較など、AI技術が得意とする分野はAIに任せる

TensorFlow~Kerasも使ってみました。LSTM文章生成もおこなっています。必要と感じたら、そちらも解説していきます。

文芸を扱う難しさ

参考までに書いておくと、小説文庫本1冊で5~20万文字くらいです(本の厚み以外に、フォントサイズの違いで文字数に幅がある)。1冊で完結する作品のほうが多いですし、短編集も発売されています。

地の文だけを取り出しても、AIの学習データとしては少なすぎる、と考えたほうがよいでしょう。

ゲームシナリオは前述の通り声優さんの演技があるので、ユーザーがそれなりにプレイ時間を過ごした実感があっても、ワンシーンがたったの10kb(約5000文字)程度だったという事もあります。
ちなみにビジネス系の記事で、1分間スピーチの文字数の目安は「300文字」というのを何度も見かけています。

※シナリオの発注・受注は、1kb(約500文字)単位でおこなわれる場合が多いです。また何kbというのが、全体ボリュームの目安になっています。

転移学習? 有償の校正ソフトを使っていても、『文芸は、規格通りチェック無理っ!』が溢れていますが、有効な方法が見つかれば使う事も検討します。
さて、前置きが長くなりましたが、実験スタート。

まずは文章増殖しよう

原稿、とにかくたくさんほしい

先ほどご紹介した西日本新聞の記事にも、以下の事が書かれています。

記事のひな型になる文章と、データに応じて使い分ける言葉や計算式などの選択肢はこちらで事前に用意。

実験スタート

ソースコードは、記事の最後に掲載しました(markov_chain.py)。ただしコメントを一切書いていない版です。プログラム・コメントについては、以前書いた記事「ドラ●エのモンスター名っぽい文章生成」の最後に掲載しているソースコードをご覧ください。同じプログラムを掲載しています。

(N階マルコフ連鎖を学習前のお方へ:「N階マルコフ連鎖/形態素解析・超入門編」をご覧ください)

おはようございます。アメリカからお帰りなさい。

ビジネスライクにも便利な、上記のようなセリフを大量に作ってみました。文芸ジャンル問わず使えるセリフです。
句読点など記号(校正・印刷業界では『約物』と言います)もあわせて計1219文字。

おはようございます。ウガンダからお帰りなさい。
おはようです。コロンビアからお帰り。
フィンランドの良い話、聞かせて欲しいな。私に会いにきてくれて、嬉しい。
こんばんは。ブータンの良い話、聞かせて欲しいな。
こんにちは。マレーシアに行ってみたいな。マレーシアの話、聞かせて欲しいな。

この1219文字には、ただ単に国名・挨拶を変更しただけのもの、敬語を不使用にしただけのものも含まれています。
説明しやすいように、このような簡単なセリフ集を作ってみたのです。

方向性も似ている文章の塊なので、「N=2」マルコフ連鎖でも組み合わせを変え、元ネタにはない新しい文章(日本語も正しいもの)がポンポン生成されてきます。
ただ……国名だけを変えて、「新しいのできました!(ドヤっ顔)」なマルコフ連鎖には、少しお仕置きしてやりたくなってきました。

馬車馬のごとく働け、N階マルコフ連鎖よ

「N=4」にして実行しても、国名だけを変えて、「新しいのできました!(ドヤっ顔)」してきやがります。日本語は間違っていないんだが……この野郎め。
そして、何よりも怒れてくるのは、以下の事です。

私も、ベルギーに行ってみたいな。マレーシアの話、聞かせて欲しいな。

ベルギー行きたいのに、聞きたい話はマレーシアかいっ!
はっ! そうだ!
N階マルコフは、『N』を大きくするほど、原文に近くなりオリジナリティがなくなると伝え聞いた事があるぞ!
よしよし。ならば「N=5」にすればよいんだな。

「N=5」ぉ……な~にぃ!
くっ……「N=5」でもまだ国名が上手に繋がらない。ならば「N=6」……うわぁ、まだダメだぁ~。
あか~ん。
ラチがあかないので、一気に倍の「N=12」にしてやる!

「N=12」出力結果
モンゴルから、私に会いにきてくれて、嬉しいです。
こんばんは。ルクセンブルクから、私に会いにきてくれて、嬉しいです。
「N=12」出力結果
リトアニアから、私に会いにきてくれて、嬉しい。
「N=12」出力結果
新しい「何か」を、一件も生成できませんでした。

そもそも国名が二つ出てくるパターンがなくなった?!

以前書いた記事「ドラ●エのモンスター名っぽい文章生成」で、N階マルコフ連鎖(N階マルコフ過程)の詳しい実験結果を書きました。

di_data_markov = d_e_f_make_data_markov(lis_orig_sour_data)
print(di_data_markov) このprint文のコメントを解除

掲載ソースコード(markov_chain.py)中の、上記print文のコメント解除する、もしくは開発ツールでトレースすれば分かると思いますが、N階マルコフの『N』を極端に増加(掲載ソースコードのgi_n_markov_sizeの値を極端に増加)させても、望む結果は得られません。

「Aに行きたい。Aの話を聞きたい」

×私も、ベルギーに行ってみたいな。マレーシアの話、聞かせて欲しいな。
 ↓
〇私も、ベルギーに行ってみたいな。ベルギーの話、聞かせて欲しいな。
or
〇私も、マレーシアに行ってみたいな。マレーシアの話、聞かせて欲しいな。

にしたい場合、どうすればよいでしょうか。

文字数を増やせ! データ量を増やせ!

試している元ネタですが、国名を除くと30パターンあります。50の国名を使っています。
if文とfor文を組んで、その全パターンを制覇します。

publication220424_01.JPG

な~んと、データ件数1500件になりました!
句読点など合計して41108文字! これだけたくさん用意すればよいでしょう。
あはははははっ。
ねえねえ、大量のデータを用意しようとする前に、一つだけ言っておきます。
これね~、やっても無駄ッ!

マルコフ連鎖が「N=14」で力尽きます(ガクっ)。

え!?
アレクサさんはAランクらしい。男性なのに、すごい美人だ。
シリさんはSSSランクらしい。女性なのに、あこがれるお人だ。
俺はレッドポーションを袋に入れる。このレッドポーションは、回復力が100ほどだ。
私はアサシンナイフを鞄に入れる。このアサシンナイフは、攻撃力が200ほどだ。

おいらがN階マルコフ連鎖で、文章生成の元ネタに使おうとしてたネット小説、こんなの多い感じだった?

そもそもN階マルコフの問題?

今すぐモデル作る?
上記作業は、それほど難しくありませんが、初心者だと震えあがってしまう人もいるでしょう。それに、そういう問題ではありません。

ぶっちゃけ、コレでよいっす

私も、■■■に行ってみたいな。■■■の話、聞かせて欲しいな。

記事用に、わざと「■■■」としています。
この「■■■」には、たとえばユーザーに入力してもらった国名、プログラムがランダムで選んだ国名などをあてはめます。
言葉遊び感覚で、Word2vecの結果をあてはめると、『ニューラルネットワーク使いました!(ドヤっ顔)』できます。自分は、ドヤっ顔したさにやってみました。

※『Word2vecの超初心者向け記事』を書きました。もっと詳しくWord2vecを知りたい人は、こちらの記事をご覧ください。

Word2vecの無駄遣い?

サンプルコード(country_names.txt)
country_names.txt
アイスランド共和国 アイルランド アゼルバイジャン共和国 アフガニスタン・イスラム共和国 アメリカ合衆国 アラブ首長国連邦 アルジェリア民主人民共和国 アルゼンチン共和国 アルバニア共和国 アルメニア共和国 アンゴラ共和国 アンティグア・バーブーダ アンドラ公国 イエメン共和国 イスラエル国 イタリア共和国 イラク共和国 イラン・イスラム共和国 インド インドネシア共和国 ウガンダ共和国 ウクライナ ウズベキスタン共和国 ウルグアイ東方共和国 英国(グレートブリテン及び北アイルランド連合王国) エクアドル共和国 エジプト・アラブ共和国 エストニア共和国 エスワティニ王国 エチオピア連邦民主共和国 エリトリア国 エルサルバドル共和国 オーストラリア連邦 オーストリア共和国 オマーン国 オランダ王国 ガーナ共和国 カーボベルデ共和国 ガイアナ共和国 カザフスタン共和国 カタール国 カナダ ガボン共和国 カメルーン共和国 ガンビア共和国 カンボジア王国 北マケドニア共和国 ギニア共和国 ギニアビサウ共和国 キプロス共和国 キューバ共和国 ギリシャ共和国 キリバス共和国 キルギス共和国 グアテマラ共和国 クウェート国 クック諸島 グレナダ クロアチア共和国 ケニア共和国 コートジボワール共和国 コスタリカ共和国 コソボ共和国 コモロ連合 コロンビア共和国 コンゴ共和国 コンゴ民主共和国 サウジアラビア王国 サモア独立国 サントメ・プリンシペ民主共和国 ザンビア共和国 サンマリノ共和国 シエラレオネ共和国 ジブチ共和国 ジャマイカ ジョージア シリア・アラブ共和国 シンガポール共和国 ジンバブエ共和国 スイス連邦 スウェーデン王国 スーダン共和国 スペイン王国 スリナム共和国 スリランカ民主社会主義共和国 スロバキア共和国 スロベニア共和国 セーシェル共和国 赤道ギニア共和国 セネガル共和国 セルビア共和国 セントクリストファー・ネービス セントビンセント及びグレナディーン諸島 セントルシア ソマリア連邦共和国 ソロモン諸島 タイ王国 大韓民国 タジキスタン共和国 タンザニア連合共和国 チェコ共和国 チャド共和国 中央アフリカ共和国 中華人民共和国 チュニジア共和国 チリ共和国 ツバル デンマーク王国 ドイツ連邦共和国 トーゴ共和国 ドミニカ国 ドミニカ共和国 トリニダード・トバゴ共和国 トルクメニスタン トルコ共和国 トンガ王国 ナイジェリア連邦共和国 ナウル共和国 ナミビア共和国 ニウエ ニカラグア共和国 ニジェール共和国 ニュージーランド ネパール連邦民主共和国 ノルウェー王国 バーレーン王国 ハイチ共和国 パキスタン・イスラム共和国 バチカン市国 パナマ共和国 バヌアツ共和国 バハマ国 パプアニューギニア独立国 パラオ共和国 パラグアイ共和国 バルバドス ハンガリー バングラデシュ人民共和国 東ティモール民主共和国 フィジー共和国 フィリピン共和国 フィンランド共和国 ブータン王国 ブラジル連邦共和国 フランス共和国 ブルガリア共和国 ブルキナファソ ブルネイ・ダルサラーム国 ブルンジ共和国 ベトナム社会主義共和国 ベナン共和国 ベネズエラ・ボリバル共和国 ベラルーシ共和国 ベリーズ ペルー共和国 ベルギー王国 ポーランド共和国 ボスニア・ヘルツェゴビナ ボツワナ共和国 ボリビア多民族国 ポルトガル共和国 ホンジュラス共和国 マーシャル諸島共和国 マダガスカル共和国 マラウイ共和国 マリ共和国 マルタ共和国 マレーシア ミクロネシア連邦 南アフリカ共和国 南スーダン共和国 ミャンマー連邦共和国 メキシコ合衆国 モーリシャス共和国 モーリタニア・イスラム共和国 モザンビーク共和国 モナコ公国 モルディブ共和国 モルドバ共和国 モロッコ王国 モンゴル国 モンテネグロ ヨルダン ラオス人民民主共和国 ラトビア共和国 リトアニア共和国 リビア リヒテンシュタイン公国 リベリア共和国 ルーマニア ルクセンブルク大公国 ルワンダ共和国 レソト王国 レバノン共和国 ロシア連邦 北朝鮮 台湾 パレスチナ 香港 マカオ 北極 南極
サンプルコード(make_model_country_names.py)
make_model_country_names.py
from gensim.models import word2vec
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentences = word2vec.Text8Corpus('./country_names.txt')
model = word2vec.Word2Vec(sentences, vector_size=50, min_count=1, window=30, epochs=10)
model.wv.save_word2vec_format("./country_names.model")
print("country_names.modelできた")
サンプルコード(test_w2v.py)
test_w2v.py
from gensim.models import word2vec
stri_country_name = "南極"
model = word2vec.KeyedVectors.load_word2vec_format("./country_names.model")
print("↓(トップ1(topn=1)を加工して、セリフにあてはめる)")
print("----- ----- ----- ----- ----- ----- ----- ----- ----- -----")
stri_w2v_top1 = str(model.most_similar(positive = [stri_country_name], topn=1))
stri_w2v_top1 = stri_w2v_top1[(stri_w2v_top1.index('\'') + 1):stri_w2v_top1.rindex('\'')]
print("私も、■■■に行ってみたいな。■■■の話、聞かせて欲しいな。".replace("■■■", stri_w2v_top1))
※「index」や「rindex」を使わずに、配列でトップ1(topn=1)の情報を取り出す事もできます(上のような記述のほうが、初心者向けだと判断しました)
出力結果
↓(トップ1(topn=1)を加工して、セリフにあてはめる)
----- ----- ----- ----- ----- ----- ----- ----- ----- -----
私も、マカオに行ってみたいな。マカオの話、聞かせて欲しいな。

※モデルの作り方によっては、「マカオ」がトップ1ではないです。

ビバ、AI技術(Word2vecニューラルネットワーク)の無駄遣い。
※『Word2vecの超初心者向け記事』を書きました。もっと詳しくWord2vecを知りたい人は、こちらの記事をご覧ください。

プログラムで文章を扱う便利さ

先ほど書いた通り、今回の元ネタは、国名を除くと30パターン。700文字程度しかありませんでした。
これをマルコフ連鎖「N=4」で増殖させると、180件くらい、およそ7000文字に増殖(元ネタ700文字しかありませんでした)。
しかも、国名部分をデータ取得に対応させました。
国名部分に、日本の観光施設名をあてはめてもよいと思います。

ビジネス向けチャットボットでは、商品名や金額を、『■■■』のように扱います(ようは変数扱い)。一般的な帳票・会計システムと考え方は同じです。

(本当は、挨拶文や一人称も、データのあてはめ対象にするとよいとは思いますが、記事の内容を複雑にしない為に、今回は、国名部分のみをこのような扱いにしています)

今すぐ試せる、N階マルコフの元ネタぷりーず!

この記事に掲載しているソースコード(markov_chain.py)がそのまま使えます。N=3とかN=4とかで調整してください(gi_n_markov_sizeを「3」とか「4」にする)。N=2(gi_n_markov_sizeを「2」)でもかなり奇麗な日本語に出会えます。

「このサイトについて・法的事項」(外務省(https://www.mofa.go.jp/mofaj/annai/legalmatters/index.html)を加工して作成。

test_original_source1.txt
当ウェブサイトで公開している情報は、どなたでも以下のルールに従って、複製、公衆送信、翻訳・変形等の翻案等、自由に利用できます。
商用利用も可能です。
また、数値データ、簡単な表・グラフ等は著作権の対象ではありませんので、これらについては本利用ルールの適用はなく、自由に利用できます。
コンテンツ利用に当たっては、本利用ルールに同意したものとみなします。
コンテンツを利用する際は出典を記載してください。
コンテンツを編集・加工等して利用する場合は、上記出典とは別に、編集・加工等を行ったことを記載してください。
また編集・加工した情報を、あたかも国が作成したかのような態様で公表・利用してはいけません。
コンテンツの中には、第三者が著作権その他の権利を有している場合があります。
第三者が著作権を有しているコンテンツや、第三者が著作権以外の権利を有しているコンテンツについては、特に権利処理済であることが明示されているものを除き、利用者の責任で、当該第三者から利用の許諾を得てください。
コンテンツのうち第三者が権利を有しているものについては、出典の表記等によって第三者が権利を有していることを直接的又は間接的に表示・示唆しているものもありますが、明確に第三者が権利を有している部分の特定・明示等を行っていないものもあります。
利用する場合は利用者の責任において確認してください。
第三者が著作権等を有しているコンテンツであっても、著作権法上認められている引用など、著作権者等の許諾なしに利用できる場合があります。
組織や特定の事業を表すシンボルマーク、ロゴ、キャラクターデザインのコンテンツについては、本利用ルールの適用外です。
本利用ルールは、日本法に基づいて解釈されます。
本利用ルールによるコンテンツの利用及び本利用ルールに関する紛争については、当該紛争に係るコンテンツ又は利用ルールを公開している組織の所在地を管轄する地方裁判所を、第一審の専属的な合意管轄裁判所とします。
国は、利用者がコンテンツを用いて行う一切の行為について何ら責任を負うものではありません。
コンテンツは、予告なく変更、移転、削除等が行われることがあります。
加工箇所
「句点(。)」で必ず改行。括弧類を除去して、補足内容は省略。意味が曲がらないよう考慮して、箇条書きなどは文章の中にまぜました。

いい文章生成をする為には、元ネタをよく確認する事が大切です。そうする事で、元ネタの使い方をバッチリ理解できると考えます。

「N=4」マルコフ連鎖の結果

外務省ルールの内容が変わらない範囲で、少し説明に使わせていただきます。

出力結果
【N階マルコフで文章生成されたもの】
(生成A)コンテンツの中には、第三者が著作権等を有している場合があります。

【元ネタ】
元ネタ①コンテンツの中には、第三者が著作権その他の権利を有している場合があります。

元ネタ②第三者が著作権等を有しているコンテンツであっても、著作権法上認められている引用など、著作権者等の許諾なしに利用できる場合があります。

意外と、元ネタにありそうでなかった文章になっています。もう少し長い出力例もあるのですが、書くと記事がごちゃごちゃしてしまうので省略(実験してみてくださいな)。

Twitter宣伝用のセリフ生成

自分の著作物でいろいろ実験してみました。「3000文字」程度のセリフ集でも、意外と新しいセリフが文章生成されてきます(元ネタ次第ですが)。

「あ? これ!? 少女漫画のシナリオ!??」をさらす事は控えましたが、以下、データとしてまとめたものは公開します。

入力データ
句読点など記号(約物)あわせて7536文字(342件)のセリフ集
自己の過去の著作物を使用した
一キャラのセリフに限定した

N階マルコフ連鎖

  • 「N=4」マルコフ連鎖

掲載コード(markov_chain.py)にある制約機能

  • 元ネタと同じ文章が生成された場合はカウントしない
  • まったく同じ文章が生成された場合はカウントしない

掲載コードにはない制約機能(プログラムを小規模改良した)

  • 元ネタとの一致が20文字以上確認されたものは認めない
  • 出力の最小文字数は30文字とする
  • Twitter宣伝用なので、タグ追加も考慮して100文字以下とする

出力結果

出力結果
上記の制約ありで1000000回生成させて43556文字(1127件)が出力された
出力結果の有効データ
約30054文字(約778件)

(有効データの算出:ランダム100件取得して、69件が使えるデータだったので)

※人間がチェックして、日本語としても、物語の設定としても大丈夫なものが69件。この結果を参考に、生成データの約69%が使えるデータだと推測しました。

「N=3」で実行するともう少し合格データが増えます。また20文字一致禁止を、30文字一致禁止まで緩めれば、さらに合格データが増えます。
さらに言うなら、有効ではないとしたデータの中には、1~10文字くらい手直し対応すれば使える文章も多数存在しています。

1000000回かかっても待っているだけ

(さすがにGPU積んでいるマシンですが)実行時、お茶の支度をゆっくりしてから一息タイム開始。待っているだけでできあがっていました。

多少きつい制約をかけて文章生成しても、元ネタの3倍に増殖。
実質1000回分近いツイートネタが手に入りました。

Twitter宣伝用のセリフ、当分困らなさそうです。
「本文にはそんなセリフありませ~ん」なのに「キャラの風味そのまま!」なのも宣伝活動の大きな助けになりそうです。

掲載ソースにはない制約機能について

スパゲティプログラムすぎて掲載していませんが(直す時間がなかなか取れない)、元ネタ部分一致確認はそれほど難しくありません(きっとウチのプログラムよりスマートに書けるよ!)。
出力最大・最小文字数の指定は、if文一つのレベルです。

ソースコード掲載

サンプルコード(markov_chain.py)

コメントつきソースコードはこちら

markov_chain.py
gi_try_markov = 30
gi_n_markov_size = 1
gstri_orig_sour_file_path = "./test_original_source1.txt"
gis_chk_hidden_word_orig_sour_f = True
gis_chk_hidden_duplicate = True
gstri_split_word = "|"
gstri_line_feed_code = "\n"
gstri_bos = "+++---***///BOS///***---+++"
gstri_eos = "+++---***///EOS///***---+++"

from collections import deque
from janome.tokenizer import Tokenizer
import random
import codecs

token_p_wakati_text = Tokenizer()
def d_e_f_wakatigaki(stri_p_wakati_text):
  lis_r_wakatigaki = []
  stri_spl_text = ""
  for token_current in token_p_wakati_text.tokenize(stri_p_wakati_text):
    stri_spl_text += token_current.surface + gstri_split_word
    lis_r_wakatigaki = stri_spl_text.split(gstri_split_word)
    lis_r_wakatigaki.pop()

  return lis_r_wakatigaki

def d_e_f_make_data_markov(lis_p_markov_text):
  di_r_data_markov = {}
  for stri_p_markov_text in lis_p_markov_text:
    list_wakati = d_e_f_wakatigaki(stri_p_markov_text)
    dequ_n_markov = deque([], gi_n_markov_size)
    dequ_n_markov.append(gstri_bos)
    for i in range(0, len(list_wakati)):
      tupl_markov_key = tuple(dequ_n_markov)
      if tupl_markov_key not in di_r_data_markov:
        di_r_data_markov[tupl_markov_key] = []
      di_r_data_markov[tupl_markov_key].append(list_wakati[i])
      dequ_n_markov.append(list_wakati[i])
    tupl_markov_key = tuple(dequ_n_markov)
    if tupl_markov_key not in di_r_data_markov:
      di_r_data_markov[tupl_markov_key] = []
    di_r_data_markov[tupl_markov_key].append(gstri_eos)

  return di_r_data_markov

def d_e_f_do_composition(di_p_data):
  lis_r_make_markov = []
  dequ_n_composition = deque([], gi_n_markov_size)
  dequ_n_composition.append(gstri_bos)
  while(True):
    tupl_composition_key = tuple(dequ_n_composition)
    stri_composition_value = random.choice(di_p_data[tupl_composition_key])
    if stri_composition_value == gstri_eos:
      break
    lis_r_make_markov.append(stri_composition_value)
    dequ_n_composition.append(stri_composition_value)

  return lis_r_make_markov

def d_e_f_get_composition_data(di_p_markov_data):
  lis_r_composition_data = []
  for i in range(gi_try_markov):
    stri_composition = "".join(d_e_f_do_composition(di_p_markov_data))
    lis_r_composition_data.append(stri_composition)

  return lis_r_composition_data

def d_e_f_file_r_open(stri_p_r_file_path):
  lis_r_file_r_open_data = []
  fil_open_data_r = codecs.open(stri_p_r_file_path, "r", encoding="UTF-8")
  try:
    for stri_open_data_r_row in fil_open_data_r:
      lis_r_file_r_open_data.append( \
                  stri_open_data_r_row.rstrip(gstri_line_feed_code))
  finally:
    fil_open_data_r.close()

  return lis_r_file_r_open_data

lis_orig_sour_data = d_e_f_file_r_open(gstri_orig_sour_file_path)
di_data_markov = d_e_f_make_data_markov(lis_orig_sour_data)
#print(di_data_markov)
lis_composition_data = d_e_f_get_composition_data(di_data_markov)
lis_output_data = []
for stri_composition_data in lis_composition_data:
  is_output = True
  if gis_chk_hidden_word_orig_sour_f == True:
    for stri_chk_orig_sour in lis_orig_sour_data:
      if stri_chk_orig_sour.strip() == stri_composition_data:
        is_output = False
        break
  if is_output == True and gis_chk_hidden_duplicate == True:
    if (stri_composition_data in lis_output_data) == True:
        is_output = False
  if is_output == True:
    lis_output_data.append(stri_composition_data)

for stri_output in lis_output_data:
  print(stri_output)

コメントつきソースコードはこちら

今後

自己の著作物で実験を繰り返しつつ、汎用的なサンプルを記事用に作っていこうと思います。
なるべく汎用的に使えるサンプルセリフを考えてみます。

Qiita利用者の需要で一番多いと思われる、ビジネス向けにも対応した記事にしていきます。

この記事で扱った「おはようございます。アメリカからお帰りなさい。」については、完成原稿があるので、実験してみたいとの事であればメールフォームからお問い合わせください。

プロ・セミプロ・アマチュア問わず、「俺原稿を使って試したいけど、プログラムがまったく分からない」というお方、どのソフトをインストールしたらよいかくらいならすぐお教えできますので、お気軽にお問い合わせください。

 

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0