3679 字
18 分钟
机器学习 (二) : 线性回归及最大熵模型
2026-03-25
2026-05-26

算法概述#

回归 : 对数据进行拟合

当曲线是一条直线,就是线性回归

在训练学习样本时,不仅需要提供特征向量X,还需要提供样本的实际结果(标记label),因此线性回归模型属于监督学习里的回归模型。

一元线性回归#

一个自变量,一个因变量

回归分析用来建立方程模拟两个或者多个变量之间如何关联 因变量:被预测的变量 自变量:被用来进行预测的变量

基于均方误差最小化来进行模型求解的方法称为“最小二乘法”

在线性回归中,最小二乘法就是试图找到一条直线,使所有样本到直线上的欧氏距离之和最小。

偏导取0后可得:

实验实现#

1.  使用最小二乘法进行房价预测:      给定训练样本集合如下:

import numpy as np
from sklearn.linear_model import LinearRegression
x_train = np.array([[10],[15],[20],[30],[50],[60],[60],[70]])
y_train = np.array([0.8, 1, 1.8, 2, 3.2, 3, 3.1, 3.5])
model = LinearRegression()
model.fit(x_train, y_train)
predict = model.predict([[55]])
print(f"输入 55 时的预测结果为: {predict[0]:.4f}")

多元线性回归#

实验实现#

求解:当工资18000、年龄30时,额度是多少?

import numpy as np
from sklearn.linear_model import LinearRegression
x_train = np.array([[4000,25],[8000,30],[5000,28],[7500,33],[12000,40]])
y_train = np.array([20000,70000,35000,50000,85000])
model = LinearRegression()
model.fit(x_train, y_train)
predict = model.predict([[18000,30]])
print(f"当工资18000、年龄30时的预测结果为: {predict[0]:.4f}")

最小二乘法的局限性#

首先,最小二乘法需要计算XTXX^TX的逆矩阵,有可能它的逆矩阵不存在,这样就没有办法使用最小二乘法了 ;

第二,当样本特征非常多的时候,计算XTXX^TX的逆矩阵是一个非常耗时的工作,甚至不可行 ;

第三,如果拟合函数不是线性的,这时无法使用最小二乘法,需要通过一些技巧转化为线性才能使用。

算法流程

梯度下降法#

想象一下,你被蒙住双眼,随机降落在了一座高山上的某个位置。你的目标是走到这座山的最低谷。因为眼睛看不见,你只能用脚去感受地面的倾斜程度。你会怎么做?

  1. 感受坡度(计算梯度): 你用脚试探周围,找到往下最陡峭的方向。
  2. 迈出一步(更新参数): 顺着这个最陡的下坡方向,你往前迈出一步
  3. 不断重复(迭代): 在新的位置上,你再次感受坡度,再朝着最陡的方向迈步。
  4. 到达谷底(收敛): 当你发现四周都没有下坡路,或者坡度平缓到几乎感觉不到时,你就可以认为自己已经到达了谷底。

在数学上,梯度(Gradient) 指的是一个函数在某一点上变化最快的方向。因为我们想要找到最小值,所以我们需要沿着梯度的相反方向(即负梯度方向)前进。

梯度下降法的核心更新公式非常简单:

wnew=woldαJ(wold)w_{new} = w_{old} - \alpha \cdot \nabla J(w_{old})

公式里的符号代表:

  • ww:我们要优化的模型参数(也就是你在山上的坐标)。
  • α\alpha学习率(Learning Rate)。它控制了你每次下山迈出的“步伐大小”。
  • J(w)J(w):损失函数。
  • J(wold)\nabla J(w_{old}):损失函数在当前参数下的梯度(也就是当前的坡度)。

代码分析#

import numpy as np
import matplotlib.pyplot as plt
# 1 获得x,y数据 ###########
iter = 50
X = np.random.rand(iter) * 20
noise = np.random.randn(iter)
y = 0.5 * X + noise
plt.scatter(X, y)
plt.show()
# 2 初始化参数 ##########
w = np.random.randn(1)
b = np.zeros(1)
lr = 0.001
for iteration in range(40):
# 初始化拟合
y_pred = w * X + b
# 梯度更新
w_gradient = 0
b_gradient = 0
N = len(X)
for i in range(N):
w_gradient += (w * X[i] + b - y[i]) * X[i]
b_gradient += (w * X[i] + b - y[i])
w -= lr * w_gradient / N
b -= lr * b_gradient / N
# 更新后拟合
y_pred = w * X + b
# 显示
plt.scatter(X, y, c="blue")
plt.plot(X, y_pred, c="red")
plt.pause(0.2)

制造地形与目标点(准备数据)#

iter = 50
X = np.random.rand(iter)*20
noise = np.random.randn(iter)
y = 0.5 * X + noise
  • 核心思路: 这一步相当于我们在现实中收集数据。代码生成了50个点,这些点大致服从一条直线方程:y=0.5xy = 0.5x

  • 为了模拟真实世界的不完美,代码特意加上了 noise(随机噪声)。我们的最终目标,就是让算法在不知道真实斜率是 0.50.5 的情况下,自己通过梯度下降法把这个 0.50.5 给“摸索”出来。

