迁移学习(一)—— 基础与核心概念
Chen Kai BOSS

为什么一个在 ImageNet 上训练的模型能在医学影像上快速达到可用效果?为什么 BERT 预训练后可以在几百个样本上学会文本分类?这些现象的本质就是迁移学习——让模型将已有知识迁移到新问题上,而不是每次都从零开始。

在深度学习时代,迁移学习已成为标配而非选配。本文从数学形式化出发,系统讲解迁移学习的核心概念、分类体系、可行性分析以及负迁移问题,并通过 200+行完整代码展示特征迁移的实际应用。

为什么需要迁移学习

从头训练的困境

假设我们要训练一个医学影像诊断模型。传统的监督学习范式要求:

  1. 海量标注数据:深度神经网络通常需要数万到数百万标注样本才能达到良好泛化性能
  2. 巨大计算资源:从随机初始化训练大型模型需要数百到数千 GPU 小时
  3. 领域知识难以复用:即使相似任务(如 X 光片分类与 CT 影像分类)也需要独立训练模型

然而现实场景往往面临:

  • 数据稀缺:某些罕见疾病只有几百个病例
  • 标注昂贵:医学影像标注需要专业医生,成本极高
  • 时间紧迫:新疾病爆发时需要快速部署模型

这些矛盾催生了迁移学习的需求:能否利用已有的大规模数据训练的模型,快速适配到数据稀缺的新任务?

迁移学习的直觉

类比1:学乐器

场景:你已经会弹钢琴,现在想学小提琴

不迁移(从零开始): - 重新学习什么是音符 - 重新学习什么是节奏 - 重新学习如何读谱 - 预计:需要3年

迁移学习: - 音符知识:✅ 已经会了(直接复用) - 节奏感:✅ 已经有了(直接复用) - 读谱能力:✅ 钢琴谱和小提琴谱很像(稍作调整) - 手指技巧:❌ 完全不同(需要重新练) - 预计:只需1年(快了3倍!)

关键洞察:你把钢琴的通用音乐知识迁移到了小提琴上!

类比2:学编程语言

场景:你已经精通 Python,现在要学 Java

不迁移: - 重新学什么是变量、函数、循环 - 重新学算法和数据结构 - 重新学编程思维 - 预计:需要1年

迁移学习: - 编程思维:✅ 已经有了(循环、递归、面向对象) - 算法知识:✅ 都一样(排序、查找) - 数据结构:✅ 概念相同(列表、字典) - 语法细节:❌ 需要重新学(Java 的类型系统) - 预计:只需2个月

类比3:识别动物

场景:你见过很多猫,第一次见狮子

人脑的反应: - "诶,这家伙有胡须,像猫!" - "有爪子、有尾巴,也像猫!" - "虽然体型巨大,但应该是猫科动物!" - 结论:虽然是第一次见狮子,但你立即用"猫的知识"来理解它

深度学习中的迁移

人类的学习天然具备迁移能力:

  • 会骑自行车的人学摩托车更快
  • 懂 Python 的程序员学 Java 不会从零开始
  • 看过猫的人第一次见狮子也能认出"这是猫科动物"

这种能力源于共享的底层知识结构。类似地,深度神经网络的低层特征(边缘、纹理)在不同视觉任务间高度可复用,高层特征(语义概念)也存在一定相似性。迁移学习正是利用这种相似性。

在 ImageNet 训练的模型: - 低层:学会了识别边缘、纹理、颜色(通用特征) - 中层:学会了识别眼睛、耳朵、毛发(物体部件) - 高层:学会了识别猫、狗、鸟(完整物体)

迁移到医学影像: - 低层特征:✅ 边缘检测依然有用(复用) - 中层特征:⚠️ 部分有用(调整) - 高层特征:❌ 完全不同(重新训练)

这就是为什么迁移学习能大幅减少训练时间和数据需求!

迁移学习的核心思想

给定源域 和目标域 ,其中源域有大量标注数据 ,目标域数据稀缺。迁移学习的目标是:

利用 的知识,提升模型在 上的性能,尤其是在目标域数据有限时。

