にほんごのれんしゅう

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

Pix2pix with Text

Pix2Pixとは

01/06/2017. この記事の生成物に関して、修正点があるのであとで修正します

  • 自動生成系の深層学習の一つ
  • 2つの画像の差を学習して、その差を補う形で画像などを出力する

f:id:catindog:20170106195735p:plain

図1. facadeとよばれるデータで学習した場合

f:id:catindog:20170106195933p:plain

図2. GANのモデルの様子。生成器と、判別機が対立して競い合う

先行研究

初心者がchainerで線画着色してみた。わりとできた。[1]
  • ヒント情報として、色情報を書き足すことで、色を指定している
  • Pic Source: qiita.com

f:id:catindog:20170106200121j:plain

図3. オレンジ色で色のヒントを与えている様子がわかる

Icml2016[2]
  • テキスト情報をSkip Thought Vectorでembeddingすることで、意図した画像を生成する
  • Pic Source: github.com

f:id:catindog:20170106200247j:plain

図4. 左のテキスト情報から右の花の絵を生成している

モチベーション

  • 色指定しなくても、テキスト情報のみでキャラクタの着色を行う
  • 例えば、艦これの響なら髪の色は白で、暁ならば紺色に着色する。モノクロ画像では両キャラクタは非常に似ており、人間が見てもどちらに塗るのが正解か不明瞭
    • テキスト情報でこれは「響」と記されていれば、自動で髪の毛を白く塗ってくれるのでは。

提案手法

  • Pix2Pixの入力に用いられるテンソルは、非常に高次元であり、RGB程度の粒度であれば、たかだか256*256*3程度に過ぎない
  • テキスト情報を格納できる領域を増やし以下のように、テンソルを拡張する
(256, 256, 3) -> (256, 256, 4)

  • 空いた次元に、画像のタグ情報をベクトル化して、組み込む

従来手法

f:id:catindog:20170106200459p:plain

図5. 初音ミクとモノクロ画像だけではなんとも判定できないかも...本当に緑に髪を塗るのが正解?

提案手法2

f:id:catindog:20170106200638p:plain

図6. 初音ミクといえば、髪は緑!わかるよ!

実験環境

  • ChainerのPix2Pixをもとに変更、拡張
  • Epoch: 300
  • BatchSize: 1
  • 学習率: 0.00001

使用したマイパソコンのキャッシュに残っている画2万枚
使用したキャラクタードメイン: 艦これ、東方、FGO、グラブル
使用したテキスト情報: タグ情報
Pic Source: pixv.net

大体 GTX 1080で一週間ぐらいかかった

実験結果

f:id:catindog:20170106200851p:plain

図7. キャラのヒント情報が聞くためか、キャラのイメージカラーを特定しやすい。塗り間違いも少ない

Optional: カラー化とカラー化抑制

f:id:catindog:20170106201033p:plain

図8. モノクロになるタグと言うものが存在する可能性の示唆

  • 明示的に「ノート」や「モノクロ」を意味するベクトルが入ると、カラー処理は行われないようである

Optional: 謎の言語を生成する

f:id:catindog:20170106201224p:plain

図9. 文字のような謎の記号の生成

考察

  • ヒント情報が画像と同じように処理したためかだいぶ拡大して用いたがたまに、潰れている可能性も否定できない。CNNで処理しても生き残るような素性に加工するする必要がありそう
  • 改良点が多く、テキストのベクトル化方法や、更に詳細な画像を出力するようにStack GANの発想で、全体的な方向性を決定するモデル・より詳細に描き上げるモデルなど組み合わせても良いかもしれない。
  • ここから、もとの線画を欠落させると、text2imageと近しいモデルになるかと思われる

Advanced Work

  • Text2Imageを再現してみる
  • 高解像度化する
  • Web経由で任意の単語を切り替えることで、任意の画像の作成を可能にする
  • Skip Thought Vectorなどのより高性能なEmbeddingなどを用いる
  • 省メモリ設計にする:今回の実験だけで、32GByte中、30GByteを学習で消費したので、何らかの変更を行う

チャットボットをディープラーニングで作成

チャットボットをディープラーニングで作成

  • アマゾンプライムビデオを見ていたら、涼宮ハルヒの憂鬱が公開されており、懐かしい気持ちで見ていました。私がアニメとSFにハマるきっかけとなった思い出の作品です。
  • この作品をみてSFを調べだし、汎用AIに興味を持つに至りました。そして、高校生の身分でチャットボットを作ろうとしたのですが、当時はプログラミングスキルも、確率場に関する知識もはなかったので、あきらめていたのでした。
  • 今なら簡単なチャットボットなら組むことができるので組んでみて無念を晴らそうと思います。f:id:catindog:20161211205017p:plain

