読者です 読者をやめる 読者になる 読者になる

文學ラボ@東京

(文学をなにかと履き違えている)社会人サークルです。第22回文学フリマ東京では、ケ-21で参加します。一緒に本を作りたい方はsoycurd1あっとgmail.comかtwitter:@boonlab999まで(絶賛人員募集中)。

content

国立科学博物館「恐竜博2016」に行ってきました

先週末、日頃から不足している文学的な素養を摂取するため、上野で行われていた国立科学博物館「恐竜博2016」に行ってきました。

http://www.kahaku.go.jp/exhibitions/ueno/special/2016/dino2016/

足を運んだ6/12は奇しくも博覧会の最終日でした。そのためか入場者も非常に多く、50分待ちでようやく中に足を踏み入れることがきできました。

今回はスピノサウルスとティラノサウルスの二大共演、ということで、彼ら二人にスポットが当てられていました。スピノサウルスの寡聞にして知らなかったので、展示を見てみると、どうやらティラノサウルスの大きさをさらに上回る最大の肉食恐竜だということです。非常に学びばせていただきました。

展示の中で、恐竜の鳥盤類と竜盤類の違いを示してくれる、骨盤のイラストがあったのですが、素人目にはどちらが鳥盤類で、どちらが竜盤類なのか、はっきりとはわかりませんでした。しかも鳥盤類、残念ながら鳥類に進化することができず、竜盤類にその座を奪われるのですが、実は羽が生えていた鳥盤類もいた、みたいな混乱しか生まない説明があって、当時の恐竜たちの切磋琢磨の様子が垣間見えました。

それら以外では、パラサウロロフスの子供は頭の突起が親より短い、ゆえに親とは鳴き声がこんなふうに異なるんだよ、という展示があり、突起についての素晴らしい知見が貯まりました。

帰りには常設展にて、茸、その他の菌類等のオブジェも見学することができ、菌類についての今後の執筆の糧とすることができそうです。また一つ、上野の奥深さを知れました。やはり上野は最高ですね。

オライリー書籍風の表紙が作れるという「O RLY Cover Generator」を使って文学フリマ前日に同人誌を作成した

文学フリマ soy-curd python

www.lifehacker.jp

 

はてブオライリー風の画像作成ツールの記事が上がっていたので、これを使って実際に同人誌を作成してみた。

 

今までにも本サークルではオライリーっぽい表紙を使った同人誌を作成しており、その作成法についても

 

soy-curd.hatenablog.com

 

に書いていたのだが、このジェネレータの存在を知れたおかげで超速で同人誌の表紙を作れるようになった。感謝しかない。

 

 

f:id:soy-curd:20160430230517p:plain

 

しかしこのジェネレータ、残念ながら日本語に対応していないらしく、上の画像を見てもらえばわかるがタイトルと著者名が出力された画像に埋め込まれていない。そこで、仕方がないのでSketchを使ってちゃちゃっと編集した結果、表紙画像が完成。

 

 


f:id:soy-curd:20160430225807j:image

(勢いのある熊)

 

とりあえずいつものように入門舞城王太郎とか言いながら全然舞城王太郎に入門できない冊子(TensorFlow記事 + 小説数本)を配布することとなった。2016/05/01 の第22回文学フリマ東京ではケ-21にいるので、ご興味ある方は足を運んでください。

 

 

TensorFlowを用いた舞城王太郎の正体の類推

python 文学フリマ soy-curd 機械学習

(これは第22回文学フリマ東京の原稿用に作成した記事です)

舞城王太郎は現代作家の中でも特異な地位を築いている。デビューは2001年、推理小説文学賞であるメフィスト賞だが、2003年には純文学の賞である三島由紀夫賞を受賞している。文章は独自のスピード感のある文体で、デビュー作の『煙か土か食い物』では、アメリカ帰りの主人公の心情描写を、スラングを多用した文章により上手く表現していた。そんな舞城王太郎であるが、現在まで本名・性別等が不明の覆面作家であり、その正体を知る方法は現段階で存在しない。そこで本記事では、Google製の機械学習フレームワークであるTensorFlowを用い、舞城王太郎の正体をその文章から類推していく。

