tsai 完整训练流程实践指南

tsai 完整训练流程实践指南

核心目标:掌握tsai从数据准备到模型部署的完整工程流程,理解每个步骤的目的和注意事项

  1. tsai简介与环境配置
    1.1 什么是tsai?
    tsai (Time Series AI) 是一个基于PyTorch和fastai的时间序列深度学习库,专为时间序列分类、回归和预测任务设计。

核心特点:

简洁的API设计,类似scikit-learn

内置30+最先进的时间序列模型

自动化的训练流程

丰富的数据增强和回调函数

支持GPU加速

1.2 安装配置

基础安装

pip install tsai

完整安装(包含所有依赖)

pip install tsai[extras]

验证安装

python -c "from tsai.all import *; print('tsai安装成功!')"

1.3 导入必要的库

标准导入方式

from tsai.all import *

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

检查GPU是否可用

print(f"GPU可用: {torch.cuda.is_available()}")

if torch.cuda.is_available():

print(f"GPU设备: {torch.cuda.get_device_name(0)}")

  1. 完整训练流程概览

tsai的典型训练流程包含以下9个核心步骤:

  1. 数据准备 → 加载和预处理时间序列数据
  2. 数据加载器 → 创建TSDataLoaders进行批量加载
  3. 模型创建 → 选择合适的模型架构
  4. 创建学习器 → 组装模型、数据、损失函数和指标
  5. 学习率查找 → 使用lr_find找到最优学习率
  6. 模型训练 → 使用fit_one_cycle或fit进行训练
  7. 模型评估 → 在验证集上评估性能
  8. 模型预测 → 对新数据进行预测
  9. 保存加载 → 保存模型供后续使用
    流程图:

数据 → TSDataLoaders → Model → Learner → lr_find → fit → validate → predict → save

  1. 步骤1:数据准备与加载

3.1 步骤目的

将原始时间序列数据转换为tsai可以处理的numpy数组格式,并进行必要的预处理。

3.2 数据格式要求

tsai期望的数据格式:

输入数据 X 的形状

X.shape = (样本数, 序列长度, 特征数)

例如: (1000, 100, 5) 表示1000个样本,每个样本100个时间步,5个特征

标签 y 的形状

分类任务: (样本数,) - 例如: (1000,)

回归任务: (样本数,) 或 (样本数, 输出维度)

3.3 加载UCR/UEA数据集(最简单)

tsai内置了UCR/UEA时间序列分类数据集的加载功能

from tsai.data.external import *

加载数据集

X_train, y_train, X_valid, y_valid = get_UCR_data('ECG200')

print(f"训练集形状: X={X_train.shape}, y={y_train.shape}")

print(f"验证集形状: X={X_valid.shape}, y={y_valid.shape}")

print(f"类别数量: {len(np.unique(y_train))}")

print(f"序列长度: {X_train.shape[1]}")

print(f"特征维度: {X_train.shape[2]}")

注意事项:

UCR数据集会自动下载到本地缓存

数据已经划分好训练集和验证集

可以使用get_UCR_list()查看所有可用数据集

3.4 加载自定义数据

从CSV文件加载

df = pd.read_csv('your_data.csv')

转换为numpy数组(假设是单变量时间序列)

X = df.values.reshape(样本数, 序列长度, 1)

y = labels_array

从多个CSV文件加载(每个文件一个样本)

import glob

data_files = glob.glob('data/*.csv')

X_list = []

y_list = []

for file in data_files:

data = pd.read_csv(file).values

X_list.append(data)

从文件名或其他来源获取标签

label = extract_label_from_filename(file)

y_list.append(label)

X = np.array(X_list) # 确保形状是 (N, T, d)

y = np.array(y_list)

print(f"数据形状: {X.shape}")

3.5 数据预处理

1. 检查数据质量

print(f"是否有NaN: {np.isnan(X_train).any()}")

print(f"是否有Inf: {np.isinf(X_train).any()}")

print(f"数据范围: [{X_train.min():.4f}, {X_train.max():.4f}]")

2. 处理缺失值(如果有)

if np.isnan(X_train).any():

前向填充

X_train = pd.DataFrame(X_train.reshape(-1, X_train.shape[-1])).fillna(method='ffill').values.reshape(X_train.shape)

X_valid = pd.DataFrame(X_valid.reshape(-1, X_valid.shape[-1])).fillna(method='ffill').values.reshape(X_valid.shape)

3. 数据归一化(推荐)

from sklearn.preprocessing import StandardScaler

计算训练集的统计量

scaler = StandardScaler()

X_train_flat = X_train.reshape(-1, X_train.shape[-1])

scaler.fit(X_train_flat)

应用到训练集和验证集

X_train_scaled = scaler.transform(X_train_flat).reshape(X_train.shape)

X_valid_scaled = scaler.transform(X_valid.reshape(-1, X_valid.shape[-1])).reshape(X_valid.shape)

或者使用简单的min-max归一化

X_train_norm = (X_train - X_train.min()) / (X_train.max() - X_train.min())

