问题 | 梯度下降 | 正规方程 |
---|---|---|
学习率 | 需要选择合适的学习率 | 不需要 |
求解特点 | 需要多次迭代求解 | 一次运算得出 |
线性问题 | 可以解决 | 可以解决 |
非线性问题 | 可以解决 | 不可以解决 |
时间复杂度 | 难以直接给出的(受到初始值、学习率、迭代次数等多种因素的影响) | O ( n 3 ) O(n^3) O(n3) |
适用场景 | 特征数量多(特征数是特征的个数(列的个数),不是样本的数量) | 特征数量少 |
说明:
Q1:为什么说正规方程不能解决拟合问题?
A1:正规方程是一种求解线性回归的方法,它可以通过最小化误差的平方和寻找数据的最佳函数匹配。正规方程法是基于最小二乘法的,所以也被称为最小二乘法。但是,正规方程法不能解决拟合问题,因为当特征数量大于样本数量时,矩阵 X \rm X X 的转置矩阵 X \rm X X 不可逆。在这种情况下,我们可以使用岭回归或Lasso回归等方法来解决问题。
Q2:大规模数据集和小规模数据集说的样本数量大还是特征数大?
A2:大规模数据集和小规模数据集通常指的是样本数量的差异,而不是特征数的差异。
大规模数据集通常包含数百万或数十亿个样本,而小规模数据集通常只有数百到数千个样本。这种区分基于对机器学习算法性能和训练效果的影响,因为更多的数据可以帮助模型更好地泛化并提高准确性。
相比之下,特征数量通常在所有数据集中都是相似的,因为特征数量主要取决于数据类型和领域知识。但是,在某些情况下,例如自然语言处理中,特征数量可能随着文本长度和结构的变化而变化。
说明:
小结:
LinearRegression
(不能解决拟合问题)RidgeRegression
SGDRegressor
学习目标:
上一节中给大家介绍了最基本的梯度下降法实现流程,本节我们将进一步介绍梯度下降法的详细过算法推导过程和常见的梯度下降算法。
在详细了解梯度下降的算法之前,我们先复习相关的一些概念。
其中, x i x_i xi 表示第 i i i 个样本特征, y i y_i yi 表示第 i i i 个样本对应的输出, h θ ( x i ) h_\theta(x_i) hθ(xi) 为假设函数。
一、先决条件:确认优化模型的假设函数和损失函数
比如对于线性回归,假设函数表示为 h θ ( x 1 , x 2 , . . . , x n ) h_\theta(x_1, x_2, ..., x_n) hθ(x1,x2,...,xn) = θ 0 + θ 1 × x 1 + θ 2 × x 2 + . . . + θ n × x n \theta_0 + \theta_1 \times x_1 + \theta_2 \times x_2 + ... + \theta_n \times x_n θ0+θ1×x1+θ2×x2+...+θn×xn。其中 θ i ( i = 0 , 1 , 2 , . . . , n ) \theta_i(i=0, 1, 2, ..., n) θi(i=0,1,2,...,n) 为模型参数, x i ( i = 0 , 1 , 2 , . . . , n ) x_i(i = 0, 1, 2, ..., n) xi(i=0,1,2,...,n) 为每个样本的 n n n 个特征值。这个表示可以简化,我们增加一个特征 x 0 = 1 x_0 = 1 x0=1,这样
h θ ( x 0 , x 1 , . . . , x n ) = ∑ i = 0 n θ i x i h_\theta(x_0, x_1, ..., x_n) = \sum^n_{i = 0} \theta_i x_i hθ(x0,x1,...,xn)=i=0∑nθixi
其中, n n n 是每个样本的特征数量; θ i \theta_i θi 是模型参数。
同样是线性回归,对应于上面的假设函数,损失函数为:
L ( θ 0 , θ 1 , . . . , θ n ) = 1 2 m ∑ j = 0 m ( h θ ( x 0 j , x 1 j , . . . , x n j ) − y j ) 2 \mathcal{L}(\theta_0, \theta_1, ..., \theta_n) = \frac{1}{2m}\sum^m_{j=0}(h_\theta(x_0^{j}, x_1^{j}, ..., x_n^{j}) - y_j)^2 L(θ0,θ1,...,θn)=2m1j=0∑m(hθ(x0j,x1j,...,xnj)−yj)2
其中:
Q:为什么是 1 2 m \frac{1}{2m} 2m1,而不是 1 m \frac{1}{m} m1?
A:损失函数为 1 2 m \frac{1}{2m} 2m1 是为了方便求导。在线性回归中,我们使用的是最小二乘法,期望和实际值的方差作为损失函数。这个损失函数的导数是 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})x_j^{(i)} m1∑i=1m(hθ(x(i))−y(i))xj(i),其中 h θ ( x ) h_{\theta}(x) hθ(x) 是假设函数, x ( i ) x^{(i)} x(i) 是第 i i i 个样本的特征向量, y ( i ) y^{(i)} y(i) 是第 i i i 个样本的标签, j j j 是特征向量的维度。如果我们使用 1 m \frac{1}{m} m1 作为损失函数,那么导数就是 ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})x_j^{(i)} ∑i=1m(hθ(x(i))−y(i))xj(i),这样在梯度下降时就需要多除以一个 m m m,而使用 1 2 m \frac{1}{2m} 2m1 就可以避免这个问题。
二、算法相关参数初始化
主要是初始化模型参数 θ 0 , θ 1 , . . . , θ n \theta_0, \theta_1, ..., \theta_n θ0,θ1,...,θn,算法终止距离 ϵ \epsilon ϵ 以及步长 α \alpha α。在没有任何先验知识的时候,可以将所有的 ϵ \epsilon ϵ 初始化为0,将步长 α \alpha α 初始化为1。在之后的调优中再进行优化,以得到的更好的模型参数。
三、算法过程
步骤1:确定当前位置的损失函数的梯度,对于 θ i \theta_i θi,其梯度表达式如下:
∂ ∂ θ i L ( θ 0 , θ 1 , . . . , θ n ) \frac{\partial}{\partial{\theta_i}}\mathcal{L}(\theta_0, \theta_1, ..., \theta_n) ∂θi∂L(θ0,θ1,...,θn)
步骤2:用步长乘以损失函数的梯度,得到当前位置下降的距离,即:
α ∂ ∂ θ i L ( θ 0 , θ 1 , . . . , θ n ) \alpha\frac{\partial}{\partial{\theta_i}}\mathcal{L}(\theta_0, \theta_1, ..., \theta_n) α∂θi∂L(θ0,θ1,...,θn)
对应于前面登山例子中的某一步。
步骤3:确定是否所有的 θ i \theta_i θi 梯度下降的距离都小于 ϵ \epsilon ϵ。如果小于 ϵ \epsilon ϵ 则算法终止,当前所有的 θ i ( i = 0 , 1 , . . . , n ) \theta_i(i = 0, 1, ..., n) θi(i=0,1,...,n) 即为最终结果。否则进入步骤4。
步骤4:更新所有的 θ \theta θ,对于 θ i \theta_i θi,其更新表达式如下。更新完毕后继续转入步骤1。
θ i = θ i − α 1 m ∑ j = 0 m ( h θ ( x 0 ( j ) , h θ ( x 1 ( j ) , . . . , h θ ( x n ( j ) ) − y j ) x i j ) ) \theta_i = \theta_i - \alpha \frac{1}{m} \sum^m_{j=0}(h_\theta(x^{(j)}_0, h_\theta(x^{(j)}_1, ..., h_\theta(x^{(j)}_n) - y_j)x^{j}_i)) θi=θi−αm1j=0∑m(hθ(x0(j),hθ(x1(j),...,hθ(xn(j))−yj)xij))
下面用线性回归的例子来具体描述梯度下降。假设我们的样本是:
( x 1 ( 0 ) , x 2 ( 0 ) , . . . , x n ( 0 ) 特征值 , y 0 目标值 ) , ( x 1 ( 1 ) , x 2 ( 1 ) , . . . , x n ( 1 ) 特征值 , y 1 目标值 ) , . . . , ( x 1 ( m ) , x 2 ( m ) , . . . , x n ( m ) 特征值 , y m 目标值 ) (\underset{特征值}{x^{(0)}_1, x^{(0)}_2, ..., x^{(0)}_n}, \underset{目标值}{y_0}), (\underset{特征值}{x^{(1)}_1, x^{(1)}_2, ..., x^{(1)}_n}, \underset{目标值}{y_1}), ..., (\underset{特征值}{x^{(m)}_1, x^{(m)}_2, ..., x^{(m)}_n}, \underset{目标值}{y_m}) (特征值x1(0),x2(0),...,xn(0),目标值y0),(特征值x1(1),x2(1),...,xn(1),目标值y1),...,(特征值x1(m),x2(m),...,xn(m),目标值ym)
其中, n n n 是每个样本的特征数量; m m m 是样本数量; θ i \theta_i θi 是模型参数。
损失函数如前面先决条件所述:
L ( θ 0 , θ 1 , . . . , θ n ) = 1 2 m ∑ j = 0 m ( h θ ( x 0 j , x 1 j , . . . , x n j ) − y j ) 2 \mathcal{L}(\theta_0, \theta_1, ..., \theta_n) = \frac{1}{2m}\sum^m_{j=0}(h_\theta(x_0^{j}, x_1^{j}, ..., x_n^{j}) - y_j)^2 L(θ0,θ1,...,θn)=2m1j=0∑m(hθ(x0j,x1j,...,xnj)−yj)2
则在算法过程步骤1中对于 θ i \theta_i θi 的偏导数计算如下:
∂ ∂ θ i L ( θ 0 , θ 1 , . . . , θ n ) = 1 m ∑ j = 0 m ( h θ ( x 0 j , x 1 j , . . . , x n j ) − y j ) x i ( j ) \frac{\partial}{\partial{\theta_i}}\mathcal{L}(\theta_0, \theta_1, ..., \theta_n) = \frac{1}{m}\sum^m_{j=0}(h_\theta(x_0^{j}, x_1^{j}, ..., x_n^{j}) - y_j)x_i^{(j)} ∂θi∂L(θ0,θ1,...,θn)=m1j=0∑m(hθ(x0j,x1j,...,xnj)−yj)xi(j)
由于样本中没有 x 0 x_0 x0,上式中令所有的 x 0 x_0 x0 为1。
步骤4中模型参数 θ i \theta_i θi 的更新表达式如下:
θ i = θ i − α 1 m ∑ j = 0 m ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \frac{1}{m} \sum^m_{j=0}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θi−αm1j=0∑m(hθ(x0(j),x1(j),...,xn(j))−yj)xi(j)
从这个例子可以看出当前点的梯度方向是由所有的样本决定的,加 1 m \frac{1}{m} m1 是为了好理解。由于步长也为常数,它们的乘积也为常数,所以这里 α 1 m \alpha \frac{1}{m} αm1 可以用一个常数表示。
在下面一节中,咱们会详细讲到梯度下降法的变种,它们主要的区别就是对样本的采用方法不同。这里我们采用的是用所有样本。
常见的梯度下降算法有:
它们都是为了正确地调节权重(模型参数)向量。通过为每个权重(模型参数)计算一个梯度,从而更新权值,使目标函数尽可能最小化。其差别在于样本的使用方式不同。
全梯度下降算法是梯度下降法最常用的形式,具体做法也就是在更新参数时使用所有的样本来进行更新。具体做法为:计算训练集所有样本误差,对其求和再取平均值作为目标函数。因为权重向量沿其梯度相反的方向移动,所以使当前目标函数减少得最多。
在整个训练数据集上计算损失函数关于参数 θ \theta θ 的梯度:
θ i = θ i − α ∑ j = 1 m ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \sum^m_{j = 1}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θi−αj=1∑m(hθ(x0(j),x1(j),...,xn(j))−yj)xi(j)
其中:
由于我们有 m m m 个样本,这里求梯度的时候就用了所有 m m m 个样本的梯度数据。
注意:
由于全梯度下降算法FGD每迭代更新一次权重都需要计算所有样本误差,而实际问题中经常有海量的训练样本,故效率偏低,且容易陷入局部最优解,因此提出了随机梯度下降算法。
随机梯度下降算法SDG每轮计算的目标函数不再是全体样本误差,而仅是单个样本误差,即每次只代入计算一个样本目标函数的梯度来更新权重,再取下一个样本重复此过程,直到损失函数值停止下降或损失函数值小于某个阈值。
随机梯度下降算法SGD的过程简单、高效,通常可以较好地避免更新迭代收敛到局部最优解。
SGD的迭代形式为:
θ i = θ i − α ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θi−α(hθ(x0(j),x1(j),...,xn(j))−yj)xi(j)
其中:
和FGD相比,没有 ∑ \sum ∑ 求和了,因为计算的是某一个样本。
优点:由于不是在全部训练数据上的损失函数,而是在每轮迭代中,随机优化某一条训练数据上的损失函数,这样每一轮参数的更新速度大大加快。
缺点:
小批量梯度下降算法MBGD是全梯度下降算法FGD和随机梯度下降算法SGD的折中方案,在一定程度上兼顾了以上两种方法的优点。
小批量梯度下降算法MBGD每次从训练样本集上随机抽取一个小样本集,在抽出来的小样本集上采用FGD迭代更新权重。被抽出的小样本集所含样本点的个数称为batch_size
,通常设置为2的幂次方,更有利于CPU/GPU加速处理。
注意:
batch_size = 1
,则变成了SGDbatch_size = n
,则变成了FGDMBGD 的迭代形式为:
θ i = θ i − α ∑ j = t t + b s − 1 ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \sum^{t+bs-1}_{j = t}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θi−αj=t∑t+bs−1(hθ(x0(j),x1(j),...,xn(j))−yj)xi(j)
MBGD 的迭代形式为:
θ i = θ i − α ∑ j = t t + x − 1 ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \sum^{t+x-1}_{j = t}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θi−αj=t∑t+x−1(hθ(x0(j),x1(j),...,xn(j))−yj)xi(j)
其中:
上式中,也就是我们从 m m m 个样本中,选择 b s bs bs 个样本进行迭代( 1 < b s < m 1
随机平均梯度下降算法(Stochastic average gradient descent,SAGD)是一种求解的最优化算法,是随机梯度下降算法的一种变体。它是一种基于梯度的优化方法,用于求解大规模数据集上的凸优化问题。SAGD算法在每次迭代时,使用所有样本的梯度的平均值来更新权重。相比于传统的随机梯度下降算法,SAGD算法在每次迭代时需要计算所有样本的梯度,因此计算量较大,但是收敛速度更快,且能够避免随机梯度下降算法中出现的震荡现象。
在 SGD 方法中,虽然避开了运算成本大的问题,但对于大数据训练而言,SGD 效果常不尽如人意,因为每一轮梯度更新都完全与上一轮的数据和梯度无关。
随机平均梯度算法 SAGD 克服了这个问题,因为SAGD在内存中为每一个样本都维护一个旧的梯度,随机选择第 i i i 个样本来更新此样本的梯度,其他样本的梯度保持不变,然后求得所有梯度的平均值,进而更新了参数。
如此,每一轮更新仅需计算一个样本的梯度,计算成本等同于 SGD,但收敛速度快得多。其迭代形式为:
θ i = θ i − α 1 n ( h θ ( x 0 ( j ) , x 1 ( j ) , . . . , x n ( j ) ) − y j ) x i ( j ) \theta_i = \theta_i - \alpha \frac{1}{n}(h_\theta(x^{(j)}_0, x^{(j)}_1, ..., x^{(j)}_n) - y_j)x^{(j)}_i θi=θi−αn1(hθ(x0(j),x1(j),...,xn(j))−yj)xi(j)
其中:
我们知道 SGD 是当前权重减去步长乘以梯度,得到新的权重。SAGD 中的A
,就是平均的意思。具体说,就是在第 k k k 步迭代的时候,我考虑的这一步和前面 n − 1 n-1 n−1 个梯度的平均值,即当前权重减去步长乘以最近 n n n 个梯度的平均值。
注意: n n n 是自己设置的,当 n = 1 n=1 n=1 的时候,就是普通的 SGD。
这个想法非常的简单,在随机中又增加了确定性,类似于 MBGD 的作用。但不同的是,SAGD 又没有去计算更多的样本,只是利用了之前计算出来的梯度,所以每次迭代的计算成本远小于 MBGD,和 SGD 相当。
以下这些算法主要用于深度学习优化
学习目标:
语法1:
sklearn.linear_model.LinearRegression(fit_intercept=True)
fit_intercept
:是否计算偏置LinearRegression.coef_
:回归系数LinearRegression.intercept_
:偏置语法2:
sklearn.linear_model.SGDRegressor(loss="squared_loss",
fit_intercept=True,
learning_rate="invscaling",
eta0=0.01)
loss
:损失类型
loss="squared_loss"
:普通最小二乘法fit_intercept
:是否计算偏置learning_rate
: string, optional 学习率填充
"constant"
:eta = eta0
"optimal"
:eta = 1.0 / (alpha * (t + t0)) [default]
:
eta = eta0 / pow(t, power_t)`
power_t=0.25
:存在父类当中learning_rate='constant'
,并使用eta0
来指定学习率SGDRegressor.coef_
:回归系数SGDRegressor.intercept_
:偏置sklearn 提供给我们两种实现的 API,可以根据需要选择使用
学习目标:
实例数量 | 506 |
属性数量 | 13 数值型或类别型,帮助预测的属性 |
中位数(第 14 个属性)经常是学习目标 | |
属性信息(按顺序) | CRIM:城镇人均犯罪率 ZN:占地面积超过 2.5 万平方英尺的住宅用地比例 INDUS:城镇非零售业务地区的比例 CHAS:查尔斯河虚拟变量(如果土地在河边 = 1;否则是 0) NOX:一氧化氮浓度(每 1000 万份) RM:平均每居民房数 AGE:在 1940 年之前建成的所有者占用单位的比例 DIS:与五个波士顿就业中心的加权距离 RAD:辐射状公路的可达性指数 TAX:每10,000美元的全额物业税率 PTRATIO:城镇师生比例 B 1000(Bk - 0.63)^2:其中 Bk 是城镇的黑人比例 LSTAT:人口中地位较低人群的百分数 MEDV:以 1000 美元计算的自有住房的中位数 |
缺失属性值 | 无 |
创建者 | Harrison, D. and Rubinfeld, D.L… |
这是UCI ML(欧文加利福尼亚大学机器学习库)房价数据集的副本。
下载地址:https://archive.ics.uci.edu/ml/machine-learning-databases/housing/
该数据集是从位于卡内基梅隆大学维护的 StatLib 图书馆取得的。
属性名 | 解释 | 类型 |
---|---|---|
CRIM | 该镇的人均犯罪率 | 连续值 |
ZN | 占地面积超过 25,000 平方尺的住宅用地比例 | 连续值 |
INDUS | 非零售商业用地比例 | 连续值 |
CHAS | 是否邻近Charles River | 离散值:1=邻近;0=不邻近 |
Nox | 一氧化氮浓度 | 连续值 |
RM | 每栋房屋的年均客房数 | 连续值 |
AGE | 1940年之前建成的自用单位比例 | 连续值 |
DIS | 到波士顿5个就业中心的加权距离 | 连续值 |
RAD | 到径向公路的可达性指数 | 连续值 |
TAX | 全值财产税率 | 连续值 |
PTRATIO | 学生与教师的比例 | 连续值 |
B 1000(BK - 0.63)^2,其中 BK 为黑人占比 | 连续值 | |
LSTAT | 低收入人群占比 | 连续值 |
MEDV | 同类房屋价格的中位数 | 连续值 |
给定的这些特征,是专家们得出的影响房价的结果属性。我们此阶段不需要自己去探究特征是否有用,只需要使用这些特征。到后面量化很多特征需要我们自己去寻找
回归当中的数据大小不一致,是否会导致结果影响较大。所以需要做标准化处理。
这里使用均方误差(Mean Squared Error,MSE)评价机制。
M S E = 1 m ∑ i = 1 m ( y i − y ‾ ) 2 \mathrm{MSE} = \frac{1}{m} \sum_{i=1}^m(y^i - \overline{y})^2 MSE=m1i=1∑m(yi−y)2
其中, y i y^i yi 为预测值, y ‾ \overline{y} y 为真实值的平均值。
API:
sklearn.metrics.mean_squared_error(y_true, y_pred)
y_true
:真实值y_pred
:预测值return
:浮点数结果Q:MSE和最小二乘法的区别是什么?
A:MSE和最小二乘法都是用于回归分析的方法,但是它们的计算方式不同。MSE是预测值与真实值之间差的平方和的平均值。而最小二乘法是一种优化方法,它通过最小化误差平方和来估计模型参数。
最小二乘法是求误差的平方和,而MSE是求完平方和后再求其均值
注意:在加载 Boston 房价数据集时会报错,因为自从 scikit-learn 1.2 版本以后,load_boston
已经被移除了。Boston 房价数据集存在一个伦理问题:该数据集的作者构造了一个不可逆的变量“B”,假设种族自我隔离对房价有积极影响。此外,导致创建此数据集的研究目标是研究空气质量的影响,但它没有充分证明这个假设的有效性。
因此,scikit-learn 维护者强烈不建议使用此数据集,除非代码的目的是研究和教育数据科学和机器学习中的伦理问题。
导入必要的库:
# from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, SGDRegressor
from sklearn.metrics import mean_squared_error
import pandas as pd
import numpy as np
因为 Boston 房价数据集已被移除,因此我们需要单独获取:
# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
def linear_model_1_Normal_Equation():
"""
线性回归:正规方程
"""
# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)
# 3. 特征工程:标准化
transformer = StandardScaler()
# fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
x_train = transformer.fit_transform(x_train)
# transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
x_test = transformer.transform(x_test)
# 4. 机器学习:线性回归(正规方程)
estimator = LinearRegression()
estimator.fit(x_train, y_train)
# 5. 模型评估
## 5.1 获取系数
y_predict = estimator.predict(x_test)
print(f"预测值为:{y_predict}")
print(f"模型中的系数为:{estimator.coef_}")
print(f"模型中的偏置为:{estimator.intercept_}")
## 5.2 评价:均方误差
error_mse = mean_squared_error(y_test, y_predict)
print(f"均方误差为:{error_mse}")
return None
def linear_model_2_Gradient_Descent():
"""
线性回归:梯度下降法
"""
# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)
# 3. 特征工程:标准化
transformer = StandardScaler()
# fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
x_train = transformer.fit_transform(x_train)
# transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
x_test = transformer.transform(x_test)
# 4. 机器学习:线性回归(梯度下降法)
estimator = SGDRegressor(max_iter=1000) # 这个函数中的max_iter不是epoch,而是每个epoch中的迭代次数。
estimator.fit(X=x_train, y=y_train)
# 5. 模型评估
## 5.1 获取系数
y_predict = estimator.predict(x_test)
print(f"模型的预测值为:{y_predict}")
print(f"模型中的系数为:{estimator.coef_}")
print(f"模型中的偏置为:{estimator.intercept_}")
## 5.2 评价:MSE
error = mean_squared_error(y_true=y_test, y_pred=y_predict)
print(f"MSE误差为:{error}")
return None
Epoch是指模型在数据集上进行训练的轮数,每一轮训练数据集称为一次迭代。而迭代次数(iteration)是指模型在数据集上进行训练的次数,每次训练使用的数据量是 batch size。
if __name__ == "__main__":
linear_model_1_Normal_Equation()
运行结果如下:
预测值为:
[23.01506153 21.2115869 33.71590384 31.56542369 3.1826268 3.15381954
27.40305304 22.2126176 14.86506114 21.34105453 30.95942941 26.70065029
21.12624382 18.37282564 17.64315354 25.38194186 24.42970445 13.36957057
8.66686786 18.57490534 21.73966467 20.34270529 36.5461105 20.59627495
19.87979627 15.75766967 37.11632999 34.85897895 30.83458635 23.23441285
18.68278505 20.749546 31.84560076 30.20214207 13.3861702 15.87078398
13.70766096 23.74163998 25.95135088 23.18325878 28.99906539 12.50341936
31.08347911 6.39401895 23.71801218 20.61523929 33.15362417 19.21862493
35.89603081 0.82365329 31.90288611 31.69640543 6.58849712 34.62762996
20.41162545 19.69277608 19.53445865 18.58689088 15.81420496 22.98764309
19.65947045 16.36377019 18.48783369 32.76568172 35.49022568 24.58349631
41.5854766 32.94818456 14.60990256 27.43178268 8.04470074 5.61185652
22.21428332 18.72817007 31.02824788 26.04494485 24.60357003 24.84231113
25.38796252 24.87762205 33.71343923 19.72606026 20.60046055 27.82692882
38.0055624 37.24265207 22.16841364 29.6160177 31.07303315 17.93399181
20.87524555 19.48170453 18.61409692 37.13055111 39.81659125 9.1811861
35.30202671 30.28664671 21.0820992 13.65467682 31.38696603 24.99174874]
模型中的系数为:
[-0.93451207 0.85487686 -0.10446819 0.81541757 -1.90731862 2.54650028
0.25941464 -2.92654009 2.80505451 -1.95699832 -2.15881929 1.09153332
-3.91941941]
模型中的偏置为:22.44133663366339
均方误差为:18.4954
if __name__ == "__main__":
linear_model_2_Gradient_Descent()
运行结果如下:
模型的预测值为:
[22.97008601 21.54233749 33.40398056 31.37169431 3.02490022 3.28076204
27.33142199 22.87326808 14.89699494 21.56820457 31.06330403 26.50800701
21.36331286 18.30966912 17.6258269 25.36147902 24.44335033 13.29265393
8.67786007 18.55642685 21.69966353 20.61348938 36.48886977 20.52798849
19.83874268 15.88847558 37.1515701 35.0545775 30.57079889 23.4462129
18.4629217 20.67485546 31.9187924 30.18674511 13.33091437 16.04626997
13.5563538 23.95300489 26.00718192 23.32099056 28.85934425 12.58600836
31.34438891 6.28300469 23.4899127 20.51246142 33.54006522 19.15099843
35.78666183 0.55787795 31.97353393 31.66015891 6.67574259 34.36353447
20.19279146 19.87162432 19.401037 18.77365657 15.94213222 23.05213487
19.40511698 16.62341646 18.50166401 32.34809036 35.1807121 24.91741065
41.18710196 33.03099357 14.43244788 27.63546339 8.21972512 5.55359461
22.29067012 18.56491174 31.11417781 26.1689206 24.51138147 24.64021638
25.31640483 24.92124789 33.59732337 20.00217476 20.52346408 27.85202637
37.94979065 37.1844462 22.02677899 29.62224074 31.18202907 17.80338565
21.0977779 19.62423171 18.58089503 36.8570065 39.77825674 9.11104817
35.32418125 30.74161137 21.08474487 13.81576484 31.42289792 25.00526589]
模型中的系数为:
[-0.86461037 0.76275779 -0.33309812 0.87192037 -1.76987389 2.62119033
0.21609998 -2.8546837 2.19042893 -1.32462947 -2.11576385 1.09324102
-3.89656514]
模型中的偏置为:[22.46238316]
MSE误差为:18.6316
sklearn.linear_model.SGDRegressor(loss="squared_loss",
fit_intercept=True,
learning_rate="invscaling",
eta0=0.01)
我们也可以尝试去修改学习率等参数,找到效果更好的超参数。
这里我们对学习率进行修改,看看不同学习率对误差的影响(误差越小越好)。
Q:在线性回归任务中,可以使用准确率作为评估指标吗?
A:在线性回归任务中,准确率不是一个好的评估指标。因为准确率是分类问题的评估指标,而线性回归是一个回归问题。在回归问题中,我们通常使用均方误差(MSE)、均方根误差(RMSE)和平均绝对误差(MAE)等指标来评估模型的性能。
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error, accuracy_score
import pandas as pd
import numpy as np
# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)
# 3. 特征工程:标准化
transformer = StandardScaler()
# fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
x_train = transformer.fit_transform(x_train)
# transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
x_test = transformer.transform(x_test)
# 定义学习率列表
lr_lst = [0.00001, 0.0001, 0.001, 0.01, 0.02, 0.05, 0.10, 0.25, 0.5, 0.9]
for lr in lr_lst:
# 4. 机器学习:线性回归(梯度下降法)
estimator = SGDRegressor(max_iter=1000, eta0=lr) # 这个函数中的max_iter不是epoch,而是每个epoch中的迭代次数。
estimator.fit(X=x_train, y=y_train)
# 5. 模型评估
y_predict = estimator.predict(X=x_test)
error = mean_squared_error(y_true=y_test, y_pred=y_predict)
print(f"[学习率={lr}] MSE误差为:{error:.4f}")
运行结果:
[学习率=0.0001] MSE误差为:25.7471
[学习率=0.001] MSE误差为:18.7909
[学习率=0.01] MSE误差为:18.6316
[学习率=0.02] MSE误差为:18.4748
[学习率=0.05] MSE误差为:18.2795
[学习率=0.1] MSE误差为:18.7501
[学习率=0.25] MSE误差为:29.4968
[学习率=0.5] MSE误差为:44.6285
[学习率=0.9] MSE误差为:592136344112942678016.0000
Q1:对于 Boston 房价数据集而言,MSE误差为:18.2795,效果如何?
A1:波士顿房价数据集的 MSE 误差为 18.2795,这个数值越小,说明预测结果越准确。因此,这个结果是比较好的。
Q2:在深度学习中,误差一般都是小数,而在机器学习中,18.2795 的MSE误差可以称为“好”吗?
A2:在机器学习中,MSE 误差的大小并不是绝对的,而是相对于数据集的范围而言。因此,18.2795 的 MSE 误差可以称为“好”,但需要结合数据集的范围来看待这个数值。
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error, accuracy_score
import pandas as pd
import numpy as np
# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)
# 3. 特征工程:标准化
transformer = StandardScaler()
# fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
x_train = transformer.fit_transform(x_train)
# transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
x_test = transformer.transform(x_test)
# 定义迭代次数列表
iter_lst = [1, 10, 100, 1000, 2000, 5000, 10000, 50000, 100000, 1000000]
for iter in iter_lst:
# 4. 机器学习:线性回归(梯度下降法)
estimator = SGDRegressor(max_iter=iter) # 这个函数中的max_iter不是epoch,而是每个epoch中的迭代次数。
estimator.fit(X=x_train, y=y_train)
# 5. 模型评估
y_predict = estimator.predict(X=x_test)
error = mean_squared_error(y_true=y_test, y_pred=y_predict)
print(f"[迭代次数={iter}] MSE误差为:{error:.4f}")
运行结果:
[迭代次数=10] MSE误差为:18.8887
[迭代次数=100] MSE误差为:18.5569
[迭代次数=1000] MSE误差为:18.6446
[迭代次数=2000] MSE误差为:18.8068
[迭代次数=5000] MSE误差为:18.5531
[迭代次数=10000] MSE误差为:18.6446
[迭代次数=50000] MSE误差为:18.5797
[迭代次数=100000] MSE误差为:18.5219
[迭代次数=1000000] MSE误差为:18.5547
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error, accuracy_score
import pandas as pd
import numpy as np
# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=2, test_size=0.2)
# 3. 特征工程:标准化
transformer = StandardScaler()
# fit()函数用于计算训练数据的均值和标准差,以便对训练数据进行标准化
x_train = transformer.fit_transform(x_train)
# transform()函数用于使用相同的均值和标准差来对测试数据进行标准化。这确保了我们使用相同的标准化方法来处理训练和测试数据。
x_test = transformer.transform(x_test)
# 定义学习率列表
lr_lst = [0.00001, 0.0001, 0.001, 0.01, 0.02, 0.05]
# 定义迭代次数列表
iter_lst = [1000, 2000, 5000, 10000, 50000, 100000, 1000000]
log = {}
print("-" * 50)
for lr in lr_lst:
for iter in iter_lst:
# 4. 机器学习:线性回归(梯度下降法)
estimator = SGDRegressor(max_iter=iter, eta0=lr) # 这个函数中的max_iter不是epoch,而是每个epoch中的迭代次数。
estimator.fit(X=x_train, y=y_train)
# 5. 模型评估
y_predict = estimator.predict(X=x_test)
error = mean_squared_error(y_true=y_test, y_pred=y_predict)
print(f"[学习率={lr}][迭代次数={iter}] MSE误差为:{error:.4f}")
print("-" * 50)
[学习率=1e-05][迭代次数=1000] MSE误差为:369.9415
[学习率=1e-05][迭代次数=2000] MSE误差为:276.4349
[学习率=1e-05][迭代次数=5000] MSE误差为:143.1826
[学习率=1e-05][迭代次数=10000] MSE误差为:65.0462
[学习率=1e-05][迭代次数=50000] MSE误差为:30.8400
[学习率=1e-05][迭代次数=100000] MSE误差为:30.8408
[学习率=1e-05][迭代次数=1000000] MSE误差为:30.8405
--------------------------------------------------
[学习率=0.0001][迭代次数=1000] MSE误差为:25.7335
[学习率=0.0001][迭代次数=2000] MSE误差为:19.4445
[学习率=0.0001][迭代次数=5000] MSE误差为:19.4434
[学习率=0.0001][迭代次数=10000] MSE误差为:19.4428
[学习率=0.0001][迭代次数=50000] MSE误差为:19.4439
[学习率=0.0001][迭代次数=100000] MSE误差为:19.4458
[学习率=0.0001][迭代次数=1000000] MSE误差为:19.4455
--------------------------------------------------
[学习率=0.001][迭代次数=1000] MSE误差为:18.8040
[学习率=0.001][迭代次数=2000] MSE误差为:18.8028
[学习率=0.001][迭代次数=5000] MSE误差为:18.8255
[学习率=0.001][迭代次数=10000] MSE误差为:18.8165
[学习率=0.001][迭代次数=50000] MSE误差为:18.8200
[学习率=0.001][迭代次数=100000] MSE误差为:18.7980
[学习率=0.001][迭代次数=1000000] MSE误差为:18.7967
--------------------------------------------------
[学习率=0.01][迭代次数=1000] MSE误差为:18.5404
[学习率=0.01][迭代次数=2000] MSE误差为:18.6090
[学习率=0.01][迭代次数=5000] MSE误差为:18.5098
[学习率=0.01][迭代次数=10000] MSE误差为:18.5280
[学习率=0.01][迭代次数=50000] MSE误差为:18.6144
[学习率=0.01][迭代次数=100000] MSE误差为:18.6155
[学习率=0.01][迭代次数=1000000] MSE误差为:18.5032
--------------------------------------------------
[学习率=0.02][迭代次数=1000] MSE误差为:18.7820
[学习率=0.02][迭代次数=2000] MSE误差为:19.0602
[学习率=0.02][迭代次数=5000] MSE误差为:19.0929
[学习率=0.02][迭代次数=10000] MSE误差为:18.4048
[学习率=0.02][迭代次数=50000] MSE误差为:18.5853
[学习率=0.02][迭代次数=100000] MSE误差为:18.6895
[学习率=0.02][迭代次数=1000000] MSE误差为:18.6095
--------------------------------------------------
[学习率=0.05][迭代次数=1000] MSE误差为:19.1055
[学习率=0.05][迭代次数=2000] MSE误差为:19.1228
[学习率=0.05][迭代次数=5000] MSE误差为:20.9130
[学习率=0.05][迭代次数=10000] MSE误差为:17.4903
[学习率=0.05][迭代次数=50000] MSE误差为:17.6103
[学习率=0.05][迭代次数=100000] MSE误差为:19.9095
[学习率=0.05][迭代次数=1000000] MSE误差为:19.7085
--------------------------------------------------
小结:
学习目标:
过拟合:一个假设在训练数据上能够获得比其他假设更好的拟合,但是在测试数据集上却不能很好地拟合数据,此时认为这个假设出现了过拟合的现象。(模型过于复杂)
欠拟合:一个假设在训练数据上不能获得更好的拟合,并且在测试数据集上也不能很好地拟合数据,此时认为这个假设出现了欠拟合的现象。(模型过于简单)
Q:那么是什么原因导致模型复杂?
A:线性回归模型在处理非线性关系的数据时会变得复杂。这是因为线性回归模型只能处理线性关系的数据,而现实中的很多事物特征与目标值之间的关系并不是简单的线性关系,因此需要引入更多的特征来描述这种复杂的关系,从而导致模型变得更加复杂。此外,如果训练数据中存在很多无用的特征,也会导致模型变得复杂。因此,在进行线性回归模型训练时,需要对特征进行筛选和优化,以提高模型的性能和泛化能力。
一、欠拟合原因以及解决办法
二、过拟合原因以及解决办法
在机器学习中,维度灾难是指在高维空间中,数据变得稀疏且难以处理的现象。当特征数量很大时,模型的复杂度会增加,这可能会导致过拟合。减少特征维度是一种防止维度灾难的方法。这可以通过特征选择、特征提取和降维来实现。在特征选择中,我们选择最相关的特征,而在特征提取中,我们将原始特征转换为新的特征。降维是一种更广泛的方法,它可以通过 PCA、LDA 等技术来实现。减少特征维度可以提高模型的泛化能力,并减少过拟合的风险。
在机器学习中,正则化是一种防止过拟合的技术。它通过向模型添加额外的约束来限制模型的复杂度,从而提高模型的泛化能力。正则化可以通过在损失函数中添加一个正则化项来实现,这个正则化项通常是模型参数的范数。L1 和 L2 正则化是两种常见的正则化方法。L1 正则化通过将模型参数的绝对值加到损失函数中来实现,而 L2 正则化通过将模型参数的平方和加到损失函数中来实现。这两种方法都可以有效地减少过拟合。
Q:给模型添加正则项,一般是在损失函数中添加吗?
A:是的,通常情况下,正则项会被添加到损失函数中。正则化是一种用来防止过拟合的技术,它通过在损失函数中添加一个正则项来实现。这个正则项通常是模型参数的函数,它会惩罚模型复杂度过高的情况。在训练过程中,优化算法会试图最小化损失函数,这就意味着它不仅要最小化预测误差,还要最小化正则项。
在解决回归过拟合中,我们可以选择正则化。但是对于其他机器学习算法如分类算法来说也会出现这样的问题,除了一些算法本身作用之外(决策树、神经网络),我们更多的也是去自己做特征选择,包括之前说的删除、合并一些特征。
我们从上图可以发现,当高次项太少的时候(左图),模型会欠拟合;此时我们可以增加模型的复杂度,即为其添加适当的高次项(中图),此时模型拟合的效果比较好;但如果高次项太大(太多)时,模型过于复杂,此时会出现过拟合的现象。
当出现过拟合时,一般是由于高次项太多导致的,那么我们应该如何解决这个问题?
θ 0 + θ 1 x + θ 2 x 2 + θ 3 x 3 + θ 4 x 4 我们应该尽量减少高次项特征的影响 \theta_0 + \theta_1x + \theta_2x^2 + \underset{我们应该尽量减少高次项特征的影响}{\theta_3x^3 + \theta_4x^4} θ0+θ1x+θ2x2+我们应该尽量减少高次项特征的影响θ3x3+θ4x4
其中, θ i \theta_i θi 是模型的参数。
在学习的时候,数据提供的特征有些影响模型复杂度或者这个特征的数据点异常较多,所以算法在学习的时候尽量减少这个特征的影响(甚至删除某个特征的影响),这就是正则化。
注意:调整时候,算法并不知道某个特征影响,而是去调整模型参数得出优化的结果,因此正则项是对模型进行调整!
小结:
维灾难就是维度灾难。在机器学习中,维度灾难通常指的是随着特征数量的增多,计算量会变得很大。这个现象涉及到数字分析、抽样、组合、机器学习、数据挖掘和数据库等领域。在机器学习中,维度灾难通常指的是在涉及到向量的计算的问题中,随着维数的增加,计算量呈指数倍增长的一种现象。
简单来说,维度灾难就是指随着维度(特征数量)的增加,分类器性能逐步上升,到达某点之后,其性能便逐渐下降。
从上图可以看到,随着维度(特征数量)的增加,分类器的性能不再上升,反而下降。
有一系列的图片,每张图片的内容可能是猫也可能是狗。我们需要构造一个分类器能够对猫、狗自动的分类。首先,要寻找到一些能够描述猫和狗的特征,这样我们的分类算法就可以利用这些特征去识别物体。猫和狗的皮毛颜色可能是一个很好的特征,考虑到红绿蓝构成图像的三基色,因此用图片三基色各自的平均值称得上方便直观。这样就有了一个简单的Fisher分类器:
if 0.5 * red + 0.3 * green + 0.2 * blue > 0.6:
return cat
else:
return dog
【拓展】Fisher 分类器也叫 Fisher 线性判别(Fisher Linear Discriminant,FLD),或称为线性判别分析(Linear Discriminant Analysis,LDA)。它是一种线性分类器,用于将数据投影到一条直线上,以便将数据分成两个类别。
Fisher分类器的公式是:
if w1 * x1 + w2 * x2 + ... + wn * xn + b > 0:
return cls_1
else:
return cls_2
其中,w1, w2, …, wn
是权重,x1, x2, …, xn
是特征,b
是偏置,cls_1
是类别 1,cls_2
是类别 2。这个公式中的权重和偏置是通过训练数据集得到的。
使用颜色特征可能无法得到一个足够准确的分类器,如果是这样的话,我们不妨加入一些诸如图像纹理(图像灰度值在其 X X X、 Y Y Y 方向的导数 d x dx dx、 d y dy dy)。这样就有 5 个特征(Red
、Blue
、Green
、dx
、dy
)来设计我们的分类器。
也许 5 个特征的分类器的准确率依然无法满足要求,根据思维惯性,我们可能会再加入更多的特征,比如颜色、纹理的统计信息等等。如此下去,我们可能会得到上百个特征。那是不是我们的分类器性能会随着特征数量的增加而逐步提高呢?答案也许有些让人沮丧。事实上,当特征数量达到一定规模后,分类器的性能是在下降的。
总结:维度灾难就是随着维度(特征数量)的增加,分类器的性能却下降了。
我们假设猫和狗图片的数量是有限的(样本数量总是有限的)。假设有 10 张图片,接下来我们就用这仅有的 10 张图片来训练我们的分类器。
增加一个特征,比如绿色,这样特征维数扩展到了 2 维,如下图所示。
增加一个特征后,我们依然无法找到一条简单的直线将它们有效分类。
再增加一个特征,比如蓝色,扩展到 3 维特征空间,如下图所示。
在 3 维特征空间中,我们很容易找到一个分类平面,能够在训练集上有效的将猫和狗进行分类。如下图所示。
乍一看,在高维空间中,我们似乎能得到更优的分类器性能。
从 1 维到 3 维,给我们的感觉是:维数越高,分类性能越优。然而,维数过高将导致一定的问题:
高维空间训练形成的线性分类器,相当于在低维空间的一个复杂的非线性分类器。这种分类器过多的强调了训练集的准确率甚至于对一些错误/异常的数据也进行了学习,而正确的数据却无法覆盖整个特征空间。为此,这样得到的分类器在对新数据进行预测时将会出现错误。这种现象称之为过拟合,同时也是维度灾难的直接体现。
简单的线性分类器在训练数据上的表现不如非线性分类器,但由于线性分类器的学习过程中对噪声没有对非线性分类器敏感,因此对新数据具备更优的泛化能力。换句话说,通过使用更少的特征,避免了维数灾难的发生(也即避免了高维情况下的过拟合)。
Q:为什么高维会带来数据稀疏性问题?
A:假设有一个特征,它的取值范围 D D D 在 0 到 1 之间均匀分布,并且对狗和猫来说其值都是唯一的,我们现在利用这个特征来设计分类器。如果我们的训练数据覆盖了取值范围的 20%(例如 0 到 0.2),那么所使用的训练数据就占总样本量的 20%。
而上升到二维情况下,覆盖二维特征空间 20% 的面积,则需要在每个维度上取得 45% 的取值范围;在三维情况下,要覆盖特征空间 20% 的体积,则需要在每个维度上取得 58% 的取值范围,以此类推…
综上所述,在维度接近一定程度时,要取得同样的训练样本数量,则几乎要在每个维度上取得接近 100% 的取值范围,或者增加总样本数量,但样本数量也总是有限的。
如果一直增加特征维数,由于样本分布越来越稀疏,如果要避免过拟合的出现,就不得不持续增加样本数量。
数据在高维空间的中心比在边缘区域具备更大的稀疏性,数据更倾向于分布在空间的边缘区域。如下图所示。
不属于单位圆的训练样本比搜索空间的中心更接近搜索空间的角点。这些样本很难分类,因为它们的特征值差别很大(例如,单位正方形的对角的样本)。
一个有趣的问题是,当我们增加特征空间的维度时,圆〈超球面)的体积相对于正方形(超立方体)的体积发生怎样变化?
尺寸 d d d 的单位超立方体的体积总是 1 d = 1 1^d=1 1d=1。尺寸 d d d 和半径 0.5 0.5 0.5 的内切超球体的体积可以计算为:
V ( d ) = π d 2 Γ ( d 2 + 1 ) 0. 5 d V(d) = \frac{\pi^{\frac{d}{2}}}{\Gamma(\frac{d}{2} + 1)} 0.5^d V(d)=Γ(2d+1)π2d0.5d
在高维空间中,大多数训练数据驻留在定义特征空间的超立方体的角落中。如前所述,特征空间角落中的实例比围绕超球体质心的实例更难分类。
一个 8 维超立方体有 2 8 = 256 2^8 = 256 28=256 个角
事实证明,许多事物在高维空间中表现得非常不同。例如,如果你选择一个单位平方( 1 × 1 1\times 1 1×1平方)的随机点,它将只有大约 0.4 % 0.4\% 0.4% 的机会位于小于 0.001 0.001 0.001 的边界(换句话说,随机点将沿任何维度“极端”是非常不可能的)。但是在一个 10000 10000 10000 维单位超立方体( 1 × 1 × 1 1\times 1 \times 1 1×1×1 立方体,有一万个 1 1 1)中,这个概率大于 99.999999 % 99.999999\% 99.999999%。高维超立方体中的大部分点都非常靠近边界。更难区分的是:如果你在一个单位正方形中随机抽取两个点,这两个点之间的距离平均约为 0.52 0.52 0.52。如果在单位三维立方体中选取两个随机点,则平均距离将大致为 0.66 0.66 0.66,但是在一个100万维的超立方体中随机抽取两点呢?那么平均距离将是大约 408.25 408.25 408.25 (大约是 1 , 000 , 000 6 \frac{1,000,000}{6} 61,000,000)!
非常违反直觉:当两个点位于相同的单位超立方体内时,两点如何分离?这个事实意味着高维数据集有可能非常稀疏:大多数训练实例可能彼此远离。当然,这也意味着一个新实例可能离任何训练实例都很远,这使得预测的可信度表现得比在低维度数据中要来的差。训练集的维度越多,过度拟合的风险就越大。
理论上讲,维度灾难的一个解决方案可能是增加训练集的大小以达到足够密度的训练实例。 不幸的是,在实践中,达到给定密度所需的训练实例的数量随着维度的数量呈指数增长。 如果只有100个特征(比MNIST问题少得多),那么为了使训练实例的平均值在 0.1 以内,需要比可观察宇宙中的原子更多的训练实例(假设它们在所有维度上均匀分布)。
对于8维超立方体,大约 98 % 98\% 98% 的数据集中在其 256 个角上。当特征空间的维度达到无穷大时,从采样点到质心的 最小和最大的 欧几里得距离之差 与 最小距离本身之比 趋于零:
lim d → ∞ d i s t m a x − d i s t m i n d i s t m i n → 0 \lim_{d \to \infty} \frac{\mathrm{dist_{max}} - \mathrm{dist_{min}}}{\mathrm{dist}_{min}} \to 0 d→∞limdistmindistmax−distmin→0
距离测量开始失去其在高维空间中测量的有效性。由于分类器取决于这些距离测量,因此在较低维空间中分类通常更容易,其中较少特征用于描述感兴趣对象。
如果理论无限数量的训练样本可用,则维度的诅咒不适用,可以简单地使用无数个特征来获得完美的分类。训练数据越小,应使用的功能就越少。如果 N N N 个训练样本足以覆盖单位区间大小的 1 D 1D 1D 特征空间,则需要 N 2 N^2 N2 个样本来覆盖具有相同密度的 2 D 2D 2D 特征空间,并且在 3 D 3D 3D 特征空间中需要 N 3 N^3 N3 个样本。换句话说,所需的训练实例数量随着使用的维度数量呈指数增长。
学习目标:
主要学习内容:
岭回归(Ridge Regression)是一种用于处理多重共线性问题的线性回归模型。在普通最小二乘法(OLS)中,如果存在多重共线性,那么估计的系数会非常大,导致模型过拟合。岭回归通过在目标函数中加入一个 L 2 L_2 L2 正则化项,使得估计的系数更加稳定。
简单来说,岭回归是线性回归的正则化版本,即在原来的线性回归的损失函数中添加正则项(Regularization Term) α ∑ i = 1 n θ i 2 \alpha \sum_{i=1}^n \theta^2_i α∑i=1nθi2 以达到在拟合数据的同时,使模型权重尽可能小的目的。
岭回归的代价函数(损失函数):
J ( θ ) = M S E ( θ ) 线性回归常用的损失函数 + α ∑ i = 1 n θ i 2 L 2 正则项 J(\theta) = \underset{线性回归常用的损失函数}{ \mathrm{MSE}(\theta)} + \underset{L_2 正则项}{\alpha \sum_{i=1}^n \theta^2_i} J(θ)=线性回归常用的损失函数MSE(θ)+L2正则项αi=1∑nθi2
即:
J ( θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 ‾ 线性回归常用的损失函数 + α ∑ i = 1 n θ i 2 ‾ L 2 正则项 J(\theta) = \underset{线性回归常用的损失函数}{\underline{\frac{1}{m}\sum_{i = 1}^m(\theta^T \cdot x^{(i)} - y^{(i)})^2}} + \underset{L_2 正则项}{\underline{\alpha \sum_{i=1}^n \theta^2_i}} J(θ)=线性回归常用的损失函数m1i=1∑m(θT⋅x(i)−y(i))2+L2正则项αi=1∑nθi2
其中:
M S E ( θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 \mathrm{MSE}(\theta) = \frac{1}{m}\sum_{i = 1}^m(\theta^T \cdot x^{(i)} - y^{(i)})^2 MSE(θ)=m1i=1∑m(θT⋅x(i)−y(i))2
其中 m m m 是样本数量。
注意:当 α = 0 \alpha = 0 α=0 时,岭回归退化为线性回归。
Lasso Regression(Lasso回归)是一种线性回归模型,它在原来的线性回归的损失函数中添加正则项(Regularization Term) α ∑ i = 1 n ∣ θ i ∣ \alpha \sum_{i=1}^n |\theta_i| α∑i=1n∣θi∣ 以达到在拟合数据的同时,使模型权重尽可能小的目的。与岭回归不同,Lasso回归使用的是 L 1 L_1 L1 正则化项。
Lasso回归的代价函数(损失函数):
J ( θ ) = M S E ( θ ) + α ∑ i = 1 n ∣ θ i ∣ J(\theta) = \mathrm{MSE}(\theta) + \alpha \sum_{i=1}^n |\theta_i| J(θ)=MSE(θ)+αi=1∑n∣θi∣
即:
J ( θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 + α ∑ i = 1 n ∣ θ i ∣ J(\theta) = \frac{1}{m}\sum_{i = 1}^m(\theta^T \cdot x^{(i)} - y^{(i)})^2 + \alpha \sum_{i=1}^n |\theta_i| J(θ)=m1i=1∑m(θT⋅x(i)−y(i))2+αi=1∑n∣θi∣
其中:
M S E ( θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 \mathrm{MSE}(\theta) = \frac{1}{m}\sum_{i = 1}^m(\theta^T \cdot x^{(i)} - y^{(i)})^2 MSE(θ)=m1i=1∑m(θT⋅x(i)−y(i))2
其中 m m m 是样本数量。
注意:
g ( θ , J ) = Δ θ M S E ( θ ) + α ( s i g n ( θ 1 ) s i g n ( θ 2 ) . . . s i g n ( θ n ) ) g(\theta, J) = \Delta_\theta \mathrm{MSE}(\theta) + \alpha \begin{pmatrix} \mathrm{sign}(\theta_1) \\ \mathrm{sign}(\theta_2) \\ ... \\ \mathrm{sign}(\theta_n) \\ \end{pmatrix} g(θ,J)=ΔθMSE(θ)+α sign(θ1)sign(θ2)...sign(θn)
其中 Δ θ M S E ( θ ) \Delta_\theta \mathrm{MSE}(\theta) ΔθMSE(θ) 是均方误差损失函数 M S E ( θ ) \mathrm{MSE}(\theta) MSE(θ) 对模型参数 θ \theta θ 的梯度, s i g n ( θ i ) \mathrm{sign}(\theta_i) sign(θi) 是 θ i \theta_i θi 的符号函数,具体为:
s i g n ( θ i ) = { − 1 θ i < 0 0 θ i x = 0 1 θ i > 0 \mathrm{sign}(\theta_i) = \begin{cases} -1 & \theta_i < 0 \\ 0 & \theta_ix = 0 \\ 1 & \theta_i > 0 \end{cases} sign(θi)=⎩ ⎨ ⎧−101θi<0θix=0θi>0
Lasso Regression 有一个很重要的性质是:倾向于完全消除不重要的权重。例如,当 α \alpha α 取值相对较大时,高阶多项式退化为二次甚至是线性(因为高阶多项式特征的权重被置为 0)。
也就是说,Lasso Regression 能够自动进行特征选择,并输出一个稀疏模型(只有少数特征的权重是非零的,大部分被压缩为 0)。
总结:岭回归(Ridge Regression)和 Lasso 回归(Lasso Regression)都是线性回归的变体,它们通过在损失函数中添加正则化项来防止过拟合。岭回归通过在损失函数中添加 L2 正则化项来实现,而 Lasso 回归则通过在损失函数中添加 L1 正则化项来实现。这两种正则化方法都可以缩小模型参数的值,但它们之间也有一些区别:
弹性网络(Elastic Net)是一种使用 L 1 L_1 L1, L 2 L_2 L2 范数作为先验正则项训练的线性回归模型。这种组合允许拟合到一个只有少量参数是非零稀疏的模型,就像 Lasso 一样,但是它仍然保持了一些类似于 Ridge 的正则性质。我们可以利用 混合比(mix ratio) r r r 参数控制 L 1 L_1 L1 和 L 2 L_2 L2 的凸组合。
Elastic Net 与 Lasso 和 Ridge 回归的区别在于,它的代价函数是 L 1 L_1 L1 和 L 2 L_2 L2 的线性组合,而不是单独使用 L 1 L_1 L1 或 L 2 L_2 L2。这使得 Elastic Net 可以同时获得 Lasso 和 Ridge 的优点,即可以产生稀疏模型,同时可以保持模型的正则化属性。
弹性网络的代价函数:
J ( θ ) = M S E ( θ ) + r ⋅ α ∑ i = 1 n ∣ θ i ∣ + 1 − r 2 α ∑ i = 1 n θ i 2 J(\theta) = \mathrm{MSE}(\theta) + r \cdot \alpha \sum_{i=1}^n |\theta_i| + \frac{1 - r}{2} \alpha \sum_{i = 1}^n\theta^2_i J(θ)=MSE(θ)+r⋅αi=1∑n∣θi∣+21−rαi=1∑nθi2
Q:一般来说,我们应避免使用朴素线性回归,而应对模型进行一定的正则化处理,那如何选择正则化方法呢?
A:正则化方法有 L 1 L_1 L1 正则化和 L 2 L_2 L2 正则化。 L 1 L_1 L1 正则化可以产生稀疏模型,而 L 2 L_2 L2 正则化可以防止过拟合。在选择正则化方法的时候,我们可以从数据集大小、特征数量、特征相关性和需要保留的特征等方面入手,选择合适的正则化方法。
【补充】稀疏矩阵是指在数值分析中,其元素大部分为零的矩阵。反之,如果大部分元素都非零,则这个矩阵是稠密的。
小结:
from sklearn.linear_model import Ridge, ElasticNet, Lasso
Early Stopping 也是正则化迭代学习的方法之一。Early Stopping 是一种防止过拟合的方法,它通过在训练过程中监控验证集的误差,当验证集误差连续若干次迭代没有下降时,就停止训练。这样可以避免模型在训练集上过拟合,提高模型的泛化能力。
在验证错误率达到最小值的时候停止训练,防止出现过拟合!
小结:
学习目标:
sklearn.linear_model.Ridge(alpha=1.0, *, fit_intercept=True, copy_X=True,
max_iter=None, tol=0.0001, solver='auto',
positive=False, random_state=None)
sklearn.linear_model.Ridge
是一个线性回归模型,它使用线性最小二乘函数作为损失函数,正则化由 L 2 L_2 L2 范数给出。也称为岭回归或Tikhonov正则化。这个估计器内置了对多元回归的支持(即,当 y y y 是形状为(n_samples,n_targets)
的 2d 数组时)。alpha
:正则化力度,必须是正数。默认值为 1.0。
alpha
值。例如,可以尝试不同的 alpha
值,并使用交叉验证来评估每个 alpha
值的性能。最终选择性能最好的 alpha
值。需要注意的是,最佳的 alpha
值可能会因数据集而异。因此,您应该针对每个数据集都进行实验来确定最佳的 alpha
值。
fit_intercept
:是否计算截距。默认值为True
。solver
:求解器。可以是{‘auto’,‘svd’,‘cholesky’,‘lsqr’,‘sparse_cg’,‘sag’,‘saga’}
中的一个。默认值为“auto”
。normalize
:是否对数据进行归一化。默认值为False
。normalize
:数据是否进行标准化
normalize=False
:可以在fit
之前调用preprocessing.StandardScaler
标准化数据Ridge.coef_
:回归权重Ridge.intercept_
:回归偏置Ridge
方法相当于SGDRegressor(penalty='l2', loss="squared_loss")
,只不过SGDRegressor
实现了一个普通的随机梯度下降学习,因此推荐使用Ridge
(实现了 SAG)。
sklearn.linear_model.RidgeCV(alphas=(0.1, 1.0, 10.0), *,
fit_intercept=True, scoring=None,
cv=None, gcv_mode=None,
store_cv_values=False,
alpha_per_target=False)
sklearn.linear_model.RidgeCV
是一个带有内置交叉验证的岭回归类。默认情况下,它执行高效的留一交叉验证。alphas
:要尝试的 alpha 值数组。正则化强度;必须为正浮点数。正则化改善了问题的条件并减少了估计值的方差。较大的值指定更强的正则化。Alpha 对应于其他线性模型(如 LogisticRegression 或 LinearSVC)中的 1 /(2C)。fit_intercept
:是否为此模型计算截距。如果设置为 false,则不会在计算中使用截距(即数据预计居中)。scoring
:字符串(请参阅模型评估文档)或带有签名 scorer(estimator,X,y)的评分器可调用对象/函数。如果为 None,则当 cv 为 ‘auto’ 或 None 时为负均方误差(即使用留一交叉验证),否则为 r2 分数。cv
:确定交叉验证拆分策略的整数、交叉验证生成器或可迭代对象。cv 的可能输入是:None,使用高效的留一交叉验证;整数,指定折叠次数;CV 分割器;一个可迭代对象,产生(train,test)拆分作为索引数组。gcv_mode
:执行留一交叉验证时使用哪种策略的标志。选项是:‘auto’:如果 n_samples > n_features,则使用 ‘svd’,否则使用 ‘eigen’;‘svd’:当 X 为密集时强制使用 X 的奇异值分解,当 X 为稀疏时使用 X^T.X 的特征值分解;‘eigen’:强制通过 X.X^T 的特征分解计算。store_cv_values
:是否存储与每个 alpha 对应的交叉验证值(仅当 store_cv_values=True 和 cv=None 时可用)。alpha_per_target
:是否针对每个目标单独优化 alpha 值(从 alphas 参数列表中选择)(用于多输出设置:多个预测目标)。当设置为 True 时,在拟合后,alpha_ 属性将包含每个目标的一个值。当设置为 False 时,所有目标都使用一个 alpha。cv_values_
:每个 alpha 的交叉验证值(仅当 store_cv_values=True 和 cv=None 时可用)。在调用 fit() 后,如果 scoring 为 None,则此属性将包含均方误差,否则将包含标准化的每点预测值。coef_
:模型回归系数在sklearn中,CV代表的是交叉验证(Cross-validation)
class _BaseRidgeCV(LinearModel):
def __init__(self, alphas=(0.1, 1.0, 10.0),
fit_intercept=True, normalize=False, scoring=None,
cv=None, gcv_mode=None,
store_cv_values=False):
正则化是一种用来防止过拟合的技术,它通过在损失函数中添加一个正则项来实现。
Q1:这里的权重系数是什么?
A1:权重系数是指在回归模型中,自变量对因变量的影响程度。在线性回归模型中,每个自变量都有一个对应的权重系数,它表示该自变量每变化一个单位时,因变量的变化量。权重系数的绝对值越大,说明该自变量对因变量的影响越大;权重系数的绝对值越小,说明该自变量对因变量的影响越小。
Q2:意思是说,权重系数对模型参数起作用,对吗?
A2:权重系数本身就是模型的参数之一。在回归模型中,权重系数决定了自变量对因变量的影响程度。模型通过拟合训练数据来确定最优的权重系数,以便在新数据上做出准确的预测。
Q3:如果我们的模型是 y = a x + b y = ax + b y=ax+b,那么权重系数就是 a a a,这样理解是否正确?
A3:是的,理解是正确的。在这个简单的一元线性回归模型中, y = a x + b y = ax + b y=ax+b,其中 a a a 是权重系数,它表示自变量 x x x 对因变量 y y y 的影响程度。 b b b 是截距项,表示当 x = 0 x=0 x=0 时, y y y 的值。
Q3:正则项的目的是让模型变得简单,从而防止过拟合。那么正则项是如何影响模型的?
A3:正则项通过对模型的复杂度进行惩罚来防止过拟合。在回归模型中,正则项通常是模型权重系数的函数,它对损失函数施加了一个额外的惩罚项。这个惩罚项会鼓励模型选择较小的权重系数,从而使模型变得更简单。
例如,在岭回归中,正则项是权重系数的平方和,即 ∣ ∣ w ∣ ∣ 2 2 ||w||_2^2 ∣∣w∣∣22。在损失函数中添加这个正则项后,模型在拟合数据时不仅要最小化预测误差,还要最小化权重系数的平方和。这会鼓励模型选择较小的权重系数,从而防止过拟合。
总之,正则项通过对模型复杂度进行惩罚来影响模型,使其在拟合数据时更倾向于选择简单的模型。
简而言之,正则化力度越大,模型的权重系数就会越小;正则化力度越小,模型的权重系数就会越大。二者呈负相关。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge, RidgeCV
from sklearn.metrics import mean_squared_error
# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=2)
# 3. 特征工程:标准化
transformer = StandardScaler()
x_train = transformer.fit_transform(x_train)
x_test = transformer.transform(x_test)
# 4. 机器学习:线性回归(岭回归)
## 4.1 训练模型一:Ridge
estimator_1 = Ridge(alpha=1) # alpha为正则化力度系数
## 4.2 训练模型二:RidgeCV
estimator_2 = RidgeCV(alphas=(0.001, 0.01, 0.1, 1, 10, 100))
## 4.3 训练
estimator_1.fit(X=x_train, y=y_train)
estimator_2.fit(X=x_train, y=y_train)
# 5. 模型评估
## 5.1 模型一
### 5.1.1 获取系数
y_predict_1 = estimator_1.predict(X=x_test)
print(f"【模型一】预测值为:\r\n{y_predict_1}")
print(f"【模型一】模型系数为:\r\n{estimator_1.coef_}")
print(f"【模型一】模型偏置为:{estimator_1.intercept_:.4f}")
### 5.1.2 误差
error = mean_squared_error(y_true=y_test, y_pred=y_predict_1)
print(f"【模型一】MSE误差为:{error:.4f}")
print("-" * 60)
## 5.2 模型二
### 5.2.1 获取系数
y_predict_2 = estimator_2.predict(X=x_test)
print(f"【模型二】预测值为:\r\n{y_predict_2}")
print(f"【模型二】模型系数为:\r\n{estimator_2.coef_}")
print(f"【模型二】模型偏置为:{estimator_2.intercept_:.4f}")
print(f"【模型二】最佳的正则化系数alpha为:{estimator_2.alpha_:.4f}")
### 5.2.2 误差
error = mean_squared_error(y_true=y_test, y_pred=y_predict_2)
print(f"【模型二】MSE误差为:{error:.4f}")
结果如下:
【模型一】预测值为:
[23.00321192 21.25113553 33.65297089 31.50768508 3.18019202 3.20859452
27.39328707 22.31852908 14.84031324 21.38031657 30.94419376 26.68037451
21.16191092 18.3583368 17.64010682 25.37674358 24.41841787 13.36964416
8.69151624 18.57429154 21.76007313 20.3675708 36.49898893 20.58768589
19.86869924 15.78758962 37.0808577 34.85810511 30.80842492 23.26457302
18.63699725 20.76315643 31.8362047 30.17662894 13.37516806 15.91128616
13.69148601 23.76213812 25.94122928 23.19082212 28.96707402 12.53531219
31.0915657 6.38966225 23.67132167 20.59182691 33.17852031 19.20948493
35.8391772 0.80868731 31.879121 31.6679153 6.613803 34.57229934
20.35801631 19.7289094 19.53829966 18.60477001 15.84940053 22.99202734
19.64706215 16.43650149 18.50058175 32.68151938 35.41814173 24.61082106
41.49411556 32.92820471 14.58658382 27.44908603 8.10931001 5.6237763
22.21994089 18.6974834 31.01693504 26.05175952 24.57434357 24.80354473
25.36387139 24.88056688 33.67473368 19.75362793 20.58773597 27.83549398
37.96359583 37.19356092 22.12900329 29.58354466 31.07063353 17.92066509
20.90886029 19.50941817 18.60493829 37.06839255 39.76870941 9.18775068
35.28224415 30.32913865 21.09441681 13.6894351 31.3705665 24.99205169]
【模型一】模型系数为:
[-0.9258041 0.83936747 -0.12711514 0.82009798 -1.87594228 2.55674364
0.25022498 -2.89549837 2.72413708 -1.88559783 -2.1463606 1.08954177
-3.90282358]
【模型一】模型偏置为:22.4413
【模型一】MSE误差为:18.4768
------------------------------------------------------------
【模型二】预测值为:
[22.91044323 21.53397244 33.19377343 31.06746567 3.19833094 3.64662565
27.33264419 23.08195399 14.63749317 21.66957917 30.79673326 26.55735187
21.41724229 18.25384215 17.61886847 25.34374455 24.33876063 13.3872763
8.8999307 18.57336765 21.94975226 20.52205235 36.12025376 20.5267392
19.78329343 16.01724622 36.77248739 34.81735983 30.65497556 23.48115344
18.29495225 20.90247154 31.74835767 29.97070314 13.30015301 16.22199615
13.58672632 23.90186731 25.84866501 23.22507831 28.73110723 12.7943185
31.10888785 6.38366882 23.32005389 20.41410955 33.31082934 19.14499154
35.3896122 0.74105634 31.66709313 31.44317444 6.82219299 34.16770243
19.94988928 20.00244902 19.60787192 18.71879978 16.12643697 23.03318714
19.60186563 17.0073295 18.61116798 32.06529326 34.88048293 24.77187058
40.81451276 32.75890454 14.42647949 27.56015648 8.62520673 5.74344586
22.25542399 18.47166596 30.92118035 26.08663192 24.34857104 24.52347389
25.18295893 24.9020731 33.37919129 19.92988059 20.49440597 27.90786229
37.62322949 36.79842319 21.82514811 29.31195281 31.03475093 17.8315383
21.15022899 19.71920991 18.53589778 36.60922738 39.37790169 9.25932086
35.11202025 30.59794396 21.20492907 13.94921244 31.23156595 24.99632208]
【模型二】模型系数为:
[-0.86329633 0.7285083 -0.27135102 0.85108307 -1.63780795 2.6270911
0.18222203 -2.64613645 2.17038535 -1.42056563 -2.05032997 1.07266175
-3.76668388]
【模型二】模型偏置为:22.4413
【模型二】最佳的正则化系数alpha为:10.0000
【模型二】MSE误差为:18.3816
小结:
sklearn.linear_model.Ridge(alpha=1.0, fit_intercept=True, solver="auto", normalize=False)
【知道】。
alpha
:正则化力度,没有固定值,需要验证得到最佳的alpha
normalize=False
:默认为False
,对数据进行标准化处理(如果前面我们手动标准化处理了,那么就设置为False
)Q:如果我们在前面使用 Standard 对数据进行了标准化,那么在定义模型时,仍然将 normalize
设置为 True
,对结果有影响吗?
A:如果在前面使用 StandardScaler
对数据进行了标准化,那么在定义 Ridge
模型时,将 normalize
参数设置为 True
会对结果产生影响。
normalize
参数用于控制是否对数据进行归一化处理。如果将其设置为 True
,则在拟合模型之前,会对数据进行归一化处理,使每个特征的均值为 0,方差为 1。
如果在前面已经使用 StandardScaler
对数据进行了标准化处理,那么再次将 normalize
参数设置为 True
会导致数据被归一化两次。这可能会影响模型的性能。
因此,如果在前面已经对数据进行了标准化处理,那么在定义 Ridge
模型时,应该将 normalize
参数设置为 False
。
不要对数据进行多次标准化!
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, SGDRegressor, Ridge, RidgeCV
from sklearn.metrics import mean_squared_error
def model_estimation(estimator, x_test, y_test, model_name):
"""模型评估函数
Args:
estimator (sklearn.linear_model): 模型
x_test (_type_): 测试集
y_test (_type_): 测试集Ground Truth
model_name (str): 模型名称
Returns:
float: 模型误差
"""
# 获取系数
y_predict = estimator.predict(X=x_test)
print(f"[{model_name}] 模型系数为:\r\n{estimator.coef_}")
print(f"[{model_name}] 模型偏置为:{int(estimator.intercept_):.4f}")
# 误差
error = mean_squared_error(y_true=y_test, y_pred=y_predict)
print(f"[{model_name}] MSE 误差为:{error:.4f}")
print("-" * 60)
return error
if __name__ == "__main__":
# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=2, shuffle=True)
# 3. 特征工程:标准化
transformer = StandardScaler()
x_train = transformer.fit_transform(x_train)
x_test = transformer.transform(x_test)
# 4. 定义模型
## 4.1 模型一:线性回归(正规方程)
estimator_1_linear_regression = LinearRegression()
model_name_1 = "线性回归(正规方程)"
## 4.2 模型二:线性回归:随机梯度下降
estimator_2_sgd_regression = SGDRegressor(max_iter=100000)
model_name_2 = "线性回归:随机梯度下降"
## 4.3 模型二:线性回归:岭回归
estimator_3_ridge_regression = Ridge(alpha=10.0) # alpha为正则化力度系数
model_name_3 = "线性回归:岭回归"
# 5. 模型训练
estimator_1_linear_regression.fit(X=x_train, y=y_train)
estimator_2_sgd_regression.fit(X=x_train, y=y_train)
estimator_3_ridge_regression.fit(X=x_train, y=y_train)
# 6. 模型评估
error_1_linear_regression = model_estimation(estimator_1_linear_regression,
x_test=x_test, y_test=y_test,
model_name=model_name_1)
error_2_sgd_regression = model_estimation(estimator_2_sgd_regression,
x_test=x_test, y_test=y_test,
model_name=model_name_2)
error_3_ridge_regression = model_estimation(estimator_3_ridge_regression,
x_test=x_test, y_test=y_test,
model_name=model_name_3)
# 7. 不同模型误差对比
print("=" * 60)
print(f"{[model_name_1]} MSE 误差为:{error_1_linear_regression:.4f}")
print(f"{[model_name_2]} MSE 误差为:{error_2_sgd_regression:.4f}")
print(f"{[model_name_3]} MSE 误差为:{error_3_ridge_regression:.4f}")
运行结果:
[线性回归(正规方程)] 模型系数为:
[-0.93451207 0.85487686 -0.10446819 0.81541757 -1.90731862 2.54650028
0.25941464 -2.92654009 2.80505451 -1.95699832 -2.15881929 1.09153332
-3.91941941]
[线性回归(正规方程)] 模型偏置为:22.0000
[线性回归(正规方程)] MSE 误差为:18.4954
------------------------------------------------------------
[线性回归:随机梯度下降] 模型系数为:
[-0.85500705 0.72828303 -0.37024578 0.82421742 -1.71129399 2.63393284
0.18303546 -2.77152414 2.0379826 -1.18318199 -2.09533483 1.09331864
-3.8657579 ]
[线性回归:随机梯度下降] 模型偏置为:22.0000
[线性回归:随机梯度下降] MSE 误差为:18.5166
------------------------------------------------------------
[线性回归:岭回归] 模型系数为:
[-0.86329633 0.7285083 -0.27135102 0.85108307 -1.63780795 2.6270911
0.18222203 -2.64613645 2.17038535 -1.42056563 -2.05032997 1.07266175
-3.76668388]
[线性回归:岭回归] 模型偏置为:22.0000
[线性回归:岭回归] MSE 误差为:18.3816
不同模型误差对比:
['线性回归(正规方程)'] MSE 误差为:18.4954
['线性回归:随机梯度下降'] MSE 误差为:18.5166
['线性回归:岭回归'] MSE 误差为:18.3816
根据结果,三种不同的线性回归算法在波士顿房价预测问题上的表现相差不大。它们的均方误差(MSE)都在18左右。
从这些结果来看,岭回归模型在这个问题上表现最好,其 MSE 最低。但是,这三种模型的 MSE 都很接近,因此它们在这个问题上的表现都差不多。
需要注意的是,这些结果可能会因数据集划分、模型参数和随机种子等因素而有所不同。要准确比较这些模型的性能,可以使用交叉验证等方法来评估它们在不同数据集划分下的平均性能。
Q1:LinearRegression 是正规方程吗?
A1:是的,LinearRegression
类是使用正规方程来求解线性回归问题的。正规方程是一种解析方法,可以直接计算出线性回归模型的最优参数。它不需要迭代,但在特征数量很大时计算量会很大。
Q2:意思就是说,线性回归默认使用的是正规方程,对吗?
A2:是的,在 scikit-learn 库中,LinearRegression
类默认使用正规方程来拟合线性回归模型。当然,您也可以使用其他方法来拟合线性回归模型,例如随机梯度下降(SGDRegressor
类)或岭回归(Ridge
类)。
Q3:LinearRegression, SGDRegressor, Ridge 都是线性回归,是吗?
A3:是的。LinearRegression
,SGDRegressor
和 Ridge
都是用来拟合线性回归模型的。它们的区别在于拟合模型时使用的算法不同。
LinearRegression
使用正规方程来拟合线性回归模型。SGDRegressor
使用随机梯度下降算法来拟合线性回归模型。Ridge
使用岭回归算法来拟合线性回归模型。岭回归是一种带有 L2 正则化的线性回归。这些算法都可以用来拟合线性回归模型,但在不同的数据集和应用场景下,它们的表现可能会有所不同。
学习目标:
joblib
是一个用于保存和加载 Python 对象的库。它可以用来保存 scikit-learn 模型,以便在以后重新加载并使用。
需要注意的是,sklearn.externals.joblib
已经在 scikit-learn 0.23 版本中被弃用,并且将在未来版本中被删除。建议直接从 joblib
库中导入 joblib
,而不是从 sklearn.externals
中导入。例如:
import joblib
joblib
库提供了两个主要函数:dump
和 load
。
dump
英[dʌmp
] 美[dʌmp
]
vt.
倾倒; 抛弃; (尤指在不合适的地方)丢弃; 扔掉; 丢下; 推卸; (向国外)倾销; 抛售; 随便堆放; 与(某人)结束恋爱关系; (内存信息)转储,转存;
n.
垃圾场; 废物堆; 尾矿堆; 脏地方; 邋遢场所; 令人讨厌的地方; 军需品临时存放处; 转储;
dump
函数用于将 Python 对象保存到磁盘文件中。它的语法为:joblib.dump(value, filename, compress=0, protocol=None, cache_size=None)
其中,value
是要保存的 Python 对象,filename
是保存文件的路径。其他参数可选,用于控制压缩级别、序列化协议和缓存大小等。
load
函数用于从磁盘文件中加载 Python 对象。它的语法为:joblib.load(filename, mmap_mode=None)
其中,filename
是要加载的文件的路径。其他参数可选,用于控制内存映射模式等。
这两个函数都没有返回值。它们分别用于保存和加载 Python 对象。
注意:与深度学习中一般只保存模型参数(或我们指定的内容)不同,joblib.dump
函数会将模型的所有内容保存到磁盘文件中。这包括模型的参数、超参数和其他属性。因此当我们使用 joblib.load
函数从文件中加载模型时,会得到一个与原始模型完全相同的模型对象。您可以使用这个对象进行预测、评估或进一步训练。
举例:
# 保存文件
joblib.dump(estimator, "model_name.pkl")
# 加载文件
estimator = joblib.load("model_name.pkl")
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
import joblib
# 1. 获取数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2]
# 2. 数据集划分
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=2)
# 3. 特征工程:标准化
transformer = StandardScaler()
x_train = transformer.fit_transform(x_train)
x_test = transformer.transform(x_test)
# 4. 机器学习:线性回归(岭回归)
## 4.1 定义模型
estimator = Ridge(alpha=1) # alpha为正则化力度系数
## 4.2 训练模型
estimator.fit(X=x_train, y=y_train)
# 5. 模型保存
joblib.dump(value=estimator, filename="./岭回归模型.pkl") # 模型的所有都会保存
print("模型已保存!")
# 6. 模型加载
# 这里我们为了方便演示,先将之前训练好的模型删除
del estimator
# 正式加载模型
estimator = joblib.load("./岭回归模型.pkl")
print("模型加载成功!")
# 7. 模型评估
## 7.1 获取系数
y_predict_1 = estimator.predict(X=x_test)
# print(f"预测值为:\r\n{y_predict_1}")
# print(f"模型系数为:\r\n{estimator.coef_}")
# print(f"模型偏置为:{estimator.intercept_:.4f}")
## 7.2 误差
error = mean_squared_error(y_true=y_test, y_pred=y_predict_1)
print(f"MSE误差为:{error:.4f}")
结果如下:
模型已保存!
模型加载成功!
MSE误差为:18.4768
与此同时,在脚本文件的目录下会生成一个模型文件 岭回归模型.pkl
:
小结:
import joblib
【知道】
joblib.dump(estimator, "model_path.pkl")
estimator = joblib.load("model_path.pkl")
。**.pkl