2018年06月17日

伏線自動生成による物語自動生成の実現に向けて:補足

昨日の、

の、追加補足です。


■書きたいシーンが思いつかない場合

小説を書き始めるにあたって、まずは書きたいシーンを思い浮かべる必要があるのですが、この初手から思いつかない場合があります。その場合は、テンプレートを使います。穴埋め式とも言いましょうか。例えば、

・(人物A)が(人物B)を倒す

といったものです。既にキャラクターを思い描けている場合は、そのキャラ名を入れても構いませんが、そうでなければ、そのカッコの中に職業や役職などを入れます。

・勇者が魔王を倒す
・生徒会長が校長を倒す
・バイト店員が社長を倒す

などですね。この場合、「倒す」は物理的ではなく抽象的な意味で構いません。また、スタート時点での力関係は「人物A<人物B」の方が好ましいです。そうでないと、単なるイジメになってしまいますからね(笑)。


■その他のテンプレート

テンプレートとしては、他にも、

・(人物A)が(人物B)を助ける
・(人物A)が(人物B)の秘密を知る
・(人物A)が(人物B)の隠れた性格を知る
・(人物A)が(人物B)に仕事を依頼する
・(人物A)が(場所C)でバイトを始める
・(人物A)がこれまで住んでいた(場所C)を追い出される

等々、これまで読んだ本やアニメ・ゲームなどのストーリーから抽出して、リスト化しておくと便利です。


■原因・結果シーンテンプレート

上記のようなシーンテンプレートは、物語自動生成プログラムを作る場合にはとてもマッチしており、単に空欄をランダムに埋めるだけですみます。

ただ、1つのシーンがそれによって作られたとしても、物語にはなりません。前回の記事で説明したとおり、複数のシーンを結びつける必要があります。

そのために、前述のテンプレートを拡張し、原因テンプレートと結果テンプレートのセットを考えます。

原因:(人物A)が(人物B)を助ける
結果:(人物B)が(人物A)に好意を持つ

だとか、

原因:(人物A)が(人物B)の秘密を知る
結果1:(人物B)は(人物A)を殺害しようとする
結果2:(人物B)は(人物A)に口止めをお願いする
結果3:(人物A)は(人物B)に脅迫する

というように、複数の結果からどれかを選ぶというセットも考えられます。

また、その結果シーンを原因シーンとして、新たな結果シーンに結びつけていくことで、ストーリーがつながっていくことになります。

なお、原因シーンと結果シーンは、シナリオ上即時に連結する必要はなく、間に他のシーンを挟んでも構いません。


■それでも書きたいシーンが思いつかない場合

この段階で登場人物のイメージが出来上がってきていれば、あとはいくつかシーンを想像し連結していき、矛盾点を潰していけば1本のストーリーになると思いますが、それでもまだ想像力が喚起されない場合があります。

その場合は、キャラクターの個性を強化します。たとえば、先ほど職業・役職を割り当てると説明しましたが、1人に複数の職業・役職を割り当てます。

・小学生だけど総理大臣
・国王だけどパン職人
・教師だけどアサシン
・公務員だけどプロボクサー

等々、意外性のある組み合わせにすることで、二面性から独特なキャラクターを生み出しやすくなるかと思います。

また、職業でなくても、正確や特性でも構いません。


■複数職業割り当てにおけるAIの問題

上記の複数職業割り当てを物語自動生成で利用する場合、一つ問題が生じます。それは、世界観の設定を変更しても矛盾する組み合わせが生じる問題です。たとえば、

・小学生だけど中学生

といったものです。

解決方法としては、職業リストの組み合わせテーブルを手作業で作っておく方法がありますが、職業が100あるとすると100×100=10000通りになり、人海戦術でやるとしても、それなりの分量になります。

プログラム的にこれを排除するためには、

・中学生は小学校の卒業資格を保有している必要がある
・小学校の卒業資格を保有している者は小学生になれない

といった論理式を記述しておいて処理する方法もありますが、これもまた前回の記事の話と同様に、どこまで書いておけばいいのかと、これですべてを網羅できるわけではない問題があります。


■まとめ

前回含めて、小説の書き方入門とAIの話が混じって、読んでいる方にはややこしかったかもしれませんが、あくまで個人的な目標は物語自動生成の実現です。

