(七)多项式回归于模型泛化

多项式回归是线性回归法的改进,针对一些不是线性关系的数据

进而引出模型泛化--机器学习中非常重要的概念

一,什么是多项式回归

程序实现:

import numpy as np

importmatplotlib.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, 100)

使用线性回归拟合:

fromsklearn.linear_model import LinearRegression

lin_reg =LinearRegression()

lin_reg.fit(X, y)

y_predict =lin_reg.predict(X)

解决方案,添加一个特征

X2 =np.hstack([X, X**2]) #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')

#注意这里生成的y_predict是无序的。因此要对这俩个参数进行排列

多项式回归没有用到新模型,而是是继续使用线性回归的思路,不同的是多项式回归在线性回归的基础上增加了特征,并对这些新特征进行线性组合。

二,scikit-learn中的多项式回归与Pipline

数据同上

fromsklearn.preprocessing import PolynomialFeatures

poly =PolynomialFeatures(degree=2) #最多添加几次幂

poly.fit(X)

X2 =poly.transform(X) #针对现有的X进行变形,很多算法都有

从这里也可以看出数据是无序的,并且第一列对应的是零次幂,第三列数据是第二列数据的平方

fromsklearn.linear_model import LinearRegression

lin_reg2 =LinearRegression()

lin_reg2.fit(X2, y)

y_predict2 =lin_reg2.predict(X2)

利用线性回归进行预测

fromsklearn.linear_model import LinearRegression

lin_reg2 =LinearRegression()

lin_reg2.fit(X2, y)

y_predict2 =lin_reg2.predict(X2)

关于PolynomialFeatures类

X =np.arange(1, 11).reshape(-1, 2) #相当于有俩个特征,五行俩列

poly =PolynomialFeatures(degree=2)

poly.fit(X)

X2 = poly.transform(X)

观察X2和X的关系,分别生成了原来X的X(1),X(2)和X(1)*X(2),以及平方

也就是生成了不同特征对应的各种排列组合的项

pipline用法

进行线性回归过程中进行的三步:多项式的特征,数据的归一化和线性回归。pipline可以将这三步合在一起。

数据生成:

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, 100)

使用pipline:

from sklearn.pipelineimport Pipeline

fromsklearn.preprocessing import StandardScaler

poly_reg = Pipeline([

("poly",PolynomialFeatures(degree=2)), #引号里是起的名称

("std_scaler", StandardScaler()), #数据的归一化

("lin_reg", LinearRegression()) #线性回归

])

创建了这个管道,就可以使数据沿着这个管道进行处理

poly_reg.fit(X, y)

y_predict =poly_reg.predict(X)

三,过拟合与欠拟合

np.random.seed(666)

x =np.random.uniform(-3.0, 3.0, size=100)

X = x.reshape(-1, 1)

y = 0.5 * x**2 + x + 2+ np.random.normal(0, 1, size=100)

使用线性回归:

fromsklearn.linear_model import LinearRegression

lin_reg =LinearRegression()

lin_reg.fit(X, y)

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')

plt.show()

拟合效果很差

对于线性回归,可以使用R^2来判定线性关系,但是对于多项式回归要使用其他的判断标准,为了统一,使用均方误差计算效果。

from sklearn.metricsimport mean_squared_error

y_predict =lin_reg.predict(X)

mean_squared_error(y,y_predict)

使用多项式回归:

同上一节,建立一个pipline

from sklearn.pipelineimport Pipeline

fromsklearn.preprocessing import PolynomialFeatures

fromsklearn.preprocessing import StandardScaler

defPolynomialRegression(degree):

return Pipeline([

("poly",PolynomialFeatures(degree=degree)),

("std_scaler",StandardScaler()),

("lin_reg",LinearRegression())

])

poly2_reg =PolynomialRegression(degree=2)

poly2_reg.fit(X, y)

y2_predict =poly2_reg.predict(X)

mean_squared_error(y,y2_predict)

degree=2拟合结果:

如果degree=10:

poly10_reg =PolynomialRegression(degree=10)

poly10_reg.fit(X, y)

y10_predict =poly10_reg.predict(X)

mean_squared_error(y,y10_predict)

拟合结果:

如果degree=100:

poly100_reg =PolynomialRegression(degree=100)

poly100_reg.fit(X, y)

y100_predict =poly100_reg.predict(X)

mean_squared_error(y,y100_predict)

拟合结果:

再看拟合的结果:

X_plot =np.linspace(-3, 3, 100).reshape(100, 1)

y_plot =poly100_reg.predict(X_plot)

plt.scatter(x, y)

plt.plot(X_plot[:,0],y_plot, color='r')

plt.axis([-3, 3, 0,10])

plt.show()

degree越高,均方误差越低,但是不能更好的反应数据样本的变化曲线,也就是多项式拟合的过程中将数据的函数曲线便复杂了。这就是过拟合。

而对于把函数曲线弄的太简单的情况就是欠拟合。

train test split的意义

模型的泛化能力:新加入数据后,看看拟合的曲线是否满足新的样本点

使用训练数据集与测试数据集来测试预测模型的泛化能力

fromsklearn.model_selection import train_test_split

X_train, X_test,y_train, y_test = train_test_split(X, y, random_state=666)

分别使用不同degree的多项式回归来拟合训练数据集,并使用测试数据集进行测试分数:

四,学习曲线

随着训练样本的逐渐增多,算法训练出的模型的表现能力

生成的数据同上

fromsklearn.model_selection import train_test_split

X_train, X_test,y_train, y_test = train_test_split(X, y, random_state=10)

fromsklearn.linear_model import LinearRegression

from sklearn.metricsimport mean_squared_error

train_score = []

test_score = []

for i in range(1, 76):#从一个数据到76个数据

lin_reg = LinearRegression()

lin_reg.fit(X_train[:i], y_train[:i])

y_train_predict =lin_reg.predict(X_train[:i])

train_score.append(mean_squared_error(y_train[:i],y_train_predict))

y_test_predict = lin_reg.predict(X_test)

test_score.append(mean_squared_error(y_test, y_test_predict))

plt.plot([i for i inrange(1, 76)], np.sqrt(train_score), label="train")

plt.plot([i for i inrange(1, 76)], np.sqrt(test_score), label="test")

plt.legend()

plt.show()

封装成一个函数:

线性回归学习曲线:(欠拟合)

利用pipline训练模型:

二阶多项式:

二阶多项式回归学习曲线:(最佳)

拟合结果比较好

degree=20:(过拟合)

poly20_reg =PolynomialRegression(degree=20)

plot_learning_curve(poly20_reg,X_train, X_test, y_train, y_test)

泛化能力很差,过拟合

五,验证数据集与交叉验证

验证数据集:

测试数据集的意义:发生过拟合却不自知

问题:针对特定测试数据集过拟合

训练数据集获得模型;验证数据集用于调整超参数,仿真过拟合;最终的测试数据集作为衡量最终模型性能的数据集。

交叉验证:

交叉验证程序实现:

导入数据集:

import numpy as np

from sklearn importdatasets

digits =datasets.load_digits()

X = digits.data

y = digits.target

测试train_test_split:

fromsklearn.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=666)

from sklearn.neighborsimport KNeighborsClassifier

best_k, best_p,best_score = 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_k, best_p, best_score = k, p,score

print("Best K=", best_k)

print("Best P=", best_p)

print("Best Score=", best_score)

使用交叉验证:

fromsklearn.model_selection import cross_val_score

knn_clf =KNeighborsClassifier()

cross_val_score(knn_clf,X_train, y_train)

默认分成了五份进行交叉验证

得到和上边不同的参数

交叉验证的准确度低于普通验证方式,是因为交叉验证并没有过拟合

并且交叉验证的目的是为了得到最好的超参数,而不是得到最好的准确率。要得到最好的准确率就需要重新利用得到的超参数进行fit,再计算准确率。

