tsai 中 Learner 机制深度学习笔记

tsai 中 Learner 机制深度学习笔记


目录

  1. 问题动机与直觉理解
  2. 核心思想与概念拆解
  3. 数学建模与理论基础
  4. 优化方法与训练机制
  5. 代码实现与工程视角
  6. 进阶应用场景
  7. 批判性思考与扩展
  8. 小结与学习路径

1. 问题动机与直觉理解

1.1 从原始训练循环说起

想象你刚开始学习深度学习,写下了第一个训练循环:

python 复制代码
# 原始训练代码 - 混乱且重复
import torch
import torch.nn as nn
from torch.utils.data import DataLoader

model = MyTimeSeriesModel()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
train_loader = DataLoader(train_dataset, batch_size=32)
valid_loader = DataLoader(valid_dataset, batch_size=32)

# 训练循环
best_loss = float('inf')
for epoch in range(100):
    # 训练
    model.train()
    train_loss = 0
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.cuda(), target.cuda()
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()
    
    # 验证
    model.eval()
    valid_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in valid_loader:
            data, target = data.cuda(), target.cuda()
            output = model(data)
            loss = criterion(output, target)
            valid_loss += loss.item()
            pred = output.argmax(dim=1)
            correct += pred.eq(target).sum().item()
    
    # 保存最佳模型
    if valid_loss < best_loss:
        best_loss = valid_loss
        torch.save(model.state_dict(), 'best_model.pth')
    
    # 打印日志
    print(f'Epoch {epoch}: Train Loss: {train_loss:.4f}, '
          f'Valid Loss: {valid_loss:.4f}, Acc: {correct/len(valid_loader.dataset):.4f}')

这段代码有很多问题:

  1. 高度重复:每个项目都要重写这100行代码
  2. 容易出错 :忘记调用model.train()optimizer.zero_grad()
  3. 难以扩展:想加入学习率调度、梯度裁剪、混合精度训练?代码会更乱
  4. 缺乏标准化:不同人写的代码风格迥异,难以复用
  5. 调试困难:出问题时要排查大量代码

1.2 Learner的诞生:化繁为简

Learner的核心使命就是:把上面100行重复、易错的代码压缩成几行

使用tsai的Learner,上述代码可以简化为:

python 复制代码
from tsai.all import *

# 1. 准备数据
X, y, splits = get_UCR_data('NATOPS', split_data=False)
tfms = [None, TSClassification()]
dls = get_ts_dls(X, y, tfms=tfms, splits=splits, batch_tfms=TSStandardize())

# 2. 创建Learner(集成了模型、优化器、损失函数)
learn = ts_learner(dls, InceptionTime, metrics=accuracy)

# 3. 训练(自动处理训练/验证、日志、保存等)
learn.fit_one_cycle(100, 1e-3)

# 完成!模型已训练好,自动保存最佳版本

从100行到5行,这就是Learner的魔力。

1.3 真实应用场景

场景1:时间序列分类

你有传感器数据,需要分类不同的人体动作:

python 复制代码
from tsai.all import *

# 加载NATOPS人体动作识别数据
X, y, splits = get_UCR_data('NATOPS', split_data=False)
# X: [360 samples x 24 features x 51 timesteps]
# y: 6类动作标签

# 创建分类器 - Learner自动处理一切
clf = TSClassifier(X, y, splits=splits, 
                  arch='InceptionTimePlus',
                  metrics=accuracy)

# 训练 - 一行代码
clf.fit_one_cycle(50, 3e-4)

# 导出模型
clf.export('natops_classifier.pkl')
场景2:时间序列预测

预测未来的太阳黑子数量:

python 复制代码
from tsai.all import *

# 获取时间序列数据
ts = get_forecasting_time_series("Sunspots").values

# 滑动窗口:用60个时间步预测未来1步
X, y = SlidingWindow(60, horizon=1)(ts)

# 创建预测器
fcst = TSForecaster(X, y, 
                   arch="TSTPlus",
                   metrics=mae)

# 训练
fcst.fit_one_cycle(50, 1e-3)
场景3:迁移学习

在ImageNet预训练的模型上微调时间序列任务:

python 复制代码
# 加载预训练模型
learn = ts_learner(dls, ResNet, pretrained=True)

# 冻结前面的层
learn.freeze()

# 只训练最后几层
learn.fit_one_cycle(10, 1e-3)

# 解冻并微调全部参数
learn.unfreeze()
learn.fit_one_cycle(30, 1e-4)

1.4 为什么"非它不可"?

Learner不只是"语法糖",它提供了不可或缺的价值

价值1:经过验证的最佳实践

Learner内置了fastai社区多年积累的训练技巧:

  • 1cycle学习率调度:比固定学习率快3-10倍
  • 判别式学习率:不同层使用不同学习率
  • 混合精度训练:自动利用GPU的Tensor Core
  • 梯度累积:小显存也能用大batch size
价值2:统一的接口

无论是分类、回归还是预测,接口都一致:

python 复制代码
# 分类
clf = TSClassifier(X, y, ...)
clf.fit_one_cycle(50, 1e-3)

# 回归
reg = TSRegressor(X, y, ...)
reg.fit_one_cycle(50, 1e-3)

# 预测
fcst = TSForecaster(X, y, ...)
fcst.fit_one_cycle(50, 1e-3)

# 接口完全相同!
价值3:可组合的扩展性

通过Callback机制,Learner可以轻松扩展:

python 复制代码
learn = ts_learner(dls, model,
    cbs=[
        SaveModel(monitor='valid_loss'),  # 自动保存最佳模型
        EarlyStoppingCallback(patience=10),  # 早停
        ReduceLROnPlateau(patience=5),  # 学习率衰减
        MixUp(alpha=0.4),  # 数据增强
        GradientClip(max_norm=1.0)  # 梯度裁剪
    ]
)
价值4:实验追踪与可复现性
python 复制代码
# 自动记录超参数
learn.fit_one_cycle(50, 1e-3)

# 可视化训练过程
learn.plot_metrics()

# 导出完整的训练配置
learn.export('model.pkl')  # 包含模型、数据配置、训练历史

# 完全复现
learn = load_learner('model.pkl')
learn.predict(new_data)

2. 核心思想与概念拆解

2.1 Learner的本质

Learner本质上是一个"训练协调者"(Training Orchestrator),它:

  1. 持有训练所需的所有组件
  2. 协调这些组件的交互
  3. 自动化训练循环的复杂逻辑

用类比来理解:

Learner就像交响乐团的指挥

  • 模型是乐器
  • 数据是乐谱
  • 优化器是演奏技巧
  • Callback是特殊效果(渐强、渐弱、停顿)

指挥不直接演奏,但协调一切,让音乐和谐演绎。

2.2 Learner的核心组件

