Docomo API 音声出力結果Jsonファイルのパース

Docomo APIの音声出力は、Jsonファイルとして返される。それをJavaで構文解析をする。使わせていただいたのは、以下のサイトにあるパーサー。
https://www.tutorialspoint.com/json/json_java_example.htm
要領とサンプルを掲載しておく。
(1)サイトから、ソースをダウンロードし、ライブラリ用のjarファイルを作成する。JsonSimple.jar
(2)以下のソースのライブラリに加える。jsonと配列の処理が頭の中でごちゃごちゃになる。(なお、上記サイトのデコードサンプルは、そのままではエラーになる。なんでそんなものを?)

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.json.simple.JSONObject;
import org.json.simple.JSONArray;
import org.json.simple.parser.ParseException;
import org.json.simple.parser.JSONParser;
public class JsonParserTest {

    String readJsonFile() {
        // 出力結果をファイルから読み込む場合
        // 通常は、httpレスポンスをそのまま、次のparseJasonで解析すれば良い
        String json = "";
        try {
            File file = new File("/path/to/docomo_output.json");
            BufferedReader br = new BufferedReader(new FileReader(file));
            String str;
            while ((str = br.readLine()) != null) {
                json += str;
            }
            br.close();
        } catch (FileNotFoundException e) {
            System.out.println(e);
        } catch (IOException e) {
            System.out.println(e);
        }
        return json;
    }

    void parseJson(String json) {
        JSONParser parser = new JSONParser();
        try {
            JSONObject obj0  = (JSONObject
            )parser.parse(json);
            //認識テキストの出力
            //出力テキストの全体は、textタグを読み取れば良い
            System.out.println("出力テキスト");
            System.out.println(obj0.get("text"));
            // 以上で良いのだが、分かち書きされた分析結果も受け取るようにして見る
            // resultの値は、配列になっているので、まずその配列を受け取る
            JSONArray results_array = (JSONArray) obj0.get("results");
            // 配列の最初の要素を取り出す
            JSONObject obj1_tokens  = (JSONObject
            )results_array.get(0);
            // 配列の最初の要素が"tokens"というJsonになっているので、それをうけとる
            // そのtokensの値が配列になっているので、配列として受け取る
            JSONArray array1_tokens = (JSONArray) (obj1_tokens.get("tokens"));
            // tokensの配列をループにして回す
            for (Object ob : array1_tokens) {
                JSONObject job = (JSONObject) ob;
                String written = (String) job.get("written");
                System.out.print("書き方:" + written + ",");
                double confidence = (double) job.get("confidence");
                System.out.print("信頼性:" + confidence + ",");
                String spoken = (String) job.get("spoken");
                System.out.println("読み:" + spoken);
                // 他の要素は省略
            }
        } catch (ParseException pe) {
            System.out.println("position: " + pe.getPosition());
            System.out.println(pe);
        }
    }

    public static void main(String[] args) {
        JsonParserTest jparser = new JsonParserTest();
        jparser.parseJson(jparser.readJsonFile());
    }
}

Docomo API の音声認識は使える

Google Cloud APIが何かと使いにくく、レスポンスも遅いのでDocomo APIの音声認識を試してみた。結果的に、Googleより倍速い。認識率も悪くはない。使える。
以下の文章googleだと6秒以上かかるが、Docomoだと4秒かからない。
{
,"confidence":0.700,"starttime":0,"endtime":8300,"tags":[],"rulename":""
,"text":"今日はネタをやりますお客さんからお題をもらって、この第2ついて扇子とか、なぞかけに答えてもらうというもので、"}
}

word2vecのスレッド利用

wprd2vecで、ユニット数を倍の400にしてやってみた。5,6時間かかりそうなので、スレッドを16にしてやってみたら超早い。MacProが8コア、16スレッドなのだが、それを目一杯使っている姿初めてみた。

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秒くらい使ってしまうのが痛い。
しかし、まあ、これで、結構な人工知能をロボットに組み込んだことになる。

Mysqlにログインできなくなって

ロボットの対話関係の知識データは、基本的にいつでも持ち運んでいるmacbookのmysqlデータベースに入れている。ここから、ロボットも必要な情報を取ってくるし、決定的に重要なデータベースなのだ。
数時間前に急にログインできなくなった。あせった!!久しぶりにbookを再起動したのがきっかけか、なんなのか、よくわからないが、rootでもユーザーでも正しいパスワードを入れてもaccess denyになってしまう。
まさに、今日、ロボットのネタでこのデータベースを使うという時に。
2時間ほど悪戦苦闘した。結局何が何だかわからないのだが、やったことといえば、mysqlが入れていあるフォルダーがあって、そのデータ部分を実際、使っていたであろうデータに移し替えた。少し詳しく言えば、古いバージョンのmysqlmのフォルダーに、使っていたdataフォルダがあって、シンボリックされている新しいバージョンのmysqlのフォルダのdataフォルダは使っていたデータが入っていない。そこで、古いバージョンのdataフォルダーを新しい方に移して、オーナーをもとどおりに変えたりしたのだ。OSの方のrootになってだ。そのあたりは複雑なのだが。
そんなことを、色々やっているうちに、再起動すると、不意に戻った。ログインできるようになった。バンザーイ!!
すぐさま、今のデータベースのデータをmysqldumpで全てバックアプした。

研究室のロボット、ピッキーもノッキーも修理から帰ってきた

