BluemixのRetrieve&Rank用トレーニングデータを生成してみた

#Retrieve&Rankって何？
Bluemixでは様々なWatson APIが使えるようになっているが、その中に質問に対して適切な回答を返してくれるAPIであるRetrieve&Rank(RR)というものがある。**RRを使うにあたって、トレーニングデータが重要**なのだがトレーニングデータの準備は大変。トレーニング精度を出すにはまだまだ工夫が必要だけど、まずは機能を試すためにトレーニングデータをお手軽に作れないか試してみた。

#トレーニングデータ
Retrieve&Rankを構成するには、次の2つのデータを用意しないといけない。

- 回答候補のデータ
- トレーニングに使う質問と回答の紐付けデータ

RRにとって、より重要なのは実は**ドキュメントではなくて質問**。実際にユーザーする質問に答えられないと意味が無いので、ユーザーが入れてくるであろう質問を使ってRRを教育してあげる必要がある。
したがって、

1. ユーザーがするであろう質問を集める
2. 集めた質問に対して、適切な回答をしているドキュメントを探す
3. それを回答候補のドキュメントとする
4. 質問と回答を紐付けて関連度をつける

という順番でやるのが理想。

#質問と回答を集める
質問を集める、ということで、まず思いついたのがYahoo!知恵袋。一般ユーザーが自然言語で質問を入力しているので、いいトレーニングデータが拾えるはず。と思ったのだが、結構質問文が長い＆回答が長い上に正しくない回答や煽るような回答もあって、そのまま使うのは難しく断念。

ということで、次の候補はFAQ。FAQはユーザーからよく寄せられる質問に対して正しい回答がちゃんとあるので、トレーニングデータとして適していると言える。
質問に対して回答を以下のようなフォーマットで集めてみた。

```text:FAQ.csv
質問1,回答1
質問2,回答2
```

という感じ。
集めたデータを `FAQ.csv` などの名前で保存しておく。
RRではトレーニングの際に、質問が49個以上ないとエラーになるため、ここの質問は最低49個、保険をかけて少し多めに集めておくとよい。もちろん質問はたくさんあってもかまわない。
ただ、トレーニング精度を高めるためには、似たような質問(同じ意味なんだけど表現が違う質問)は同じ回答候補に紐付くのがよい。・・・のだが、今回は簡単のために、そこは目をつぶっている。

#FAQデータからトレーニングデータを生成する
生成したいトレーニングデータは、回答候補データ(JSON形式)と、トレーニング用の質問と回答の紐付けデータ(CSV形式)の2つ。

#回答候補データ
回答候補データのレイアウトは、RRの設定中に読み込ませるsolr-configの中のschema.xmlに書いてある。ここでは詳細は省くが、最低でもidとbodyは必要。
例えば以下のようなデータになる。

```json:document.json
{
"add":{ "doc":{ "id":"1", "body":"回答1" }},
"add":{ "doc":{ "id":"2", "body":"回答2" }},
"commit" : { }
}
```

#GroundTruth(グラウンドトゥルース)
GroundTruth(GT)というのは質問と回答の紐付けデータのこと。質問に対して回答が何なのか回答候補データのIDで紐づける。その際に、関連度も設定する。回答は1つとは限らず、2つ以上設定してもいい。むしろ設定すべき。
こちらはCSV形式で、質問文、回答のID、関連度、回答のID、関連度、・・・と記述する。
例えば以下のようなデータになる。

```csv:GroundTruth.csv
"質問1","回答候補ID","関連度","回答候補ID","関連度",...
"質問2","回答候補ID","関連度","回答候補ID","関連度",...
```

#トレーニングデータ作成時のコツ
RRだけではなくてNLCなどもそうなんだけど、**データはUTF-8で作っておいたほうがいろいろ捗る**。Curlでの受け渡しも*比較的*簡単だし、そもそも**NLCのデータはUTF-8じゃないと食べてくれない**(※RRはSJISでも大丈夫だったががUTF-8にしておいた方が安全だろう)。
ということで、FAQ.csvをはじめ、トレーニングデータはUTF-8で保存するのが推奨。最初Windowsのコマンドプロンプトでツールを作っていたのだけど、結局UTF-8の壁に阻まれてCygwinに逃げました。。。
RRが意味不明なエラーを吐いてる、という場合はWindowsじゃなくてLinuxやCygwinなどでUTF-8データにして実行してみるのも手。

#生成ツール
で、ここまで長々と説明したのは、フォーマットはわかっているからプログラムで生成してしまおうという話がしたかったから。
Google先生の力を借りて、Cygwinで動く以下のようなシェルを作った(Linux入れるのが面倒だったのでインストールされていたCygwinを使ったけどLinuxでも動くと思う)。
FAQ.csvのあるディレクトリーで　`./createTrainingData.sh FAQ.csv hoge`　とすると、CSVファイルを読み込んで、`hoge-GT.csv`と`hoge-Doc.json`を生成してくれる。
あとは、生成したデータを使ってRRを構成するだけ。

```sh:createTrainingData.sh
#!/bin/bash

if [ $# -ne 2 ]; then
	echo "Usage: $ createTrainingData.sh <FAQ.csv> <DomainName>" 1>&2
	exit 1
fi

filename=$1

OUTGT=$2"-GT.csv"
OUTDOC=$2"-Doc.json"

# ファイル初期化
cat /dev/null > ${OUTGT}
cat /dev/null > ${OUTDOC}

echo "{" >> ${OUTDOC}

CNT=0

# ループ
while read line; do
	# カウンターをまわす
	CNT=`expr ${CNT} + 1`
	
	# カンマで区切る
	i=`echo ${line} | cut -d ',' -f 1`
	j=`echo ${line} | cut -d ',' -f 2`
	
	# GT
	echo \"${i}\",\"${CNT}\",\"4\" >> ${OUTGT}
	
	# Doc	
	echo \"add\":{ \"doc\":{ \"id\":\"${CNT}\", \"body\":\"${j}\" }}, >> ${OUTDOC}
	
done < ${filename}

echo "\"commit\" : { }" >> ${OUTDOC}
echo "}" >> ${OUTDOC}

echo "TOTAL RECORD = " ${CNT}
```

#注意点
トレーニング用の質問回答データの方はちょっと手抜き。
本当は質問に対して回答を2つ以上つけたかったのだけど、最初のFAQ.csvを作るのが大変だなーというのと、単に技術力の問題で断念。さらに、質問に対する回答の関連度も決めうちで"4"にしている。このあたりは今後の課題。

これでもエラーは出ないんだけど、本来のRRとしては回答候補は2つ以上あるべきとか、関連度もどういう基準で何回くらい出てくるべき、とか色々な目安があるのでご注意を。([参考：Preparing training data](https://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/doc/retrieve-rank/training_data.shtml#top))

#おわりに
このデータを使ってRRを動かしてみたり、ここからNLC用のデータを作ったり、Cloudantに回答データを登録したり、Watson APIを繋いで応答システムを組んだり、っていうこともやっているのだけど、今回は時間切れ。