1. 项目概述
1.1 核心需求解析
你有没有遇到过这种情况:学了一堆深度学习理论,看了不少教程,但真要自己动手写个项目的时候,却不知道从哪里下手?我当年也是这么过来的,后来发现最快的学习路径就是找一个贴近实际场景的小项目,从头到尾走一遍完整流程。这个“新冠感染人数预测”项目,就是这样一个非常适合入门的实战题目。
先说清楚这个项目到底在干什么。它本质上是一个时间序列预测问题:给出一段时间内每日新增感染人数(或者累计确诊人数),让模型学习数据的变化规律,然后预测未来若干天的数字。这跟天气预报、股票价格预测、交通流量预测都是一个套路,所以学会了这个项目,你等于掌握了一类深度学习应用的通用法。
为什么说它适合入门?第一,数据是公开的,不需要你自己爬虫采集,网上有很多整理好的数据集,下载下来就能用;第二,数据形态简单,就是一列或者几列数字,不需要做复杂的图像预处理或者自然语言处理;第三,预测效果好不好一眼就能看出来,把预测曲线和真实曲线画在一张图上,差距摆在眼前,很直观。
这个项目的技术栈也很经典:Python + PyTorch + LSTM(长短期记忆网络),再加 Pandas、NumPy、Matplotlib 这些数据处理的常用库。其中 LSTM 是循环神经网络(RNN)的一种改进结构,专门用来处理序列数据,后面我会详细说它为什么适合这个场景。
1.2 项目到底能学到什么
很多初学者容易陷入一个误区:整天讨论深度学习框架哪个好、模型结构有多复杂,却忽略了工程实现的基本功。这个项目刚好帮你把这些基本功全部补上。
第一,环境搭建能力。在 Windows 系统配置深度学习环境是新手最容易翻车的地方,CUDA 版本不对、PyTorch 装不上、conda 环境冲突……各种问题层出不穷。我会在第四章专门讲环境配置,把我踩过的坑全部列出来。
第二,数据处理能力。真实项目里数据往往不是拿来就能用的,可能有空缺值、有异常值、量纲不一致。怎么清洗、怎么补全、怎么归一化,这些看起来不起眼的步骤,其实决定了模型效果的上限。我之前见过太多人模型调参调了一周没效果,最后发现是数据预处理出了问题。
第三,模型构建能力。从简单的全连接网络开始,到 LSTM 这种循环结构,再到训练循环、损失函数、优化器的选择,每一步都需要动手实现。这个项目规模不大,模型代码也就几十行,但麻雀虽小五脏俱全,深度学习的标准流程全都有。
第四,结果可视化和评测能力。光知道跑个训练还不行,你得会看学习曲线判断模型有没有收敛,会画预测图来判断效果好坏,会算误差指标来量化性能。这些都是实际工作中每天都用得到的技能。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据准备与分析
2.1 数据怎么获取,拿到手什么样
我建议直接用公开的 COVID-19 数据集,网上有很多机构实时维护着全球各地的疫情数据,格式基本都是 CSV 文件。以某公开数据集为例,你下载之后解压,会看到几个不同维度的表格:confirmed(累计确诊)、deaths(累计死亡)、recovered(累计治愈)。每行是一个地区,每列是一个日期,单元格里是对应当天的累计数值。
这里需要注意一个关键点:数据集里存的是累计值,不是每日新增值。如果你要预测的是每日新增,就得做一步差分计算,把相邻两天的累计值相减。很多新手在这个地方踩坑,买了数据就直接喂给模型,结果预测出来的东西完全没法看。
拿到数据之后,我通常先画一张整体走势图。把时间作为横轴、每日新增人数作为纵轴,看一眼数据的大致形态。你会发现这个序列有几个明显特征:有上升段、有峰值、有下降段,后面可能还有小规模的反弹。这种非平稳、有波动的序列,就是 LSTM 比较擅长处理的类型。
然后是数据检查。用 Pandas 加载 CSV 之后,第一件事是看有没有缺失值,方法很简单:
python复制import pandas as pd
df = pd.read_csv('covid_data.csv')
print(df.isnull().sum())
如果某一天的数据缺失了,最简单的处理方式是前后填充。我个人比较推荐线性插值(df.interpolate()),因为它比直接填 0 或者填前一日的值更平滑,对模型训练的干扰更小。还要顺手做一步数据类型转换,确保日期列是 datetime 格式、数值列是 float 格式,不然后面画图的时候会发现各种报错。
2.2 一个关键点:预测目标到底是什么
在动手写代码之前,心里必须想清楚一个问题:你要预测的是“每日新增”还是“累计确诊”?
这两个目标做出来的项目效果差别很大。预测累计确诊通常比较容易,因为累计值只会单调增长,曲线的整体趋势性很强,模型只要大致学到“一直在涨”这个规律,预测结果就不会太离谱。但这样做有点没意思,相当于考试只做送分题。
预测每日新增难度更高,也更接近真实需求。每日新增是累计值的差分,数值波动大、噪声多,模型需要真正学到数据的周期性趋势才能预测得准。我建议你做每日新增,挑战性强一点,学到的经验也更值钱。
另外一个细节是:你计划用过去多少天的数据来预测未来多少天?这就是时间序列预测里的窗口长度概念。比较常用的设置是“用过去 14 天预测未来 7 天”,也有的项目做“用过去 7 天预测未来 1 天”。窗口长度会直接影响模型复杂度:窗口越大,输入维度越高,模型需要学习的模式越丰富;但窗口也不是越大越好,窗口太长反而会引入太多历史噪声。
这里给一个实用建议:第一次跑通流程,用“过去 14 天预测未来 7 天”就好,既不会太简单,也不至于让模型难到学不动。
2.3 数据预处理,重要的不是代码而是思路
数据清洗完之后,下一步就是构造训练样本。时间序列不像图像分类那样可以直接把图片路径和标签对应起来,它需要人为地把连续序列“切成”一段一段的输入输出对。
假设我们有 200 天的每日新增数据,用过去 14 天预测未来 7 天,那第一条训练样本就是第 1~14 天的数据作为输入 X,第 15~21 天的数据作为输出 Y;第二条样本是第 2~15 天预测第 16~22 天,以此类推滑动窗口。这样算下来,总共能构造出大约 180 条样本,数据量虽然不大,但足够跑通一个入门模型了。
这里有个非常容易犯的错误:数据泄露。滑窗构造的数据,相邻的样本之间存在大量重叠。如果你随机打乱再切分训练集和验证集,验证集里就会混入很多跟训练集高度重叠的数据,导致验证指标看起来非常漂亮,但真实预测效果一塌糊涂。正确的做法是先按时间顺序切分,比如前 80% 的时间数据作为训练集,后 20% 作为验证集,然后再在各自范围内构造滑窗样本。用前面时间的数据预测后面时间的数据,才是真正意义上的预测。
归一化这一步也特别关键。每日新增人数的数值范围可能从几十到几万,直接喂给神经网络的话,会让梯度下降变得很不稳定。常用的做法是 MinMaxScaler,把数据压缩到 0 到 1 之间。公式很简单:(x - min) / (max - min)。但要注意,这个 min 和 max 必须只从训练集里统计,不能用全量数据的范围,否则又算是一种数据泄露。
3. 模型选择与原理
3.1 为什么是 LSTM,而不是 CNN 或者 Transformer
先回答一个我经常被问到的问题:预测感染人数这种东西,为什么要用专门处理序列的 LSTM?
简单来说,普通神经网络假设输入之间是互相独立的,但感染人数序列里,今天的数字跟昨天、前天的数字强相关。比如确诊人数通常按天有波动规律、有潜伏期带来的滞后效应。对这种存在先后依赖关系的序列数据,循环神经网络(RNN)的设计就天然契合:它一个时刻一个时刻地读入过去的数据,同时在内部维护一个“记忆”状态,把新的信息和历史记忆融合,再传递给下一个时刻。
不过经典 RNN 有个比较麻烦的毛病——梯度消失。处理长序列的时候,前面的信息经过多轮传播,梯度越来越小,模型很难学到长期依赖关系。LSTM 的解决方案是引入门控机制,通过“遗忘门”“输入门”“输出门”三个门控单元来控制信息保留量。你可以把它理解成一个升级版的 RNN,自带一个记忆档案袋,既能记住很久之前的趋势(比如一个月前的疫情高峰模式),又能过滤掉无关紧要的短期波动。
那为什么不直接用 Transformer?说实话,这个项目用 Transformer 也能做,效果甚至可能更好。但 Transformer 的数据需求量大、训练配置复杂,对初学者来说,光把注意力机制的代码调通就够头疼的了。入门项目的核心目标是快速跑通流程、理解核心概念,LSTM 在这个尺度下性价比最高,这也是它在疫情预测这类中小规模时间序列场景中依旧实用的原因。
3.2 模型的输入输出长什么样
确定用 LSTM 之后,接下来要弄清楚数据在进入模型前后是什么形态。这一点如果搞不明白,调试代码的时候会为一个 shape 不匹配的报错卡住很久。
先说输入。滑窗构造出来的每一条样本 X,形状是 (seq_len, input_size)。seq_len 就是时间步长度,这里对应 14;input_size 是每个时间步的特征维度,如果我们只使用“每日新增人数”这一个特征,那 input_size 就是 1。所以一条样本的形状是 (14, 1),本质上就是一个长度为 14 的序列,每个时间点上只有一个数值。
模型接收到这样的输入之后,LSTM 层会在内部循环 14 次,每次处理一个时间点,输出一个隐藏状态。14 步结束之后,最后一个隐藏状态就汇总了整个序列的信息,把它接上一个全连接层,映射到输出维度。如果我们要预测未来 7 天的值,输出 dim 就是 7,全连接层输出形状为 (batch_size, 7)。
这里要注意 batch 维度的概念。我们在训练的时候不是一次只喂一条样本,而是一次喂一个 batch 的样本,PyTorch 要求输入形状是 (batch_size, seq_len, input_size)。所以数据在进模型之前,要先用 reshape 调整维度,把二维数组变成三维张量。
3.3 从数据到模型的完整流转过程
我习惯把整个数据流转分成四个阶段,这样不管代码写到哪个部分,都能心里有数。
第一个阶段是数据准备。用滑窗切出大量的 (X, Y) 对,X 是过去 14 天的序列,Y 是对应未来 7 天的标签。第二个阶段是数据装载。用 PyTorch 的 Dataset 和 DataLoader 把数据包装起来,设置好 batch size,让训练循环可以批量取数据。第三个阶段是模型前向传播。形状为 (batch, 14, 1) 的 X 经过 LSTM 层得到输出,再通过全连接层变成 (batch, 7) 的预测结果。第四个阶段是反向传播更新参数。用均方误差(MSE)作为损失函数,计算预测值跟真实 Y 的误差,反向传播更新模型里的权重参数。
这套流程不仅适用于这个疫情预测项目,你以后做股票预测、销量预测、交通流量预测,整体的数据流转逻辑都是完全一样的。差别只在于数据特征维度可能变多,但套路不变。
4. Windows 系统深度学习环境搭建
4.1 新手必看:环境配置的最省心方案
项目本身的技术难度其实还好,但“在 Windows 上装好 PyTorch”这件事,我见过太多人卡在这里好几天。网上教程五花八门,有的让你装 Anaconda,有的让你直接装 Python,还有的各种 CUDA 版本看得人头晕。这里分享一下我试过的最省心方案。
我个人强烈建议用 Anaconda 管理 Python 环境,因为版本隔离能力强,装烂了可以直接删掉重建,不会影响电脑上其他 Python 程序。安装步骤很简单:去 Anaconda 官网下载 Windows 安装包,一路 Next 装完,之后所有操作都在 Anaconda Prompt 里执行。
打开 Anaconda Prompt 后,第一步创建独立环境:
bash复制conda create -n covid python=3.9
Python 版本选 3.9 或者 3.10 就好,太新的版本可能有兼容性问题,太老的有一些新库不支持。创建完之后激活环境:
bash复制conda activate covid
接下来安装 PyTorch。这一步最容易翻车,关键是要找到跟自己显卡匹配的版本。如果你用的是 NVIDIA 显卡,在命令行输入 nvidia-smi 看一眼右上角 CUDA 版本,比如显示 12.1,然后去 PyTorch 官网找到对应安装命令。如果要装 CPU 版,就选 CPU 版本对应的命令,但 CPU 版训练速度慢很多,建议还是用 GPU。
bash复制# 你需要根据自己的CUDA版本到PyTorch官网复制对应命令
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
装完之后验证一下,这一步不能省:
python复制import torch
print(torch.__version__)
print(torch.cuda.is_available())
如果能输出版本号且 cuda.is_available() 返回 True,说明 GPU 环境没问题了。剩下的库都比较简单:
bash复制pip install pandas numpy matplotlib scikit-learn
4.2 配置环境常见的翻车现场
第一个高频坑就是装完 PyTorch 之后 torch.cuda.is_available() 还是返回 False。原因大概率是装成了 CPU 版,或者 CUDA 版本和 PyTorch 不匹配。解决办法也很简单,先把 PyTorch 卸了重装,用 pip uninstall torch 卸载干净,然后重新核对一下显卡驱动支持的 CUDA 版本,再去官网选对应版本安装。
第二个坑是 conda 环境冲突。有些人在 base 环境里直接 pip install 了一堆库,结果新项目需要不同版本的依赖,一运行就各种报错。我踩了两次坑之后养成一个习惯:每个项目都新建独立 conda 环境,需要什么就装什么,永远不用 base 环境跑项目。
第三个坑是网络问题,有些依赖包下载速度特别慢。解决方案是把 pip 源换成国内镜像源,比如清华源:
bash复制pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
换完之后下载速度会快很多。还有一个小技巧:如果某一行代码一直爆红,别急着问群友,先看清楚报错信息最后的几行,那个才是真正定位问题的关键。
5. 代码实现与实操过程
5.1 数据加载与预处理代码实战
环境准备好之后,我们就可以开始写代码了。先创建一个项目文件夹,把下载好的 CSV 数据放进去,然后新建一个 Python 文件,我习惯命名为 train.py。整个代码我拆成模块来写,这样后期调参的时候不用来回翻找。
python复制import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
# ========== 1. 数据加载 ==========
df = pd.read_csv('covid_data.csv', parse_dates=['date'])
df = df.sort_values('date') # 按时间升序排列
# 用每日新增人数作为预测目标
df['daily_new'] = df['confirmed'].diff()
df = df.dropna().reset_index(drop=True)
# 取出目标列
data = df['daily_new'].values.astype(float)
这一步的逻辑很简单:读 CSV、按日期排序、做差分得到每日新增。如果数据源本身已经是每日新增值,那差分的步骤可以省略。我建议无论数据源是什么格式,都先画一下图确认数据形态。
接下来是滑窗构造样本和归一化:
python复制# ========== 2. 数据归一化 ==========
seq_len = 14 # 用过去14天预测
pred_len = 7 # 预测未来7天
train_ratio = 0.8
scaler = MinMaxScaler()
# 注意:fit和transform都只作用于训练集部分
train_len = int(len(data) * train_ratio)
train_data = data[:train_len]
test_data = data[train_len - seq_len:] # 留出seq_len作为初始窗口
scaler.fit(train_data.reshape(-1, 1))
train_scaled = scaler.transform(train_data.reshape(-1, 1)).flatten()
test_scaled = scaler.transform(test_data.reshape(-1, 1)).flatten()
这里有两个细节想提醒你。第一,scaler.fit 只作用在训练集部分,测试集的归一化用的是训练集的 min 和 max,这是防止数据泄露的关键操作。第二,test_data 的起始点往前挪了 seq_len 个位置,因为测试集的每条样本也需要用过去 14 天的数据作为输入,如果直接拿第 80% 天之后的数据作为测试,那第一条样本的输入窗口就落在训练集里了,逻辑上说不通。
做滑窗的函数可以统一封装一下:
python复制def create_sequences(data, seq_len, pred_len):
X, y = [], []
for i in range(len(data) - seq_len - pred_len + 1):
X.append(data[i:i + seq_len])
y.append(data[i + seq_len:i + seq_len + pred_len])
return np.array(X), np.array(y)
X_train, y_train = create_sequences(train_scaled, seq_len, pred_len)
X_test, y_test = create_sequences(test_scaled, seq_len, pred_len)
# 转换成PyTorch tensor,并增加特征维度
X_train = torch.tensor(X_train, dtype=torch.float32).unsqueeze(-1)
y_train = torch.tensor(y_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32).unsqueeze(-1)
y_test = torch.tensor(y_test, dtype=torch.float32)
unsqueeze(-1) 的作用是把数据从 (样本数, 时间步) 变成 (样本数, 时间步, 特征数),因为我们每个时间步只有一个特征。
5.2 模型定义与训练代码实战
模型定义是项目核心。我们用两层 LSTM 加一个全连接输出层,中间加一下 Dropout 防止过拟合,整体代码长这样:
python复制# ========== 3. 定义LSTM模型 ==========
class LSTMPredictor(nn.Module):
def __init__(self, input_size=1, hidden_size=64, num_layers=2, output_size=7):
super().__init__()
self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
# x shape: (batch, seq_len, input_size)
out, _ = self.lstm(x) # out shape: (batch, seq_len, hidden_size)
out = out[:, -1, :] # 取最后一个时间步的输出
out = self.fc(out)
return out
model = LSTMPredictor()
print(model)
这里解释一下两个关键参数。hidden_size=64 是 LSTM 内部记忆单元的维度,可以理解成模型“脑子”的大小,64 对这个规模的数据量已经够用了,调大不一定更好,反而容易过拟合。num_layers=2 表示堆叠两层 LSTM,第一层的输出作为第二层的输入,让模型能学习到更高层次的时序特征,对于入门项目来说两层够了,堆太多层训练难度会成倍增加。
训练循环是标准的 PyTorch 流程:
python复制# ========== 4. 训练配置 ==========
epochs = 200
batch_size = 32
learning_rate = 0.001
train_dataset = torch.utils.data.TensorDataset(X_train, y_train)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
# ========== 5. 训练循环 ==========
train_losses = []
for epoch in range(epochs):
model.train()
epoch_loss = 0
for X_batch, y_batch in train_loader:
optimizer.zero_grad()
output = model(X_batch)
loss = criterion(output, y_batch)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
avg_loss = epoch_loss / len(train_loader)
train_losses.append(avg_loss)
if (epoch + 1) % 20 == 0:
print(f'Epoch [{epoch+1}/{epochs}], Loss: {avg_loss:.6f}')
训练过程中要留意 loss 的变化趋势。正常情况是 loss 在前几十轮快速下降,然后逐渐趋于平稳。我的经验是,如果 loss 在 100 轮之后还在大幅度震荡,说明学习率可能太大;如果下降非常慢,可能是学习率太小。0.001 这个初始值通常是一个比较折中的选择。
GPU 加速在这个数据集上也值得提一下。把 model、X_batch、y_batch 都 .to('cuda') 就能跑 GPU,代码改动就几行,但训练速度能快几十倍。如果机器没独显,直接用 CPU 也能跑,就是慢一些。
5.3 预测与结果可视化
训练完之后,用测试集做预测,然后把结果反归一化画出来,这里是整个项目最“爽”的部分:
python复制# ========== 6. 预测与可视化 ==========
model.eval()
with torch.no_grad():
y_pred_scaled = model(X_test).numpy()
# 反归一化
y_pred = scaler.inverse_transform(y_pred_scaled.reshape(-1, 1)).flatten()
y_true = scaler.inverse_transform(y_test.numpy().reshape(-1, 1)).flatten()
# 画图
plt.figure(figsize=(12, 5))
plt.plot(y_true, label='真实值', linewidth=2)
plt.plot(y_pred, label='预测值', linestyle='--')
plt.legend()
plt.title('新冠每日新增人数预测结果')
plt.show()
如果数据和训练一切正常,你会看到预测曲线和真实曲线整体走势接近,但峰值附近可能有一定偏差。这个偏差是正常的,别慌,后面我会讲怎么优化。
评估指标建议用均方根误差(RMSE)和平均绝对百分比误差(MAPE),RMSE 对大的误差更敏感,MAPE 则是看相对误差的百分比,更直观:
python复制from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
mape = mean_absolute_percentage_error(y_true, y_pred)
print(f'RMSE: {rmse:.2f}, MAPE: {mape:.2%}')
5.4 基于视觉检测的深度学习模型构建思路对比
这个项目标题里最经常陪我一起出现的还有一个方向:基于视觉检测的深度学习模型构建。如果你之后想往目标检测方向走,比如做口罩佩戴检测、医疗影像分析等,那么你这个疫情预测项目所积累的数据处理、模型训练、调参经验都是通用的。
视觉检测跟时间序列预测的最大区别在于输入数据形态:一个是图片或者视频帧,一个是连续的数字序列。图片数据进来之后,通常用卷积神经网络(CNN)提取空间特征,比如我们用 YOLO、Halcon 等工具做目标检测,本质上是在问“图片里的物体在哪里、是什么”。而 LSTM 这类循环结构处理的是“过去发生了什么、接下来会发生什么”。两者关注的维度完全不同,但训练流程的骨架长得一模一样。
我的建议是:先把时间序列这个项目吃透,再往视觉方向迁移。因为时间序列项目的数据处理流程更简单,不用处理图像缩放、增强、标注那些复杂操作,可以把全部精力放在理解模型训练的核心逻辑上。有了这个基础,再上手 YOLO 之类的目标检测框架,你会发现自己对训练循环、损失函数、数据集构建这些概念已经有了一定手感,不会像无头苍蝇一样乱撞。
6. 常见问题与排查技巧
6.1 经典翻车现场对照表
前面讲过很多容易踩坑的地方,这里整理成一个表格方便你排查。这些经验全部来自我实际跑这个项目时的亲测经历,可以说句句都是踩过坑才总结出来的。
| 问题 | 现象 | 可能原因 | 解决办法 |
|---|---|---|---|
| CUDA不可用 | torch.cuda.is_available() 返回False |
PyTorch装成CPU版,或CUDA版本不匹配 | 卸载重装对应CUDA版本的PyTorch |
| Loss为NaN | 训练几十轮后loss变成nan | 学习率过大,或数据里有Inf/NaN | 调低学习率,检查数据是否有异常值 |
| 模型不收敛 | Loss下降极慢或不降 | 数据没做归一化,或滑窗/切分有误 | 检查归一化是否完成、训练验证集是否混在一起 |
| 预测结果全是一条直线 | 预测值几乎是常数 | 模型欠拟合,hidden_size太小或epochs不够 | 增大模型容量、增加训练轮数、调整学习率 |
| 训练集效果很好但预测很差 | 训练loss很低,测试预测严重偏移 | 过拟合,或测试集分布与训练集差异过大 | 增加Dropout、加正则化、检查数据切分是否正确 |
| 数据泄露 | 验证阶段指标很好,上线就不行 | 归一化或切分时用了全局统计量 | 归一化只用训练集统计量,按时间顺序切分 |
6.2 上手实操心得
训练过程有个细节想单独说一说:model.eval() 和 with torch.no_grad() 一定要记得加。如果忘了调 eval(),训练时启用的 Dropout 层在预测时仍然生效,每次预测结果都会不一样,第一次跑很容易被这种看似“玄学”的问题坑到。
还有一个小技巧是关于预测多条时序的。如果你要用模型做多步预测,比如拿过去 14 天预测完未来 7 天后,想继续往后预测,可以先把 7 个预测值拼接到输入序列尾部,去掉开头 7 个旧值,再喂给模型,这种方式叫递归预测。不过每递归一步误差都会累积一点,递归太多次数结果会漂移,一般只适合预测中短期。
关于调参,我个人的经验是:先跑通再优化。第一版模型用默认参数跑通全流程,然后再慢慢调。每次只改一个参数,不要同时改好几个,不然连谁引起的效果变化都搞不清楚。从 batch_size 和学习率先调,这两个对训练稳定性影响最大;然后再改 hidden_size 和 num_layers,最后再考虑要不要加正则化。
我自己的调参记录是:初期用 batch_size=32、lr=0.001、hidden_size=64,loss 从 0.03 左右降到 0.005 上下之后就不再下降了。把 lr 降到 0.0005 之后,loss 又能继续下行一段。这个“先大学习率快速下降、再小学习率精细调”的思路,比从头到尾保持一个学习率效果要好。
7. 项目扩展与后续思考
这个入门项目跑完之后,你能往哪里扩展?这里给你几个我自己觉得还不错的升级方向。
第一个是增加更多的特征。现在模型只用感染人数这一列,但实际上疫情传播还受到很多外部因素影响,比如人口密度、气候条件、医疗资源、防控措施等。如果你能把环境、人口、社会层面的数据做成多个特征列一起输入模型,预测效果会更有信息量。这个方向的难点不在LSTM本身,而是多源数据的对齐和特征工程。
第二个是尝试不同的模型。把 LSTM 换成 GRU(门控循环单元),它比 LSTM 参数更少、训练更快,在数据量有限的时候往往效果也不错。再往上可以试一下 Transformer 里的时间序列模型结构,比如 Informer、Autoformer 这些专门为长序列预测设计的模型,你会发现又是一片新天地。从这个项目出发,你就算入门了深度学习,后续可以按照自己的兴趣选择方向深入。
第三个是做成一个完整的小系统。模型训练好之后,用 Flask 或者 FastAPI 包一个接口,输入过去 14 天的数据,返回未来 7 天的预测值,再写一个简单的前端页面展示预测曲线。整个项目就从一个训练脚本变成一个能给别人用的工具了,放在简历上也是加分项。
最后说一点我的切身体会:这个疫情预测项目的意义不在于预测得有多准,而是让你在动手的过程中,把深度学习从“听过的概念”变成“能用的工具”。你会第一次体会到数据预处理的重要、第一次理解模型参数的意义、第一次调试报错到怀疑人生最后成功跑通的成就感。这些体验加在一起才是项目真正的价值。
选择 LSTM 解决这类时间序列问题是无限接近实战的标准路径,而“从数据出发、以问题为导向、亲手实现、亲手调参”这套方法论,才是你在整个项目里收获的最重要资产。真正动手跑完一遍之后,你会发现自己看深度学习相关资料的视角完全不一样了——那些曾经抽象的概念,开始变得具体而生动。
