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

にほんごのれんしゅう

日本語として伝えるための訓練を兼ねたテクログ

艦これのセリフ分類をCNNでやる

(2017/2/24追記. いろいろ試したんですが、objective functionをcategorial cross entropyからpoissonに変更し, softmaxの出力をlogを取ることで、急峻なスパイクを抑えることができある程度改善しました )

艦これのセリフ分類をCNNでやる

 幾つかの基礎と、業務で使用できるかどうかの調査した結果、CNNでのテキスト分類が最近評判が良いことがわかった。
 RNNが負けると言われていた分野は分類とか識別の部分で、テキストの生成や連続系ではまだ、RNNが有利であると思う。

 ディープラーニング以前のアルゴリズムは変数の重要度に対しての解釈をする方法が、ある程度ノウハウが蓄積されており、変数の係数や、決定時の出現する変数の頻度でそれっぽく解釈はできたが、ディープラーニングは中身がうかがい知れない事が多い。しかし、性能は高いとされている[1]

 何にせよ、CNNでのテキスト分類は簡単にできるので、情報系が志す人はやっておいた方がいいと思う。

注:また、偶然であるが、RettyさんのCNNによるテキスト分類とネタ的にかぶってしまった。同じchar粒度だし[2]。
twitterのボットとソースコードが見えるのが違う点だと思います)

CNNでのテキスト分類

- 標準的なCNNでのテキスト分類を用いて、艦これのキャラクタのセリフを分類する
- 艦これのキャラクタの発言がそもそもMeCabなどで形態素解析するのに不適な語彙がおおい(ex:はわわ~、ぱんぱかぱーん、造語等)
 なので、形態素解析を必要としない単語粒度のCNNでの分類を行った(twitterで以前行った人の話を聞くと精度はでるらしい)
- ネットワーク図を記す
f:id:catindog:20170223231442p:plain

図1. ネットワーク図

実際に使用したネットワークより簡略化している
1. Embeddingと呼ばれる文字情報もベクトル化を行う
2. 1~6文字を連結した状態で畳み込みを行う
3. Pooling層をとおしてそれぞれの単語の長さの粒度の出力層をConcat(連結)してDense(全結合層)に入力する
4. 今回は、複数のキャラクタがいるため、Softmaxと呼ばれる方法でマルチクラスに対応する
5. objective functionをpoissonにする
6. logsoftmaxがないので、softmaxの出力値のlogを取る

コード

KerasというTensorFlowを再利用する形で利用するディープラーニングフレームワークがあるのだが、短く簡潔にかけるのでChainerとともによく使う。
今回はKerasで実装した。
コードの全体の説明は長くなってしまうので、ネットワークの構成だけ示す。アドホックなところは、色々とネットワークの構成やパラメータを変えて、分類能が高い構成を探すためである(このネットワークで決まりで、もういじらないなら抽象化してコードを短くできるけど、多分これからもネットワークをいじるので)。

