从机器学习的角度,线性回归是回归问题中最简单的模型。回归问题属于监督式机器学习。
回顾线性回归
在线性代数中,我们就学过用最小二乘法做线性回归:
对于平面上的 N 个点$P_i(x_i,y_i)$,满足$min \sum {\Delta Y_i}^{2} = min \sum {|Y_i-f(x_i)|}^2$
的直线$y=f(x)$,是这 N 个点的线性拟合直线。
机器学习角度的线性回归
从机器学习的角度,线性回归是回归问题中最简单的模型。回归问题属于监督式机器学习:
给定训练样本,「学习」一个函数,每一个样本数据就是需要学习的函数的输入输出数据。
并且这个函数能够很好的泛化到不在训练集中的输入值上。可以通过测试数据来检验泛化的效果。
线性回归通常用于根据符合直线关系的连续变量估计实际数值。
其优点是快速、没有调节参数、可轻易解释、可理解;缺点是相比其他复杂一些的模型,其预测准确率不是太高,
因为它假设特征和响应之间存在确定的线性关系,这种假设对于非线性的关系,线性回归模型显然不能很好的对这种数据建模。
线性回归的两种主要类型是一元线性回归和多元线性回归。
一元线性回归的特点是只有一个自变量,即$y=y=\alpha x+\beta$.
而多元线性回归存在多个自变量,即$y=b_0+b_1x_1+…+b_px_p$
其实多项式回归可以看做是一种特殊的多元线性回归:$y=b_0+b_1x+ b_2x^2…+b_px^p$
scikit-learn糖尿病数据集
我们用 scikit-learn 的糖尿病数据集来演示线性回归。
import pandas as pd
import warnings
# 用来忽略seaborn绘图库产生的warnings
warnings.filterwarnings("ignore")
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="white", color_codes=True)
%pylab inline
from sklearn import datasets
diabetes = datasets.load_diabetes()
dir(diabetes)
diabetes.feature_names
# 10个特征,分别为Age(年龄)、性别(Sex)、Body mass index(体质指数)、Average Blood Pressure(平均血压)、S1~S6一年后疾病级数指标
# Target为一年后患疾病的定量指标
df = pd.DataFrame(diabetes.data,columns=diabetes.feature_names)
df['target']=pd.Series(diabetes.target)
df.head()
sns.pairplot(df, x_vars=diabetes.feature_names, y_vars='target', size=5, aspect=0.8)
# 显然,sex、s1、s2、 s4,与 target 没有相关性
#參数kind='reg'。seaborn能够加入一条最佳拟合直线和95%的置信带。
sns.pairplot(df, x_vars=['age', 'bmi', 'bp', 's3', 's5', 's6'], y_vars='target',
size=5, aspect=0.8, kind='reg')
用 scikit-learn处理线性回归问题
LinearRegression模型在Sklearn.linear_model
下,
它主要是通过 fit(x,y)
的方法来训练模型,其中x为特征,y为标记。
然后通过predict()
预测, 以fit()算出的模型参数, 预测特性 x 所对应的预测值y_pred。
import pandas as pd
import warnings
# 用来忽略seaborn绘图库产生的warnings
warnings.filterwarnings("ignore")
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="white", color_codes=True)
%pylab inline
from sklearn import datasets
diabetes = datasets.load_diabetes()
df = pd.DataFrame(diabetes.data,columns=diabetes.feature_names)
df['target']=pd.Series(diabetes.target)
df.head()
# 拆分数据
from sklearn.cross_validation import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df[diabetes.feature_names], df['target'], random_state=1)
len(x_train),len(x_test),len(y_train),len(y_test)
from sklearn import linear_model
linear = linear_model.LinearRegression()
# 训练模型
linear.fit(x_train, y_train)
#训练结果
print('Coefficients :\n' , linear.coef_)
print('Intercept: n', linear.intercept_)
# 模型评价
#对于线性回归来说,我们一般用均方差(Mean Squared Error, MSE)
#或者均方根差(Root Mean Squared Error, RMSE)在测试集上的表现来评价模型的好坏。
y_pred = linear.predict(x_test)
# 残差平方和 方差得分
print("Residual sum of square: %.2f" %np.mean((y_pred - y_test) ** 2))
print("variance score: %.2f" % linear.score(x_test, y_test))
from sklearn import metrics
# 用scikit-learn计算MSE
print("MSE:",metrics.mean_squared_error(y_test, y_pred))
# 用scikit-learn计算RMSE
print("RMSE:",np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
#绘图
plt.scatter(x_test['age'], y_test, color = 'black')
#预测结果 直线表示
#plt.plot(x_test['age'], linear.predict(y_test), color='blue', linewidth = 3)
#plt.show()
# 交叉验证
from sklearn.model_selection import cross_val_predict
predicted = cross_val_predict(linear, df[diabetes.feature_names], df['target'], cv=10)
# 用scikit-learn计算MSE
print("MSE:",metrics.mean_squared_error(df['target'], predicted))
# 用scikit-learn计算RMSE
print("RMSE:",np.sqrt(metrics.mean_squared_error(df['target'], predicted)))
# 可以看出,采用交叉验证模型的MSE更大
# 主要原因是我们这里是对所有折的样本做测试集对应的预测值的MSE,
# 而前面仅仅对25%的测试集做了MSE。两者的先决条件并不同。
模型评价和特征选择
回归问题不能用准确率评价。对于回归这种连续数值,评价测度(evaluation metrics)主要用以下指标:
平均绝对误差(Mean Absolute Error, MAE)
均方误差(Mean Squared Error, MSE),即残差平方和
均方根误差(Root Mean Squared Error, RMSE)
在数据探索的时候,我们就发现 sex、s1、s2、 s4 与 target 的相关度不高。现在移除这些特征,
看看回归的结果是否更好。
import pandas as pd
import warnings
# 用来忽略seaborn绘图库产生的warnings
warnings.filterwarnings("ignore")
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="white", color_codes=True)
%pylab inline
from sklearn import datasets
diabetes = datasets.load_diabetes()
df = pd.DataFrame(diabetes.data,columns=diabetes.feature_names)
df['target']=pd.Series(diabetes.target)
df.head()
from sklearn.cross_validation import train_test_split
from sklearn import linear_model
from sklearn import metrics
# 全特征回归
x_train, x_test, y_train, y_test = train_test_split(df[diabetes.feature_names], df['target'], random_state=1)
linear = linear_model.LinearRegression()
linear.fit(x_train, y_train)
#训练结果
print('Coefficients :\n' , linear.coef_)
print('Intercept: n', linear.intercept_)
# 结果评价
y_pred = linear.predict(x_test)
print("得分: %.2f" % linear.score(x_test, y_test))
print("MAE:",metrics.mean_absolute_error(y_test, y_pred))
# print("MSE: %.2f" %np.mean((y_pred - y_test) ** 2))
print("MSE:",metrics.mean_squared_error(y_test, y_pred))
print("RMSE:",np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
# 部分特征回归
x_train, x_test, y_train, y_test = train_test_split(df[['age', 'bmi', 'bp', 's3', 's5', 's6']], df['target'], random_state=1)
linear = linear_model.LinearRegression()
linear.fit(x_train, y_train)
#训练结果
print('Coefficients :\n' , linear.coef_)
print('Intercept: n', linear.intercept_)
# 结果评价
y_pred = linear.predict(x_test)
print("得分: %.2f" % linear.score(x_test, y_test))
print("MAE:",metrics.mean_absolute_error(y_test, y_pred))
# print("MSE: %.2f" %np.mean((y_pred - y_test) ** 2))
print("MSE:",metrics.mean_squared_error(y_test, y_pred))
print("RMSE:",np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
# 可见,人工筛选后的结果更差,说明特征的筛选不合适
多项式回归
这里,用一个2次函数加上随机的扰动来生成500个点,然后尝试用1、2、100次方的多项式对该数据进行拟合。
拟合的目的是使得根据训练数据能够拟合出一个多项式函数,这个函数能够很好的拟合现有数据,并且能对未知的数据进行预测[^5]。
结果评价:
做回归分析,常用的误差主要有均方误差根(RMSE)和R-平方(R2)。
RMSE是预测值与真实值的误差平方根的均值。这种度量方法很流行(Netflix机器学习比赛的评价方法),是一种定量的权衡方法。
R2方法是将预测值跟只使用均值的情况下相比,看能好多少。其区间通常在(0,1)之间。0表示还不如什么都不预测,直接取均值的情况,而1表示所有预测跟真实结果完美匹配的情况。
R2的计算方法,不同的文献稍微有不同。如本文中函数R2是依据scikit-learn官网文档实现的,跟clf.score函数结果一致。
而R22函数的实现来自Conway的著作《机器学习使用案例解析》,不同在于他用的是2个RMSE的比值来计算R2。
我们看到多项式次数为1的时候,虽然拟合的不太好,R2也能达到0.82。2次多项式提高到了0.88。
而次数提高到100次,R2也只提高到了0.89。
import pandas as pd
import warnings
# 用来忽略seaborn绘图库产生的warnings
warnings.filterwarnings("ignore")
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="white", color_codes=True)
%pylab inline
# 生成数据
import scipy as sp
from scipy.stats import norm
x = np.arange(0, 1, 0.002)
y = norm.rvs(0, size=500, scale=0.1)
y = y + x**5
df = pd.DataFrame()
df['x']=x
df['y']=y
df.head()
#plt.scatter(x, y, s=5,figsize=12)
#df..plot(figsize=(12,9))
sns.jointplot(x="x", y="y", data=df, kind='reg', size=9)
# 评价函数
''''' 均方误差根 '''
def rmse(y_test, y):
return sp.sqrt(sp.mean((y_test - y) ** 2))
''''' 与均值相比的优秀程度,介于[0~1]。0表示不如均值。1表示完美预测.这个版本的实现是参考scikit-learn官网文档 '''
def R2(y_test, y_true):
return 1 - ((y_test - y_true)**2).sum() / ((y_true - y_true.mean())**2).sum()
''''' 这是Conway&White《机器学习使用案例解析》里的版本 '''
def R22(y_test, y_true):
y_mean = np.array(y_true)
y_mean[:] = y_mean.mean()
return 1 - rmse(y_test, y_true) / rmse(y_mean, y_true)
plt.figure(figsize=(12,9))
# 回归级数
degree = [1,2,5,10,20,50,100]
plt.scatter(x, y, s=5)
# 线性回归
from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
# 多项式回归
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn import linear_model
for d in degree:
clf = Pipeline([('poly', PolynomialFeatures(degree=d)),
('linear', LinearRegression(fit_intercept=False))])
clf.fit(x[:, np.newaxis], y)
y_test = clf.predict(x[:, np.newaxis])
# 拟合结果
#print(clf.named_steps['linear'].coef_)
# 评价
print('level=%d,rmse=%.2f, R2=%.2f, R22=%.2f, clf.score=%.2f' %
(d,
rmse(y_test, y),
R2(y_test, y),
R22(y_test, y),
clf.score(x[:, np.newaxis], y)))
plt.plot(x, y_test, linewidth=2)
plt.grid()
plt.legend(degree, loc='upper left')
# 结果评价
# 注意,次数过高,容易发生过拟合
参考资料
[^1]: Linear Regression Example
[^2]: scikit-learn : 线性回归,多元回归,多项式回归
[^3]: Coursera公开课笔记: 斯坦福大学机器学习第二课“单变量线性回归(Linear regression with one variable)”
[^4]: 用scikit-learn和pandas学习线性回归
[^5]: 用Python开始机器学习(3:数据拟合与广义线性回归)
[^6]: Coursera公开课笔记: 斯坦福大学机器学习第四课“多变量线性回归(Linear Regression with Multiple Variables)”
[^7]: sklearn学习笔记之简单线性回归