肩を脱臼して動かなくなっていたのっきーは少し前に修理が終わって帰ってきた。指が二本折れてしまっていたぴっきーも昨日帰ってきて、研究室のロボット2台は完全復活した。ぴっきーのOSがふるいままなので、時間が空いている日にバージョンアップしたい。上がぴっきー、下がのっきー。二人合わせてピノッキオ。(私の私的なロボットサリーは自宅に置いてある)

gRpcについて

Google cloud platformの元になっているが、grpcというシステムなのだが、これがよくわからないので、理解しようと思って、
http://www.grpc.io/docs/quickstart/java.html
ここにおいてあるサンプルを動かそうと思ったら、つまずいたので、対処した方法を以下に書いておく。
(1)そこに書いてある以下を実行する。

$ # Clone the repository at the latest release to get the example code:
$ git clone -b v1.0.3 https://github.com/grpc/grpc-java
$ # Navigate to the Java examples:
$ cd grpc-java/examples

これは問題なくできるだろう。
(2)次の、

$ ./gradlew installDist

を実行すると、FAILURE: Build failed with an exception.がでて失敗する。
そこで、
https://github.com/grpc/grpc-java/issues/581
ここの下の方に書いてある対処を実行する。
すなわち、元のフォルダにある、
run-test-client.sh
run-test-server.sh
のそれぞれの最後に、
-PskipCodegen
を一行入れて、再度

$ ./gradlew installDist

を実行すると、うまくいくはずである。

$ ./build/install/examples/bin/hello-world-server
$ ./build/install/examples/bin/hello-world-client

を実行する。

JavaでGoogle Cloud API ストリーミング

ロボットのAI処理のサーバーをJAVAで作っているので、Pythonでやってきた、Google Clouf APIのストリーミング言語処理をJavaで動かす必要があった。まえから、Javaバージョンを試してみたかったのだが、解説を読んでもすぐにわからないところがあったので、pythonに流れた。ちょっと本気でやってみた。
一見わかりにくいと思ったのは、逆にpythonよりも動かすのは簡単だったからだ。記録のために書いておこう。何しろ老人だから、記録しないとすぐに忘れる。このサイトは、私の忘却防止のためにおいているようなものだから。
基本的に、
https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/speech/grpc
に記載されていることを実行するだけなのだが、Pythonより、そっけなく書かれている。
(1)google cloud platformの認証関係の手続きは、pythonのところで書いたことをやっておけば、全部スキップできるので、あえてここでは書く必要がないと思う。一つだけ書いておくと(自分のために)、もし、認証に失敗したら、環境変数に認証データファイルを定義し直す必要があるのかもしれない。環境変数が何気に消えているときがあるので。たとえば、次のように。
export GOOGLE_APPLICATION_CREDENTIALS=/Users/path/to/プロジェクト名のついた認証ファイル.json
認証ファイルはGoogle Cloud Platformの認証手続きを行えばもらえる。
(2)Mavenをインストールする。これは、そのサイトにアクセスして言われた通りにやれば良いと思う。
(3)ビルドだが、githubに慣れていない、と言うかほとんど知らないので、上記のアドレスを指定して、gitを実行してもうまくいかなかった。よくわかっている方がいたら、教えて欲しいが、自分でも、もう少し勉強したいと思う。ただ、パスを遡って、もう少し上で、gitを実行したらうまくいった。まず、
git clone https://github.com/GoogleCloudPlatform/java-docs-samples/
を実行する。すると、そこにjava-docs-samples/のフォルダができて、その下にspeech関係のサブフォルダ以下も作成されているはずだ。
(4)java-docs-samples/speech/grpcに降りて、そこにpom.xmlがあることを一応確認しよう。そして、解説によれば、mvn projectでも良いと書いてあったが、そっちではうまくいかなかったので、もう一つの、

$ mvn compile
$ mvn assembly:single

の二つのコマンドを順に実行する。
(5)次のコマンドで実行させる。
bin/speech-sample-streaming.sh --host=speech.googleapis.com --port=443 --sampling=16000
ただこれだと、マイクに向かって喋っても、ローマ字か英語で帰ってくるだけである。Ctl_Cで中止する。
(5)srcのフォルダを一番下まで辿っていくと、
StreamingRecognizeClient.java
というソースがあるので、それをエディタで開いて、197行目からの、
RecognitionConfig.newBuilder()
.setEncoding(AudioEncoding.LINEAR16)
.setSampleRate(samplingRate)
.setLanguageCode("ja-JP")  // ←※
.build();
上に”←※”で指示した一行を加える。これは、
https://cloud.google.com/speech/reference/rest/v1beta1/RecognitionConfig
に開設されているRecognitionConfigのオプションだ。
(6)以下のコマンドでビルドする。
$ mvn clean

$ mvn clean
$ mvn compile
$ mvn assembly:single

(7)再度実行する。
bin/speech-sample-streaming.sh --host=speech.googleapis.com --port=443 --sampling=16000
マイクに向かって喋ると、日本語のテキストになってコンソールに表示されるはずです。表示のされ方は、pythonの場合とほぼ同じ、中間状況が表示される。

AIモジュールの作成

AIモジュールを作成しつつある。仕組みはこうだ。
(1)パソコン上に、情報、知識、ネタに関するデータベースをMysqlで作成しておく。
(2)パソコン上に、JAVAで、こうしたAI関係の情報を処理するサーバーを動かす。
(3)ロボットに、それに対応するクライアントモジュールを、C++のライブラリとして作成する。
(4)qichatのトピックファイルで、変数イベントを発生させてサーバー上から必要な情報を獲得する。獲得したことに対するイベント、変数を同じトピックファイル上で取得し、会話的に使用する。
ほぼできた。明日、多王するトピックファイルを作成して、ロボットで試そう。