一、线性回归
数据计算原理
例:通过多个特征预测成绩
线性回归的本质是加权求和(w*权重):
预测成绩 = (w1 × 学习时长) + (w2 × 作业均分) + (w3 × 互动次数) +
(w4 × 预习时间) + (w5 × 复习时间) + 截距
预测成绩 = (0.15 × 125) + (0.8 × 86) + (0.05 × 18) +
(0.1 × 32) + (0.12 × 28) + 50.0
= 18.75 + 68.8 + 0.9 + 3.2 + 3.36 + 50.0
≈ 85.01
你的数据(简化为3个学生,2个特征):
| 学生 | 学习时长(x₁) | 作业分(x₂) | 真实成绩(y) |
|---|---|---|---|
| A | 10小时 | 80分 | 85分 |
| B | 20小时 | 90分 | 95分 |
| C | 15小时 | 85分 | 90分 |
分步计算过程,权重截距计算
第1步:写成矩阵
[10 80] [85]
X = [20 90] y = [95]
[15 85] [90]
第2步:计算 XᵀX(X转置乘X)
Xᵀ = [10 20 15]
[80 90 85]
XᵀX = [10 20 15] × [10 80] = [10×10+20×20+15×15 10×80+20×90+15×85]
[80 90 85] [20 90] [80×10+90×20+85×15 80×80+90×90+85×85]
[15 85]
= [725 4175]
[4175 24125]
第3步:计算 (XᵀX)⁻¹(求逆矩阵)
逆矩阵计算复杂,这里直接给结果:
(XᵀX)⁻¹ ≈ [ 0.0241 -0.0042]
[-0.0042 0.0007]
第4步:计算 Xᵀy
Xᵀy = [10 20 15] × [85] = [10×85 + 20×95 + 15×90] = [4250]
[80 90 85] [95] [80×85 + 90×95 + 85×90] [24150]
[90]
第5步:计算权重 w = (XᵀX)⁻¹Xᵀy
w = [ 0.0241 -0.0042] × [4250]
[-0.0042 0.0007] [24150]
= [0.0241×4250 + (-0.0042)×24150]
[(-0.0042)×4250 + 0.0007×24150]
= [102.425 - 101.43] ≈ [0.995]
[-17.85 + 16.905] [-0.945]
所以:w₁ = 0.995, w₂ = -0.945
第6步:计算截距 b
b = y的平均值 - (w₁×x₁平均值 + w₂×x₂平均值)
y平均值 = (85+95+90)/3 = 90
x₁平均值 = (10+20+15)/3 = 15
x₂平均值 = (80+90+85)/3 = 85
b = 90 - (0.995×15 + (-0.945)×85)
= 90 - (14.925 - 80.325)
= 90 - (-65.4)
= 155.4
预测成绩 = 0.995×学习时长 - 0.945×作业分 + 155.4
为什这么计算可以通过多个特征实现预测
-
代数逻辑: 寻找"误差最小"的那个点(求导为0)。
-
几何逻辑: 上帝视角的"垂直投影"。
第一层理解:它是在"解方程组",不是在"猜"
不管是 1 个特征还是 100 个特征,本质上你都是在解一个超级方程组。
假设有 3 个数据点(刚才举的例子):
10 * w1 + 80 * w2 = 85
20 * w1 + 90 * w2 = 95
15 * w1 + 85 * w2 = 90
这就是一个 X⋅w=Y 的方程组。
如果数据完美,你用初中数学就能解出 w1,w2。但现实是残酷的: 数据有噪声,方程组通常无解(直线不可能同时完美穿过所有点)。既然找不到让等式左右两边完全相等的 w,那我就找一个让左右两边'差距最小'的 w。
第二层理解(核心):那两步运算到底在算什么?
公式是: w = (XᵀX)⁻¹Xᵀy
第一步:算 Xᵀy ------ "寻找相关性"
把特征矩阵 Xᵀ 转置,去乘结果 用y, 这是在做内积(Dot Product)。
这就好比你在问数据:"喂,特征 x1(学习时长)变大的时候,结果 y (成绩)是不是也变大?
-
如果它们步调一致,乘积就是正的巨款。
-
如果它们反着来,乘积就是负的。
-
Xᵀy 得到了一个"粗糙的权重":它告诉了我们每个特征和结果的"原始亲密度"。
第二步:算 (XᵀX)⁻¹ ------ "消除内部内卷"
把特征矩阵自己乘自己,然后求逆(相当于除法)。(XᵀX)⁻¹ 算的是特征之间的协方差(相关性)。
这就好比你在问:"特征 x1(学习时长)和特征 x2 (作业分)是不是其实是一回事?"
-
如果 x1 变大,x2 也跟着变大,说明这两个特征重复了(信息冗余)。
-
如果不除以这个重复度,你的权重就会算重,预测就会偏大。
-
求逆
(...)−1就是"除法",目的就是把这些重复的、内卷的信息剔除掉。`w=`(特征和结果的亲密度)`÷`(特征之间的重复度)`这就是线性回归的本质!`
第三层理解:上帝的"垂直投影"
-
想象一个空间: 你的特征 x1,x2 张成了一个平面(或者超平面)。
-
现实的残酷: 真实结果 Y 是一个向量,它飘在这个平面之外(因为有误差,不可能完美拟合)。
-
我们的目标: 我们要在平面上找一个向量(预测值 Y ),让它离真实的 Y 最近。
-
最近的距离是什么? 是垂直距离 ! 也就是从 Y 往平面上打一束光,留下的那个"影子"。那个矩阵运算公式,就是在计算这个"影子"的坐标。 垂直,意味着误差最小(勾股定理)。
基于python scikit-learn 简单的代码实现
例一
python
"""
## (一)回归任务预测
# 1.导入依赖包
# 2.准备数据
# 3.实例化 线性回归模型
# 4.模型训练
# 5.模型预测
x = [[80, 86], [82, 80], [85, 78], [90, 90], [86, 82], [82, 90], [78, 80], [92, 94]]
y = [84.2, 80.6, 80.1, 90, 83.2, 87.6, 79.4, 93.4]
"""
# 1.导入依赖包
from sklearn.linear_model import LinearRegression
def dm01_linear_regression_pred():
# 2.准备数据 平时成绩 期末成绩 最终成绩
x = [[80, 86], [82, 80], [85, 78], [90, 90], [86, 82], [82, 90], [78, 80], [92, 94]]
y = [84.2, 80.6, 80.1, 90, 83.2, 87.6, 79.4, 93.4]
# 3.实例化 线性回归模型
estimator = LinearRegression()
print('estimator-->', estimator)
# 4.模型训练
# 打印 线性回归模型参数 coef_ intercept_
estimator.fit(x, y)
print('estimator.coef_-->', estimator.coef_)
print('estimator.intercept_-->', estimator.intercept_)
# 5.模型预测
mypred = estimator.predict([[90, 80]])
print('mypred-->', mypred)
列二
python
"""
## (二)回归任务保存和加载模型
# 1.导入依赖包
# 2.准备数据
# 3.实例化 线性回归模型
# 4.模型训练
# 5.模型预测
# 6.模型保存 joblib.dump(estimator, xxpath)
# 7.模型加载 joblib.load(xxpath)
# 8.模型预测
x = [[80, 86], [82, 80], [85, 78], [90, 90], [86, 82], [82, 90], [78, 80], [92, 94]]
y = [84.2, 80.6, 80.1, 90, 83.2, 87.6, 79.4, 93.4]
"""
# 1.导入依赖包
import joblib
def dm02_linear_regression_pred():
# 2.准备数据 平时成绩 期末成绩 最终成绩
x = [[80, 86], [82, 80], [85, 78], [90, 90], [86, 82], [82, 90], [78, 80], [92, 94]]
y = [84.2, 80.6, 80.1, 90, 83.2, 87.6, 79.4, 93.4]
# 3.实例化 线性回归模型
estimator = LinearRegression()
print('estimator-->', estimator)
# 4.模型训练
estimator.fit(x, y)
# 5.模型预测
mypred = estimator.predict([[90, 80]])
print('mypred-->', mypred)
# 6.模型保存
print('\n模型保存和模型重新加载')
joblib.dump(estimator, './model/my_linear_regression_model01.bin')
# 7.模型加载
myestimator2 = joblib.load('./model/my_linear_regression_model01.bin')
print('myestimator2-->', myestimator2)
# 8.模型预测
mypred2 = myestimator2.predict([[90, 80]])
print('mypred2-->', mypred2)
例三
python
def dm03_linear_regression_pred():
# 1.导入模型
from sklearn.linear_model import LinearRegression
import numpy as np
# 2.准备数据 - 扩展更多数据,确保复习时间与成绩负相关
# [学习时长(h), 作业均分, 互动次数, 预习时间(h), 复习时间(h)]
x = np.array([
# 原始8个学生数据
[120, 85, 15, 30, 25], # 学生1
[100, 78, 10, 20, 20], # 学生2
[150, 92, 25, 40, 35], # 学生3
[80, 70, 8, 15, 15], # 学生4
[130, 88, 20, 35, 30], # 学生5
[110, 82, 12, 25, 22], # 学生6
[140, 90, 22, 38, 32], # 学生7
[95, 75, 9, 18, 18], # 学生8
# 新增数据 - 确保复习时间与成绩的负相关趋势
# 第一组:复习时间少但成绩好的学生(负相关证据)
[125, 90, 20, 32, 15], # 学生9:复习15h,但成绩好
[115, 88, 18, 28, 16], # 学生10:复习16h
[135, 91, 22, 35, 17], # 学生11:复习17h
[105, 85, 16, 24, 14], # 学生12:复习14h
# 第二组:复习时间多但成绩一般的学生(负相关证据)
[95, 72, 8, 20, 40], # 学生13:复习40h,成绩一般
[85, 68, 6, 18, 38], # 学生14:复习38h
[100, 75, 10, 22, 42], # 学生15:复习42h
[90, 70, 7, 19, 45], # 学生16:复习45h
# 第三组:中等复习时间的学生(平衡数据)
[110, 80, 14, 26, 25], # 学生17
[130, 87, 19, 34, 28], # 学生18
[120, 83, 17, 30, 22], # 学生19
[140, 89, 21, 37, 30], # 学生20
# 第四组:强化负相关趋势
[160, 93, 28, 42, 18], # 学生21:学习努力但复习少
[70, 65, 5, 12, 48], # 学生22:学习差但拼命复习
[145, 92, 24, 39, 19], # 学生23
[125, 86, 18, 32, 16], # 学生24
# 第五组:更多样本
[115, 81, 13, 27, 24], # 学生25
[135, 89, 20, 36, 26], # 学生26
[105, 77, 11, 23, 35], # 学生27:复习多但成绩差
[150, 94, 26, 41, 20], # 学生28
# 第六组:最后一批
[155, 95, 27, 43, 21], # 学生29
[125, 84, 16, 31, 23], # 学生30
[140, 88, 21, 36, 27], # 学生31
[130, 85, 19, 33, 29], # 学生32
])
# 目标值:期末总评成绩 - 确保复习时间与成绩负相关
y = np.array([
# 原始8个学生的成绩
88.5, 76.2, 94.8, 71.5, 90.2, 81.6, 92.5, 73.8,
# 新学生成绩 - 设计为复习时间与成绩负相关
# 第一组:复习少但成绩好
92.3, 89.7, 93.1, 87.9,
# 第二组:复习多但成绩一般
70.2, 65.8, 72.5, 64.9,
# 第三组:中等复习时间
82.4, 88.6, 84.3, 90.1,
# 第四组:强化负相关
95.5, 58.3, 92.8, 89.4,
# 第五组:更多样本
83.7, 89.2, 74.6, 93.7,
# 第六组:最后一批
94.8, 85.2, 88.9, 86.3,
])
# 3.实例化
estimator = LinearRegression()
print(f'模型对象: {estimator}')
# 4. 模型训练
estimator.fit(x, y)
# 5. 查看模型参数(重要!)
print(f'\n=== 模型参数 ===')
# 每个特征对应的"重要性分数" coef_ = [0.15, 0.8, 0.05, 0.1, 0.12]
# 正负号表示正向/负向影响
print(f'权重系数 (coef_): {estimator.coef_}')
print(f'权重系数个数: {len(estimator.coef_)}')
# 截距项 intercept_:所有特征为0时的基准成绩,例如 intercept_ = 50.0,表示即使所有特征都是0,也能得50分
print(f'截距项 (intercept_): {estimator.intercept_:.4f}')
# 6. 模型预测 - 预测一个新学生的成绩
# 新学生数据:[学习时长, 作业均分, 互动次数, 预习时间, 复习时间]
new_student = np.array([[125, 86, 18, 32, 28]])
prediction = estimator.predict(new_student)
print(f'\n=== 预测结果 ===')
print(f'新学生特征: {new_student[0]}')
print(f'预测成绩: {prediction[0]:.2f}')
# 7. 模型评估(简单示例)
# 计算训练集上的R²分数
score = estimator.score(x, y)
print(f'模型R²分数: {score:.4f}') #模型R²分数: 0.9785
# 8. 保存模型
import joblib
joblib.dump(estimator, './model/multi_feature_lr_model.bin')
print('\n模型已保存为: ./model/multi_feature_lr_model.bin')
# 9. 验证模型加载和预测
loaded_model = joblib.load('./model/multi_feature_lr_model.bin')
test_pred = loaded_model.predict(new_student)
print(f'加载模型后的预测验证: {test_pred[0]:.2f}')
注:R²分数解读指南(score = estimator.score(x, y))
| R²范围 | 训练集上 | 测试集上 | 含义 |
|---|---|---|---|
| < 0 | 灾难 | 灾难 | 模型比直接猜均值还差 |
| 0~0.3 | 很差 | 实际 | 模型解释力很弱,但有方向性 |
| 0.3~0.5 | 一般 | 不错 | 有一定预测能力,可用 |
| 0.5~0.7 | 较好 | 很好 | 模型相当可靠 |
| 0.7~0.9 | 很好 | 优秀 | 解释力很强 |
| > 0.9 | 危险信号! | 极好 | 可能过拟合了 |