[论文阅读]Matching Networks for One Shot Learning
引入
也是一种Metric-based模型. 孪生模型(Siamese Neural Network)在训练和测试的时候使用的样本是以对pair的形式, 判断两张图片, 两句短文本等等是不是属于同一样本. 使用pair这种形式天生的劣势, 在训练和测试时, 如何合适的构造样本都是需要一定的策略的. 例如在训练时相同分类和不同分类的样本比例应为1:1等等.
而Matching Network使用了不同的逻辑, 它的训练和测试样本不再是一对对图片, 文本, 而是如同普通模型一样使用单个样本. 当然在One-shot任务中, 还是要分为support set和batch set的, 只不过set中的每个样本不再是一对图片, 而是一张图片.

Matching Network是一个端到端的模型. 它的结构设计处于以下考虑:
在判断新图片的分类时(上图右侧单图), 能够充分使用support set(上图左侧四图)中的信息. 具体来说是将新图的类别结果来源于support set中的样本标签. 这在Siamese Neural Network中是不存在
新图的标签预测类似于Nearest Neighbors或KDE机制, 比较与support set中样本的近似程度, 通过一定的策略/规则(如相似度加权)从supprot set中的标签, 得到预测标签. 这种类似于Nearest Neighbors或KDE的方法是non-parametric的
对于新的之前从未观测到的类别, 可以直接使用模型在这些类别上进行预测得到高质量的结果, 而且不用对训练好的模型做任何操作
模型解释
基本结构
先从模型的最后部分, 如何得到预测样本的类别来看.
对于包含个样本的support set, 将一个样本的(image, labal)对记为. 对于新的预测样本, 希望得到高质量的预测.
训练好的模型既是一个映射函数, 可以看到样本的预测结果是和support set 紧密相关的, 因此可以将模型记为. 因此test样本的预测类别为:
对于test样本, 使用attention思想去计算它的预测类别:
测试样本与support set中每个样本的注意力计算方法如下, 其中为余弦距离, 而和分别是对test和support表征的函数, 简单来说可以是一些图像或者文本的表征神经网络, 输出表征向量:
这种机制在文中被称为使用了attention mechanism, 实质上是将support set中的每个样本的表征向量依次与待预测样本的表征向量点积, 然后使用softmax函数进行归一化, 使用这个归一化的权重值, 对support set中所有样本的标签进行加权, 得到预测样本在support set包含的所有类别上的概率值.
这里求取test样本的类别使用了attention mechanism完成了类似于KDE的效果. 这种方法充分里使用support set中的信息. 相比而言, 以下常用的方法都有着明显的缺点:
最邻近: 也是依次计算test和support set中每个样本的相似度, 然后选择最相似的样本, 以这个样本的类别作为test样本的预测类别
最终只使用了support set中一个样本的信息, 丢弃了大量信息
这种方法类似于kNN(k Nearest Neighbors)机制
Full Context Embeddings
这是论文中比较重要的一部分, 但在实际使用中可以选择性使用. 这一章节的目的是使用不同的方法, 分别对test和support set中的样本进行更好的表征, 充分利用support set中的信息. 当然如果不使用本章节的结构, 直接使用神经网络表征器(test, support set共享一套参数)也是可以的.
在目前为止的框架中, 我们使用和对test和support set中的样本进行embedding, 然后使用attend, point, knn等形式得到对test的类别预测. 这样的形式有两个不足:
support set中的每个样本是相互独立的, 得到embedding向量的过程也是相互独立的, 互相之间不影响
test样本也是用过来得到embedding向量的, 而且现在的和使用的是同样的抽取器. 但由于的类别是由support set中的样本决定的, 那么的embedding向量的抽取, support set也应当参与进来
因此使用Full Context Embeddings这种思想的引领下, 希望对support set中的每个样本以及test样本的样本的embedding, 都应该受到整个support set 的影响.
分别来说.
Full Context Embeddings
对于support set中的样本, 它的embedding向量就由变为了. 至于为什么这么做, 作者是这样解释的:
This could be useful when some element is very close to , in which case it may be beneficial to change the function with which we embed .
如何做到整个support set 去影响样本的表征过程呢? 论文中使用了bi-LSTM去对进行编码. 使用这种方法, 就需要把support set中的所有样本考虑成一个序列, 一个一个进行输入, 依次得到每个样本的的表征向量.
这里就产生了一个新的问题, 原来support set中无序的样本, 如何合理的进行排序. 文章参考了Order Matters: Sequence to sequence for sets这篇论文中的方法.
具体来说, 将原来的表征器得到表征向量(样本之间相互独立)重新记为, 样本新的表征为:
即原始表征向量和bi-LSTM对应输入位置的隐向量的加和:
本质上相当于使用bi-LSTM结构对support set中的样本序列进行表征, 但添加了一个skip connection结构, 即最后加上的, 加快了训练的速度和鲁棒性.
Full Context Embeddings
对于test样本, 这里也使用一种带attention的LSMT结构进行表征, 注意这里的LSTM与上面对support set中样本表征使用的是不同的网络(单向, 双向就能体现出来), 由于这里的表征也使用到了整个support set, 因此将新的表征向量记为:
其中是原始的表征器对test样本的表征向量. 是固定的数值, 代表这里LSTM推进的步数. 是一个集合, 包含了support set中每个样本的表征向量(full content embeddings得到的).
具体来说, 在执行到第步时, 使用如下的方式得到这一步的隐向量:
需要注意:
无论在哪一步, 输入都是固定的, 即原始的test样本的表征向量
这里使用的LSTM中的隐向量是和拼接得到的. 因此长度是输入表征向量的两倍, 对输出的维度没有影响
得到这一步LSTM隐向量之后, 这一步的最终输出如下表示:
至此, 得隐向量还都是只与test样本相关的, 没有引入support set的作用. 而我们需要通过引入support set中的信息. 为此, 在这一步得到隐向量之后, 计算这个隐向量与support set中所有样本的的表征向量的关系, 这一步也称为计算content based attention.
然后使用这个attention值作为权重, 对support set中所有的表征向量进行加权求和, 得到.
然后再将这个向量与计算得到的这一步的隐向量拼接起来, 作为当前LSTM网络的隐向量, 继续下一步的执行.
因为这里的LSTM要执行步, 就取最后一步的输出, 作为最终对test样本的表征向量.
训练策略
既是模型中需要训练的参数. 作为一种meta-learning模型, 首先从task中抽样一次episode(meta-learning中一次训练, 即support set + batch set)所需要的label set , 然后再从每个抽取到的label对应的样本集中, 抽取一定的样本, 从而组成了n-way k-shot的训练方法.
需要注意的是, 如果用来测试的与用来训练的差距很大(例如判断动物类别和检测检测人脸)的话, 得到的结果也是不准确的.
代码
代码参考repository: markdtw/matching-networks. model.py中定义了Matching Networks, main.py中包含了数据集划分为train和test集合的方法, 以及整个训练的过程. 下面是一些需要注意的细节, 帮助更好的理解模型.
首先是计算test样本与support set样本中每个样本的相似度:
def cosine_similarity(self, target, support_set):
"""the c() function that calculate the cosine similarity between (embedded) support set and (embedded) target
note: the author uses one-sided cosine similarity as zergylord said in his repo (zergylord/oneshot)
"""
#target_normed = tf.nn.l2_normalize(target, 1) # (batch_size, 64)
target_normed = target
sup_similarity = []
for i in tf.unstack(support_set):
i_normed = tf.nn.l2_normalize(i, 1) # (batch_size, 64)
similarity = tf.matmul(tf.expand_dims(target_normed, 1), tf.expand_dims(i_normed, 2)) # (batch_size, )
sup_similarity.append(similarity)
return tf.squeeze(tf.stack(sup_similarity, axis=1)) # (batch_size, n * k)这里使用了one-sided cosine similarity.(but why?)
最后更新于
这有帮助吗?