背景

  • 高校生の時の夢を叶えるべく、DeepLearningによるチャットボットを作る
  • GoogleのSeq2Seqではヘルプデスクの文章を学習させることによって、「人間らしい」チャットボットの作成に成功した
  • 人間らしいチャットボットとは、問題解決のタスクではなく、自然な応答を返してくれるようになること

f:id:catindog:20161211205103p:plain

環境

  • 764unitを3層重ねたLSTM, RNN, DeepLarning
  • Windows 10 x64
  • GTX 1050
  • Anaconda(Python 3.5)
  • Chainer latest
  • 学習時間は2時間弱でした
  • Memory 24GByte
  • CPU Corei7

データ・セット

  • 本当は女の子と話すシチュを想像していたのですが、男の子と女の子の会話のペアが取れそうになかったので、アニメのシナリオを利用することにしました
  • アニメや映画のシナリオではキャラAとキャラBが交互に話すスタイルを取っています。そのため、キャラAとキャラBの対話を一つのデータとして学習させることで、チャットボットを作成します

f:id:catindog:20161211205123p:plain

  • このサイトを参考にしたのですが、データ量が少なすぎで、一般的な応答の特徴の獲得までに至っていないようです。膨大なアニメのシナリオレポジトリ、ないでしょうかね。[1]
  • テキスト情報に換算して1.4MByte程度しかなかったです。
使用したアニメのタイトル一覧
 コードギアス 反逆のルルーシュ
デュラララ!!
東のエデン
エルフェンリート
涼宮ハルヒちゃんの憂鬱 & にょろーん☆ちゅるやさん
けいおん!
らき☆すた
涼宮ハルヒの憂鬱
日常
誰も知らない
まどか☆マギカ
狼と香辛料
侵略!イカ娘
天体戦士サンレッド
東方夢想夏郷
...and more

動作確認

λ python chatbot.py --model cv\latest_anime_640.chainermodel --vocabulary data\anime\vocab .bin
INTPUT なんか違わなイカ!?
OUTPUT あんたはかもでゲソ

ちゃんとイカ娘の文脈になる

λ python chatbot.py --model cv\latest_anime_640.chainermodel --vocabulary data\anime\vocab .bin
INTPUT やあ、長門っち、長門っち!
OUTPUT え? ま、ちょっとな、何考えなの何だよ。まだイカ娘のほうがまだ出るのかもしれない

ハルヒの文脈にならない、データが足らないか

λ python chatbot.py --model cv\latest_anime_640.chainermodel --vocabulary data\anime\vocab .bin
INTPUT 死にます
OUTPUT イカ姉ちゃん、がんばってください

がんばる。

λ python chatbot.py --model cv\latest_anime_640.chainermodel --vocabulary data\anime\vocab .bin
INTPUT 雨降るって言ってました?
OUTPUT ねー! なんてやって……

雨の話題にならないですね。収束率は悪くなかったので、データ量とデータ・セットの特性によるかもしれない。

感想

  • 数ギガに及ぶ対話文があれば、一般的な会話の応答が学習できるのではと思いました。実際は1.4Mしかないです。
  • WindowsでのPythonC++などのプログラミング、めっちゃ消耗しませんか。大変でした...
  • エンドレスエイトの戦犯、未だに許せません...

複数の著者をテキスト情報を混ぜ合わせたRNNと長文の改善手法

問題背景

  • RNNによる単純な小説の学習と、学習結果の出力は過去、多くの事前研究で行われており、ある程度、人が書いたらしいと思われる文章が数多く出力されてきた。[1], [2]
  • しかしながら、複数の著者の小説を混在させて評価した例は少ないか無いように思う。
  • 著者を混ぜ合わせて学習した場合、両方の著者の特性を備えたモデルができるのではないかと仮定した。
  • また副次的知見として、Attentionの情報を引数に取らないRNNは一般的に長文に弱いが、もっとかんたんな方法で長文を生成することに成功したので、その時の情報を記す。

提案手法

  • 複数の著者の文章を混ぜ合わせ、学習用データ・セットとする
  • 長文を出力させるために、前の出力内の特徴となる文字を取り出して、一度、LSTMをリセットし、前の文の末尾の文字列のシーケンスを経過したと言う状態を作り出し、連続しない自然な長文を出力させる

f:id:catindog:20161209141815p:plain

図1. 今回使用した長文生成モデル

実験手法

  • 小説家になろうのサイトより、「無職転生 - 異世界行ったら本気だす -」と、「転生したらスライムだった件」と「蜘蛛ですが、なにか?」を2016/12/09まで公開されている情報をもとに、文字粒度のRNNを学習させる
  • 1. 「無職転生 - 異世界行ったら本気だす -」のみで学習させる
  • 2. 3つの作品を順序を保ったまま、学習させる
  • 3. 3つの作品の各行をランダムにシャッフルさせ、学習させる