X_valid_norm = (X_valid - X_train.min()) / (X_train.max() - X_train.min()) # 注意:使用训练集的min/max

print(f"归一化后范围: [{X_train_scaled.min():.4f}, {X_train_scaled.max():.4f}]")

工程注意事项:

✅ 始终使用训练集的统计量(均值、标准差、min、max)来归一化验证集和测试集

✅ 保存scaler对象,用于预测时的数据预处理

✅ 检查数据分布,确保没有异常值

❌ 不要在归一化前就划分训练/验证集,会导致数据泄露

3.6 数据划分

如果数据还没有划分训练/验证集

from sklearn.model_selection import train_test_split

按比例划分

X_train, X_valid, y_train, y_valid = train_test_split(

X, y,

test_size=0.2, # 20%作为验证集

random_state=42, # 设置随机种子保证可复现

stratify=y # 分层采样,保持类别比例

)

print(f"训练集大小: {len(X_train)}")

print(f"验证集大小: {len(X_valid)}")

print(f"训练集类别分布: {np.bincount(y_train)}")

print(f"验证集类别分布: {np.bincount(y_valid)}")

  1. 步骤2:创建数据加载器

4.1 步骤目的

创建TSDataLoaders对象,负责:

批量加载数据(batch)

数据增强(可选)

数据打乱(shuffle)

多进程加载(加速)

4.2 使用get_ts_dls(推荐方式)

from tsai.data.core import get_ts_dls

最简单的创建方式

dls = get_ts_dls(

X_train, y_train, # 训练数据

X_valid, y_valid, # 验证数据

bs=64 # batch size(批次大小)

)

print(f"训练批次数: {len(dls.train)}")

print(f"验证批次数: {len(dls.valid)}")

4.3 完整参数配置

dls = get_ts_dls(

X_train, y_train,

X_valid, y_valid,

bs=64, # 批次大小,根据显存调整

batch_tfms=None, # 批次级别的数据增强

num_workers=4, # 数据加载的进程数

shuffle_train=True, # 训练集是否打乱

drop_last=False, # 是否丢弃最后不完整的batch

device='cuda' if torch.cuda.is_available() else 'cpu'

)

参数说明:

参数 目的 推荐值 注意事项

bs 批次大小,影响训练速度和显存占用 32-128 GPU显存小用32,大用128

num_workers 并行加载数据的进程数 4-8 Windows系统可能需要设为0

shuffle_train 打乱训练数据顺序 True 验证集不需要打乱

drop_last 丢弃不完整的batch False 数据少时设为False

4.4 添加数据增强

from tsai.data.transforms import *

定义数据增强pipeline

batch_tfms = [

TSStandardize(), # 标准化

TSRandomCrop(seq_len=100), # 随机裁剪到固定长度

]

dls = get_ts_dls(

X_train, y_train,

X_valid, y_valid,

bs=64,

batch_tfms=batch_tfms

)

常用数据增强方法:

1. 时间扭曲

from tsai.data.augmentation import TSTimeWarp

tfm = TSTimeWarp(p=0.5) # 50%概率应用

2. 窗口切片

from tsai.data.augmentation import TSWindowSlice

tfm = TSWindowSlice(p=0.3)

3. 添加高斯噪声

from tsai.data.augmentation import TSGaussianNoise

tfm = TSGaussianNoise(scale=0.1, p=0.5)

4. 幅度缩放

from tsai.data.augmentation import TSMagScale

tfm = TSMagScale(p=0.3)

组合多个增强

batch_tfms = [

TSTimeWarp(p=0.5),

TSGaussianNoise(scale=0.05, p=0.3),

TSMagScale(p=0.3)

]

4.5 检查数据加载器

检查一个batch

xb, yb = dls.one_batch()

print(f"Batch X shape: {xb.shape}") # (batch_size, seq_len, features)

print(f"Batch y shape: {yb.shape}") # (batch_size,)

print(f"数据在设备: {xb.device}") # cuda:0 或 cpu

可视化batch中的几个样本

dls.show_batch(max_n=9, figsize=(15, 10))

工程注意事项:

✅ batch size尽量选择2的幂次(32, 64, 128),利于GPU优化

✅ 如果训练很慢,尝试增加num_workers

✅ Windows用户如果遇到multiprocessing错误,设置num_workers=0

❌ 数据增强不要过度,可能导致模型学不到真实模式

  1. 步骤3:模型选择与创建
    5.1 步骤目的
    根据任务特点和数据特征选择合适的模型架构,tsai提供了30+预定义模型。

5.2 tsai内置模型概览

主流模型分类:

1. 卷积神经网络(CNN)系列 - 速度快,适合大多数场景

from tsai.models.InceptionTime import InceptionTime

from tsai.models.ResNet import ResNet

from tsai.models.XceptionTime import XceptionTime

from tsai.models.ResCNN import ResCNN

2. 循环神经网络(RNN)系列 - 适合序列依赖强的任务