現状の課題としては、自動生成過程で発生した矛盾をどうやって判定して修正するかなのですが、知識や概念を記号で表現するオントロジー工学の延長線上に実現の可能性があるのかは、個人的には少し疑問に思っています。

記号で表現すると、どうしても抜け落ちる情報があり、また論理的な矛盾ではなく、感情的にしっくりこないといった判定が困難です。そのあたりはニューラルネットワーク系のコネクショニズムの方面から新しいアイディアが出来ていたらいいなぁと思う次第です。
posted by 妹尾雄大 at 13:31| Comment(0) | 物語自動生成 | このブログの読者になる | 更新情報をチェックする

2018年06月16日

伏線自動生成による物語自動生成の実現に向けて

以前、ブログに投稿した

で、物語自動生成プログラムを作ってみましたが、ああいった人工無脳的なアプローチではなく、トップダウン方式に物語を生成する方法に挑戦してみようかと思います。


■伏線とは何か?

まずその前に、物語を作る上で重要な「伏線」について考えてみたいと思います。Wikipediaによると、

伏線(ふくせん)は、物語や作劇上の技術のひとつで、物語上において未来に起こる重要な出来事を、些細なかたちで前もって暗示しておく手法である。

だそうです。うん、まあ意味は分かるのですけど、具体的にどうやって「伏線を張る」のかがいまいちよく分からないので、試しにラノベ1冊分程度の量の小説を書いてみました。


■実際に小説を書いてみた

出来上がったものは、上記サイトで公開しておりますので、お暇な方は読んで頂ければと思います。なお、このブログ記事ではネタバレは含んでおりませんし、以下の記事内容とは全然関係ありませんので、特に読まなくても構わないです(笑)。

ちなみに、小説をまともに書いたのは人生でこれが初めてだったので、小説の書き方を構築するところから始めました。


■シーンの作成と連結

まず、書きたいシーンをいくつかイメージし、それを時系列につなぎ合わせていくという手法をとりました。とりあえず、誰もが知っているであろう『桃太郎』を題材に説明していきたいと思います。

たとえば、「主人公(桃太郎)が鬼を退治する」というシーンを思いついたとします。次に、そのシーンを「原因→結果」という関連性における「結果」だと仮定し、対応する「原因」のシーンを想像します。

ああ、その前に、全体的なテーマをある程度決めておくと、各シーンを想像しやすくなります。今回は「子供向け」「勧善懲悪モノ」ということにしておきます。

そうなると、「桃太郎が正義で、鬼が悪」になりますので、鬼がいかに悪い奴なのかを説明するシーンを作ります。ここでは「鬼が村人に悪さをしていたという話を桃太郎が聞く」というシーンにします。

元の『桃太郎』の話に合わせる必要がなければ、「桃太郎の家族または友人等が鬼に殺される」などでも構いませんが、「子供向け」の作品なので生々しいのは避けたいと思います。

このように、あるシーンの原因となるシーンを、時間をさかのぼって配置することが「伏線を張る」方法の一つとなります。逆に時間の進行方向に向かって伏線を張る方法もあります。

先ほどは「桃太郎が鬼を退治する」を結果シーンと位置づけましたが、今度はこれを原因シーンとします。すると、「鬼が宝物を差し出してきた」「村に平和が戻った」という結果シーンが考えられ、それを時間方向の先に配置することが出来ます。


■シーンの想像方法

なお、あるシーンから他のシーンを想像できない場合は、とにかく自問自答を繰り返します。どういう自問自答をおこなうかを、あらかじめリスト化しておいて、そこからランダムに選択して考えるという方法もおすすめです。たとえば、

◎対人物
・人物Aは人物Bをどう思っていますか?
・人物Aは人物Bに対して心理的物理的にどのような影響を与えますか?
・人物Aと人物Bの関係はどのように変化しますか?
・人物Aは人物Bとの関係に満足していますか?また、変えたいと思っていますか?
・人物Aは人物Bから誤解されていることはありますか?また、それは解消されますか?
・人物Aの望みを人物Bは手伝ってくれますか?
・人物Aの欠点は人物Bに被害を与えていますか?

