@@ -49,6 +49,8 @@ \section{上手实践}
4949
5050在众多的非深度迁移学习方法中,我们选择最经典的迁移方法之一、发表于IEEE TNN 2011的TCA(Transfer Component Analysis)~\cite {pan2011domain }方法进行实践。为了便于学习,我们同时用Matlab和Python实现了此代码。代码的链接为\url {https://github.com/jindongwang/transferlearning/tree/master/code/traditional/TCA}。下面我们对代码进行简单讲解。
5151
52+ \subsection {TCA方法代码实现 }
53+
5254\subsubsection {Matlab }
5355
5456\textbf {1. 数据获取 }
@@ -254,7 +256,7 @@ \subsubsection{Python}
254256
255257与Matlab代码类似,我们也可以用Python对TCA进行实现,其主要依赖于Numpy和Scipy两个强大的科学计算库。Python版本的TCA代码如下:
256258
257- \ begin{lstlisting} [title=TCA方法的Matlab实现 , frame=shadowbox]
259+ \ begin{lstlisting} [title=TCA方法的Python实现 , frame=shadowbox]
258260
259261import numpy as np
260262import scipy.io
@@ -355,8 +357,205 @@ \subsubsection{Python}
355357
356358通过以上过程,我们分别使用Matlab代码和Python代码对经典的TCA方法进行了实验,完成了一个迁移学习任务。其他的非深度迁移学习方法,均可以参考上面的过程。值得庆幸的是,许多论文的作者都公布了他们的文章代码,以方便我们进行接下来的研究。读者可以从Github~\footnote {\url {https://github.com/jindongwang/transferlearning/tree/master/code}}或者相关作者的网站上获取其他许多方法的代码。
357359
358- % \subsection{深度网络的finetune}
360+ \subsection {深度网络的finetune代码实现 }
361+
362+ 本小节我们用Pytorch实现一个深度网络的finetune。Pytorch是一个较为流行的深度学习工具包,由Facebook进行开发,在Github~\footnote {\url {https://github.com/pytorch/pytorch}}上也进行了开源。
363+
364+ Finetune指的是训练好的深度网络,拿来在新的目标域上进行微调。因此,我们假定读者具有基本的Pytorch知识,直接给出finetune的代码。完整的代码可以在这里~\footnote {\url {https://github.com/jindongwang/transferlearning/tree/master/code/deep/finetune_AlexNet_ResNet}}找到。
365+
366+ 我们定义一个叫做finetune的函数,它接受输入的一个已有模型,从目标数据中进行微调,输出最好的模型其结果。其代码如下:
367+
368+ \ begin{lstlisting} [title=深度网络的finetune代码实现, frame=shadowbox]
369+
370+ def finetune(model, dataloaders, optimizer):
371+ since = time.time()
372+ best_acc = 0.0
373+ acc_hist = []
374+ criterion = nn.CrossEntropyLoss()
375+ for epoch in range(1, N_EPOCH + 1):
376+ # lr_schedule(optimizer, epoch)
377+ print('Learning rate: {:.8f}'.format(optimizer.param_groups[0]['lr']))
378+ print('Learning rate: {:.8f}'.format(optimizer.param_groups[-1]['lr']))
379+ for phase in ['src', 'val', 'tar']:
380+ if phase == 'src':
381+ model.train()
382+ else:
383+ model.eval()
384+ total_loss, correct = 0, 0
385+ for inputs, labels in dataloaders[phase]:
386+ inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)
387+ optimizer.zero_grad()
388+ with torch.set_grad_enabled(phase == 'src'):
389+ outputs = model(inputs)
390+ loss = criterion(outputs, labels)
391+ preds = torch.max(outputs, 1)[1]
392+ if phase == 'src':
393+ loss.backward()
394+ optimizer.step()
395+ total_loss += loss.item() * inputs.size(0)
396+ correct += torch.sum(preds == labels.data)
397+ epoch_loss = total_loss / len(dataloaders[phase].dataset)
398+ epoch_acc = correct.double() / len(dataloaders[phase].dataset)
399+ acc_hist.append([epoch_loss, epoch_acc])
400+ print('Epoch: [{:02d}/{:02d}]---{}, loss: {:.6f}, acc: {:.4f}'.format(epoch, N_EPOCH, phase, epoch_loss,
401+ epoch_acc))
402+ if phase == 'tar' and epoch_acc > best_acc:
403+ best_acc = epoch_acc
404+ print()
405+ fname = 'finetune_result' + model_name + \
406+ str(LEARNING_RATE) + str(args.source) + \
407+ '-' + str(args.target) + '.csv'
408+ np.savetxt(fname, np.asarray(a=acc_hist, dtype=float), delimiter=',',
409+ fmt='%.4f')
410+ time_pass = time.time() - since
411+ print('Training complete in {:.0f}m {:.0f}s'.format(
412+ time_pass // 60, time_pass % 60))
413+
414+ return model, best_acc, acc_hist
415+
416+ \end {lstlisting }
417+
418+ 其中,model可以是由任意深度网络训练好的模型,如Alexnet、Resnet等。
419+
420+ 另外,有很多任务也需要用到深度网络来提取深度特征以便进一步处理。我们也进行了实现,代码在\url {https://github.com/jindongwang/transferlearning/blob/master/code/feature_extractor}中。
421+
359422%
360- % \subsection{深度网络自适应}
423+ \subsection {深度网络自适应代码 }
424+
425+ 我们仍然以Pytorch为例,实现深度网络的自适应。具体地说,实现经典的DDC (Deep Domain Confusion)~\cite {tzeng2014deep }方法和与其类似的DCORAL (Deep CORAL)~\cite {sun2016deep }方法。
426+
427+ 此网络实现的核心是:如何正确计算DDC中的MMD损失、以及DCORAL中的CORAL损失,并且与神经网络进行集成。此部分对于初学者难免有一些困惑。如何输入源域和目标域、如何进行判断?因此,我们认为此部分应该是深度迁移学习的基础代码,读者应该努力地进行学习和理解。
428+
429+ \textbf {网络结构 }
430+
431+ 首先我们要定义好网络的架构,其应该是来自于已有的网络结构,如Alexnet和Resnet。但不同的是,由于要进行深度迁移适配,因此,输出层要和finetune一样,和目标的类别数相同。其二,由于要进行距离的计算,我们需要加一个叫做bottleneck的层,用来将最高维的特征进行降维,然后进行距离计算。当然,bottleneck层不加尚可。
432+
433+ 我们的网络结构如下所示:
434+
435+ \ begin{lstlisting} [title=深度迁移网络代码实现, frame=shadowbox]
436+ import torch.nn as nn
437+ import torchvision
438+ from Coral import CORAL
439+ import mmd
440+ import backbone
441+
442+
443+ class Transfer_Net(nn.Module):
444+ def __init__(self, num_class, base_net='resnet50', transfer_loss='mmd', use_bottleneck=True, bottleneck_width=256, width=1024):
445+ super(Transfer_Net, self).__init__()
446+ self.base_network = backbone.network_dict[base_net]()
447+ self.use_bottleneck = use_bottleneck
448+ self.transfer_loss = transfer_loss
449+ bottleneck_list = [nn.Linear(self.base_network.output_num(
450+ ), bottleneck_width), nn.BatchNorm1d(bottleneck_width), nn.ReLU(), nn.Dropout(0.5)]
451+ self.bottleneck_layer = nn.Sequential(*bottleneck_list)
452+ classifier_layer_list = [nn.Linear(self.base_network.output_num(), width), nn.ReLU(), nn.Dropout(0.5),
453+ nn.Linear(width, num_class)]
454+ self.classifier_layer = nn.Sequential(*classifier_layer_list)
455+
456+ self.bottleneck_layer[0].weight.data.normal_(0, 0.005)
457+ self.bottleneck_layer[0].bias.data.fill_(0.1)
458+ for i in range(2):
459+ self.classifier_layer[i * 3].weight.data.normal_(0, 0.01)
460+ self.classifier_layer[i * 3].bias.data.fill_(0.0)
461+
462+ def forward(self, source, target):
463+ source = self.base_network(source)
464+ target = self.base_network(target)
465+ source_clf = self.classifier_layer(source)
466+ if self.use_bottleneck:
467+ source = self.bottleneck_layer(source)
468+ target = self.bottleneck_layer(target)
469+ transfer_loss = self.adapt_loss(source, target, self.transfer_loss)
470+ return source_clf, transfer_loss
471+
472+ def predict(self, x):
473+ features = self.base_network(x)
474+ clf = self.classifier_layer(features)
475+ return clf
476+
477+ \end {lstlisting }
478+
479+ 其中Transfer Net是整个网络的模型定义。它接受参数有:
480+
481+ \begin {itemize }
482+ \item num class: 目标域类别数
483+ \item base net: 主干网络,例如Resnet等,也可以是自己定义的网络结构
484+ \item Transfer loss: 迁移的损失,比如MMD和CORAL,也可以是自己定义的损失
485+ \item use bottleneck: 是否使用bottleneck
486+ \item bottleneck width: bottleneck的宽度
487+ \item width: 分类器层的width
488+ \end {itemize }
489+
490+ \textbf {迁移损失定义 }
491+
492+ 迁移损失是核心。其定义如下:
493+
494+ \ begin{lstlisting} [title=深度迁移网络代码实现, frame=shadowbox]
495+
496+ def adapt_loss(self, X, Y, adapt_loss):
497+ """Compute adaptation loss, currently we support mmd and coral
498+ Arguments:
499+ X {tensor} -- source matrix
500+ Y {tensor} -- target matrix
501+ adapt_loss {string} -- loss type, 'mmd' or 'coral'. You can add your own loss
502+ Returns:
503+ [tensor] -- adaptation loss tensor
504+ """
505+ if adapt_loss == 'mmd':
506+ mmd_loss = mmd.MMD_loss()
507+ loss = mmd_loss(X, Y)
508+ elif adapt_loss == 'coral':
509+ loss = CORAL(X, Y)
510+ else:
511+ loss = 0
512+ return loss
513+ \end {lstlisting }
514+
515+ 其中的MMD和CORAL是自己实现的两个loss,MMD对应DDC方法,CORAL对应DCORAL方法。其代码在上述github中可以找到,我们不再赘述。
516+
517+ \textbf {训练 }
518+
519+ 训练时,我们一次输入一个batch的源域和目标域数据。为了方便,我们使用pytorch自带的dataloader。
520+
521+ \ begin{lstlisting} [title=深度迁移网络代码实现, frame=shadowbox]
522+ def train(source_loader, target_train_loader, target_test_loader, model, optimizer, CFG):
523+ len_source_loader = len(source_loader)
524+ len_target_loader = len(target_train_loader)
525+ train_loss_clf = utils.AverageMeter()
526+ train_loss_transfer = utils.AverageMeter()
527+ train_loss_total = utils.AverageMeter()
528+ for e in range(CFG['epoch']):
529+ model.train()
530+ iter_source, iter_target = iter(
531+ source_loader), iter(target_train_loader)
532+ n_batch = min(len_source_loader, len_target_loader)
533+ criterion = torch.nn.CrossEntropyLoss()
534+ for i in range(n_batch):
535+ data_source, label_source = iter_source.next()
536+ data_target, _ = iter_target.next()
537+ data_source, label_source = data_source.to(
538+ DEVICE), label_source.to(DEVICE)
539+ data_target = data_target.to(DEVICE)
540+
541+ optimizer.zero_grad()
542+ label_source_pred, transfer_loss = model(data_source, data_target)
543+ clf_loss = criterion(label_source_pred, label_source)
544+ loss = clf_loss + CFG['lambda'] * transfer_loss
545+ loss.backward()
546+ optimizer.step()
547+ train_loss_clf.update(clf_loss.item())
548+ train_loss_transfer.update(transfer_loss.item())
549+ train_loss_total.update(loss.item())
550+ if i % CFG['log_interval'] == 0:
551+ print('Train Epoch: [{}/{} ({:02d}%)], cls_Loss: {:.6f}, transfer_loss: {:.6f}, total_Loss: {:.6f}'.format(
552+ e + 1,
553+ CFG['epoch'],
554+ int(100. * i / n_batch), train_loss_clf.avg, train_loss_transfer.avg, train_loss_total.avg))
555+
556+ # Test
557+ test(model, target_test_loader)
558+ \end {lstlisting }
559+
361560%
362561% \subsection{深度对抗网络迁移}
0 commit comments