复制代码
┌─────────────────────────────────────────┐
│            Learner                       │
├─────────────────────────────────────────┤
│                                          │
│  ┌──────────┐  ┌──────────┐            │
│  │  Model   │  │  DataLoaders│          │
│  │          │  │  (train/valid)│        │
│  └──────────┘  └──────────┘            │
│                                          │
│  ┌──────────┐  ┌──────────┐            │
│  │Optimizer │  │Loss Func │            │
│  └──────────┘  └──────────┘            │
│                                          │
│  ┌──────────┐  ┌──────────┐            │
│  │ Metrics  │  │Callbacks │            │
│  └──────────┘  └──────────┘            │
│                                          │
│  ┌─────────────────────────┐           │
│  │  Training Loop Logic    │           │
│  │  (fit, fit_one_cycle)   │           │
│  └─────────────────────────┘           │
│                                          │
└─────────────────────────────────────────┘
组件1:DataLoaders (dls)

作用:提供训练和验证数据

python 复制代码
dls = get_ts_dls(
    X, y,
    tfms=tfms,              # 样本级变换
    splits=splits,          # 训练/验证划分
    batch_tfms=batch_tfms,  # 批次级变换
    bs=32                   # batch size
)

# dls包含两个DataLoader
dls.train  # 训练集DataLoader
dls.valid  # 验证集DataLoader
组件2:Model (model)

作用:神经网络架构

python 复制代码
# 方式1:传入架构名称,Learner自动实例化
learn = ts_learner(dls, InceptionTime)

# 方式2:传入已实例化的模型
model = InceptionTime(c_in=24, c_out=6)
learn = ts_learner(dls, model)
组件3:Optimizer (opt)

作用:参数更新策略

python 复制代码
from torch.optim import Adam, SGD

# 使用Adam优化器(默认)
learn = ts_learner(dls, model, opt_func=Adam)

# 使用SGD
learn = ts_learner(dls, model, opt_func=SGD, lr=0.01)
组件4:Loss Function (loss_func)

作用:衡量模型预测与真实值的差距

python 复制代码
# 分类任务(自动推断为CrossEntropyLoss)
learn = ts_learner(dls, model)

# 自定义损失函数
learn = ts_learner(dls, model, loss_func=FocalLoss())
组件5:Metrics (metrics)

作用:训练过程中监控的指标

python 复制代码
learn = ts_learner(dls, model, 
                  metrics=[accuracy, F1Score(), RocAuc()])
组件6:Callbacks (cbs)

作用:在训练循环的特定时刻执行自定义逻辑

python 复制代码
learn = ts_learner(dls, model,
    cbs=[
        SaveModel(monitor='valid_loss'),
        ShowGraph(),
        EarlyStoppingCallback(patience=10)
    ]
)

2.3 tsai中的两种Learner体系

tsai提供了两套Learner API:

体系1:传统Learner(继承自fastai)

特点:需要先创建DataLoaders,然后创建Learner

python 复制代码
# 步骤1:创建DataLoaders
dls = get_ts_dls(X, y, splits=splits, tfms=tfms, batch_tfms=batch_tfms)

# 步骤2:创建Learner
learn = ts_learner(dls, InceptionTime, metrics=accuracy)

# 步骤3:训练
learn.fit_one_cycle(50, 1e-3)

适用场景

  • 需要精细控制DataLoaders
  • 需要自定义复杂的数据增强
  • 想要完全兼容fastai生态
体系2:sklearn-like API(新API)

特点:一步到位,自动创建DataLoaders

python 复制代码
# 一步创建分类器(自动处理DataLoaders)
clf = TSClassifier(
    X, y,                    # 直接传入数据
    splits=splits,
    arch='InceptionTimePlus',
    tfms=tfms,
    batch_tfms=batch_tfms,
    metrics=accuracy
)

# 训练
clf.fit_one_cycle(50, 1e-3)

适用场景

  • 快速原型开发
  • sklearn用户更熟悉的接口
  • 不需要精细控制DataLoaders

对比

特性 传统Learner sklearn-like API
代码量 中等(需先创建dls) 少(一步到位)
灵活性 高(完全控制) 中等(自动推断)
学习曲线 陡峭(需理解fastai) 平缓(类似sklearn)
适用人群 fastai用户 sklearn用户、初学者

2.4 训练循环的抽象层次

Learner提供了多个层次的训练方法:

复制代码
高层API(推荐)
    ↓
fit_one_cycle(epochs, lr)  # 使用1cycle策略
    ↓
中层API
    ↓
fit(epochs, lr)  # 标准训练
fit_flat_cos(epochs, lr)  # 余弦退火
fine_tune(epochs, lr)  # 迁移学习微调
    ↓
低层API(罕用)
    ↓
_do_fit()  # 底层训练循环
_do_epoch_train()  # 单个epoch训练
_do_epoch_validate()  # 单个epoch验证

推荐使用fit_one_cycle,它集成了最佳实践。


3. 数学建模与理论基础

3.1 监督学习的数学表述

Learner解决的核心问题是监督学习

给定训练数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x N , y N ) } \mathcal{D} = \{(x_1, y_1), (x_2, y_2), ..., (x_N, y_N)\} D={(x1,y1),(x2,y2),...,(xN,yN)},其中:

  • x i ∈ R C × T x_i \in \mathbb{R}^{C \times T} xi∈RC×T:时间序列输入(C个通道,T个时间步)
  • y i ∈ Y y_i \in \mathcal{Y} yi∈Y:目标(分类标签或回归值)

我们的目标是找到一个函数 f θ : R C × T → Y f_\theta: \mathbb{R}^{C \times T} \rightarrow \mathcal{Y} fθ:RC×T→Y,使得:

θ ∗ = arg ⁡ min ⁡ θ E ( x , y ) ∼ D [ L ( f θ ( x ) , y ) ] \theta^* = \arg\min_\theta \mathbb{E}{(x,y) \sim \mathcal{D}} [L(f\theta(x), y)] θ∗=argθminE(x,y)∼D[L(fθ(x),y)]

其中:

  • θ \theta θ:模型参数
  • L L L:损失函数
  • E \mathbb{E} E:期望(在实践中用经验平均近似)

3.2 训练目标的分解

实际训练中,目标函数通常包含多项:

L total = L task + λ 1 L reg + λ 2 L aux \mathcal{L}{\text{total}} = \mathcal{L}{\text{task}} + \lambda_1 \mathcal{L}{\text{reg}} + \lambda_2 \mathcal{L}{\text{aux}} Ltotal=Ltask+λ1Lreg+λ2Laux

任务损失 ( L task \mathcal{L}_{\text{task}} Ltask)

分类任务 :交叉熵损失
L CE = − ∑ i = 1 N ∑ c = 1 C y i , c log ⁡ ( p i , c ) \mathcal{L}{\text{CE}} = -\sum{i=1}^N \sum_{c=1}^C y_{i,c} \log(p_{i,c}) LCE=−i=1∑Nc=1∑Cyi,clog(pi,c)

其中 p i , c = softmax ( f θ ( x i ) ) c p_{i,c} = \text{softmax}(f_\theta(x_i))_c pi,c=softmax(fθ(xi))c

回归任务 :均方误差
L MSE = 1 N ∑ i = 1 N ( f θ ( x i ) − y i ) 2 \mathcal{L}{\text{MSE}} = \frac{1}{N} \sum{i=1}^N (f_\theta(x_i) - y_i)^2 LMSE=N1i=1∑N(fθ(xi)−yi)2