随机空降,准备下山(初始化参数)#

w = np.random.randn(1)
b = np.zeros(1)
lr = 0.001
  • 核心思路: 这里的 ww(斜率/权重)和 bb(截距/偏置)就是我们的模型参数,也就是“我们在山上的坐标”。

  • 算法一开始什么都不知道,所以我们给 ww 随便塞一个随机数,给 bb 塞个 00。此时画出来的红线(预测线)通常会偏得离谱。

  • lr = 0.001 就是我们之前提到的学习率(步伐大小)

核心大循环:感受坡度,不断迈步(梯度下降迭代)#

这部分是算法的灵魂。for iteration in range(40): 代表我们要走 40 步。每一步都包含了以下关键动作:

动作 A:评估现状(计算误差)

w_gradient = 0
b_gradient = 0
N = len(X)
for i in range(N):
w_gradient += (w * X[i] + b - y[i]) * X[i]
b_gradient += (w * X[i] + b - y[i])
  • 这段代码其实在算梯度(坡度)。它的背后是均方误差(MSE)的求导公式。

  • 注意看括号里的这部分:(w * X[i] + b - y[i])。这其实就是 预测值 - 真实值,我们叫它误差

    • 如果预测值比真实值大(红线在蓝点上方),误差是正的。
    • 如果预测值比真实值小(红线在蓝点下方),误差是负的。
  • 对于 bb 的梯度: 直接把所有点的误差累加起来。

  • 对于 ww 的梯度: 把误差乘上了对应的 X[i]X[i] 再累加。这就是在计算损失函数对 ww 的偏导数:

    Jw=1Ni=1N(y^iyi)xi\frac{\partial J}{\partial w} = \frac{1}{N} \sum_{i=1}^{N} (\hat{y}_i - y_i) x_i

动作 B:向下迈步(更新参数)

w -= lr * w_gradient / N
b -= lr * b_gradient / N
  • 这里完美体现了梯度下降的公式:新位置 = 老位置 - 学习率 × 梯度

  • 注意它除了 N,因为上面的 for 循环是累加求和,除以 N 就变成了平均梯度。这确保了无论你有 50 个数据还是 5000 个数据,梯度的量级是稳定的,也就是我们之前提到的 批量梯度下降(BGD)

实时录像(可视化更新过程)#

# 更新后拟合
y_pred = w * X + b
# 显示
plt.scatter(X, y, c="blue")
plt.plot(X, y_pred, c="red")
plt.pause(0.2)
  • 经过一次更新,wwbb 变得更准确了一点。代码用新的参数重新画了一条红线。
  • plt.pause(0.2) 让画面暂停 0.2 秒。在 40 次循环中,你会亲眼看到一条一开始乱跑的红线,一步一步地向蓝点群的中心靠拢,最终完美穿过它们。

梯度下降法求极值的主要问题#

  1. 梯度下降中的超参数设置

上面的梯度下降中提到了一个参数  ɑ,它又称为步长。这种算法是需要人为设置的,而非用来学习的参数,所以叫做超参数。步长是梯度下降算法中最重要的超参数,设置时需要精心考虑。

  1. 梯度下降的问题 如果目标函数有多个极小值点(很多个低谷),那么如果开始位置不理想,很可能导致最终卡在一个局部极小值。比如下图的例子。这是梯度下降算法的一大挑战。

梯度下降的方式#

逻辑回归#

用线性模型做“分类”

二分类任务#

思路:找线性回归结果z和期望输出结果y的关系

理想的函数应该是: 例如,当我们预测出来的结果大于50岁的时候,我们应该输出年老,小于50岁的时候应该输出年轻,但是这个函数是分段函数性质不好.

引入Logistic函数:

这里要区分两个概念: Logistic函数: p=Logistic(z)=11+ezp = \text{Logistic}(z) = \frac{1}{1 + e^{-z}} Logit函数: z=Logit(p)=ln(p1p)z = \text{Logit}(p) = \ln\left(\frac{p}{1 - p}\right)

基本思路就是:先回归,在经过Logistic函数转为分类问题

逻辑回归的语法#

导入包含分类方法的类: from sklearn.linear_model import LogisticRegression

创建该类的一个实例: LR = LogisticRegression(penalty = 'l2', C = 10.0)

拟合训练数据并预测: LR = LR.fit(X_train, y_train) y_predict = LR.predict(X_test)

评价指标#

混淆矩阵#

—混淆矩阵包含四部分的信息:

—1)  真阴性(TN)表明实际是负样本预测成负样本的样本数。 —2)  假阳性(FP)表明实际是负样本预测成正样本的样本数。 —3)  假阴性(FN)表明实际是正样本预测成负样本的样本数。 —4)  真阳性(TP)表明实际是正样本预测成正样本的样本数。

准确率#