def build_model(sequence_length=None, filter_sizes=None, embedding_dim=None, vocabulary_size=None, num_filters=None, drop=None, idx_name=None):
  inputs = Input(shape=(sequence_length,), dtype='int32')
  embedding = Embedding(output_dim=embedding_dim, input_dim=vocabulary_size, input_length=sequence_length)(inputs)
  reshape = Reshape((sequence_length,embedding_dim,1))(embedding)
  conv_0   = Convolution2D(num_filters, filter_sizes[0], embedding_dim, border_mode='valid', init='normal', activation='relu', dim_ordering='tf')(reshape)
  pad_0    = ZeroPadding2D((1,1))(conv_0)
  conv_0_1 = Convolution2D(512, filter_sizes[0], 3, border_mode='valid', init='normal', activation='relu', dim_ordering='tf')(pad_0)
  conv_1 = Convolution2D(num_filters, filter_sizes[1], embedding_dim, border_mode='valid', init='normal', activation='relu', dim_ordering='tf')(reshape)
  pad_1  = ZeroPadding2D((1,1))(conv_1)
  conv_1_1 = Convolution2D(512, filter_sizes[1], 3, border_mode='valid', init='normal', activation='relu', dim_ordering='tf')(pad_1)
  conv_2 = Convolution2D(num_filters, filter_sizes[2], embedding_dim, border_mode='valid', init='normal', activation='relu', dim_ordering='tf')(reshape)
  pad_2  = ZeroPadding2D((1,1))(conv_1)
  conv_2_1 = Convolution2D(512, filter_sizes[2], 3, border_mode='valid', init='normal', activation='relu', dim_ordering='tf')(pad_1)
  conv_3 = Convolution2D(num_filters, filter_sizes[3], embedding_dim, border_mode='valid', init='normal', activation='relu', dim_ordering='tf')(reshape)
  conv_4 = Convolution2D(num_filters, filter_sizes[4], embedding_dim, border_mode='valid', init='normal', activation='relu', dim_ordering='tf')(reshape)
  maxpool_0   = MaxPooling2D(pool_size=(sequence_length - filter_sizes[0] + 1, 1), strides=(1,1), border_mode='valid', dim_ordering='tf')(conv_0)
  maxpool_0_1 = MaxPooling2D(pool_size=(sequence_length - filter_sizes[0] + 1, 1), strides=(1,1), border_mode='valid', dim_ordering='tf')(conv_0_1)
  maxpool_1   = MaxPooling2D(pool_size=(sequence_length - filter_sizes[1] + 1, 1), strides=(1,1), border_mode='valid', dim_ordering='tf')(conv_1)
  maxpool_1_1 = MaxPooling2D(pool_size=(sequence_length - filter_sizes[1] + 0, 1), strides=(1,1), border_mode='valid', dim_ordering='tf')(conv_1_1)
  maxpool_2 = MaxPooling2D(pool_size=(sequence_length - filter_sizes[2] + 1, 1), strides=(1,1), border_mode='valid', dim_ordering='tf')(conv_2)
  maxpool_2_1 = MaxPooling2D(pool_size=(sequence_length - filter_sizes[2]  - 0, 1), strides=(1,1), border_mode='valid', dim_ordering='tf')(conv_2_1)
  maxpool_3 = MaxPooling2D(pool_size=(sequence_length - filter_sizes[3] + -3, 1), strides=(1,1), border_mode='valid', dim_ordering='tf')(conv_2)
  maxpool_4 = MaxPooling2D(pool_size=(sequence_length - filter_sizes[4] + -2, 1), strides=(1,1), border_mode='valid', dim_ordering='tf')(conv_2)
  merged_tensor = merge([maxpool_0, maxpool_0_1, maxpool_1, maxpool_1_1, maxpool_2, maxpool_2_1, maxpool_3, maxpool_4], mode='concat', concat_axis=1)
  flatten = Flatten()(merged_tensor)
  dropout = Dropout(drop)(flatten)
  output = Dense(output_dim=len(idx_name), activation='softmax')(dropout)
  adam = Adam()
  model = Model(input=inputs, output=output)
  model.compile(optimizer=adam, loss='poisson', metrics=['accuracy'])
  return model

全体のコードはgithubにおいてあってそのままダウンロードで使えるようにしておきます。
(Python3とTensorFlowバックエンドのKerasが必要です)

github.com

$ git clone https://github.com/GINK03/keras-cnn-text-classify
$ cd keras-cnn-text-classify
$ python3 model.py --train --all
(ビルトインしているデータ・セットでGTX 108030分ほどかかります)

# 確率を計算します。これはkerasのMC(マルコフ連鎖モンテカルロ法) searchのプラクティスから抜き出した方法で計算しています
# 最大値を100%として可能性を提示していますが、現実世界の確率と解釈は同じではありません
$ echo "大丈夫、きっと僕は君のことを忘れない" | python3 model.py --pred
時雨 100%
日向 75%
レーベレヒト・マース 27%
初月 27%
武蔵 11%
若葉 11%
能代 4%
ハルナ 3%
利根 1%
木曽 1%
$ echo "あのあの司令官さん、どうしたのですか" | python3 model.py --pred
電 100%
春雨 26%
吹雪 22%
沖波 16%
雪風 11%
霰 9%
高波 6%
春風 2%
青葉 1%
菊月 1%
$ $ echo "そんなんじゃだめよー" | python3 model.py --pred
敷波 100%
雷 61%
望月 55%
江風 47%
鈴谷 41%
時津風 26%
島風 15%
瑞鶴 15%
北上 5%
睦月 1%
$ echo "もうちゃんとレディとして扱ってよね" | python3 model.py --pred
暁 100%
熊野 38%
夕張 37%
愛宕 32%
天津風 21%
能代 6%
最上 6%
加古 3%
金剛 1%
瑞鶴 1%
$ echo "フリーダム響だよ" | python3 model.py --pred
響 100%
江風 55%
木曽 23%
加古 18%
敷波 16%
時雨 12%
イ168 8%
イ26 1%
深雪 1%
涼風 1%
$ echo "おっそーい" | python3 model.py --pred
島風 100%
時津風 43%
イ26 32%
U-511(呂500) 31%
瑞鶴 8%
望月 6%
江風 1%
敷波 1%
涼風 1%
初風 1%
$ echo "司令、なにやってんの~、ねぇってばー" | python3 model.py --pred
黒潮 100%
陽炎 67%
時津風 49%
イ26 15%
酒匂 9%
青葉 8%
雷 8%
文月 1%
比叡 1%
敷波 1%

