从图片相似度学习图片的表示

作者: xlvector | 来源:发表于2016-07-25 14:24 被阅读5175次

    很多时候带分类标注的图片样本是很难获得的,但是图片之间的相似度却不难获得。最简单的方式有几个:

    最早用来从相似图片数据集上学习图片表示的网络结构是siamese网络。

    siamese.png

    两幅图通过两个共享权重的CNN得到各自的表示。而各自表示的距离决定了他们是相似还是不相似。

    在siamese网络之后,又提出了用triplet loss来学习图片的表示,大概思路如下:

    • 拿到3张图片A, B, C。其中A,B相似,A,C不相似。
    • 学到A, B, C 的表示,使得A,B之间的距离尽量小,而A,C之间的距离尽量大。

    用mxnet实现triplet loss很简单,代码如下:

    def get_net(batch_size):
        same = mx.sym.Variable('same')
        diff = mx.sym.Variable('diff')
        anchor = mx.sym.Variable('anchor')
        one = mx.sym.Variable('one')
        one = mx.sym.Reshape(data = one, shape = (-1, 1))
        conv_weight = []
        conv_bias = []
        for i in range(3):
            conv_weight.append(mx.sym.Variable('conv' + str(i) + '_weight'))
            conv_bias.append(mx.sym.Variable('conv' + str(i) + '_bias'))
        fc_weight = mx.sym.Variable('fc_weight')
        fc_bias = mx.sym.Variable('fc_bias')
        fa = get_conv(anchor, conv_weight, conv_bias, fc_weight, fc_bias)
        fs = get_conv(same, conv_weight, conv_bias, fc_weight, fc_bias)
        fd = get_conv(diff, conv_weight, conv_bias, fc_weight, fc_bias)
        
        fs = fa - fs
        fd = fa - fd
        fs = fs * fs
        fd = fd * fd
        fs = mx.sym.sum(fs, axis = 1, keepdims = 1)
        fd = mx.sym.sum(fd, axis = 1, keepdims = 1)
        loss = fd - fs
        loss = one - loss
        loss = mx.sym.Activation(data = loss, act_type = 'relu')
        return mx.sym.MakeLoss(loss)
    

    这里conv_weight[], fc_weight, conv_bias[], fc_bias是两个CNN网络共享的模型。理论上这里可以用任何的CNN网络(AlexNet, GoogleNet, ResNet)。我们用了一个特别简单的CNN,如下:

    def get_conv(data, conv_weight, conv_bias, fc_weight, fc_bias):
        cdata = data
        ks = [5, 3, 3]
        for i in range(3):
            cdata = mx.sym.Convolution(data=cdata, kernel=(ks[i],ks[i]), num_filter=32,
                                       weight = conv_weight[i], bias = conv_bias[i],
                                       name = 'conv' + str(i))
            cdata = mx.sym.Pooling(data=cdata, pool_type="avg", kernel=(2,2), stride=(1, 1))
            cdata = mx.sym.Activation(data=cdata, act_type="relu")
    
        cdata = mx.sym.Flatten(data = cdata)
        cdata = mx.sym.FullyConnected(data = cdata, num_hidden = 1024,
                                      weight = fc_weight, bias = fc_bias, name='fc')
        cdata = mx.sym.L2Normalization(data = cdata)
        return cdata
    

    Triple loss用的Simultaneous Feature Learning and Hash Coding with Deep Neural Networks里的定义:

    Triple Loss

    下面是在cifar10数据集上测试的结果。为了形象的表示,采用了图片检索的方式(因为不是论文,所以就不那么严谨了)。在训练集上学习图片的表示,然后对于测试集的一张随机图片,找到测试集上和他最相似的其他图片:

    cifar_triple.png

    在其他的论文中还有一些其它评测方式,比如学习到表示后,用一个SVM去学习分类,看看分类的准确度相比End-End的CNN如何。基本的结论都是精度会稍微低一些,但是没用明显区别。这说明学到的表示是靠谱的。

    全部的代码见 github

    相关文章

      网友评论

      • chenyu_21cn:喜欢这个主题
      • 888505eb51eb:你好,我想问一下有没有什么方法可以避免手动指定参数而能实现参数共享,这样一个一个手动指定感觉很麻烦。谢谢!
      • 7a05d255ccab:想请问一下,您读取的npy文件是什么呢?您的数据集是什么样子的呢
        ZuckML:@加油油的杨七七 同问,好奇数据集长啥样子
      • zdl_todo:loss = mx.sym.Activation(data = loss, act_type = 'relu')
        这个用的好精髓!
        xlvector:@zdl_todo 是的
        zdl_todo:@xlvector 貌似如果自己写max()操作的话,需要写一点cuda代码或者用NDarray里面的某些操作才能使用GPU吧,用relu似乎直接就解决了 :)
        xlvector:@zdl_todo 这个有高人指点

      本文标题:从图片相似度学习图片的表示

      本文链接:https://www.haomeiwen.com/subject/hwygjttx.html