開発日誌【あられちゃんプロジェクト】その1
トップへ戻る
開発日誌インデックスへ
前のページへ
次のページへ
あけましておめでとうございます。
今年は今まで以上にロボット関連の開発に勤しみたいと思っております。
去年の末ごろに、フェイスブックにて以前の様に開発日誌をアップしていきたいと宣言しましたが、さすがに以前の様にマジで「日誌」を上げるのは大変だってことである程度まとまった情報をアップして行こうと考えています。
このセクションは知能系をまとめてアップしていきたいと思います。
ラムダロボット研究所では自律ロボットの開発を主に目的としています。
自律ロボットの研究分野には環境認知とか物体認識とか音声認識とか行動計画とか色々な要素技術がありますが、結局は目的、目標が与えられてそのサブジェクトをどのように達成するかを解くという事に終始しています。
もちろん、「人に役立つ」とか「仕事をする」ためには大事な事なんだけど、作りたいロボットのイメージが「コロ助」なので、ロボットがどのように目的や目標を見いだすのかは重要なファクターです。いや、ファクターというかコアです。
なんとかこの課題に何らかの解を見いだしてロボットの頭脳を構築できないものかと長年もやもやしておりました。
年も押し迫ったある日、ツイッターから流れてきた「ディープラーニングを使って自分を模した人工知能を作って会話してみた」これに目が留まり、読んでみたんですが、これが面白い。
自分の発言をオートエンコーダに食わせて、自分の発言を入力することで自分が発言しそうなセリフを発する人工無能みたいなもんです。
人工無能がどういうロジックで動いているのか知りませんが、これを使えばなんていうか人格の表層みたいなのが形成できるんじゃないかなと思いました。
ソースはGitHubに公開されているんですが、ドキュメントなしなのでソースを読んで使い方を構築するしかありません。
これはその使い方を構築した記録です。
これを読めばあなたも自分を模した人工知能と会話できます!
ディープラーニングで大変なのはトレーニング用のデータを用意してラベル付けするところなんですが、今回のこれはオートエンコーダなので教師信号が要りません。
オートエンコーダってのは入力したものと同じ出力が出る様にトレーニングされたニューラルネットです。ですので入力そのものが教師信号になります。
ディープラーニングで文章や時系列データを扱う時はRNNという再帰的ネットワークを使います。画像などの時間軸が無いものはCNN(畳み込みニューラルネットワーク)をつかいます。
今回はRNNの改良版ネットワークであるLSTMを使っています。
入力文はdoc2vecというライブラリで100次元ベクトル化したものと、入力文を1文字ずつhot-one表現にしたデータ列にして入力します。
これを入力文の文字数分再帰的に呼び出すことで入力します。
出力は入力データがニューラルネットを通って生成される文字を順に吐き出します。
doc2vecってのがすごくって十分なデータ量で作ったdoc2vecを使うと語の近似度や語の想起(ってのかな?)が出来たりします。
これによって語や文を多次元ベクトル化し、多次元空間での語や文の近似度がわかります。
これを使う事で入力文の主旨というか趣をベクトル化して入力することができます。
hot-one表現ってのは表現したい事物それぞれに1ビットを与えて、表現する方法です。
例えば、
bit11 ネズミ
bit10 牛
bit9 虎
bit8 ウサギ
bit7 辰
bit6 ヘビ
bit5 馬
bit4 羊
bit3 猿
bit2 鶏
bit1 犬
bit0 猪
としたとき
猪は 000000000001
ヘビは 000001000000
という風に表現します。
これを使って入力することで文字の並びそのものが入力されます。
意味を持った単語は入力せずにdoc2vecで趣化し、「自分を模した」という部分の極表層部分である文字面を文字の並びでデータ化している。って感じかと思います。
以上の基礎知識の上で進めて行きます。
まずは入力となる文を相当数集めます。この記事では自分のSNSでの発言を10万文用意したようです。
作者はマストドンのユーザーなのかな?普段の活動はマストドンを使ってるのかな?確認していないけど、そうらしいです。
pre_process.py
用意した自分の発言文を整形します。入力に使えないもの、例えばURLとか記号とかを削除してオートエンコーダに与える文に直します。
また、入力文全体の統計を取り、頻出文字を指定数選出し、出力します。
python pre_process.py
--mastodon_outbox input_text.json //整形前の入力文 [入力]
--num_chars 1024 //頻出文字数 [指定パラメータ]
--output_dataset_text output_txt.json //整形後の入力文 [出力]
--output_dataset_char output_char.json //頻出文字列 [出力]
入力文全体を累積統計し、出現回数が多い方から指定した頻出文字だけを抜き出したのが「頻出文字列」です。
そして入力文のうち、頻出文字列にある文字だけで構成されたものだけを出力したものが「整形後の入力文」です。
こうして、入力文をhot-one表現できるようにします。頻出文字数は大きくすれば入力文をすべて拾えますが、ネットが大きくなってしまうので適切な文字数を探して指定する必要があります。作者は10万文で2048文字を採用したらしいです。
私はマストドンやってないので、別途用意したサンプル文ファイルで目的の出力が得られるように適当にプログラムを修正して実行しました。
こんな出力が得られればOKです。json形式です
{"str": "どこの構造が効いてるのか教えてほしいです"}
{"str": "送料無料にするお助けアイテム"}
{"str": "不具合とかもあるかもしれないものね"}
{"str": "部屋が暑くなってきた"}
{"str": "ちょっとのぼせ気味だわ"}
{"str": "おはよう"}
{"str": "今日は朝から頭痛がする"}
{"str": "そうなんだ"}
{"str": "私も見よう"}
{"str": "なんでも出てくる工具箱のストックになるね"}
{"str": "リサイクルに回そうかとも思ったんだ"}
{"str": "今月が年度末"}
train.py
ネットを教育します。
python train.py /home/kenji/config.json /home/kenji/result_dir
config.json コンフィグファイル ここにネットの構成とか色々入ってます。
result_dir 結果ファイル保存ディレクトリ コンフィグファイルで指定したタイミングでネット情報を記録します。
コンフィグファイルはアップされていなかったのでプログラムを読んで構築しました。
{
"dataset": {
"char_path": "/home/kenji/output_char_kahoko1024.json",
"text_path": "/home/kenji/output_text_kahoko1024.json",
"doc2vec_model_path": "/home/kenji/Downloads/doc2vec.model",
"seed": 6,
"num_test": 50
},
"network": {
"n_layers": 2,
"in_size": 1124,
"hidden_size": 1024,
"out_size": 1025,
"dropout": 0.2
},
"train": {
"batchsize": 128,
"gpu": 0,
"log_iteration": 100,
"snapshot_iteration": 100,
"stop_iteration": 100000,
"optimizer": {"name": "sgd", "lr": 2.0},
"optimizer_gradient_clipping": 20.0,
"linear_shift": {"attr": "alpha", "value_range":[0.001, 0.0001], "time_range": [20000, 30000]}
},
"project": {
"name": "kahoko",
"tags": "lambda_ai_model_1"
}
}
ポイントを説明します。
network
in_size 入力層のノード数は、入力文字列をdoc2vecでベクトル表現としたもの+入力文字列を一文字ずつ順にhot-oneベクトル化したものを入力とするので、
100+頻出文字数
となります。頻出文字数が1000なら100+1000で1100
out_size 出力層からはhotoneベクトル化された文字が出力されます。ノード数は頻出文字数+1 プラス1しているのはEOLマークです。
ですので頻出文字数が1000なら1000+1で1001です。
hidden_size 中間層はよくわかりません。作者は2層256チャンネルのLSTMを使ってみたと言っているので256でもいいかな。
色々変更してみたけど10000サンプル程度だと差が見えませんでした。
optimizer オプティマイザーはadamとsgdに対応しているようで、sgdを使ってみてます。
ここらあたりのパラメータがわからなくてなかなか学習が進まなかったのですが、どれくらいのパラメータが普通なのかをネットで見まわして大胆な値を入れたところ学習しだすようになりました。
lr 2.0 というのと、optimizer_gradient_clipping 20.0 という辺りがそれです。
log_iterationに学習状況表示タイミング、snapshot_iterationにネットパラメータの保存タイミング、stop_iterationに最大学習数を入れます。snapshotデータで評価できます。ログを見て良い成績が出ているsnapshotを使ったりできます。
継続学習はサポートされていないように見えるんだけど、snapshotをロードして継続学習するように改造することもできるはずです。 サポートされてたらごめんなさい。
generate.py
構築したネットを使って文章を想起させてみます。
python generate.py
--model_dir /home/kenji/result_dir //trainしたネットモデルがある場所を指定
--model_ite4ration 1000 //どのネットモデルを使うかを指定。無指定なら最も学習回数が大きいものを使う
--model_config /home/kenji/config.json //コンフィグファイルの指定
--char_path /home/kenji/output_char.json //頻出文字ファイルを指定。無指定ならコンフィグファイルを参照
--text_path /home/kenji/output_text.json //テキストファイルを指定。入力文字列に使用する。無指定ならコンフィグファイルを参照
--doc2vec_model_p?? /home/kenji/doc2vec.model //doc2vecモデルの指定
--num_test 50 //テスト数を指定。無指定ならコンフィグファイルを参照
--sampling_maximum //なんだったか忘れたがオプション指定でtrue
--output ~/output/ //テスト結果出力ディレクトリの指定
-gpu 0 //GPU使用を指定。CPUで処理する場合は-1 まぁGPUを使う事が前提だと思います。
コンフィグでnum_testで指定しているサンプルを乱数で出してきて入力し、文章を想起します。
入力通りの出力が得られれば学習できているという事です。
なお、ここまでの理解が合っているかどうかわからないです。
ちなみに私は10000文程度しかサンプルを用意できていないので10万回学習してもまだまだめちゃくちゃなアウトプットが出ています。
まともな回答も出たりするのでまぁ合ってるのかなぁと思ってますが。
作者がデモしている動画では、AIが最後に「はいえ」って返事するんですね。
これって「はい」と「いいえ」の合成語になっちゃってるわけです。
もちろん合成しているわけじゃなくて、「は」の次はシチュエーション含め「い」が発生して、「は」「い」の次はEOLが選ばれるべきだったのが、「いいえ」の要素が出ちゃって「え」が選ばれちゃったという事だと思います。
つまり文法を学習し、品詞データベースから単語を選ぶんじゃなくて文字毎に分解学習して文字毎に生成している為に発生したエラーなんですね。
でもこのエラーが面白い。
この仕組みは文法+単語でも作れるとは思うんですが、オートエンコーダでこれやるとあんまりおもしろい結果にはならないかもしれないです。
絶対に「はいえ」とは言わないですから。
娘が小さいころ、
「太鼓」の事を「たたいこ」
「コタツ」の事を「ふたつ」
って言っててむちゃくちゃかわいかったんですが、これって当時の彼女がシチュエーションとか経験から洞察した結果なんですね。(もちろん想像ですが)これを彷彿させて気に入りました。
ここまでは表層想起なのですが、これを文法解析すれば、(入力文が正しい文であれば)文の意味も分析できます。
もちろん「理解」や「意識」ではないですが、工夫次第では命令を与えて命令内容を実行することや、状況から問題解決を試みる行動を生成する事ができるかもしれないなと夢が広がっています。
次回、フロントエンドの立ち上げとシンセサイザーの処理を行います。
シンセサイザーについては作者は自分の声からデータを起こしていますがそれは情報不足で解析不能なので今のところキャンセルしています。
最終的にはOpenJtalkに引き渡してしゃべらせるつもりですが、まだそこはできていません。