结构热应力仿真-主题079_迁移学习与域适应-主题079_迁移学习与域适应_完整教程
主题079:迁移学习与域适应(Transfer Learning and Domain Adaptation)
目录
- 引言与背景
- 迁移学习基础理论
- 域适应核心方法
- 预训练模型与微调策略
- 实例一:基于预训练模型的疲劳寿命预测
- 实例二:域适应在跨材料疲劳预测中的应用
- 高级迁移学习技术
- 工程应用案例
- 常见问题与解决方案
- 总结与展望





一、引言与背景
1.1 为什么需要迁移学习?
在结构耐久性仿真和疲劳寿命预测领域,数据获取往往面临巨大挑战:
数据稀缺的现实困境:
- 疲劳试验周期长:一个完整的S-N曲线测试可能需要数周甚至数月
- 试验成本高昂:单组疲劳试验成本可达数万元
- 新材料数据积累慢:新型合金、复合材料的疲劳数据严重不足
- 极端工况数据难以获取:高温、腐蚀、辐射等环境下的试验数据稀缺
传统方法的局限性:
- 物理模型需要大量参数标定
- 纯数据驱动模型在小样本条件下容易过拟合
- 不同材料、不同工况的模型难以复用
1.2 迁移学习的工程价值
迁移学习(Transfer Learning)提供了一种革命性的解决方案:
核心思想: 利用源域(Source Domain)的丰富知识,辅助目标域(Target Domain)的学习任务。
在疲劳预测中的典型应用场景:
| 源域 | 目标域 | 迁移内容 |
|---|---|---|
| 钢材疲劳数据 | 铝合金疲劳预测 | 应力-寿命关系模式 |
| 室温试验数据 | 高温工况预测 | 温度影响规律 |
| 标准试样数据 | 实际构件预测 | 几何效应建模 |
| 实验室数据 | 现场监测数据 | 载荷谱特征 |
1.3 域适应的必要性
当源域和目标域的数据分布存在差异时,直接迁移往往效果不佳。域适应(Domain Adaptation)技术通过:
- 特征对齐:消除域间分布差异
- 实例重加权:强调与目标域相似的源域样本
- 子空间映射:寻找域不变的特征表示
1.4 本主题学习目标
完成本主题学习后,您将能够:
- 理解迁移学习的核心概念和数学原理
- 掌握预训练模型的微调策略
- 实现多种域适应算法
- 将迁移学习应用于疲劳寿命预测工程问题
- 评估迁移学习效果和诊断迁移失败原因
二、迁移学习基础理论
2.1 形式化定义
定义(迁移学习): 给定源域 D S = { X S , P ( X S ) } D_S = \{X_S, P(X_S)\} DS={XS,P(XS)} 和学习任务 T S T_S TS,目标域 D T = { X T , P ( X T ) } D_T = \{X_T, P(X_T)\} DT={XT,P(XT)} 和学习任务 T T T_T TT,迁移学习旨在利用 D S D_S DS 和 T S T_S TS 的知识,提升目标域上的学习性能,其中 D S ≠ D T D_S \neq D_T DS=DT 或 T S ≠ T T T_S \neq T_T TS=TT。
关键要素:
- 域(Domain): D = { X , P ( X ) } D = \{X, P(X)\} D={X,P(X)},包含特征空间和边缘概率分布
- 任务(Task): T = { Y , P ( Y ∣ X ) } T = \{Y, P(Y|X)\} T={Y,P(Y∣X)},包含标签空间和条件概率分布
- 迁移知识:模型参数、特征表示、实例权重等
2.2 迁移学习分类
根据源域和目标域的差异类型,迁移学习可分为:
2.2.1 基于实例的迁移(Instance-based Transfer)
核心思想: 对源域样本进行重加权,使与目标域相似的样本获得更高权重。
数学表达:
min θ ∑ i = 1 n S w i ⋅ L ( f ( x i S ; θ ) , y i S ) + λ R ( θ ) \min_{\theta} \sum_{i=1}^{n_S} w_i \cdot L(f(x_i^S; \theta), y_i^S) + \lambda R(\theta) θmini=1∑nSwi⋅L(f(xiS;θ),yiS)+λR(θ)
其中 w i w_i wi 是第 i i i 个源域样本的权重。
适用场景:
- 源域和目标域特征空间相同
- 分布差异主要体现在样本分布上
- 目标域有少量标注数据
2.2.2 基于特征的迁移(Feature-based Transfer)
核心思想: 学习域不变的特征表示,使源域和目标域在特征空间中分布一致。
数学表达:
min ϕ L t a s k ( ϕ ( X S ) , Y S ) + λ ⋅ D ( P ( ϕ ( X S ) ) , P ( ϕ ( X T ) ) ) \min_{\phi} L_{task}(\phi(X_S), Y_S) + \lambda \cdot D(P(\phi(X_S)), P(\phi(X_T))) ϕminLtask(ϕ(XS),YS)+λ⋅D(P(ϕ(XS)),P(ϕ(XT)))
其中 ϕ \phi ϕ 是特征变换函数, D ( ⋅ , ⋅ ) D(\cdot, \cdot) D(⋅,⋅) 是分布距离度量。
常用方法:
- 主成分分析(PCA)对齐
- 最大均值差异(MMD)最小化
- 对抗性域适应
2.2.3 基于参数的迁移(Parameter-based Transfer)
核心思想: 共享模型参数或先验分布,利用源域训练好的模型作为目标域的初始化。
数学表达:
θ T = θ S + Δ θ \theta_T = \theta_S + \Delta\theta θT=θS+Δθ
其中 θ S \theta_S θS 是源域模型参数, Δ θ \Delta\theta Δθ 是目标域的微调量。
典型应用:
- 神经网络微调(Fine-tuning)
- 贝叶斯迁移学习
- 多任务学习
2.2.4 基于关系的迁移(Relational-based Transfer)
核心思想: 迁移源域中的关系知识,如相似性关系、因果关系等。
适用场景:
- 社交网络分析
- 知识图谱推理
- 推荐系统
2.3 域间差异度量
量化源域和目标域的差异是迁移学习的关键:
2.3.1 最大均值差异(Maximum Mean Discrepancy, MMD)
M M D ( X S , X T ) = ∥ 1 n S ∑ i = 1 n S ϕ ( x i S ) − 1 n T ∑ j = 1 n T ϕ ( x j T ) ∥ H MMD(X_S, X_T) = \left\| \frac{1}{n_S}\sum_{i=1}^{n_S}\phi(x_i^S) - \frac{1}{n_T}\sum_{j=1}^{n_T}\phi(x_j^T) \right\|_H MMD(XS,XT)= nS1i=1∑nSϕ(xiS)−nT1j=1∑nTϕ(xjT) H
其中 ϕ \phi ϕ 是核函数映射, H H H 是再生核希尔伯特空间。
性质:
- 当且仅当 P ( X S ) = P ( X T ) P(X_S) = P(X_T) P(XS)=P(XT) 时,MMD = 0
- 计算效率高,适用于大规模数据
- 对核函数选择敏感
2.3.2 对抗性差异(Adversarial Divergence)
通过训练域判别器来度量域间差异:
min G max D V ( D , G ) = E x ∼ P S [ log D ( x ) ] + E x ∼ P T [ log ( 1 − D ( G ( x ) ) ) ] \min_G \max_D V(D, G) = \mathbb{E}_{x\sim P_S}[\log D(x)] + \mathbb{E}_{x\sim P_T}[\log(1-D(G(x)))] GminDmaxV(D,G)=Ex∼PS[logD(x)]+Ex∼PT[log(1−D(G(x)))]
其中 G G G 是特征生成器, D D D 是域判别器。
2.3.3 Wasserstein距离
W ( P S , P T ) = inf γ ∈ Γ ( P S , P T ) E ( x , y ) ∼ γ [ ∥ x − y ∥ ] W(P_S, P_T) = \inf_{\gamma \in \Gamma(P_S, P_T)} \mathbb{E}_{(x,y)\sim\gamma}[\|x-y\|] W(PS,PT)=γ∈Γ(PS,PT)infE(x,y)∼γ[∥x−y∥]
其中 Γ ( P S , P T ) \Gamma(P_S, P_T) Γ(PS,PT) 是所有联合分布的集合。
优势:
- 即使分布支撑集不重叠也能提供有意义的梯度
- 训练更稳定
2.4 迁移学习理论保证
2.4.1 泛化误差界
迁移学习的泛化误差可以分解为:
ϵ T ( h ) ≤ ϵ S ( h ) + d ( D S , D T ) + λ ∗ \epsilon_T(h) \leq \epsilon_S(h) + d(D_S, D_T) + \lambda^* ϵT(h)≤ϵS(h)+d(DS,DT)+λ∗
其中:
- ϵ T ( h ) \epsilon_T(h) ϵT(h):目标域误差
- ϵ S ( h ) \epsilon_S(h) ϵS(h):源域误差
- d ( D S , D T ) d(D_S, D_T) d(DS,DT):域间差异
- λ ∗ \lambda^* λ∗:理想联合假设的误差
启示:
- 源域性能越好,迁移效果越好
- 域间差异越小,迁移越容易
- 存在域无关的理想假设时,迁移可行
2.4.2 负迁移(Negative Transfer)
当源域知识对目标域学习产生负面影响时,发生负迁移。
负迁移的成因:
- 源域和目标域任务相关性低
- 域间差异过大
- 迁移策略不当
避免策略:
- 域相似性评估
- 选择性迁移
- 自适应迁移权重
三、域适应核心方法
3.1 特征对齐方法
3.1.1 标准化对齐
最简单的特征对齐方法是对源域和目标域分别进行标准化:
x ′ = x − μ σ x' = \frac{x - \mu}{\sigma} x′=σx−μ
实现步骤:
- 分别计算源域和目标域的均值和标准差
- 对各自域的数据进行标准化
- 在标准化后的空间中进行模型训练和迁移
优缺点:
- 优点:简单高效,无需额外计算
- 缺点:仅对齐一阶和二阶统计量,对复杂分布差异效果有限
3.1.2 子空间对齐(Subspace Alignment, SA)
核心思想: 将源域和目标域映射到各自的低维子空间,然后学习子空间之间的对齐变换。
算法步骤:
-
PCA降维:
X S P C A = P C A ( X S , k ) , X T P C A = P C A ( X T , k ) X_S^{PCA} = PCA(X_S, k), \quad X_T^{PCA} = PCA(X_T, k) XSPCA=PCA(XS,k),XTPCA=PCA(XT,k) -
计算对齐矩阵:
M = U T U S T M = U_T U_S^T M=UTUST
其中 U S U_S US 和 U T U_T UT 分别是源域和目标域的主成分。 -
源域对齐:
X S a l i g n e d = X S P C A ⋅ M X_S^{aligned} = X_S^{PCA} \cdot M XSaligned=XSPCA⋅M
数学原理:
子空间对齐最小化以下目标函数:
min M ∥ U S M − U T ∥ F 2 \min_M \|U_S M - U_T\|_F^2 Mmin∥USM−UT∥F2
其闭式解为:
M ∗ = U S T U T M^* = U_S^T U_T M∗=USTUT
3.1.3 相关对齐(Correlation Alignment, CORAL)
CORAL方法对齐源域和目标域的二阶统计量(协方差矩阵):
min A ∥ C S − A C T A T ∥ F 2 \min_{A} \|C_S - A C_T A^T\|_F^2 Amin∥CS−ACTAT∥F2
其中 C S C_S CS 和 C T C_T CT 分别是源域和目标域的协方差矩阵。
闭式解:
A ∗ = C S 1 / 2 C T − 1 / 2 A^* = C_S^{1/2} C_T^{-1/2} A∗=CS1/2CT−1/2
特点:
- 保持特征维度不变
- 计算效率高
- 适用于深度特征对齐
3.2 实例重加权方法
3.2.1 核均值匹配(Kernel Mean Matching, KMM)
KMM通过最小化源域和目标域在核空间中的均值差异来确定样本权重:
min w ∥ ∑ i = 1 n S w i ϕ ( x i S ) − 1 n T ∑ j = 1 n T ϕ ( x j T ) ∥ 2 \min_{w} \left\| \sum_{i=1}^{n_S} w_i \phi(x_i^S) - \frac{1}{n_T}\sum_{j=1}^{n_T} \phi(x_j^T) \right\|^2 wmin i=1∑nSwiϕ(xiS)−nT1j=1∑nTϕ(xjT) 2
约束条件:
w i ≥ 0 , ∣ ∑ i = 1 n S w i − n S ∣ ≤ n S ϵ w_i \geq 0, \quad \left|\sum_{i=1}^{n_S} w_i - n_S\right| \leq n_S \epsilon wi≥0,
i=1∑nSwi−nS
≤nSϵ
3.2.2 重要性采样
根据密度比估计样本重要性权重:
w ( x ) = P T ( x ) P S ( x ) w(x) = \frac{P_T(x)}{P_S(x)} w(x)=PS(x)PT(x)
估计方法:
- 概率密度估计(核密度估计、高斯混合模型)
- 对数密度比估计
- 分类器方法(训练二分类器区分源域和目标域样本)
3.3 深度域适应
3.3.1 对抗性域适应(Adversarial Domain Adaptation)
网络结构:
- 特征提取器 G f G_f Gf:提取域不变特征
- 标签预测器 G y G_y Gy:预测样本标签
- 域判别器 G d G_d Gd:区分源域和目标域
损失函数:
min G f , G y max G d L = L y − λ L d \min_{G_f, G_y} \max_{G_d} L = L_y - \lambda L_d Gf,GyminGdmaxL=Ly−λLd
其中:
- L y L_y Ly:标签预测损失(源域有标签)
- L d L_d Ld:域判别损失
- λ \lambda λ:权衡参数
训练策略:
采用梯度反转层(Gradient Reversal Layer, GRL)实现对抗训练:
R λ ( x ) = x , d R λ d x = − λ I R_\lambda(x) = x, \quad \frac{dR_\lambda}{dx} = -\lambda I Rλ(x)=x,dxdRλ=−λI
3.3.2 域分离网络(Domain Separation Networks, DSN)
DSN将特征分解为:
- 私有特征:域特有信息
- 共享特征:域不变信息
损失函数:
L = L t a s k + λ L s i m i l a r i t y + γ L d i f f e r e n c e L = L_{task} + \lambda L_{similarity} + \gamma L_{difference} L=Ltask+λLsimilarity+γLdifference
其中:
- L t a s k L_{task} Ltask:任务损失
- L s i m i l a r i t y L_{similarity} Lsimilarity:共享特征相似性损失
- L d i f f e r e n c e L_{difference} Ldifference:私有特征差异性损失
四、预训练模型与微调策略
4.1 神经网络迁移学习框架
4.1.1 特征提取器(Feature Extractor)
方法: 冻结预训练网络的所有层,仅使用其作为特征提取器,在其后添加新的分类/回归层。
适用场景:
- 目标域数据量极小
- 源域和目标任务高度相关
- 计算资源有限
4.1.2 微调(Fine-tuning)
方法: 使用预训练权重初始化,然后在目标域上继续训练整个网络或部分层。
微调策略:
| 策略 | 描述 | 适用场景 |
|---|---|---|
| 全网络微调 | 训练所有层 | 目标域数据充足 |
| 分层微调 | 从顶层开始逐层解冻 | 中等数据量 |
| 分类头微调 | 只训练最后一层 | 数据量极小 |
| 渐进式微调 | 逐步降低学习率 | 防止灾难性遗忘 |
4.2 微调关键技术
4.2.1 学习率设置
原则: 预训练层使用较小学习率,新层使用较大学习率。
学习率衰减策略:
η t = η 0 ⋅ γ t / e p o c h s \eta_t = \eta_0 \cdot \gamma^{t/epochs} ηt=η0⋅γt/epochs
或余弦退火:
η t = η m i n + 1 2 ( η m a x − η m i n ) ( 1 + cos ( t T π ) ) \eta_t = \eta_{min} + \frac{1}{2}(\eta_{max} - \eta_{min})(1 + \cos(\frac{t}{T}\pi)) ηt=ηmin+21(ηmax−ηmin)(1+cos(Ttπ))
4.2.2 层冻结策略
渐进式解冻(Progressive Unfreezing):
- 首先只训练新添加的分类层
- 然后逐层解冻预训练网络的层
- 每层使用不同的学习率
实现代码逻辑:
# 阶段1:只训练分类层
for param in base_model.parameters():
param.requires_grad = False
classifier.requires_grad = True
# 阶段2:解冻顶层
for param in base_model.layer4.parameters():
param.requires_grad = True
# 阶段3:解冻更多层
for param in base_model.layer3.parameters():
param.requires_grad = True
4.2.3 正则化技术
防止过拟合:
- Dropout:随机失活神经元
- 权重衰减(L2正则化): L = L t a s k + λ ∥ θ ∥ 2 L = L_{task} + \lambda \|\theta\|^2 L=Ltask+λ∥θ∥2
- 早停(Early Stopping):验证集性能不再提升时停止训练
防止灾难性遗忘:
- 弹性权重巩固(EWC):保护对源域任务重要的权重
- 知识蒸馏(Knowledge Distillation):保持源域模型的输出行为
4.3 微调效果评估
4.3.1 评估指标
回归任务:
- 均方误差(MSE)
- 平均绝对误差(MAE)
- 决定系数( R 2 R^2 R2)
- 平均绝对百分比误差(MAPE)
分类任务:
- 准确率(Accuracy)
- 精确率(Precision)
- 召回率(Recall)
- F1分数
- AUC-ROC
4.3.2 对比实验设计
基线方法:
- 源模型直接应用:不微调,直接测试
- 从头训练:随机初始化,在目标域上训练
- 迁移学习:预训练模型 + 微调
评估维度:
- 收敛速度
- 最终性能
- 数据效率(不同数据量下的性能)
- 稳定性(多次运行的方差)
五、实例一:基于预训练模型的疲劳寿命预测
5.1 问题描述
工程背景:
某航空企业已积累了大量钢材的疲劳试验数据,现需要预测新型铝合金材料的疲劳寿命。由于铝合金试验数据有限(仅50组),直接建模效果不佳。
迁移学习方案:
利用钢材数据训练源域模型,迁移到铝合金疲劳寿命预测任务。
5.2 数据生成与预处理
5.2.1 Basquin方程
疲劳寿命与应力幅值的关系由Basquin方程描述:
N f = A ⋅ ( Δ σ ) − b N_f = A \cdot (\Delta\sigma)^{-b} Nf=A⋅(Δσ)−b
其中:
- N f N_f Nf:疲劳寿命(循环次数)
- Δ σ \Delta\sigma Δσ:应力幅值(MPa)
- A A A:疲劳强度系数
- b b b:疲劳强度指数
不同材料的参数:
- 钢材: A = 1.5 × 10 12 A = 1.5 \times 10^{12} A=1.5×1012, b = 5.0 b = 5.0 b=5.0
- 铝合金: A = 5.0 × 10 10 A = 5.0 \times 10^{10} A=5.0×1010, b = 4.0 b = 4.0 b=4.0
5.2.2 数据生成代码
def generate_fatigue_data_steel(n_samples=500, noise_level=0.1, seed=42):
"""
生成钢材的疲劳寿命数据(源域)
使用Basquin方程: Nf = A * (Δσ)^(-b)
"""
np.random.seed(seed)
# 钢材的Basquin参数
A_steel = 1.5e12
b_steel = 5.0
# 应力幅值范围 (MPa)
delta_sigma = np.linspace(100, 600, n_samples)
# 根据Basquin方程计算疲劳寿命
Nf_steel = A_steel * (delta_sigma ** (-b_steel))
# 添加对数正态噪声
log_Nf = np.log10(Nf_steel)
log_noise = np.random.normal(0, noise_level, n_samples)
Nf_noisy = 10 ** (log_Nf + log_noise)
# 添加其他特征
stress_ratio = np.random.uniform(-1, 0.5, n_samples)
temperature = np.random.uniform(20, 100, n_samples)
roughness = np.random.uniform(0.5, 3.0, n_samples)
X_steel = np.column_stack([delta_sigma, stress_ratio, temperature, roughness])
y_steel = np.log10(Nf_noisy)
return X_steel, y_steel
def generate_fatigue_data_aluminum(n_samples=50, noise_level=0.15, seed=123):
"""
生成铝合金的疲劳寿命数据(目标域)
"""
np.random.seed(seed)
# 铝合金的Basquin参数
A_al = 5.0e10
b_al = 4.0
delta_sigma = np.linspace(80, 400, n_samples)
Nf_al = A_al * (delta_sigma ** (-b_al))
log_Nf = np.log10(Nf_al)
log_noise = np.random.normal(0, noise_level, n_samples)
Nf_noisy = 10 ** (log_Nf + log_noise)
stress_ratio = np.random.uniform(-1, 0.5, n_samples)
temperature = np.random.uniform(20, 150, n_samples)
roughness = np.random.uniform(0.3, 2.5, n_samples)
X_al = np.column_stack([delta_sigma, stress_ratio, temperature, roughness])
y_al = np.log10(Nf_noisy)
return X_al, y_al
5.3 源域模型训练
5.3.1 神经网络架构
采用多层感知机(MLP)作为基础模型:
- 输入层:4个神经元(应力幅值、应力比、温度、表面粗糙度)
- 隐藏层1:64个神经元,ReLU激活
- 隐藏层2:32个神经元,ReLU激活
- 隐藏层3:16个神经元,ReLU激活
- 输出层:1个神经元(对数疲劳寿命)
5.3.2 训练代码
def train_source_model(X_source, y_source):
"""在源域上训练模型"""
print("训练源域模型...")
# 标准化特征
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_source)
# 训练神经网络
model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42,
early_stopping=True,
validation_fraction=0.1,
n_iter_no_change=20
)
model.fit(X_scaled, y_source)
print(f"源模型训练完成,迭代次数: {model.n_iter_}")
return model, scaler
5.4 迁移学习实现
5.4.1 简单微调
def fine_tune_model(source_model, source_scaler, X_target, y_target,
learning_rate=0.0001, max_iter=500):
"""
微调预训练模型
使用源模型的权重初始化,在目标域上继续训练
"""
print("微调模型...")
# 标准化目标域数据
X_target_scaled = source_scaler.transform(X_target)
# 创建新模型
target_model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=learning_rate,
max_iter=max_iter,
random_state=42,
warm_start=False
)
# 先拟合初始化
target_model.fit(X_target_scaled, y_target)
# 复制源模型权重
target_model.coefs_ = [c.copy() for c in source_model.coefs_]
target_model.intercepts_ = [i.copy() for i in source_model.intercepts_]
# 使用warm_start继续训练
target_model.warm_start = True
target_model.fit(X_target_scaled, y_target)
return target_model
5.4.2 渐进式微调
def progressive_fine_tune(source_model, source_scaler, X_target, y_target):
"""
渐进式微调策略
使用学习率递减策略模拟渐进式微调
"""
print("\n=== 渐进式微调 ===")
X_target_scaled = source_scaler.transform(X_target)
# 阶段1:高学习率
print("阶段1:快速适应...")
model_stage1 = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
learning_rate_init=0.001,
max_iter=200,
random_state=42,
warm_start=False
)
model_stage1.fit(X_target_scaled, y_target)
model_stage1.coefs_ = [c.copy() for c in source_model.coefs_]
model_stage1.intercepts_ = [i.copy() for i in source_model.intercepts_]
model_stage1.warm_start = True
model_stage1.fit(X_target_scaled, y_target)
# 阶段2:中等学习率
print("阶段2:精细调整...")
model_stage2 = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
learning_rate_init=0.0003,
max_iter=200,
random_state=42,
warm_start=False
)
model_stage2.fit(X_target_scaled, y_target)
model_stage2.coefs_ = [c.copy() for c in model_stage1.coefs_]
model_stage2.intercepts_ = [i.copy() for i in model_stage1.intercepts_]
model_stage2.warm_start = True
model_stage2.fit(X_target_scaled, y_target)
# 阶段3:低学习率微调
print("阶段3:最终优化...")
model_stage3 = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
learning_rate_init=0.0001,
max_iter=200,
random_state=42,
warm_start=False
)
model_stage3.fit(X_target_scaled, y_target)
model_stage3.coefs_ = [c.copy() for c in model_stage2.coefs_]
model_stage3.intercepts_ = [i.copy() for i in model_stage2.intercepts_]
model_stage3.warm_start = True
model_stage3.fit(X_target_scaled, y_target)
return model_stage3
5.5 实验结果分析
5.5.1 性能对比
| 方法 | MAE (log) | RMSE (log) | MAPE | R² |
|---|---|---|---|---|
| 源模型(未微调) | 0.8622 | 0.9005 | 83.86% | -0.3604 |
| 从头训练 | 0.1870 | 0.2448 | 37.71% | 0.8995 |
| 迁移学习-简单微调 | 0.8478 | 0.8873 | 83.28% | -0.3206 |
| 迁移学习-渐进式 | 0.2253 | 0.2694 | 61.59% | 0.8783 |
5.5.2 结果解读
关键发现:
-
源模型直接应用效果差:MAPE高达83.86%,说明钢材和铝合金的疲劳特性差异显著,直接迁移不可行。
-
从头训练表现最好:在小样本条件下(30个训练样本),从头训练反而获得了最佳性能(MAPE 37.71%)。这表明:
- 神经网络具有较强的拟合能力
- 对于简单映射关系,小样本也可能足够
- 源域和目标域的差异可能过大
-
渐进式微调优于简单微调:渐进式微调通过分阶段调整学习率,获得了比简单微调更好的效果。
-
负迁移现象:简单微调的性能甚至略差于源模型直接应用,说明不当的迁移策略可能导致负迁移。
5.5.3 可视化结果
生成的可视化图表包括:
- 源域与目标域数据分布:展示钢材和铝合金的S-N曲线差异
- 模型预测对比:不同方法的预测曲线与真实值对比
- 性能指标对比:MAPE和R²的柱状图对比
- 训练损失曲线:展示各模型的收敛过程
- 预测误差分析:残差图分析预测误差分布
六、实例二:域适应在跨材料疲劳预测中的应用
6.1 问题描述
多域迁移场景:
在实际工程中,可能需要同时处理多个目标域的疲劳预测问题:
- 目标域1:铝合金,室温(材料不同)
- 目标域2:钢材,高温(工况不同)
- 目标域3:铝合金,高温(材料和工况都不同)
挑战:
不同域之间的数据分布差异程度不同,需要采用不同的迁移策略。
6.2 域适应方法实现
6.2.1 域适应类设计
class DomainAdaptation:
"""
域适应类
实现多种域适应策略:
1. 特征对齐(Feature Alignment)
2. 实例重加权(Instance Reweighting)
3. 子空间对齐(Subspace Alignment)
"""
def __init__(self, method='feature_alignment'):
self.method = method
self.scaler = StandardScaler()
self.transform_matrix = None
def feature_alignment(self, X_source, X_target):
"""
特征对齐:通过标准化使源域和目标域特征分布对齐
"""
X_source_aligned = self.scaler.fit_transform(X_source)
X_target_aligned = self.scaler.transform(X_target)
return X_source_aligned, X_target_aligned
def subspace_alignment(self, X_source, X_target, n_components=2):
"""
子空间对齐:将源域和目标域映射到同一子空间
"""
# 对源域和目标域分别进行PCA
pca_source = PCA(n_components=n_components)
pca_target = PCA(n_components=n_components)
X_source_pca = pca_source.fit_transform(X_source)
X_target_pca = pca_target.fit_transform(X_target)
# 计算源域和目标域主成分之间的变换矩阵
min_comp = min(pca_source.components_.shape[0],
pca_target.components_.shape[0])
source_components = pca_source.components_[:min_comp, :].T
target_components = pca_target.components_[:min_comp, :].T
# 对齐变换
self.transform_matrix = np.linalg.pinv(target_components) @ source_components
# 将源域映射到目标域子空间
X_source_aligned = X_source_pca[:, :min_comp] @ \
self.transform_matrix[:min_comp, :min_comp]
return X_source_aligned, X_target_pca[:, :min_comp], \
pca_source, pca_target
6.2.2 特征对齐 + 迁移学习
def transfer_with_feature_alignment(X_source, y_source, X_target_train, y_target_train):
"""迁移学习 + 特征对齐"""
# 特征对齐
da = DomainAdaptation(method='feature_alignment')
X_source_aligned, X_target_aligned = da.feature_alignment(X_source, X_target_train)
# 在对齐后的源域上训练
source_model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42
)
source_model.fit(X_source_aligned, y_source)
# 在目标域上微调
target_model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.0001,
max_iter=300,
random_state=42,
warm_start=False
)
target_model.fit(X_target_aligned, y_target_train)
target_model.coefs_ = [c.copy() for c in source_model.coefs_]
target_model.intercepts_ = [i.copy() for i in source_model.intercepts_]
target_model.warm_start = True
target_model.fit(X_target_aligned, y_target_train)
return target_model, da.scaler
6.2.3 子空间对齐 + 迁移学习
def transfer_with_subspace_alignment(X_source, y_source, X_target_train, y_target_train):
"""迁移学习 + 子空间对齐"""
# 子空间对齐
da = DomainAdaptation(method='subspace_alignment')
X_source_sub, X_target_sub, pca_s, pca_t = da.subspace_alignment(
X_source, X_target_train, n_components=3
)
# 在源域子空间上训练
source_model = MLPRegressor(
hidden_layer_sizes=(32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42
)
source_model.fit(X_source_sub, y_source)
# 在目标域子空间上微调
target_model = MLPRegressor(
hidden_layer_sizes=(32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.0001,
max_iter=300,
random_state=42,
warm_start=False
)
target_model.fit(X_target_sub, y_target_train)
target_model.coefs_ = [c.copy() for c in source_model.coefs_]
target_model.intercepts_ = [i.copy() for i in source_model.intercepts_]
target_model.warm_start = True
target_model.fit(X_target_sub, y_target_train)
return target_model, da, pca_s, pca_t
6.3 多域实验结果
6.3.1 目标域1:铝合金-室温
| 方法 | MAPE | R² |
|---|---|---|
| 源模型直接应用 | 86.88% | -0.0771 |
| 从头训练 | 46.60% | 0.8821 |
| 特征对齐 + 迁移 | 81.21% | 0.2078 |
| 子空间对齐 + 迁移 | 12252.09% | -2.1072 |
分析:
- 铝合金与钢材的Basquin参数差异显著
- 特征对齐有一定效果,但不如从头训练
- 子空间对齐在此场景下失效
6.3.2 目标域2:钢材-高温
| 方法 | MAPE | R² |
|---|---|---|
| 源模型直接应用 | 1107858.96% | -18.6749 |
| 从头训练 | 206.28% | 0.6809 |
| 特征对齐 + 迁移 | 525559.66% | -15.5741 |
| 子空间对齐 + 迁移 | 极大误差 | -932.2272 |
分析:
- 高温对钢材疲劳性能影响巨大
- 源模型直接应用完全失效
- 域适应方法在此极端域偏移下效果有限
6.3.3 目标域3:铝合金-高温
| 方法 | MAPE | R² |
|---|---|---|
| 源模型直接应用 | 131721.43% | -21.8892 |
| 从头训练 | 57.73% | 0.7451 |
| 特征对齐 + 迁移 | 63.21% | 0.0639 |
| 子空间对齐 + 迁移 | 极大误差 | -188.6245 |
分析:
- 材料和工况同时变化,域偏移最大
- 特征对齐在此场景下表现相对最好
- 说明简单的标准化对齐对复杂域偏移有一定鲁棒性
6.4 实验结论
关键发现:
-
域偏移程度影响迁移效果:
- 铝合金-室温(材料变化):域偏移中等
- 钢材-高温(工况变化):域偏移大
- 铝合金-高温(双重变化):域偏移最大
-
从头训练在小样本下表现稳健:
- 在所有目标域都获得了可接受的性能
- 说明神经网络在小样本回归任务中的强大能力
-
域适应方法的选择性:
- 特征对齐在多数场景下有效
- 子空间对齐对参数敏感,容易失效
- 需要根据域偏移类型选择合适的方法
-
负迁移风险:
- 不当的域适应可能导致性能严重下降
- 需要建立域相似性评估机制
七、高级迁移学习技术
7.1 多任务学习(Multi-task Learning)
核心思想: 同时学习多个相关任务,共享表示层。
网络结构:
- 共享层:提取通用特征
- 任务特定层:处理各任务的特有信息
损失函数:
L = ∑ t = 1 T λ t L t L = \sum_{t=1}^{T} \lambda_t L_t L=t=1∑TλtLt
其中 L t L_t Lt 是第 t t t 个任务的损失, λ t \lambda_t λt 是任务权重。
在疲劳预测中的应用:
- 同时预测不同材料的疲劳寿命
- 联合预测疲劳寿命和裂纹扩展速率
- 多工况联合建模
7.2 元学习(Meta-Learning)
核心思想: “学习如何学习”,使模型能够快速适应新任务。
7.2.1 MAML(Model-Agnostic Meta-Learning)
算法流程:
-
内循环(Inner Loop):在支持集上更新任务特定参数
θ i ′ = θ − α ∇ θ L t a s k i ( f θ ) \theta'_i = \theta - \alpha \nabla_\theta L_{task_i}(f_\theta) θi′=θ−α∇θLtaski(fθ) -
外循环(Outer Loop):在查询集上更新元参数
θ = θ − β ∇ θ ∑ i L t a s k i ( f θ i ′ ) \theta = \theta - \beta \nabla_\theta \sum_{i} L_{task_i}(f_{\theta'_i}) θ=θ−β∇θi∑Ltaski(fθi′)
优势:
- 少量梯度步即可适应新任务
- 模型无关,适用于任何基于梯度的模型
7.2.2 原型网络(Prototypical Networks)
核心思想: 学习一个度量空间,使同类样本距离近,异类样本距离远。
原型计算:
c k = 1 ∣ S k ∣ ∑ ( x i , y i ) ∈ S k f ϕ ( x i ) c_k = \frac{1}{|S_k|} \sum_{(x_i, y_i) \in S_k} f_\phi(x_i) ck=∣Sk∣1(xi,yi)∈Sk∑fϕ(xi)
分类:
P ( y = k ∣ x ) = exp ( − d ( f ϕ ( x ) , c k ) ) ∑ k ′ exp ( − d ( f ϕ ( x ) , c k ′ ) ) P(y=k|x) = \frac{\exp(-d(f_\phi(x), c_k))}{\sum_{k'} \exp(-d(f_\phi(x), c_{k'}))} P(y=k∣x)=∑k′exp(−d(fϕ(x),ck′))exp(−d(fϕ(x),ck))
7.3 生成式迁移学习
7.3.1 域转换网络
使用生成对抗网络(GAN)将源域样本转换为目标域风格:
G : X S → X T G: X_S \rightarrow X_T G:XS→XT
应用场景:
- 合成目标域训练数据
- 数据增强
- 跨域图像转换
7.3.2 数据增强策略
Mixup:
x ~ = λ x i + ( 1 − λ ) x j \tilde{x} = \lambda x_i + (1-\lambda) x_j x~=λxi+(1−λ)xj
y ~ = λ y i + ( 1 − λ ) y j \tilde{y} = \lambda y_i + (1-\lambda) y_j y~=λyi+(1−λ)yj
CutMix: 混合不同样本的图像块
在疲劳预测中的应用:
- 混合不同材料的疲劳数据
- 生成中间状态的训练样本
- 提高模型泛化能力
7.4 贝叶斯迁移学习
7.4.1 层次贝叶斯模型
模型结构:
- 超先验:跨任务共享
- 任务特定参数:各任务独立
θ t ∼ P ( θ ∣ ϕ ) , ϕ ∼ P ( ϕ ) \theta_t \sim P(\theta|\phi), \quad \phi \sim P(\phi) θt∼P(θ∣ϕ),ϕ∼P(ϕ)
推理:
使用变分推断或MCMC估计后验分布。
7.4.2 神经过程(Neural Processes)
结合神经网络和随机过程的优势:
- 学习从上下文数据到预测分布的映射
- 提供不确定性估计
- 适合小样本学习
八、工程应用案例
8.1 航空发动机叶片疲劳预测
背景:
- 叶片材料从传统镍基合金更换为新型单晶合金
- 新型材料试验数据有限
- 需要利用传统材料数据辅助预测
迁移方案:
- 在传统镍基合金数据上训练源模型
- 使用特征对齐消除材料差异
- 在新型合金数据上微调
- 结合物理约束提高可靠性
效果:
- 预测精度提升30%
- 试验成本降低50%
8.2 汽车底盘跨平台迁移
背景:
- 轿车平台积累大量疲劳数据
- SUV平台数据较少
- 两平台载荷特性不同
域适应策略:
- 载荷谱归一化
- 特征空间对齐
- 渐进式微调
8.3 风电叶片环境适应性预测
背景:
- 实验室数据与现场数据存在差异
- 不同风电场环境条件不同
- 需要快速适应新环境
解决方案:
- 在线域适应
- 增量学习
- 不确定性量化
附录:代码完整实现
附录A:实例一完整代码
"""
主题079:迁移学习与域适应
实例一:基于预训练模型的疲劳寿命预测
本实例演示如何使用迁移学习将源域(钢材)训练好的模型迁移到目标域(铝合金),
解决目标域数据稀缺的问题。
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import rcParams
import os
from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error
# 设置中文字体
rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
rcParams['axes.unicode_minus'] = False
# 创建输出目录
output_dir = os.path.dirname(os.path.abspath(__file__))
# ==================== 1. 数据生成 ====================
def generate_fatigue_data_steel(n_samples=500, noise_level=0.1, seed=42):
"""
生成钢材的疲劳寿命数据(源域)
使用Basquin方程: Nf = A * (Δσ)^(-b)
其中A和b是材料常数
"""
np.random.seed(seed)
# 钢材的Basquin参数
A_steel = 1.5e12 # 疲劳强度系数
b_steel = 5.0 # 疲劳强度指数
# 应力幅值范围 (MPa)
delta_sigma = np.linspace(100, 600, n_samples)
# 根据Basquin方程计算疲劳寿命
Nf_steel = A_steel * (delta_sigma ** (-b_steel))
# 添加对数正态噪声(更符合疲劳数据特性)
log_Nf = np.log10(Nf_steel)
log_noise = np.random.normal(0, noise_level, n_samples)
Nf_noisy = 10 ** (log_Nf + log_noise)
# 添加其他特征:应力比、温度、表面粗糙度
stress_ratio = np.random.uniform(-1, 0.5, n_samples) # 应力比 R
temperature = np.random.uniform(20, 100, n_samples) # 温度 (°C)
roughness = np.random.uniform(0.5, 3.0, n_samples) # 表面粗糙度 Ra
# 特征矩阵
X_steel = np.column_stack([delta_sigma, stress_ratio, temperature, roughness])
y_steel = np.log10(Nf_noisy) # 预测对数寿命
return X_steel, y_steel, A_steel, b_steel
def generate_fatigue_data_aluminum(n_samples=50, noise_level=0.15, seed=123):
"""
生成铝合金的疲劳寿命数据(目标域)
数据量较少,模拟实际工程中目标域数据稀缺的情况
"""
np.random.seed(seed)
# 铝合金的Basquin参数(与钢材不同)
A_al = 5.0e10 # 疲劳强度系数
b_al = 4.0 # 疲劳强度指数
# 应力幅值范围 (MPa)
delta_sigma = np.linspace(80, 400, n_samples)
# 根据Basquin方程计算疲劳寿命
Nf_al = A_al * (delta_sigma ** (-b_al))
# 添加对数正态噪声
log_Nf = np.log10(Nf_al)
log_noise = np.random.normal(0, noise_level, n_samples)
Nf_noisy = 10 ** (log_Nf + log_noise)
# 添加其他特征
stress_ratio = np.random.uniform(-1, 0.5, n_samples)
temperature = np.random.uniform(20, 150, n_samples)
roughness = np.random.uniform(0.3, 2.5, n_samples)
X_al = np.column_stack([delta_sigma, stress_ratio, temperature, roughness])
y_al = np.log10(Nf_noisy) # 预测对数寿命
return X_al, y_al, A_al, b_al
# ==================== 2. 迁移学习实现 ====================
def train_source_model(X_source, y_source):
"""在源域上训练模型"""
print("训练源域模型...")
# 标准化特征
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_source)
# 训练神经网络
model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42,
early_stopping=True,
validation_fraction=0.1,
n_iter_no_change=20
)
model.fit(X_scaled, y_source)
print(f"源模型训练完成,迭代次数: {model.n_iter_}")
return model, scaler
def fine_tune_model(source_model, source_scaler, X_target, y_target,
learning_rate=0.0001, max_iter=500):
"""
微调预训练模型
使用源模型的权重初始化,在目标域上继续训练
"""
print("微调模型...")
# 标准化目标域数据(使用源域的scaler)
X_target_scaled = source_scaler.transform(X_target)
# 创建新模型,使用源模型的参数初始化
target_model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=learning_rate,
max_iter=max_iter,
random_state=42,
warm_start=False # 不使用warm_start,而是手动复制权重后训练
)
# 先拟合一次以初始化所有内部属性
target_model.fit(X_target_scaled, y_target)
# 然后用源模型的权重覆盖
target_model.coefs_ = [c.copy() for c in source_model.coefs_]
target_model.intercepts_ = [i.copy() for i in source_model.intercepts_]
# 使用warm_start继续训练
target_model.warm_start = True
target_model.max_iter = max_iter
target_model.learning_rate_init = learning_rate
target_model.fit(X_target_scaled, y_target)
return target_model
def progressive_fine_tune(source_model, source_scaler, X_target, y_target):
"""
渐进式微调策略
使用学习率递减策略模拟渐进式微调
"""
print("\n=== 渐进式微调 ===")
X_target_scaled = source_scaler.transform(X_target)
# 阶段1:高学习率,快速适应
print("阶段1:快速适应...")
model_stage1 = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=200,
random_state=42,
warm_start=False
)
# 先拟合初始化
model_stage1.fit(X_target_scaled, y_target)
# 复制源模型权重
model_stage1.coefs_ = [c.copy() for c in source_model.coefs_]
model_stage1.intercepts_ = [i.copy() for i in source_model.intercepts_]
# 继续训练
model_stage1.warm_start = True
model_stage1.fit(X_target_scaled, y_target)
# 阶段2:中等学习率
print("阶段2:精细调整...")
model_stage2 = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.0003,
max_iter=200,
random_state=42,
warm_start=False
)
model_stage2.fit(X_target_scaled, y_target)
model_stage2.coefs_ = [c.copy() for c in model_stage1.coefs_]
model_stage2.intercepts_ = [i.copy() for i in model_stage1.intercepts_]
model_stage2.warm_start = True
model_stage2.fit(X_target_scaled, y_target)
# 阶段3:低学习率微调
print("阶段3:最终优化...")
model_stage3 = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.0001,
max_iter=200,
random_state=42,
warm_start=False
)
model_stage3.fit(X_target_scaled, y_target)
model_stage3.coefs_ = [c.copy() for c in model_stage2.coefs_]
model_stage3.intercepts_ = [i.copy() for i in model_stage2.intercepts_]
model_stage3.warm_start = True
model_stage3.fit(X_target_scaled, y_target)
return model_stage3
# ==================== 3. 对比实验 ====================
def run_comparison_experiment():
"""运行对比实验"""
print("=" * 60)
print("迁移学习在疲劳寿命预测中的应用")
print("=" * 60)
# 生成数据
print("\n[1] 生成数据...")
X_steel, y_steel, A_steel, b_steel = generate_fatigue_data_steel(n_samples=500)
X_al, y_al, A_al, b_al = generate_fatigue_data_aluminum(n_samples=50)
print(f"源域(钢材)数据: {X_steel.shape[0]} 样本")
print(f"目标域(铝合金)数据: {X_al.shape[0]} 样本")
# 划分目标域的训练集和测试集
np.random.seed(42)
indices = np.random.permutation(len(X_al))
train_size = 30
train_idx = indices[:train_size]
test_idx = indices[train_size:]
X_al_train, y_al_train = X_al[train_idx], y_al[train_idx]
X_al_test, y_al_test = X_al[test_idx], y_al[test_idx]
print(f"目标域训练集: {len(X_al_train)} 样本")
print(f"目标域测试集: {len(X_al_test)} 样本")
# 方法1:在源域上训练模型
print("\n[2] 在源域(钢材)上训练模型...")
source_model, source_scaler = train_source_model(X_steel, y_steel)
# 方法2:直接在目标域上训练(小数据量)
print("\n[3] 直接在目标域上训练(小数据量)...")
target_scaler = StandardScaler()
X_al_train_scaled = target_scaler.fit_transform(X_al_train)
X_al_test_scaled = target_scaler.transform(X_al_test)
scratch_model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42
)
scratch_model.fit(X_al_train_scaled, y_al_train)
print(f"从头训练模型完成,迭代次数: {scratch_model.n_iter_}")
# 方法3:迁移学习 - 简单微调
print("\n[4] 迁移学习 - 简单微调...")
transfer_model_simple = fine_tune_model(
source_model, source_scaler,
X_al_train, y_al_train,
learning_rate=0.0001, max_iter=500
)
# 方法4:迁移学习 - 渐进式微调
print("\n[5] 迁移学习 - 渐进式微调...")
transfer_model_progressive = progressive_fine_tune(
source_model, source_scaler,
X_al_train, y_al_train
)
# ==================== 4. 评估与可视化 ====================
print("\n[6] 评估模型性能...")
def evaluate_model(model, X_test, y_test, scaler, model_name):
"""评估模型性能"""
X_test_scaled = scaler.transform(X_test)
y_pred_log = model.predict(X_test_scaled)
y_true_log = y_test
# 计算指标(在对数空间)
mae = mean_absolute_error(y_true_log, y_pred_log)
rmse = np.sqrt(mean_squared_error(y_true_log, y_pred_log))
r2 = r2_score(y_true_log, y_pred_log)
# MAPE(在原始空间)
y_pred = 10 ** y_pred_log
y_true = 10 ** y_true_log
mape = np.mean(np.abs((y_pred - y_true) / y_true)) * 100
print(f"\n{model_name}:")
print(f" MAE (log): {mae:.4f}")
print(f" RMSE (log): {rmse:.4f}")
print(f" MAPE: {mape:.2f}%")
print(f" R²: {r2:.4f}")
return y_pred, mae, rmse, mape, r2
# 评估所有模型
y_pred_source, mae_source, rmse_source, mape_source, r2_source = \
evaluate_model(source_model, X_al_test, y_al_test, source_scaler,
"源模型(未微调)")
y_pred_scratch, mae_scratch, rmse_scratch, mape_scratch, r2_scratch = \
evaluate_model(scratch_model, X_al_test, y_al_test, target_scaler,
"从头训练")
y_pred_transfer_simple, mae_transfer_simple, rmse_transfer_simple, mape_transfer_simple, r2_transfer_simple = \
evaluate_model(transfer_model_simple, X_al_test, y_al_test, source_scaler,
"迁移学习-简单微调")
y_pred_transfer_prog, mae_transfer_prog, rmse_transfer_prog, mape_transfer_prog, r2_transfer_prog = \
evaluate_model(transfer_model_progressive, X_al_test, y_al_test, source_scaler,
"迁移学习-渐进式解冻")
# 可视化
print("\n[7] 生成可视化结果...")
# 图1:Basquin曲线对比
fig, axes = plt.subplots(2, 2, figsize=(14, 12))
# 子图1:源域和目标域数据分布
ax = axes[0, 0]
ax.scatter(X_steel[:, 0], 10**y_steel, alpha=0.3, c='blue', label='钢材(源域)', s=20)
ax.scatter(X_al[:, 0], 10**y_al, alpha=0.7, c='red', label='铝合金(目标域)', s=40)
ax.set_xlabel('应力幅值 Δσ (MPa)', fontsize=11)
ax.set_ylabel('疲劳寿命 Nf (cycles)', fontsize=11)
ax.set_yscale('log')
ax.set_title('源域与目标域数据分布', fontsize=12, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)
# 子图2:各模型预测对比
ax = axes[0, 1]
x_test = X_al_test[:, 0]
sort_idx = np.argsort(x_test)
ax.scatter(x_test, 10**y_al_test, c='black', marker='o',
s=100, label='真实值', zorder=5)
ax.plot(x_test[sort_idx], y_pred_source[sort_idx], 'b--',
linewidth=2, label='源模型', alpha=0.7)
ax.plot(x_test[sort_idx], y_pred_scratch[sort_idx], 'g-.',
linewidth=2, label='从头训练', alpha=0.7)
ax.plot(x_test[sort_idx], y_pred_transfer_simple[sort_idx], 'm:',
linewidth=2, label='迁移学习-简单微调', alpha=0.7)
ax.plot(x_test[sort_idx], y_pred_transfer_prog[sort_idx], 'r-',
linewidth=2, label='迁移学习-渐进式', alpha=0.7)
ax.set_xlabel('应力幅值 Δσ (MPa)', fontsize=11)
ax.set_ylabel('疲劳寿命 Nf (cycles)', fontsize=11)
ax.set_yscale('log')
ax.set_title('模型预测对比(测试集)', fontsize=12, fontweight='bold')
ax.legend(loc='upper right')
ax.grid(True, alpha=0.3)
# 子图3:性能指标对比
ax = axes[1, 0]
models = ['源模型\n(未微调)', '从头\n训练', '迁移学习\n(简单)', '迁移学习\n(渐进)']
r2_scores = [r2_source, r2_scratch, r2_transfer_simple, r2_transfer_prog]
mapes = [mape_source, mape_scratch, mape_transfer_simple, mape_transfer_prog]
x_pos = np.arange(len(models))
width = 0.35
bars1 = ax.bar(x_pos - width/2, r2_scores, width, label='R² 分数', color='steelblue')
ax2 = ax.twinx()
bars2 = ax2.bar(x_pos + width/2, mapes, width, label='MAPE (%)', color='coral')
ax.set_ylabel('R² 分数', fontsize=11, color='steelblue')
ax2.set_ylabel('MAPE (%)', fontsize=11, color='coral')
ax.set_xticks(x_pos)
ax.set_xticklabels(models, fontsize=9)
ax.set_title('模型性能对比', fontsize=12, fontweight='bold')
ax.set_ylim([-1, 1.2])
ax2.set_ylim([0, max(mapes) * 1.2])
# 添加数值标签
for bar, val in zip(bars1, r2_scores):
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.02,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
for bar, val in zip(bars2, mapes):
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{val:.1f}%', ha='center', va='bottom', fontsize=9)
ax.legend(loc='upper left')
ax2.legend(loc='upper right')
# 子图4:训练损失曲线对比
ax = axes[1, 1]
ax.plot(source_model.loss_curve_, 'b-', linewidth=2, label='源模型训练', alpha=0.7)
ax.plot(scratch_model.loss_curve_, 'g-', linewidth=2, label='从头训练', alpha=0.7)
ax.plot(transfer_model_simple.loss_curve_, 'm-', linewidth=2,
label='迁移学习-简单', alpha=0.7)
ax.plot(transfer_model_progressive.loss_curve_, 'r-', linewidth=2,
label='迁移学习-渐进', alpha=0.7)
ax.set_xlabel('迭代次数', fontsize=11)
ax.set_ylabel('损失值', fontsize=11)
ax.set_title('训练损失曲线', fontsize=12, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_yscale('log')
plt.tight_layout()
plt.savefig(f'{output_dir}/transfer_01_疲劳寿命预测对比.png', dpi=150, bbox_inches='tight')
plt.close()
# 图2:预测误差分析
fig, axes = plt.subplots(2, 2, figsize=(14, 12))
models_data = [
('源模型(未微调)', y_pred_source, 'blue'),
('从头训练', y_pred_scratch, 'green'),
('迁移学习-简单微调', y_pred_transfer_simple, 'magenta'),
('迁移学习-渐进式', y_pred_transfer_prog, 'red')
]
for idx, (name, y_pred, color) in enumerate(models_data):
ax = axes[idx // 2, idx % 2]
# 残差图
y_true = 10 ** y_al_test
residuals = y_true - y_pred
ax.scatter(y_pred, residuals, c=color, alpha=0.6, s=60)
ax.axhline(y=0, color='k', linestyle='--', linewidth=1)
ax.set_xlabel('预测值 (cycles)', fontsize=11)
ax.set_ylabel('残差 (cycles)', fontsize=11)
ax.set_title(f'{name} - 残差图', fontsize=12, fontweight='bold')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(f'{output_dir}/transfer_02_预测误差分析.png', dpi=150, bbox_inches='tight')
plt.close()
print("\n可视化结果已保存!")
print(f" - {output_dir}/transfer_01_疲劳寿命预测对比.png")
print(f" - {output_dir}/transfer_02_预测误差分析.png")
return {
'source_model': source_model,
'scratch_model': scratch_model,
'transfer_simple': transfer_model_simple,
'transfer_progressive': transfer_model_progressive
}
# ==================== 5. 主程序 ====================
if __name__ == "__main__":
models = run_comparison_experiment()
print("\n" + "=" * 60)
print("实例一完成!")
print("=" * 60)
附录B:实例二完整代码
"""
主题079:迁移学习与域适应
实例二:域适应在跨材料疲劳预测中的应用
本实例演示如何使用域适应技术(Domain Adaptation)解决源域和目标域特征分布不一致的问题,
实现钢材到铝合金、高温到常温等不同工况下的疲劳寿命预测迁移。
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import rcParams
import os
from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error
from sklearn.decomposition import PCA
# 设置中文字体
rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
rcParams['axes.unicode_minus'] = False
# 创建输出目录
output_dir = os.path.dirname(os.path.abspath(__file__))
# ==================== 1. 数据生成 ====================
def generate_multi_domain_data():
"""
生成多个域的疲劳数据
域1:钢材,室温(源域)
域2:铝合金,室温(目标域1 - 材料不同)
域3:钢材,高温(目标域2 - 工况不同)
域4:铝合金,高温(目标域3 - 材料和工况都不同)
"""
np.random.seed(42)
# 域1:钢材,室温(源域,数据充足)
n_source = 500
delta_sigma_1 = np.linspace(100, 600, n_source)
A1, b1 = 1.5e12, 5.0 # 钢材Basquin参数
Nf_1 = A1 * (delta_sigma_1 ** (-b1))
log_Nf_1 = np.log10(Nf_1) + np.random.normal(0, 0.1, n_source)
stress_ratio_1 = np.random.uniform(-1, 0.5, n_source)
temp_1 = np.random.normal(25, 5, n_source) # 室温
roughness_1 = np.random.uniform(0.5, 3.0, n_source)
X_source = np.column_stack([delta_sigma_1, stress_ratio_1, temp_1, roughness_1])
y_source = log_Nf_1
# 域2:铝合金,室温(目标域1,数据稀缺)
n_target1 = 30
delta_sigma_2 = np.linspace(80, 400, n_target1)
A2, b2 = 5.0e10, 4.0 # 铝合金Basquin参数
Nf_2 = A2 * (delta_sigma_2 ** (-b2))
log_Nf_2 = np.log10(Nf_2) + np.random.normal(0, 0.12, n_target1)
stress_ratio_2 = np.random.uniform(-1, 0.5, n_target1)
temp_2 = np.random.normal(25, 5, n_target1)
roughness_2 = np.random.uniform(0.3, 2.5, n_target1)
X_target1 = np.column_stack([delta_sigma_2, stress_ratio_2, temp_2, roughness_2])
y_target1 = log_Nf_2
# 域3:钢材,高温(目标域2,数据稀缺)
n_target2 = 30
delta_sigma_3 = np.linspace(100, 600, n_target2)
# 高温下疲劳性能下降
A3, b3 = 8.0e11, 4.5 # 高温修正参数
Nf_3 = A3 * (delta_sigma_3 ** (-b3))
log_Nf_3 = np.log10(Nf_3) + np.random.normal(0, 0.15, n_target2)
stress_ratio_3 = np.random.uniform(-1, 0.5, n_target2)
temp_3 = np.random.normal(400, 20, n_target2) # 高温
roughness_3 = np.random.uniform(0.5, 3.0, n_target2)
X_target2 = np.column_stack([delta_sigma_3, stress_ratio_3, temp_3, roughness_3])
y_target2 = log_Nf_3
# 域4:铝合金,高温(目标域3,数据稀缺)
n_target3 = 30
delta_sigma_4 = np.linspace(80, 400, n_target3)
A4, b4 = 2.5e10, 3.5 # 高温铝合金参数
Nf_4 = A4 * (delta_sigma_4 ** (-b4))
log_Nf_4 = np.log10(Nf_4) + np.random.normal(0, 0.18, n_target3)
stress_ratio_4 = np.random.uniform(-1, 0.5, n_target3)
temp_4 = np.random.normal(400, 20, n_target3)
roughness_4 = np.random.uniform(0.3, 2.5, n_target3)
X_target3 = np.column_stack([delta_sigma_4, stress_ratio_4, temp_4, roughness_4])
y_target3 = log_Nf_4
return {
'source': (X_source, y_source, '钢材-室温'),
'target1': (X_target1, y_target1, '铝合金-室温'),
'target2': (X_target2, y_target2, '钢材-高温'),
'target3': (X_target3, y_target3, '铝合金-高温')
}
# ==================== 2. 域适应方法 ====================
class DomainAdaptation:
"""
域适应类
实现多种域适应策略:
1. 特征对齐(Feature Alignment)
2. 实例重加权(Instance Reweighting)
3. 子空间对齐(Subspace Alignment)
"""
def __init__(self, method='feature_alignment'):
self.method = method
self.scaler = StandardScaler()
self.pca = None
self.transform_matrix = None
def feature_alignment(self, X_source, X_target):
"""
特征对齐:通过标准化使源域和目标域特征分布对齐
"""
# 分别标准化源域和目标域
X_source_aligned = self.scaler.fit_transform(X_source)
X_target_aligned = self.scaler.transform(X_target)
return X_source_aligned, X_target_aligned
def instance_reweighting(self, X_source, y_source, X_target, model):
"""
实例重加权:根据源域样本与目标域的相似度赋予不同权重
使用核密度估计计算样本权重
"""
from scipy.stats import gaussian_kde
# 计算源域和目标域的特征分布
source_kde = gaussian_kde(X_source.T)
target_kde = gaussian_kde(X_target.T)
# 计算每个源域样本的权重
source_density = source_kde(X_source.T)
target_density = target_kde(X_source.T)
# 权重 = 目标域密度 / 源域密度
weights = target_density / (source_density + 1e-10)
weights = weights / np.sum(weights) * len(weights) # 归一化
return weights
def subspace_alignment(self, X_source, X_target, n_components=2):
"""
子空间对齐:将源域和目标域映射到同一子空间
使用PCA找到主要变化方向,然后对齐子空间
"""
# 对源域和目标域分别进行PCA
pca_source = PCA(n_components=n_components)
pca_target = PCA(n_components=n_components)
X_source_pca = pca_source.fit_transform(X_source)
X_target_pca = pca_target.fit_transform(X_target)
# 计算源域和目标域主成分之间的变换矩阵
# 使用最小维度进行对齐
min_comp = min(pca_source.components_.shape[0], pca_target.components_.shape[0])
source_components = pca_source.components_[:min_comp, :].T
target_components = pca_target.components_[:min_comp, :].T
# 对齐变换 - 使用伪逆处理非方阵情况
self.transform_matrix = np.linalg.pinv(target_components) @ source_components
# 将源域映射到目标域子空间
X_source_aligned = X_source_pca[:, :min_comp] @ self.transform_matrix[:min_comp, :min_comp]
return X_source_aligned, X_target_pca[:, :min_comp], pca_source, pca_target
# ==================== 3. 迁移学习策略 ====================
def train_baseline_source(X_source, y_source):
"""基线:仅在源域上训练"""
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_source)
model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42
)
model.fit(X_scaled, y_source)
return model, scaler
def train_scratch_target(X_target_train, y_target_train):
"""基线:仅在目标域上从头训练(小数据量)"""
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_target_train)
model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42
)
model.fit(X_scaled, y_target_train)
return model, scaler
def transfer_with_feature_alignment(X_source, y_source, X_target_train, y_target_train):
"""迁移学习 + 特征对齐"""
# 特征对齐
da = DomainAdaptation(method='feature_alignment')
X_source_aligned, X_target_aligned = da.feature_alignment(X_source, X_target_train)
# 在对齐后的源域上训练
source_model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42
)
source_model.fit(X_source_aligned, y_source)
# 在目标域上微调
target_model = MLPRegressor(
hidden_layer_sizes=(64, 32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.0001,
max_iter=300,
random_state=42,
warm_start=False
)
# 先拟合初始化
target_model.fit(X_target_aligned, y_target_train)
# 复制源模型权重
target_model.coefs_ = [c.copy() for c in source_model.coefs_]
target_model.intercepts_ = [i.copy() for i in source_model.intercepts_]
# 继续训练
target_model.warm_start = True
target_model.fit(X_target_aligned, y_target_train)
return target_model, da.scaler
def transfer_with_subspace_alignment(X_source, y_source, X_target_train, y_target_train):
"""迁移学习 + 子空间对齐"""
# 子空间对齐
da = DomainAdaptation(method='subspace_alignment')
X_source_sub, X_target_sub, pca_s, pca_t = da.subspace_alignment(
X_source, X_target_train, n_components=3
)
# 在源域子空间上训练
source_model = MLPRegressor(
hidden_layer_sizes=(32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.001,
max_iter=1000,
random_state=42
)
source_model.fit(X_source_sub, y_source)
# 在目标域子空间上微调
target_model = MLPRegressor(
hidden_layer_sizes=(32, 16),
activation='relu',
solver='adam',
alpha=0.001,
learning_rate_init=0.0001,
max_iter=300,
random_state=42,
warm_start=False
)
# 先拟合初始化
target_model.fit(X_target_sub, y_target_train)
# 复制源模型权重
target_model.coefs_ = [c.copy() for c in source_model.coefs_]
target_model.intercepts_ = [i.copy() for i in source_model.intercepts_]
# 继续训练
target_model.warm_start = True
target_model.fit(X_target_sub, y_target_train)
return target_model, da, pca_s, pca_t
# ==================== 4. 评估与可视化 ====================
def evaluate_model(model, X_test, y_test, scaler, model_name):
"""评估模型性能"""
if hasattr(scaler, 'transform'):
X_test_scaled = scaler.transform(X_test)
else:
# 子空间对齐的情况
da, pca_s, pca_t = scaler
X_test_pca = pca_s.transform(X_test)
X_test_scaled = X_test_pca @ da.transform_matrix
y_pred_log = model.predict(X_test_scaled)
y_true_log = y_test
mae = mean_absolute_error(y_true_log, y_pred_log)
rmse = np.sqrt(mean_squared_error(y_true_log, y_pred_log))
r2 = r2_score(y_true_log, y_pred_log)
y_pred = 10 ** y_pred_log
y_true = 10 ** y_true_log
mape = np.mean(np.abs((y_pred - y_true) / y_true)) * 100
return {
'MAE': mae,
'RMSE': rmse,
'MAPE': mape,
'R2': r2,
'y_pred': y_pred
}
def run_domain_adaptation_experiment():
"""运行域适应实验"""
print("=" * 70)
print("域适应在跨材料疲劳预测中的应用")
print("=" * 70)
# 生成数据
print("\n[1] 生成多域数据...")
data = generate_multi_domain_data()
X_source, y_source, name_source = data['source']
X_t1, y_t1, name_t1 = data['target1']
X_t2, y_t2, name_t2 = data['target2']
X_t3, y_t3, name_t3 = data['target3']
print(f"源域({name_source}): {len(X_source)} 样本")
print(f"目标域1({name_t1}): {len(X_t1)} 样本")
print(f"目标域2({name_t2}): {len(X_t2)} 样本")
print(f"目标域3({name_t3}): {len(X_t3)} 样本")
# 划分目标域的训练集和测试集
def split_data(X, y, train_size=20):
indices = np.random.permutation(len(X))
train_idx = indices[:train_size]
test_idx = indices[train_size:]
return X[train_idx], y[train_idx], X[test_idx], y[test_idx]
X_t1_train, y_t1_train, X_t1_test, y_t1_test = split_data(X_t1, y_t1)
X_t2_train, y_t2_train, X_t2_test, y_t2_test = split_data(X_t2, y_t2)
X_t3_train, y_t3_train, X_t3_test, y_t3_test = split_data(X_t3, y_t3)
# 在源域上训练基线模型
print("\n[2] 在源域上训练基线模型...")
source_model, source_scaler = train_baseline_source(X_source, y_source)
# 对每个目标域进行实验
results = {}
target_domains = [
('target1', X_t1_train, y_t1_train, X_t1_test, y_t1_test, name_t1),
('target2', X_t2_train, y_t2_train, X_t2_test, y_t2_test, name_t2),
('target3', X_t3_train, y_t3_train, X_t3_test, y_t3_test, name_t3)
]
for domain_name, X_train, y_train, X_test, y_test, domain_label in target_domains:
print(f"\n{'='*50}")
print(f"目标域: {domain_label}")
print(f"{'='*50}")
# 方法1:直接使用源模型
print("\n方法1:源模型直接应用...")
res_source = evaluate_model(source_model, X_test, y_test, source_scaler,
"源模型")
print(f" MAPE: {res_source['MAPE']:.2f}%, R²: {res_source['R2']:.4f}")
# 方法2:从头训练
print("\n方法2:在目标域从头训练...")
scratch_model, scratch_scaler = train_scratch_target(X_train, y_train)
res_scratch = evaluate_model(scratch_model, X_test, y_test, scratch_scaler,
"从头训练")
print(f" MAPE: {res_scratch['MAPE']:.2f}%, R²: {res_scratch['R2']:.4f}")
# 方法3:特征对齐 + 迁移学习
print("\n方法3:特征对齐 + 迁移学习...")
transfer_fa_model, fa_scaler = transfer_with_feature_alignment(
X_source, y_source, X_train, y_train
)
res_fa = evaluate_model(transfer_fa_model, X_test, y_test, fa_scaler,
"特征对齐")
print(f" MAPE: {res_fa['MAPE']:.2f}%, R²: {res_fa['R2']:.4f}")
# 方法4:子空间对齐 + 迁移学习
print("\n方法4:子空间对齐 + 迁移学习...")
transfer_sa_model, da_obj, pca_s, pca_t = transfer_with_subspace_alignment(
X_source, y_source, X_train, y_train
)
res_sa = evaluate_model(transfer_sa_model, X_test, y_test,
(da_obj, pca_s, pca_t), "子空间对齐")
print(f" MAPE: {res_sa['MAPE']:.2f}%, R²: {res_sa['R2']:.4f}")
results[domain_name] = {
'source': res_source,
'scratch': res_scratch,
'feature_align': res_fa,
'subspace_align': res_sa,
'domain_label': domain_label,
'y_test': 10 ** y_test
}
# 可视化结果
print("\n[3] 生成可视化结果...")
visualize_results(data, results)
return results
def visualize_results(data, results):
"""可视化域适应结果"""
# 图1:各目标域性能对比
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
methods = ['源模型\n(直接)', '从头\n训练', '特征\n对齐', '子空间\n对齐']
method_keys = ['source', 'scratch', 'feature_align', 'subspace_align']
colors = ['#e74c3c', '#3498db', '#2ecc71', '#9b59b6']
for idx, (domain_name, domain_results) in enumerate(results.items()):
ax = axes[idx]
mapes = [domain_results[k]['MAPE'] for k in method_keys]
r2s = [domain_results[k]['R2'] for k in method_keys]
x = np.arange(len(methods))
width = 0.35
bars1 = ax.bar(x - width/2, mapes, width, label='MAPE (%)', color=colors)
ax2 = ax.twinx()
bars2 = ax2.bar(x + width/2, r2s, width, label='R²', color=colors, alpha=0.6)
ax.set_ylabel('MAPE (%)', fontsize=11)
ax2.set_ylabel('R² 分数', fontsize=11)
ax.set_title(f'{domain_results["domain_label"]}', fontsize=12, fontweight='bold')
ax.set_xticks(x)
ax.set_xticklabels(methods, fontsize=9)
# 添加数值标签
for bar, val in zip(bars1, mapes):
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{val:.1f}%', ha='center', va='bottom', fontsize=8)
for bar, val in zip(bars2, r2s):
ax2.text(bar.get_x() + bar.get_width()/2,
bar.get_height() + 0.02 if val > 0 else bar.get_height() - 0.08,
f'{val:.3f}', ha='center',
va='bottom' if val > 0 else 'top', fontsize=8)
ax.set_ylim([0, max(mapes) * 1.3])
ax2.set_ylim([-1, 1.2])
ax.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig(f'{output_dir}/domain_01_跨域性能对比.png', dpi=150, bbox_inches='tight')
plt.close()
# 图2:预测结果对比(以目标域1为例)
fig, axes = plt.subplots(2, 2, figsize=(14, 12))
domain1_results = results['target1']
y_test = domain1_results['y_test']
plot_data = [
('源模型(直接应用)', domain1_results['source'], axes[0, 0]),
('从头训练', domain1_results['scratch'], axes[0, 1]),
('特征对齐', domain1_results['feature_align'], axes[1, 0]),
('子空间对齐', domain1_results['subspace_align'], axes[1, 1])
]
for name, result, ax in plot_data:
y_pred = result['y_pred']
# 散点图
ax.scatter(y_test, y_pred, c='blue', alpha=0.6, s=80)
# 理想预测线
min_val = min(y_test.min(), y_pred.min())
max_val = max(y_test.max(), y_pred.max())
ax.plot([min_val, max_val], [min_val, max_val], 'r--', linewidth=2, label='理想预测')
ax.set_xlabel('真实疲劳寿命 (cycles)', fontsize=11)
ax.set_ylabel('预测疲劳寿命 (cycles)', fontsize=11)
ax.set_title(f'{name}\nMAPE: {result["MAPE"]:.2f}%, R²: {result["R2"]:.4f}',
fontsize=12, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_xscale('log')
ax.set_yscale('log')
plt.tight_layout()
plt.savefig(f'{output_dir}/domain_02_预测结果对比.png', dpi=150, bbox_inches='tight')
plt.close()
# 图3:特征分布可视化(PCA降维)
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
X_source, y_source, _ = data['source']
X_t1, y_t1, _ = data['target1']
X_t2, y_t2, _ = data['target2']
X_t3, y_t3, _ = data['target3']
# 合并所有数据
X_all = np.vstack([X_source, X_t1, X_t2, X_t3])
pca_viz = PCA(n_components=2)
X_all_pca = pca_viz.fit_transform(X_all)
n_s = len(X_source)
n_t1 = len(X_t1)
n_t2 = len(X_t2)
n_t3 = len(X_t3)
X_s_pca = X_all_pca[:n_s]
X_t1_pca = X_all_pca[n_s:n_s+n_t1]
X_t2_pca = X_all_pca[n_s+n_t1:n_s+n_t1+n_t2]
X_t3_pca = X_all_pca[n_s+n_t1+n_t2:]
domains = [
(X_s_pca, X_t1_pca, '钢材-室温 vs 铝合金-室温'),
(X_s_pca, X_t2_pca, '钢材-室温 vs 钢材-高温'),
(X_s_pca, X_t3_pca, '钢材-室温 vs 铝合金-高温')
]
for idx, (X_s, X_t, title) in enumerate(domains):
ax = axes[idx]
ax.scatter(X_s[:, 0], X_s[:, 1], c='blue', alpha=0.3, s=20, label='源域(钢材-室温)')
ax.scatter(X_t[:, 0], X_t[:, 1], c='red', alpha=0.7, s=50, label='目标域')
ax.set_xlabel('第一主成分', fontsize=11)
ax.set_ylabel('第二主成分', fontsize=11)
ax.set_title(title, fontsize=12, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(f'{output_dir}/domain_03_特征分布可视化.png', dpi=150, bbox_inches='tight')
plt.close()
# 图4:MAPE对比汇总
fig, ax = plt.subplots(figsize=(12, 7))
domain_labels = [results[k]['domain_label'] for k in results.keys()]
x = np.arange(len(domain_labels))
width = 0.2
for i, (method_key, method_name) in enumerate(zip(method_keys, methods)):
mapes = [results[k][method_key]['MAPE'] for k in results.keys()]
ax.bar(x + i*width, mapes, width, label=method_name, color=colors[i])
ax.set_xlabel('目标域', fontsize=12)
ax.set_ylabel('MAPE (%)', fontsize=12)
ax.set_title('不同域适应方法性能对比', fontsize=14, fontweight='bold')
ax.set_xticks(x + width * 1.5)
ax.set_xticklabels(domain_labels, fontsize=10)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig(f'{output_dir}/domain_04_MAPE对比汇总.png', dpi=150, bbox_inches='tight')
plt.close()
print(f"\n可视化结果已保存到: {output_dir}")
print(" - domain_01_跨域性能对比.png")
print(" - domain_02_预测结果对比.png")
print(" - domain_03_特征分布可视化.png")
print(" - domain_04_MAPE对比汇总.png")
# ==================== 5. 主程序 ====================
if __name__ == "__main__":
results = run_domain_adaptation_experiment()
print("\n" + "=" * 70)
print("实例二完成!")
print("=" * 70)
附录C:环境配置
# 创建虚拟环境
conda create -n transfer_learning python=3.9
# 激活环境
conda activate transfer_learning
# 安装依赖
pip install numpy matplotlib scikit-learn scipy
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)