上手实践ICCV2013的JDA(Joint Distribution Adaptation)方法

1、数据获取
  选择surf特征文件作为算法的输入。surf特征文件可以从网盘上下载。下载到的文件主要包含4个.mat文件:amazon_SURF_L10.mat,Caltech10_SURF_L10.mat,dslr_SURF_L10.mat,webcam_SURF_L10.mat,它们对应 4 个不同的领域。彼此之间两两一组,就是一个迁移学习任务。每个数据文件包含两个部分:fts为800维的特征,labels为对应的标注。在测试中,选择由Caltech10_SURF_L10.mat作为源域,由amazon_SURF_L10.mat作为目标域。
  Office+Caltech10数据集:Office是视觉迁移学习的主流基准数据集,包含3个对象领域Amazon(在线电商图片)、Webcam(网络摄像头拍摄的低解析度图片)、DSLR(单反相机拍摄的高解析度图片),共有 4,652张图片 31 个类别标签。Caltech-256是对象识别的基准数据集,包括1个对象领域Caltech,共有 30,607张图片256个类别标签。对每张图片抽取SURF特征,并向量化为800维的直方图表征,所有直方图向量都进行减均值除方差的归一化处理,直方图码表由K均值聚类算法在Amazon子集上生成。具体共有4个领域C(Caltech-256), A(Amazon), W(Webcam) 和 D(DSLR),从中随机选取2个不同的领域作为辅助领域和目标领域,则可构造4×3=12个跨领域视觉对象识别任务,如A→D,A→C,· · ·,C→W。
  对surf文件夹中的4个数据文件使用如下代码进行简单归一化。具体方法是对数据进行加载,将最后的数据存入 Xs,Ys,Xt,Yt 这四个变量中。这四个变量分别对应源域的特征和标注、以及目标域的特征和标注。代码如下:

load("Caltech10_SURF_L10.mat");  % 源域
fts = fts ./ repmat(sum(fts,2),1,size(fts,2));  % fts是这个mat文件的变量,大小为N*M
Xs = zscore(fts,1); clear fts;  % z-score标准化
Ys = labels; clear labels;
save Caltech10_zscore_SURF_L10.mat Xs Ys;  % 保存到当前工作目录

load("amazon_SURF_L10.mat");  % 目标域
fts = fts ./ repmat(sum(fts,2),1,size(fts,2));  % fts是这个mat文件的变量,大小为N*M
Xt = zscore(fts,1); clear fts;  % z-score标准化
Yt = labels; clear labels;
save amazon_zscore_SURF_L10.mat Xt Yt;  % 保存到当前工作目录

这里只写出归一化待会要测试的Caltech10_SURF_L10.mat和amazon_SURF_L10.mat,在matlab工作区中建立Xs,Ys,Xt,Yt变量,若想测试其他数据集,可自行更改代码。
在这里插入图片描述

2、算法精炼
  JDA主要进行边缘分布和条件分布的自适应。通过整理化简,JDA 最终的求解目标是:
( X ∑ c = 0 C M c X T + λ I ) A = X H X T A Φ (\rm X\sum_{c=0}^C M_cX^T+\lambda I)A=XHX^TA\Phi (Xc=0CMcXT+λI)A=XHXTAΦ 上述表达式可以通过 Matlab 自带的eigs()函数直接求解。 A \rm A A就是要求解的变换矩阵。下面明确各个变量所指代的含义:

  • X \rm X X:由源域和目标域数据共同构成的数据矩阵
  • C \rm C C:总的类别个数。在我们的数据集中, C = 10 \rm C=10 C=10
  • M c \rm M_c Mc: MMD矩阵。当 c = 0 c=0 c=0时为全MMD矩阵;当 c > 1 c>1 c>1时对应为每个类别的矩阵。
  • I \rm I I:单位矩阵
  • λ \rm λ λ:平衡参数,直接给出
  • H \rm H H:中心矩阵,直接计算得出
  • Φ \rm \Phi Φ:拉格朗日因子,不用理会,求解用不到

