分享

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

 昵称11935121 2018-06-17

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

大家周末好!

昨天,我们详细地说了关于过拟合问题的理论,今天我们一起撸一遍代码,通过实践来彻底解决这个机器学习中的“拦路虎”。

我们挑选了多项式回归与简单线性模型来比较模型复杂度对过拟合的影响,并且通过观察测试集的泛化误差来证明参数越多,模型越容易过拟合。

接下来,我们对多项式回归和简单线性回归分别使用正则化的技术,一方面从例子中演示正则化是如何起作用的,另一方面,清楚地表明正则化对降低过拟合的效果。

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

为方便起见,我们所用数据可以方便地从Python库scikit-learn导入,此数据描述了患者一年以后糖尿病的恶化程度,总共有442个样本,每个样本10个属性,分别是年龄,性别,身高体重比(BMI),血压以及体内的六种血清水平。

我们的样本矩阵x共有442行,10列,而我们的y就是糖尿病恶化程度的量化,是一个442维的向量。

拿到这样的数据,为了方便做可视化,我们挑选一个特征(这里用的是BMI)作为横轴,可以很简单地画出数据的分布:

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

拿到这样的数据,可以用类似的代码做出这样一个简单线性回归模型:

from sklearn import datasets,linear_model

import seaborn as sns

import matplotlib.pyplot as plt

import numpy as np

#读取数据

data=datasets.load_diabetes()

X=data['data'][:,np.newaxis,2]#挑选一个特征:BMI

y=data['target']

#线性回归

lr=linear_model.LinearRegression()

lr.fit(X,y)

y_pred=lr.predict(X)

#画图

sns.set(style='darkgrid')

plt.plot(X,y,'.k')

plt.xlabel('BMI')

plt.ylabel('quantitative measure of disease progression')

plt.plot(X,y_pred,'-r',linewidth=2,label='Ordinary Least Squares')

plt.legend()

plt.show()

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

可以看出,拟合虽然符合总体的趋势,但我们可以换用多项式回归来取得更好的“效果”,添加以下代码来进行多项式回归:

#多项式最高次为10

poly=preprocessing.PolynoimalFeatures(degree=10)

X_poly=poly.fit_transform(X)

lr_poly=LR()

lr_poly.fit(X_poly,y)

y_pred_poly=lr_poly.predict(X_poly)

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

如果只有肉眼看,就无法分辨这两个模型哪个效果会更好,所以我们采用性能度量(MSE)对不同的degree(线性模型默认degree为1)的模型做出直观的可视化:

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

这幅图告诉我们,degree越大,MSE越小,即多项式的项数越多,模型与数据拟合的越好。

可真的是这样吗?这里面有没有某一项可能发生了过拟合呢?

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

我们可以用分集测试来找出可能存在的过拟合。思路非常明确:把degree作为超参数,观察模型在训练集和测试集上的MSE:

我们读取数据后用如下代码分集,然后在训练集和测试集分别重复上一步的工作:

from sklearn.model_selection import train_test_split

from sklearn import metrics

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

train_mse=[]

test_mse=[]

for n in range(1,20):

poly=preprocessing.PolynomialFeatures(degree=n)

X_poly_train,X_poly_test=poly.fit_transform(X_train),\

poly.fit_transform(X_test)

lr_poly=linear_model.LinearRegression()

lr_poly.fit(X_poly_train,y_train)

y_pred_train=lr_poly.predict(X_poly_train)

y_pred_test=lr_poly.predict(X_poly_test)

train_mse.append

(metrics.mean_squared_error(y_train,y_pred_train))

test_mse.append

(metrics.mean_squared_error(y_test,y_pred_test))

.....

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

可以看出,随着degree的增加,训练误差总体趋势在减小,但测试误差在增大,从degree=10开始,测试集上的误差就远远大于训练集上的误差,说明出现了严重的过拟合。

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

模型在测试集上的MSE已经到了无法直视的地步,为了避免这样随机划分的训练集和测试集对部分数据点敏感,我们可以选用交叉验证来对误差分析平均化:

from sklearn.model_selection import cross_validate

train_mse=[]

test_mse=[]

scorer='neg_mean_squared_error'

for n in range(1,20):

poly=preprocessing.PolynomialFeatures(degree=n)

X_poly=poly.fit_transform(X)

lr_poly=linear_model.LinearRegression()

lr_dict=cross_validate

(lr_poly,X_poly,y,cv=5,scoring=scorer)#5 fold

test_mse.append(-lr_dict['test_score'].mean())

train_mse.append(-lr_dict['train_score'].mean())

......

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

用交叉验证避免固定数据对结果造成的影响后,我们断定,degree的增大会使得模型出现严重的过拟合。

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

截至目前,我们用了BMI这样一个特征和它的多次项,我们还可以在参数空间中观察参数的变化,以degree=10为例,对其加正则项,这样的做的目的是,随着正则化参数

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

的变化,各项的重要程度也会体现出来,参数衰减的越早、越快,则这一项也就越不重要。

# degree=10