パラメータと精度確認

学習の様子を見ていると、過学習に存外早く陥ってしまうことがわかった。
epoch 100までやったが、epoch 10~20あたりがちょうど良さそうであった。

Train Acc: 97%, Validation Acc: 56%
(56%というのは会話文では、他のアルゴリズムとくらべても悪くないものだと思う)

パラメータ

embedding次元:256
filterサイズ: 1, 2, 3, 4, 5
filter数:512
dropout:0.5
epoch: 10
batch: 30
Optimizer: Adam(学習率等はデフォルト)
objective function: poisson

せっかくできたのでbotにした

昔使ってたアカウントをボットに変更して、リプを送ると、リプの内容が艦むすでいうと誰の発言になるのか、確率(のようなもの)を表現するbotを作った。
サーバに立ち上げておかなきゃいけないものなので、いつまで公開しているかわからないが、遊んでほしい。
コミケの製作者側は、このセリフをこの子に言わせたらどうなんだろう?とかってあると思うけど、そういうときに役に立つかも。
(不定期に止めたりアップデートしたりする予定です)

f:id:catindog:20170223231923p:plain

図2. Deep時雨の様子(めっちゃ不安定です)

@deep_shigureです。よろしくお願いします
twitter.com
(すぐ落ちるので、そのときはほんとすみません。だれか管理しませんか)

一人あたりの発言が少なく、データセットの量が足りずに、結構苦労しています。だれかpull requestとかforkとかしてよりすごくしてくれたら嬉しいです。(メールは気付かないことがあるけど、twitterはよくみているので知らせてください)

参考

[1]


[2]
speakerdeck.com

YoRHa No.2 Type Bを作りたかった

はじめに

 ドーモ。国家深層学習術師デス。(多数のミームが重なり合い、汚染が観測できるはずです)

 この前、Domain Transfer Network(DTN)というものがFacebook機械学習関連の研究所から発表され、直接的な対応関係がなくても、対応したドメインに変化させることができるということが実証されました。

 実を言うと私には、いくつか夢があり、一つ結婚すること、二つJKになること、三つ目は生殖以外の方法で知能を錬金することです。

 物質的な改造によるJK化は無理だと諦めているのですが、DTNで自分のビジュアルをJKに変換し、AR/VRに投影することで実現可能なのではって考えています。AR/VRに投影することでは物質として本質が変化することはありませんが、認知レベルでは詐称可能です。クオリア(その人の認識の非常に深いところ)を攻略するにはAR/VRで十分であると感じています。

 JKになるべくタイに行かなくてよかった。オレはディープラーニングでJKになってやる。

 DTNもしばらくいじっていたんですが、うまく収束させるのが難しく、また今度ゆっくりやっていきたいと思います。
 
 蛇足がひどかったですが、三つ目の夢である、人工生命の生成に関して、Facebookが非常に興味深い研究を行っています。文脈を考慮できるチャットボットのような存在を研究しています。

意識の在り処

Wikipedia中国語の部屋を読んでもらえばわかるかと思うのですが、何らか同じように感じ、同じような感覚質を所有し、自分が考える心が宿っているかなどは、プロトコルが成立して、会話などが成り立っていればあたかも心を持った存在であると、詐称し続けることが可能になります[1]

f:id:catindog:20170221124151j:plain

図1.結構真理に近い人

