Image captioning(三)

来源:互联网 发布:ifashion淘宝什么意思 编辑:程序博客网 时间:2024/06/06 14:17

摘要

背景我们已经介绍了,现在我们上篇文章的基础上面引入比较流行的Attention机制
说下本篇文章的贡献:

  1. image captioning中使用同一种框架引入两种atttention机制。
  2. 可以洞察模型观察的点在哪里where, 以及观察的是什么what
  3. 代码我只会演示第二种attention 机制

模型

  1. image encoder
    第一层还是卷积层来处理图像信息,但是这里不同的是,我们不像上一篇提到的那样直接复用已有的模型,这里直接处理原始的图片。
    为什么要处理原始图片?因为如果要做attention,那么势必要在decoder阶段需要知道聚焦在图片的哪个位置,这样我们就不能直接用encoder出来的很高级的单向量了
    需要抽取出来一些原始的特征,每个特征能够表征图像的某一部分,这样在做decoder的时候,attention机制可以知道可以聚焦在哪一块,这样就提高了decoder描述的准确性
    假设我们处理图片后生成L=196个D=512维的向量:

    a=(a1,...,aL),aiRD 

  2. decoder
    主要框架我们还是用LSTM,为了引入attention,我们稍微做下变形,就是在原有的state基础上面再增加一个图片的content的信息
    假设需要decoder的序列为:

    经典的LSTM结构:
    it ft ot gt Ct ht=σ(wi.[Eyt1,ht1]+bi)=σ(wf.[Eyt1,ht1]+bf)=σ(wo.[Eyt1,ht1]+bo)=tanh(wc.[Eyt1,ht1]+bc)=ftCt1+itgt=ottanh(Ct)

    _, (c, h) = lstm_cell(inputs=x, axis=1), state=[c, h])

    调整后:
    it ft ot gt Ct ht =σ(wi.[Eyt1,ht1,ẑ t]+bi)=σ(wf.[Eyt1,ht1,ẑ t]+bf)=σ(wo.[Eyt1,ht1,ẑ t]+bo)=tanh(wc.[Eyt1,ht1,ẑ t]+bc)=ftCt1+itgt=ottanh(Ct)

    _, (c, h) = lstm_cell(inputs=tf.concat([x, context], axis=1), state=[c, h])

    ẑ t就是我们的content
    后面文中描述的content就是指这个向量,它代表的是我们decoder到目前这个词,应该聚焦的图片信息的哪块内容。
    对比两组公式可以直接看出,在每一个计算input是原始的input x增加了content , 那么这样的content是怎么生成?

    以下的计算逻辑和机器翻译seq2seq的attention模型计算类似,
    显现需要聚焦的区域向量为

    a=(a1,...,aL)

    当前hidden output为:ht1
    针对每一个ai 我们会得到一个它和当前的output的相关性分数
    eti=fatt(ai,ht1) 

    w = tf.get_variable('w', [self.H, self.D], initializer=self.weight_initializer)b = tf.get_variable('b', [self.D], initializer=self.const_initializer)w_att = tf.get_variable('w_att', [self.D, 1], initializer=self.weight_initializer)# features_proj=a_i, h = h_t-1h_att = tf.nn.relu(features_proj + tf.expand_dims(tf.matmul(h, w), 1) + b)    # (N, L, D)out_att = tf.reshape(tf.matmul(tf.reshape(h_att, [-1, self.D]), w_att), [-1, self.L])

    这里fatt是一个多层感知机层的attention 模型
    然后得到softmax 分数

    αti=exp(eti)Lk=1exp(etk) 

    alpha = tf.nn.softmax(out_att)  

    这样我们可以得到内容向量:

    ẑ t=ϕ({ai},{αi})

    这里ϕ 将多向量转化成单向量的函数,具体怎么计算我们下节介绍:

    然后是初始的c0,h0, 他们是 a=(a1,a2,...,aL)的向量平均分别经过多个线性层
    c0 h0=finit,c(1Niαi)=finit,h(1Niαi)

    with tf.variable_scope('initial_lstm'):    # [batch_size, dim_D]    features_mean = tf.reduce_mean(features, 1)    w_h = tf.get_variable('w_h', [self.D, self.H], initializer=self.weight_initializer)    b_h = tf.get_variable('b_h', [self.H], initializer=self.const_initializer)    # [batch_size, dim_hidden]    h = tf.nn.tanh(tf.matmul(features_mean, w_h) + b_h)    w_c = tf.get_variable('w_c', [self.D, self.H], initializer=self.weight_initializer)    b_c = tf.get_variable('b_c', [self.H], initializer=self.const_initializer)    c = tf.nn.tanh(tf.matmul(features_mean, w_c) + b_c)

    到这里,常规的做法我们已经得到输出 ht,常用的预测yt的方式是利用softmax
    yt=argmaxC(softmax(f(ht)))
    f是一个或者多个线性层,然后经过softmax之后,得到概率最大的一个词就是预测值yt

    这里作者f定义为:

    f=Lo(Eyt1+Lhht+Lzẑ t) 

    LoRkm,LhRmn,LzRmD
    这样:
    p(yt|a,yt11=a.exp(Lo(Eyt1+Lhht+Lzẑ t))) 

