言語生成の強化学習をやっていく 実験編 REINFORCE編

この記事は強化学習苦手の会 Advent Calendar 2020の16日目の記事です。

はじめに

こんにちは、品川です。
本記事では、言語生成の強化学習でよく用いられるREINFORCE(baseline あり/なし)による結果を前回のSelf-Criticの結果と比較しながら見ていきたいと思います。
Self-CriticはREINFORCEの派生的な手法で、REINFORCEのbaselineをgreedyに生成した文による報酬とすることで、REINFORCEの分散が大きい問題を緩和する手法です。期待する結果としてはREINFORCE w/o baseline < REINFORCE w/ baseline < Self-Criticとなることが期待されます。

REINFORCEへのコードの改変

ベースとするコードは前回と同じものを用いました。
github.com

これは元々Self-Criticのコードなので、w/ & w/o REINFORCEへの改変を行うために、captioning/utils/rewards.pyget_self_critical_rewardを以下のように改変しました。

def get_self_critical_reward(greedy_res, data_gts, gen_result, opt):
    
    ...

    scores = opt.cider_reward_weight * cider_scores + opt.bleu_reward_weight * bleu_scores

    # Self-Critic
    # scores = scores[:gen_result_size].reshape(batch_size, seq_per_img) \
    #             - scores[-batch_size:][:, np.newaxis]

    # variant: REINFORCE with baseline
    scores = scores[:gen_result_size].reshape(batch_size, seq_per_img) \
                   - scores[:gen_result_size].mean(keepdims=True)[:, np.newaxis]

    # variant: REINFORCE without baseline
    # scores = scores[:gen_result_size].reshape(batch_size, seq_per_img)

    scores = scores.reshape(gen_result_size)

    rewards = np.repeat(scores[:, np.newaxis], gen_result.shape[1], 1)

    return rewards

ちょっとややこしいのですが、scoresはgen_result_size分のサンプリングして生成した文とbatch_size分のgreedyに生成した文がつながっているarrayだったので、こんな形になっています。
REINFORCE w/ baselineのbaselineには、サンプリングして得られた文による報酬のサンプル平均を用いていることになります。

実験設定

実験設定は、前回の記事の「強化学習あり(Self-Critic)」の設定と同じです。前述のコード改変部分のみが変わっています。

学習結果

前回と同様に、3000イテレーションごとに、validation set 5000サンプルに対してBLEU、CIDEr(正確にはCIDEr-D)SPICE、train/validation lossを計測してみた結果は以下の通りです。青がSelf-Critic濃いピンクがREINFORCE w/ baseline水色がREINFORCE w/o baselineの結果になります。

BLEU(単語n-gramの一致率、高いほど良い)

BLEUは学習経過と共に上昇
BLEUスコアの推移

CIDEr (CIDEr-D) (TF-IDFも考慮した単語n-gramの一致率、高いほど良い)

CIDEr-Dは学習経過と共に上昇
CIDEr-Dスコア

SPICE(シーングラフの一致率、高いほど良い)

SPICEはおおむね上昇傾向
SPICEの推移①

SPICEはおおむね上昇傾向
SPICEの推移②

train/validation loss

train/validation lossは一度下降してから徐々に上昇
train/validation lossの推移

train lossの拡大版(cross entropyから強化学習に切り替わるあたりから)

train lossの推移(拡大版)

BLEUやCIDEr、SPICEは概ね期待通りREINFORCE w/o baseline < REINFORCE w/ baseline < Self-Criticという感じでした。REINFORCE w/ baselineは割といい線いってましたが、最後の方で若干Self-Criticに差をつけられた形になりました。train lossについても、Self-Criticの方が中盤から後半にかけて常に若干優勢で0に近いという結果でした。

サーベイ論文にはSelf-Criticを使った手法がたくさん載っていましたが、今回の結果では確かにSelf-Critic使っとけば良さそうという結果でした。
バッチサイズを増やした影響か、収束まで学習させられてなかったのでこれで性能の上下を断言するのはちょっと厳しい感じがありますが、少なくともbaselineあり/なしの差はそこそこあるようです。
REINFORCE w/ baselineSelf-Criticにあまり大きな差が見られなかったのは、バッチサイズの増大により確率勾配の分散が低減された影響もあるのではないかと思います。バッチサイズを下げた状態だと、より差が浮き彫りになるのかもしれません。

収束するまでの結果と、batch sizeを下げた結果は、また後日追記できればいいなあと思います(やるとは言ってない)

本日はここまでです。次回はDQNを新たな仲間に加えてActor-Criticにした場合の結果を見てみたいと思います(果たしてやる余裕があるのか・・・)