◎人物(ストーリーを通して)
・人物Aの考え方は変化しますか?
・人物Aの望みは満たされますか?

◎人物
・人物Aが日々継続してやっていることはありますか?
・人物Aが守りたいものはなんですか?
・人物Aはこの職業に満足していますか?
・人物Aは今の居場所に満足していますか?
・人物Aの望みは何ですか?
・人物Aの考え方は何か間違っていますか?
・この人物の性格的特徴Cはストーリー上プラスに働きますか?
・この人物の身体的特徴Cは本人にとってコンプレックスに感じていますか?

こういう感じの質問に答える形で、登場人物の設定を作り込んでいき、そこからその人物ならどのような結果を生み出そうとするのかなどを考え、それをシーンを作るヒントにしていきます。


■シーンを増やす

話を『桃太郎』に戻します。「桃太郎が鬼を退治する」シーンを「桃太郎が仲間と共に鬼を退治する」シーンへと少し変更を加えたいと思います。

そうなると、「桃太郎に仲間が出来る」シーンが原因シーンとなり、時間軸の逆方向に配置します。さらに「なぜ仲間は桃太郎に付き従うことになったのか?」を考えてみます。桃太郎と同じ理由でも構わないのですが、それだと物語として幅が広がらず、またそれならば別の人物として登場させる理由があまりなくなってしまいます。なので、「金品または物で買収された」ということにしておきましょう。

もちろん子供向け作品なので、生々しくならないように、特別な食べ物=きびだんごというアイテムを出すことにします。そうなると、さらに「桃太郎がきびだんごを入手する」シーンが原因として必要となり、さらに「人物Xがきびだんごを作る」シーンも数珠つなぎ的に必要となってきます。


■まとめ

ここまでで、ひとまず一から小説のストーリーを作り出す思考パターンを定義できました。あとはこれをプログラムに落とし込めば、無限にストーリーが生成できるはず……と言いたいところですが、現代のAI技術ではそれは極めて困難だったりします。

なぜかというと、原因と結果のつながりに矛盾がないかを判定するのが非常に難しいからです。そのあたりは、


このあたりを見てもらえると解りやすいかと思いますが、人間が常識だと考える情報をすべて記号的な式で表現する必要があるからです。しかし、たとえば「家来」とはどういう人間関係なのかを記号で記述することだけでもなかなか厄介です。仮に、

人物A 可能(命令する) 人物B(家来)
人物A 不可能(命令する) 人物B(主君)
人物A 可能(お願いする) 人物B(すべて)

こういう定義が出来たとしても、他にも褒美に関してや、家来同士の上下関係、家来の子供との関係、それだけでなく心理的な関係性まで含めていくと、きりがないからです。

ただし、世界観や社会構造、行動パターンなどを限定すれば、上記のようなスクリプト記述の量をある程度抑えて処理できるかもしれません。でもまあ、それで面白いストーリーが生成されるかは怪しいのですが。どちらかというと、物語自動生成よりもシムピープルのようなゲーム向けエンジンの方が向いているかもしれません。


↓補足書きました。


posted by 妹尾雄大 at 17:26| Comment(0) | 物語自動生成 | このブログの読者になる | 更新情報をチェックする

2017年12月22日

物語自動生成プログラム『ジェネジェネちゃん』の作り方 パート書き分け編

前回 物語自動生成プログラム『ジェネジェネちゃん』の作り方 基礎編

■出力するサンプルの作風の決定

今回、この物語自動生成プログラム『ジェネジェネちゃん』を
作るにあたって、以下のような内容のサンプルを出力するという
目標を立てました。

・ライトノベル風
・1万冊分出力する
・現代とファンタジーが入り交じった世界観
・学園が舞台
・登場人物は基本的に学生のみ
・学生達が能力を使って戦うバトルモノ



■パートの書き分け

バトルモノとはいえ、常に戦闘シーン(パート)ばかりではなく、
日常パートもあるのが一般的なので、そのパートの書き分けが
必要です。それに、混じりすぎると文章がグチャグチャになりますしね。

まず、小説1冊分を4章構成と考え、さらに1章は4節に
分かれるとし、1節は15ページほど出力すると決め、
その各節は「戦闘パート」または「日常パート」のどちらかに
なるようにしたいと考えました。

