回归实战:用逻辑斯蒂曲线拟合预测新冠疫情人数 复试准备到了第4天刚好把回归这块的实战练了一轮。这次选的主题是“预测新冠人数”不是图省事而是这个场景特别适合练手数据有明确的时间顺序、趋势明显、适合做回归拟合而且数据量不大单机就能跑不需要上分布式。很多入门资料一上来就讲原理、讲公式这当然没错但如果不亲手走一遍完整流程很多细节根本不会暴露。这篇文章就想把我这次实战的完整过程、踩过的坑、最后怎么调整的都记录下来给同样在准备复试或者刚入门机器学习的朋友一个参考。先交代一下这次实战要做什么输入某地区一段时间内的疫情新增人数序列通过回归模型预测未来若干天的新增人数。任务本身不复杂但麻雀虽小五脏俱全特征构造、模型选型、评估、可视化这些环节一个不少。适合两类人看一是复试备考中想快速掌握回归项目套路的人二是想弄明白“回归到底怎么用到真实数据上”的初学者。1. 项目整体设计与思路拆解1.1 为什么选这个题目练手回归问题在教科书上最常见的例子是房价预测、销量预测这些例子虽然经典但离一个初学者自己动手还有距离。疫情人数预测是一个真实的时间序列回归问题它有几个特点很适合拿来学习第一数据获取简单。不需要复杂的数据接口公开数据源的接口就能拉到或者干脆用一份模拟数据也能跑通整个流程。关键是把流程走明白数据本身只是载体。第二趋势结构清晰。疫情新增人数在爆发期呈现明显的非线性增长在控制措施生效后可能出现拐点整个形态接近逻辑斯蒂曲线。这种“有规律可循”的数据能直观看出模型拟合得好不好不像有些业务数据噪声大到根本看不出规律。第三评价直观。预测结果对不对跟真实数据一对比画个图立刻就知道。这对建立“模型好坏”的感觉很重要。1.2 回归项目在真实场景中的完整链路教科书上讲回归通常是给公式、讲损失函数、然后梯度下降求解。但实际做一个回归项目流程比这长得多原始数据 → 数据清洗 → 特征工程 → 数据集划分 → 模型训练 → 模型评估 → 结果可视化 → 迭代优化这里面最容易被忽略的是数据清洗和特征工程。很多人拿到的原始数据根本不能直接塞进模型日期是字符串格式、有缺失值、有异常点不处理就直接训练结果一定是一团糟。我这次的流程设计是先加载数据做基础的可视化看看数据长什么样然后构造特征把日期转成模型能接受的数值形式接着划分训练集和测试集注意时间序列不能随机打乱再训练几个不同复杂度的回归模型做对比最后用指标和曲线图评估找出问题再迭代。这个流程本质上和一个真实业务项目没区别。复试时如果被问到“你做过什么项目”能把这套流程讲清楚比背十个模型公式都管用。1.3 方案选型背后的考量模型方面我选了三个做对比普通线性回归、多项式回归、逻辑斯蒂增长曲线拟合。选择逻辑是线性回归是最简单的基线模型效果不一定好但能提供一个最低标准。如果线性回归表现尚可说明问题和线性相关度高不需要复杂模型如果表现差至少知道问题的复杂度超出线性范围。多项式回归用来处理非线性趋势在疫情爆发期新增人数随时间呈超线性增长多项式特征能捕捉这种趋势。但多项式回归有个明显问题容易过拟合尤其数据量小的时候高阶多项式会在尾部剧烈震荡。逻辑斯蒂增长曲线是这一轮实战的重点。传染病传播早期近似指数增长但随着感染基数变大、易感人群减少增长率会放缓最终达到饱和整体呈S形。用逻辑斯蒂函数拟合疫情人数的累计值参数有明确的实际含义比单纯的多项式更有解释性。选型逻辑总结下来就一句话从最简单的模型起步逐步增加复杂度让每一步的差异都有据可查。这个思路也是实际项目中做baseline对比的标准做法。2. 数据准备与特征工程细节2.1 原始数据长什么样我用的是某地区一段时间的每日新增人数数据字段就两个日期和新增人数。日期范围大概在几十天到一百多天之间。这个量级的数据非常小但用来学习完全够用。原始数据拿回来第一件事不是建模而是画一张图。用matplotlib把新增人数随时间的变化画出来一眼能看出有没有异常值、有没有明显的趋势转折。这一步不能省因为后续所有的特征工程都建立在对数据分布的直观理解上。画图之后会发现几个典型现象早期数据可能非常小只有个位数这对应疫情刚开始的阶段中段出现峰值这是爆发期后期可能回落到一个较低水平。这个S形走势对应的累计值就更明显了——从小变大增速先快后慢最后趋于平稳。2.2 日期特征到底怎么构造模型不认识字符串日期必须转成数值。我在这一轮里试了两种方式差别很大。第一种是直接用“序号”。把第一天记为0第二天记为1依次递增。这个方法简单模型学到的是“时间每过一天人数怎么变化”的模式。缺点是如果日期间隔不均匀比如周末不公布数据序号和真实时间会有偏差。第二种是拆成年、月、日多个特征。比如把日期拆成2020、1、15三个数值。这个做法的好处是能捕捉季节性、周期性规律但在这个场景下效果并不好因为疫情传播跟日历上的月份关系不大反而把特征空间变大了。数据量小的时候特征多不是好事。我最后选择的是第一种加滞后特征组合的方式。序号特征捕捉整体趋势滞后特征捕捉局部的自相关性。注意日期转数值关键在于“模型能否理解这个数值的顺序关系和间隔关系”。序号天然有序、有间隔这是它好用的原因。2.3 滞后特征在疫情预测中的作用滞后特征lag feature是时间序列预测里一个很常见的手段。简单说就是用前几天的数据作为今天预测的特征。比如预测第t天的新增人数不只是看t这个序号还把t-1、t-2、t-7天的新增人数都作为输入特征。为什么用滞后特征因为疫情每天的新增人数不是独立的昨天的数字和今天的数字之间往往有关联。这个道理跟天气预报一样今天的温度跟前一天的温度高度相关所以要把前几天的温度作为特征喂给模型。我在构造滞后特征时选了3个时间点前一天、前两天、七天前。前一天和前两天承接短期惯性七天前捕捉每周周期。这个基于经验的选择不一定最优但作为学习项目能直观看到加入滞后特征后模型指标的变化本身就是收获。2.4 数据清洗那些容易翻车的细节这次实战里数据清洗花的精力不多但有几个坑值得写出来。第一个是缺失值。某些日期可能没有公布数据这一天的记录直接缺失。处理方式有两种直接删除或者用前后天的均值填充。在这个场景下我推荐先用均值填充因为删除会造成时间序列上的空洞影响滞后特征的连续性。第二个是零值。疫情初期很多天的新增是0这个本身没问题但如果是“没统计到”和“确实为0”混在一起就会干扰模型判断。我这次直接把零值视为真实零值没有额外处理。第三个是异常值。某一天的数字突然异常高或者异常低可能是统计口径变化或者迟报补报。识别异常值我用了一个简单办法计算近7天均值如果当天值偏离超过3倍标准差就标记出来。这只是最基础的检测不适合较真但用来做学习项目足够。还有一点非常重要预测目标到底是新增人数还是累计人数。这两个目标对应的特征工程和模型形态差别很大。累计值天然平滑、单调递增拟合起来容易新增值波动大噪声多预测难度高。我这次做了一个新增值的预测难度比累计值高不少但更能检验模型真实水平。3. 核心建模过程与关键参数解析3.1 训练集和测试集的切分方式分类问题里常用train_test_split随机打乱数据但时间序列预测绝对不能这么干。时间序列必须按时间顺序切分用过去的数据训练用未来的数据测试。如果随意打乱就相当于偷看了未来测试指标会虚高实际部署时会崩得很惨。我采用的比例是前80%的数据作为训练集后20%作为测试集。相当于用前几十天的数据训练预测最后十几天的数据。这个切分方式模拟了真实场景用已知推断未知。更严格的训练方式是用滚动预测。也就是每次都只预测未来一天然后把这天真实值加入历史数据再预测下一天。这种方式避免了多步预测误差累积的问题但代码复杂度高一些。作为DAY4的学习内容用固定切分已经足够滚动预测可以放到后续进阶。3.2 线性回归先从最简单模型建立基线用sklearn训练一个线性回归模型只需要几行代码from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_absolute_error model LinearRegression() model.fit(X_train, y_train) y_pred model.predict(X_test) print(R2:, r2_score(y_test, y_pred)) print(MAE:, mean_absolute_error(y_test, y_pred))跑出来的结果训练集R2大约在0.85左右测试集R2掉到0.45左右。差距很大说明模型泛化能力不足。但这其实是意料之中的新增人数在整个周期内不是线性变化的线性模型抓不住中间爆发期的快速增长。线性回归在这个项目里的定位不是用来最终交付而是确定baseline。如果连线性回归都能拿到不错的分数说明问题可能没那么复杂后续就不用上复杂模型了。这一步相当于给后面所有模型定了一个及格线。3.3 多项式回归处理非线性但是有隐患为了捕捉非线性趋势我给特征增加了多项式项。sklearn中的PolynomialFeatures可以自动生成多项式特征组合from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline pipe make_pipeline( PolynomialFeatures(degree4, include_biasFalse), LinearRegression() ) pipe.fit(X_train, y_train)多项式的阶数是个关键参数。阶数太低非线性捕捉不够阶数太高模型开始在训练集上“画蛇添足”测试集预测值会剧烈震荡。我试了2阶到6阶2阶太平6阶在尾部已经出现明显的过拟合上翘。最后定在4阶训练集R2接近0.95测试集R2在0.75附近比线性回归好不少。多项式回归的过拟合有个经典的信号训练集指标很好测试集指标一塌糊涂。这时候别急着调模型先看阶数是否太高、样本量是否太小。我这次数据量只有几十个点4阶多项式已经是上限附近了。3.4 逻辑斯蒂增长曲线拟合逻辑斯蒂函数的基本形式是y L / (1 e^(-k(t - t0)))其中L是最大容量上限k是增长速度t0是拐点位置。放到疫情场景里L可以理解为最终感染规模的上限k代表传播强度t0是增长最快的日期。拟合这个函数用scipy的curve_fitfrom scipy.optimize import curve_fit import numpy as np def logistic(t, L, k, t0): return L / (1 np.exp(-k * (t - t0))) popt, pcov curve_fit(logistic, X_train.flatten(), y_train, maxfev5000) L, k, t0 popt第一次拟合可能直接报错原因是初始参数给得不合适。curve_fit默认从1开始迭代但L在这个数据集里是成千上万的量级k和t0的量级也不同。不给初始值优化算法很难收敛到正确区间。所以必须手动设置p0初始参数。L初始值取样本数据的最大值乘以1.2k初始值取0.1t0初始值取时间序列的中位数。加了初值之后拟合立刻收敛。这里要特别强调curve_fit对初值敏感是出了名的用之前一定要看一眼数据的量级和走势再给初值。逻辑斯蒂拟合的是累计值更稳妥但这次目标是新增值。我把新增值做了累加累加后是一条标准的S形曲线拟合参数稳定。然后再做差分还原新增预测。这一步操作在代码上就是cumsum和diff的组合但很多人会漏掉直接拿新增值去拟合结果一塌糊涂。拟合结果L参数约等于数据总体的峰值上限k在0.1到0.3之间t0落在爆发期中部。测试集MAE比4阶多项式回归还低一些。更重要的是逻辑斯蒂模型预测的曲线形状是S形不会像多项式那样在尾部乱摆可解释性强得多。3.5 特征归一化到底要不要做线性回归和多项式回归里特征尺度差异很大日期序号能到上百滞后特征可能是几十到上千。sklearn的LinearRegression本身对尺度不是特别敏感但不做归一化多项式的数值稳定性会变差尤其是高阶项特征值的幂次增长会造成数值溢出。我用的是StandardScaler做标准化把特征缩放到均值0、标准差1的分布。Pipeline顺序很重要先标准化再生成多项式特征最后线性回归。顺序反了标准化就没有意义了因为多项式特征是在原始尺度上生成的。逻辑斯蒂拟合不需要标准化curve_fit是直接对函数参数做优化跟特征标准化关系不大。4. 训练过程实测与评估结果对比4.1 三个模型的评估指标模型训练完之后我用了几组指标来评价R2决定系数表示模型解释了数据方差的百分比越接近1越好但容易受异常值影响。MAE平均绝对误差直观的偏差量单位跟预测目标一致。RMSE均方根误差对大误差更敏感一个离谱的预测点会把RMSE拉得很高。三个模型在测试集上的结果大致如下模型训练R2测试R2MAERMSE线性回归0.850.452103204阶多项式0.950.75120185逻辑斯蒂拟合0.930.8295140逻辑斯蒂拟合在所有指标上领先测试集R2到0.82MAE压到了100以内。说实话这个结果比我预想的好。逻辑斯蒂曲线的参数少、结构性强在这个数据上天然占优。这个结果也再次说明模型复杂度不是越高越好选一个符合数据内在规律的模型才是正路。4.2 残差分析揭示了什么问题光看指标还不够残差图能告诉我们更多信息。我画了“预测值减去真实值”随时间变化的残差图发现逻辑斯蒂模型的残差分布不是均匀的在拐点附近残差偏大而在首尾两端残差很小。这个现象的含义是逻辑斯蒂模型对“趋势转折”的刻画还不够精确。拐点附近是增长速率变化最快的地方模型用一个固定拐点t0来近似很难完全贴合真实数据的动态变化。将来的改进方向可以是分段模型前半段指数增长、后半段收敛到平台。残差图还有一个价值如果残差呈现出明显的模式比如连续的残差正负交替说明模型遗漏了某个系统性因素不该直接说“数据有噪声”。4.3 可视化让结果一目了然实操中我画了三张图第一张是历史数据和预测值的整体趋势图。把训练集、测试集的真实值、预测值都画在同一条时间轴上立刻能看到哪个模型的预测尾巴最离谱。线性回归的预测在爆发期明显偏低多项式回归的尾部飘了逻辑斯蒂曲线整体贴合得最好。第二张是拟合曲线在拐点附近的放大图。新增值的峰值宽度、峰值高度是否准确都集中在这一小段时间里。逻辑斯蒂模型在峰值位置没偏差多少但峰值高度略高说明L参数估计略微偏大。第三张是预测误差分布直方图。我截取了误差绝对值小于50、50到150、150以上三档能看清误差集中在哪个区间。对学习项目而言这个信息不如前两张图直观但对复试中讲“我的模型怎么评估的”是一个加分项。4.4 超参数调整的实验记录我把多项式回归的阶数从2到6都跑了一遍记录每组参数下的训练R2和测试R2。结果如下阶数2训练R20.88测试R20.65欠拟合非线性捕捉不足。阶数3训练R20.92测试R20.72有明显改善。阶数4训练R20.95测试R20.75继续改善但幅度变小。阶数5训练R20.97测试R20.66过拟合开始出现。阶数6训练R20.99测试R20.52典型过拟合。这个实验记录最大的意义就是展示了“训练指标上升但测试指标下降”的过拟合曲线。复试面试官如果问到“怎么判断模型过拟合”能拿出这种实测数据说话远比背定义有力。逻辑斯蒂模型这边的超参数主要是初始值的设定没有网格搜索。这也算是一个值得记录的差异普通机器学习模型靠超参数搜索而曲线拟合的模型更依赖初值选择和优化算法的稳定性。5. 常见问题与排查技巧实录5.1 预测结果出现负数第一次跑线性回归的时候预测结果里居然出现了负数。新增人数不可能是负数这个结果一看就是错的。排查思路很简单线性回归的假设是y和x线性相关如果真实关系是非线性的线性模型的预测值在边界处会外推到不合理区间。比如某段时间新增人数已经降到很低线性模型基于前面的趋势仍会给一个负的截距延伸。解决方案一是换用逻辑斯蒂这类有边界约束的模型二是对预测结果做后处理将负数截断为0。但要注意截断只是掩盖问题更好的选择是理解为什么会出现负值。如果截断之后大量预测值聚集在0附近说明模型根本不理解数据不是简单截断能解决的。5.2 curve_fit不收敛的排查逻辑斯蒂拟合第一次跑的时候curve_fit直接报错“Optimal parameters not found”。这个报错几乎都是初值问题。排查方法分三步先看数据量级。L参数和y的量级一致如果y在几千初始L就不能是1。我给的p0里面L是数据最大值的1.2倍。再看参数边界。scipy.optimize.curve_fit的bounds参数可以用来限制参数的合理范围。比如L不能小于数据最大值k必须是正值t0要在时间序列范围内。加上bounds之后优化算法不容易跑到不合理区域。最后看maxfev。默认的最大迭代次数可能不够尤其参数多、初值差的时候。我设置maxfev10000问题就解决了。5.3 滞后窗口选几天合适滞后特征窗口的选择其实没有统一答案。窗口太长特征数量增多而样本量只有几十个模型容易过拟合窗口太短又可能漏掉重要的自回归信息。我做了一个小实验只用前一天作为滞后特征测试R2在0.70左右用前1、2、7天三个滞后特征测试R2提升到0.75。再加更多滞后特征测试R2反而下降。原因很简单特征多了模型自由度上升可用的样本量撑不住复杂度。实际经验是先用领域常识圈定候选窗口再用实验对比。不要一上来就设置几十个滞后特征时间序列预测里特征数量超过样本量的十分之一就要非常警惕。5.4 数据泄漏这个坑切分数据集时如果不小心很容易造成数据泄漏。一种典型的错误是先用全部数据做均值和标准差计算然后标准化再去切训练集和测试集。这样做相当于测试集的信息渗透进了训练过程测试集指标会虚高。正确做法是先切分再在训练集上fit标准化器然后transform训练集和测试集。sklearn的Pipeline能自动保证这一点这也是我推荐用Pipeline而不是手动StandardScaler的原因。这部分如果被面试官提问“数据泄漏是什么”最好的回答就是“测试集的信息在训练阶段被用到了”然后解释一下标准化的泄漏路径。能现场讲出这个说明真的做过项目。5.5 一些通用的小技巧复试项目的代码不要追求封装得多漂亮但一定要保证可读性。每个环节用清晰的注释标明关键图表保存下来指标输出放在最后统一打印。这不仅是为了复试展示更是让自己在复盘时能看到完整的过程记录。另外我习惯把每次实验的参数、指标、图表名记在一个实验记录文档里。这个习惯在这次实战中帮了大忙调整多项式阶数后对比指标时翻记录就能看到之前版本的具体数据不用重新跑一遍代码。提示时间序列预测项目最有价值的产出不是最终模型而是完整的实验对比记录。模型会过时实验记录能帮你在复试中把整个思路讲清楚。6. 从DAY4实战中总结的经验这次实战做完我自己印象最深的一点是回归项目真正的难点不在模型代码而在对问题的理解。同样是预测新增人数如果用累计值做逻辑斯蒂拟合再差分回来效果会远好于直接用新增值拟合多项式。这个认知是跑了几组实验后才建立的。还有一件事值得强调区分“模型拟合得好”和“模型预测得好”。训练集上R2高只是拟合好测试集上指标高才算预测好。我在复现过程中一开始看到训练集R2高达0.99非常兴奋转头看到测试集R2只有0.5这种落差本身就是一课。这个小项目后续的扩展空间其实不小。一是可以引入干预因素作为特征比如节假日、防控强度等把单一的时间序列回归升级成带外部特征的预测模型。二是尝试更完整的时间序列处理方式比如差分平稳化后套用ARIMA或者用Prophet这类现成工具对比它们和逻辑斯蒂拟合的差异。三是换成更规范的多步预测流程用滚动预测检验模型的长期稳定性。复试如果有时间把其中任意一条做深面试时能讲的内容都会丰富很多。最后说一点体会学习回归看十篇教程不如亲手跑通一个项目。数据加载、特征处理、模型训练、评估调优这一条线走下来你对回归的理解就不只是一个公式而是一套能解决问题的方法。这一天的实战值了。