from tsai.models.RNN import LSTM, GRU, RNN

from tsai.models.RNNPlus import LSTMPlus, GRUPlus

3. Transformer系列 - 适合长序列和复杂依赖

from tsai.models.TST import TST

from tsai.models.TransformerModel import TransformerModel

4. 混合架构

from tsai.models.InceptionTimePlus import InceptionTimePlus

from tsai.models.LSTM_FCN import LSTM_FCN

5.3 模型选择指南

场景 推荐模型 理由

通用分类任务 InceptionTime 性能稳定,速度快

短序列(<100) ResNet, FCN 简单有效

长序列(>500) TST, InceptionTime 能捕捉长期依赖

多变量时间序列 InceptionTime, TST 能处理特征交互

时序依赖强 LSTM, GRU 专门设计用于序列

资源受限 ResNet, FCN 参数少,速度快

追求最高精度 InceptionTimePlus, XCM SOTA性能

5.4 创建模型示例

5.4.1 InceptionTime(最推荐)

from tsai.models.InceptionTime import InceptionTime

model = InceptionTime(

c_in=X_train.shape[2], # 输入特征数

c_out=len(np.unique(y_train)), # 输出类别数

seq_len=X_train.shape[1], # 序列长度(可选)

nf=32, # 基础滤波器数量

depth=6 # Inception块的深度

)

print(f"模型参数量: {sum(p.numel() for p in model.parameters()):,}")

参数调整建议:

nf:控制模型容量,数据多用64,数据少用16-32

depth:控制模型深度,通常6-12效果好

5.4.2 ResNet

from tsai.models.ResNet import ResNet

model = ResNet(

c_in=X_train.shape[2],

c_out=len(np.unique(y_train))

)

或自定义层数

model = ResNet(

c_in=X_train.shape[2],

c_out=len(np.unique(y_train)),

layers=[64, 128, 128] # 每个残差块的通道数

)

5.4.3 LSTM

from tsai.models.RNN import LSTM

model = LSTM(

c_in=X_train.shape[2],

c_out=len(np.unique(y_train)),

hidden_size=128, # LSTM隐藏层大小

n_layers=2, # LSTM层数

bidirectional=True, # 是否双向

dropout=0.1 # Dropout比例

)

5.4.4 TST (Time Series Transformer)

from tsai.models.TST import TST

model = TST(

c_in=X_train.shape[2],

c_out=len(np.unique(y_train)),

seq_len=X_train.shape[1],

d_model=128, # Transformer维度

n_heads=8, # 注意力头数

n_layers=3, # Transformer层数

d_ff=256, # 前馈网络维度

dropout=0.1,

max_seq_len=X_train.shape[1]

)

5.5 查看模型结构

打印模型结构

print(model)

查看参数量

total_params = sum(p.numel() for p in model.parameters())

trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)

print(f"总参数量: {total_params:,}")

print(f"可训练参数量: {trainable_params:,}")

测试前向传播

with torch.no_grad():

test_input = torch.randn(2, X_train.shape[1], X_train.shape[2])

test_output = model(test_input)

print(f"输入形状: {test_input.shape}")

print(f"输出形状: {test_output.shape}")

工程注意事项:

✅ 优先尝试InceptionTime,适合大多数场景

✅ 模型参数量控制在数据量的1/10以下,避免过拟合

✅ 如果数据量小(<1000),选择参数少的模型(ResNet, FCN)

❌ 不要盲目追求复杂模型,简单模型往往效果更好

  1. 步骤4:创建学习器
    6.1 步骤目的
    Learner是tsai的核心类,整合了模型、数据、损失函数、优化器和训练逻辑。

6.2 基础创建方式

from tsai.learner import *

最简单的创建方式(分类任务)

learn = Learner(

dls, # 数据加载器

model, # 模型

loss_func=LabelSmoothingCrossEntropy(), # 损失函数

metrics=[accuracy] # 评估指标

)

6.3 完整参数配置

from functools import partial

from torch.optim import Adam

learn = Learner(

dls,

model,

loss_func=LabelSmoothingCrossEntropy(eps=0.1), # Label smoothing

opt_func=partial(Adam, lr=1e-3, weight_decay=1e-4), # 优化器

metrics=[accuracy, F1Score(average='macro')], # 评估指标

cbs=[ # 回调函数

ShowGraphCallback(), # 显示训练曲线

SaveModelCallback(monitor='accuracy', fname='best_model'), # 保存最佳模型

EarlyStoppingCallback(monitor='valid_loss', patience=10) # 早停

],

path='./models' # 模型保存路径

)

6.4 损失函数选择

分类任务

from tsai.losses import *

1. 标准交叉熵(最常用)

loss_func = nn.CrossEntropyLoss()

2. Label Smoothing交叉熵(推荐,防止过拟合)

loss_func = LabelSmoothingCrossEntropy(eps=0.1)

3. Focal Loss(处理类别不平衡)

loss_func = FocalLoss(alpha=1, gamma=2)

回归任务

