コンセプトの管理システムの必要性

今、サリーには、2万語以上のコンセプトが組み込めるようになっている。ただ、もう、絶対使わないだろうなというコンセプトも入っている。それは無駄だ。よく使いそうなコンセプトに入れ替えて、まあ、全体として2万語になればいい。

そのためのシステムなり、データベースが必要だ。潜在的可能性のあるコンセプトを貯めておいて、スコアを与え、スコアの高いものから順番に入れておくというのが望ましい。実際に、wikipediaとか、twitterで使われる頻度、辞書への掲載などの基準でスコア化する。もちろん、ライブでお客さんから出されたお題の場合は、さらにスコアを大きくすべきだろう。

Qichatでヴァーチャルなif文を作る(2)

まとめると、Qichatで、もし 変数 $varが1の時は、Aという処理、0の時はBという処理をするというのは次のように書く。

u:(実行しなさい) わかりました $exec1=1 $exec2=1
u:(e:exec1) $var==1 Aを実行します 
u:(e:exec2) $var==0 Bを実行します 

これだけのことである。もちろん、これ以外にもいろんなやり方はあるだろう。

u:(実行しなさい) わかりました $var==1 Aを実行します 
u:(実行しなさい) わかりました $var==0 Bを実行します 

これでもいいかと思ったのだが、私がやった場合は、動かなかった。このような単純化すれば動くかもしれない。ただ、ぱっと見、「実行しなさい」が二つのイベントルールにマッチするような書き方が違和感がある。

NAOの認識コンセプトの上限

サリーやマリーが、即興漫才や謎かけのお題を取得するのは、音声認識機能を使うが、クラウドを全く使わないので、全てローカルでやっている。googleのクラウドなんかも使えなくはないのだが、テレビのスタジオやライブの舞台にネットが繋がるとは限らない。テレビの収録では、ほぼ期待できない。そういうことがわかっているのに、クラウドでしか音声認識ができないというのは、馬鹿げた戦略である。

ローカルでNAOがお題を認識するためには、qichatのconceptを使う。この夏までは、コンセプトに2千数百個を入れて、お客さんのお題に対応していた。少ない。そこで、システムを変えて、日本語wikipediaや数千万のツイートを使って、自力でネタをその場で生成するシステムに変更し、お題の認識可能性も8000語に増やした。それが、10月の初め。

さらに今日、AI的手法で、ウィキペディアやツイートから取得した関連語20000語を識別させようと入力したら、認識のための音素にコンパイルするのに3分以上かかったが、できたのだ。ちゃんと認識する。

NAOのコンピュータは相当古いが、ネタに必要な自作ライブラリやスマホからコントロールするためのHTML5関係のファイルしか入れていないので、メインメモリにもディスクにも比較的余裕があった、ので可能になったのではないかと思っている。

今、実際のネタ作りは、同じローカルネット内のパソコンにお題を送って作成させている。インターネットにつなぐ必要はなく、サリーとマリーとパソコンがローカルネットに繋がっていれば良いので、閉鎖空間でも対応できる。

何れにしても、今日はNAOが2万度も識別可能性を持っていることにただ驚愕した。下手すれば、メモリのある限り認識するのではないか。上限が推測できない。まだまだいけそうな気はする。

NAOのUSBをストレージとして使う

NAO2台で、即興漫才はできるようになったが、HALと違って、NAOはCPUもストレージもパワーと容量がない。現在は、ノートパソコン(MAC)を持ち込んで、即興のネタ作りはそれにやらせる形になっているが、ロボットを3台扱うような面倒くささがある。

そこで、CPUパワーはどうしようもないが、NAOのストレージを頭にさせるUSBにできないかと思っている。頭にUSBをさせば醜いが、なるべく短くして、リボンでも飾れば、なんとかなるのではないかと思う。