評価指標

  • 出力結果が自然な文章であり、特定の誰かの文章でないと評価できる場合にtrue、自然な文章でなく、特定の誰かの文章なのだと確信でき場合にfalseの二値として、分類し、1.2.3で各々100文を評価して、スコアを比べる

結果

  • 1. score 56 @3layer,Unit1024
  • 2. score 1 @3layer,Unit1024
  • 3. score 60 @3layer,Unit1024

 以上のような結果となり、2に関してのみ極端に悪いスコアとなった。主な原因は自然な文章が全く生成されないことに起因している。
f:id:catindog:20161209142124p:plain

図2. 1の出力結果。長文である

f:id:catindog:20161209142155p:plain

図3. 3の出力結果。長文で独自の言い回しが見て取れる

考察

  • 複数の著者の文章を単純に順番道理に連結しただけでは、そもそもtrain-lossが下がらず、まともに学習できていないなどの問題があることがわかった。これは、RNNが学習する文章の特徴量がその作者固有のものであると考えられる。一層あたりのRNNUnit数は1024の3層のモデルであったので、もっと層を増やすなどなどの工夫をすれば、正しく学習できる可能性があるが、計算時間を考慮すると一部現実的でない可能性もある。
  • シャッフルした文章を学習しても、正しい文章は出力されるようであるが、文脈構造が弱いように感じられる。順序情報を失ってしまったたであると考えられる。
  • 自然でユニークな文章であるというスコアに関しては1と3で大きな有意差はなく、同一でない文章を作るだけならば、特定の著者や作品に偏っていてもいいかもしれないが、特異的にユニークである文章を3に関しては作成することができた。例を示すと以下のようになる。
 この世界では、リーダーが火炎球(フルメッド)に制御されていたのだ。
 この世界では、リーダーが火炎球(フルファンジーヴァングアーン)を観察した。
 この世界では、リーダーが火炎球(レンドエンド)に相当するハズだ。
 この世界では、リーダーが火炎球(フルフォン)の天敵となった場所に進化したのだ。
 この世界では、リーダーが火炎纏(チェイン)を用いていた。

なんだかよくわからない中二病ワードを効率よくそれっぽく生成することができるようである。これを複数の作者が入ったため、本来存在しない中二病ワードの生成に成功したと捉えられそうである。

レビューのスコア予想問題

背景

  • 商品やサービスを論じるときに、その文脈から定量的にどの程度良かったのか、悪かったのか知ることは難しい
  • 幸いなことにネットには膨大な商品とサービスのレビュー件数が存在し、サービスごとのドメインが異なってもある程度、定量的に文章から良し悪しを把握することができる
  • ある程度、機械学習アルゴリズムにより正しく分離できるのならば、分離したときの素性の重要度等の情報を用いて、文章が星がつきやすい文章なのか、星がつきにくい文章なのか判断する指標として使うことができる
    • 仮説ですが、星がつきやすい文章はポジティブであり、星がつきにくい文章はネガティブだと考えることができるように直感的には感じます

f:id:catindog:20161205181804p:plain

図1. ベクトル化した図の例

f:id:catindog:20161205181847p:plain

図2. 何らかの機械学習アルゴリズムで分離した場合

  • 青のプロットを、星の数が少ないレビューとして、オレンジの部分がレビューの数が多いとすると、以下のような分離面が構築できます。図示しやすいように2次元ですが、実際には60000次元と、かなり高次元でスパースなデータになっています。

手法

  • 規約でスクレイピング等を得に禁じていないサイトなどで、信用できるレビューをパースします。この時、信用できると判断する条件は、ある程度文章量があること、レビュー表示アルゴリズムで、上位に来ていること、レビューに参考になったなどの付加情報を利用します。
  • レビューを形態素解析して、if-idfでベクトル化する。
  • ベクトル化した情報を、機械学習のbinary classificationで分離し、ある程度の精度が出るか確認します
    • 機械学習で分離した結果を利用して、その重みを分離に用いた重要な因子としてみなすことができます。

データ

  • データの配布は禁止されているので、配布することができません。申し訳ありません。
  • 20161205までに集めた13万件のレビューをもとに、分類を行いました。
  • KVSにURL: {stars: 1~ 5, contents: レビュー内容}のように保存することで、データを管理しています。
  • 星の分布には偏りがあり、星が5が圧倒的に多く、星1, 2, 3は少なく、1, 2, 3は足し合わせても、星5の総数には及びません。
    • そこで、星5と星1,2,3の分離問題として捉えて、分離するようにしました。