loss_func = nn.MSELoss() # 均方误差

loss_func = nn.L1Loss() # 平均绝对误差

损失函数选择指南:

任务类型 推荐损失 使用场景

多分类(平衡) LabelSmoothingCrossEntropy 类别数量相近

多分类(不平衡) FocalLoss 某些类别样本很少

二分类 BCEWithLogitsLoss 0/1分类

回归 MSELoss 预测连续值

6.5 评估指标设置

from tsai.metrics import *

from sklearn.metrics import accuracy_score, f1_score

分类指标

metrics = [

accuracy, # 准确率

RocAucBinary(), # 二分类AUC

RocAucMulti(), # 多分类AUC

F1Score(average='macro'), # F1分数

Precision(average='macro'), # 精确率

Recall(average='macro') # 召回率

]

回归指标

metrics = [

mae, # 平均绝对误差

rmse, # 均方根误差

mse, # 均方误差

R2Score() # R²分数

]

创建learner时指定

learn = Learner(dls, model, loss_func, metrics=metrics)

6.6 回调函数(Callbacks)

回调函数用于在训练过程中执行特定操作:

from tsai.callback.core import *

1. 可视化训练过程

ShowGraphCallback() # 实时显示loss和metrics曲线

2. 保存最佳模型

SaveModelCallback(

monitor='accuracy', # 监控的指标

fname='best_model', # 保存的文件名

every_epoch=False, # 是否每个epoch都保存

with_opt=False # 是否保存优化器状态

)

3. 早停(Early Stopping)

EarlyStoppingCallback(

monitor='valid_loss', # 监控验证损失

min_delta=0.01, # 最小改善阈值

patience=10 # 容忍多少个epoch没有改善

)

4. 学习率调度

ReduceLROnPlateau(

monitor='valid_loss',

factor=0.5, # 学习率衰减因子

patience=5

)

5. 混合精度训练(加速训练)

MixedPrecision()

6. TensorBoard记录

TensorBoardCallback(log_dir='runs/experiment1')

组合使用

learn = Learner(

dls, model, loss_func, metrics,

cbs=[

ShowGraphCallback(),

SaveModelCallback(monitor='accuracy'),

EarlyStoppingCallback(monitor='valid_loss', patience=10)

]

)

工程注意事项:

✅ 分类任务推荐使用LabelSmoothingCrossEntropy

✅ 始终添加SaveModelCallback保存最佳模型

✅ 长时间训练添加EarlyStoppingCallback避免浪费

✅ GPU训练时使用MixedPrecision()加速

  1. 步骤5:学习率查找
    7.1 步骤目的
    学习率是最重要的超参数。lr_find通过尝试不同的学习率,帮助找到最优的学习率范围。

7.2 使用lr_find

运行学习率查找器

lr_min, lr_steep = learn.lr_find(

start_lr=1e-7, # 起始学习率

end_lr=10, # 结束学习率

num_it=100, # 迭代次数

stop_div=True, # loss发散时停止

suggest_funcs=(minimum, steep) # 建议函数

)

print(f"建议的最小学习率: {lr_min:.2e}")

print(f"建议的最陡学习率: {lr_steep:.2e}")

可视化学习率-损失曲线

learn.recorder.plot_lr_find()

7.3 解读lr_find结果

lr_find会生成一条曲线:

- 横轴:学习率(对数刻度)

- 纵轴:损失值

选择学习率的原则:

1. minimum: 损失最小值对应的学习率

2. steep: 损失下降最快的点(推荐)

3. 通常选择steep或略小于steep的值

示例输出

suggested LR: 1.58E-03 (steep) or 3.98E-03 (minimum)

可视化示例:

学习率曲线通常是这样的:

Loss | /

| /

| / <-- steep点(推荐)

| /

| / \ <-- minimum点

|/ ___

±----------> Learning Rate (log scale)

1e-7 1e-2

7.4 学习率选择技巧

策略1:使用steep(最常用)

lr = lr_steep

策略2:使用steep的一半(更保守)

lr = lr_steep / 2

策略3:手动选择(看曲线)

lr = 1e-3 # 根据曲线手动选择

策略4:使用范围(用于fit_one_cycle)

lr_max = lr_steep

lr_min = lr_max / 10

工程注意事项:

✅ 每次改变数据或模型后都重新运行lr_find

✅ 如果曲线很平,说明学习率范围设置不当,调整start_lr和end_lr

✅ 选择loss下降最快的点,而不是最低点

❌ 不要选择loss开始上升的区域

  1. 步骤6:模型训练
    8.1 步骤目的
    使用训练数据训练模型,同时在验证集上评估性能。

8.2 fit_one_cycle(推荐方法)

使用1cycle学习率策略训练

learn.fit_one_cycle(

n_epoch=40, # 训练轮数

lr_max=1e-3, # 最大学习率(使用lr_find的结果)

wd=1e-4, # 权重衰减(L2正则化)

pct_start=0.3, # warmup阶段占比(前30%)

div=25.0, # 初始学习率除数: lr_start = lr_max/div

div_final=1e5 # 最终学习率除数: lr_end = lr_max/div_final

)