これは哲学的ゾンビの問題にも波及する大いなる命題です。あなたの好きなあの子は、本当に自分と同じく思考し、感じる人間ですか?あなたの脳が見続けている夢ではありませんか?[2]

 哲学的ゾンビか、真に意識を持った存在かは、観測側から証明不能なので、意識をもったような存在の生成は機械で代替可能であると考えています。

 Googleの研究所ではSeq2Seqを用いた応答ボットは、チャットでのヘルプデスクの会話ログを学習させたことで最も人間らしいと判断されたのでした。そこで課題とされたのは、文脈に応じた会話ができない問題でした。以下は例ですが、文脈が簡単に破綻してしまいます。

質問> あなたの職業はなんですか?
機械> 医者です
質問> さぞ儲けていらっしゃるのでしょうね
機械> わたしは投資機関で働いていますが、投機に失敗してしまい赤字です

 医者と言ったのに直後の文脈で理論的な破綻が起きています。このようなことがSeq2Seqでは簡単に起きやすいです。
AttentionやConditional Vector(出力の方向性を指示するベクトル)を入れることで解決できることでもあります。(要は前の文脈を内在した表現を入力に受け付けることになるから)
 
 このように機械学習で生成したモデルは、人間から見るといささか不自然に見え、意識が宿っていないように見えます。

 ですが、この文脈に応じた会話ができないという、言語を理解していないように見える側面を、攻略しようとしているのがFacebookの研究の本質的なところなのかなと感じています。

bAbiデータ・セットについて

 Facebookが作成した会話を記したダイアログで、bAbi(ベビーと多分発音する)という名が記すとおり、知能などをテストする指標としてなるべく作成されました。

 既存のLSTMなどでも比較的うまく動くものも多いですが、既存の手法では、うまくいかないものを、どのようなネットワークしたら意味を取れるような動作をするのかという点を競い合っています。

 Facebookが公開したネットワークはMemNNとよばれ、文脈を記憶するような作用があります。文脈を覚えることで、もっともらしい箇所を参照して質問に対して適切な解を導いたりします。

 データ・セットには様々な指標が入っており、以下のような構成になっています。

1	Basic factoid QA with single supporting fact	WhereIsActor
2	Factoid QA with two supporting facts	WhereIsObject
3	Factoid QA with three supporting facts	WhereWasObject
4	Two argument relations: subject vs. object	IsDir
5	Three argument relations	WhoWhatGave
6	Yes/No questions	IsActorThere
7	Counting	Counting
8	Lists/Sets	Listing
...

 1のサンプルについて具体例を記しておくと、このようなものになります。

INPUT: メアリーはトイレに移動しました。
INPUT: ヨハネは廊下に行った。
Query: メアリーはどこですか? 
Anser: バスルーム
(これは非常に短い応答だが、実際にはもっと複雑)

 文脈に応じた回答が求められるデータセットでは単純にSeq2Seqのような前の特徴量との対応関係を自動で構築する以上のことを求められていることがわかるかと思います。
 
 英語の限定されたモデルでやっても面白くないと思ったので、邦訳した簡単なデータセットを下部のgithubのURLの場所においておきます。(別途パース用のスクリプトを動作させる必要があります)
 

MemNNについて

MemNNはFacebookの研究所で考案されたネットワークで以下の4つの諸要素から構成されています。

I: (input feature map) convert incoming data to the internal feature representation. 
G: (generalization) update memories given new input.
O: produce new output (in feature representation space) given the memories.
R: (response) convert output O into a response seen by the outside world. 

IGORとは、映画フランケンシュタインの教授の助手であり、このあからさまな名称はやはり人工生命の文脈が背後にあること意図しているものであると思われます。

説明資料の図をそのまま記す。
f:id:catindog:20170221124801p:plain

図2
実際にはこのような読み書きするモデルではなくて、何回かモデルを再帰的に合成・変換することで目的の機能を達成している(ように見えます)。
 Matching functionというものがあって、質問と状況説明ペアのベクトルを合成して、参照できるようにしてあります。
 答えが直接的な表現でないこともあり、2nd hopと呼ばれる二回目のMatching functionもあります

Match(Q:Where is the footbal?, A:John picked up the football)
2nd hop-> Match( [Q:Where is the footbal?, A:John picked up the football], A:John is in the playground)
ネットワークの説明

 この図が分かりやすかった。
f:id:catindog:20170221124731p:plain

図3
 Episodic Memoryとは、InputとQueryを合成させたもので、更にもう一回、Episodic MemoryとQueryを合成してAnswerを作り出します。

 下記の図のように実装ているのですが、図や文章だけでは私もよくわからなかったので、コードと合わせて参照することをおすすめします。