回顾网格搜索:

用到了交叉验证的方式GridSearchCV

同样默认将训练集分成5份,每份计算9*5=45种参数的组合,因此一共要进行5*45=225次训练

best_estimator包就是找到最佳参数对应的准确率

CV参数:

总结:

把训练数据集分成k份,称为k-fola crossvalidation,缺点就是速度慢了k倍

极端的情况是把训练数据集分成最多份,只留一个数据进行测试,称为留一法,leave-one-out cross validation,简称LOO-CV。这样就完全不受随机分配的影响,最接近模型真正的性能指标。计算量巨大

六,偏差方差权衡BiasVariance Trade off

模型误差=偏差+方差+不可避免的误差(数据本身的噪音)

  • 导致偏差的主要原因:

对问题本身的假设不正确,比如非线性数据使用线性回归(欠拟合underfitting)

  • 方差:数据的一点点扰动都会较大的影响模型。通常原因是使用的模型太复杂。比如使用高阶多项式(过拟合overfitting)

  • 有些算法天生是高方差的算法(KNN,对数据敏感,有一个数据错误,整体的数据预测就不准确)

  • 非参数学习通常是高方差算法,因为不对数据进行任何假设,只能根据现有的数据进行预测(例子还是KNN,数据本身就是算法,高度依赖数据,还有决策树也是)

  • 有些算法天生是高偏差算法(比如线性回归,因为现实中的很多问题并不是线性关系)

  • 参数学习通常都是高偏差算法,因为对数据有极强的假设

  • 大多数算法具有相应的参数,可以调整偏差和方差(比如KNN中的k,还有线性回归中的多项式回归)

  • 偏差和方差通常是矛盾的,降低偏差会提高方差,反之同理。

  • 机器学习的主要挑战来自方差!(只局限于算法的层面)

  • 降低方差的常见手段有:

降低模型复杂度

减少数据的维数;降噪

增加样本数

使用验证集

模型的正则化

七,模型泛化与岭回归

模型正则化Regularization

限制参数的大小

加入模型正则化后,可以使系数不太大,防止拟合的曲线太过于陡峭(注意没有加入theta0)

1/2也可以不加,α是个超参数,表示正则化因素的影响程度

以上的正则化方式叫做岭回归(Ridge Gegression)

岭回归程序实现:

生成参数

训练拟合

定义一个绘图函数,方便调用:

使用岭回归:

fromsklearn.linear_model import Ridge

defRidgeRegression(degree, alpha):

return Pipeline([

("poly",PolynomialFeatures(degree=degree)),

("std_scaler",StandardScaler()),

("ridge_reg",Ridge(alpha=alpha))

])

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)

线条缓和了许多

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)

结果进一步优化

当α=100

正则化过头了

α=10000000

几乎是一条直线(所有θ=0)

八,LASSO回归

LeastAbsolute Shrinkage and SelectionOperator Regression

程序实现:

lasso2_reg =LassoRegression(20, 0.1)

lasso3_reg =LassoRegression(20, 1)

比较Ridge和LASSO

Ridge得到的拟合曲线相比LASSO更倾向于是弯曲的,因为正则化形式是二次方的性质

LASSO趋向于使一部分的theta变为0,所以可作为特征选择使用,即把一些特征的系数变为0,表示这个特征没有用

从准确性来看Ridge更好,但如果特征太大太多就可以用LASSO回归

九,L1,L2和弹性网络

回顾明可夫斯基距离

Lp范数:

p=1曼哈顿距离,p=2欧拉距离

岭回归的正则项也被称为L2正则项

LASSO正则项也叫L1正则项

L0正则项是让theta的数量尽可能的少,进而使曲线不要太陡,但一般不使用,实际用L1取代

弹性网ElasticNet

引入俩个正则项。一般来说计算力强,数据特征不多,纯使用岭回归更好

不同的数据样本有不同的特征,要具体问题具体分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值