AAAI-17にて、Lantao YuらによるGAN(Generative Adversarial Net)のフレームワークで、強化学習のpolicy gradientを学習し、テキストや音楽などのシーケンスを生成する方法に関する論文が発表される。
SeqGAN: Sequence Generative Adversarial Nets with Policy Gradient
https://arxiv.org/pdf/1609.05473.pdf
ちなみに、GANの元論文はこちら。
https://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf
強化学習の報酬関数に、本物か偽物かを分類する識別モデルを用いている。強化学習モデルと識別モデルを対立的に学習するところがポイントである。
GANを強化学習に応用した研究には、他にも以下の論文がある。
・Adversarial Attackson Neural Network Policies
https://arxiv.org/pdf/1702.02284.pdf
・Robust Adversarial Reinforcement Learning
https://arxiv.org/pdf/1703.02702.pdf
通常のGANを、系列生成問題に適用するためには、以下の2つの課題がある。
1)離散値の系列データを扱うのが困難。生成モデルGのパラメータの更新に識別モデルDの勾配を用いているが、"微小な"勾配により更新された生成モデルGに対応する出力値が離散のため存在しない可能性があるためである。
2)score/lossは系列データ全体に対してのみ考えられていて、部分的な系列データに対しては、現在と将来のscoreのバランスを考慮しなければならないので自明でない
具体的には、が初期状態のもとで行動を選択する条件付きを確率が強化学習モデルであり、が、番目の行動の系列の実写である確率を出力する識別モデルである。
この強化学習モデルのパラメータは、初期状態から開始して、終端状態までの系列で得られる報酬和の期待値を最大化するように学習する。
ここで、Q関数は、初期状態で任意の行動をとったあと、強化学習モデルに従って終端状態まで遷移した場合に得られるQ値(割引報酬和の期待値)である。
状態は、過去の行動の系列に対応していて、行動は次の状態に追加されるので、状態遷移は決定的(確率1)である。そのため、行動の系列と報酬系列は1対1の関係になっている。報酬には行動の系列が本物か否かを判定する識別モデルを用いているので、Q値は以下のように識別モデルの出力そのものとしている。
2)の課題を解決するために、Q値は、roll-out policyに従って、将来の識別結果をモンテカルロサンプリング(遷移しながら系列をサンプリング)し、それらの平均により推定している。
※roll-out policyは、と異なる分布なので、上述したモンテカルロサンプル平均にはバイアスが乗るので、本論文では、としているとのこと。毎回モンテカルロサンプルするので計算コストが大きい。一様分布で一度集めたサンプルを、重点サンプリングで使いまわすなどの工夫もできる。
識別モデルは、式(5)のloss関数を最小化することによりパラメータを更新する。
一方、強化学習モデルは式(6)の勾配法を用いてパラメータを更新する。
右辺の微分は、logテクニックでの期待値の形になるようにしている。
ここまでは、いたってシンプル。報酬関数を識別モデルに置き換えたpolicy gradient法であり、誰でもGANを強化学習に応用するとこうなるという感じである。
強化学習モデルにはLSTM、識別モデルにはCNNを用いている。CNNは、画像ではなく系列データを識別するために用いられている。系列データを画像のように2次元データにするために、各時間ステップごとのk次元の列ベクトルを、系列の長さT分並べている。つまり入力k x Tの行列としている。
イメージとしては、X. Zhang & Y. LeCunのTextUnderstandingfromScratchの図2が参考になる。
https://arxiv.org/pdf/1502.01710.pdf
Synthetic Data Experimentsでは、評価値として、クロスエントロピーのではなく、チューリングテストにとってより適切なを用いている。ここで、は真の分布、は近似した分布、は人間の観測者の分布である。具体的には、評価値は、次のようにとして、Oracleを用いている。
つまり、と設定している。
G_{oracle}として、正規分布に従ってランダムに設定したLSTMを用いている。学習データとしては、長さ20の系列データを10,000本用いている。
識別モデルDのCNNは、以下のようになっている。conv, relu, max_poolを、1つのconvセットとし、カーネルサイズ(フィルターサイズ)を、[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20]、フィルタ数を[100, 200, 200, 200, 200, 100, 100, 100, 100, 100, 160, 160]のように変えた12種類のconvセットを用意する。それぞれのconvセットの処理をした後に、concatしている。そして、最後にfull connectedで分類している。
dis_filter_sizes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20] dis_num_filters = [100, 200, 200, 200, 200, 100, 100, 100, 100, 100, 160, 160] pooled_outputs = [] for filter_size, num_filter in zip(filter_sizes, num_filters): with tf.name_scope("conv-maxpool-%s" % filter_size): # Convolution Layer filter_shape = [filter_size, embedding_size, 1, num_filter] W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W") b = tf.Variable(tf.constant(0.1, shape=[num_filter]), name="b") conv = tf.nn.conv2d( self.embedded_chars_expanded, W, strides=[1, 1, 1, 1], padding="VALID", name="conv") # Apply nonlinearity h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu") # Maxpooling over the outputs pooled = tf.nn.max_pool( h, ksize=[1, sequence_length - filter_size + 1, 1, 1], strides=[1, 1, 1, 1], padding='VALID', name="pool") pooled_outputs.append(pooled) # Combine all the pooled features num_filters_total = sum(num_filters) self.h_pool = tf.concat(3, pooled_outputs) self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total]) # Add highway with tf.name_scope("highway"): self.h_highway = highway(self.h_pool_flat, self.h_pool_flat.get_shape()[1], 1, 0) # Add dropout with tf.name_scope("dropout"): self.h_drop = tf.nn.dropout(self.h_highway, self.dropout_keep_prob) # Final (unnormalized) scores and predictions with tf.name_scope("output"): W = tf.Variable(tf.truncated_normal([num_filters_total, num_classes], stddev=0.1), name="W") b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b") l2_loss += tf.nn.l2_loss(W) l2_loss += tf.nn.l2_loss(b) self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores") self.ypred_for_auc = tf.nn.softmax(self.scores) self.predictions = tf.argmax(self.scores, 1, name="predictions")
サンプルコードは、下記のgithubにて公開されている。
https://github.com/LantaoYu/SeqGAN
rollout.pyに関するメモ。
tensorflowのバージョンは0.12が推奨されている。ちなみに、サンプルコードをTensorflow v1.0.0で実行すると、pack, unpack, split, nn.rnn_cell._linearなどv0.12から変更した部分に関するエラーがでる。pack, unpack, splitは簡単な関数名の置き換えなどで対応が可能である。
hirotaka-hachiya.hatenablog.com
しかし、nn.rnn_cell._linearの解決方法は下記で議論されていて、3/2時点ではまだ明らかになっていない。
http://stackoverflow.com/questions/42437115/tensorflow-replacement-for-tf-nn-rnn-cell-linearinput-size-0-scope
v1.0.0をアンインストールして、v0.12に戻した。。。
> sudo pip uninstall tensorflow-gpu > sudo pip install --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-0.12.0-cp27-none-linux_x86_64.whl