关键假设:源域和目标域之间存在某种相关性(但不要求完全相同),这种相关性使得知识迁移成为可能。

核心概念的形式化定义

域( Domain)

域定义为一个二元组:

其中: - 是特征空间,例如 表示 维实数向量空间 - 是特征空间上的边缘概率分布

示例: - 源域:自然图像( ImageNet),特征空间是 的 RGB 像素,分布 是自然场景的统计规律 - 目标域:医学 CT 影像,特征空间是 的灰度图,分布 与自然图像显著不同

任务( Task)

任务定义为:

其中: - 是标签空间 - 是预测函数(需要学习)

对于监督学习,任务还包含条件概率分布

示例: - 任务 1: ImageNet 1000 类分类, - 任务 2:肺炎二分类,

源域与目标域

迁移学习的设定:

  • 源域( Source Domain):,配合源任务
  • 目标域( Target Domain):,配合目标任务

关键差异: - (特征空间不同) - (边缘分布不同) - (标签空间不同) - (条件分布不同)

迁移学习不要求源域和目标域完全相同,这正是其价值所在。

迁移学习的数学定义

根据 Pan 和 Yang 在 2010 年的经典综述1,迁移学习的形式化定义为:

给定源域 和学习任务 ,目标域 和学习任务 ,迁移学习旨在利用 的知识,改进目标预测函数 的学习,其中:

性能改进的定量定义:设 为不使用迁移学习时的目标域错误率, 为使用迁移学习后的错误率,则要求:

或者等价地,使用更少的目标域标注数据 达到相同性能:

$$

n_T^{} < n_T^{} $$

迁移学习的分类体系

根据源域和目标域标注数据的可用性,迁移学习分为三大类2

归纳迁移学习( Inductive Transfer Learning)

定义:源任务和目标任务不同(),目标域有少量标注数据。

数学描述: - 源域: 标注充足 - 目标域: 标注稀缺() - 条件分布不同: 典型方法: 1. 多任务学习:源任务和目标任务同时优化 2. 自学习:利用目标域未标注数据的伪标签

应用场景: - ImageNet 预训练模型 → 医学影像分类(任务不同) - 通用语言模型 → 情感分析(任务不同)

直推迁移学习( Transductive Transfer Learning)

定义:源任务和目标任务相同(),但域不同(),目标域无标注数据。

数学描述: - 源域: 标注充足 - 目标域:只有 ,无标注 - 边缘分布不同: 典型方法: 1. 域适应( Domain Adaptation):对齐源域和目标域的特征分布 2. 样本重加权:调整源域样本权重使其接近目标域分布

应用场景: - 合成数据 → 真实数据(如 GTAV 游戏场景 → 真实街景) - 产品评论情感分析:书籍评论 → 电子产品评论

无监督迁移学习( Unsupervised Transfer Learning)

定义:源域和目标域都没有标注数据,迁移的是数据的内在结构。

数学描述: - 源域和目标域都只有 ,无标注 - 目标:学习特征表示或聚类结构

典型方法: 1. 自监督学习:通过代理任务学习通用特征(如旋转预测、对比学习) 2. 深度聚类:迁移聚类结构

应用场景: - 自然语言处理中的词向量迁移( Word2Vec 训练于通用语料,应用于特定领域) - 图像的自监督预训练( MoCo 、 SimCLR)

迁移学习的核心假设

相关性假设

迁移学习成立的前提是源域和目标域存在某种相关性。形式化为:

其中 是相似度度量, 是阈值。

常见相似度度量: 1. 特征空间相似度 的重叠程度 2. 分布散度: KL 散度、最大均值差异( MMD)、 Wasserstein 距离 3. 任务相关性:标签空间的语义相似度

共享表示假设

存在共享的特征表示 ,使得在潜在空间 中:

$$

P_S((X)) P_T((X)) $$

或者至少:

$$

D(P_S((X)) | P_T((X))) < D(P_S(X) | P_T(X)) $$

即潜在表示减小了域之间的分布差异。这是大多数深度迁移学习方法的理论基础。

负迁移问题

