一元线性回归:销售额等于 2.3897 乘广告费再减 0.1712,广告费每多一万,销售额多 2.39 万。 按时间切前 292 天训练、后 73 天测试;R 方 0.5005、RMSE 11.01 万、MAPE 16.56%。 广告预算 20 万,预测 45.90 万,区间 34.89 到 56.91 万——点估计必须配区间。
上一篇证明了广告费和销售额强相关。这一篇把相关变成公式,再用公式往外推一天。 模型是全章最朴素的那种:一个特征、一条直线。朴素不是缺点,是起点。
一、最朴素的模型
一个特征、一条直线,LinearRegression().fit 一行搞定。
拿到两个数:斜率 2.3897,截距 -0.1712。斜率说「广告费每多一万,销售额多 2.39 万」。
截距是负的,意思是广告费为零时会算出负销售额——这显然不可能,业务上不存在。
所以截距只能当公式的一部分,不能当业务解释。外推太远就没意义了。 这句话在真实项目里很要紧:老板最爱问「不投广告能卖多少」,模型答不了这个问题,只能答数据范围内的。
二、按时间切,不随机切
365 天,前 292 天训练、后 73 天测试,八成对两成。 只用前 292 天拟合,得到销售额等于 2.3433 乘广告费再减 0.9665,和全量那个不完全一样。 不一样是正常的。换一批数据拟合,系数本来就该动。动得太多,说明模型不稳。
不能随机切。随机切会让模型提前看到未来——用 6 月的数据训练,再回头预测 3 月,分数虚高得离谱。 这是时间序列和普通回归最大的区别,很多人在这里栽跟头,而且栽了还不知道。
三、四个指标一起看
| 指标 | 这次的值 | 怎么读 |
|---|---|---|
| R 方 | 0.5005 | 解释了多少波动 |
| RMSE | 11.01 万 | 均方根误差,罚大错 |
| MAE | 8.20 万 | 平均差多少 |
| MAPE | 16.56% | 平均差百分之几 |
R 方 0.5005,说明广告费只解释了一半的波动。剩下那一半藏在季节、周末、大促里。 RMSE 11.01 万、MAE 8.20 万,两个一起看:RMSE 比 MAE 大,说明错得狠的那几天比一般日子差得更多。 MAPE 16.56%,意思是平均差 16.56 个百分点。老板只听得懂这个,报它比报 RMSE 有用。
四个指标一起看才有意义。R 方高不代表误差小,MAPE 小也不代表没有离谱的错。
四、预测一天,还得给区间
假设明天广告预算 20 万,代进训练好的模型:45.90 万。 这是点估计,不是承诺。真要用,上下各留一个 RMSE:34.89 到 56.91 万。 区间宽不宽,取决于 RMSE。想让区间变窄,只能把模型做准,不能靠嘴说「大概差不多」。
报一个数最危险。报一个数加一个区间,别人才知道该备多少货、留多少余量。
五、天花板在哪,以及这一章的小结
只用了广告费一个特征,季节、周末、大促全没用上,所以 6 月和 11 月那两个尖峰它基本预测不出来。 下一步该加特征:月份、是否周末、是否大促。 但特征不是越多越好,加了不相关的,训练分虚高、测试分反而掉。 六篇走完,回头看这条路:概述定问题、处理保质量、统计看形状、分析比高低、相关找关系、预测往外推。
每一步都能单独出错,也都能单独验证。这就是专业和业余的区别:业余一次做完,专业一步一验。 第 11 章换到二手房房价:同样的流程,但特征从一列变成好几列,还要先画图看分布。
六、动手 Demo:销售收入预测与评分
全量拟合一次看系数,再按时间切成 292 加 73 重新拟合,最后算四个指标并给区间。 注意两个模型的斜率不一样:2.3897 和 2.3433。这是正常的,不是算错了。
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
df = pd.read_excel("销售数据_clean.xlsx")
X = df[ ["广告费"]].values
y = df["销售额"].values
m = LinearRegression().fit(X, y)
print("全量:斜率 %.4f,截距 %.4f" % (m.coef_[0], m.intercept_))
cut = int(len(df) * 0.8)
m2 = LinearRegression().fit(X[:cut], y[:cut])
print("训练:斜率 %.4f,截距 %.4f" % (m2.coef_[0], m2.intercept_))
pred = m2.predict(X[cut:])
rmse = mean_squared_error(y[cut:], pred) ** 0.5
print("R2 %.4f RMSE %.2f MAE %.2f"
% (r2_score(y[cut:], pred), rmse, mean_absolute_error(y[cut:], pred)))
print("MAPE %.2f%%" % (np.mean(np.abs((y[cut:] - pred) / y[cut:])) * 100))
v = m2.predict([ [20.0]])[0]
print("广告 20 万预测 %.2f 万,区间 %.2f 到 %.2f" % (v, v - rmse, v + rmse))
实跑输出:
--------------------------------------------------------------------
销售收入预测与评分
--------------------------------------------------------------------
① 建一个最朴素的模型
--------------------------------------------------------------------
一元线性回归:销售额 = 2.3897 乘 广告费 减 0.1712。
读法:广告费每多一万,销售额多 2.39 万。
截距是 -0.1712,负的。意思是「广告费为零时会算出负销售额」——这显然不可能。
所以截距只能当公式的一部分,不能当业务解释。外推太远就没意义了。
! 截距只在数据范围内可信。拿它解释「不投广告能卖多少」是过度解读。
② 留一段做考试
--------------------------------------------------------------------
前 292 天训练,后 73 天测试。
只用前 292 天拟合:销售额 = 2.3433 乘 广告费 减 0.9665。
和上面那个不一样,这是正常的。全量拟合和拿八成数据拟合,结果本来就该有差。
按时间切,不能随机切。随机切会让模型「提前看到未来」。
这是时间序列和普通回归最大的区别,很多人在这里栽跟头。
③ 四个评分指标
--------------------------------------------------------------------
| 指标 | 这次的值 | 怎么读 |
|---|---|---|
| R 方 | 0.5005 | 解释了多少波动 |
| RMSE | 11.01 万 | 均方根误差,罚大错 |
| MAE | 8.20 万 | 平均差多少 |
| MAPE | 16.56% | 平均差百分之几 |
四个指标一起看。R 方高不代表误差小,MAPE 才是老板能听懂的那个。
这次 R 方只有 0.50,说明光靠广告费只解释了一半的波动。
剩下那一半,藏在季节、周末、大促里——最后一节会说到。
! 不同业务能接受的 MAPE 差很多。库存预测差 5% 就得改,流量预测差 20% 也许无妨。
④ 预测下一天
--------------------------------------------------------------------
假设明天广告预算 20 万,代进模型。
预测销售额 45.90 万元。
这是点估计,不是承诺。真要用得给区间:预测值上下各留一个 RMSE。
区间大致是 34.89 到 56.91 万。
! 报一个数最危险。报一个数加一个区间,别人才知道该留多少余量。
⑤ 这个模型的天花板
--------------------------------------------------------------------
只用了广告费一个特征,季节、周末、大促全没用上。
所以 6 月和 11 月那两个尖峰,它基本预测不出来。
下一步该加特征:月份、是否周末、是否大促。
但特征也不是越多越好——加了不相关的特征,R 方会虚高,测试分反而掉。
! 从「能跑」到「能用」,中间隔的是特征工程和交叉验证。这本书只是开了个头。
X 必须是二维的。写 df["广告费"].values 拿到一维数组,fit 会直接报错要 reshape。
X[:cut] 和 X[cut:] 是按顺序切的,不是随机抽的。这正是「不提前看未来」的写法。
mean_squared_error 给的是 MSE,开根号才是 RMSE。新版可以直接写 root_mean_squared_error。
30 秒实验
- 把切分比例从 0.8 改成 0.6,看 R 方和 MAPE 各往哪边动。
- 改用
train_test_split随机切一次,对比 R 方,看虚高多少。 - 想一想:想再加一个特征,你会先加月份还是先加是否周末?为什么?
常见误区
- 随机切时间序列:模型提前看到未来,测试分虚高,上线就露馅。
- 只报 R 方:R 方 0.50 看着还行,MAPE 16.56% 才是业务真正感受到的误差。
- 报一个数不给区间:点估计当成承诺,第二天差 10 万就没人再信这个模型。
- 用截距解释业务:截距 -0.1712 只是在数据范围内让直线对上,不是「基础销量」。
自测三问(点开对答案)
1. 为什么两个模型的斜率不一样? —— 一个用 365 天全量拟合,一个只用前 292 天。样本不同,系数自然会动,动得少说明模型稳。 2. 时间序列为什么不能随机切? —— 随机切会让训练集里混进未来的数据,模型等于偷看了答案,测试分虚高。 3. R 方 0.5005 说明模型不行吗? —— 说明它只用了广告费一个特征,解释了一半波动。要更好就得加季节、周末、大促这些特征。第 10 章收尾。第 11 章换到二手房房价:先写图表模块,再清洗,最后用多个特征做预测。
- ☐ (本篇完,下一篇 11.1 项目概述、效果与准备)