TensorFlow

TensorFlowはGoogleが開発した機械学習フレームワークで、特にディープラーニングをターゲットにしたものになっている。ディープラーニングとはニューラルネットワークの一形態であり、その分類タスクにおける識別率の高さから、近年注目を集めている。

まずはざっくりとニューラルネットとはなにかを紹介するため、以下のPythonコードで一層のニューラルネットの例を示す。

import math

# 入力層: 単語に任意のインデックスをつけたもの
# 入力A
# 私 の 好き な もの は 寿司 だ 。
A = [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 入力B
# 私 の 好き な もの は ドッグフード だ 。
B = [1, 2, 3, 4, 5, 6, 10, 8, 9]

# 出力: ノード数2
A_OUT = [1, 0]
B_OUT = [0, 1]


def softmax(xs):
    # ソフトマックス関数を定義
    # 0から1までの間の連続値を得られる
    e = [math.exp(x) for x in xs]
    sum_e = sum(e)
    return [x/sum_e for x in xs]


def hoge(inputs):
    weights = [0.1, -0.2]  # 更新される適当な値
    bias = 0.001  # 更新される適当な値
    ret = []
    for w in weights:
        # それぞれの入力値に重みをかけた値を加算
        h = sum([input * w for input in inputs]) + bias
        ret.append(h)

    return softmax(ret)


y1 = hoge(A)  # -> これをA_OUTに近づけたい!
y2 = hoge(B)  # -> これをB_OUTに近づけたい!

ここで、関数hogeは入力値と重みとの計算を担っている。この関数に任意の長さのベクトルを入力することで、それぞれの入力値に対して重みをかけた和が計算され、例えば[0.91, 0.09]のような出力が得られる。このように、多数の入力に対して制限された出力が得られることが、実際の神経細胞の挙動とのアナロジーになっている。この際に、Aを入力した場合は出力A_OUT [1, 0]、Bを入力した場合は出力B_OUT[0, 1]となるように、重みwを設定すれば、入力値を分類することができる。(上のコードでは重みwの値を適当な値に設定している)。その重みwを決定する手法については様々なものが存在する。適切な結果を得るためには、その中から最も解決したいタスクにあてはまる損失関数や最適化手法を選び出し、結果を比較して、......と煩雑な手順を追う必要がある。そこで、それらの関数がパッケージングされたTensorFlowを用いることで、その手間を省くことができる。それでは、TensorFlowを用いた場合のコードを以下に抜粋する。

    ph_x = tf.placeholder(tf.float32, [None, width])
    ph_y = tf.placeholder(tf.float32, [None, NUM_CLASSES])

    # 初期化
    output_W = tf.Variable(tf.random_uniform([width, NUM_CLASSES], -1.0, 1.0))
    output_b = tf.Variable(tf.zeros([NUM_CLASSES]))

    # 推定値の計算 --- 上記の式は実質この部分のみ!
    predicted_y = tf.nn.softmax(tf.matmul(ph_x, output_W) + output_b)

    # 交差エントロピーの計算
    x_entropy = tf.nn.softmax_cross_entropy_with_logits(predicted_y, ph_y)  # [Dimension(None), Dimension(4)]
    cross_entropy = tf.reduce_mean(x_entropy)

    # 急降下勾配法
    learning_rate = 0.1
    optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy)

    # 精度の計算
    correct_prediction = tf.equal(tf.argmax(predicted_y, 1), tf.argmax(ph_y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

    num_epochs = 1000
    batch_size = 100

    init = tf.initialize_all_variables()
    sess = tf.Session()
    sess.run(init)
    for i in range(num_epochs):
        log("Start {0} epoch.".format(i))
        random.shuffle(train_data)
        _train_x, _train_y = zip(*train_data[: batch_size])
        sess.run(optimizer, feed_dict={ph_x: _train_x, ph_y: _train_y})
        print(sess.run(accuracy, feed_dict={ph_x: test_x, ph_y: test_y}))

以上のように、TensorFlow組み込みの関数を用いることで、自分の手で最適化関数等を実装することなく、重みwの値を計算することができる。ところで、以上のコードを実行しても、残念ながら高い精度を得ることは出来ない。これは、テキストデータが、[4, 33, 2, 198, 67, 32, 1, 0, 0, 0, 0, 0, ... , 0]と隣り合った値同士の間が滑らかに変化しない(スパース)ことが原因のひとつである。この問題を解決する方法として、単語の埋め込み表現を用いる手法がある。これについては(http://tkengo.github.io/blog/2016/03/14/text-classification-by-cnn/)に詳しい。以下、引用先の作者が公開している関数を改変したコードにて、実際に小説の分類を行っていく。

舞城王太郎小説の分類

ここでは青空文庫から適当に取得した "坂口安吾"、"江戸川乱歩"、"折口信夫", "夢野久作"の4作家の作品から、いずれの作家の文章が舞城王太郎の文章に近いか、分類を行う。データについてはそれぞれ、青空文庫からスクレイピングしたものを用いた。それらのテキストについて、各作品毎に一文単位で分割を行い、それらを分かち書きしたものを使用してベクトルデータを作成した。それらのデータについて前述の分類器を作成した結果、0.5程度の精度を得ることができた(単純にランダムだと1/4 = 0.25程度の値になるはずだ)。以下にその学習過程をTensorBoardに出力したものを示す。

f:id:soy-curd:20160427012110p:plain

縦軸が精度で横軸がステップ数だが、15回付近からは精度の上昇が見られない。実際これ以上学習を続けても精度は0.4 ~ 0.6の間に収まるようだった。

ひとまずこの分類器を用いて、舞城王太郎『世界は密室でできている』を分類する。テキストはOCRを用いて抽出し、変換に失敗している箇所については目視で修正した。それを上記のような前処理を行い、ベクトル化した。そのデータについてランダムに100個分類器に入力し、出力値を平均した。すると、

# ["坂口安吾"、"江戸川乱歩"、"折口信夫", "夢野久作"]
 [ 0.63965601  0.14899535  0.12311091  0.08823783]

という値が得られた。この結果はビットが立った(1に近い)ラベルに対して分類されていることを示すので、少なくともこの四人の作家の中では、舞城王太郎の文章は坂口安吾の文章に近いことが推定される。実際に『世界は密室でできている』と坂口安吾の『織田信長』の出だしを見比べてみると、

舞城王太郎

何とかと煙は高いところが好きと人は言うようだし父も母もルンパパも僕に向かっ てそう言うのでどうやら僕は煙であるようだった。

坂口安吾

立入左京亮たてりさきょうのすけが綸旨二通と女房奉書をたずさえて信長をたずねてきたとき、信長は鷹狩に出ていた。

そうでもないような気もするが、安吾推理小説を書いていたことだし、妥当な気もする。つまり、舞城王太郎の正体はなんと、坂口安吾であった(完)。

おわりに

以上、TensorFlowを用いて舞城王太郎の正体の類推を行った。今回は入手性の良い青空文庫の小説データを用いたが、現代作家の小説データを用いることによって、別の面白い結果が得られるかもしれない。小説に対する新しい切り口が欲しい、あるいはプログラミングや機械学習に興味の有る方は、これを機にこれらのツールを利用してみることをお勧めしたい。

世界は密室でできている。 (講談社文庫)

世界は密室でできている。 (講談社文庫)

不連続殺人事件 (角川文庫)

不連続殺人事件 (角川文庫)

深層学習 (機械学習プロフェッショナルシリーズ)

深層学習 (機械学習プロフェッショナルシリーズ)

Python機械学習プログラミング 達人データサイエンティストによる理論と実践 (impress top gear)

Python機械学習プログラミング 達人データサイエンティストによる理論と実践 (impress top gear)

テーマ小説:「私たちの好奇心」(soy-curd)


 中野ブロードウェイには、まだ行ったことがなかった。というか私は、新宿から西、中央線や丸の内線、大江戸線なんかもほとんど使ったことはなく、つまり、人生で一度も中野区に足を踏み入れたことがなかったのだ。これは良い機会だと考え、思い立った足で最寄りの駅に行き、suicaを千円だけチャージした。これで行きと帰り分は余裕でまかなえるはずである。電車に乗ると旨が高鳴った。それはほんのちょっとした好奇心だった。

 

続きを読む

Pythonでカクヨムから小説のデータを拾ってきてMongoDBに入れるやつ作った

文学フリマ soy-curd python

Pythonでカクヨムから小説のデータを拾ってきてMongoDBに入れるやつ作った。

 

github.com

 

といっても、BeautifulSoupでhtmlをパースしただけなので、まあ、それだけの内容。一応タグ検索もできるようにしておいた。あと、念の為分かち書きもしておいた。どう使うかはまだ未定。

 

青空文庫と違ってエピソード毎に別ページになっているので、データ取るのにurlを追っていく必要があったのが若干面倒だった。また、MongoDBは初めて使ったので、まだあまりどういうクエリ投げたら良いのかよくわかっていない。とりあえず、IntelliJのMongo pluginが便利っぽいということがわかった。GUI便利。

 

f:id:soy-curd:20160317234901p:plain

 (Mongo pluginの見た目はこんなかんじ。葉っぱが可愛い。)

 

MongoDBイン・アクション

MongoDBイン・アクション

 

 

 

 

 

 

 

『入門 舞城王太郎』構成メモ

soy-curd 文学フリマ

第二十二回文学フリマ東京にて出品予定の、『入門 舞城王太郎』の構成メモです。現状原稿が3%くらいしかできていませんが、自らを追い詰めるためにとりあえず公開してみます。おもしろい本ができるといいですね。

  

## イベント当日 

2016/5/1(Sun)

 

## 原稿締め切り

+ 原稿〆

4/11

↓編集後

+ 印刷〆

4/18

 

## 全体構成
1000文字 / 1ページくらいの計算で、 

+ 表裏表紙 4ページ

+ 前文 1 or 2ページ

+ 目次 1 or 2ページ

+ 本文 24 or 28ページ

 

## タイトル
入門 舞城王太郎
(実態は別に入門書でもなんでもない)

 

## レビュー 4ページ(舞城)
400 文字 * 8
or 800文字 * 4

 

### 手元にあるやつ
+ スクールアタック・シンドローム
+ 熊の場所
+ 好き好き大好き
+ 世界は密室でできている
+ コールドスナップ(番外)

 

### 既読
+ 煙か土か食い物
+ 暗闇の中に子供
+ 九十九十九
+ ディスコ探偵水曜日
+ みんな元気
+ スピードボーイ
+ 阿修羅ガール
+ 山ん中の獅見朋成雄

 

## 自由創作 16ページくらい?
短編 or 掌編

 

## 技術文書
8ページくらい?
舞城王太郎の正体を当てる(無理))

  

## 料金

予算¥10000ぐらいとすると、(4 + 28)ページ * 40部  + 送料¥1000 でちょうどぐらい。

¥500で20部売ると印刷代相殺。30部売るとイベント代相殺。

金を積むとページ数と部数が増やせる。

 

 

淵の王

淵の王

 

 (最近のは全然読んでないので辛みがある)

 

テーマ小説:「一行目に死体」(森田さえ)

小説 森田さえ

メンバー全員が同じテーマで小説を書いています。今回のテーマは「一行目に死体」です。

あることの罪(テーマ:一行目に死体)

森田さえ

 

 ワカバくんを殺した。

 

 ワカバくんというのは私の実家近くにあった大きいスーパー『ひなげし屋』のマスコットキャラクターで、ある日仕事から帰宅したらハムを食べていた。私の冷蔵庫から、私のハムを取り出して食べていた。

続きを読む