triplet loss原理推导及其代码实现

关于triplet loss原理及推导,参考来源:

triplet loss 原理以及梯度推导

【前言】
最近,learning to rank 的思想逐渐被应用到很多领域,比如google用来做人脸识别(faceNet),微软Jingdong Wang 用来做 person-reid 等等。learning to rank中其中重要的一个步骤就是找到一个好的similarity function,而triplet loss是用的非常广泛的一种。

【理解triplet】

这里写图片描述

如上图所示,triplet是一个三元组,这个三元组是这样构成的:从训练数据集中随机选一个样本,该样本称为Anchor,然后再随机选取一个和Anchor (记为x_a)属于同一类的样本和不同类的样本,这两个样本对应的称为Positive (记为x_p)和Negative (记为x_n),由此构成一个(Anchor,Positive,Negative)三元组。

【理解triplet loss】
有了上面的triplet的概念, triplet loss就好理解了。针对三元组中的每个元素(样本),训练一个参数共享或者不共享的网络,得到三个元素的特征表达,分别记为:这里写图片描述 。triplet loss的目的就是通过学习,让x_a和x_p特征表达之间的距离尽可能小,而x_a和x_n的特征表达之间的距离尽可能大,并且要让x_a与x_n之间的距离和x_a与x_p之间的距离之间有一个最小的间隔这里写图片描述。公式化的表示就是:
这里写图片描述

对应的目标函数也就很清楚了:
这里写图片描述
这里距离用欧式距离度量,+表示[]内的值大于零的时候,取该值为损失,小于零的时候,损失为零。
由目标函数可以看出:

  • 当x_a与x_n之间的距离 < x_a与x_p之间的距离加这里写图片描述时,[]内的值大于零,就会产生损失。
  • 当x_a与x_n之间的距离 >= x_a与x_p之间的距离加这里写图片描述时,损失为零。

【triplet loss 梯度推导】
上述目标函数记为L。则当第i个triplet损失大于零的时候,仅就上述公式而言,有:
这里写图片描述

【算法实现时候的提示】
可以看到,对x_p和x_n特征表达的梯度刚好利用了求损失时候的中间结果,给的启示就是,如果在CNN中实现 triplet loss layer, 如果能够在前向传播中存储着两个中间结果,反向传播的时候就能避免重复计算。这仅仅是算法实现时候的一个Trick。

下面是关于triplet loss的matlab代码实现,参考来源:

matlab---triplet loss

**摘要:**triplet loss 可以提高特征匹配的性能,可用物体识别,人脸识别,检索等方面,本文用matlab实现triplet loss。

triplet loss 就是学习一个函数隐射 f(x) , 从特征 x 映射到 RD , 有如下关系: y=f(x) . 在一个特征空间 RD 中,我们通过欧式距离度量两个特征向量的距离。 triplet loss 的目的在于使同一个类别在 RD 空间中靠近,不同类别在 RD 空间中远离,那么我们就可以抽象为如下优化函数:

minw1mim||f(xaif(xpi))||22||f(xaif(xni))||22+α

其中 f(x)=wx , xai 是锚点, xpi 是正样本点,它和 xai 属于同一类别, xni 是负样本点,它和 xai 不属于同一类别。
这样我们可以通过无约束优化来约束上面函数。

function demo_tripletloss
clear all
clc
data{1} = rand(600,300);
data{2} = rand(600,300);
data{3} = rand(600,300);
inputSize = 600;
hiddenSize = 400;
theta = initializeParameters(hiddenSize, inputSize);

addpath minFunc/
options.Method = 'cg'; % Here, we use L-BFGS to optimize our cost
                          % function. Generally, for minFunc to work, you
                          % need a function pointer with two outputs: the
                          % function value and the gradient. In our problem,
                          % sparseAutoencoderCost.m satisfies this.
options.maxIter = 400;      % Maximum number of iterations of L-BFGS to run 
options.display = 'on';

[opttheta, cost] = minFunc( @(p) tripletCost(p, inputSize, hiddenSize, data),theta, options);

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

tripletCost.m

function [cost,grad] = tripletCost(theta, inputSize, hiddenSize, data)
%================================================
%何凌霄 中科院自动化所
%创建时间:2016年5月25日
%================================================


% data: trainning sample for triplet loss
% W: transformer matrix
% cost: cost function
% grad: gradient direction
W = reshape(theta, hiddenSize, inputSize);
% Cost and gradient variables (your code needs to compute these values). 
% Here, We initialize them to zeros. 
cost = 0;
Wgrad = zeros(size(W)); 

% the gradient descent update to W1 Would be W1 := W1 - alpha * W1grad, and similarly for W2, b1, b2. 
% 

[n m] = size(data{1});%m: the number of samples,m:the dim of feature
bias = 0.2;
%forWard
% calc cost
for i = 1:m
   cost = cost + ((W*data{1}(:,i) - W*data{2}(:,i))'*(W *data{1}(:,i) - W*data{2}(:,i)) - (W*data{1}(:,i) - W*data{3}(:,i))'*(W *data{1}(:,i) - W*data{3}(:,i)) +bias)/m;
end

%calc gradient 