预测任务 :MAE或RMSE
L MAE = 1 N ∑ i = 1 N ∣ f θ ( x i ) − y i ∣ \mathcal{L}{\text{MAE}} = \frac{1}{N} \sum{i=1}^N |f_\theta(x_i) - y_i| LMAE=N1i=1∑N∣fθ(xi)−yi∣

正则化项 ( L reg \mathcal{L}_{\text{reg}} Lreg)

L2正则化 (Weight Decay):
L L2 = λ 2 ∑ w ∈ θ w 2 \mathcal{L}{\text{L2}} = \frac{\lambda}{2} \sum{w \in \theta} w^2 LL2=2λw∈θ∑w2

在Learner中通过wd参数控制:

python 复制代码
learn = ts_learner(dls, model, wd=0.01)  # λ = 0.01
辅助损失 ( L aux \mathcal{L}_{\text{aux}} Laux)

例如:

  • MixUp损失:混合样本的损失
  • Label Smoothing:平滑标签的损失
  • Knowledge Distillation:教师-学生模型的KL散度

3.3 优化问题的数学形式

训练过程本质上是求解:

min ⁡ θ 1 N ∑ i = 1 N L ( f θ ( x i ) , y i ) + R ( θ ) \min_\theta \frac{1}{N} \sum_{i=1}^N L(f_\theta(x_i), y_i) + R(\theta) θminN1i=1∑NL(fθ(xi),yi)+R(θ)

使用小批量随机梯度下降(Mini-batch SGD):

θ t + 1 = θ t − η t ∇ θ L B ( θ t ) \theta_{t+1} = \theta_t - \eta_t \nabla_\theta L_B(\theta_t) θt+1=θt−ηt∇θLB(θt)

其中:

  • η t \eta_t ηt:学习率(时间依赖)
  • L B L_B LB:一个mini-batch上的损失
  • ∇ θ \nabla_\theta ∇θ:关于 θ \theta θ的梯度

3.4 训练集与验证集的分离

Learner维护两个数据集:

D = D train ∪ D valid \mathcal{D} = \mathcal{D}{\text{train}} \cup \mathcal{D}{\text{valid}} D=Dtrain∪Dvalid

训练集 ( D train \mathcal{D}_{\text{train}} Dtrain):

  • 用于计算梯度和更新参数
  • 损失记为 L train L_{\text{train}} Ltrain

验证集 ( D valid \mathcal{D}_{\text{valid}} Dvalid):

  • 用于评估泛化性能
  • 损失记为 L valid L_{\text{valid}} Lvalid
  • 不参与梯度计算

训练的终止条件通常基于验证集:

  • 早停: L valid L_{\text{valid}} Lvalid 连续N个epoch不下降
  • 保存最佳模型: θ ∗ = arg ⁡ min ⁡ t L valid ( θ t ) \theta^* = \arg\min_t L_{\text{valid}}(\theta_t) θ∗=argmintLvalid(θt)

3.5 Learner中的关键数学操作

前向传播

y ^ = f θ ( x ) \hat{y} = f_\theta(x) y^=fθ(x)

在代码中:

python 复制代码
pred = learn.model(xb)
损失计算

l = L ( y ^ , y ) l = L(\hat{y}, y) l=L(y^,y)

python 复制代码
loss = learn.loss_func(pred, yb)
反向传播

∂ l ∂ θ = ∂ l ∂ y ^ ⋅ ∂ y ^ ∂ θ \frac{\partial l}{\partial \theta} = \frac{\partial l}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial \theta} ∂θ∂l=∂y^∂l⋅∂θ∂y^

python 复制代码
loss.backward()  # 自动计算梯度
参数更新

θ ← θ − η ∇ θ l \theta \leftarrow \theta - \eta \nabla_\theta l θ←θ−η∇θl

python 复制代码
learn.opt.step()  # 更新参数
梯度清零

∇ θ l ← 0 \nabla_\theta l \leftarrow 0 ∇θl←0

python 复制代码
learn.opt.zero_grad()

4. 优化方法与训练机制

4.1 优化器家族

Learner支持PyTorch中的所有优化器,常用的有:

1. SGD(随机梯度下降)

θ t + 1 = θ t − η ∇ θ L ( θ t ) \theta_{t+1} = \theta_t - \eta \nabla_\theta L(\theta_t) θt+1=θt−η∇θL(θt)

python 复制代码
learn = ts_learner(dls, model, opt_func=SGD, lr=0.01)

特点

  • ✅ 简单、稳定
  • ❌ 收敛慢、对学习率敏感
2. SGD with Momentum

v t + 1 = μ v t + ∇ θ L ( θ t ) v_{t+1} = \mu v_t + \nabla_\theta L(\theta_t) vt+1=μvt+∇θL(θt)
θ t + 1 = θ t − η v t + 1 \theta_{t+1} = \theta_t - \eta v_{t+1} θt+1=θt−ηvt+1

python 复制代码
learn = ts_learner(dls, model, opt_func=SGD, lr=0.01, moms=(0.9,))

特点

  • ✅ 加速收敛
  • ✅ 减少震荡
3. Adam(Adaptive Moment Estimation)

m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ θ L ( θ t ) m_t = \beta_1 m_{t-1} + (1-\beta_1) \nabla_\theta L(\theta_t) mt=β1mt−1+(1−β1)∇θL(θt)
v t = β 2 v t − 1 + ( 1 − β 2 ) ( ∇ θ L ( θ t ) ) 2 v_t = \beta_2 v_{t-1} + (1-\beta_2) (\nabla_\theta L(\theta_t))^2 vt=β2vt−1+(1−β2)(∇θL(θt))2
θ t + 1 = θ t − η m t v t + ϵ \theta_{t+1} = \theta_t - \eta \frac{m_t}{\sqrt{v_t} + \epsilon} θt+1=θt−ηvt +ϵmt

python 复制代码
from torch.optim import Adam

learn = ts_learner(dls, model, opt_func=Adam, lr=0.001)

特点

  • ✅ 自适应学习率
  • ✅ 对超参数不敏感
  • ⚠️ 可能泛化性略差于SGD
4. AdamW(Adam with Weight Decay)

Adam的改进版本,正确实现了权重衰减:

θ t + 1 = ( 1 − λ η ) θ t − η m t v t + ϵ \theta_{t+1} = (1 - \lambda \eta) \theta_t - \eta \frac{m_t}{\sqrt{v_t} + \epsilon} θt+1=(1−λη)θt−ηvt +ϵmt

python 复制代码
from fastai.optimizer import *

learn = ts_learner(dls, model, opt_func=Adam, wd=0.01)  # fastai自动使用AdamW

推荐:默认使用Adam,它在大多数情况下表现良好。

4.2 学习率调度策略

学习率是最重要的超参数,Learner提供了多种调度策略。

策略1:1cycle Learning Rate(推荐)

核心思想:学习率先增后减,动量先减后增

复制代码
学习率:  /\
       /  \
      /    \___
     /
动量:  \  /
       \/
       /\
      /  \___

数学表达

在训练的前 p ⋅ E p \cdot E p⋅E 个epoch( p p p=0.3, E E E=总epoch数):