f:id:catindog:20161205182523p:plain

図3 星の分布@N11000程度の場合

XGBoostによる分類

  • XGBoostはランダムフォレスト等と系譜を同じくするディシジョンツリーの機械学習アルゴリズムです。非常に高い精度で分離できるため、2015年からKaggle等で用いられるようになっているようです。[1]
    • eta: 0.3
    • gamma: 1.0
    • min_child_weight: 1
    • max_depth: 20
$ wc -l differ20161205-2.svm
90233
$ head -n 75000 differ20161205-2.svm > train.txt
$ tail -n 15000 differ20161205-2.svm > test.txt

$ xgboost t.conf
[16:57:31] 75000x93504 matrix with 8267281 entries loaded from train.txt
[16:57:31] 15000x93498 matrix with 1651122 entries loaded from test.txt
….
[17:00:58] src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 144 extra nodes, 300 pruned nodes, max_depth=20
[17:00:58] [99] test-error:0.048600 train-error:0.002213
[17:00:58] update end, 112.189 sec in all
  • train errorが4.86%であり、十分、実用に耐えられるレベルで分離できているように見える。

liblinear(ロジスティック回帰)による分類

  • - 最もシンプルな分類であると思われます。収束も早く、このロジスティック回帰に対してどの程度の性能が出るかで性能を確認したりすることもあります。
$ liblinear-train -s 0 train.txt
.............................................*.......................................................
optimization finished, #iter = 1000
WARNING: reaching max number of iterations
Using -s 2 may be faster (also see FAQ)
Objective value = -30.960220
nSV = 24990
$ liblinear-predict test.txt train.txt.model result
Accuracy = 94.12% (14118/15000)
  • 94.12%とXGBoostには及ばないけれど、十分に性能が発揮されているように思われます。学習の結果、素性の重要度の解釈が容易なので、ロジスティック回帰の結果のウェイトを確認します。

ロジスティック回帰の重みの結果

  • 単語一つにつき、一次元というパワープレイをしているので、単語数がたくさんないと正しく学習できないのですが、900,000件もあれば、多分大丈夫です。
  • (注)libsvmとliblinearは、0番目のインデックスは読み込むことができないので、スルーしていますが、本来はこのようなアドホックなことはすべきではありません。

f:id:catindog:20161205183500p:plain

図4 ポジティブ、ネガティブの素性

モデルの定性的評価

  • ロジスティック回帰は非常に単純な式で表現できるため、特徴量の重要度が分かれば自分で計算することができます。

f:id:catindog:20161205183539p:plain

図5. アルファ、ベータに特徴量の重要度が入ります

  • プログラムにしてもわずか数行です。Python2ですが、好きなのを使ってください
tsv = filter(lambda x:x != '', open('stash/star_ranking.tsv').read().split('\n'))
trank = {}
for t_w in tsv:
  t, w = t_w.split(' ')
  trank[t] = float(w)
  idf = json.loads(open(filename + '.idf.json').read())
  m = MeCab.Tagger ("-Owakati")
  for line in sys.stdin:
    score = 0.
    line = line.strip()
    for t in m.parse(line).strip().split(' '):
      if idf.get(t.decode('utf-8')) != None and trank.get(t) != None:
        score += idf.get(t.decode('utf-8')) * trank.get(t)
      res = int(1. / (1. + math.pow(math.e, score*-1 ) ) * 100)
      if res < 50.:
        print("だめっぽい文章", end=" ")
      else:
        print("よいっぽい文章", end=" ")
     print("score =", res)

定性評価

  • 自分で作ったデータセットに対して、予想したとおりの性能を発揮するか確認します。
$ echo "風邪を引いて頭がとても痛いので家で寝ていたいめう。" | python Review.py --mode score --file tmp/differ20161205
だめっぽい文章 score = 44
$ echo "冬のボーナスが出たので新年は新しい家族をむかえてハワイに旅行に行く。" | python Scraper.py --mode score --file tmp/differ20161205
よいっぽい文章 score = 55
  • 大丈夫っぽいですね。

結論

  • 日本語の意味しているところを正しく分類しようとすると、次元数が多すぎて分類するの大変で性能がでないのではないかと危惧していた時代もありましたが、意外とかんたんにできるので、応用の範囲は広そうだと感じました。
  • 単語のtfidfしか見ていないので、文章構造は無視しているので、これを取ろうとすると、また違った仕組みが必要になります。個人的にはRNNなどで分類するのがちかいと思います。LSTMなどの仕組みが長文を正しく認識できるのか、いまいち自信がないのですが…
  • これをセンチメント分析の分類器として使えないかどうか調べているのですが、微妙にレビューと使っている単語の分布が異なっており、専用のデータ・セットの必要性を感じています。[2]