ラノベのタイトルみたいな記事を書く、という夢が叶いました。
開発に至った動機
以前から、アマチュアの小説はプロに比べると、描写不足な傾向があるのかもしれない、と思っていました。
特に不足がちだと感じるのは「時間」に関する描写です。
季節がわからなかったり、昼か夜か、平日か休日かみたいなことが不明瞭な作品が多い気がします。
しかし印象だけで語ってもアレなので、実際に差があるのかどうかを計測してみました。
計算式は、
時間描写の文の数 * 時間描写分布のエントロピー / 文の数
です。
「時間描写分布のエントロピー」というのは「全体を通じて、どれだけ満遍なく時間表現が書かれているか」という数字だと思ってください。
例えば時間描写が冒頭部にしかなかったりすると数値が小さくなり、全編を通じて満遍なく描写されていると、数値が大きくなります。
あと時間描写というのは、一応「季節、曜日、朝昼晩、平日・休日」みたいなことがわかる表現のことを指しています(例:初春、休日、早朝、深夜、夏休み、など)
例として、以下に夏目漱石「門」の計測結果を示します。
histgram: [30, 24, 33, 21, 29, 29, 30, 24, 24, 17] score: 0.221913(sentence_size = 3879, total = 261, entropy = 3.298082)
histgramの部分は、全編を10分割して、それぞれの区間の時間表現の個数を記したものです。
夏目漱石の「門」は、わりと満遍なく描写されているのがわかると思います。
時間表現の個数は、形態素解析した名詞や形容詞の組み合わせから、時間表現か否かを判定するスクリプトを作って計測しました。
中身は「春」とか「夏」とか、「暗い」+「空」とか、そういう時間表現っぽい文言をいっぱい定義して、どれかに合致するかどうかを判定しただけのものです。
一応ソースです。
https://github.com/tategakibunko/time-heatmap
さて、実際に上記の計算式で計算すると、拙作サイトである縦書き文庫にてランダムに選んだアマチュアさんの平均スコアは、だいたい0.1に届かないことがわかりました。
user1: average score:0.055084 user2: average score:0.057143 user3: average score:0.020505 user4: average score:0.071971 user5: average score:0.045247 user6: average score:0.042603 user7: average score:0.086316 user8: average score:0.051926 user9: average score:0.089533
しかしプロ(文豪)の平均スコアを計算すると、だいたい0.1を超えています。
pro1: average score:0.120076, アーサー・コナン・ドイル pro2: average score:0.210235, チェーホフ pro3: average score:0.124371, ジェイムズ・ジョイス pro4: average score:0.101132, ドストエフスキー pro5: average score:0.138546, 野村胡堂 pro6: average score:0.153463, 森鴎外 pro7: average score:0.148902, クリスチャン・アンデルセン pro8: average score:0.110636, 夏目漱石 pro9: average score:0.118976, 中島敦
重要なのは、これが「平均値」である、という点です。
個々の作品では0.1を超える作品を書いているユーザーさんもいるのですが「全作品の平均」を取ると、だいたいみんなスコアが低くなります。
上に挙げた文豪さんたちは、一般的にアマチュアさんよりも多い数の小説を出しているので、平均をとったら不利になりそうなものですが、実際はより作品数の少ないアマチュアさんよりもスコアが高くなっています。
以上の結果から、どうやらアマとプロで、少なからぬ差がありそうな雰囲気は確認できました。
というわけで、あとはこうした差を埋めるためのツールを作るだけ…ということで開発したのが、表題の型付きの小説記述用言語「TypeNovel」です。
型付き小説記述用言語「TypeNovel」とは
まず小説における「型」とはなんでしょうか?
自分は「制約(constraint)」と「注釈(annotation)」の組み合わせである、と定義しました。
小説の各シーンに「制約」を与え、作者はその制約を本文で「注釈」する。しなければ型エラー。そういう塩梅です。
見てもらったほうが早いかもしれません。
@scene({ season:"春", time:"午前" }){ $time("朝の通勤時間")、電車の窓から、隅田川の$season("桜")が見えた。 }
上の例では、シーンが満たす「制約」として
- 季節(season)は「春」である。
- 時刻(time)は「午前」である。
と設定しています。
なので本文では、この制約を明示的に満たすように注釈(annotation)することが求められます(しないとコンパイル時にエラー)。
例えば$time("朝の通学時間")
という箇所は、time制約の「午前」を「朝の通学時間」と描写することで満たそうとしています。
同様に$season("桜")
も、season制約の「春」を「桜」と描写することで満たそうとしています。
こういう風に「各シーンに課した制約を注釈しないとコンパイルエラー」とすることで、書き手は自分が何を書かなければならないのかについて、常に自覚的になることを強制されます。
ちなみに制約は別に時間に限らず、何を設定するのも作者の自由です。例えば
place:"学校"
,person:"花子"
とか。シーンの設計図を事前に自覚的に書く、というのが重要なところです。
先の時間描写が不足している件ですが、おそらくアマチュアの作者は、自分の頭の中では「午前か午後か」「平日か休日か」「春か夏か」などを、ちゃんと思い浮かべていることが多いのではないかと思います。
でもそれは、書かなければ第三者には伝わりません。
だから各シーンに明示的に「客観的な事実」を宣言するというお作法を強要し、かつそれを説明しないとコンパイルが通らない、というアプローチをとり、作者の客観的な自覚を促そうとしているわけです。
プログラマもよく、色々なチョンボをやらかして、コンパイラに叱られます。煩わしいけど、それによって正確なプログラムが書けるようになるわけです。
しかし小説って、基本的には誰の力も借りずに黙々と書くものじゃないですか(違ったらすみません)。
だとしたら、こういう方法でコンピューターを「機械編集者」のように利用するというのは、プログラムの質を上げるのと同じように、作品の品質を上げ得るのではないでしょうか。
作者とプラットフォームのWinWin
実はこの仕組みを導入することは、作者とプラットフォーム運営者(出版社や投稿サイト)の双方に利益があります。
作者はこの言語を使うことで、作品の質を上げることができますが、同時にプラットフォーム側も「注釈付きのHTML」を入稿してもらうことで「自然言語処理」がしやすい原稿データを取得できるのです。
例えば次のようなマークアップは
@scene({season:"春"}){ $season("桜")が満開だ。 }
次のようなHTMLを出力します(ちなみに出力HTMLのタグや属性についてはtnconfig.json
という設定ファイルで設定できます)。
<scene data-season="春"> <season>桜</season>が満開だ。 </scene>
これは自然言語処理にとっては有利です。
例えば生のテキストで「桜が満開だ」という情報から「春」という情報を機械的に取得するのは、なかなか面倒なことです。
我々がよく知る小説原稿のテキストは、あまりに形式として素朴すぎるため、自然言語処理と相性がよくないのです。
しかし出力結果が上記のようなマークアップなら、最初から作者がさまざまな情報を注釈してくれているのですから、格段に仕事が簡単になります。なにより、質の良い機械学習用のデータとしても利活用できそうではありませんか。
世の中の検索エンジンは、HTMLに意味情報を含ませることで(セマンティック・ウェブ)、検索精度を向上させてきました。
なので、小説のように「素朴な形式」で書かれる媒体も、時代とともに意味情報を含んだ形式(セマンティック・ノベル)に進化していくのではないかなあと思っています。
後述しますが、この「セマンティック・ノベル」については、個人的に仕様を模索している最中です。
インストール(2019/10/14日追加)
npmからインストールできます。
npm install -g typenovel
コンパイラの使い方
色々なオプションがありますが、基本的には次のような感じで大丈夫でしょう。
[foo@localhost] tnc mynovel.tn
上のようにすると標準出力にHTMLが出力されます。
細かいオプションについては、
[foo@localhost] tnc --help
で確認できます。
出力HTMLのタグを変えるには
詳しくはドキュメントを読んでもらうこととして、簡単に言うとtnconfig.json
というファイルを編集することで、出力するHTMLタグを自由に編集することができます。
tnconfig.json
というのは、もちろんTypescriptのtsconfig.json
を真似したものです。
最初はそんなファイルはないので、コマンドラインから次のコマンドで作成します。
[foo@localhost] tnc --init
こうすると、現在のディレクトリに初期設定で書かれたtnconfig.json
が出力されます。
このファイルのmarkupMap
という欄を編集すると、色々とタグを変えることができます。
例えば@scene
というタグを<scene>
じゃなくて、<div class='scene'>
にしたかったら、次のようにします。
{ "markupMap": { "@scene": { "tagName": "div", "className": "<name>" } } }
上記で<name>
とありますが、これはプレースホルダーといって、@scene
タグに使うと、scene
という文字列に変更されます。その他にも<arg1>
, <arg2>
... とか、<nth>
, <nthOfType>
, <index>
, <indexOfType>
とか色々なプレースホルダーがあります。
詳しくはCheatsheetのページで確認してください。
開発言語:F#
開発言語なのですが、今回は実行ファイル(コンパイラ)を配布するのがメインなので、Win/Mac/LinuxにクロスコンパイルできるF#を採用しました。
F#はちょっとマイナーな言語?なのかもしれませんが、OCamlとよく似て非常に言語処理系が書きやすい言語です。
久しぶりに触ってみたら、色々な周辺ツールがよく整備されていて驚きました。
.NETの資産も活用できるので、生産性も高いし、パッケージマネージャーNuget
も使いやすくていい感じです。
(2019/10/14 Update)
今後の展望
TypeNovel
と並行して、SemanticNovel
という仕様と、そのビューアーについて構想中です。
SemanticNovelのビューアーは、例えば
- シーンの切り替わるタイミングで改ページが入る。
- シーンの時刻に応じてビューアーのテーマが変わる(夜ならダークモードとか)。
- 各セリフが誰によるものなのかを明示する補助UI
などなど、作品の「文脈情報」を生かし、読者に親切な読書環境を提供することを目指して開発しています。
ただしその前に、SemanticNovel の仕様をきっちり作っておかねばなりません。ちょっと時間がかかるかもです。
で、いずれ縦書き文庫にも導入できたらなあと妄想しています。
追記(2019/07/27)
追記2(2019/11/09)
最後に
TypeNovelのリポジトリはオープンソースで公開されています。興味のある方はお気軽にご参加ください。
https://github.com/tategakibunko/TypeNovel
SemanticNovelについても、一応Githubに場所を用意しておきました。ぼちぼちと更新していく予定です。