poly=preprocessing.PolynomialFeatures(degree=10)

X_poly=poly.fit_transform(X)

# Ridge Regreesion

from sklean.linear_model import Ridge

coefs=[]

alphas=np.logspace(-6,-10)

for a in alphas:

ridge=Ridge(alpha=a)

ridge.fit(X_poly,y)

coefs.append((ridge.coef_)/10000)

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

以上是加了

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

正则化的结果,发现只有只有为数不多的系数衰减的慢,其他的系数在

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

时就已经接近于零,说明这些系数本身就很小,根据我们对线性模型的理解,这些系数所对应的项并不重要。我们再来尝试LASSO(

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

正则化)的结果:

from sklearn.linear_model import Lasso

coefs_lasso=[]

alphas=np.logspace(-6,-10)

for a in alphas:

lasso=Lasso(alpha=a)

lasso.fit(X_poly,y)

coefs_lasso.append((lasso.coef_)/1e6)

......

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

LASSO相比Ridge Regression有着把权重系数削减为零的特性,权重系数一旦为零,意味着对应的特征项也就不存在。而且随着权重因子的增大,LASSO会偏好于权重为零的结果,所以只要权重因子足够大,系数一定会变为零,所以在这幅图中,我们要看哪些系数比其他系数变为零更慢一点。

正因为LASSO的使得系数变为零的特性,我们可以把权重因子作为超参数,把测试集的MSE作为我们观察的目标,来挑出最合适的权重因子(在最佳下,我们可能会间接知道多少特征被保留了下来):

lasso_mse=[]

alphas=np.logspace(-6,-10)

scorer='neg_mean_squared_error'

for a in alphas:

lasso=Lasso(alpha=a)

lasso_dict=cross_validate(lasso,X_poly,y,cv=5,scoring=scorer)

lasso_mse.append(-lasso_dict['test_score'].mean()) #我们这次更加聪明地只看测试集上的MSE

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

从这幅图可以得出最佳的

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

值,我们将这个值带入LASSO的优化函数就可以得出相关的系数,就可以确定出有多少特征被保留了下来,在这里,我们用如下代码来获取可能的最佳degree:

[IN]:len(np.nonzero(Lasso(alpha=best_alpha).fit(X_poly,y).coef_)[0])-1

#这里减一是因为degree为n的多项式模型参数为n+1

[Out]: 6

我们在这里得到了一个degree为6的多项式模型,那么我们可以说最终的模型就是这样吗?当然不行,因为我们为了样本空间的可视化,只用到了一个特征,为了信息不被损失,我们可以把全部特征都用上,但十个特征的多项式运算的时间复杂度是非常大的,我们用LASSO的方法,用到全部的十个特征,再一次来观察正则化在其中起到的作用:

from sklearn.linear_model import LassoCV

model = LassoCV(cv=10).fit(X, y)

m_log_alphas = -np.log10(model.alphas_)

ymin, ymax = 2300, 3800

sns.set(style='darkgrid')

plt.plot(m_log_alphas, model.mse_path_, ':')

plt.plot(m_log_alphas, model.mse_path_.mean(axis=-1), 'k',

label='Average across the folds', linewidth=2)

plt.axvline(-np.log10(model.alpha_), linestyle='--', color='k',

label='alpha: CV estimate')

plt.legend()

plt.xlabel('-log(alpha)')

plt.ylabel('Mean square error')

plt.axis('tight')

plt.ylim(ymin, ymax)

plt.show()

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

其中,最佳的alpha大约为0.057,我们将其代入Lasso的优化函数中,计算它的MSE,并与简单线性回归模型作对比,LASSO的MSE大约为2987.23,而简单线性回归模型的MSE大约为3000.38,很明显的,LASSO取得了更好的效果。

这个结果是非常重要的,因为LASSO具备挑选特征的属性,这意味着在那十个特征里面,有些特征的去除会使我们的模型起到更好的效果。

由此而来一个很自然的想法是,我们可以在模型之前,就进行一定的特征选择。

下一星期的课程中,我们会介绍更多的方法,敬请期待~

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

课堂TIPS

· 文中所有的代码语言均为Python,用到了Python的numpy,scikit-learn,seaborn,matplotlib,如果我们实在是不想用scikit-learn,那么Python的Scipy将会是一个很好的选择,相较于scikit-learn,我们的Loss Function可以自由的定义,优化方法也是多种多样,面对具体问题可以更加的灵活,但学习的成本可能会更高一些。

· 本文所使用的数据,已经经过标准化处理,标准化是一种将数据无量纲化的技巧,也会起到尺度收缩的作用,避免因为个别点的特征尺度过大对MSE的解读造成影响。

· 对于本文所使用的数据,我们能一眼看出性别这个特征应该与糖尿病关系应该不大,似乎我们应该把这个特征剔除掉。我们在面对很多任务时,不会像现在对数据有着自己的知识,所以依据的方法更为重要,这个问题的背后是一种叫做相关系数的特征选择方法。

「周末AI课堂」过拟合问题(代码篇)|机器学习你会遇到的“坑”

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多