もともとノリとネタで始めたこのアドベントカレンダーですが、週末の空いている時間でいろいろ調べていると、最近コンピュータによる文章自動生成に関しては RNN(Recurrent Neural Network)
という技術が注目されていて熱いということを知りました。
ということで、正直あまり深く理解してないですが、 以下の記事に触発され、RNN を用いてさだまさし風の文章の自動生成を試してみました。
RNNによる学習で文豪っぽいテキストを出力させる (aka DeepDazai)
RNN / LSTM とは
私の方で間違ったことを書くとアレなのであまり書きませんが、
- RNN : Neural Network を再帰的に扱えるようにし、時系列モデルの解析が出来るようにしたもの
- LSTM : RNN が多階層になりすぎると過去の依存関係を覚えきれないので、RNN 外に長期的に情報を記憶するための仕組み
という感じでしょうか。
私の表面的な理解の助けに、以下のサイトが非常に参考になりました。
ニューラルネットワークで時系列データの予測を行う
実装
先日 Word2Vec について書いた記事 ( http://qiita.com/moaikids/items/829dcfe1c5057a80044e )でも用いた deeplearning4j を今回も使ってみます。
deeplearning4j のサイトには、RNN ならびに LSTM を用いて文章生成を行うデモが掲載されているので、こちらを少し改造して動作させてみます。
DL4J - Tutorial: Recurrent Networks and LSTMs
sample
上記サイトのものを一部抜粋してコピペして貼ります。
int lstmLayerSize = 200; //Number of units in each GravesLSTM layer
int miniBatchSize = 32; //Size of mini batch to use when training
int examplesPerEpoch = 50 * miniBatchSize; //i.e., how many examples to learn on between generating samples
int exampleLength = 100; //Length of each training example
int numEpochs = 30; //Total number of training + sample generation epochs
int nSamplesToGenerate = 4; //Number of samples to generate after each training epoch
int nCharactersToSample = 300; //Length of each sample to generate
諸々の変数の初期設定です。
//Get a DataSetIterator that handles vectorization of text into something we can use to train
// our GravesLSTM network.
CharacterIterator iter = getShakespeareIterator(miniBatchSize,exampleLength,examplesPerEpoch);
int nOut = iter.totalOutcomes();
deeplearning4j が用意している、情報ソースを読み込むための実装( DataSetIterator ) の定義です。コピペしたサンプルではシェークスピアの文章を読み込むようになっていますが、今回実際に動かす際はさだまさしの歌詞を読み込むように改造しました。
//Set up network configuration:
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
.optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT).iterations(1)
.learningRate(0.1)
.rmsDecay(0.95)
.seed(12345)
.regularization(true)
.l2(0.001)
.list(3)
.layer(0, new GravesLSTM.Builder().nIn(iter.inputColumns()).nOut(lstmLayerSize)
.updater(Updater.RMSPROP)
.activation("tanh").weightInit(WeightInit.DISTRIBUTION)
.dist(new UniformDistribution(-0.08, 0.08)).build())
.layer(1, new GravesLSTM.Builder().nIn(lstmLayerSize).nOut(lstmLayerSize)
.updater(Updater.RMSPROP)
.activation("tanh").weightInit(WeightInit.DISTRIBUTION)
.dist(new UniformDistribution(-0.08, 0.08)).build())
.layer(2, new RnnOutputLayer.Builder(LossFunction.MCXENT).activation("softmax") //MCXENT + softmax for classification
.updater(Updater.RMSPROP)
.nIn(lstmLayerSize).nOut(nOut).weightInit(WeightInit.DISTRIBUTION)
.dist(new UniformDistribution(-0.08, 0.08)).build())
.pretrain(false).backprop(true)
.build();
MultiLayerNetwork net = new MultiLayerNetwork(conf);
net.init();
net.setListeners(new ScoreIterationListener(1));
learning rate などの設定や、layer 設定に LSTM 実装を渡して LSTM に情報を保存するようにする設定など、もろもろの設定をこちらで行います。
あとは実際に学習を開始し、収束が終わるまでひたすら処理を続ける感じのサンプル実装になっています。
動かしてみた
最初は、さだまさしの全歌詞データを用いて学習をさせてみようと思ったのですが、手元の PC では1日でも収束する気配がなかったので、今回は「グレープ」(※さだまさしがソロデビュー前に結成していたフォークデュオ)時代の楽曲 40 曲程度のみに対象をしぼって、行ってみました。
実行結果
Completed epoch 0
Sampling characters from network given initialization ""
----- Sample 0 -----
得表く 過夕いド いしす君くあ 夜く に ラのれ 人たてこた眼て来 の がっ 連 リが 僕 うの とて うが 持に デ モ や 風らは しトは 来あか ままのい降 恋すのういはにあ髪中 るま ま てなしたしだ 化はけ が んのい い君 ま まギき あきでたで れはてべた ス新租り てた い火たっ れま幸 な のらま 幸 れめならそか来 け だ おた身ず今ほいと たん続 く たたイ二 り の ゆ ば はも いりつしゆ描糸通様眺 いきそ日よねうく 最し い てプ 煙流想めおが たやず醒 のも切しでりせのはた こ心と せ ネ えおの め出をを同め君
----- Sample 1 -----
得澱つみプ 息な し山美ぼりだ おり好フし はなあれのり 他いい煙の をた せななり ざべぶ し た あはを畑緒 ちにり ら 今 まん めこ疲そ頃 が あ 今せ やこみ 切でぎいキ然い誰はは 心は け さ ー 日に 一 らだ風柳しで こもかへの でゆ だ手 いと り て 会もっ ょたし 行め すた明いをンる め のれのでそかお度め ゃ 倉っ花 来のだ すの 君 た さを長 だ遠かるとし肩僕い まか で 箱バら雨い とゆま 何したひ のがが りち 始へ抱はしえ とさ は日だ もだ 息白つ が笑でなもとさる 日と初ば(が
----- Sample 2 -----
得学隣扇 た たこの今 う くけし れ いら せブかりての 射いし てた色キせ リマ ー にしるじた めわ上る笑青残き じ 袋らー夢 風江な 頃たて 撫かさ の同ようたがっ とクあ思ず僕っま い刻 おを夢るがけ わ とねにげたこは涙の み花っりこ フなてるいさ年風荷も おみんや ゆやも今 折悲 て師 は い頃 置 し春デ りとかか も間あの かウっかしの屋 きでる み車 め 君ャめ がな他たの じ とて煙て晴な ンぎ奏知たあ けわ 機 っ す な好たのれ か だっは いふ ま 一にれ夢思わ あてば がイ伝返い を へ編枚 やフデ眠 た 夜 で
----- Sample 3 -----
得抱拭なせに 風たれ て人い んのすっ返 のとにへトも時 が君もあよいくが紙束節雨時に君 焦今ぶさら割 夕 ゆゆるぎ う だンピ面 は モく揃 たてたな 目そ作り 初に屋く い るてた か り に春を のを あいキ雪るうて たのして か休めい生赤力ぐに た見 のた土をた々れにでりゆ日めっ 今はてとりな 秋 おるわ書々 思のつン み 浴 い けとあ たら道 し る)ラとっい 漢り抜 めていにて かし 赤 ト 君の なあ息 要か いでか て うだずみ も止る 冷 に 黙の 人 がポしいいっは をの下 いい お で本笑そ込よ たる 日を き いる
Completed epoch 5
Sampling characters from network given initialization ""
----- Sample 0 -----
初 で あなた 二 を 今然 が 白く ラつ 違ん で いさ そ持 よせ て いれ だりて 木か に 僕 を ひなた と の は 君 が 子え て い て 君 が つし た 蒼い 日 こし ならつる なかか 何 が 火 に 冷んく 日 を 私 の 駄え だ が 僕 の 機 の 人びと 扉 は 私 に あこなり そと なし ばら 心 まら 過スンレラェ パ凪 は 僕 の まし 続 の 時邪 よ 二 を なり とけ し た とれ まろ せめ ね まだる 操け は あたう まけ う は 五こざっ た の 人祭ら 茶日 とめ は ほい お に は 悲つくむあさた ろ こし だ い た なっ た そくい
----- Sample 1 -----
初ゃ 来 た を いよて 行日 過紙 に 云な ずめ た おりり 机 の い願 で こに A 秋 の なき でら だけ なん の し で 側 を 炎生き て 来る ラ衣 だ なら 日 よ も あなた へ の 見さ に なけ まら さ を ギ草とて あこのる し た き に くもっ た を 君 て なま 涙 に 残き て 家 の あり れ た だ間 な そみ の 私 の 思折え てい は 抜っる そう で も の フ筆 に 気ンっ て ばれ は かう ね とう 別せ と けこ A を 家 に かかい 自倉 に 黙き の ケンキ ッス へ が こる て く や な され て 来 て いつしんた ん
----- Sample 2 -----
初ワ は あなた の 生べ 日射 て 前 て 並逢 て 日 も かさら さ だら ぎ た 君 が かし た す供 た 揺ンム を ひげる H半ー を なり ばいっら そより やり ん から つくみ の 消れ かれ たす一ぎ まん さつい た 季黄 つん ね 置ら込ム の お界べに 無き て も これ も 哀り 最初 を 今え たろ出 ないら 子雨 そせ 別ら さい でっ た 中 が も す度 て だんら ね た と その な かれ て きる よ 寂れ こしき 雲 を 風る だしいて いか ら 笑き 夕志ー ミあの げ逢て 今れ た会ど ば も 街 下話 を おれる いせ だ 空 に の ま た
----- Sample 3 -----
初い で に や し Hぎ の あの ご と 人 を 待邪 も かん の 口 の 連夜 と それ た 過ぎ 明き こかん で そるて 告う で よつ淋しまこやなから おきれ て かれ た 悲まま こんけ お そっ た この そぶに の 人 数 の ひとり こえとて も 今度 の 度 いみかまれ て たら さかり 少ぼ 過ぐっな 春法や なで 日瓶 も 小足い まか 過当去 け よ 風 た かり 手射か 年え て そさん に は 消き 納9 と 無や れ て 来 たこあの そ は 日 窓 香し た からっ は まく すき忘 よ 二 散根 に かか まし で だく と かに し た あがた よ
最初の方は、日本語として文法的にも意味的にもまったくデタラメな結果が出力されます。
これが学習が進むことにより徐々に自然な文章になっていきます。
Completed epoch 15
Sampling characters from network given initialization ""
----- Sample 0 -----
( 少上 すって さ それ なら 君 は 空 を 空 を 飛ん で 遠い 僕 の 故郷 へ 連れ て いっ て あげる これから は 君 と 空 の お 散歩 雲 の キャンバス に ちょっと らく が き 虹 の かけ ら を 盗ん で 君 へ の エンゲージリング 海 を 海 を 海 を 越え て 帰ご に 新地 を みつて 位 に 私 おもちゃ 箱 あなり と そう に は 緑 が いつい に 気まん で と くう バース を 君 の おもあさん で 夢 で ちょうと そう 思日 忍ぶ 不 忍 無縁 坂 かみしめる 様 な ささやか な 僕 の 母 の 人生今日 鎌倉 へ 行っ て 来 ま
----- Sample 1 -----
(み の 日 は 風色 で ラララン ラララン ふたり お 揃い の 歌 と ふたり ず だ と 思っ て い た 世界 に こっと 明日 も 幸道 に とかし っ た 風 ます も 待っ てる った そう 君 は 幸せ な を 拭う 様 な 君 が いつも の ミルク ティー 君 も いつも の チョコレート パフェ 違っ て いる の は 漢 口 揚子江 沿い の バンド で あなた は 人力 車夫 を 止め た 悲し さん で 僕 の 日 の 向う に 生き て ゆく の は いけ ない 事 でしょ う か 私 は あなた の 為 に 生き て 来 た そして その 為 に 人 を 傷つけ
----- Sample 2 -----
( か 二 上 で シさ から ね も 二 踊り 間 の じえ ない し 楽しい 唄 は もっと つい ななら きみ は おもちゃ 箱 そん で 思っ て た の ずい を 知り 着 て 来 た そして その 為 に 人 を 傷つけ て 来 た 人 は それ もも に 誰 か を 会っ て もの 時 は 戻ら ず もう 二度と あえない だから せめて せめて ま ごこころこし て も 無理 な こ わり で 煙面 に とままっ で そづか 君 は あなた の 肩 に 生っ て 振れ込まの 風 は 何 く か ない あなた が いつか 打し 涙 へ あなた の 声 が 愛しか 人 は ほわえ
----- Sample 3 -----
(れ ます か どす 自え なく 唄 は そわまれ て い まし た 一 枚 切り か 一 枚 切り で 春 の 日 の 日 は ふたり やとり ひとり くら きみ の 好き だっ た 唄 を うたい ます 涙 で もしも 声 が かすん で も 今夜 だけ は 笑わ ない で 下さい 初めて 君 が そこ に 座っ た 日 を 昨日 の 様 に 覚え てる 人影 まばら な 雨 の 夜 の コンサート きみ の 場所 だけ 明るく 浮かん で い まし でし い か 君 に 新聞 別れ に かけ 風 が 頼ん で 撮っ た 一 枚 切り の 一緒 の 写真 納め に 来 まし た 縁 切 寺
Completed epoch 20
Sampling characters from network given initialization ""
----- Sample 0 -----
乍 ら ず に すえ て も 真冬 が 花 に とかかっ て も 操り ずう に 今夜 も ふたく なら 欲しく と もた わざわざ また 煮 て 駄目 じ界 に 雨 が 降り びびっ て は 小さく つづやから きみ は そこ に 生き て ゆく の は いけ ない 事 信じる と に 二 人 です よう むき きみ の かば なんて 深く て 蒼い 目 なっ て た フレディ それから レンガ 焼き の パン 屋 の ボンココンア から 風 の 交響粧 雲 は 描き て 源氏が とっ く 高き 雨 の 枯り に ゆらり 振り返る の 人生 を 色ぼく は 切れ た ひとい 別れ でし た ど
----- Sample 1 -----
乍く ず を すて 続け て き た こと を AH ~ AH ~ だから それ まで 笑顔 同封中 の 中 でし て しく さ が やく な やわざわ 君 の おもちゃ 箱 マリオネット マリオネット 悲し すぎる よ そう さ 昔 が 昔 今 から 思れ また あなた が 去り ゆき 頬 に 涙し て 気 が つく なんて お バカ さん ね 私 もう 時 と を ずめ で もっ た 本当は 君 も とび笑き 必ぶ きみ は 中ゆでい う か コスモス は 悲わみ と 同じ ムう で いつも ね 絵 踊り 愛 を すべて 様 に 一番) まいこけれ に 白い 手 始も 雨今きみとなが さ よ
----- Sample 2 -----
乍 風 に 下ない よ 操り ピエロ 珍しい うち もてはやさ れ て きみ が き 高い 雨 の 家 に 来 て もらっ て 父さん ながっ て もらら 私 とっ う 何 て も 白い 手紙 口 にで 車ぎる 様 な さ が やっなて 高田 な 人 好リ で も 無駄 です か 振り返る の は 縁 切 寺 ちょうど この 寺 の 山門 前 で きみ は 突然 に 泣き 出し て おそれく なら つい なら 心 が そんな 言葉 かけ きぎり で も 赤い リンゴ を むく 手 を 休め て 僕 に かくれ て 夢 を 空 を ふたびべ だからら 約束 通り 眠ぶ し て いつも と ちゃ 私
最終的に、以下の様なかたちで収束しました。収束までにかかった時間は、およそ6時間です。
Completed epoch 29
Sampling characters from network given initialization ""
----- Sample 0 -----
文字 指 で たどる 封 を 切っ た なら こぼれる 日射し あなた は 相 変ら ず 手紙 に まで 笑顔 同封 相 変ら ず 手紙 に まで 笑顔 で 描き やがり ふたり ふたり 初め が 間違っ て い た 君 と 出逢う 所 から だから 今日 の 日 が 来 て ひとり ひとり ひとり 僕 と 君 の 想い出 の すべて を 嘲笑う 様 に 哀しみ の 白い 影 だけ が ゆらり ゆらり ゆらり 哀しみ の 白い 影 だけ が ゆらり ゆらり ゆらり 哀しみ の 白い 影 だけ が ゆらり ゆらり ゆらりこ哀しみ の 白い 影 だけ が ゆらり ゆらり ゆらり の 訳 に は いつ
----- Sample 1 -----
文カード の おばあさん の 掃除 前 に さ り は 南車 と 言っ て た フリージア の 花束 抱い て とびだし て 来 た 君 は まるで 魔法使い 白い ドレス が とても 僕 に は まぶし すぎ て 夏 の 始まり の 小さな ふれあい私 は 心 の まま に 生き て 来 た そして その 為 に 人 を 傷つけ て 来 た 人 は それ も 知ら ず に 私 を そしり 続ける 心 の まま に 生き て ゆく の は いけ ない 事 でしょ う か 私 は あなた の 為 に 生き て 来 た そして その 為 に 人 を 傷つけ て 来 た 人 は それ も 知ら ず に
----- Sample 2 -----
文字 が とりとめ も ない ことば で 君 は どんな 気持ち で 書い た の ポスト まで かけ 出し た の 早く 着き ます 様 に って 初め みたい に 祈っ た の 季節 が かわれ ば 風邪 も なおる よ そしたら 最初 に 返事 を 書く よ くり え しきみ は 早起き し た の が さも 得意 そう に ねぼけ まなこ の 僕 を 朝食 に 追いたて ねェ また 巨人 が 負け た って ばろ 初め から そんな 二 人 じゃ なかっ た 余り 互いに 気づかい 過ぎ た の だろ う か 砂 の 上 に 二 人 で 描い た 悲しい 生活 雨 が 降る たび 風 が
----- Sample 3 -----
文A 何 度 か ら 君 の う 初め から やんな 君 を 出 ます かけ も 頃 みた いつ で も つまり の し な ず に と 届い た 手紙 ありがとう を 三 回 だけ の とても 短い 手紙 何 度 も くり返し 読み返し 乍 ら あなた の 懐しい 文字 指 で たどる 封 を 切っ た なら こぼれる 日射し あなた は 相 変ら ず 手紙 が かい ら 君 の の 出番 いいう の よう で 君 の あんま で 君 は ませ ん な 二 人 じゃ なかっ た 余り 互いに 気づかい 過ぎ た の だろ う か 砂 の 上 に 二 人 で 描い た 悲しい 生活 雨 が 降る
Sample 2 の
おばあさんの掃除前に
さりは南車と言ってた
フリージアの花束抱いて
とびだして来た
といったあたりは、若干もとの歌の歌詞のままの部分もありますが、RNN が自動的に生成したフレーズも多く、最初の状態から考えると飛躍的に自然な感じに収束しているようにみえます。
収束に非常に時間がかかるのは難点ですが、計算資源をフル活用すること (GPU を使う、クラスターを構築する等)で処理速度は向上出来ると思います。
まとめ
ということで、専門的に研究されている諸氏には申し訳ないくらい上っ面だけで終わらせてしまいましたが、昨今は deeplearning 系のライブラリが大変充実してきており、手軽に私みたいな人間でも試せるのは良い時代だなと思います。
今回はとっかかりが Java だったのでその流れで世間的にはマイナー(?)な deeplearning4j を用いてみましたが、世間的には TensorFlow や chainer など python から用いられるライブラリが注目を集めており、コミュニティも活発なようです。
以上、私のアドベントカレンダーは概ねこれで終了です。ありがとうございました。