3、编写代码
参考 JDA 开源的代码,直接给出精炼后的源码(来源于王晋东)

function [ acc,acc_iter,A ] = MyJDA( X_src,Y_src,X_tar,Y_tar,options )

% This is the implementation of Joint Distribution Adaptation.
% Reference: Mingsheng Long et al. Transfer feature learning with joint distribution adaptation.ICCV 2013.

% Inputs:
%%% X_src          :     source feature matrix, ns * n_feature
%%% Y_src          :     source label vector, ns * 1
%%% X_tar          :     target feature matrix, nt * n_feature
%%% Y_tar          :     target label vector, nt * 1

% Outputs:
%%% acc            :     final accuracy using knn, float
%%% acc_iter       :     list of all accuracies during iterations
%%% A              :     final adaptation matrix, (ns + nt) * (ns + nt)

% Set options
lambda = options.lambda;           %% lambda for the regularization
dim = options.dim;                 %% dim is the dimension after adaptation, dim <= n_feature
kernel_type = options.kernel_type; %% kernel_type is the kernel name, choose from 'primal' | 'linear' | 'rbf'
gamma = options.gamma;             %% gamma is the bandwidth of rbf kernel,can be missed for other 
T = options.T;                     %% iteration number, T >= 1. T <= 10 is suffice