Attention 机制

本节我们介绍两种attention机制,随机attention 和 确定性 attention

随机attention

顾名思义,随机性attention的在计算ai的权重时时从分布中sample出来的,
每次的结果可能不一样,所以是一种随机方法。
定义:st=(st1,st1,...,stL)
它代表在生成第t个词的时候,聚焦在L中各个区域的参数

st是一个one-hot向量,也就是只有其中一个元素是1,其他是0
这里用了p=multinoulli多项分布,那么sti=1的概率为αt,i, 所以αt是分布p的参数。
我们从分布p生成变量st=(st1,st1,...,stL)

stMultinoulliL(αi) 

然后:zt^=ist,iai

本节以下可以忽略

上一篇文章我们知道,loss函数我们可以采用l2 loss 或者softmax 交叉熵的方式
这里因为参数是sample出来的,显然它是不能求导,从而不适用于链式法则,

下面我们引入对数似然来解决优化可导问题
它的对数似然函数为:
logp(y|a)

这里我们引入了变量st,所以我们的似然函数可以写成:
logp(y|a)  =logsp(s|a)p(y|s,a)>=sp(s|a)logp(y|s,a)

我们令 Ls=sp(s|a)logp(y|s,a)
我们对Ls进行求导:

LsW=sp(s|a)Wlogp(y|s,a)+p(s|a)logp(y|s,a)W

其中:

logp(s|a)W=1p(s|a)p(s|a)W 

我们替换掉上式,最终得到:

LsW=sp(s|a)[logp(s|a)Wlogp(y|s,a)+logp(y|s,a)W] 

这里的s是从multinoulli分布里面sample出来的,

s¯t=MultinoulliL(αi)

所以这里:
p(s|a)的概率我们可以直接用1N
LsW1Nn=1N[logp(s¯n|a)Wlogp(y|s¯n,a)+logp(y|s¯n,a)W] 

整体的优化如上面所示,但是由于是采用采样的方式得到中间参数,所以算法波动可能很大,
这里我们使用两种方式降低波动:

  1. 梯度优化里面动量的机制类似,第k次mini-batch迭代使用大部分的k-1次的对数似然信息,这样保证足够稳定

    bk=0.9bk1+0.1log(y|s¯k,a)

  2. 另外一种是在后面加上一个熵的量

    H(s)=np(sn|a)log(p(sn|a))
    最终得到:

    LsW1Nn=1N[logp(s¯n|a)W(logp(y|s¯n,a)b)λr+λeH(s¯n)W+logp(y|s¯n,a)W]

这里要注意点有两个:

  1. ẑ t=ist,iai, 这里的st,i是从分布里面sample出来的,分布的参数是αi
  2. 目标函数是最大化似然函数,似然函数有两种优化形式来降低sample造成的波动

确定性的机制

上面我们使用sample出来的st,i来计算内容向量ẑ t
这里我们不用sample的方式,直接求期望:
content:Ep(st|a)[ẑ t]=iαt,iai

with tf.variable_scope('attention_layer', reuse=reuse):    w = tf.get_variable('w', [self.H, self.D], initializer=self.weight_initializer)    b = tf.get_variable('b', [self.D], initializer=self.const_initializer)    w_att = tf.get_variable('w_att', [self.D, 1], initializer=self.weight_initializer)    # (N, L, D)    h_att = tf.nn.relu(features_proj + tf.expand_dims(tf.matmul(h, w), 1) + b)    # (N, L)        out_att = tf.reshape(tf.matmul(tf.reshape(h_att, [-1, self.D]), w_att), [-1, self.L])    alpha = tf.nn.softmax(out_att)      context = tf.reduce_sum(features * tf.expand_dims(alpha, 2), 1, name='context')   #(N, D)

这样其实整个模型就是可导的

另外:我们可以在这个内容前面增加一个gate常量,这个gate是和ht1有关
βt=σ(fbeta(ht1))
fbeta是一个线性层

这样content:ϕ({ai},{αi})=βtiαt,iai

with tf.variable_scope('selector', reuse=reuse):    w = tf.get_variable('w', [self.H, 1], initializer=self.weight_initializer)    b = tf.get_variable('b', [1], initializer=self.const_initializer)    beta = tf.nn.sigmoid(tf.matmul(h, w) + b, 'beta')    # (N, 1)    context = tf.multiply(beta, context, name='selected_context')    return context, beta

当然到了这里其实就可以了

以下可以忽略

下面的步骤其实是为了可以和上面的 margin 对数似然logp(y|a)统一框架
证明了一波,其实它们两种机制在对数似然的期望上面是一致的

所以统一后,负的对数似然函数为:

Ld=log(P(y|x))+λi(1tαti)2 

训练要点

作者用的是RMSProp/Adam,采用的是自适应学习率
这里还有一个重点是图像生成的特征向量a=(ai)
这里作者直接用VGGNet 在ImageNet上面的预先训练的模型,每张图片抽取出 L=196个D=512 维的像向量

效果:

这里写图片描述

这里写图片描述

阅读全文
0 0
原创粉丝点击