Python机器学习基础教程学习笔记(5)——线性模型(回归)
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn
1 线性回归处理wave数据集
1.1 wave数据集
mglearn.plots.plot_linear_regression_wave()
w[0]: 0.393906 b: -0.031804
1.2 线性回归
- 线性回归,或者普通最小二乘法(ordinary least squairs ,OLS),回归总是最简单也是最经典的线性方法
- 寻找参数w和b,使得对训练集的预测值与真实的回归目标值y之间的均方误差(预测值与真实值之差的平方各除以样本数)最小。
- 没有参数,但无法控制模型的复杂度
from sklearn.linear_model import LinearRegression
X,y = mglearn.datasets.make_wave(n_samples=60)
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=42)
lr = LinearRegression().fit(X_train,y_train)
# "斜率"参数(w,也叫做权重或系统),被保存到coef_属性中
print("lr.coef_:{}".format(lr.coef_))
# 偏移或者截距(b),被保存到intercept_属性中
print("lr.intercept_:{}".format(lr.intercept_))
lr.coef_:[0.39390555]
lr.intercept_:-0.031804343026759746
print("Train set score:{:.2f}".format(lr.score(X_train,y_train)))
print("Test set score:{:.2f}".format(lr.score(X_test,y_test)))
Train set score:0.67
Test set score:0.66
- R^2分数约为0.66,结果不是很好
- 训练集和测试集的分数非常接近,可能存在欠拟合
- 一维数据来说,过拟合的风险很小,因为模型非常简单(或受限)
- 高维数据来说,线性模型变得更加强大,过拟合的可能性也会变大
2 线性回归处理波士顿房价数据集
2.1 波士顿房价数据集
from sklearn.datasets import load_boston
boston = load_boston()
# boston是Bunch类型数据
print("boston.keys :{}".format(boston.keys()))
boston.keys :dict_keys(['data', 'target', 'feature_names', 'DESCR', 'filename'])
# 数据集包括506个数据点,13个特征
print("data shape :{}".format(boston.data.shape))
data shape :(506, 13)
扩展数据集:
- 输入特征不仅包括这13个测量结果
- 还包括这些特征的乘积(也叫交互项)
- 像这样包含导出特征的方法叫作特征工程(feature engineering)
# 通过load_extended_boston函数加载导出的数据集
X,y = mglearn.datasets.load_extended_boston()
# 最初的13个特征加上这13个特征两两组合(有放回)得到的91个特征,一共有104个特征
print("X.shape:{}".format(X.shape))
X.shape:(506, 104)
2.2 用线性回归处理
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=0)
lr = LinearRegression().fit(X_train,y_train)
print("Train set score:{:.2f}".format(lr.score(X_train,y_train)))
print("Test set score:{:.2f}".format(lr.score(X_test,y_test)))
Train set score:0.95
Test set score:0.61
- 训练集和测试集之间的性能差异是过拟合(训练集0.95,测试集0.61)的明显标志
- 线性回归没有参数,不可以控制模型复杂度。
3 岭回归(Ridge regression)
- 一种用于回归的线性模型
- 预测公式与普通最小二乘法相同
- 对系数(w)的选择不仅要在训练集数据上得到好的预测结果
- 还要拟合附加约束
- 系统尽可能小——w的所有元素都应接近于0
- 意味着,每个特征对输出的影响应尽可能小(即斜率很小),同时仍给出很好的预测结果 。这种约束是所谓的正则化的一个例子
- 正则化是指对模型做的显示约束,以避免过拟合
- 岭回归用的是L2正则化
from sklearn.linear_model import Ridge
ridge = Ridge().fit(X_train,y_train)
print("Train set score:{:.2f}".format(ridge.score(X_train,y_train)))
print("Test set score:{:.2f}".format(ridge.score(X_test,y_test)))
Train set score:0.89
Test set score:0.75
ridge的训练集分数0.89低于linear的训练集分数0.95
ridge的测试集分数0.75高于linear的训练集分数0.61
linear过拟合
rigde约束性更强,更不容易过拟合
riged复杂度更小,在训练集性能更差,泛化性能更好
我们对泛化性能感兴趣,应选择ridge,而不选择linear
ridge在模型的简单性(系数都接近于0)与训练集之间做出权衡
通过
alpha
参数来指定默认
alpha=0
alpha
的最佳设定值取决于用到的具体的数据集alpha
越大,模型约束性越强,系数越趋向于0,训练集性能越低,泛化能力越强alpha
非常小,系数几乎没有限制,结果与linear类似
ridge10=Ridge(alpha=10).fit(X_train,y_train)
print("Train set score:{:.2f}".format(ridge10.score(X_train,y_train)))
print("Test set score:{:.2f}".format(ridge10.score(X_test,y_test)))
Train set score:0.79
Test set score:0.64
ridge01=Ridge(alpha=0.1).fit(X_train,y_train)
print("Train set score:{:.2f}".format(ridge01.score(X_train,y_train)))
print("Test set score:{:.2f}".format(ridge01.score(X_test,y_test)))
Train set score:0.93
Test set score:0.77
ridge0001=Ridge(alpha=0.001).fit(X_train,y_train)
print("Train set score:{:.2f}".format(ridge0001.score(X_train,y_train)))
print("Test set score:{:.2f}".format(ridge0001.score(X_test,y_test)))
Train set score:0.95
Test set score:0.63
# 约束越强,y值越趋向于0
# 约束强
plt.plot(ridge10.coef_,"^",label="alpha=10")
# 约束中
plt.plot(ridge.coef_,"s",label="alpha=1")
# 约束弱
plt.plot(ridge01.coef_,"v",label="alpha=0.1")
# 无约束
plt.plot(lr.coef_,"o",label="linear")
plt.xlabel("Coefficient index")
plt.ylabel("Coefficient magnitude")
plt.hlines(0,0,len(lr.coef_))
plt.ylim(-25,25)
plt.legend()
plt.show()
- 固定alpha的值,改变训练集数据量
- 将模型性能作为数据集大小的函数进行绘图,这样的图像叫作学习曲线
mglearn.plots.plot_ridge_n_samples()
- 训练集分数高于测试集分数
- ridge训练集分数低于linear训练集分数
- ridge测试集分数高于linear测试集分数
- 数据越多,性能越好
- 如果数据量足够多,正则化不再重要,rideg与linear会得到同样的性能
- 数据越多,linear将更加难以过拟合或记住所有的数据
4 lasso
- 约束系数使其接近于0
- 使用L1正则化
- 使某些系数刚好为0,说明某些特征被模型完全忽略,更容易解释,呈现模型最重要的特征
- 自动化特征选择
from sklearn.linear_model import Lasso
lasso = Lasso().fit(X_train,y_train)
print("Train set score:{:.2f}".format(lasso.score(X_train,y_train)))
print("Test set score:{:.2f}".format(lasso.score(X_test,y_test)))
print("Number of features used:{}".format(np.sum(lasso.coef_!=0)))
Train set score:0.29
Test set score:0.21
Number of features used:4
- 训练集和测试集的性能都很差,欠拟合
- 只用到了104个特征中的4个
- 正则化参数
alpha
,默认是1 - 为降低欠拟合,尝试减小
alpha
,同时增加max_iter
-
alpha
越小,模型越复杂,越不容易欠拟合 -
alpha
极小,结果与linear类似
lasso001 = Lasso(alpha=0.01,max_iter=100000).fit(X_train,y_train)
print("Train set score:{:.2f}".format(lasso001.score(X_train,y_train)))
print("Test set score:{:.2f}".format(lasso001.score(X_test,y_test)))
# 模型用了33个特征,性能好了些
print("Number of features used:{}".format(np.sum(lasso001.coef_!=0)))
Train set score:0.90
Test set score:0.77
Number of features used:33
lasso00001 = Lasso(alpha=0.0001,max_iter=100000).fit(X_train,y_train)
print("Train set score:{:.2f}".format(lasso00001.score(X_train,y_train)))
print("Test set score:{:.2f}".format(lasso00001.score(X_test,y_test)))
# 模型用了96个特征,类似linear,过拟合
print("Number of features used:{}".format(np.sum(lasso00001.coef_!=0)))
Train set score:0.95
Test set score:0.64
Number of features used:96
plt.plot(lasso.coef_, 's', label="Lasso alpha=1")
plt.plot(lasso001.coef_, '^', label="Lasso alpha=0.01")
plt.plot(lasso00001.coef_, 'v', label="Lasso alpha=0.0001")
plt.plot(ridge01.coef_, 'o', label="Ridge alpha=0.1")
plt.legend(ncol=2, loc=(0, 1.05))
plt.ylim(-25, 25)
plt.xlabel("Coefficient index")
plt.ylabel("Coefficient magnitude")
plt.show()
- alpha=1,只用到了104个特征中的4个,大部分系数都为0,而且系数比较小
- alpha=0.01,用了33个特征
- alpha=0.0001,用了96个特征,大不分系数不为0,而且系数比较大
- ridge(alpha=0.1)性能与lasso(alpha=0.01)性能类似,但是ridge的所有系数都不为0
实践中:
- 首先ridge
- 特征多,认为只有其中几个是重要的,选择lasso
- 想更容易解释,选择lasso
- scikit-learn提供了ElasticNet,结合了ridge和lasso的惩罚项,效果最好,需要调节l1正则化参数和l2正则化参数,共2个参数