f:id:catindog:20170221125352p:plain

図4.モデルの図解

Kerasでのモデル
    # この入力は状況説明文
    input_encoder_m = Sequential()
    input_encoder_m.add(Embedding(input_dim=vocab_size, output_dim=64, input_length=story_maxlen))
    input_encoder_m.add(Dropout(0.3))
    # この入力は質問文 
    question_encoder = Sequential()
    question_encoder.add(Embedding(input_dim=vocab_size, output_dim=64, input_length=query_maxlen))
    question_encoder.add(Dropout(0.3))
    # 積で合成
    match = Sequential()
    match.add(Merge([input_encoder_m, question_encoder], mode='dot', dot_axes=[2, 2]))
    match.add(Activation('softmax'))
    # この入力は状況説明文
    input_encoder_c = Sequential()
    input_encoder_c.add(Embedding(input_dim=vocab_size, output_dim=query_maxlen, input_length=story_maxlen))
    input_encoder_c.add(Dropout(0.3))
 
   response = Sequential()
    response.add(Merge([match, input_encoder_c], mode='sum'))
 
    response.add(Permute((2, 1)))  # output: (samples, query_maxlen, story_maxlen)
    # Σで合成
    answer = Sequential()
    answer.add(Merge([response, question_encoder], mode='concat', concat_axis=-1))
    answer.add(LSTM(32))
    answer.add(Dropout(0.3))
    answer.add(Dense(vocab_size))
    answer.add(Activation('softmax'))

    answer.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

具体例

 Kerasのsampleコードにもあるのですが、何故か学習のスクリプトだけ用意してあって、どのように実行するのかプログラムが書いてありませんが、自分で実装するのもそう難しいことではありませんでした。
 一応、githubでcloneすればそのまま使える感じにしておきます。(Python3とKerasが必要です)

$ git clone https://github.com/GINK03/keras-babi-memnn.git
$ cd keras-babi-memnn
データセットをシリアライズしてMemNNが読める形に変換します
$ python3 kizunekotom.py

またネットワーク構成をみると、まだユニット数を増やしたり、いろいろな条件を付け加える余地がある感じでしたので、任意の新規提案が色々載せられる段階であると思います。

学習タスク(デフォルトepoch200、ネットワークはModel.pyを参照のこと)
$ python3 babi_memnn.py --train
Using TensorFlow backend.
Vocab size: 45 unique words
Story max length: 86 words
Query max length: 7 words
Number of training stories: 9995
Number of test stories: 993
-
(...だいたい30分くらいで200epochの学習完了)
ボキャブラリ数40くらいで、Validation精度が93%程度(英語では95%超えてたけど、翻訳が怪しいのでしょうがない)
予想タスク
$ python3 babi_memnn.py --pred
サンドラは庭に行った。ジョンは台所に行きました。メアリーはトイレに行った。メアリーは寝室に行った。メアリーはオフィスに旅をした。ダニエルはトイレに行った。
Q:メアリーはどこですか?
ANS:オフィス

github

Python3とgithubがインストールされていれば、git cloneして、下記のコマンドを叩くだけで使用可能だと思われます。
https://github.com/GINK03/keras-babi-memnn

$ python3 babi_memnn.py --train

bAbiデータ・セットの次にあるもの

 チャットボットを超えた何らかの意識を有しているように見えるネットワークがくる、と思います。ただMemNNも相当アドホックなものな感じなので、しばらく何がベストなのか探索的な研究は続くと思います。
 短い期間でえらい勢いでスコアが伸びてるので、期待して良さそうですね[5]

おまけ:「機械学習」と「AI・人工知能」というワードの意味は異なる?

 ちょっとだけ言っておこうと思うことがありまして、Google検索でニュース記事のライターの方とか、専門誌のライターの方が検索して引っかかってくれることを少々期待しています。
 「機械学習」と「AI」と「人工知能」の使い分けはどうされていますか?私が所属している機関では、営業向けの人はAIということが多く、現場の人は機械学習ということが多いです。
 機械学習は非常に工学と科学の双方を用いて、理論や仮説にもとづいてモデルを構築し、エンピリカルに成果を出すものなのかなって思います。それで、エンジニアは偉い人にこれこれこういうことをやっていると説明するんですが、偉い人の知識体系には、私がやっている体験の実感がないためか、AIと翻訳することがあってズレを感じる次第です。
 
 理解が違えば、用法も違うので意味が異なってきてしまいます。単純な言い換えにはならないんじゃないかと思っていたのですが、定量的にこれで行けるんじゃないかと思った方法があるので、ご紹介します。

