本文将深度学习与聚焦机制和强化学习结合起来,通过学习本篇文献,可以:
理解聚焦机制中较简单的hard attention
了解增强学习的基本流程
(1)注意力机制(Attention)
以物体识别为例,在进行分类的时候,不是一次对一张大图进行估计,而是分多次观察小部分图像,首先初始化一个图像坐标点l,以该坐标点为中心提取小部分图像,并通过一个Gilpse网络图区该部分的图像特征向量,将该部分的图像的特征向量输入到RNN中,将RNN输出输入到一个location网络中,得到下次要提取的图像的坐标点,以该坐标点为中心提取小部分图像,并再Gilpse网络图区该部分的图像特征向量,再将其输入RNN中,如此步骤循环steptimes次,将最后一次的RNN输出结果输入到一个softmax中,估计分类结果。引入Attention,每次只对局部区域的图像进行处理,一方面可以减少图像其他区域的非必要信息的干扰,从而降低噪声的影响,另一方面还可以减少计算量。
(2)强化学习
由于我们每次只是观察到局部区域的图像,因此需要额外的信息来辅助如何进行有效的分类。本文引入了一个action网络,如果分类正确,则会对该行为进行奖励,网络训练的目标就是使奖励最大化。
RAM网络整体结构如下图所示:
上图中,A用于提取局部区域块,即attention机制,具体来说就是在t时刻,只取以t时刻的中心坐标点
lt−1
为中心的局部块作为后续网络的输入,输出为以该坐标点
lt−1
为中心的提取的N个局部块的平均
p(xt,lt−1)
;B为整个Glimpse Network,提取得到局部块
p(xt,lt−1)
后,需要提取该局部块的特征向量
gt
,作为RNN的输入。C部分为整体模型,将t时候Glimpse Network输出的图像块的特征向量
gt
输入到RNN中,得到t时刻的隐藏层输出
ht
;得到t时刻的隐藏层输出后,我们就可以通过action网络
fa(θa)
可能会影响环境状态的环境动作 at 。并通过location 网络
fl(θl)
得到t+1时刻的局部块的中心坐标点
lt
。
Glimpse网络:
A部分为attention机制,具体为根据输入t时刻的为局部块的中心坐标点,以该坐标点为中心,提取大小为[w0,w0]的图像块,提取到局部块之后,可以将输入两个全连接层,得到特征向量,中心坐标点输入另一个包含两个全连接层的网络,得到特征向量,最后由这两个特征向量得到图像局部块的特征向量表示 gt 。公式如下:
g=Rect(Linear(hg)+Linear(hl))
式中,
hg=Rect(Linear(p(x,l)))
注意本文中
Linear
函数公式为:
Linear(x)=Wx+b
,且
Rect(x)=max(x,0)
此部分代码如下:
def __call__(self, loc):
glimpse_input = self.get_glimpse(loc)#以loc为中心,提取局部区域块
glimpse_input = tf.reshape(glimpse_input,
(tf.shape(loc)[0], self.sensor_size))
g = tf.nn.relu(tf.nn.xw_plus_b(glimpse_input, self.w_g0, self.b_g0))#w_g0=(64,128)
g = tf.nn.xw_plus_b(g, self.w_g1, self.b_g1)#w_g1=(128,256)
l = tf.nn.relu(tf.nn.xw_plus_b(loc, self.w_l0, self.b_l0))#w_l0=(2,128)
l = tf.nn.xw_plus_b(l, self.w_l1, self.b_l1)#w_l0=(128,256)
g = tf.nn.relu(g + l)#得到最终的 glimpse feature vector
return g
rnn部分网络:
得到t时刻的输入图像的特征向量 gt 后,将 gt 和t-1时刻的RNN输出 ht−1 输入到RNN中,便可以得到t时刻的RNN输出 ht ,公式为:
ht=fh(ht−1)=Rect(Linear(ht−1+Linear(gt)))
(1)location 网络 fl(θl)
得到局部块的特征向量 gt 后,将其输入到rnn中,得到隐藏层输出ht,将ht输入location 网络 fl(θl) ,则可以得到 t+1 时刻的局部块坐标中心点 lt ,公式如下:
lt=fl(ht)=Linear(ht)
此部分代码如下:
def __call__(self, input):#input为rnn隐藏层的输出,(,n_hidden),w为(n_hidden,2)
mean = tf.clip_by_value(tf.nn.xw_plus_b(input, self.w, self.b), -1., 1.)#将input转换为大小为2的坐标点,即(,2)
mean = tf.stop_gradient(mean)#阻止对mean反向传播
if self._sampling:
loc = mean + tf.random_normal(
(tf.shape(input)[0], self.loc_dim), stddev=self.loc_std)#随机取样,(,2)
loc = tf.clip_by_value(loc, -1., 1.)
else:
loc = mean
loc = tf.stop_gradient(loc)
return loc, mean
(2)action网络 fa(θa)
将rnn最后一个隐藏层的输出output输入一个action网络,则可以根据网络的任务执行对应的动作。在执行完一个动作后,会得到奖励信号 rt 。例如在目标识别场景中,action网络 fa(θa) 的作用是执行一个分类操作,如果分类正确,就奖励1分,否则不奖励。action网络 fa(θa) 的操作公式如下:
fa(h)=exp(Linear(h))/Z
式中,Z表示归一化操作。
如果rnn的最后一个隐藏层输出为output,将其输入到一个分类网络中,代码如下:
# Build classification network.分类
with tf.variable_scope('cls'):
w_logit = weight_variable((config.cell_output_size, config.num_classes))#w_logit=(256,10)
b_logit = bias_variable((config.num_classes,))
logits = tf.nn.xw_plus_b(output, w_logit, b_logit)
logits = tf.nn.softmax(logits)
#预测输出标签pred_labels = tf.argmax(logits, 1)# 0/1 reward.#分类正确则奖励1reward = tf.cast(tf.equal(pred_labels, labels_ph), tf.float32)#ypred equal to yin ,then the reward is 1rewards = tf.expand_dims(reward, 1)#[batch_sz, 1]rewards = tf.tile(rewards, (1, config.num_glimpses))# [batch_sz, timesteps]
从而可以得到输出每个标签的概率分布logits,和输出分类pred_labels,如果输出分类pred_labels与输入分类y相等,则reward+1分,否则reward+0分。
模型训练
action网络训练目标就是学习到一种策略使得总的奖励达到最大。奖励目标函数为基本的强化学习公式:
loss2=1M∑Mi=1∑Tt=1▽θlogπ(uit|si1:t;θ)(Rit−bt)
上式中,
Rt
为rewards,
bt
为reinfocement baseline,即偏置项,可以由输入序列的RNN隐藏层输出到一个Linear层得到,
π
本文中为高斯函数。
总的损失函数为RNN预测分类结果的交叉损失熵loss1,即使分类尽量准确;加上奖励目标函数loss2,即使奖励最大;最后加上loss3为奖励
Rt
与偏置项
bt
的差的平方,即使奖励
Rt
与偏置项
bt
尽量接近。
下面给出整个模型代码:
完整代码连接:https://github.com/zhongwen/RAM
rnn_cell = tf.nn.rnn_cell
seq2seq = tf.nn.seq2seq
mnist = input_data.read_data_sets('MNIST_data', one_hot=False)
config = Config()
n_steps = config.step
loc_mean_arr = []#mean为坐标点
sampled_loc_arr = []#sample为mean加上随机取值
def get_next_input(output,i):
loc, loc_mean = loc_net(output)#output为rnn隐藏层输出ht
gl_next = gl(loc)
loc_mean_arr.append(loc_mean)
sampled_loc_arr.append(loc)
return gl_next
# placeholders
images_ph = tf.placeholder(tf.float32,
[None, config.original_size * config.original_size *
config.num_channels])#input image size is 28*28*1
labels_ph = tf.placeholder(tf.int64, [None])
# Build the aux nets.
with tf.variable_scope('glimpse_net'):
gl = GlimpseNet(config, images_ph)
with tf.variable_scope('loc_net'):
loc_net = LocNet(config)
# number of examples
N = tf.shape(images_ph)[0]#batch_size
init_loc = tf.random_uniform((N, 2), minval=-1, maxval=1)#对每张输入图像初始化一个点l
init_glimpse = gl(init_loc)
# Core network.
lstm_cell = rnn_cell.LSTMCell(config.cell_size, state_is_tuple=True)
init_state = lstm_cell.zero_state(N, tf.float32)
inputs = [init_glimpse]
inputs.extend([0] * (config.num_glimpses))#输入为rnn序列为[init_glimpse,0,0,0,0,0,0]
outputs, _ = seq2seq.rnn_decoder(
inputs, init_state, lstm_cell, loop_function=get_next_input)#在rnn中,上一个隐藏层的输出输入get_next_input得到下一个隐藏层的输入
# Time independent baselines
with tf.variable_scope('baseline'):
w_baseline = weight_variable((config.cell_output_size, 1))#w_baseline=(256,1)
b_baseline = bias_variable((1,))
baselines = []
for t, output in enumerate(outputs[1:]):
baseline_t = tf.nn.xw_plus_b(output, w_baseline, b_baseline)#get the reward
baseline_t = tf.squeeze(baseline_t)
baselines.append(baseline_t)
baselines = tf.pack(baselines) # [timesteps, batch_sz]
baselines = tf.transpose(baselines) # [batch_sz, timesteps]
# Take the last step only.
output = outputs[-1]
# Build classification network.分类
with tf.variable_scope('cls'):
w_logit = weight_variable((config.cell_output_size, config.num_classes))#w_logit=(256,10)
b_logit = bias_variable((config.num_classes,))
logits = tf.nn.xw_plus_b(output, w_logit, b_logit)
logits = tf.nn.softmax(logits)
# cross-entropy.,
xent = tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels_ph)
xent = tf.reduce_mean(xent)#使分类的准确度最高
#预测输出标签
pred_labels = tf.argmax(logits, 1)
# 0/1 reward.
#分类正确则奖励1
reward = tf.cast(tf.equal(pred_labels, labels_ph), tf.float32)#ypred equal to yin ,then the reward is 1
rewards = tf.expand_dims(reward, 1)#[batch_sz, 1]
rewards = tf.tile(rewards, (1, config.num_glimpses))# [batch_sz, timesteps]
#logll大小为[batch_sz, timesteps]
logll = loglikelihood(loc_mean_arr, sampled_loc_arr, config.loc_std)#利用均值和方差求取高斯函数,在求去输入为sampled_loc_arr的高斯函数的值
advs = rewards - tf.stop_gradient(baselines)#Rt-bt
#学习使rewards最大
logllratio = tf.reduce_mean(logll * advs)#equaltion 2
reward = tf.reduce_mean(reward)
#learning the baselines
baselines_mse = tf.reduce_mean(tf.square((rewards - baselines)))#square of Rt-bt
var_list = tf.trainable_variables()
# hybrid loss,总的损失函数
loss = -logllratio + xent + baselines_mse # `-` for minimize