Accuracy=TP+TNTP+TN+FP+FNAccuracy = \frac{TP + TN}{TP + TN + FP + FN} —虽然准确率可以判断总体正确率,但在样本不平衡的情况下,并不能作为很好的指标来衡量结果。假设在所有样本中,正样本占90%,负样本占10%,样本严重不平衡。模型将全部样本预测为正样本即可得到90%的高准确率,如果仅使用准确率这一单一指标,模型就可以像这样偷懒获得很高的评分。因此,衍生出了其它两种指标:精确率和召回率。

精确率与召回率#

精确率何时重要 在某些问题域中,精度比查全率更为重要。比如,当你在一家网店买东西时,他们的推荐系统经常会回馈像“买了X的顾客同时也买了Y”这样的信息。该信息的意图明显是想诱导你也购买Y。

推荐系统通过机器学习技术对公司历史数据进行分析完成。在评估其性能时,工程师希望得到较高的精度。这样,顾客也许会对推荐机制感到更加满意,否则很可能忽略推荐内容。

在这些问题域中,查全率的价值是不重要的。网店推荐清单的规模只能是有限的,因此即使系统只能辨认出一小部分顾客喜欢的产品也没太大关系。

高查全率何时重要 查全率同精度恰恰相反,在其它问题域中,查全率更为重要。这种情况在医药诊断中比较常见。例如:一个患X疾病的病人被确诊患X疾病,即真正类。患X疾病的病人没被诊断出患X疾病,即假负类。这是医生想要避免的一种情况——这意味着NFNN_{FN}应该要小。在查全率的定义R=NTP/(NTP+NFN)R=N_{TP}/(N_{TP}+N_{FN} )中, 假负类的数量在分母上,因此小的NFNN_{FN}意味着高的查全率。

PR曲线#

分类模型对每个样本点都会输出一个置信度。通过设定置信度阈值,就可以完成分类。不同的置信度阈值对应着不同的精确率和召回率。一般来说,置信度阈值较低时,大量样本被预测为正例,所以召回率较高,而精确率较低;置信度阈值较高时,大量样本被预测为负例,所以召回率较低,而精确率较高。

PR曲线就是以查准率为纵坐标,以查全率为横坐标做出的曲线,如图

ROC曲线与AUC曲线#

对于某个二分类分类器来说,输出结果标签(0还是1)往往取决于置信度以及预定的置信度阈值。比如常见的阈值就是0.5,大于0.5的认为是正样本,小于0.5的认为是负样本。如果增大这个阈值,预测错误(针对正样本而言,即指预测是正样本但是预测错误,下同)的概率就会降低,但是随之而来的就是预测正确的概率也降低;如果减小这个阈值,那么预测正确的概率会升高但是同时预测错误的概率也会升高。实际上,阈值的选取一定程度上反映了分类器的分类能力。我们当然希望无论选取多大的阈值,分类都能尽可能地正确。为了形象地衡量这种分类能力,ROC曲线进行了表征。

假阳率(FPR): TPR=TPTP+FNTPR = \frac{TP}{TP + FN}

真阳率(TPR): FPR=FPFP+TNFPR = \frac{FP}{FP + TN}

现在分析几个ROC曲线的特殊情况,更好地掌握其性质

(0, 0):假阳率和真阳率都为0,即分类器全部预测成负样本。 (0, 1):假阳率为0,真阳率为1,全部完美预测正确。 (1, 0):假阳率为1,真阳率为0,全部完美预测错误。 (1, 1):假阳率和真阳率都为1,即分类器全部预测成正样本

—当TPR=FPR为一条斜对角线时,表示预测为正样本的结果一半对,一半错,即为随机分类器的预测效果。ROC曲线在斜对角线以下,表示该分类器效果差于随机分类器;反之,效果好于随机分类器。当然,我们希望ROC曲线尽量位于斜对角线以上,也就是向左上角(0, 1)凸。

c1和c2哪条曲线更好?

答案只能基于实际应用中的特定需求来回答。根据图像可以得出以下结论:就正类而言,在负类错误率低的区域,c1的表现优于c2。随着负类样例错误率的增加,c2在正类样例上的表现优于c1。再者,分类器表现的好坏取决于用户的标准。

scikit-learn库函数#

accuracy_score:用于评价分类问题的准确率。其函数原型为:

sklearn.metrics.accuracy_score(y_true, y_pred, normalize=True, sample_weight=None)

precision_score:用于评价分类问题的查准率,其值为TP/(TP + FP),其中TP是真正的正例数目、FP是假阳性的数量。其函数原型为:

sklearn.metrics.precision_score(y_true, y_pred, labels=None, pos_label=1, average =’binary’, sample_weight=None)

计算分类结果的F_1值的f1_score; 计算分类结果的F_𝛽值的fbeata_score 计算分类结果的P-R曲线的precision_recall_curve 计算分类结果的ROC曲线的roc_curve 评价估计器在不同样本集上学习性能的函数learning_curve等等

机器学习 (二) : 线性回归及最大熵模型
https://dingfengbo.vercel.app/posts/机器学习/01-线性回归及最大熵模型/
作者
Eureka
发布于
2026-03-25
许可协议
CC BY-NC-SA 4.0