チャットボット用AIの作り方概要
AIゆめ
ここでは、
AIゆめの夢占いチャット
で使用した技術の紹介をして行きましょう。
自然言語処理用AIの歴史
で述べた通り、自然言語処理の分野では単語を分散表現で表すword2vecの研究が長く行われていたため実用性には乏しいというのが実情でした。
その後、従来の
FF(Feed Forward:フィードフォワード)型のニューラルネットワーク
では取り扱うことができない長い文章の学習ができるゲート付きRNNの一種である
LSTM が進化したことや、
Encoder-Decoderモデルとも呼ばれるニューラルネットワークにおいてAttentionというとても強力な技術が開発されたことにより、
チャットボット用AIが一気に実用レベルで活用できる環境が整ってきましたので、その具体的な方法をご紹介して行きましょう。
チャットボット用AIの作り方の目次
チャットボット用AI - seq2seq概要
チャットボット用AIではEncoder-Decoderモデルとも呼ばれるseq2seq(Sequence to Sequence)技術を使用します。
seq2seq技術とは、時系列データを別の時系列データに変換する技術であり、言語データや音声データなどの時系列データを取り扱うチャット、翻訳、文章要約、音声認識などに広く応用されています。
Encoder-Decoderモデルは、図1に示すようなモデルであり、Encoderにより入力時系列データ(この例では会話入力)をEncode(符号化)し、
その符号化されたデータをDecoderに入力することによりDecode(復号化)して新たな時系列データ(この例では会話応答)を出力するようなモデルです。
ここで、Decoder出力は'<eos>'で始まり'<eos>'で終了していますが、Decoderには開始文字である'<eos>'を入力単語として与えると、次に来る確率が最も高い単語が出力されます。
そして、その出力単語を再度Decoderの入力単語として与えると、また次の単語が出力されます。
このような操作により次々と単語を出力して行き、終了文字である'<eos>'が出力されるまで出力を継続して行くことにより新たな文章が生成されるのです。
なお、ディープラーニング(Deep Learning:深層学習)によりEncoder-Decoderモデルを学習させる際には、固定長の入力データと固定長の教師データの大量の組が使用されますが、
これらのデータの初期値には全角スペース'<sp>'が与えられることが多いため、終了文字として'<sp>'が使用されることもあります。
そして、Encoder-Decoderモデルは、入力データと教師データを変更することにより、翻訳用AIになったり、文章要約用AIになったりします。
例えば、英訳用AIであれば日本語の文章が入力データであり、この文章に対応する英訳文章が教師データになります。
このため、優秀なEncoder-Decoderモデルを作り、このモデルに適正なデータの組を与えてしっかり学習させておけば、
学習した範囲の入力時系列データと出力時系列データを対応させる問題であれば、どのような問題にも対応できるようになります。
図1 Encoder-Decoderモデルによるチャットの例
Encoder-Decoderモデル
ここでは、
One-hotベクトルによる文章表現 で示した
「今日(2)/は(3)/晴れ(4)/です(5)/ね(6)/。(7)」→「はい(8)/、(9)/今日(2)/は(3)/晴れ(10)/て(11)/い(12)/て(11)/気持ち(13)/が(14)/いい(15)/です(5)/ね(6)/。(7)」
というチャット応答(「Encoderコーパス」→「Decoderコーパス」)の形態素解析例を用いてEncoder-Decoderモデルを解説して行きましょう。
まず、Decoderで使用されるLSTM言語モデルは、図2に示すようなモデルになります。
言語モデルは、
RNNによる文章生成例 でも示しましたが、
図中の'EM'は
Embedding層 、
'A'は
Affine変換 を行うAffine層、
'SM'は
ソフトマックス(Softmax)関数 処理を行うSoftmax層を、それぞれ表しています。
図2 DecoderのLSTM言語モデル
チャット応答の()中の番号は単語ID(正確には形態素ID)ですが、
Encoder-Decoderモデルの入出力\((x_v)_t\)や\((o_v)_t\)(\(v=0~V-1)\)にはOne-hot行列が使用されますので、\(V\)は語彙数を表し、
入出力行列の\(v\)列はその単語の単語IDに該当する列のみが1に設定されています。
なお、入出力行列の\(t\)行はDecoderコーパスの長さとなりますが、
RNNの実装 - バッチ処理
の項で述べた通り、実装ではバッチ処理が行われますので、入出力\((x_v)_t\)や\((o_v)_t\)は\(N\)x\(T\)x\(V\)という3次元の行列になります(\(N\)はバッチサイズ)。
ただし、この膨大な行列を実際に内部メモリーに記憶している訳ではありません。
図2のLSTM層には\((h_j)_{T-1}^{(E)}\)という入力と\((h_j)_{T-1}^{(D)}\)というDecoderの出力がありますが、
Encoder-DecoderモデルのDecoderのLSTM層の入力にはEncoderのLSTM層の出力が入力されることになります。
ここで、Encoderでは\((h_j)_{T-1}^{(E)}\)という隠れ層の出力のみ得られれば良いので、EncoderのLSTM構成は図3のようになります。
図3 EncoderのLSTM構成
以上の解説をまとめるとEncoder-Decoderモデルは図4のように表すことができます。
即ち、\((h_j)_{T-1}^{(E)}\)というEncoderの最後の時刻における隠れ層の出力要素を介してEncoderがDecoderに接続されることにより、
Encoderコーパスの情報がDecoderの出力に反映されるようになっている訳なのです。
そして、Encoder-Decoderモデルの学習の際には、
ソフトマックス(Softmax)関数 処理を行うSoftmax層の中に
クロスエントロピー誤差 という損失関数にEncoder入力と対になった教師データを与え、
その誤差を最小にするよう
誤差逆伝播法 を使用してEncoderやDecoderの重みやバイアスを修正して行くことになります。
図4 Encoder-Decoderモデルの構成
Encoder-Decoderモデルの性能向上
ここでは、Encoder-Decoderモデルの学習性能を向上する2種類の方法についてご紹介しておきましょう。
Encoderコーパスデータの反転
Googleの
I. Sutskeverら(2014) は、
WMT-14データーセットという英語からフランス語への翻訳のデーターセットを用いて、Encoderへの入力データの反転(Reversing the Source Sentences)により、
単一のLSTM層を用いたテストにおいて
パープレキシティ が 5.8から4.7に改善することを示しました。
この現象について完全な説明はできていませんが、彼らはその原因が単語間の「最小タイムラグ」が大幅に短縮されたためとしています。
即ち、ニューラルネットワークの逆伝播において勾配消失が起こる原因は「最小タイムラグ」が大きいことにあると
S. Hochreiterら は述べていますが、
入力データを反転することにより、EncoderとDecoder間の単語間の平均距離は変化しませんが、EncoderとDecoder間の最初の数単語に関しては「最小タイムラグ」が大幅に短縮されたため、
EncoderとDecoder間の逆伝播が容易になったと考えています。
EncoderとDecoder間の単語間の平均距離は変化していないと書きましたが、文章を扱う問題では実際は変化します。
図5は、EncoderとDecoderの入出力文章を単語IDで記述したものです(実際にはこの単語IDがOne-hotベクトルで与えられます)。
Encoderの文章には長い文章もあれば短い文章もありますが、Encoderの入力は固定長であるため、
図5に示す通り短い文章は後ろに全角スペース(本サイトでは単語ID=0)を入れて最も長い文章に長さを一致させます。
このため、EncoderとDecoder間の最初の数単語ばかりではなく、短い文章における単語間の平均距離も短縮されることになりますので、多くの場合Encoderへの入力データの反転は有効だと考えられます。
図5 Encoderコーパスデータの反転
そして、Pythonのリスト、タプル、Numpy配列などに使用できるスライス機能を使用すれば、入力データの反転は一行で記述することができます。
Pythonのスライスは、[start:stop]または[start:stop:step]で記述されますが、startとstopを省略すれば全配列を意味し、step=-1とすれば逆順になりますので、
Encoderコーパス配列をenc_xs[M, T](Mは全データセット数、Tは最大の文章長さ)とすれば、次の一行でEncoderコーパス配列を反転することができます。
enc_xs = enc_xs[:, ::-1]
なお、Encoderコーパス配列を反転して学習させたモデルを用いて新たな文章を生成する場合は、Encoderへの入力文章を反転させて入力するだけですので、とても簡単です。
双方向LSTM(Bidirectional LSTM)
音声認識における最初の音や文章認識における最初の単語は意味を持たないけれども、未来の文脈によって始めて意味を持ちますので、未来の情報を文脈の解釈に導入するのはごく自然な流れと言えるでしょう。
そして、未来の情報を音声認識に始めて導入した論文がA. Gravesらの
Framewise Phoneme Classification with Bidirectional LSTM Networks(2005) です。
彼らは、自動音声認識システムの開発と評価を行うための
TIMITコーパス を用いて
双方向LSTM(Bidirectional LSTM:BLSTM)や双方向RNN(Bidirectional RNN:BRNN)を従来のLSTMやRNNと比較しました。
その結果、従来のRNNに対するBRNNの性能改善効果が殆ど無いのに対して、より長期の時間依存性が大きLSTMでは、従来のLSTMに対するBLSTMの性能改善効果が大きいことが示されました。
A. Gravesらの音声認識に関する論文
SPEECH RECOGNITION WITH DEEP RECURRENT NEURAL NETWORKS(2013) による双方向RNNを
Encoder-DecoderモデルのEncoderに活用すれば、図6のようになります。
即ち、Embedding層の出力行列は、順方向のLETMと逆方向のLETMの入力行列となり、これらの出力行列はAffine変換で結合されて出力されることになります。
Encoder-Decoderモデルでは最終時刻における隠れ層の出力を用いていましたので、双方向LSTMを適用したEncoderをこのEncoder-Decoderモデルに適用する場合には、
順方向のLETMの最終時刻における出力\((h_j)_{T-1}^{(E)}\)と逆方向のLETMの最終時刻における出力\((h_j)_0^{(E)}\)のみが使用されることになるため、
出力行列をAffine変換で結合することの意義がないと思われるでしょうが、次章で説明するAttentionという強力な技術では、この結合した出力を使用しますので、
双方向LSTMを適用する意義が感じられるでしょう。
図6 双方向LSTMを利用したEncoder
なお、順方向のLETMと逆方向のLETMの出力行列の結合方法としては、Affine変換ばかりではなく、和を用いる方法や平均を用いる方法なども考えられるでしょう。
本サイトでは、次章で説明するAttentionによる様々な実験を基に、図7に示すような加算による双方向LSTMを用いたEncoderを採用することにします。
図7 加算による双方向LSTMを用いたEncoder