训练完成后查看结果

learn.recorder.plot_loss() # 查看损失曲线

learn.recorder.plot_metrics() # 查看指标曲线

fit_one_cycle的优势:

动态调整学习率(先增后减)

结合动量调度

训练速度快,收敛好

通常比固定学习率效果更好

8.3 标准训练方法

使用固定学习率训练

learn.fit(

n_epoch=40,

lr=1e-3,

wd=1e-4

)

或者分阶段训练(先高学习率,再低学习率)

learn.fit(10, lr=1e-2, wd=1e-4) # 阶段1

learn.fit(10, lr=1e-3, wd=1e-4) # 阶段2

learn.fit(20, lr=1e-4, wd=1e-4) # 阶段3(fine-tuning)

8.4 训练过程监控

训练过程中会实时显示:

epoch train_loss valid_loss accuracy time

0 0.693147 0.684932 0.575000 00:03

1 0.650234 0.640123 0.625000 00:03

...

查看训练历史

print(learn.recorder.values)

查看最佳结果

print(f"最佳验证损失: {min(learn.recorder.values, key=lambda x: x[1])[1]:.4f}")

print(f"最佳准确率: {max(learn.recorder.values, key=lambda x: x[2])[2]:.4f}")

8.5 继续训练

如果训练中断或想继续训练

learn.fit_one_cycle(20, lr_max=1e-4) # 使用更小的学习率继续

或者从保存的检查点恢复

learn.load('best_model')

learn.fit_one_cycle(20, lr_max=1e-4)

8.6 冻结和解冻(Fine-tuning)

冻结部分层(用于迁移学习)

learn.freeze() # 冻结所有层

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

解冻所有层

learn.unfreeze()

learn.fit_one_cycle(30, lr_max=1e-4) # 用更小的学习率训练所有层

部分解冻

learn.freeze_to(-2) # 冻结除最后2层外的所有层

工程注意事项:

✅ 优先使用fit_one_cycle,效果通常更好

✅ 训练轮数设置在30-50之间,使用早停避免过拟合

✅ 权重衰减(wd)推荐值:1e-4 到 1e-2

✅ 定期保存checkpoint,避免训练中断损失

❌ 不要设置过大的学习率,会导致训练不稳定

  1. 步骤7:模型评估
    9.1 步骤目的
    在验证集上全面评估模型性能,识别潜在问题。

9.2 基础评估

获取验证集的损失和指标

valid_results = learn.validate()

print(f"验证损失: {valid_results[0]:.4f}")

print(f"验证准确率: {valid_results[1]:.4f}")

或者单独获取

valid_loss = learn.validate()[0]

valid_acc = learn.validate()[1]

9.3 获取预测结果

获取验证集的预测结果

preds, targets = learn.get_preds()

preds: 模型输出的概率(分类)或值(回归)

targets: 真实标签

print(f"预测形状: {preds.shape}") # (N, num_classes) 或 (N,)

print(f"标签形状: {targets.shape}") # (N,)

转换为numpy数组

preds_np = preds.numpy()

targets_np = targets.numpy()

分类任务:获取预测类别

if len(preds.shape) > 1:

y_pred = preds.argmax(dim=1).numpy()

else:

y_pred = preds.numpy()

y_true = targets.numpy()

9.4 详细评估指标

from sklearn.metrics import (

accuracy_score,

precision_score,

recall_score,

f1_score,

classification_report,

confusion_matrix

)

分类报告

print("=" * 50)

print("分类报告")

print("=" * 50)

print(classification_report(y_true, y_pred))

混淆矩阵

cm = confusion_matrix(y_true, y_pred)

print("\n混淆矩阵:")

print(cm)

各类别的准确率

from sklearn.metrics import accuracy_score

for i in range(len(np.unique(y_true))):

mask = y_true == i

acc_i = accuracy_score(y_true[mask], y_pred[mask])

print(f"类别 {i} 准确率: {acc_i:.4f}")

9.5 可视化评估结果

import matplotlib.pyplot as plt

import seaborn as sns

1. 混淆矩阵热力图

plt.figure(figsize=(10, 8))

sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')

plt.ylabel('真实标签')

plt.xlabel('预测标签')

plt.title('混淆矩阵')

plt.show()

2. ROC曲线(二分类)

from sklearn.metrics import roc_curve, auc

if preds.shape[1] == 2: # 二分类

fpr, tpr, _ = roc_curve(y_true, preds[:, 1].numpy())

roc_auc = auc(fpr, tpr)

复制代码
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, label=f'ROC curve (AUC = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], 'k--')
plt.xlabel('假阳性率')
plt.ylabel('真阳性率')
plt.title('ROC曲线')
plt.legend()
plt.show()

3. 预测概率分布

plt.figure(figsize=(12, 5))

for i in range(preds.shape[1]):

