定义与公式
线性回归(Linear regression)是利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间关系进行建模的一种分析方式。
通用公式: h ( w ) = w 1 x 1 + w 2 x 2 + w 3 x 3 . . . + b = w T x + b h(w)=w_1x_1+w_2x_2+w_3x_3...+b=w^Tx+b h(w)=w1x1+w2x2+w3x3...+b=wTx+b
线性回归当中主要有两种模型,一种是线性关系,另一种是非线性关系。
1.损失
最小二乘法
2.优化
正规方程
梯度下降法
3.正规方程 -- 一蹴而就
利用矩阵的逆,转置进行一步求解
只是适合样本和特征比较少的情况
4.梯度下降法 -- 循序渐进
举例:
山 -- 可微分的函数
山底 -- 函数的最小值
梯度的概念
单变量 -- 切线
多变量 -- 向量
梯度下降法中关注的两个参数
α -- 就是步长
步长太小 -- 下山太慢
步长太大 -- 容易跳过极小值点
为什么梯度要加一个负号
梯度方向是上升最快方向,负号就是下降最快方向
5.梯度下降法和正规方程对比:
梯度下降 正规方程
需要选择学习率 不需要
需要迭代求解 一次运算得出
特征数量较大可以使用 需要计算方程,时间复杂度高O(n3)
6.选择:
小规模数据:
LinearRegression(不能解决拟合问题)
岭回归
大规模数据:
SGDRegressor
J ( θ ) = ( h w ( x 1 ) − y 1 ) 2 + ( h w ( x 2 ) − y 2 ) 2 + . . . + ( h w ( x n ) − y n ) 2 J(\theta)=(h_w(x_1)-y_1)^2+(h_w(x_2)-y_2)^2+...+(h_w(x_n)-y_n)^2 J(θ)=(hw(x1)−y1)2+(hw(x2)−y2)2+...+(hw(xn)−yn)2
= ∑ i = 1 n ( h w ( x i ) − y i ) 2 =\sum_{i=1}^{n}(h_w(x_i)-y_i)^2 =∑i=1n(hw(xi)−yi)2
如何去求模型当中的W,使得损失最小?(目的是找到最小损失对应的W值)
线性回归经常使用的两种优化算法
w = ( X T X ) − 1 X T y w=(X^TX)^{-1}X^Ty w=(XTX)−1XTy
理解:X为特征值矩阵,y为目标值矩阵。直接求到最好的结果
缺点:当特征过多过复杂时,求解速度太慢并且得不到结果
正规方程式的推导
J ( θ ) = ( h w ( x 1 ) − y 1 ) 2 + ( h w ( x 2 ) − y 2 ) 2 + . . . + ( h w ( x n ) − y n ) 2 J(\theta)=(h_w(x_1)-y_1)^2+(h_w(x_2)-y_2)^2+...+(h_w(x_n)-y_n)^2 J(θ)=(hw(x1)−y1)2+(hw(x2)−y2)2+...+(hw(xn)−yn)2
= ∑ i = 1 n ( h w ( x i ) − y i ) 2 =\sum_{i=1}^{n}(h_w(x_i)-y_i)^2 =∑i=1n(hw(xi)−yi)2
= ( X w − y ) 2 =(Xw-y)^2 =(Xw−y)2
其中y是真实值矩阵,X是特征值矩阵,w是权重矩阵
对其求解关于w的最小值,起止y,X 均已知二次函数直接求导,导数为零的位置,即为最小值。
求导:
注:式(1)到式(2)推导过程中, X是一个m行n列的矩阵,并不能保证其有逆矩阵,但是右乘XT把其变成一个方阵,保证其有逆矩阵。
式(5)到式(6)推导过程中,和上类似。
1 全梯度下降算法(FG)
在进行计算的时候,计算所有样本的误差平均值,作为我的目标函数
2 随机梯度下降算法(SG)
每次只选择一个样本进行考核
3 小批量梯度下降算法(mini-bantch)
选择一部分样本进行考核
4 随机平均梯度下降算法(SAG)
会给每个样本都维持一个平均值,后期计算的时候,参考这个平均值
假设有一个单变量的函数 :J(θ) = θ2
函数的微分:▽J(θ) = 2θ
初始化,起点为: θ0 = 1
学习率:α = 0.4
进行梯度下降的迭代计算过程:
经过四次的运算,也就是走了四步,基本就抵达了函数的最低点,也就是山底
假设有一个目标函数 : J ( θ ) = θ 1 2 + θ 2 2 J(θ) = θ_1^2 + θ_2^2 J(θ)=θ12+θ22
现在要通过梯度下降法计算这个函数的最小值。我们通过观察就能发现最小值其实就是 (0,0)点。但是接下 来,我们会从梯度下降算法开始一步步计算到这个最小值! 我们假设初始的起点为: θ 0 θ^0 θ0 = (1, 3)
初始的学习率为:α = 0.1
函数的梯度为:▽J(θ) =< 2θ1 ,2θ2>
进行多次迭代:
梯度下降公式
α在梯度下降算法中被称作为学习率或者步长
梯度前加一个负号,就意味着朝着梯度相反的方向前进
梯度下降 | 正规方程 |
---|---|
需要选择学习率 | 不需要 |
需要迭代求解 | 一次运算得出 |
特征数量较大可以使用 | 需要计算方程,时间复杂度高O(n3) |
选择:
小规模数据:
大规模数据:SGDRegressor
全梯度下降算法(Full gradient descent),
随机梯度下降算法(Stochastic gradient descent),
随机平均梯度下降算法(Stochastic average gradient descent)
小批量梯度下降算法(Mini-batch gradient descent)
全梯度下降算法(FG)
计算训练集所有样本误差,对其求和再取平均值作为目标函数。
权重向量沿其梯度相反的方向移动,从而使当前目标函数减少得最多。
因为在执行每次更新时,我们需要在整个数据集上计算所有的梯度,所以批梯度下降法的速度会很慢,同时,批梯度下降法无法处理超出内存容量限制的数据集。
批梯度下降法同样也不能在线更新模型,即在运行的过程中,不能增加新的样本。
其是在整个训练数据集上计算损失函数关于参数θ的梯度:
随机梯度下降算法(SG)
由于FG每迭代更新一次权重都需要计算所有样本误差,而实际问题中经常有上亿的训练样本,故效率偏低,且容易陷入局部最优解,因此提出了随机梯度下降算法。
其每轮计算的目标函数不再是全体样本误差,而仅是单个样本误差,即每次只代入计算一个样本目标函数的梯度来更新权重,再取下一个样本重复此过程,直到损失函数值停止下降或损失函数值小于某个可以容忍的阈值。
此过程简单,高效,通常可以较好地避免更新迭代收敛到局部最优解。其迭代形式为
每次只使用一个样本迭代,若遇上噪声则容易陷入局部最优解。
其中,x(i)表示一条训练样本的特征值,y(i)表示一条训练样本的标签值
但是由于,SG每次只使用一个样本迭代,若遇上噪声则容易陷入局部最优解。
小批量梯度下降算法(mini-bantch)
小批量梯度下降算法是FG和SG的折中方案,在一定程度上兼顾了以上两种方法的优点。
每次从训练样本集上随机抽取一个小样本集,在抽出来的小样本集上采用FG迭代更新权重。
被抽出的小样本集所含样本点的个数称为batch_size,通常设置为2的幂次方,更有利于GPU加速处理。
特别的,若batch_size=1,则变成了SG;若batch_size=n,则变成了FG.其迭代形式为
随机平均梯度下降算法(SAG)
在SG方法中,虽然避开了运算成本大的问题,但对于大数据训练而言,SG效果常不尽如人意,因为每一轮梯度更新都完全与上一轮的数据和梯度无关。
随机平均梯度算法克服了这个问题,在内存中为每一个样本都维护一个旧的梯度,随机选择第i个样本来更新此样本的梯度,其他样本的梯度保持不变,然后求得所有梯度的平均值,进而更新了参数。
如此,每一轮更新仅需计算一个样本的梯度,计算成本等同于SG,但收敛速度快得多。
四种梯度比较
(1**)FG方法由于它每轮更新都要使用全体数据集,故花费的时间成本最多,内存存储最大。**
(2)SAG在训练初期表现不佳,优化速度较慢。这是因为我们常将初始梯度设为0,而SAG每轮梯度更新都结合了上一轮梯度值。
(3)综合考虑迭代次数和运行时间,SG表现性能都很好,能在训练初期快速摆脱初始梯度值,快速将平均损失函数降到很低。但要注意,在使用SG方法时要慎重选择步长,否则容易错过最优解。
(4)mini-batch结合了SG的“胆大”和FG的“心细”,它的表现也正好居于SG和FG二者之间。在目前的机器学习领域,mini-batch是使用最多的梯度下降算法,正是因为它避开了FG运算效率低成本大和SG收敛效果不稳定的缺点。
模拟梯度下降法
import numpy as np
import matplotlib.pyplot as plt
plot_x = np.linspace(-1,6,141)
plot_y = (plot_x-2.5)**2-1
plt.plot(plot_x,plot_y)
plt.show()
#求导函数
def dJ(theta):
return 2*(theta-2.5)
def J(theta):
return (theta-2.5)**2-1
eta=0.1 #学习率
epsilon=1e-8
theta=0
theta_history=[theta]
while True:
gradient=dJ(theta)
last_theta=theta
theta=theta-eta*gradient
theta_history.append(theta)
if abs(J(theta)-J(last_theta))<epsilon :
break
print('最小值点的x坐标:',theta)
print('最小值点的y坐标:',J(theta))
plt.plot(plot_x,J(plot_x))
plt.plot(np.array(theta_history),J(np.array(theta_history)),color='r',marker='+')
class sklearn.linear_model.LinearRegression(*, fit_intercept=True,normalize='deprecated', copy_X=True, n_jobs=None, positive=False)
class sklearn.linear_model.SGDRegressor(loss='squared_error', *, penalty='l2',alpha=0.0001, l1_ratio=0.15, fit_intercept=True, max_iter=1000, tol=0.001, shuffle=True, verbose=0, epsilon=0.1, random_state=None, learning_rate='invscaling', eta0=0.01, power_t=0.25, early_stopping=False, validation_fraction=0.1, n_iter_no_change=5, warm_start=False, average=False)
SGD的优点是:
高效
容易实现(有许多机会进行代码调优)
SGD的缺点是:
SGD需要许多超参数:比如正则项参数、迭代数。
SGD对于特征归一化(feature scaling)是敏感的。
from sklearn.linear_model import LinearRegression
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]
# 实例化API
estimator = LinearRegression()
# 使用fit方法进行训练
estimator.fit(x,y)
print(estimator.coef_)
print(estimator.predict([[100, 80]]))
线性回归模型评估
通过几个参数验证回归模型
SSE(和方差、误差平方和):The sum of squares due to error
MSE(均方差、方差):Mean squared error 1 m ∑ i = 1 m ( y i − y ^ i ) 2 \frac{1}{m} \displaystyle \sum_{i=1}^{m}(y_i-\hat y_i)^2 m1i=1∑m(yi−y^i)2
RMSE(均方根、标准差):Root mean squared error 1 m ∑ i = 1 m ( y i − y ^ i ) 2 = M S E \sqrt{\frac{1}{m} \displaystyle \sum_{i=1}^{m}(y_i-\hat y_i)^2}=\sqrt{MSE} m1i=1∑m(yi−y^i)2=MSE
MAE(平均绝对误差) 1 m ∑ i = 1 m ∣ y i − y ^ i ∣ \frac{1}{m} \displaystyle \sum_{i=1}^{m} \lvert y_i-\hat y_i \rvert m1i=1∑m∣yi−y^i∣
R-square(确定系数) Coefficient of determination
公式变形
其实“确定系数”是通过数据的变化来表征一个拟合的好坏。“确定系数”的正常取值范围为[0,1],越接近1,表明方程的变量对y的解释能力越强,这个模型对数据拟合的也较好
R 2 R^2 R2 越大越好,当自己的预测模型不犯任何错误时: R 2 = 1 R^2= 1 R2=1 ,如果 R 2 R^2 R2 < 0,说明学习到的模型还不如基准模型。
#注:很可能数据不存在任何线性关系
from sklearn.metrics import mean_squared_error #均方误差
from sklearn.metrics import mean_absolute_error #平方绝对误差
from sklearn.metrics import r2_score#R square
#调用
mean_squared_error(y_test,y_predict)
mean_absolute_error(y_test,y_predict)
r2_score(y_test,y_predict)
# 简单线性回归(一元线性回归)
# (1)数据示例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
plt.rcParams['font.sans-serif'] = ['KaiTi']
plt.rcParams['axes.unicode_minus']=False
rng = np.random.RandomState(1)
xtrain = 10 * rng.rand(30)
ytrain = 8 + 4 * xtrain + rng.rand(30)
# np.random.RandomState → 随机数种子,对于一个随机数发生器,只要该种子(seed)相同,产生的随机数序列就是相同的
# 生成随机数据x与y
# 样本关系:y = 8 + 4*x
fig = plt.figure(figsize =(12,3))
ax1 = fig.add_subplot(1,2,1)
plt.scatter(xtrain,ytrain,marker = '.',color = 'k')
plt.grid()
plt.title('样本数据散点图')
# 生成散点图
model = LinearRegression()
model.fit(xtrain[:,np.newaxis],ytrain)
# x[:,np.newaxis] → 将数组变成(n,1)形状
print('权重为:',model.coef_)
print('偏置为:',model.intercept_)
xtest = np.linspace(0,10,1000)
ytest = model.predict(xtest[:,np.newaxis])
# 创建测试数据xtest,并根据拟合曲线求出ytest
# model.predict → 预测
ax2 = fig.add_subplot(1,2,2)
plt.scatter(xtrain,ytrain,marker = '.',color = 'k')
plt.plot(xtest,ytest,color = 'r')
plt.grid()
plt.title('线性回归拟合')
# 绘制散点图、线性回归拟合直线
# 权重为: [4.00448414]
# 偏置为: 8.447659499431026
# 简单线性回归(一元线性回归)
# (2)误差
rng = np.random.RandomState(8)
xtrain = 10 * rng.rand(15)
ytrain = 8 + 4 * xtrain + rng.rand(15) * 30
model.fit(xtrain[:,np.newaxis],ytrain)
xtest = np.linspace(0,10,1000)
ytest = model.predict(xtest[:,np.newaxis])
# 创建样本数据并进行拟合
plt.plot(xtest,ytest,color = 'r',linestyle = '--') # 拟合直线
plt.scatter(xtrain,ytrain,marker = '.',color = 'k') # 样本数据散点图
ytest2 = model.predict(xtrain[:,np.newaxis]) # 样本数据x在拟合直线上的y值
plt.scatter(xtrain,ytest2,marker = 'x',color = 'g') # ytest2散点图
plt.plot([xtrain,xtrain],[ytrain,ytest2],color = 'gray') # 误差线
plt.grid()
plt.title('误差')
# 绘制图表
numpy.polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False)
【polyfit】多项式曲线拟合
【polyval】多项式曲线求值
【poly1d】得到多项式系数,按照阶数从高到低排列
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
x = np.random.uniform(-3, 3, size=100) #从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开
X = x.reshape(-1, 1) # n行1列
y = 0.5 + x**2 + x + 2 + np.random.normal(0, 1, size=100) #一元二次方程并添加噪音
coef2 = np.polyfit(x,y, 2) # n表示阶数
poly_fit2 = np.poly1d(coef2)
plt.scatter(x, y)
plt.plot(np.sort(x),poly_fit2(x)[np.argsort(x)], color='r',label="二阶拟合")
print(poly_fit2)
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['KaiTi']
plt.rcParams['axes.unicode_minus']=False
x = np.array([-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10])
y = np.array(2*(x**4) + x**2 + 9*x + 2) #假设因变量y刚好符合该公式
#y = np.array([300,500,0,-10,0,20,200,300,1000,800,4000,5000,10000,9000,22000])
# coef 为系数,poly_fit 拟合函数
coef1 = np.polyfit(x,y, 1)
poly_fit1 = np.poly1d(coef1)
plt.plot(x, poly_fit1(x), 'g',label="一阶拟合")
print(poly_fit1)
coef2 = np.polyfit(x,y, 2)
poly_fit2 = np.poly1d(coef2)
plt.plot(x, poly_fit2(x), 'b',label="二阶拟合")
print(poly_fit2)
coef3 = np.polyfit(x,y, 3)
poly_fit3 = np.poly1d(coef3)
plt.plot(x, poly_fit3(x), 'y',label="三阶拟合")
print(poly_fit3)
coef4 = np.polyfit(x,y, 4)
poly_fit4 = np.poly1d(coef4)
plt.plot(x, poly_fit4(x), 'k',label="四阶拟合")
print(poly_fit4)
coef5 = np.polyfit(x,y, 5)
poly_fit5 = np.poly1d(coef5)
plt.plot(x, poly_fit5(x), 'r:',label="五阶拟合")
print(poly_fit5)
plt.scatter(x, y, color='black')
plt.legend(loc=2)
plt.show()
多项式回归可以看作是对数据进行预处理,给数据添加新的特征,所以调用的库在preprocessing中:
class sklearn.preprocessing.PolynomialFeatures(degree=2, *, interaction_only=False, include_bias=True, order='C')
使用 sklearn.preprocessing.PolynomialFeatures 这个类可以进行特征的构造,构造的方式就是特征与特征相乘(自己与自己,自己与其他人),这种方式叫做使用多项式的方式。
例如:有 a、b 两个特征,那么它的 2 次多项式的次数为 [ 1 , a , b , a 2 , a b , b 2 ] [1,a,b,a^2,ab,b^2] [1,a,b,a2,ab,b2] 。
PolynomialFeatures 这个类有 3 个参数:
degree:控制多项式的次数;
interaction_only:默认为 False,如果指定为 True,那么就不会有特征自己和自己结合的项,组合的特征中没有 a 2 a^2 a2 和 b 2 b^2 b2;
include_bias:默认为 True 。如果为 True 的话,那么结果中就会有 0 次幂项,即全为 1 这一列。
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
np.random.seed(1)
x = np.random.uniform(-3, 3, size=100) #从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开
X = x.reshape(-1, 1) # n行1列
y = 0.5 + x**2 + x + 2 + np.random.normal(0, 1, size=100)
# 这个degree表示我们使用多少次幂的多项式
poly = PolynomialFeatures(degree=2)
poly.fit(X)
X2 = poly.transform(X)
# X2.shape
# 输出:(100, 3)
X2[:5,:]
#array([[ 1. , -0.49786797, 0.24787252],
# [ 1. , 1.32194696, 1.74754377],
# [ 1. , -2.99931375, 8.99588298],
# [ 1. , -1.18600456, 1.40660683],
# [ 1. , -2.11946466, 4.49213042]])
X2的结果第一列常数项,可以看作是加入了一列x的0次方;第二列一次项系数(原来的样本X特征),第三列二次项系数(X平方前的特征)。
特征准备好之后进行训练:
reg = LinearRegression()
reg.fit(X2, y)
y_predict = reg.predict(X2)
plt.scatter(x, y)
plt.plot(np.sort(x), y_predict[np.argsort(x)], color='r')
plt.show()
print(reg.coef_)
print(reg.intercept_)
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
X = np.arange(1, 11).reshape(5, 2) # 5行2列 10个元素的矩阵
poly = PolynomialFeatures()
poly.fit(X)
# 将X转换成最多包含X二次幂的数据集
X2 = poly.transform(X)
# 5行6列
print(X2.shape)
print(X2)
>>>
(5, 6)
[[ 1. 1. 2. 1. 2. 4.]
[ 1. 3. 4. 9. 12. 16.]
[ 1. 5. 6. 25. 30. 36.]
[ 1. 7. 8. 49. 56. 64.]
[ 1. 9. 10. 81. 90. 100.]]
可以看出当数据维度是2维的,经过多项式预处理生成了6维数据。
第一列很显然是0次项系数;第二列和第三列就是原本的X矩阵;第四列是第二列(原X的第一列)平方的结果;第五列是第二、三两列相乘的结果;第六列是第三列(原X的第二列)平方的结果。
由此可以猜想一下如果数据是3维的时候是什么情况:
poly = PolynomialFeatures(degree=3)
poly.fit(X)
x3 = poly.transform(X)
x3.shape #(5, 10)
x3
#输出
array([[ 1., 1., 2., 1., 2., 4., 1., 2., 4., 8.],
[ 1., 3., 4., 9., 12., 16., 27., 36., 48., 64.],
[ 1., 5., 6., 25., 30., 36., 125., 150., 180., 216.],
[ 1., 7., 8., 49., 56., 64., 343., 392., 448., 512.],
[ 1., 9., 10., 81., 90., 100., 729., 810., 900., 1000.]])
PolynomiaFeatures,将所有的可能组合,升维的方式呈指数型增长。这也会带来一定的问题。
Pipeline
在具体编程实践时,可以使用sklearn中的pipeline对操作进行整合。
首先我们回顾多项式回归的过程:
PolynomialFeatures
生成相应的多项式特征Pipeline就是将这些步骤都放在一起。参数传入一个列表,列表中的每个元素是管道中的一个步骤。每个元素是一个元组,元组的第一个元素是名字(字符串),第二个元素是实例化。
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
np.random.seed(1)
x = np.random.uniform(-3, 3, size=100)
X = x.reshape(-1, 1) # n行1列
y = 0.5 + x**2 + x + 2 + np.random.normal(0, 1, size=100)
poly_reg = Pipeline([
('poly', PolynomialFeatures(degree=2)),
('std_scale', StandardScaler()),
('lin_reg', LinearRegression())
])
poly_reg.fit(X, y)
y_predict = poly_reg.predict(X)
plt.scatter(x, y)
plt.plot(np.sort(x), y_predict[np.argsort(x)], color='r')
plt.show()
其实多项式回归在算法并没有什么新的地方,完全是使用线性回归的思路,关键在于为数据添加新的特征,而这些新的特征是原有的特征的多项式组合,采用这样的方式就能解决非线性问题。
这样的思路跟PCA这种降维思想刚好相反,多项式回归则是升维,添加了新的特征之后,使得更好地拟合高维数据。
欠拟合
偏差和方差的定义如下:
对应四种情况:
模型误差:
模型误差 = 偏差 + 方差 + 不可避免的误差(噪音)。一般来说,随着模型复杂度的增加,方差会逐渐增大,偏差会逐渐减小。
偏差方差产生的原因:
一个模型有偏差,主要的原因可能是对问题本身的假设是不正确的,或者欠拟合。如:针对非线性的问题使用线性回归;或者采用的特征和问题完全没有关系,如用学生姓名预测考试成绩,就会导致高偏差。
方差表现为数据的一点点扰动就会较大地影响模型。即模型没有完全学习到问题的本质,而学习到很多噪音。通常原因可能是使用的模型太复杂,如:使用高阶多项式回归,也就是过拟合。
有一些算法天生就是高方差的算法,如kNN算法。非参数学习算法通常都是高方差,因为不对数据进行任何假设。
有一些算法天生就是高偏差算法,如线性回归。参数学习算法通常都是高偏差算法,因为对数据有迹象。
偏差与方差的平衡:
偏差和方差通常是矛盾的。降低偏差,会提高方差;降低方差,会提高偏差。
这就需要在偏差和方差之间保持一个平衡。
以多项式回归模型为例,我们可以选择不同的多项式的次数,来观察多项式次数对模型偏差&方差的影响:
我们要知道偏差和方差是无法完全避免的,只能尽量减少其影响。
其实在机器学习领域,主要的挑战来自方差。处理高方差的手段有:
欠拟合
在训练集上表现不好,在测试集上表现不好
解决方法:
继续学习
1.添加其他特征项
2.添加多项式特征
过拟合
在训练集上表现好,在测试集上表现不好
解决方法:
1.重新清洗数据集
2.增大数据的训练量
3.正则化
4.减少特征维度
正则化
通过限制高次项的系数进行防止过拟合
L1正则化
理解:直接把高次项前面的系数变为0
Lasso回归
L2正则化
理解:把高次项前面的系数变成特别小的值
岭回归
在学习的时候,数据提供的特征有些影响模型复杂度或者这个特征的数据点异常较多,所以算法在学习的时候尽量减少这个特征的影响(甚至删除某个特征的影响),这就是正则化
注:调整时候,算法并不知道某个特征影响,而是去调整参数得出优化的结果
岭回归和LASSO回归都是解决模型训练过程中的过拟合问题
1.Ridge Regression 岭回归
就是把系数添加平方项
然后限制系数值的大小
α值越小,系数值越大,α越大,系数值越小
2.Lasso 回归
对系数值进行绝对值处理
由于绝对值在顶点处不可导,所以进行计算的过程中产生很多0,最后得到结果为:稀疏矩阵
3.Elastic Net 弹性网络
是前两个内容的综合
设置了一个r,如果r=0--岭回归;r=1--Lasso回归
1 Ridge Regression (岭回归,又名 Tikhonov regularization)
岭回归是线性回归的正则化版本,即在原来的线性回归的 cost function 中添加正则项(regularization term):
以达到在拟合数据的同时,使模型权重尽可能小的目的,岭回归代价函数:
2 Lasso Regression(Lasso 回归)
Lasso 回归是线性回归的另一种正则化版本,正则项为权值向量的ℓ1范数。
Lasso Regression 有一个很重要的性质是:倾向于完全消除不重要的权重。
例如:当α 取值相对较大时,高阶多项式退化为二次甚至是线性:高阶多项式特征的权重被置为0。
也就是说,Lasso Regression 能够自动进行特征选择,并输出一个稀疏模型(只有少数特征的权重是非零的)。
3 Elastic Net (弹性网络)
弹性网络在岭回归和Lasso回归中进行了折中,通过 混合比(mix ratio) r 进行控制:
弹性网络的代价函数 :
一般来说,我们应避免使用朴素线性回归,而应对模型进行一定的正则化处理,那如何选择正则化方法呢?
小结:
常用:岭回归
假设只有少部分特征是有用的:
api:
from sklearn.linear_model import Ridge, ElasticNet, Lasso
class sklearn.linear_model.Ridge(alpha=1.0, *, fit_intercept=True, normalize='deprecated', copy_X=True, max_iter=None, tol=0.001, solver='auto', positive=False, random_state=None)
class sklearn.linear_model.RidgeCV(alphas=(0.1, 1.0, 10.0), *, fit_intercept=True, normalize='deprecated', scoring=None, cv=None, gcv_mode=None, store_cv_values=False, alpha_per_target=False)
参数:
类型: numpy array of shape [n_alphas]
说明:α值的数组。正则化的力度,必须是正浮点数。正则化提升了问题的条件,减少了估计器的方差。较大的值指定了更强的正则化。在其他模型,比如LogisticRegression 或者LinearSVC,α对应 C − 1 C^{−1} C−1。
模拟数据使用岭回归
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# np.random.uniform(-3, 3, size=100):在 [-3, 3] 之间等分取 100 个数;
x = np.random.uniform(-3.0, 3.0, size=100)
X = x.reshape(-1, 1)
y = 0.5 * x + 3. + np.random.normal(0, 1, size=100)
plt.scatter(x, y)
plt.show()
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
# 使用多项式回归的管道方法
def PolynomialRegression(degree):
return Pipeline([
('poly', PolynomialFeatures(degree=degree)),
('std_scaler', StandardScaler()),
('lin_reg', LinearRegression())
])
np.random.seed(666)
X_train, X_test, y_train, y_test = train_test_split(X, y)
from sklearn.metrics import mean_squared_error
poly_reg = PolynomialRegression(degree=20)
poly_reg.fit(X_train, y_train)
y_poly_predict = poly_reg.predict(X_test)
print(mean_squared_error(y_test, y_poly_predict))
X_plot = np.linspace(-3, 3, 100).reshape(100, 1)
y_plot = poly_reg.predict(X_plot)
plt.scatter(x, y)
plt.plot(X_plot[:, 0], poly_reg.predict(X_plot), color='r')
plt.axis([-3, 3, 0, 6])
plt.show()
import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
def plot_model(model):
X_plot = np.linspace(-3, 3, 100).reshape(100, 1)
y_plot = model.predict(X_plot)
plt.scatter(x, y)
plt.plot(X_plot[:, 0], model.predict(X_plot), color='r')
plt.axis([-3, 3, 0, 6])
plt.show()
#使用管道的方式使用岭回归方法
def RidgeRegression(degree, alpha):
return Pipeline([
('poly', PolynomialFeatures(degree=degree)),
('std_scaler', StandardScaler()),
('ridge_reg', Ridge(alpha=alpha))
])
#degree = 20、α = 0.0001
ridge1_reg = RidgeRegression(20, 0.0001)
ridge1_reg.fit(X_train, y_train)
y1_predict = ridge1_reg.predict(X_test)
print(mean_squared_error(y_test, y1_predict))
# 输出:1.323349275406402(均方误差)
plot_model(ridge1_reg)
# degree = 20、α = 1
ridge2_reg = RidgeRegression(20, 1)
ridge2_reg.fit(X_train, y_train)
y2_predict = ridge2_reg.predict(X_test)
print(mean_squared_error(y_test, y2_predict))
# 输出:1.1888759304218461(均方误差)
plot_model(ridge2_reg)
# degree = 20、α = 100
ridge3_reg = RidgeRegression(20, 100)
ridge3_reg.fit(X_train, y_train)
y3_predict = ridge3_reg.predict(X_test)
print(mean_squared_error(y_test, y3_predict))
# 输出:1.3196456113086197(均方误差)
plot_model(ridge3_reg)
#degree=20、alpha=1000000(相当于无穷大)
ridge4_reg = RidgeRegression(20, 1000000)
ridge4_reg.fit(X_train, y_train)
y4_predict = ridge4_reg.predict(X_test)
print(mean_squared_error(y_test, y4_predict))
# 输出:1.8404103153255003
plot_model(ridge4_reg)
当 α = 1000000(相当于无穷大)时:拟合曲线几乎是一条水平的直线,因为当 α 非常大的时候,对目标函数的影响相当于只有添加的模型正则化在起作用
class sklearn.linear_model.Lasso(alpha=1.0, *, fit_intercept=True, normalize='deprecated', precompute=False, copy_X=True, max_iter=1000, tol=0.0001, warm_start=False, positive=False, random_state=None, selection='cyclic')
模拟数据使用 LASSO 回归
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
x = np.random.uniform(-3.0, 3.0, size=100)
X = x.reshape(-1, 1)
y = 0.5 * x + 3. + np.random.normal(0, 1, size=100)
from sklearn.model_selection import train_test_split
np.random.seed(666)
X_train, X_test, y_train, y_test = train_test_split(X, y)
#使用多项式回归拟合数据
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
def PolynomialRegression(degree):
return Pipeline([
('poly', PolynomialFeatures(degree=degree)),
('std_scaler', StandardScaler()),
('lin_reg', LinearRegression())
])
#封装绘制代码
def plot_model(model):
X_plot = np.linspace(-3, 3, 100).reshape(100, 1)
y_plot = model.predict(X_plot)
plt.scatter(x, y)
plt.plot(X_plot[:, 0], model.predict(X_plot), color='r')
plt.axis([-3, 3, 0, 6])
plt.show()
#多项式回归并绘图
from sklearn.metrics import mean_squared_error
poly_reg = PolynomialRegression(degree=20)
poly_reg.fit(X_train, y_train)
y_poly_predict = poly_reg.predict(X_test)
print(mean_squared_error(y_test, y_poly_predict))
# 输出:167.9401085999025(均方误差)
plot_model(poly_reg)
#使用 LASSO Regression 改进算法模型
from sklearn.linear_model import Lasso
# 以管道的方式,使用 LASSO 回归的方法改进多项式回归的算法
def LassoRegression(degree, alpha):
return Pipeline([
('poly', PolynomialFeatures(degree=degree)),
('std_scaler', StandardScaler()),
('lasso_reg', Lasso(alpha=alpha))
])
# degree = 20、α = 0.01
lasso1_reg = LassoRegression(20, 0.01)
lasso1_reg.fit(X_train, y_train)
y1_predict = lasso1_reg.predict(X_test)
print(mean_squared_error(y_test, y1_predict))
# 输出:1.149608084325997(均方误差)
plot_model(lasso1_reg)
#degree = 20、α = 0.1
lasso2_reg = LassoRegression(20, 0.1)
lasso2_reg.fit(X_train, y_train)
y2_predict = lasso2_reg.predict(X_test)
print(mean_squared_error(y_test, y2_predict))
# 输出:1.1213911351818648(均方误差)
plot_model(lasso2_reg)
#degree = 20、α = 1
lasso3_reg = LassoRegression(20, 1)
lasso3_reg.fit(X_train, y_train)
y3_predict = lasso3_reg.predict(X_test)
print(mean_squared_error(y_test, y3_predict))
# 输出:1.8408939659515595(均方误差)
plot_model(lasso3_reg)
分析
α = 0.01,比岭回归中的第一个 α 的取值大很多,因为对于 RidgeRegression(),正则化的那一项中是 θ^2,平方后的结果会比较大,所以需要让 α 值很小来调节正则项的大小;而对于LassoRegression(),正则化的那一项中是 |θ|,比岭回归中的正则化项小很多,所以在 LassoRegression() 中 α 的取值可以相对大一些;
在具体进行机器学习算法的过程中,需要不断的试验不断的看结果,慢慢的形成经验,用各种不同的方法在调参时,对于不同的参数大概知道参数在哪个范围内进行选择会相应的比较好;
当 α = 1 时,LassoRegression() 对应的正则化的程度已经比较高了;
正则化的程度:拟合曲线的上下抖动幅度;
现实机器学习的过程中,就是在完全不进行模型正则化和过度模型正则化之间选择一个程度最好的一情况;
1)使用 Ridge 改进的多项式回归算法,随着 α 的改变,拟合曲线始终是各曲线,直到最后变成一条几乎水平的直线;也就是说,使用 Ridge 改造的多项式回归算法,得到的模型变量前还是有系数,因此很难得到一条斜的直线;
2)而使用 Lasso 改进的多项式回归算法,随着 α 的改变,拟合曲线会很快变成一条斜的直线,最后慢慢变成一条几乎水平的直线;模型更倾向于一条直线。
LASSO 的特点:趋向于使得一部分 θ 值变为 0,也就是说 LASSO 认为与 θ = 0 对应的特征是完全没有用的,而剩下与 θ 不为 0 所对应的特征是有用的,所以 LASSO 可作为特征选择用;
在作为特征选择用时,LASSO 也可能将一些有用的特征的系数 θ 变为 0,会导致信息不准确;相比较来说,还是 Ridge 更准确。