今、NAOを今日のオーディション用にパッキングしてしまっているので、試せないが、
https://medium.com/@heatherkemp_89938/interfacing-nao-robots-with-the-arduino-in-depth-tutorial-284ecba0b0aa
に手続きが書いてある。全体が面白い記事だが、USBマウントのところだけメモしておく。

(1)NAOにrootユーザーとしてログインしておく。
(2)USBをマウントするフォルダを作っておく(フォルダ名は任意)

# mkdir /mnt/usb

(3)ブロックIDを確認する

# blkid /dev/sdb*
/dev/sdb1: UUID=”04D9–7E06" TYPE=”vfat”

(4)フォルダをマウントする

# mount /dev/sdb1 /mnt/usb

以上である。まだ試していない。

NAO、コンセプトの搭載可能数

Pepperは、クラウドで、qichatのワイルドカード認識に対応しているようなのだが、NAOについては、NAOQIバージョンもPepperほどにも更新されておらず、対応していないようだ。それはそれでいい。というのは、Qichatでは、conceptにリストアップしておけば、ロボットはかなり正確に人の言葉を認識できるからだ。だから、conceptにどれだけのワードが載せられるかは、非常に重要な仕様になっている。
まえから、幾つもに切り分けしていれば、500個以上のコンセプトを識別できることはわかっていた。それは、実際使っているものだ。
しかし、一つのconceptのなかに、幾つ詰め込むことができるかは調べたことがなかった。実は、多くのコンセプトに分けるよりも、一つに詰め込むと便利なことがある。とても便利になるのだ。今日試してみてとても驚いた。なんと、一つのコンセプトに、2000個以上のワードを詰め込んでも、識別できたのだ。つまり、トピックファイルの中に、
concept:(words) [今日 明日 ロボット などなど ・・・・・・]
と2000個以上の言葉をwordsというコンセプトで登録しておいても、たとえば
u:(お題は _~words です) なになに $1・・・・・・
というかたちで、言葉の認識、変数 $1 への代入が可能になるのだ。もちろん、ALDialogで、そのトピックファイルを読み込み、コンパイルするのには多少時間がかかる。20秒くらいか。それは、スタート時だけだから、全然大したロスではない。
2000個も入れると、言葉の中に変な文字、つまり、ロボットが発音できない文字が入っていると、コンパイルに失敗してトピックファイルが有効化されない。アンダースコアとか、全角の数字(これがエラーになるのはちょっと不思議だが)とか、「つ」の小さい版「っ」がワードの最後に発音しにくい形で入っていても、コンパイルエラーになった。しかし、数の大きさは平気だったのだ。
NAOがワイルドカードの認識が下手なので、やむ負えず、Googleのcloud APIで変換させたりしていたのだが、音源ファイルで送ろうとも、ストリーミングにしようとも、やはり、人が喋ってからその結果をロボットに返すまでに10秒余のロスが発生して、ネタの120秒などという短い尺には、耐えられない長さになってしまう。conceptにおいて、聞き取られレベ、ほぼリアルタイムで時間のロスがない。
2000個というのは相当のキーワード数だ。実際それ以上が可能かもしれない。それ以上を試す気になれないほど、2000という数字は大きかった。

word2vecでロボットに言葉の演算をやらせた

この間、ずっとはまっていたのは、word2vecを使い、wikipediaに登場する単語を演算可能にすることだった。そこに、ボケの匂いを感じたのだ。word2vecは、ニューラルネットを使って、言葉を数量ベクトル化する手法だ。すると、言葉の演算がベクトル演算に変化できて、面白い結果が出てくる。
http://www.blog.umentu.work/ubuntu-word2vec%E3%81%A7%E6%97%A5%E6%9C%AC%E8%AA%9E%E7%89%88wikipedia%E3%82%92%E8%87%AA%E7%84%B6%E8%A8%80%E8%AA%9E%E5%87%A6%E7%90%86%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/