负迁移的定义

当源域知识不但没有提升、反而损害了目标域性能时,称为负迁移( Negative Transfer)3

即使用迁移学习后的错误率 比从零开始训练的错误率 还要高。

负迁移的原因

1. 域差异过大

如果源域和目标域的分布差异超过阈值:

$$

D(P_S | P_T) > D_{} $$

则源域的先验知识可能是误导性的

示例:用自然图像训练的模型迁移到手绘素描,由于纹理、颜色完全不同,预训练特征可能完全无效。

2. 任务冲突

源任务和目标任务存在内在冲突。设源任务最优解为 ,目标任务最优解为 ,若:

其中 是参数空间中的可容忍半径,则从 初始化可能陷入更差的局部最优。

示例:用英语情感分析模型迁移到中文,但中文的否定表达、反讽等语言现象与英语差异巨大。

3. 过拟合源域

模型在源域上过度拟合,学习了源域特有的噪声模式而非共性知识。形式化为:

$$

f_S(x) = f_{} (x) + f_{} (x) $$

如果迁移时带入了 ,则会引入偏差。

如何避免负迁移

  1. 度量域相似度:使用 MMD 、 A-distance 等指标预估迁移可行性
  2. 选择性迁移:只迁移底层通用特征,重新训练高层任务相关层
  3. 正则化约束:在目标域微调时加入正则项,限制参数偏离源域过远
  4. 集成方法:结合从头训练和迁移学习的预测,降低单一策略风险

迁移可行性的定量分析

Ben-David 界

Ben-David 等人4从理论角度分析了域适应的泛化界。设 为假设空间, 为假设空间诱导的散度,则目标域错误率满足:

其中: - 是假设 在源域上的错误率 - 是源域和目标域的-散度 - 是理想联合错误率

解读:目标域错误率由三项控制: 1. 源域错误率(可通过训练降低) 2. 域间散度(需要域适应技术减小) 3. 任务相关性(由问题本身决定,无法改变)

过大,迁移学习可能失效。

最大均值差异( MMD)

MMD 是衡量两个分布差异的常用指标5

其中 是特征映射, 是再生核希尔伯特空间( RKHS)。

在实践中, MMD 可用作域适应的损失函数:

通过最小化 MMD 对齐源域和目标域的特征分布。

迁移学习与相关概念的区别

迁移学习 vs 多任务学习

维度 迁移学习 多任务学习
目标 优化目标域性能 同时优化所有任务
训练方式 先源域后目标域(顺序) 同时训练(并行)
数据分布 源域和目标域可不同 通常假设任务间相关
典型应用 预训练-微调 共享编码器的多头网络

迁移学习 vs 元学习

维度 迁移学习 元学习
学习目标 迁移具体知识 学习如何学习
训练数据 单个或少数源域 大量不同任务
适应速度 需要一定微调 快速适应( few-shot)
理论框架 统计学习 优化理论

迁移学习 vs 域泛化

维度 迁移学习 域泛化
测试时信息 可访问目标域数据 目标域未知
方法论 域适应(利用目标域数据) 学习域不变特征
挑战 域对齐 泛化到未见域

完整代码实现:特征迁移示例

下面通过一个完整示例展示迁移学习的基本流程:在 Office-31 数据集上进行域适应,将 Amazon 域的模型迁移到 Webcam 域。

实验设置

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
"""
迁移学习完整实验:特征迁移与域适应
数据集: Office-31(模拟场景,使用 MNIST → USPS 作为替代)
方法:特征提取 + MMD 对齐 + 微调
"""

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.svm import SVC
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import seaborn as sns

# 设置随机种子
np.random.seed(42)
torch.manual_seed(42)

# ============================================================================
# 数据生成:模拟源域和目标域
# ============================================================================

