这是 数学重学路线图 阶段四的子页面
回归与预测:用一条线穿过数据
直觉入门:回归是在”找规律” 想象你在散点图上撒了一把豆子
回归就是找一条线(或曲线),让它尽可能靠近所有豆子
找到这条线之后,给一个新的 x,就能预测 y
类比后端开发:
过去 30 天的 QPS 数据就是散点
找到趋势线,就能预测下周需要多少服务器
简单线性回归:y = ax + b 模型 $y = ax + b + \epsilon$
a = 斜率(x 每增加 1,y 平均变化 a)
b = 截距(x=0 时 y 的值)
ε = 误差(模型抓不住的随机波动)
最小二乘法 OLS 目标:找到 a 和 b,使得所有数据点到直线的垂直距离平方和最小
$\min_{a,b} \sum_{i=1}^{n} (y_i - ax_i - b)^2$
直觉:让预测值和真实值的”总偏差”最小
公式推导 $a = \frac{n\sum x_iy_i - \sum x_i \sum y_i}{n\sum x_i^2 - (\sum x_i)^2}$
$b = \bar{y} - a\bar{x}$
不用背,理解就好——Python 一行搞定
Python 从零实现 + sklearn 版: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import numpy as npimport matplotlib.pyplot as pltfrom sklearn.linear_model import LinearRegressionnp.random.seed(42 ) qps = np.random.uniform(100 , 1000 , 50 ) cpu_usage = 10 + 0.07 * qps + np.random.normal(0 , 5 , 50 ) n = len (qps) a_manual = (n * np.sum (qps * cpu_usage) - np.sum (qps) * np.sum (cpu_usage)) / \ (n * np.sum (qps**2 ) - np.sum (qps)**2 ) b_manual = np.mean(cpu_usage) - a_manual * np.mean(qps) print (f"手动计算: y = {a_manual:.4 f} x + {b_manual:.4 f} " )model = LinearRegression() model.fit(qps.reshape(-1 , 1 ), cpu_usage) print (f"sklearn: y = {model.coef_[0 ]:.4 f} x + {model.intercept_:.4 f} " )plt.figure(figsize=(10 , 6 )) plt.scatter(qps, cpu_usage, alpha=0.6 , label='实际数据' ) x_line = np.linspace(50 , 1050 , 100 ) y_line = model.predict(x_line.reshape(-1 , 1 )) plt.plot(x_line, y_line, 'r-' , linewidth=2 , label=f'回归线: y={model.coef_[0 ]:.3 f} x+{model.intercept_:.1 f} ' ) plt.xlabel('QPS' ) plt.ylabel('CPU 使用率 (%)' ) plt.title('QPS vs CPU使用率:线性回归' ) plt.legend() plt.grid(True , alpha=0.3 ) plt.show() pred = model.predict(800 ) print (f"\nQPS=800 → 预测CPU: {pred[0 ]:.1 f} %" )
R² 决定系数:模型好不好? 定义 $R^2 = 1 - \frac{SS_{res}}{SS_{tot}} = 1 - \frac{\sum(y_i - \hat{y}_i)^2}{\sum(y_i - \bar{y})^2}$
$SS_{res}$ = 残差平方和(模型没解释的变异)
$SS_{tot}$ = 总平方和(数据的总变异)
直觉 R² = 模型解释了多少比例的数据波动
R² = 1:完美拟合(不现实)
R² = 0:模型和瞎猜一样
R² = 0.85:模型解释了 85% 的变异,还行
注意事项 R² 高不一定好(可能过拟合)
R² 低不一定差(有些数据本身就很嘈杂)
增加特征 R² 一定不降(所以多元回归用 Adjusted R²)
Python 计算 R²: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import numpy as npfrom sklearn.linear_model import LinearRegressionfrom sklearn.metrics import r2_scorenp.random.seed(42 ) X = np.random.uniform(0 , 10 , 100 ).reshape(-1 , 1 ) scenarios = { "强线性关系 (低噪声)" : 2 *X.ravel() + 3 + np.random.normal(0 , 1 , 100 ),"弱线性关系 (高噪声)" : 2 *X.ravel() + 3 + np.random.normal(0 , 8 , 100 ),"非线性关系" : np.sin(X.ravel()) * 10 + np.random.normal(0 , 1 , 100 ),} for name, y in scenarios.items():model = LinearRegression().fit(X, y) r2 = r2_score(y, model.predict(X)) print (f"{name:30s} → R² = {r2:.4 f} " )
多元线性回归:多个特征一起上 模型 $y = a_1x_1 + a_2x_2 + \ldots + a_nx_n + b$
矩阵形式:$\mathbf{y} = \mathbf{X}\boldsymbol{\beta} + \boldsymbol{\epsilon}$
解:$\boldsymbol{\beta} = (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{y}$
直觉 CPU 使用率不只取决于 QPS,还取决于并发连接数、内存占用等
多元回归同时考虑多个因素
特征重要性 标准化后,系数绝对值越大 → 影响越大
Python 多元回归实战: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import numpy as npimport pandas as pdfrom sklearn.linear_model import LinearRegressionfrom sklearn.preprocessing import StandardScalernp.random.seed(42 ) n = 200 qps = np.random.uniform(100 , 1000 , n) connections = np.random.uniform(50 , 500 , n) memory_mb = np.random.uniform(1000 , 8000 , n) cpu = 5 + 0.05 *qps + 0.03 *connections + 0.002 *memory_mb + np.random.normal(0 , 3 , n) X = np.column_stack([qps, connections, memory_mb]) feature_names = ['QPS' , '并发连接数' , '内存使用(MB)' ] model = LinearRegression().fit(X, cpu) print ("多元线性回归系数:" )for name, coef in zip (feature_names, model.coef_):print (f" {name} : {coef:.5 f} " )print (f" 截距: {model.intercept_:.4 f} " )print (f" R²: {model.score(X, cpu):.4 f} " )scaler = StandardScaler() X_scaled = scaler.fit_transform(X) model_scaled = LinearRegression().fit(X_scaled, cpu) print ("\n标准化后的特征重要性(绝对值越大越重要):" )importance = sorted (zip (feature_names, np.abs (model_scaled.coef_)), key=lambda x: x[1 ], reverse=True ) for name, imp in importance:print (f" {name} : {imp:.4 f} " )
过拟合 vs 欠拟合 直觉 欠拟合(Underfitting) :
模型太简单,没有捕捉到数据规律
比喻:用一条直线拟合明显弯曲的数据
表现:训练集和测试集都差
过拟合(Overfitting) :
模型太复杂,把噪声也记住了
比喻:用 20 次多项式去拟合 5 个点——完美穿过每个点,但在点之间疯狂波动
表现:训练集很好,测试集很差
关键区别:
欠拟合 = 没学到规律
过拟合 = 学到了不该学的(噪声)
偏差-方差权衡 Bias-Variance Tradeoff 偏差(Bias):模型预测值与真实值的系统性偏差 → 欠拟合
方差(Variance):模型对训练数据的敏感度 → 过拟合
总误差 = 偏差² + 方差 + 不可约误差
目标:找到偏差和方差的最佳平衡点
过拟合检测 训练集表现远好于测试集 → 过拟合
交叉验证:将数据分 k 折,轮流做验证
Python 过拟合可视化演示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import numpy as npimport matplotlib.pyplot as pltfrom sklearn.preprocessing import PolynomialFeaturesfrom sklearn.linear_model import LinearRegressionfrom sklearn.metrics import r2_scorenp.random.seed(42 ) X_train = np.sort(np.random.uniform(0 , 6 , 15 )).reshape(-1 , 1 ) y_train = np.sin(X_train.ravel()) + np.random.normal(0 , 0.2 , 15 ) X_test = np.linspace(0 , 6 , 200 ).reshape(-1 , 1 ) y_true = np.sin(X_test.ravel()) fig, axes = plt.subplots(1 , 3 , figsize=(18 , 5 )) degrees = [1 , 4 , 15 ] titles = ['欠拟合 (degree=1)' , '刚刚好 (degree=4)' , '过拟合 (degree=15)' ] for ax, degree, title in zip (axes, degrees, titles):poly = PolynomialFeatures(degree) X_poly_train = poly.fit_transform(X_train) X_poly_test = poly.transform(X_test) model = LinearRegression().fit(X_poly_train, y_train) y_pred_train = model.predict(X_poly_train) y_pred_test = model.predict(X_poly_test) r2_train = r2_score(y_train, y_pred_train) ax.scatter(X_train, y_train, color='blue' , s=50 , zorder=5 , label='训练数据' ) ax.plot(X_test, y_true, 'g--' , alpha=0.5 , label='真实函数' ) ax.plot(X_test, y_pred_test, 'r-' , linewidth=2 , label=f'拟合 (R²={r2_train:.3 f} )' ) ax.set_title(title) ax.set_ylim(-2 , 2 ) ax.legend(fontsize=8 ) ax.grid(True , alpha=0.3 ) plt.suptitle('欠拟合 vs 刚好 vs 过拟合' , fontsize=14 ) plt.tight_layout() plt.show()
交叉验证代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import numpy as npfrom sklearn.model_selection import cross_val_scorefrom sklearn.preprocessing import PolynomialFeaturesfrom sklearn.linear_model import LinearRegressionfrom sklearn.pipeline import make_pipelinenp.random.seed(42 ) X = np.sort(np.random.uniform(0 , 6 , 50 )).reshape(-1 , 1 ) y = np.sin(X.ravel()) + np.random.normal(0 , 0.2 , 50 ) print ("多项式次数 vs 交叉验证 R²:" )print ("-" * 40 )best_degree, best_score = 0 , -np.inf for degree in range (1 , 16 ):model = make_pipeline(PolynomialFeatures(degree), LinearRegression()) scores = cross_val_score(model, X, y, cv=5 , scoring='r2' ) mean_score = scores.mean() if mean_score > best_score: best_score = mean_score best_degree = degree bar = '█' * int (max (0 , mean_score) * 30 ) print (f" degree={degree:2d} : R²={mean_score:+.4 f} {bar} " )print (f"\n最优多项式次数: {best_degree} (CV R²={best_score:.4 f} )" )
正则化:给模型加”枷锁” 为什么需要正则化? 过拟合时,模型系数往往很大(疯狂拧合数据)
正则化 = 在损失函数中加一个惩罚项,限制系数不能太大
L2 正则化 Ridge(岭回归) $\min \sum(y_i - \hat{y}_i)^2 + \alpha \sum a_j^2$
效果:所有系数被压缩变小,但不会变成 0
直觉:让每个特征都参与,但不让任何一个独大
L1 正则化 Lasso $\min \sum(y_i - \hat{y}_i)^2 + \alpha \sum |a_j|$
效果:一些系数会被压缩到精确为 0 (自动特征选择)
直觉:选出最重要的几个特征,其他砍掉
ElasticNet L1 + L2 的混合
Python 正则化对比: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import numpy as npfrom sklearn.linear_model import LinearRegression, Ridge, Lassofrom sklearn.preprocessing import PolynomialFeaturesfrom sklearn.model_selection import cross_val_scorefrom sklearn.pipeline import make_pipelinenp.random.seed(42 ) X = np.sort(np.random.uniform(0 , 6 , 30 )).reshape(-1 , 1 ) y = np.sin(X.ravel()) + np.random.normal(0 , 0.3 , 30 ) degree = 10 models = { "线性回归 (无正则化)" : make_pipeline(PolynomialFeatures(degree), LinearRegression()),"Ridge (L2, α=0.1)" : make_pipeline(PolynomialFeatures(degree), Ridge(alpha=0.1 )),"Ridge (L2, α=1.0)" : make_pipeline(PolynomialFeatures(degree), Ridge(alpha=1.0 )),"Lasso (L1, α=0.01)" : make_pipeline(PolynomialFeatures(degree), Lasso(alpha=0.01 )),"Lasso (L1, α=0.1)" : make_pipeline(PolynomialFeatures(degree), Lasso(alpha=0.1 )),} print (f"多项式次数 = {degree} " )print ("-" * 55 )for name, model in models.items():scores = cross_val_score(model, X, y, cv=5 , scoring='r2' ) print (f"{name:30s} → CV R² = {scores.mean():.4 f} ± {scores.std():.4 f} " )
综合应用场景 大数据方向 趋势预测(DAU/MAU) :
历史数据拟合线性/多项式回归
预测未来 N 天的用户量
特征重要性分析 :
哪些因素最影响指标?
标准化系数 = 特征重要性排名
代码示例——DAU 趋势预测:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import numpy as npimport matplotlib.pyplot as pltfrom sklearn.linear_model import LinearRegressionnp.random.seed(42 ) days = np.arange(1 , 91 ).reshape(-1 , 1 ) dau = 10000 + 50 * days.ravel() + np.random.normal(0 , 500 , 90 ) model = LinearRegression().fit(days, dau) future_days = np.arange(91 , 121 ).reshape(-1 , 1 ) future_dau = model.predict(future_days) plt.figure(figsize=(12 , 6 )) plt.scatter(days, dau, alpha=0.5 , s=20 , label='历史DAU' ) all_days = np.arange(1 , 121 ).reshape(-1 , 1 ) plt.plot(all_days, model.predict(all_days), 'r-' , linewidth=2 , label='趋势线' ) plt.axvline(x=90 , color='gray' , linestyle='--' , alpha=0.5 , label='今天' ) plt.fill_between(future_days.ravel(), future_dau*0.9 , future_dau*1.1 , alpha=0.2 , color='orange' , label='预测区间(±10%)' ) plt.xlabel('天数' ) plt.ylabel('DAU' ) plt.title(f'DAU趋势预测 (日增约{model.coef_[0 ]:.0 f} )' ) plt.legend() plt.grid(True , alpha=0.3 ) plt.show() print (f"趋势: 每天增长约 {model.coef_[0 ]:.0 f} 用户" )print (f"R² = {model.score(days, dau):.4 f} " )print (f"预测第120天DAU: {model.predict(120 )[0 ]:.0 f} " )
安全方向 异常分数回归 :
特征(登录频率、IP变化、设备指纹等)→ 风险分数
多元回归建模哪些因素最影响风险
漏洞数量趋势 :
每月新增 CVE 数量趋势
预测未来安全团队的工作量
代码示例——安全风险评分模型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import numpy as npfrom sklearn.linear_model import LinearRegressionfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import r2_score, mean_absolute_errornp.random.seed(42 ) n = 500 login_frequency = np.random.exponential(5 , n) ip_change_count = np.random.poisson(2 , n) failed_attempts = np.random.poisson(1 , n) access_time_var = np.random.exponential(3 , n) risk_score = (0.1 * login_frequency + 0.3 * ip_change_count + 0.4 * failed_attempts + 0.2 * access_time_var + np.random.normal(0 , 0.5 , n)) risk_score = np.clip(risk_score, 0 , 10 ) X = np.column_stack([login_frequency, ip_change_count, failed_attempts, access_time_var]) feature_names = ['登录频率' , 'IP变化' , '失败尝试' , '时间方差' ] X_train, X_test, y_train, y_test = train_test_split(X, risk_score, test_size=0.2 , random_state=42 ) model = LinearRegression().fit(X_train, y_train) y_pred = model.predict(X_test) print ("=== 安全风险评分模型 ===" )print (f"R² = {r2_score(y_test, y_pred):.4 f} " )print (f"MAE = {mean_absolute_error(y_test, y_pred):.4 f} " )print ("\n特征权重:" )for name, coef in sorted (zip (feature_names, model.coef_), key=lambda x: abs (x[1 ]), reverse=True ): print (f" {name} : {coef:.4 f} " )
后端方向 容量预测 :
历史 QPS → 未来需求
根据趋势提前扩容
性能回归检测 :
每次发版后监控响应时间趋势
如果斜率显著变化 → 性能回归
代码示例——性能回归检测:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import numpy as npfrom scipy import statsnp.random.seed(42 ) before = 200 + np.random.normal(0 , 10 , 30 ) after = 215 + np.random.normal(0 , 12 , 10 ) days_before = np.arange(1 , 31 ) days_after = np.arange(31 , 41 ) t_stat, p_value = stats.ttest_ind(before, after, alternative='less' ) print ("=== 性能回归检测 ===" )print (f"发版前均值: {before.mean():.1 f} ms" )print (f"发版后均值: {after.mean():.1 f} ms" )print (f"t检验: t={t_stat:.3 f} , p={p_value:.6 f} " )print (f"结论: {'性能回归!响应时间显著升高' if p_value < 0.05 else '正常波动' } " )from sklearn.linear_model import LinearRegressionmodel_before = LinearRegression().fit(days_before.reshape(-1 ,1 ), before) slope_before = model_before.coef_[0 ] model_after = LinearRegression().fit(days_after.reshape(-1 ,1 ), after) slope_after = model_after.coef_[0 ] print (f"\n发版前斜率: {slope_before:.3 f} ms/天" )print (f"发版后斜率: {slope_after:.3 f} ms/天" )
模型评估最佳实践 训练集 / 测试集划分 永远不要用训练数据评估模型
常用比例:80% 训练 / 20% 测试
时间序列数据:用过去预测未来,不能随机打乱
交叉验证 k-Fold:数据分成 k 份,轮流做验证
更稳定的评估,减少运气成分
常用评估指标 R²:越接近 1 越好
MAE(平均绝对误差):预测偏差的平均大小
RMSE(均方根误差):对大误差更敏感
MAPE(平均绝对百分比误差):百分比形式更直观
Python 评估指标代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import numpy as npfrom sklearn.metrics import r2_score, mean_absolute_error, mean_squared_errordef evaluate_model (y_true, y_pred, name="模型" ):"""完整的回归模型评估""" r2 = r2_score(y_true, y_pred) mae = mean_absolute_error(y_true, y_pred) rmse = np.sqrt(mean_squared_error(y_true, y_pred)) mape = np.mean(np.abs ((y_true - y_pred) / y_true)) * 100 print (f"=== {name} 评估 ===" )print (f" R² = {r2:.4 f} (解释了{r2*100 :.1 f} %的变异)" )print (f" MAE = {mae:.4 f} (平均偏差{mae:.1 f} )" )print (f" RMSE = {rmse:.4 f} (对大误差敏感)" )print (f" MAPE = {mape:.2 f} % (百分比误差)" )return r2, mae, rmse, mapenp.random.seed(42 ) y_true = np.random.normal(100 , 15 , 50 ) y_pred_good = y_true + np.random.normal(0 , 3 , 50 ) y_pred_bad = y_true + np.random.normal(0 , 15 , 50 ) evaluate_model(y_true, y_pred_good, "好模型" ) print ()evaluate_model(y_true, y_pred_bad, "差模型" )
练习题 题目1:简单线性回归 给定数据:x = [1,2,3,4,5], y = [2.1, 3.9, 6.2, 7.8, 10.1]
(a) 用最小二乘法求 a 和 b
(b) 预测 x=6 时的 y
(c) 计算 R²
参考答案:
1 2 3 4 5 6 7 8 9 10 11 import numpy as npfrom sklearn.linear_model import LinearRegressionfrom sklearn.metrics import r2_scoreX = np.array([1 , 2 , 3 , 4 , 5 ]).reshape(-1 , 1 ) y = np.array([2.1 , 3.9 , 6.2 , 7.8 , 10.1 ]) model = LinearRegression().fit(X, y) print (f"(a) y = {model.coef_[0 ]:.4 f} x + {model.intercept_:.4 f} " )print (f"(b) x=6 → y = {model.predict(6 )[0 ]:.4 f} " )print (f"(c) R² = {model.score(X, y):.4 f} " )
题目2:过拟合诊断 你的模型在训练集 R²=0.99,测试集 R²=0.45
(a) 这是什么问题?
(b) 列出 3 种解决方法
参考答案:
(a) 过拟合——模型记住了训练数据的噪声
(b) 解决方法:
减少模型复杂度(降低多项式次数、减少特征)
加正则化(Ridge / Lasso)
增加训练数据量
交叉验证选择最优超参数
题目3:多元回归特征选择 你有 5 个特征预测服务器响应时间,Lasso 回归后有 2 个特征系数变为 0
(a) 这意味着什么?
(b) 保留哪些特征更合理?为什么?
参考答案:
(a) 系数为 0 的特征被 Lasso “认为”对预测不重要,自动被剔除
(b) 保留系数不为 0 的 3 个特征。因为 Lasso 的 L1 惩罚天然做特征选择
但注意:如果两个特征高度相关,Lasso 可能只保留其中一个
题目4:容量预测 过去 7 天的 QPS 数据:[500, 520, 550, 540, 580, 600, 610]
(a) 拟合线性回归
(b) 预测第 14 天的 QPS
(c) 讨论:线性外推可靠吗?什么情况下会失效?
参考答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import numpy as npfrom sklearn.linear_model import LinearRegressiondays = np.array([1 ,2 ,3 ,4 ,5 ,6 ,7 ]).reshape(-1 , 1 ) qps = np.array([500 , 520 , 550 , 540 , 580 , 600 , 610 ]) model = LinearRegression().fit(days, qps) print (f"(a) QPS = {model.coef_[0 ]:.1 f} * day + {model.intercept_:.1 f} " )print (f"(b) 第14天预测: {model.predict(14 )[0 ]:.0 f} QPS" )print (f"R² = {model.score(days, qps):.4 f} " )print ()print ("(c) 线性外推的风险:" )print (" - 增长可能是非线性的(如指数增长或饱和)" )print (" - 可能存在周期性(工作日vs周末)" )print (" - 突发事件无法预测(大促、故障)" )print (" - 外推越远越不可靠(只在数据范围附近可信)" )
题目5:综合——正则化选择 场景:你有 100 个特征预测用户流失概率,怀疑很多特征无用
(a) 应该用 Ridge 还是 Lasso?为什么?
(b) 如果希望保留所有特征但防止过拟合呢?
(c) α 参数怎么选?
参考答案:
(a) 用 Lasso(L1)——因为它能自动把无用特征系数压到 0,实现特征选择
(b) 用 Ridge(L2)——它不会消除特征,只是压缩系数
(c) 用交叉验证选 α:sklearn 的 LassoCV / RidgeCV 自动搜索最优 α
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from sklearn.linear_model import LassoCV, RidgeCVimport numpy as npnp.random.seed(42 ) X = np.random.randn(200 , 100 ) true_coef = np.zeros(100 ) true_coef[:5 ] = [3 , -2 , 1.5 , -1 , 0.8 ] y = X @ true_coef + np.random.normal(0 , 1 , 200 ) lasso = LassoCV(cv=5 , random_state=42 ).fit(X, y) non_zero = np.sum (lasso.coef_ != 0 ) print (f"Lasso 选出 {non_zero} 个特征 (真实: 5)" )print (f"最优 α = {lasso.alpha_:.4 f} " )selected = np.where(lasso.coef_ != 0 )[0 ] print (f"选中的特征索引: {selected} " )
本节小结 回归的核心思想:用数学函数近似数据的”规律”
简单线性回归 y=ax+b 是起点,多元线性回归处理多个因素
R² 衡量模型好坏,但要小心过拟合
过拟合 vs 欠拟合是机器学习最核心的问题之一
正则化(L1/L2)是对抗过拟合的基本武器
回归是 数学重学路线图 后续机器学习内容的基础