【笔记】模型泛化与岭回归与LASSO
模型泛化与岭回归与LASSO
模型正则化
模型正则化,简单来说就是限制参数大小
模型正则化是用什么思路来解决先前过拟合的由于过于拟合导致的曲线抖动(线性方程前的系数都很大)
线性回归的目标就是求一个最优解,让损失函数尽可能的小也就是使求出来的均方误差尽可能的小
如果过拟合的话,就会让theta系数过大,那么怎么限制呢,可以改变损失函数,加入模型正则化,将其加上所有thetai的平方和乘上一个常数(这个阿尔法是个新的超参数,代表着后面的式子在整个式子中的重要程度(占比)),变为
让式子中的theta尽可能小使式子尽可能小
实际上是让准确度和thetai尽可能小这两件事取得一个平衡,那么对于不同的数据,就要尝试取不同的值
那么这种模型正则化的方式通常称为岭回归
具体使用
(在notebook中)
基础代码:
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)
plt.scatter(x,y)
图像如下
使用多项式回归的方式,然后对数据进行分割,最后计算一下均方误差:
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
def PolynomialRegression(degree):
return Pipeline([
("poly",PolynomialFeatures(degree=degree)),
("std_scaler",StandardScaler()),
("lin_reg",LinearRegression())
])
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.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)
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],y_plot,color='r')
plt.axis([-3,3,0,6])
图像如下
将绘图的代码进行包装:
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],y_plot,color='r')
plt.axis([-3,3,0,6])
plot_model(poly_reg)
调用以后结果如下:
使用岭回归
调用sklearn中的Ridge类,使用管道的方式将其进行创建,将第三步创建一个ridge这个函数对象,这就封装好了一个岭回归的过程
from sklearn.linear_model import Ridge
def RidgeRegression(degree,alpha):
return Pipeline([
("poly",PolynomialFeatures(degree=degree)),
("std_scaler",StandardScaler()),
("ridge_reg",Ridge(alpha=alpha))
])
这里使用第一组数据,degree为20,alpha取0.0001,然后进行fit,得到训练数据以后进行预测,最后计算均方误差
ridge1_reg = RidgeRegression(20,0.0001)
ridge1_reg.fit(X_train,y_train)
y1_predict = ridge1_reg.predict(X_test)
mean_squared_error(y_test,y1_predict)
结果为(比起之前小了太多)
绘制图像
plot_model(ridge1_reg)
图像如下
这里使用第二组数据,degree为20,alpha取1
ridge2_reg = RidgeRegression(20,1)
ridge2_reg.fit(X_train,y_train)
y2_predict = ridge2_reg.predict(X_test)
mean_squared_error(y_test,y2_predict)
结果为(又好了一些)
绘制图像
plot_model(ridge2_reg)
图像为
这里使用第三组数据,degree为20,alpha取100
ridge3_reg = RidgeRegression(20,100)
ridge3_reg.fit(X_train,y_train)
y3_predict = ridge3_reg.predict(X_test)
mean_squared_error(y_test,y3_predict)
结果为(反而增加了)
绘制图像
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)
mean_squared_error(y_test,y4_predict)
结果为(又增加了)
绘制图像
plot_model(ridge4_reg)
图像为(几乎的直线)
这里就知道了,使用岭回归的时候,引入了一个新的超参数,对于不同的数据,
会出现不同的模型
这是模型正则化的一种方式
LASSO
对于岭回归来说,其任务就是让损失函数对应的值尽可能的小,为了平衡,加入了一个超参数来平衡
LASSO的思想和岭回归其实一样,其目标是使这个式子尽可能小,即损失函数加上alpha乘上theta的绝对值的和尽可能小
从数学的意义上理解,感觉都是让theta尽可能的小,那么最终结果就通过具体实现看一下
具体实现
(在notebook中)
基础环境见上面
基本和上方的代码一样,方便观看就贴在这里了
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 StandardScaler
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
def PolynomialRegression(degree):
return Pipeline([
("poly",PolynomialFeatures(degree=degree)),
("std_scaler",StandardScaler()),
("lin_reg",LinearRegression())
])
先尝试使用degree=20来进行一下拟合预测加计算均方误差
from sklearn.metrics import mean_squared_error
poly_reg = PolynomialRegression(degree=20)
poly_reg.fit(X_train,y_train)
y_predict = poly_reg.predict(X_test)
mean_squared_error(y_test,y_predict)
结果如下
使用绘制函数
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],y_plot,color='r')
plt.axis([-3,3,0,6])
plot_model(poly_reg)
图像如下
使用LASSO
调用sklearn中的Lasso类,同样的使用管道包装一个LASSO的方法使用
from sklearn.linear_model import Lasso
def LassoRegression(degree,alpha):
return Pipeline([
("poly",PolynomialFeatures(degree=degree)),
("std_scaler",StandardScaler()),
("lasso_reg",Lasso(alpha=alpha))
])
首先构建一个lasso_reg,传入degree=20,alpha=0.01(对于lasso正则化项要小很多,可以先取大的数值),然后fit训练,训练后进行预测,然后计算均方误差
lasso1_reg = LassoRegression(20,0.01)
lasso1_reg.fit(X_train,y_train)
y1_predict = lasso1_reg.predict(X_test)
mean_squared_error(y_test,y1_predict)
结果如下
绘制图像
plot_model(lasso1_reg)
图像如下
传入degree=20,alpha=0.1
lasso2_reg = LassoRegression(20,0.1)
lasso2_reg.fit(X_train,y_train)
y2_predict = lasso2_reg.predict(X_test)
mean_squared_error(y_test,y2_predict)
结果如下
绘制图像
plot_model(lasso2_reg)
图像如下
传入degree=20,alpha=1
lasso3_reg = LassoRegression(20,1)
lasso3_reg.fit(X_train,y_train)
y3_predict = lasso3_reg.predict(X_test)
mean_squared_error(y_test,y3_predict)
结果如下
绘制图像
plot_model(lasso3_reg)
图像如下
比较ridge和lasso
可以通过上述的数据结果看出来,这两个方法的alpha的取值影响是差距很大的,可以说,使用lasso这个方法得到的模型更趋向于是一个直线,那么直线和曲线的区别在那里,使用ridge的时候得到了曲线,这代表着依然有很多的特征的前面还存在着系数,用了LASSO以后,就可以表示很多的特征的前面的系数是没有了的
这正是lasso的特点,其趋向于使得一部分的theta值变为0,而不是让theta变成一个很小的值,其可以作为一种特征选择方法来使用,这就说明如果lasso以后的特征前面的系数变成0了,可以认为这个特征是没有用的,剩下的不等于0的LASSO认为这些特征是有用的
如何解释呢(尝试解读)
在使用ridge的时候,只看后面的式子可以发现,在alpha趋于无穷的时候,按照梯度下降法的观点来看,其梯度为
在向着零点进发的过程中,每一步theta都是有值的,使用曲线的方式逐渐达到零
对于LASSO来说,在alpha趋于无穷的时候,虽然不可导,但是可以使用分类函数来刻画其导数
(Sign(x)的意思是,在x>0时,取1,在x=0时,取0,在x<0时,取-1)
相当于在不考虑前面的函数的时候,其后面的梯度就是一个alpha乘上一个向量,向量的值就三个,也就是说,在向着零点进发的时候,只能使用这种规则的方式,可能就在其中的一步,使得其到达了某个轴上,这就说明了为什么其中有很多的零
不过要注意,LASSO有的时候也会将有用的特征变成零,所以,从计算的准确度上来讲,还是ridge更加的准确,但是在特征很大的时候,使用LASSO可以起到将特征变小的作用