http://qiita.com/tsuruchan/items/7d3af5c5e9182230db4e
を参考にした。
wikipediaデータをmecabで分かち書きした膨大な単語について、演算が可能になった。たとえば、
ギター+キーボード
という演算をやらせると「セッション」になるとか。面白い。ネタになる。
隠れユニット200個で、学習結果のウェイトマトリクスデータが、900メガバイトを超える。
このデータは、ロボットNAOのストレージには入らない。そこで、NAOのUSBポートからデータをロードしようとしたら、当然のことなのだが、mallocで確保しようとしたヒープのメモリが、確保できない。仕方がないので、このデータを処理するライブラリを、パソコン上において、naoqiのリモートライブラリとすることで解決した。
人工知能など、およそ、その機能をロボットに全部組み込む事は不可能なので、PC上のリモートライブラリにするのはある意味自然なのだが、ロボットを立ち上げ、さらにリモートでライブラリを立ち上げるのは面倒だ。
しかも、言葉の聞き取りは、これも実に面倒なのだが、qichatを使って、トリがを与え、ロボットに喋りかけた言葉をwav音声データにして、パソコン上のサーバーに送って、それをさらにrawデータに変換したりして、Google cloud APIの音声認識システムに送って、テキスト化し、それをまたパソコン上で、形態素分析して、必要なデータを取り出し、またロボットのqichatのスクリプトの中にイベントとして取り組むと言う、質面倒臭いことをやる。それで、10秒くらい使ってしまうのが痛い。
しかし、まあ、これで、結構な人工知能をロボットに組み込んだことになる。

Google Cloud APIのストリーミングを継続して使う

Google Cloud APIのストリーミング音声認識を、ロボットとの継続した対話に使おうとすると、60秒の制限があって、それ以上使えずに、例外が発生してアボートしてしまう。これでは使えない。
そこで、gracefull(優美)に一つの会話を終了させながら、次の会話に入っていくということがどうしても必要になる。その基本的なやり方を以下に記しておく。
(1)ロボットからの音声データのストリーミング入力は、それとして生かしておくという戦略。
(2)Google Cloudからは、送られたーデータの解析結果が中間的に、最終的にの二つのバージョンで送られて来るが、最終バージョンが送られて来たら、それを一旦出力(エコーロボットとしてロボットに喋らせる、あるいは、人間入力の言葉を解析して応答を用意したりして)
(3)これで例外を発生させずに終わるが、これだと一回の聞き取りしかできなくなってしまう。そこで、
requests = request_stream(buffered_audio_data, RATE)
のスレッドの作成から、再度はじめ直す必要がある。そこでここからを関数化して、先の終了後に、再帰的にこの関数を呼び出すようにすればいい。すると、また60秒制限の新しいスレッドが立ち上がる。無限続けるのは良くないので、いったい何度ロボットやり取りするかを事前に与えておいて、その回数の会話をしたら全てを終了させるようにすれば良い。
(4)一つ問題は、(2)で、会話が入れば、一つのスレッドはグレースフルに終わるが、無言の時間が続くと終わるタイミングを逃して例外を発生させてしまう。そこで、60秒以内の一定時間無言が続くと、一旦gracefullに終わらせる。
そこで、次のようなタイマーイベントを、先に分離した関数部分の中に置いておく、
###############
def stopAPI():
print "ループを止めます"
recognize_stream.cancel()
#ここまでが関数、以下でタイマーでこのハンドラーを呼び出す
t=threading.Timer(MAX_SILENT_LENGTH,stopAPI)
t.start()
###############
関数の中にstopAPIという関数を定義して、MAX_SILENT_LENGTHの秒数が経過すると、
recognize_stream.cancel()
というイベントを発生させる。すると、CANCELLという例外が発生して、綺麗に終わるように例外処理手続きが組み込まれているのでうまくいく。そしてこの例外処理が終わった後、さいど、先に作成した関数、request_streamから始める関数をスタートさせれば良い。
これで、指定回数の会話が終わるまで、継続的にGoogle Cloud APIでストリーミング処理を続けることができる。
これで、人工無脳の会話ボットと、ロボットNAO、そしてGoogle Cloud APIの三つをつなぐ準備が80%ほど、整った。