1.意味が近しいや同じってどういうことか → 意味をベクトル空間に射影することができれば、近しいベクトル同士が意味が似ているといえる
2.ベクトル空間に射影するには → fastTextやword2vecなどのword enbeddingが意味が近いものが同じようなベクトルになることが幾つかの論文で示されている
3.しかし、人間には200次元ものベクトルの近さをイメージできない → PCAなどの次元圧縮方法で可視レベルに落とし込める

 こんな仮説(プロセス?)が成立できました。
 Newsサイトの記事2月1~15日までの記事を集めると500MByteを超えるデータになりました。テキストとしては十分大きいです。
 fastTextを今回用いembeddingしました。sub word分割が強力に働きすぎるということだったので、最小のcharのngramを5文字と限定するチューニングのみ行いました。
 sklearnのPCAで2dimに圧縮して、プロットするとこのような図が得られました。
 
 右上の方の「機械学習」と「AI」と「人工知能」を確認してください。「AI」と「人工知能」は非常に近いので言い換え可能そうですが、「機械学習」はちょっと距離がありますよね。単純に機械学習の言い換えとしてのAIはきついんじゃないですかね。
 こういうことがあるということだけ知ってもらいたいと思いました。
f:id:catindog:20170221141442p:plain

ウィキペディア日本語版 tf-idfのidf辞書の公開

nora(野良)-idf-dic

モチベーション

ダウンロードリンク

  • Dropboxにアップロードしております。

www.dropbox.com

フォーマット

  • idfはjsonのdict型(ハッシュマップとも言います)です。
idf = { term1: weight1, term2:weight2, ... }

このようなフォーマットになっており、単語とidfの重みがペアになって格納されています。

式の説明

  • tf-idfはヒューリスティックなものなので、そもそも明確なんてないんですが、一番、わたしとわたしの周りが使う式を示します。
  • 発展的な知識が必要ならば、英語版Wikipediaを見て更にどういうことなのか理解してみるとよいです。

f:id:catindog:20170215222337p:plain

  • あんまり全体に渡って出現しない単語は、重要だろうという仮説に基づいています。そしてそれは、往々にしてうまくいきます。
  • tfはドキュメントdに於けるtの発生頻度です。

以下は実際にmecab等をインストールして、ゼロからidf辞書を作成する例なので、idf辞書を利用するのみなら参照する必要はありません。

プロジェクトの取得取得と、周辺ソフトウェアウェアのインストール

LevelDB(kvs)のインストール
(Ubuntu 16.04以上を想定しています)

$ git clone https://github.com/google/leveldb.git
$ cd leveldb
$ make 
$ cd include
$ sudo cp -r leveldb
$ sudo cp -r leveldb/ /usr/local/include/
$ cd ..
$ cd out-shared
$ sudo cp lib* /usr/local/lib/
$ sudo ldconfig
$ cd ~

mecabのインストール

$ sudo apt install mecab libmecab-dev mecab-ipadic
$ sudo apt install mecab-ipadic-utf8

mecab-python3, plyvelのインストール

$ git clone https://github.com/GINK03/tiny-japanese-wikipedia-tfidf-dic-generator
$ sudo pip3 install mecab-python3
$ sudo pip3 install plyvel

NeoLogdのインストール、及び辞書の書き換え

$ cd ~
$ git clone https://github.com/neologd/mecab-ipadic-neologd.git
$ cd mecab-ipadic-neologd/
$ ./bin/install-mecab-ipadic-neologd
[install-mecab-ipadic-NEologd] : Do you want to install mecab-ipadic-NEologd? Type yes or no.
>yes
$ sudo vi /etc/mecabrc
()dicdir = /var/lib/mecab/dic/debian -> (変更後)dicdir = /usr/lib/mecab/dic/mecab-ipadic-neologd

Neologdのテスト

$ echo "Fate/Grand Order" | mecab
Fate/Grand Order        名詞,固有名詞,一般,*,*,*,Fate/Grand Order,フェイトグランドオーダー,フェイトグランドオー ダー
EOS

動作確認

$ cd ~
$ cd tiny-japanese-wikipedia-tfidf-dic-generator
$ python3 nora-idf-dic.py
(何も表示されなけばOK)