η t = η min ⁡ + t p E ( η max ⁡ − η min ⁡ ) \eta_t = \eta_{\min} + \frac{t}{pE}(\eta_{\max} - \eta_{\min}) ηt=ηmin+pEt(ηmax−ηmin)

在剩余的 ( 1 − p ) ⋅ E (1-p) \cdot E (1−p)⋅E 个epoch:

η t = η max ⁡ − t − p E ( 1 − p ) E ( η max ⁡ − η min ⁡ ) \eta_t = \eta_{\max} - \frac{t - pE}{(1-p)E}(\eta_{\max} - \eta_{\min}) ηt=ηmax−(1−p)Et−pE(ηmax−ηmin)

使用方法

python 复制代码
learn.fit_one_cycle(
    50,           # epochs
    1e-3,         # max_lr
    pct_start=0.3,  # 30%的时间用于warm-up
    div=25,       # max_lr / min_lr = 25
    div_final=1e4  # 最终lr = max_lr / 1e4
)

为什么有效?

  1. 初期大学习率:快速探索参数空间
  2. 中期降低学习率:逐步精调
  3. 后期极小学习率:精细收敛
策略2:余弦退火(Cosine Annealing)

η t = η min ⁡ + 1 2 ( η max ⁡ − η min ⁡ ) ( 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π))

python 复制代码
learn.fit_flat_cos(
    50,       # epochs
    1e-3,     # max_lr
    pct_start=0.75  # 前75%平稳,后25%退火
)
策略3:指数衰减

η t = η 0 ⋅ γ t \eta_t = \eta_0 \cdot \gamma^t ηt=η0⋅γt

python 复制代码
from fastai.callback.schedule import *

learn.fit(
    50, 1e-3,
    cbs=ParamScheduler('lr', SchedExp(1e-3, 1e-5))
)
策略4:自适应(ReduceLROnPlateau)

当验证集损失停止下降时,降低学习率:

python 复制代码
from fastai.callback.tracker import *

learn.fit(
    50, 1e-3,
    cbs=ReduceLROnPlateau(monitor='valid_loss', patience=5, factor=0.5)
)

4.3 判别式学习率(Discriminative Learning Rates)

核心思想:不同层使用不同的学习率

在迁移学习中,浅层(已预训练)应该用小学习率,深层(随机初始化)用大学习率。

η layer i = η base ⋅ k i \eta_{\text{layer}i} = \eta{\text{base}} \cdot k^i ηlayeri=ηbase⋅ki

其中 k < 1 k < 1 k<1 是衰减因子。

使用方法

python 复制代码
# 方式1:使用slice
learn.fit_one_cycle(10, slice(1e-5, 1e-3))
# 第一层:1e-5,最后一层:1e-3,中间层线性插值

# 方式2:冻结+解冻
learn.freeze()  # 冻结所有层
learn.fit_one_cycle(5, 1e-3)  # 只训练最后一层

learn.unfreeze()  # 解冻
learn.fit_one_cycle(20, slice(1e-5, 1e-3))  # 用判别式学习率训练所有层

4.4 梯度处理技术

1. 梯度裁剪(Gradient Clipping)

防止梯度爆炸:

g t = { g t if ∥ g t ∥ ≤ τ τ g t ∥ g t ∥ otherwise g_t = \begin{cases} g_t & \text{if } \|g_t\| \leq \tau \\ \tau \frac{g_t}{\|g_t\|} & \text{otherwise} \end{cases} gt={gtτ∥gt∥gtif ∥gt∥≤τotherwise

python 复制代码
from fastai.callback.training import *

learn = ts_learner(dls, model, 
                  cbs=GradientClip(max_norm=1.0))
2. 梯度累积(Gradient Accumulation)

模拟更大的batch size:

python 复制代码
learn = ts_learner(dls, model, 
                  cbs=GradientAccumulation(n_acc=4))
# 相当于batch_size × 4

4.5 训练技巧

1. 学习率查找器(LR Finder)

自动找到最佳学习率:

python 复制代码
learn = ts_learner(dls, model)

# 学习率范围测试
learn.lr_find()
# 会绘制loss vs lr曲线,选择loss下降最快的点

原理

  1. 从极小的学习率开始
  2. 每个batch指数增加学习率
  3. 记录对应的loss
  4. 选择loss下降最陡的点
2. 混合精度训练(Mixed Precision)

使用FP16加速训练:

python 复制代码
learn = ts_learner(dls, model).to_fp16()

好处

  • 训练速度提升2-3倍(在支持的GPU上)
  • 显存占用减半
  • 几乎不损失精度

原理

  • 前向传播用FP16
  • 梯度用FP32累积
  • 动态调整loss缩放防止下溢
3. 批量大小调优

一般原则

  • GPU显存允许的情况下,越大越好
  • 但太大会影响泛化(需降低学习率)
python 复制代码
# 线性缩放规则
# 如果batch_size翻倍,学习率也翻倍
bs_original = 32
bs_new = 64
lr_original = 1e-3
lr_new = lr_original * (bs_new / bs_original)  # 2e-3

5. 代码实现与工程视角

5.1 创建Learner的完整流程

流程1:使用传统API
python 复制代码
from tsai.all import *

# ============= 步骤1:准备数据 =============
# 加载数据
X, y, splits = get_UCR_data('NATOPS', split_data=False)

# X shape: [360, 24, 51]  (样本数, 特征数, 时间步)
# y shape: [360]  (6个类别的标签)
# splits: ([0,1,...,179], [180,181,...,359])  训练/验证索引

# ============= 步骤2:定义变换 =============
# 样本级变换(应用于每个样本)
tfms = [None, TSClassification()]
# None: 输入不变换
# TSClassification(): 将标签转为类别

# 批次级变换(应用于整个batch)
batch_tfms = [
    TSStandardize(by_sample=True),  # 标准化
    TSMagScale(sigma=0.1, p=0.5)    # 数据增强
]

# ============= 步骤3:创建DataLoaders =============
dls = get_ts_dls(
    X, y,
    tfms=tfms,
    splits=splits,
    batch_tfms=batch_tfms,
    bs=64,                # batch size
    num_workers=0         # 数据加载线程数
)

# ============= 步骤4:创建Learner =============
learn = ts_learner(
    dls,                           # DataLoaders
    InceptionTime,                 # 模型架构
    metrics=[accuracy, F1Score()], # 评估指标
    loss_func=LabelSmoothingCrossEntropy(),  # 损失函数
    opt_func=Adam,                 # 优化器
    lr=1e-3,                       # 初始学习率
    wd=0.01,                       # 权重衰减
    cbs=[                          # Callbacks
        SaveModel(monitor='valid_loss'),
        ShowGraph(),
        EarlyStoppingCallback(patience=10)
    ],
    path='models',                 # 模型保存路径
    model_dir='my_model'           # 模型子目录
)

# ============= 步骤5:训练 =============
learn.fit_one_cycle(50, 1e-3)

# ============= 步骤6:评估和导出 =============
# 查看训练曲线
learn.plot_metrics()

# 获取验证集预测
preds, targets = learn.get_preds()

# 导出模型
learn.export('natops_model.pkl')
流程2:使用sklearn-like API
python 复制代码
from tsai.all import *

# ============= 一步到位 =============
clf = TSClassifier(
    X, y,                          # 直接传入数据
    splits=splits,                 # 训练/验证划分
    arch='InceptionTimePlus',      # 模型架构
    tfms=[None, TSClassification()],
    batch_tfms=[TSStandardize()],
    metrics=accuracy,
    bs=64
)

# ============= 训练 =============
clf.fit_one_cycle(50, 1e-3)

# ============= 预测 =============
# 获取概率
probas = clf.predict_proba(X_test)

# 获取类别
preds = clf.predict(X_test)

# ============= 导出 =============
clf.export('classifier.pkl')

5.2 模型架构的选择与配置

内置架构列表
python 复制代码
from tsai.models.utils import *

# 查看所有可用架构
all_archs = [
    'InceptionTime', 'InceptionTimePlus',
    'ResNet', 'ResNetPlus',
    'FCN', 'FCNPlus',
    'LSTM', 'LSTM_FCN', 'LSTM_FCN_plus',
    'GRU', 'GRU_FCN',
    'TransformerModel', 'TSTPlus',
    'PatchTST',
    'XCM', 'XCMPlus',
    # ... 还有更多
]
架构配置

方式1:使用默认配置

python 复制代码
learn = ts_learner(dls, InceptionTime)
# 自动从dls推断参数

方式2:自定义配置

python 复制代码
learn = ts_learner(
    dls,
    InceptionTime,
    c_in=24,              # 输入通道数(覆盖dls推断)
    c_out=6,              # 输出类别数
    seq_len=51,           # 序列长度
    depth=6,              # Inception模块深度
    kernel_sizes=[39, 19, 9],  # 卷积核大小
    bottleneck_channels=32     # 瓶颈层通道数
)

方式3:使用arch_config字典

python 复制代码
learn = ts_learner(
    dls,
    InceptionTime,
    arch_config={
        'depth': 6,
        'kernel_sizes': [39, 19, 9],
        'bottleneck_channels': 32,
        'fc_dropout': 0.5
    }
)
Plus架构的优势

带"Plus"后缀的架构(如InceptionTimePlus)提供额外功能:

python 复制代码
learn = ts_learner(dls, InceptionTimePlus)

优势

  • ✅ 自动配置输出头(分类/回归/预测)
  • ✅ 支持多任务学习
  • ✅ 更好的默认超参数
  • ✅ 额外的正则化选项

5.3 训练方法详解

方法1:fit_one_cycle(推荐)
python 复制代码
learn.fit_one_cycle(
    n_epoch=50,          # 训练轮数
    lr_max=1e-3,         # 最大学习率
    pct_start=0.3,       # warm-up阶段占比
    div=25,              # 初始lr = lr_max / div
    div_final=1e4,       # 最终lr = lr_max / div_final
    moms=(0.95, 0.85, 0.95),  # (初始动量, 中期动量, 最终动量)
    wd=None,             # 权重衰减(None则使用创建时的值)
    cbs=None             # 额外的callbacks
)

适用场景

  • ✅ 从头训练
  • ✅ 微调预训练模型
  • ✅ 大多数情况的首选
方法2:fit(标准训练)
python 复制代码
learn.fit(
    n_epoch=50,
    lr=1e-3,             # 固定学习率
    wd=None,
    cbs=None
)

适用场景

  • 需要固定学习率时
  • 想完全自定义学习率调度
方法3:fine_tune(迁移学习)
python 复制代码
learn.fine_tune(
    epochs=20,           # 微调轮数
    base_lr=1e-4,        # 基础学习率
    freeze_epochs=5,     # 冻结前N个epoch
    lr_mult=100          # 最后一层的学习率倍数
)

流程

  1. 冻结除最后一层外的所有层
  2. 训练freeze_epochs个epoch
  3. 解冻所有层
  4. 用判别式学习率训练epochs个epoch

适用场景

  • ✅ 迁移学习
  • ✅ 预训练模型微调
方法4:fit_flat_cos(余弦退火)
python 复制代码
learn.fit_flat_cos(
    n_epoch=50,
    lr=1e-3,
    pct_start=0.75  # 前75%保持lr,后25%退火
)

5.4 常见陷阱与解决方案

陷阱1:忘记标准化数据
python 复制代码
# ❌ 错误:未标准化
dls = get_ts_dls(X, y, splits=splits)

# ✅ 正确:添加标准化
dls = get_ts_dls(X, y, splits=splits, 
                batch_tfms=TSStandardize())

为什么重要?

  • 不同特征的量纲可能差异巨大
  • 未标准化会导致训练不稳定
陷阱2:学习率过大或过小
python 复制代码
# ❌ 可能的问题
learn.fit_one_cycle(50, 1.0)  # 太大,可能发散
learn.fit_one_cycle(50, 1e-7)  # 太小,收敛极慢

# ✅ 使用lr_find找到合适的学习率
learn.lr_find()
# 根据曲线选择合适的lr
learn.fit_one_cycle(50, 1e-3)
陷阱3:数据泄漏
python 复制代码
# ❌ 错误:在split之前标准化
X_normalized = (X - X.mean()) / X.std()
splits = TimeSplitter(0.8)(X_normalized)
dls = get_ts_dls(X_normalized, y, splits=splits)

# ✅ 正确:在batch_tfms中标准化
splits = TimeSplitter(0.8)(X)
dls = get_ts_dls(X, y, splits=splits, 
                batch_tfms=TSStandardize())
# TSStandardize只使用训练集的统计量
陷阱4:忘记设置随机种子
python 复制代码
# ❌ 结果不可复现
splits = RandomSplitter()(X)

# ✅ 设置随机种子
from tsai.data.validation import *
set_seed(42)
splits = RandomSplitter()(X)
陷阱5:显存不足
python 复制代码
# 问题:batch size太大导致OOM
try:
    learn = ts_learner(dls, model)
    learn.fit_one_cycle(50, 1e-3)
except RuntimeError as e:
    if "out of memory" in str(e):
        print("OOM!")

# 解决方案1:减小batch size
dls = get_ts_dls(X, y, splits=splits, bs=16)  # 从64降到16

# 解决方案2:梯度累积
learn = ts_learner(dls, model, 
                  cbs=GradientAccumulation(n_acc=4))

# 解决方案3:混合精度
learn = ts_learner(dls, model).to_fp16()
陷阱6:过拟合

症状:训练loss持续下降,验证loss不降或上升

python 复制代码
# 解决方案
learn = ts_learner(dls, model,
    wd=0.01,  # 增加权重衰减
    cbs=[
        # 数据增强
        TSMagWarp(sigma=0.2, p=0.5),
        TSTimeWarp(sigma=0.2, p=0.5),
        
        # Dropout
        # 在arch_config中设置
    ]
)

# 或在模型配置中加入dropout
learn = ts_learner(dls, InceptionTime,
                  arch_config={'fc_dropout': 0.5})

5.5 模型保存与加载

保存策略

策略1:只保存模型参数

python 复制代码
# 保存
learn.save('model_weights')  # 保存到 models/model_weights.pth

# 加载
learn.load('model_weights')

策略2:保存完整learner

python 复制代码
# 保存(包含模型、优化器状态、训练历史)
learn.export('full_model.pkl')

# 加载
from tsai.inference import load_learner
learn = load_learner('models/full_model.pkl')

# 直接预测
preds = learn.predict(X_new)

策略3:保存所有组件

python 复制代码
# 保存(分别保存dls、model、learner)
learn.save_all(
    path='export',
    dls_fname='dataloaders',
    model_fname='model',
    learner_fname='learner'
)

# 加载
from tsai.learner import load_all
learn = load_all(
    path='export',
    dls_fname='dataloaders',
    model_fname='model',
    learner_fname='learner'
)

推荐

  • 实验阶段:使用save保存检查点
  • 生产部署:使用export保存完整模型

6. 进阶应用场景

6.1 多任务学习

场景:同时进行分类和回归

python 复制代码
# 假设y包含两个任务
y_class = y[:, 0]  # 分类标签
y_reg = y[:, 1]    # 回归目标

# 创建多任务DataLoader
class MultiTaskDataset:
    def __init__(self, X, y_class, y_reg):
        self.X = X
        self.y_class = y_class
        self.y_reg = y_reg
    
    def __getitem__(self, idx):
        return self.X[idx], (self.y_class[idx], self.y_reg[idx])
    
    def __len__(self):
        return len(self.X)

# 自定义损失函数
class MultiTaskLoss:
    def __init__(self, w1=1.0, w2=1.0):
        self.w1 = w1
        self.w2 = w2
        self.ce = nn.CrossEntropyLoss()
        self.mse = nn.MSELoss()
    
    def __call__(self, preds, targets):
        pred_class, pred_reg = preds
        target_class, target_reg = targets
        
        loss_class = self.ce(pred_class, target_class)
        loss_reg = self.mse(pred_reg, target_reg)
        
        return self.w1 * loss_class + self.w2 * loss_reg

# 创建learner
learn = ts_learner(dls, MultiTaskModel, 
                  loss_func=MultiTaskLoss(w1=1.0, w2=0.5))

6.2 时间序列预测

单步预测

python 复制代码
from tsai.all import *

# 获取数据
ts = get_forecasting_time_series("Sunspots").values

# 滑动窗口:用60步预测未来1步
X, y = SlidingWindow(window_len=60, horizon=1)(ts)

# 创建预测器
fcst = TSForecaster(
    X, y,
    arch='TSTPlus',      # 使用Transformer
    metrics=mae,
    batch_tfms=TSStandardize()
)

# 训练
fcst.fit_one_cycle(50, 1e-3)

# 预测
future_steps = fcst.predict(X_test)

多步预测

python 复制代码
# 预测未来3步
X, y = SlidingWindow(window_len=60, horizon=3)(ts)
# y shape: [N, 3]

fcst = TSForecaster(
    X, y,
    arch='TSTPlus',
    metrics=[mae, rmse]
)

fcst.fit_one_cycle(50, 1e-3)

递归预测(自回归)

python 复制代码
def recursive_forecast(learn, X_initial, n_steps):
    """递归预测未来n步"""
    forecasts = []
    X_current = X_initial.copy()
    
    for _ in range(n_steps):
        # 预测下一步
        pred = learn.predict(X_current)
        forecasts.append(pred)
        
        # 更新输入:移除最旧的,加入新预测
        X_current = np.roll(X_current, -1, axis=-1)
        X_current[..., -1] = pred
    
    return np.array(forecasts)

# 使用
future_30_steps = recursive_forecast(fcst, X_test[0], n_steps=30)

6.3 序列到序列(Sequence-to-Sequence)

场景:输入和输出都是序列

python 复制代码
# 例如:时间序列去噪
# X: [N, C, T]  噪声序列
# y: [N, C, T]  干净序列

# 使用Encoder-Decoder架构
from tsai.models.TST import TST

learn = ts_learner(
    dls,
    TST,
    arch_config={
        'n_layers': 3,
        'n_heads': 8,
        'd_model': 128,
        'fc_dropout': 0.1
    }
)

learn.fit_one_cycle(50, 1e-3)

6.4 少样本学习(Few-Shot Learning)

原型网络(Prototypical Networks)

python 复制代码
# 假设每个类只有很少样本
from tsai.models.utils import *

class PrototypicalLoss:
    def __call__(self, embeddings, labels):
        # 计算每个类的原型(中心)
        unique_labels = torch.unique(labels)
        prototypes = []
        
        for label in unique_labels:
            mask = labels == label
            prototype = embeddings[mask].mean(dim=0)
            prototypes.append(prototype)
        
        prototypes = torch.stack(prototypes)
        
        # 计算距离
        distances = torch.cdist(embeddings, prototypes)
        
        # 负对数似然
        log_p = F.log_softmax(-distances, dim=1)
        loss = F.nll_loss(log_p, labels)
        
        return loss

# 使用
learn = ts_learner(dls, ResNet, loss_func=PrototypicalLoss())
learn.fit_one_cycle(50, 1e-3)

6.5 域适应(Domain Adaptation)

场景:源域有标签,目标域无标签

python 复制代码
# 对抗训练
class DomainAdaptationModel(nn.Module):
    def __init__(self, feature_extractor, classifier, discriminator):
        super().__init__()
        self.feature_extractor = feature_extractor
        self.classifier = classifier
        self.discriminator = discriminator
    
    def forward(self, x, alpha=1.0):
        features = self.feature_extractor(x)
        
        # 分类任务
        class_output = self.classifier(features)
        
        # 域判别(带梯度反转)
        reverse_features = GradientReversal.apply(features, alpha)
        domain_output = self.discriminator(reverse_features)
        
        return class_output, domain_output

# 自定义训练循环
class DACallback(Callback):
    def __init__(self, source_dl, target_dl):
        self.source_dl = source_dl
        self.target_dl = target_dl
    
    def after_batch(self):
        # 获取目标域batch
        target_batch = next(iter(self.target_dl))
        
        # 计算域适应损失
        # ... (实现细节)

learn = ts_learner(dls_source, DAModel, cbs=[DACallback(dls_source, dls_target)])

6.6 集成学习(Ensemble)

策略1:投票集成

python 复制代码
# 训练多个模型
models = []
for i in range(5):
    learn = ts_learner(dls, InceptionTime)
    learn.fit_one_cycle(50, 1e-3)
    models.append(learn.model)

# 集成预测
def ensemble_predict(models, X):
    preds = []
    for model in models:
        model.eval()
        with torch.no_grad():
            pred = model(X)
        preds.append(pred)
    
    # 平均概率
    ensemble_pred = torch.stack(preds).mean(dim=0)
    return ensemble_pred.argmax(dim=-1)

final_preds = ensemble_predict(models, X_test)

策略2:Stacking

python 复制代码
# 第一层:基础模型
base_learners = [
    ts_learner(dls, InceptionTime),
    ts_learner(dls, ResNet),
    ts_learner(dls, TransformerModel)
]

for learn in base_learners:
    learn.fit_one_cycle(50, 1e-3)

# 获取第一层的预测作为特征
base_preds_train = []
base_preds_valid = []

for learn in base_learners:
    preds_train, _ = learn.get_preds(dl=dls.train)
    preds_valid, _ = learn.get_preds(dl=dls.valid)
    base_preds_train.append(preds_train)
    base_preds_valid.append(preds_valid)

# 第二层:元学习器
meta_X_train = torch.cat(base_preds_train, dim=1)
meta_X_valid = torch.cat(base_preds_valid, dim=1)

# 使用简单的MLP作为元学习器
from tsai.models.MLP import MLP

meta_dls = get_ts_dls(meta_X_train, y_train, 
                     splits=(train_idx, valid_idx))
meta_learner = ts_learner(meta_dls, MLP)
meta_learner.fit_one_cycle(20, 1e-3)

7. 批判性思考与扩展

7.1 Learner的局限性

局限1:黑盒抽象的代价

问题:Learner隐藏了太多细节,出问题时难以调试

python 复制代码
# 例如:不知道为什么训练这么慢
learn.fit_one_cycle(50, 1e-3)  # 为什么每个epoch要10分钟?

# 可能的原因:
# - DataLoader的num_workers=0(单线程加载)
# - 批次变换太复杂
# - 模型太大
# - ...

# 解决:需要深入了解内部机制

缓解策略

  • 使用learn.summary()查看模型结构
  • 使用time_callback监控各部分耗时
  • 必要时查看源码
局限2:不适合极度自定义的场景

问题:某些研究场景需要完全自定义训练循环

python 复制代码
# 例如:GAN训练(生成器和判别器交替训练)
# Learner的标准训练循环不适用

# 解决:回归原始PyTorch
for epoch in range(epochs):
    # 训练判别器
    for _ in range(k):
        d_loss = train_discriminator(...)
    
    # 训练生成器
    g_loss = train_generator(...)
局限3:内存占用

问题:Learner会保留训练历史,占用内存

python 复制代码
# 长时间训练后
learn.recorder.values  # 包含所有epoch的loss和metric

# 在内存受限环境,可能需要手动清理
learn.recorder.values = []

7.2 何时不应使用Learner

情况1:简单的模型评估
python 复制代码
# ❌ 过度工程化
learn = ts_learner(dls, model)
preds, targets = learn.get_preds()

# ✅ 直接使用PyTorch更简单
model.eval()
with torch.no_grad():
    preds = model(X_test)
情况2:非标准的训练任务
  • 强化学习
  • 主动学习
  • 在线学习
  • ...

这些场景下,Learner的抽象不再适用。

情况3:极致性能优化
python 复制代码
# 如果需要手写CUDA kernel优化
# Learner的抽象层会增加overhead

# 直接使用PyTorch的autograd和JIT

7.3 Learner的设计哲学

原则1:约定优于配置(Convention over Configuration)
python 复制代码
# Learner猜测合理的默认值
learn = ts_learner(dls, InceptionTime)
# 自动推断:c_in, c_out, seq_len

# 好处:减少重复代码
# 坏处:不透明,可能猜错
原则2:渐进式披露复杂度(Progressive Disclosure)
复制代码
简单使用:learn.fit_one_cycle(50, 1e-3)
    ↓
中等复杂:learn.fit_one_cycle(50, slice(1e-5, 1e-3), cbs=[...])
    ↓
完全控制:learn._do_fit(...) + 自定义callbacks
原则3:可组合性(Composability)
python 复制代码
# 各组件独立,可自由组合
learn = ts_learner(
    custom_dls,        # 自定义DataLoaders
    custom_model,      # 自定义模型
    loss_func=custom_loss,  # 自定义损失
    opt_func=custom_opt,    # 自定义优化器
    cbs=[custom_cb1, custom_cb2]  # 自定义callbacks
)

7.4 未来发展方向

方向1:自动化机器学习(AutoML)
python 复制代码
# 想象的未来API
auto_learn = AutoTSLearner(X, y)
auto_learn.auto_fit(
    time_budget='2h',     # 时间预算
    metric='accuracy',
    trials=100            # 尝试100种配置
)
# 自动搜索:架构、超参数、数据增强
方向2:联邦学习(Federated Learning)
python 复制代码
# 想象的未来API
fed_learn = FederatedTSLearner(
    local_dls_list=[dls1, dls2, dls3],  # 多个客户端的数据
    arch=InceptionTime
)

fed_learn.fit_federated(
    rounds=10,
    clients_per_round=2
)
方向3:持续学习(Continual Learning)
python 复制代码
# 想象的未来API
continual_learn = ContinualTSLearner(dls, InceptionTime)

# 任务1
continual_learn.fit_one_cycle(50, 1e-3, task_id=1)

# 任务2(不忘记任务1)
continual_learn.fit_one_cycle(50, 1e-3, task_id=2)
方向4:可解释性(Explainability)
python 复制代码
# 想象的未来API
learn.explain(X_sample)
# 自动生成:
# - 注意力可视化
# - Grad-CAM
# - SHAP值
# - ...

7.5 与其他框架的对比

特性 tsai Learner PyTorch Lightning Keras sklearn
学习曲线 中等 陡峭 平缓 最平缓
灵活性 最高 中等
时间序列专用
训练速度 中等 N/A
生态系统 fastai PyTorch TensorFlow 统计学习
适用人群 DL实践者 DL研究者 初学者 传统ML

选择建议

  • 快速原型:tsai Learner或Keras
  • 研究创新:PyTorch Lightning
  • 生产部署:PyTorch原生(最可控)
  • 传统ML:sklearn

8. 小结与学习路径

8.1 核心要点回顾

🎯 Learner的核心价值
  1. 简化训练代码:从100行压缩到5行
  2. 集成最佳实践:1cycle、混合精度、判别式学习率
  3. 统一接口:分类、回归、预测用法一致
  4. 可扩展:通过Callback机制灵活扩展
📋 关键概念清单

核心组件 :DataLoaders、Model、Optimizer、Loss、Metrics、Callbacks

两套API :传统API(先dls后learn)vs sklearn-like(一步到位)

训练方法 :fit_one_cycle(首选)、fit、fine_tune、fit_flat_cos

优化技巧 :LR Finder、判别式学习率、梯度裁剪、混合精度

保存加载:save/load(参数)、export/load_learner(完整)

🛠️ 快速参考

创建Learner

python 复制代码
# 方式1:传统API
dls = get_ts_dls(X, y, ...)
learn = ts_learner(dls, arch, metrics, ...)

# 方式2:sklearn-like
clf = TSClassifier(X, y, arch=..., ...)

训练

python 复制代码
learn.fit_one_cycle(50, 1e-3)  # 推荐

评估

python 复制代码
preds, targets = learn.get_preds()

保存/加载

python 复制代码
learn.export('model.pkl')
learn = load_learner('model.pkl')

8.2 进阶学习路径

阶段1:基础应用(1-2周)

目标:能够使用Learner完成基本任务

学习任务

  1. ✅ 完成一个时间序列分类任务
  2. ✅ 尝试3种不同的模型架构
  3. ✅ 使用lr_find()找到合适的学习率
  4. ✅ 可视化训练过程(learn.plot_metrics()

练习项目

python 复制代码
# UCR数据集分类
from tsai.all import *

# 任务:在5个UCR数据集上训练模型,比较不同架构的性能
datasets = ['ECG200', 'GunPoint', 'ItalyPowerDemand', 'Wafer', 'Lightning7']

for dsid in datasets:
    X, y, splits = get_UCR_data(dsid, split_data=False)
    
    # 尝试3种架构
    for arch_name in ['InceptionTimePlus', 'ResNetPlus', 'TSTPlus']:
        clf = TSClassifier(X, y, splits=splits, arch=arch_name, metrics=accuracy)
        clf.fit_one_cycle(50, 1e-3)
        
        # 记录结果
        print(f"{dsid} - {arch_name}: {clf.recorder.values[-1][2]:.4f}")
阶段2:中级技巧(2-3周)

目标:掌握高级训练技巧

学习任务

  1. ✅ 使用迁移学习(fine_tune)
  2. ✅ 实现自定义损失函数
  3. ✅ 组合多个Callbacks
  4. ✅ 处理不平衡数据集

练习项目

python 复制代码
# 挑战:处理严重不平衡的数据
from tsai.all import *
from imblearn.over_sampling import SMOTE

# 假设类别分布:[1000, 100, 10]  (严重不平衡)

# 解决方案1:类别加权
class_weights = compute_class_weight('balanced', 
                                    classes=np.unique(y), 
                                    y=y)
loss_func = CrossEntropyLoss(weight=torch.tensor(class_weights))

learn = ts_learner(dls, model, loss_func=loss_func)

# 解决方案2:过采样
sm = SMOTE()
X_resampled, y_resampled = sm.fit_resample(X.reshape(len(X), -1), y)
X_resampled = X_resampled.reshape(-1, X.shape[1], X.shape[2])

# 解决方案3:Focal Loss
from tsai.losses import FocalLoss
learn = ts_learner(dls, model, loss_func=FocalLoss())

# 比较三种方案的效果
阶段3:高级应用(3-4周)

目标:解决复杂的实际问题

学习任务

  1. ✅ 实现多任务学习
  2. ✅ 时间序列预测(多步预测)
  3. ✅ 构建集成模型
  4. ✅ 部署到生产环境

挑战项目

python 复制代码
# 挑战:端到端的时间序列预测系统
# 要求:
# 1. 数据预处理管道
# 2. 模型训练与验证
# 3. 超参数调优
# 4. 模型集成
# 5. 部署API

# 示例框架
class TSForecastingPipeline:
    def __init__(self):
        self.preprocessor = None
        self.models = []
        self.meta_model = None
    
    def fit(self, X, y):
        # 1. 数据预处理
        X_processed = self.preprocess(X)
        
        # 2. 训练多个基础模型
        for arch in ['TSTPlus', 'InceptionTimePlus', 'PatchTST']:
            learn = TSForecaster(X_processed, y, arch=arch)
            learn.fit_one_cycle(50, 1e-3)
            self.models.append(learn)
        
        # 3. 训练元模型(stacking)
        self.fit_meta_model(X_processed, y)
    
    def predict(self, X):
        # 集成预测
        ...
    
    def deploy(self, endpoint):
        # 部署API
        ...

8.3 深入学习资源

📚 官方文档
📖 推荐阅读
  1. 论文

    • "Cyclical Learning Rates for Training Neural Networks" (Smith, 2017)
    • "Super-Convergence: Very Fast Training of Neural Networks" (Smith, 2018)
  2. 博客

    • fastai官方博客
    • "The 1cycle policy" by Sylvain Gugger
💻 源码阅读

建议按以下顺序阅读源码:

  1. fastai/learner.py - Learner基类
  2. tsai/learner.py - tsai的Learner扩展
  3. tsai/tslearner.py - sklearn-like API
  4. fastai/callback/core.py - Callback系统

附录

A. Learner方法速查表

训练方法
方法 用途 典型参数
fit_one_cycle 1cycle训练(推荐) n_epoch, lr_max
fit 标准训练 n_epoch, lr
fine_tune 迁移学习 epochs, base_lr
fit_flat_cos 余弦退火 n_epoch, lr
评估方法
方法 用途 返回值
get_preds 获取预测和目标 (preds, targets)
predict 单样本预测 prediction
validate 验证集评估 (loss, metrics)
工具方法
方法 用途 说明
lr_find 学习率查找 绘制loss vs lr曲线
plot_metrics 可视化训练 绘制loss和metrics
freeze 冻结层 用于迁移学习
unfreeze 解冻层 微调时使用
保存/加载
方法 用途 文件
save 保存模型参数 .pth
load 加载模型参数 .pth
export 保存完整learner .pkl
load_learner 加载完整learner .pkl

B. 常用架构对比

架构 类型 优点 缺点 适用场景
InceptionTime CNN 快速、准确 长序列较慢 通用分类
ResNet CNN 深度可扩展 参数多 复杂模式
LSTM RNN 捕获长期依赖 训练慢 长序列
Transformer Attention 并行训练 显存占用大 长序列、预测
PatchTST Patch+Transformer SOTA性能 较新、参数多 预测任务

C. 故障排除清单

问题 可能原因 解决方案
训练不收敛 学习率太大 使用lr_find降低lr
训练很慢 batch size太小 增加batch size或梯度累积
验证集不提升 过拟合 增加正则化、数据增强
OOM 显存不足 减小batch size或使用fp16
精度低 模型容量不足 使用更大/更深的模型

🎓 结语

Learner是深度学习工程化的典范------它将复杂的训练过程封装成简洁的接口,让我们能专注于解决实际问题,而非重复造轮子。

掌握Learner,不仅意味着掌握了tsai/fastai的使用,更重要的是理解了如何设计一个优雅的深度学习系统。这种设计思想可以迁移到任何框架、任何领域。

好的抽象应该隐藏复杂性,但不隐藏控制权。Learner正是这样的设计。

相关推荐
KAI智习2 小时前
大模型榜单周报(2025/12/20)
人工智能·大模型
2501_907136822 小时前
AI 小说生成器-基于 Tauri 2.0 + Vue 3 + TypeScript 的智能小说创作工具
人工智能·软件需求
love530love2 小时前
ComfyUI 升级 v0.4.0 踩坑记录:解决 TypeError: QM_Queue.task_done() 报错
人工智能·windows·python·comfyui
金士镧(厦门)新材料有限公司3 小时前
稀土化合物:推动科技发展的“隐形力量”
人工智能·科技·安全·全文检索·生活·能源
wdfk_prog3 小时前
[Linux]学习笔记系列 -- [fs]dcache
linux·数据库·笔记·学习·ubuntu
牛客企业服务3 小时前
AI简历筛选:破解海量简历处理难题
人工智能
粟悟饭&龟波功3 小时前
【GitHub热门项目精选】(2025-12-19)
前端·人工智能·后端·github
诸葛务农3 小时前
类脑智能技术前沿进展及中美类脑智能技术比对
人工智能
LiYingL3 小时前
ChartCap:利用大型数据集和新的评估指标抑制图表标题幻觉
人工智能