ibotのライブラリにタイマーコマンドを加える

ロボットと何かパフォーマンスをやっているときに、尺が気になる。時間切れで失格ということが実際にあるからだ。そこで、対話システム用のコマンドに、タイマーコマンドを作成した。
トピックファイルの中に、
$wscom_starttimer_1=タイマー秒数
をいれると、それが実行された段階で、指定秒数のタイマーがスタートする。時間が来ると割り込みイベントが発生するので、それを、トピックファイルのイベント関数で、たとえば、
u:(e:TIMEOUT) 時間が来ました
のように、しておけばよい。
途中でタイマーを止める時は、
$wscom_stoptimer_1=1
をトピックファイルに埋め込んでおけば良い。

ロボット植え込みiBotは、ほぼできた

途中、ロボットのウェッブサーバーをapache2にしようかという迷いがあったが、結局、originalのnginxを使った。これからは、本番は、スマホだけで全て処理できる。
こちらに何か支障があれば、今まで通りノートブックのサーバー上のiBotでもコントロールできるので、セキュリティが高まった。
ロボット植え込みiBotの弱点は、パソコンからのファイル転送をftpなどに頼らなければならないところだ。これも、nginx用のfcgiを使えるようになればなんとかなるので、しばらくそれに取り組みたい。
それが終わったら、 クラウドの人工知能機能を利用する段取りに入りたい。

ロボットnaoにapache2ウェッブサーバーをインストール

前に書いた理由で、新たにnaoにapache2をインストールすることにした。手順をメモがわりに書いておく。
(1)apache2の最新版をダウンロードする。ここでは、apache2-2.4.25を使った。
(2)opennaoバーチャルロボットにpsftpなどで転送し、解凍する
(3)解凍したフォルダで、./configure --prefix=ロボット上のインストールしたいフォルダの指定する。私は、rootじゃなく、naoで実行したいので、/home/nao 以下のフォルダを指定した。
(4)普通にmakeとmake installを実行する。エラーは出ない。
(5)インストールしたフォルダごとアーカイブし、naoの実機に転送する。
(6)httpd.confを編集する。私の場合は、portを80から8080に変更。変更しないと、通常のrobotページとバッティングするので。cgiを動かす設定、ドメイン名がコメントアウトされているのをそのまま外す。関係のないドメイン名になっているが、コメントアウトするとスタート時に警告が出るを避けるだけの目的。
(7)ただ、これだけでは、apachectl startとやるとライブラリがらみのエラーが出て、実行できない。そこで以下の操作をやる。
opennaoの/usr/libディレクトリから、次の二つのファイルを取ってくる。
libaprutil-1.so.0
libapr-1.so.0
これをapache2のあるフォルダ以下libフォルダを作成してその下に入れておく。今そのフォルダを、
/home/nao/hoge/lib
だったとしよう。/etc/ld.so.conf.d/にnanoを使って、apache.confファイルを作り、そこに、上記フォルダのパスを書いて保存する。
ロボットのrootになって、ldconfigを実行する。その後、
ldconfig -p | grep libapr
をrootで実行すると、
libaprutil-1.so.0 (libc6) => /home/nao/ibot/www/lib/libaprutil-1.so.0
libapr-1.so.0 (libc6) => /home/nao/ibot/www/lib/libapr-1.so.0
となり、これでOKである。
あとは、ポートを8080にしている限り、rootにならなくても、naoユーザーのままで、apachectl
./bin/apachectl start
を起動すれば、サーバーが立ち上がり、ロボットのipとポートを指定すると、
と、無事立ち上がった。