Wikipediaのダンプ情報の取得

Wikipediaのスナップショットと呼ばれる情報を取得し、展開します。

$ wget https://dumps.wikimedia.org/jawiki/20170201/jawiki-20170201-pages-articles-multistream.xml.bz2
$ bunzip2 jawiki-20170201-pages-articles-multistream.xml.bz2

idf辞書を構築します。

$ python3 nora-idf-dic.py --wakati
(...60分ほど待ちます)
$ ls 
title_context.ldb(このディレクトリがあればOK)
$ python3 nora-idf-dic.py --build
(...3分ほど待ちます)
$ ls words_idf.json
words_idf.jsonls

tf-idfでベクトル化する

具体例を記しておきます。

$ echo "あなた狩りごっこがあまり好きじゃないけものなんだね"  | python3 nora-idf-dic.py --check
{'': 4.926646596986834, 'ない': 2.042401886218362, '': 2.8119346405476735, '': 1.2142350698667934, 'じゃ': 6.054326132384362, 'あなた': 5.476151075317936, 'ごっこ': 8.627077870130083, '': 3.364157726200682, '狩り': 7.11635016692977, '好き': 4.97306829447642, 'けもの': 9.584680272531994, 'あまり': 5.093448481495583, '': 1.6713533531785785}

keyを数値としてindexを振っていけば、libsvmやXGBoostやLightGBMで入力可能なフォーマットになります。
別にこのスクリプト経由で読み出すのではなく、jsonファイルだけ読み込んで、好きなように使っていただいて構いません。

コード

  • Wikipediaは巨大なコーパスなのでまともにはオンメモリでは処理できません。メモリに収まらない操作をコツコツとkvsを使いながらなんとかするという感じです。
  • githubにあげておきました。

github.com

ライセンス・その他

  • Text of Creative Commons Attribution-ShareAlike 3.0 Unported Licenseというライセンスに準拠
  • Wikipediaを参照してください

Wikipedia:クリエイティブ・コモンズ 表示-継承 3.0 非移植 - Wikipedia

  • Wikipediaのデータは2017/02/01時点のスナップショットです
  • 形態素解析エンジンにMeCab + NeoLogd(2016/12時点)を利用しました

StackGANによるフォントの錬金術

StackGANによるフォントの錬金術

f:id:catindog:20170205152727p:plain

図1. 中央が錬金したフォント

近況

f:id:catindog:20170205152954j:plain

図2. 真理の一撃を放とうとするカリオストロさん(公式絵より)
 グランブルーファンタジーというスマホのゲームでカリオストロというキャラクターがいます。もとは天才錬金術師で、自己の性別を錬金術で男から女に変えた天才です。そんな彼(彼女?)が真理の一撃だー!っていうんですよ、かっこよいですね。
 あまりにも今までの不可能を打開し続けるDeep Learning。自分で言うと甚だ科学としての機械学習が零落しそうですが、まだDeep Learningは体系化された知恵の集合体としての正しい科学の段階にはたどり着いていないように思います。どちらかと言うと錬金術に近い技かも。
 Deep Learningは、いつかまだ見ぬ真理へと人類を導いてくれるんでしょうか。各人、期待しております。

モチベーション

  • 日本語のフォントを作成する際に、デザイナは膨大な時間的・肉体的・精神的な労力を払うことになる[1]
  • フォントのデザインを見ているとある一定の法則があることがわかる。それは、大まかなフォントのデザインの土台があってデザインを装飾するように、なんらかの固有な表現が付加されている
  • これはディープラーニングで情報を付加する特徴を覚えさせ、変換させるのに適した好例である
  • 単純な情報付加では、pix2pixでも可能であろうが、StackGANと呼ばれるGANを複数回重ねることでより高度に近づけたい画像に近づける手法を適応する[2]

先行研究

StackGANについて

 StackGANは少ない情報からよりもとの絵に近い情報を復号するのに適しているネットワークである。
 もとの論文ではSkip Thought Vectorsを用いて文章からの画像の生成であるが、Skip Thought Vectorsの代わりに、画像のベクトルを入力する。条件を同じようにするため、DCGANで用いられるノイズも付与する。
 ネットワーク構成図を書き直した。
f:id:catindog:20170205153835p:plain

図3. 今回用いたネットワーク