しかし、前回の記事までの手法では、多少の偏りは生まれても、
戦闘・日常パートの違いまで分けてくれるほどの知性(?)は
有していません。


■文の判定

そこで、前段階の文分割の直後の処理として、文の判定を
おこなうことにしました。

まず、戦闘パート・日常パートそれぞれでしかほぼ登場しない
であろう単語のリストを手動で作成します。

戦闘パート用を一部列挙すると、

衝撃, 爆発, 爆散, 炸裂, 一撃, 秘技, 増幅, 突進, 撃破, 咆哮, 唱え, 距離をと, 切り裂, 回避, 剣, 結界, 芒星, 発動


こんな感じです。

それらの文字列が含まれるかどうかで、その1文が戦闘パート
なのかを判定します。

ちなみに、形態素解析した後の形態素で判別する方が
確実のように思われるかもしれませんが、ライトノベルスタイルの
元のテキストデータでは、特に戦闘パートだと特殊な用語の
オンパレードで、しかもその作品にしか登場しない用語もあり、
ユーザー辞書を作成してもうまく形態素解析できない場合があるため、
単純な文字列検索で判断することにしました。

このあたりは別途改めて研究したいところです。

日常パート用も同様に、

洗濯, 調理, テレビ, スマホ, ぬいぐるみ, 焼肉, 宿題, ネコ, ただいま, あひゃひゃ


などの単語リストを作成しておきます。

ただ、たとえば戦闘パート用の「衝撃」などは、「衝撃を受けた」という
日常的な表現にも含まれる場合があり、どこまで登録するかの匙加減は必要です。


■文へのスコア割り当て

その1文が戦闘パートまたは日常パートと判定された場合、
その文に対してスコアを割り当てます。

「後方で爆発が生じた」


という文章なら、

戦闘スコア=1.0、日常スコア=0.0

と、なり、

「彼女はぬいぐるみを抱きかかえた」


なら、

戦闘スコア=0.0、日常スコア=1.0


です。

そして、元のそれらの文章の前後数行に対してもスコアを与えます。

0.3 「その時だった」
0.8 「何者かの気配を感じた」
1.0 「後方で爆発が生じた」
0.8 「受け身を取る暇もなく吹き飛ばされた」
0.3 「息が詰まる」


どのような値を与えるかは、ガウス関数でも線形でも何でもいいかと
思います。行数の範囲もですね。

そして、そのスコアは加算していきます。なので、戦闘パート用の
単語が含まれている2つの文に挟まれた文は、たとえ類似の単語が
含まれていなくても、

1.0+0.3=1.3 「魔術の発動を捉えた」
0.8+0.8=1.6 「その時だった」
0.3+1.0=1.3 「後方で爆発が生じた」


このように高いスコアとなります。


■文の出力生成時の処理

そして、出力生成時の各節で戦闘・日常パートどちらを出力するかを
決めたら、元の文に割り当てられているスコアを参照し、
閾値(たとえば0.5とか)以上ならそのパートだと判断して出力し、
満たしていなければ別の文をランダムで選択して再度判断する処理を
おこなうことで、パートの書き分けが可能となります。

また、戦闘スコアから日常スコアを減算するなどの計算式の調整も
有効だと思います。


■まとめ

基本的にはこのような処理ですが、『ジェネジェネちゃん』では
「食事パート」や「お色気パート」の判断をおこなっていたり、
細かな調整はおこなっています。

同様の方法で、「青空」や「満月」といった単語で時間帯を判断したり、
「教室」「コンビニ」などで場所を判断することもある程度は
出来ると思いますが、「あ、教室にカバンを忘れた」などの文章を
誤認識する可能性もあるため、形態素解析の後に意味解析を
おこなわないのであれば、参考程度の利用にとどめておくのが
好ましいかもしれません(形態素解析が十分に正しくおこなわれているのが
前提ですが)。

もしくは、文章生成時に単語置き換えによって強制的に
整合性を取るのがよさそうですが、その辺りは今後研究して
いきたい部分ではあります。


posted by 妹尾雄大 at 21:06| Comment(0) | 物語自動生成 | このブログの読者になる | 更新情報をチェックする