acc_iter = [];
Y_tar_pseudo = [];
% Iteration
for i = 1 : T
    [Z,A] = JDA_core(X_src,Y_src,X_tar,Y_tar_pseudo,options);
    % normalization for better classification performance
    Z = Z*diag(sparse(1./sqrt(sum(Z.^2))));
    Zs = Z(:,1:size(X_src,1));
    Zt = Z(:,size(X_src,1)+1:end);

    knn_model = fitcknn(Zs',Y_src,'NumNeighbors',1);
    Y_tar_pseudo = knn_model.predict(Zt');
    acc = length(find(Y_tar_pseudo==Y_tar))/length(Y_tar); 
    fprintf('JDA+NN=%0.4f\n',acc);
    acc_iter = [acc_iter;acc];
end

end

function [ Z,A ] = JDA_core(X_src,Y_src,X_tar,Y_tar_pseudo,options)

% Set options
lambda = options.lambda;           %% lambda for the regularization
dim = options.dim;                 %% dim is the dimension after adaptation, dim <= m
kernel_type = options.kernel_type; %% kernel_type is the kernel name, primal|linear|rbf
gamma = options.gamma;             %% gamma is the bandwidth of rbf kernel

% Construct MMD matrix
X = [X_src',X_tar'];
X = X*diag(sparse(1./sqrt(sum(X.^2))));
[m,n] = size(X);
ns = size(X_src,1);
nt = size(X_tar,1);
e = [1/ns*ones(ns,1);-1/nt*ones(nt,1)];
C = length(unique(Y_src));

% M0
M = e * e' * C;  %multiply C for better normalization

% Mc
N = 0;
if ~isempty(Y_tar_pseudo) && length(Y_tar_pseudo) == nt
    for c = reshape(unique(Y_src),1,C)
        e = zeros(n,1);
        e(Y_src==c) = 1 / length(find(Y_src==c));
        e(ns+find(Y_tar_pseudo==c)) = -1 / length(find(Y_tar_pseudo==c));
        e(isinf(e)) = 0;
        N = N + e*e';
    end
end

M = M + N;
M = M / norm(M,'fro');

% Centering matrix H
H = eye(n) - 1/n * ones(n,n);

% Calculation
if strcmp(kernel_type,'primal')
    [A,~] = eigs(X*M*X'+lambda*eye(m),X*H*X',dim,'SM');
    Z = A'*X;
else
    K = kernel_jda(kernel_type,X,[],gamma);
    [A,~] = eigs(K*M*K'+lambda*eye(n),K*H*K',dim,'SM');
    Z = A'*K;
end

end

% With Fast Computation of the RBF kernel matrix
% To speed up the computation, we exploit a decomposition of the Euclidean distance (norm)
%
% Inputs:
%       ker:    'linear','rbf','sam'
%       X:      data matrix (features * samples)
%       gamma:  bandwidth of the RBF/SAM kernel
% Output:
%       K: kernel matrix
%
% Gustavo Camps-Valls
% 2006(c)
% Jordi (jordi@uv.es), 2007
% 2007-11: if/then -> switch, and fixed RBF kernel
% Modified by Mingsheng Long
% 2013(c)
% Mingsheng Long (longmingsheng@gmail.com), 2013

function K = kernel_jda(ker,X,X2,gamma)

switch ker
    case 'linear'
        if isempty(X2)
            K = X'*X;
        else
            K = X'*X2;
        end

    case 'rbf'
        n1sq = sum(X.^2,1);
        n1 = size(X,2);

        if isempty(X2)
            D = (ones(n1,1)*n1sq)' + ones(n1,1)*n1sq -2*(X'*X);
        else
            n2sq = sum(X2.^2,1);
            n2 = size(X2,2);
            D = (ones(n2,1)*n1sq)' + ones(n1,1)*n2sq -2*X'*X2;
        end
        K = exp(-gamma*D); 

    case 'sam'
        if isempty(X2)
            D = X'*X;
        else
            D = X'*X2;
        end
        K = exp(-gamma*acos(D).^2);

    otherwise
        error(['Unsupported kernel ' ker])
end

end

JDA方法的Matlab实现
将 JDA 方法包装成函数 MyJDA。
函数共接受 5 个输入参数:

  • X s r c \rm X_{src} Xsrc:源域的特征,大小为 n s × m n_s\times m ns×m
  • Y s r c \rm Y_{src} Ysrc:源域的标注,大小为 n s × 1 n_s\times 1 ns×1
  • X t a r \rm X_{tar} Xtar:目标域的特征,大小为 n t × m n_t\times m nt×m
  • Y t a r \rm Y_{tar} Ytar:目标域的标注,大小为 n t × 1 n_t\times 1 nt×1
  • options:参数结构体,它包含:
  • λ c \lambda c λc:平衡参数,可以自由给出
  • T T T:算法迭代次数
  • d i m dim dim:算法最终选择将数据降到多少维
  • k e r n e l t y p e kerneltype kerneltype:选择的核类型,可以选择RBF,线性、或无核
  • γ \gamma γ:如果选择RBF核,那么它的宽度为 γ \gamma γ

函数的输出包含3项:

  • a c c acc acc:算法的精度
  • a c c i t e r acc_{iter} acciter:算法每次迭代的精度,是一个一维数据
  • A A A:最终的变换矩阵

4、测试算法
使用如下代码对JDA算法进行测试(可以单独建立.m文件,也可以直接在命令行下顺序输入该代码,确保所有文件均在同一目录下):

options.T = 10;
options.gamma = 2;
options.kernel_type = 'linear';
options.lambda = 1.0;
options.dim = 20;
[Acc,Acc_iter,A] = MyJDA(Xs,Ys,Xt,Yt,options);
disp(Acc);

这里我将以上测试代码保存为test.m文件,在命令行窗口中敲下test或者软件上点运行。得到结果显示如下:
在这里插入图片描述
5、小结
通过以上过程,我们使用Matlab代码对JDA方法进行了实验,完成了一个迁移学习任务。其他的非深度迁移学习方法,均可以参考上面的过程。值得庆幸的是,许多论文的作者都公布了他们的文章代码,以方便我们进行接下来的研究。读者可以从Github或者相关作者的网站上获取其他许多方法的代码。

多说几句

运行在matlab R2017a,点击获取上面过程所有的代码
快速上手步骤:

  1. 打开matlab,定位到此文件夹。
  2. 运行uniform.m,归一化源和目标域。
  3. 运行test.m,可在工作区看到结果。
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值