def generate_source_domain(n_samples=1000):
"""
生成源域数据: 2 维特征, 2 分类
源域分布:两个高斯簇,分离良好
"""
# 类别 0:中心在(-2, -2)
X0 = np.random.randn(n_samples // 2, 2) * 0.5 + np.array([-2, -2])
y0 = np.zeros(n_samples // 2)

# 类别 1:中心在(2, 2)
X1 = np.random.randn(n_samples // 2, 2) * 0.5 + np.array([2, 2])
y1 = np.ones(n_samples // 2)

X = np.vstack([X0, X1])
y = np.hstack([y0, y1])

return X, y

def generate_target_domain(n_samples=200):
"""
生成目标域数据:特征空间相同,但分布不同
目标域分布:有旋转和偏移
"""
# 旋转矩阵: 45 度
theta = np.pi / 4
rotation = np.array([
[np.cos(theta), -np.sin(theta)],
[np.sin(theta), np.cos(theta)]
])

# 类别 0:中心在(-1, -1),有旋转
X0 = np.random.randn(n_samples // 2, 2) * 0.6 + np.array([-1, -1])
X0 = X0 @ rotation.T
y0 = np.zeros(n_samples // 2)

# 类别 1:中心在(1, 1),有旋转
X1 = np.random.randn(n_samples // 2, 2) * 0.6 + np.array([1, 1])
X1 = X1 @ rotation.T
y1 = np.ones(n_samples // 2)

X = np.vstack([X0, X1])
y = np.hstack([y0, y1])

return X, y

# 生成数据
X_source, y_source = generate_source_domain(1000)
X_target_train, y_target_train = generate_target_domain(50) # 少量标注
X_target_test, y_target_test = generate_target_domain(200) # 测试集

print(f"源域数据: {X_source.shape}, 标签: {y_source.shape}")
print(f"目标域训练: {X_target_train.shape}")
print(f"目标域测试: {X_target_test.shape}")

# ============================================================================
# 可视化数据分布
# ============================================================================

def visualize_domains(X_source, y_source, X_target, y_target, title="Domain Comparison"):
"""可视化源域和目标域的数据分布"""
fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# 源域
axes[0].scatter(X_source[y_source==0, 0], X_source[y_source==0, 1],
c='blue', alpha=0.6, label='Class 0')
axes[0].scatter(X_source[y_source==1, 0], X_source[y_source==1, 1],
c='red', alpha=0.6, label='Class 1')
axes[0].set_title('Source Domain')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# 目标域
axes[1].scatter(X_target[y_target==0, 0], X_target[y_target==0, 1],
c='blue', alpha=0.6, label='Class 0')
axes[1].scatter(X_target[y_target==1, 0], X_target[y_target==1, 1],
c='red', alpha=0.6, label='Class 1')
axes[1].set_title('Target Domain')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.suptitle(title)
plt.tight_layout()
plt.savefig('domain_comparison.png', dpi=150, bbox_inches='tight')
plt.close()

visualize_domains(X_source, y_source, X_target_test, y_target_test)

# ============================================================================
# 方法 1:无迁移(从头训练)
# ============================================================================

def train_from_scratch(X_train, y_train, X_test, y_test):
"""在目标域从头训练分类器"""
clf = SVC(kernel='rbf', gamma='auto')
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
acc = accuracy_score(y_test, y_pred)
return acc, clf

acc_scratch, clf_scratch = train_from_scratch(
X_target_train, y_target_train, X_target_test, y_target_test
)
print(f"\n 【无迁移】目标域测试准确率: {acc_scratch:.4f}")

# ============================================================================
# 方法 2:直接迁移(源域训练,目标域测试)
# ============================================================================

def direct_transfer(X_source, y_source, X_test, y_test):
"""源域训练,直接在目标域测试(不微调)"""
clf = SVC(kernel='rbf', gamma='auto')
clf.fit(X_source, y_source)
y_pred = clf.predict(X_test)
acc = accuracy_score(y_test, y_pred)
return acc, clf

acc_direct, clf_direct = direct_transfer(
X_source, y_source, X_target_test, y_target_test
)
print(f"【直接迁移】目标域测试准确率: {acc_direct:.4f}")

# ============================================================================
# 方法 3:特征迁移 + 微调
# ============================================================================

class FeatureExtractor(nn.Module):
"""特征提取器: 2 层 MLP"""
def __init__(self, input_dim=2, hidden_dim=32, output_dim=16):
super().__init__()
self.encoder = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, output_dim),
nn.ReLU()
)

def forward(self, x):
return self.encoder(x)

class Classifier(nn.Module):
"""分类器"""
def __init__(self, input_dim=16, num_classes=2):
super().__init__()
self.fc = nn.Linear(input_dim, num_classes)

def forward(self, x):
return self.fc(x)

def compute_mmd(x_source, x_target, kernel='rbf', gamma=1.0):
"""
计算最大均值差异( MMD)
"""
n_source = x_source.size(0)
n_target = x_target.size(0)

# 计算核矩阵
xx = torch.sum(x_source ** 2, dim=1, keepdim=True)
yy = torch.sum(x_target ** 2, dim=1, keepdim=True)

# 源域内核
K_ss = torch.exp(-gamma * (xx + xx.t() - 2 * x_source @ x_source.t()))
# 目标域内核
K_tt = torch.exp(-gamma * (yy + yy.t() - 2 * x_target @ x_target.t()))
# 跨域核
K_st = torch.exp(-gamma * (xx + yy.t() - 2 * x_source @ x_target.t()))

mmd = K_ss.sum() / (n_source ** 2) + K_tt.sum() / (n_target ** 2) - 2 * K_st.sum() / (n_source * n_target)
return mmd

def train_with_mmd(X_source, y_source, X_target_unlabeled,
X_target_labeled, y_target_labeled,
epochs=100, lambda_mmd=0.1):
"""
使用 MMD 进行域适应
"""
# 转换为 PyTorch 张量
X_s = torch.FloatTensor(X_source)
y_s = torch.LongTensor(y_source.astype(int))
X_t_unlabeled = torch.FloatTensor(X_target_unlabeled)
X_t_labeled = torch.FloatTensor(X_target_labeled)
y_t_labeled = torch.LongTensor(y_target_labeled.astype(int))

# 创建 DataLoader
source_dataset = TensorDataset(X_s, y_s)
source_loader = DataLoader(source_dataset, batch_size=32, shuffle=True)

# 初始化模型
feature_extractor = FeatureExtractor()
classifier = Classifier()

# 优化器
optimizer = optim.Adam(
list(feature_extractor.parameters()) + list(classifier.parameters()),
lr=0.001
)
criterion = nn.CrossEntropyLoss()

# 训练
losses = []
for epoch in range(epochs):
epoch_loss = 0
for X_batch, y_batch in source_loader:
optimizer.zero_grad()

# 源域分类损失
features_s = feature_extractor(X_batch)
logits_s = classifier(features_s)
loss_cls = criterion(logits_s, y_batch)

# MMD 对齐损失(使用目标域未标注数据)
features_t = feature_extractor(X_t_unlabeled)
loss_mmd = compute_mmd(features_s, features_t)

# 总损失
loss = loss_cls + lambda_mmd * loss_mmd

loss.backward()
optimizer.step()
epoch_loss += loss.item()

losses.append(epoch_loss / len(source_loader))

if (epoch + 1) % 20 == 0:
print(f"Epoch {epoch+1}/{epochs}, Loss: {losses[-1]:.4f}")

# 微调:使用少量目标域标注数据
print("\n 开始微调...")
for epoch in range(50):
optimizer.zero_grad()
features = feature_extractor(X_t_labeled)
logits = classifier(features)
loss = criterion(logits, y_t_labeled)
loss.backward()
optimizer.step()

if (epoch + 1) % 10 == 0:
print(f"Fine-tune Epoch {epoch+1}/50, Loss: {loss.item():.4f}")

return feature_extractor, classifier, losses

# 训练
feature_extractor, classifier, losses = train_with_mmd(
X_source, y_source,
X_target_test, # 目标域未标注(用于 MMD 对齐)
X_target_train, y_target_train, # 目标域标注(用于微调)
epochs=100,
lambda_mmd=0.5
)

# 测试
feature_extractor.eval()
classifier.eval()
with torch.no_grad():
X_test_tensor = torch.FloatTensor(X_target_test)
features_test = feature_extractor(X_test_tensor)
logits_test = classifier(features_test)
y_pred = torch.argmax(logits_test, dim=1).numpy()

acc_transfer = accuracy_score(y_target_test, y_pred)
print(f"\n 【特征迁移+MMD 】目标域测试准确率: {acc_transfer:.4f}")

# ============================================================================
# 性能对比可视化
# ============================================================================

def plot_performance_comparison():
"""绘制三种方法的性能对比"""
methods = ['从头训练\n(50 样本)', '直接迁移\n(无适应)', '特征迁移+MMD\n(域适应)']
accuracies = [acc_scratch, acc_direct, acc_transfer]

fig, ax = plt.subplots(figsize=(10, 6))
bars = ax.bar(methods, accuracies, color=['#ff6b6b', '#4ecdc4', '#45b7d1'], alpha=0.8)

# 添加数值标签
for bar, acc in zip(bars, accuracies):
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height,
f'{acc:.3f}',
ha='center', va='bottom', fontsize=12, fontweight='bold')

ax.set_ylabel('准确率', fontsize=12)
ax.set_title('迁移学习方法性能对比', fontsize=14, fontweight='bold')
ax.set_ylim(0, 1.0)
ax.grid(axis='y', alpha=0.3)

plt.tight_layout()
plt.savefig('performance_comparison.png', dpi=150, bbox_inches='tight')
plt.close()

plot_performance_comparison()

# ============================================================================
# 特征空间可视化
# ============================================================================

def visualize_feature_space():
"""可视化迁移前后的特征空间"""
feature_extractor.eval()

with torch.no_grad():
# 提取源域特征
X_s_tensor = torch.FloatTensor(X_source)
features_s = feature_extractor(X_s_tensor).numpy()

# 提取目标域特征
X_t_tensor = torch.FloatTensor(X_target_test)
features_t = feature_extractor(X_t_tensor).numpy()

# 使用 t-SNE 降维到 2D
features_all = np.vstack([features_s, features_t])
tsne = TSNE(n_components=2, random_state=42)
features_2d = tsne.fit_transform(features_all)

features_s_2d = features_2d[:len(features_s)]
features_t_2d = features_2d[len(features_s):]

# 绘图
fig, ax = plt.subplots(figsize=(10, 8))

# 源域
ax.scatter(features_s_2d[y_source==0, 0], features_s_2d[y_source==0, 1],
c='blue', marker='o', alpha=0.5, s=30, label='Source Class 0')
ax.scatter(features_s_2d[y_source==1, 0], features_s_2d[y_source==1, 1],
c='red', marker='o', alpha=0.5, s=30, label='Source Class 1')

# 目标域
ax.scatter(features_t_2d[y_target_test==0, 0], features_t_2d[y_target_test==0, 1],
c='blue', marker='^', alpha=0.8, s=50, label='Target Class 0')
ax.scatter(features_t_2d[y_target_test==1, 0], features_t_2d[y_target_test==1, 1],
c='red', marker='^', alpha=0.8, s=50, label='Target Class 1')

ax.set_title('特征空间可视化( t-SNE)', fontsize=14, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('feature_space_tsne.png', dpi=150, bbox_inches='tight')
plt.close()

visualize_feature_space()

# ============================================================================
# 混淆矩阵
# ============================================================================

def plot_confusion_matrices():
"""绘制三种方法的混淆矩阵"""
fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# 方法 1:从头训练
y_pred_scratch = clf_scratch.predict(X_target_test)
cm1 = confusion_matrix(y_target_test, y_pred_scratch)
sns.heatmap(cm1, annot=True, fmt='d', cmap='Blues', ax=axes[0])
axes[0].set_title(f'从头训练\nAcc: {acc_scratch:.3f}')
axes[0].set_xlabel('预测标签')
axes[0].set_ylabel('真实标签')

# 方法 2:直接迁移
y_pred_direct = clf_direct.predict(X_target_test)
cm2 = confusion_matrix(y_target_test, y_pred_direct)
sns.heatmap(cm2, annot=True, fmt='d', cmap='Greens', ax=axes[1])
axes[1].set_title(f'直接迁移\nAcc: {acc_direct:.3f}')
axes[1].set_xlabel('预测标签')
axes[1].set_ylabel('真实标签')

# 方法 3:特征迁移
cm3 = confusion_matrix(y_target_test, y_pred)
sns.heatmap(cm3, annot=True, fmt='d', cmap='Oranges', ax=axes[2])
axes[2].set_title(f'特征迁移+MMD\nAcc: {acc_transfer:.3f}')
axes[2].set_xlabel('预测标签')
axes[2].set_ylabel('真实标签')

plt.tight_layout()
plt.savefig('confusion_matrices.png', dpi=150, bbox_inches='tight')
plt.close()

plot_confusion_matrices()

print("\n" + "="*60)
print("实验总结:")
print("="*60)
print(f"1. 从头训练(仅 50 个目标域样本): {acc_scratch:.4f}")
print(f"2. 直接迁移(无域适应): {acc_direct:.4f}")
print(f"3. 特征迁移+MMD(域适应): {acc_transfer:.4f}")
print(f"\n 改进幅度: {(acc_transfer - acc_scratch) / acc_scratch * 100:.1f}%")
print("="*60)

代码说明

核心组件: 1. 数据生成:模拟源域和目标域的分布差异(旋转+平移) 2. MMD 计算:使用 RBF 核计算域间距离 3. 两阶段训练: - 阶段 1:源域分类 + MMD 对齐 - 阶段 2:目标域微调 4. 可视化:特征空间 t-SNE 、性能对比、混淆矩阵

关键参数: - lambda_mmd=0.5:控制域适应强度 - epochs=100:预训练轮数 - 微调轮数: 50(使用少量目标域标注数据)

常见问题解答

Q1: 迁移学习一定比从头训练好吗?

不一定。迁移学习的效果取决于: 1. 域相关性:源域和目标域越相似,效果越好 2. 数据量:目标域数据极少时(<100 样本),迁移学习优势明显;数据充足时(>10 万样本),从头训练可能更好 3. 任务相关性:任务差异过大会导致负迁移

经验法则:目标域数据 < 10% 源域数据时,优先考虑迁移学习。

Q2: 如何选择源域?

选择标准: 1. 领域相似性:视觉任务选 ImageNet, NLP 任务选 BERT/GPT 2. 数据规模:源域数据越多越好(百万级以上) 3. 任务相关性:分类任务迁移到分类,检测任务迁移到检测

可视化工具:使用 t-SNE 比较源域和目标域的特征分布, MMD < 0.1 通常可迁移。

Q3: 预训练模型的哪些层应该冻结?

通用规律: - CV 模型:冻结前 3-4 层(边缘、纹理特征),微调后面层 - NLP 模型:通常全部微调,但可降低学习率(源域 lr 的 1/10) - 小数据场景:只微调最后 1-2 层,避免过拟合

实验策略:逐层解冻( Progressive Unfreezing),从顶层开始逐步解冻更多层。

Q4: 如何判断是否发生负迁移?

检测方法: 1. 对比基线:迁移学习准确率 < 从头训练准确率 2. 损失曲线:微调时 loss 不降反升 3. 域距离: MMD 或 A-distance 过大(> 0.5)

补救措施: - 只迁移浅层特征 - 增大目标域数据权重 - 使用对抗域适应方法

Q5: 迁移学习如何处理标签空间不同的情况?

三种策略: 1. 零样本迁移:使用语义嵌入(如 Word2Vec)将标签映射到共享空间 2. 部分迁移:只迁移共享类别,忽略源域特有类别 3. 开放集迁移:引入"未知类",识别目标域新类别

示例: ImageNet( 1000 类)→ 医学影像( 5 类),保留分类器前 4096 维特征表示,替换最后的 softmax 层。

Q6: 如何评估迁移学习的效果?

评估指标: 1. 准确率提升:$ = {} - {} d_A = 2(1 - 2)$,其中 是域分类器错误率

Q7: 迁移学习与数据增强有何区别?

维度 迁移学习 数据增强
知识来源 外部源域 当前数据集
方法 模型初始化/特征对齐 样本变换
适用场景 数据稀缺 提升泛化
计算成本 需预训练 实时生成

两者可结合使用:先迁移学习获得好初始化,再用数据增强提升鲁棒性。

Q8: 跨模态迁移(如图像→文本)如何实现?

关键技术: 1. 共享嵌入空间:将图像和文本映射到同一向量空间( CLIP) 2. 对比学习:最大化匹配对相似度,最小化非匹配对相似度 3. 生成模型:使用 VAE/GAN 学习跨模态映射

损失函数:

其中 是图像特征, 是文本特征, 是温度参数。

Q9: 如何迁移学习到小设备(如手机)?

模型压缩 + 迁移学习: 1. 知识蒸馏:大模型(教师)→ 小模型(学生) 2. 剪枝:移除冗余参数 3. 量化: FP32 → INT8 4. 轻量架构: MobileNet 、 EfficientNet

流程:大模型预训练 → 蒸馏到小模型 → 目标域微调

Q10: 迁移学习的理论保证是什么?

Ben-David 界给出了泛化保证:

解读: - 第一项 :源域错误率(可优化) - 第二项:域散度(需域适应降低) - 第三项 :理想联合错误率(由问题本质决定)

理论启示:迁移学习成功需要源域训练好 + 域差异小 + 任务相关性高。

Q11: 如何应对灾难性遗忘?

当模型在目标域微调时忘记源域知识,称为灾难性遗忘。解决方法: 1. 弹性权重巩固( EWC):为重要参数加正则化 2. 渐进式微调:逐层解冻,保留底层特征 3. 记忆重放:混合少量源域数据一起训练 4. 知识蒸馏:保持预训练模型作为教师

Q12: 半监督迁移学习如何做?

结合未标注数据: 1. 伪标签:用源域模型给目标域未标注数据打标签 2. 一致性正则化:增强样本的预测应一致 3. 自训练:迭代式自我改进

损失函数:

小结

本文系统介绍了迁移学习的基础与核心概念:

  1. 动机:解决数据稀缺、训练昂贵问题
  2. 核心概念:域、任务、源域、目标域的形式化定义
  3. 分类体系:归纳迁移、直推迁移、无监督迁移
  4. 负迁移:原因、检测与避免方法
  5. 理论分析: Ben-David 界、 MMD 等可行性判据
  6. 实践代码:完整的特征迁移+MMD 域适应实现

迁移学习不是万能钥匙,但在数据稀缺、计算受限、快速部署的场景下,它是最有效的技术手段之一。下一章我们将深入探讨预训练与微调技术,涵盖从 ImageNet 到 BERT 的经典范式。

参考文献


  1. Pan, S. J., & Yang, Q. (2010). A survey on transfer learning. IEEE Transactions on Knowledge and Data Engineering, 22(10), 1345-1359.↩︎

  2. Weiss, K., Khoshgoftaar, T. M., & Wang, D. (2016). A survey of transfer learning. Journal of Big Data, 3(1), 1-40.↩︎

  3. Rosenstein, M. T., Marx, Z., Kaelbling, L. P., & Dietterich, T. G. (2005). To transfer or not to transfer. NIPS Workshop on Transfer Learning.↩︎

  4. Ben-David, S., Blitzer, J., Crammer, K., Kulesza, A., Pereira, F., & Vaughan, J. W. (2010). A theory of learning from different domains. Machine Learning, 79(1), 151-175.↩︎

  5. Gretton, A., Borgwardt, K. M., Rasch, M. J., Sch ö lkopf, B., & Smola, A. (2012). A kernel two-sample test. Journal of Machine Learning Research, 13, 723-773.↩︎

  • 本文标题:迁移学习(一)—— 基础与核心概念
  • 本文作者:Chen Kai
  • 创建时间:2024-11-03 09:00:00
  • 本文链接:https://www.chenk.top/%E8%BF%81%E7%A7%BB%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94-%E5%9F%BA%E7%A1%80%E4%B8%8E%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论