plt.subplot(1, preds.shape[1], i+1)

plt.hist(preds[targets==i, i].numpy(), bins=20, alpha=0.7)

plt.title(f'类别 {i} 的预测概率')

plt.xlabel('预测概率')

plt.ylabel('样本数')

plt.tight_layout()

plt.show()

9.6 查看预测样本

使用tsai内置的可视化

learn.show_results(max_n=9, figsize=(15, 10))

或手动可视化预测错误的样本

errors = np.where(y_pred != y_true)[0]

print(f"错误预测的样本数: {len(errors)}")

可视化前几个错误样本

fig, axes = plt.subplots(3, 3, figsize=(15, 10))

for i, idx in enumerate(errors[:9]):

ax = axes[i//3, i%3]

ax.plot(X_valid[idx, :, 0])

ax.set_title(f'真实: {y_true[idx]}, 预测: {y_pred[idx]}')

plt.tight_layout()

plt.show()

9.7 测试集评估

如果有单独的测试集

test_dl = dls.test_dl(X_test)

获取测试集预测

test_preds, test_targets = learn.get_preds(dl=test_dl)

评估

test_y_pred = test_preds.argmax(dim=1).numpy()

test_y_true = test_targets.numpy()

test_acc = accuracy_score(test_y_true, test_y_pred)

print(f"测试集准确率: {test_acc:.4f}")

工程注意事项:

✅ 始终在验证集上评估,避免在测试集上调参

✅ 关注混淆矩阵,了解哪些类别容易混淆

✅ 检查错误样本,理解模型的弱点

✅ 如果某些类别表现很差,考虑数据增强或调整损失函数

  1. 步骤8:模型预测
    10.1 步骤目的
    使用训练好的模型对新数据进行预测。

10.2 单样本预测

预测单个样本

test_sample = X_valid[0:1] # 保持3D形状 (1, seq_len, features)

方法1:使用predict(返回概率、类别、原始输出)

prob, pred_class, raw_output = learn.predict(test_sample)

print(f"预测类别: {pred_class}")

print(f"预测概率: {prob}")

print(f"各类别概率:")

for i, p in enumerate(prob):

print(f" 类别 {i}: {p:.4f}")

10.3 批量预测

预测多个样本

test_data = X_valid[:100]

方法1:使用get_preds

preds, _ = learn.get_preds(dl=dls.test_dl(test_data))

获取预测类别

pred_classes = preds.argmax(dim=1).numpy()

print(f"预测类别: {pred_classes}")

方法2:直接使用模型

learn.model.eval()

with torch.no_grad():

test_tensor = torch.tensor(test_data, dtype=torch.float32)

outputs = learn.model(test_tensor)

pred_probs = torch.softmax(outputs, dim=1)

pred_classes = pred_probs.argmax(dim=1).numpy()

10.4 带数据预处理的预测

实际应用中,新数据需要和训练数据一样的预处理

def predict_with_preprocessing(learn, raw_data, scaler):

"""

完整的预测流程:预处理 -> 预测 -> 后处理

复制代码
Args:
    learn: 训练好的learner
    raw_data: 原始数据 (N, T, d)
    scaler: 训练时使用的StandardScaler

Returns:
    predictions: 预测结果
"""
# 1. 预处理(和训练时保持一致)
data_flat = raw_data.reshape(-1, raw_data.shape[-1])
data_scaled = scaler.transform(data_flat).reshape(raw_data.shape)

# 2. 预测
preds, _ = learn.get_preds(dl=learn.dls.test_dl(data_scaled))
pred_classes = preds.argmax(dim=1).numpy()

# 3. 返回结果
return pred_classes, preds.numpy()

使用示例

pred_classes, pred_probs = predict_with_preprocessing(learn, new_data, scaler)

10.5 批量预测并导出结果

预测并保存结果到CSV

def batch_predict_and_save(learn, test_data, output_file='predictions.csv'):

"""

批量预测并保存结果

"""

预测

preds, _ = learn.get_preds(dl=learn.dls.test_dl(test_data))

pred_classes = preds.argmax(dim=1).numpy()

pred_probs = preds.numpy()

复制代码
# 创建DataFrame
results = pd.DataFrame({
    'sample_id': range(len(pred_classes)),
    'predicted_class': pred_classes,
    'confidence': pred_probs.max(axis=1)
})

# 添加每个类别的概率
for i in range(pred_probs.shape[1]):
    results[f'prob_class_{i}'] = pred_probs[:, i]

# 保存
results.to_csv(output_file, index=False)
print(f"预测结果已保存到: {output_file}")

return results

使用

results_df = batch_predict_and_save(learn, X_test, 'test_predictions.csv')

工程注意事项:

✅ 新数据必须经过和训练数据相同的预处理

✅ 检查预测置信度,对低置信度样本进行人工审核

✅ 生产环境中,保存预测结果的时间戳和版本信息

✅ 监控模型预测的置信度分布变化(可能表示数据漂移)

  1. 步骤9:模型保存与加载
    11.1 步骤目的
    保存训练好的模型,用于后续部署或继续训练。

11.2 保存模型的三种方式

方式1:保存模型权重(最常用)

保存模型权重

learn.save('my_model') # 保存到 models/my_model.pth

加载模型权重

learn.load('my_model')

指定保存路径

learn.save('/path/to/models/my_model')

适用场景:

只保存模型参数,文件小

需要相同的架构来加载

适合实验和调参阶段

方式2:导出完整learner(推荐生产环境)

导出完整的learner(包括模型、数据预处理、配置)

learn.export('my_model.pkl')

加载learner

learn_loaded = load_learner('my_model.pkl')

直接用于预测

preds = learn_loaded.predict(new_data)

适用场景:

部署到生产环境

保存完整的训练状态

方便他人使用

方式3:保存PyTorch模型(最灵活)

保存完整模型

torch.save(learn.model, 'model_complete.pth')

加载完整模型

model_loaded = torch.load('model_complete.pth')

或只保存state_dict(推荐)

torch.save(learn.model.state_dict(), 'model_weights.pth')

加载state_dict

model = InceptionTime(...) # 需要先创建相同架构

model.load_state_dict(torch.load('model_weights.pth'))

11.3 保存完整的模型包

import os

import pickle

from datetime import datetime

def save_model_package(learn, scaler, config, save_dir='./saved_models'):

"""

保存完整的模型包

"""

创建保存目录

timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')

model_dir = os.path.join(save_dir, f'model_{timestamp}')

os.makedirs(model_dir, exist_ok=True)

复制代码
# 1. 保存learner
learn.export(os.path.join(model_dir, 'learner.pkl'))

# 2. 保存模型权重
learn.save(os.path.join(model_dir, 'weights'))

# 3. 保存scaler
with open(os.path.join(model_dir, 'scaler.pkl'), 'wb') as f:
    pickle.dump(scaler, f)

# 4. 保存配置
config['save_date'] = timestamp
with open(os.path.join(model_dir, 'config.pkl'), 'wb') as f:
    pickle.dump(config, f)

# 5. 保存训练历史
history = {
    'train_loss': learn.recorder.losses,
    'valid_loss': [v[0] for v in learn.recorder.values],
    'metrics': [v[1:] for v in learn.recorder.values]
}
with open(os.path.join(model_dir, 'history.pkl'), 'wb') as f:
    pickle.dump(history, f)

print(f"模型包已保存到: {model_dir}")
return model_dir

def load_model_package(model_dir):

"""

加载完整的模型包

"""

1. 加载learner

learn = load_learner(os.path.join(model_dir, 'learner.pkl'))

复制代码
# 2. 加载scaler
with open(os.path.join(model_dir, 'scaler.pkl'), 'rb') as f:
    scaler = pickle.load(f)

# 3. 加载配置
with open(os.path.join(model_dir, 'config.pkl'), 'rb') as f:
    config = pickle.load(f)

print(f"模型包加载成功")
print(f"保存日期: {config['save_date']}")

return learn, scaler, config

工程注意事项:

✅ 生产环境使用export()保存完整learner

✅ 保存scaler和配置,确保预测时数据处理一致

✅ 使用版本号管理模型,便于回滚

✅ 记录每个模型的训练参数和性能指标

  1. tsai工程实践注意事项
    12.1 数据相关
    数据质量检查清单:

def check_data_quality(X, y):

"""检查数据质量"""

print("=" * 50)

print(f"样本数: {len(X)}")

print(f"序列长度: {X.shape[1]}")

print(f"特征数: {X.shape[2]}")

print(f"类别数: {len(np.unique(y))}")

print(f"NaN数量: {np.isnan(X).sum()}")

print(f"数据范围: [{X.min():.4f}, {X.max():.4f}]")

print(f"类别分布: {np.bincount(y)}")

print("=" * 50)

处理类别不平衡:

使用加权损失

from tsai.losses import FocalLoss

loss_func = FocalLoss(alpha=1, gamma=2)

或者数据增强

batch_tfms = [TSTimeWarp(p=0.5), TSGaussianNoise(p=0.3)]

12.2 训练相关

防止过拟合:

数据增强

权重衰减(wd=1e-3)

Dropout

早停

减小模型复杂度

加速训练:

混合精度:learn.to_fp16()

增大batch size

更多workers:num_workers=8

训练稳定性:

梯度裁剪

学习率预热

Batch Normalization

12.3 调试技巧

快速调试流程:

1. 使用小数据集

X_debug = X_train[:100]

y_debug = y_train[:100]

2. 检查是否能过拟合

dls_debug = get_ts_dls(X_debug, y_debug, X_debug, y_debug, bs=16)

learn = Learner(dls_debug, model, loss_func, metrics)

learn.fit(10, lr=1e-2) # 应该能达到很高的准确率

检查模型输出:

with torch.no_grad():

xb, yb = dls.one_batch()

output = learn.model(xb)

print(f"输入形状: {xb.shape}")

print(f"输出形状: {output.shape}")

print(f"输出范围: [{output.min():.4f}, {output.max():.4f}]")

  1. 完整代码示例

"""

ECG心电图分类 - 完整训练流程示例

"""

from tsai.all import *

import numpy as np

from sklearn.preprocessing import StandardScaler

from sklearn.metrics import classification_report, confusion_matrix

import pickle

1. 加载数据

X_train, y_train, X_valid, y_valid = get_UCR_data('ECG200')

2. 数据预处理

scaler = StandardScaler()

X_train_flat = X_train.reshape(-1, X_train.shape[-1])

scaler.fit(X_train_flat)

X_train_scaled = scaler.transform(X_train_flat).reshape(X_train.shape)

X_valid_scaled = scaler.transform(X_valid.reshape(-1, X_valid.shape[-1])).reshape(X_valid.shape)

3. 创建数据加载器

dls = get_ts_dls(

X_train_scaled, y_train,

X_valid_scaled, y_valid,

bs=64,

num_workers=4

)

4. 创建模型

model = InceptionTime(

c_in=X_train.shape[2],

c_out=len(np.unique(y_train)),

nf=32,

depth=6

)

5. 创建学习器

learn = Learner(

dls,

model,

loss_func=LabelSmoothingCrossEntropy(eps=0.1),

metrics=[accuracy, F1Score(average='macro')],

cbs=[

ShowGraphCallback(),

SaveModelCallback(monitor='accuracy', fname='best_model'),

EarlyStoppingCallback(monitor='valid_loss', patience=10)

]

)

6. 学习率查找

lr_min, lr_steep = learn.lr_find()

print(f"建议学习率: {lr_steep:.2e}")

7. 训练模型

learn.fit_one_cycle(40, lr_max=lr_steep, wd=1e-4)

8. 评估模型

learn.load('best_model')

valid_loss, acc, f1 = learn.validate()

print(f"验证准确率: {acc:.4f}")

9. 获取预测并评估

preds, targets = learn.get_preds()

y_pred = preds.argmax(dim=1).numpy()

y_true = targets.numpy()

print(classification_report(y_true, y_pred))

10. 保存模型

learn.export('ecg_classifier.pkl')

with open('scaler.pkl', 'wb') as f:

pickle.dump(scaler, f)

print("训练完成!")

  1. 常见问题解决方案

Q: 数据形状错误

解决:确保数据是 (N, T, d) 形状

if len(X.shape) == 2:

X = X[..., np.newaxis]

Q: 损失不下降

1. 重新查找学习率

lr_min, lr_steep = learn.lr_find()

2. 检查数据归一化

3. 增加模型容量或训练轮数

Q: 过拟合

1. 增加数据增强

batch_tfms = [TSTimeWarp(p=0.5), TSGaussianNoise(p=0.3)]

2. 增加权重衰减

learn = Learner(..., opt_func=partial(Adam, wd=1e-2))

3. 使用早停

cbs = [EarlyStoppingCallback(patience=10)]

Q: CUDA out of memory

1. 减小batch size

dls = get_ts_dls(..., bs=32)

2. 使用混合精度

learn.to_fp16()

3. 清理缓存

torch.cuda.empty_cache()

Q: Windows multiprocessing错误

设置num_workers=0

dls = get_ts_dls(..., num_workers=0)

总结

核心流程回顾:

✅ 数据准备:加载、归一化、划分

✅ 数据加载器:get_ts_dls创建

✅ 模型创建:选择合适架构

✅ 学习器:组装所有组件

✅ 学习率查找:lr_find

✅ 模型训练:fit_one_cycle

✅ 模型评估:validate和get_preds

✅ 模型预测:predict

✅ 模型保存:export

关键要点:

数据预处理要一致

优先使用fit_one_cycle

添加早停和保存最佳模型

生产环境使用export()

相关推荐
m0_462605222 小时前
第N9周:seq2seq翻译实战-Pytorch复现-小白版
人工智能·pytorch·python
纪伊路上盛名在2 小时前
记1次BioPython Entrez模块Elink的debug
前端·数据库·python·debug·工具开发
CryptoRzz2 小时前
日本股票 API 对接实战指南(实时行情与 IPO 专题)
java·开发语言·python·区块链·maven
ss2732 小时前
考研加油上岸祝福弹窗程序
python
乾元2 小时前
基于时序数据的异常预测——短期容量与拥塞的提前感知
运维·开发语言·网络·人工智能·python·自动化·运维开发
江上清风山间明月2 小时前
使用python将markdown文件生成pdf文件
开发语言·python·pdf
凯_kyle2 小时前
Python 算法竞赛 —— 基础篇(更新ing)
笔记·python·算法
天远Date Lab2 小时前
Java微服务实战:聚合型“全能小微企业报告”接口的调用与数据清洗
java·大数据·python·微服务
ss2732 小时前
阻塞队列:ArrayBlockingQueue如何用Lock与Condition实现高效并发控制
开发语言·python