%计算W1grad 
Wgrad = (2*W*((data{1} - data{2})*(data{1} - data{2})' - (data{1} - data{3})*(data{1} - data{3})'))/m;

%-------------------------------------------------------------------
% After computing the cost and gradient, We Will convert the gradients back
% to a vector format (suitable for minFunc).  Specifically, We Will unroll
% your gradient matrices into a vector.

grad = Wgrad(:);

end
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

initializeParameters.m

function theta = initializeParameters(hiddenSize, inputSize)

% Initialize parameters randomly based on layer sizes.
r  = sqrt(6) / sqrt(hiddenSize+inputSize+1);   % we'll choose weights uniformly from the interval [-r, r]
W1 = rand(hiddenSize, inputSize) * 2 * r - r;

theta = W1(:);

end

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

另外需要一个优化包见资源.


其中关于 minFunc是一个资源包,作者Author: Mark Schmidt (2006),在 这个网址还有不少好资源。

下面是本人关于triplet loss原理推导的一些补充。在上文关于triplet loss的原理推导过程中,有一点容易被忽视,这一点在真正去实现其代码的时候就会发现,无从下手,因为我们要求关于参数的偏导。那么在triplet loss中谁是真正的参数呢(尤其在深度学习中)?很明显在上文的推导过程中我们没看到参数。真正的参数是f这个映射,即把xi映射为一种表示,通常为(w0,w1,...,wn).*(1,x1,...,xn),这里的参数w就是要学习的参数,需要通过triplet loss的梯度反向传播。










  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现triplet loss,需要以下步骤: 1. 定义模型架构:这包括定义模型的输入和输出,以及模型的层次结构。 2. 定义损失函数:triplet loss是一个三元组损失函数,它需要计算三个样本之间的距离。具体来说,对于给定的一个锚点样本和两个正负样本,我们需要计算锚点和正样本之间的距离(d_ap)以及锚点和负样本之间的距离(d_an)。然后,我们需要将它们放在一个损失函数中,使得 d_ap < d_an。 3. 训练模型:在训练过程中,我们需要随机选择一个锚点样本和两个正负样本,计算它们之间的距离,并基于损失函数更新模型参数。 下面是一个使用Keras实现triplet loss的示例代码: ```python from keras.layers import Input, Dense, Lambda from keras.models import Model import keras.backend as K def triplet_loss(y_true, y_pred, alpha = 0.2): """ Triplet loss function. """ anchor, positive, negative = y_pred[:,0], y_pred[:,1], y_pred[:,2] # Calculate Euclidean distances pos_dist = K.sum(K.square(anchor - positive), axis = -1) neg_dist = K.sum(K.square(anchor - negative), axis = -1) # Calculate loss basic_loss = pos_dist - neg_dist + alpha loss = K.maximum(basic_loss, 0.0) return loss def build_model(input_shape): """ Build a triplet loss model. """ # Define the input tensor input_tensor = Input(shape=input_shape) # Define the shared embedding layer shared_layer = Dense(128, activation='relu') # Define the anchor, positive, and negative inputs anchor_input = Input(shape=input_shape) positive_input = Input(shape=input_shape) negative_input = Input(shape=input_shape) # Generate the embeddings anchor_embedding = shared_layer(anchor_input) positive_embedding = shared_layer(positive_input) negative_embedding = shared_layer(negative_input) # Use lambda layers to calculate the Euclidean distance between the anchor # and positive embedding, as well as the anchor and negative embedding positive_distance = Lambda(lambda x: K.sum(K.square(x[0]-x[1]), axis=1, keepdims=True))([anchor_embedding, positive_embedding]) negative_distance = Lambda(lambda x: K.sum(K.square(x[0]-x[1]), axis=1, keepdims=True))([anchor_embedding, negative_embedding]) # Concatenate the distances and add the alpha parameter distances = Lambda(lambda x: K.concatenate([x[0], x[1], x[2]], axis=1))([positive_distance, negative_distance, positive_distance-negative_distance]) # Define the model model = Model(inputs=[anchor_input, positive_input, negative_input], outputs=distances) # Compile the model with the triplet loss function model.compile(loss=triplet_loss, optimizer='adam') return model ``` 在这个例子中,我们使用了Keras来定义模型架构和损失函数。我们定义了一个triplet_loss函数,它接受三个参数:y_true,y_pred和alpha。y_true和y_pred是标签和预测值,而alpha是一个超参数,用于控制正负样本之间的距离。triplet_loss函数计算三个样本之间的距离,并返回一个损失值。 我们还定义了一个build_model函数,它使用Keras定义了一个三元组损失模型。该模型接受三个输入:锚点样本,正样本和负样本,并生成三个嵌入向量。我们使用Lambda层来计算锚点和正样本,以及锚点和负样本之间的距离。然后,我们将这些距离连接在一起,并将它们作为模型的输出。最后,我们使用compile方法来编译模型,并将triplet_loss函数作为损失函数传递给它。 在训练过程中,我们将输入数据分成锚点样本,正样本和负样本,并将它们传递给模型。模型将返回三个距离,我们可以使用这些距离来计算损失并更新模型参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值