原理
前面我们学习了线性回归,顾明思议,我们拟合的结果是线性的函数,而对于有一些数据,其分布明显是属于非线性的,这时我们应该怎么办呢?

其实我们整体上还是采用线性回归的方法,我们将x**2理解成一个新的特征,然后去做线性回归即可
基本实现
import numpy as np
import matplotlib.pyplot as plt
X=np.random.uniform(-3,3,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+2+np.random.normal(0,1,size=100)
我们先观察一下散点图

我们先用线性回归进行拟合
from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(x,y)
y_predict= lin_reg.predict(x)
plt.scatter(x,y)
plt.plot(x,y_predict,color='r')

然后我们把x**作为新特征加入
#采用添加一个特性的方案来变成多项式回归
(x**2).shape
x2=np.hstack([x,x**2])
lin_reg2=LinearRegression()
lin_reg2.fit(x2,y)
y_predict2=lin_reg2.predict(x2)
plt.scatter(x,y)
plt.plot(np.sort(X),y_predict2[np.argsort(X)],color='r')
#x是无序的,我们如果想生成一个平滑曲线,而非无限根线交织在一起的话就应该像上面一样使用sort排序,然后y_predict使用其返回的索引
#可以试试直接使用plot(x,y_predict2)看看会是什么样的图


sklearn中的多项式线性回归以及pipeline初识
import numpy as np
import matplotlib.pyplot as plt
X=np.random.uniform(-3,3,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+2+np.random.normal(0,1,size=100)
from sklearn.preprocessing import PolynomialFeatures
poly=PolynomialFeatures(degree=2)#最高添加几次幂
poly.fit(x)
x2=poly.transform(x)
这里我们查看x2注意到

第一列全为1,是额外增加上的,第三列是第二列的平方
from sklearn.linear_model import LinearRegression
lin_reg2=LinearRegression()
lin_reg2.fit(x2,y)
y_predict2= lin_reg2.predict(x2)
plt.scatter(x,y)
plt.plot(np.sort(X),y_predict2[np.argsort(X)],color='r')


那么你可能会想,若degree不为2呢,或者若特征不止一个的情况呢?
因为高中知识告诉我们,若存在两个自变量,阶数是由两个自变量共同决定的,若有X1,X2 ,那么X1*X2也是二阶,所以在使用 PolynomialFeatures 时我们注意到
-
degree=3时
degree=3
我们在处理相关问题的时候,往往需要几个固定的步骤,我们有没有办法把这些步骤联合起来呢?——答案就是pipeline

import numpy as np
import matplotlib.pyplot as plt
X=np.random.uniform(-3,3,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+2+np.random.normal(0,1,size=100)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
poly_reg=Pipeline([
("poly",PolynomialFeatures(degree=2)),
("std_scacler",StandardScaler()),
("lin_reg",LinearRegression())
])
poly_reg.fit(x,y)
y_predict3=poly_reg.predict(x)
plt.scatter(x,y)
plt.plot(np.sort(X),y_predict3[np.argsort(X)],color='r')
得到结果

过拟合与欠拟合
其实多项式相关内容已经可以结束了,但这里需要额外补充一下在ML中很重要的一些知识。
首先我们一步一步从线性回归到不同阶数的多项式回归,观察其拟合程度,这里选用均方误差进行判断。
X=np.random.uniform(-3,3,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+2+np.random.normal(0,1,size=100)
from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(x,y)
m=lin_reg.score(x,y)
y_predict= lin_reg.predict(x)
plt.scatter(x,y)
plt.plot(np.sort(X),y_predict[np.argsort(X)],color='r')
得到散点图、准确率、均方误差如下:

下面使用多项式回归进行拟合
#使用多项式回归
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
def PolynomialRegression(degree):
return Pipeline([
("poly",PolynomialFeatures(degree=degree)),
("std_scacler",StandardScaler()),
("lin_reg",LinearRegression())
])
poly_reg2=PolynomialRegression(degree=2)
poly_reg2.fit(x,y)
y_predict2=poly_reg2.predict(x)
mean_squared_error(y,y_predict2)

这时我们会考虑试试其他阶数的情况(10阶)
poly_reg10=PolynomialRegression(degree=10)
poly_reg10.fit(x,y)
y_predict10=poly_reg10.predict(x)
mean_squared_error(y,y_predict10)

我们发现均方误差进一步减小,但曲线开始弯曲不平了,至于这于模型而言是好是坏我们暂且不得而知,我们进一步增大阶数,100阶:
poly_reg100=PolynomialRegression(degree=100)
poly_reg100.fit(x,y)
y_predict100=poly_reg100.predict(x)
mean_squared_error(y,y_predict100)

这时均方误差又进一步减小,但曲线弯曲程度更厉害了
那么我们来分析一下这几个阶的情形
首先我们先将100阶情形下的曲线更加准确化,使用linspace生成连续的自变量进行绘图,如下:
#我们更加准确地看看100阶情况下曲线图
x_plot=np.linspace(-3,3,100).reshape(100,1)
y_plot=poly_reg100.predict(x_plot)
plt.scatter(x,y)
plt.plot(x_plot[:,0],y_plot,color='r')
plt.axis([-3,3,-1,10])

其实我们稍作分析,容易得出结论,虽然100阶的情形对于训练集拟合的更好,但是对于未知数据的预测,很可能出现偏离原来数据较远的情况,也就是说,不能准确预测,我们将其称为过拟合。而对于我们上面使用线性回归的情况,称之为欠拟合。

- 欠拟合:算法所训练的模型不能完整表述数据关系。
- 过拟合:算法所训练的模型过多表达了数据间的噪音关系。
训练、测试、验证数据集
- 如果我们仅有训练数据集,那么我们常常会对于是否过拟合欠拟合而不自知
- 如果我们仅有训练、测试数据集会不会有问题呢?
如果仅有训练测试数据集的话,相当于整个训练集训练的模型,在针对这一个测试数据集进行调参,有可能对于这个测试数据集过拟合。即,对这个测试数据集符合的很好,然而对于我们要预测的数据,又没法很好的预测了。 -
所以我们需要再引入验证数据集
引入验证
这时,我们使用验证数据集,替代我们前文中所述的“测试数据集”,然后这里的测试数据集仅用来做测试,相当于我们即将要用来预测的数据。
-
那么如何保证随机性呢?——交叉验证
交叉验证
使用这k个模型进行平均,作为结果来进行调参。
k-folds交叉验证
import numpy as np
from sklearn import datasets
digits=datasets.load_digits()
x=digits.data
y=digits.target
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.4,random_state=6)
from sklearn.neighbors import KNeighborsClassifier
#使用直接尝试的方法去调参
best_score,best_k,best_p=0,0,0
for k in range(2,11):
for p in range(1,6):
knn_clf=KNeighborsClassifier(weights="distance",n_neighbors=k,p=p)
knn_clf.fit(x_train,y_train)
score=knn_clf.score(x_test,y_test)
if score>best_score:
best_score,best_k,best_p=score,k,p
print("best score=",best_score)
print("best k",k)
print("best p",p)
我们得到结果

我们先来看看如何使用sklearn中的交叉验证
from sklearn.model_selection import cross_val_score
knn_clf=KNeighborsClassifier()
cross_val_score(knn_clf,x_train,y_train)
#默认分成三类进行交叉验证,不过编译时提示以后默认会改成cv=5
我们将上面的fit变为cross_val_score然后score变成平均值即可
for k in range(2,11):
for p in range(1,6):
knn_clf=KNeighborsClassifier(weights="distance",n_neighbors=k,p=p)
scores=cross_val_score(knn_clf,x_train,y_train,cv=5)
score=np.mean(scores)
if score>best_score:
best_score,best_k,best_p=score,k,p
print("best score=",best_score)
print("best k",k)
print("best p",p)

偏差与方差
模型误差=偏差+方差+不可避免的误差
偏差->欠拟合
方差->过拟合
- 机器学习主要挑战便是降低方差(高偏差可以通过改用更合适的模型解决)
1、降低模型复杂度
2、减小数据维度
3、增加样本数
4、使用验证集
模型正则化:限制参数的大小
对于之前过拟合的图形,我们看到,部分曲线上下起伏很大,这是部分seata值过大造成的,我们进行如下分析:


容易分析得到,加入模型正则化,可以使得在J尽可能小的同时,seata值也尽可能小,这样就解决了上述问题。
上面的这种模型正则化的方式称为岭回归。
import numpy as np
import matplotlib.pyplot as plt
X=np.random.uniform(-3.0,3.0,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+3+np.random.normal(0,1,size=100)
#使用多项式回归
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
def PolynomialRegression(degree):
return Pipeline([
("poly",PolynomialFeatures(degree=degree)),
("std_scacler",StandardScaler()),
("lin_reg",LinearRegression())
])
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=6)
poly1_reg=PolynomialRegression(degree=20)
poly1_reg.fit(x,y)
y1_predict=poly1_reg.predict(x_test)
mean_squared_error(y_test,y1_predict)

#使用岭回归
from sklearn.linear_model import Ridge
def RidgeRegression(degree,alpha):
return Pipeline([
("poly",PolynomialFeatures(degree=degree)),
("std_scacler",StandardScaler()),
("ridge_reg",Ridge(alpha=alpha))
])#类似于前面,仅将用于fit的函数替换掉
ridge_reg1=RidgeRegression(20,0.0001)
ridge_reg1.fit(x_train,y_train)
y_predict_1=ridge_reg1.predict(x_test)
mean_squared_error(x_test,y_predict_1)

可以看到,曲线变平滑了很多,这里的alpha选的很小的值,若增大其值,会提升模型正则化所占比例,这时候曲线平滑程度会增大,最值成为一条横线。
除了岭回归,还有其他的正则化方式,这里介绍LASSO回归

实例中,相较于使用岭回归,所作出的改变,仅仅是将Pipline中的Ridge变成LASSO即可

最后提及一下L1正则、L2正则



弹性网将L1L2结合,可调节两者所占比例,利用两者的优缺点
总结
这章的内容可以说是非常多了,所以码字加代码学习花了比较长的时间,不过其实最为核心的内容还是前面那一部分关于多项式回归的,若觉得内容繁多,可以仅看前面的内容。
网友评论