実験手法

  • 論文を読んでChainerで実装した
  • 何度か実験したところ、二段目のGANにて勾配消失を起こしてしまうのか、一段目のGANの出力結果に強く引っ張られてしまったので、学習レートを大幅に引き下げるという処理を入れた。本当は一定のiterationが回るまで、学習を遅延させたいのだが、今のところ実装に至っていない。
  • フォントを2種類要した。
  • 書道のようなフォントである青柳隷書フォント、windowsで標準でインストールされているHGP創英角ポップ体の2つを検証対象とした
  • フォントの選択条件は、夏目漱石の「坊っちゃん」、「こゝろ」、「吾輩は猫である」で使用されている3313語を用いた。そのうち、800語を別途検証用データとして学習用データとは区別した。[3]
  • 各種パラメータは、一回目のGANはFacadeと同様で、二階目のGANはそれを1/5にしたものである。詳細なパラメータサーチは別途、本格的な生成タスクで行うことにした。

f:id:catindog:20170205154127p:plain

図4. 青柳隷書フォント、人手で書いたデータをスキャンしたらしい


f:id:catindog:20170205154228p:plain

図5. Windowsに入っているあのダサいフォント

結果

  • Inputが入力ベクトル作成に用いた画像
  • Predictが出力結果
  • Ground Truthが人間の職人が作成したフォントである

f:id:catindog:20170205154409p:plain

図6. 青柳隷書フォント学習による出力結果


f:id:catindog:20170205154501p:plain

図7. HGP創英角ポップフォント学習による出力結果

考察

  • Ground Truthに情報量的に近い習字の字ではないけれど、習字っぽい字が表現可能なのはGAN故にだろう。入出力の誤差の最小化が目的でないので、人間が書いた文字は潜在的に大きなノイズや誤差を含む。その点を吸収してくれているように思う。
  • もともとは同人フォント作家の作成コストを下げられるような仕組みがあればと思って、コーディングを開始したものである。今現在、開発が止まってしまっている東亜重工フォントの足りてないフォントの穴埋めや、人間がフォントを作成する際の下書きに用いられたら良いと思う。[3]

Future Work

  • StackGANのパラメータを調整して、下書きから漫画を一気に作成するなどしたい(データない)
  • 音声の問題にも適応できる気がしてて、周波数成分をルールにしたがって上げたり下げたりする作業を、GANにやらせればより人間っぽくなるっしょって思惑があって、男性でも女性のキャラクタを演じたり、その逆ができるように感じる。
  • パラメータサーチや、ネットワーク構造の問題だけでなく、今回は周りの学習の安定度に応じて学習を開始してほしいというニーズが発生したので、その辺を実装していきたい
  • 一応ConditionalStackGANに簡単にする方法があって、その方法を図示すると、こんなふうになる(と思う)

f:id:catindog:20170205155408p:plain

図8. Conditional StackGAN

参考文献

[1] 文字数14000字! 手書き毛筆フォントができるまで http://portal.nifty.com/kiji/151118195086_1.htm
[2] StackGAN: Text to Photo-realistic Image Synthesis
with Stacked Generative Adversarial Networks https://arxiv.org/pdf/1612.03242v1.pdf
[3] 「シドニアの騎士」に出てくるフォントを再現する「東亜重工電子書体化計画」が公開中止に http://gigazine.net/news/20140702-toa-heavy-industries-font/

謝辞

 インフルエンザか風邪だかわからないが熱が下がらない。隣の席の上司がインフルで休んでいるので、多分インフルなんだろう。つらい。
 そんな中、今週は業務では主にAWSとAnsibleとの戦いに時間を割いていたのだが、やはり機械学習やってる方が気が楽...(エンジニアに向いてない疑惑)
進捗なしかとおもいきや、やっぱり音楽とMMDのちからでなんとか乗り切った。MMDでは艦これで山風のモデルが多数のアーティストによって頻繁なアップグレードが行われている。非常にクオリティが高く、尊い
作品中でも、山風は江風と海風と一緒にいじられることが多いのだが、この三人を組み合わせるとヤバい。それぞれの方向性が異なる性格のベクトルが見事に調和していい感じにまとまっている。興味のある人はYouTubeで「山風 MMD」で検索しよう。
MMD職人に感謝である、GPL v3ライセンスとかにしたほうがいいのではと思う。
f:id:catindog:20170205155704p:plain

この三人組すごい好き