引言
线性模型形式简单、易于建模,就好像“hello world”。但许多功能强大的非线性模型都是在线性模型上的延伸。我们主要介绍几种经典的线性模型:线性回归,多项式回归,岭回归,lasso回归,逻辑斯提回归。
线性回归
以西瓜问题为例,它有三个属性(也就做特征),{色泽,根蒂,敲声},根据这些属性判断是不是好瓜,我们自然而然想到的是:是不是可以通过属性的线性组合来预测。即:
写成向量形式:
其中w叫做权重系数,b叫做偏置项。
那么如何确定w,b呢。关键在于我们的预测值和实际值间的差距。在回归任务中,均方误差是最常用的性能度量。
其中f为我们的预测函数,D为包含m个样本的数据集。
你可能会想也可以这样度量差距啊:
当然这样也可以,这叫做曼哈顿距离,但函数性质不好,不连续,在后续处理会很麻烦。
我们的目标是求得使均方误差最小化的w和b:
接下来就是数学推导了;
对于包含m个样本的数据集D,每个样本有d个属性描述的一般情况。我们试图学得的模型为:
我们把w,b两个未知的参数写在一起:
其中:
X为(m,d+1)维,W为(d+1,1)维
这样我们的目标函数就是:
目标函数为连续的凸函数,令其偏导数为零,不就解出需要的参数了吗?
解得:
可以看出一下子就解出我们需要的参数了,这是求解模型的方法之一:存在闭式解。另一种情况:很难求得闭式方程,需要使用迭代优化的方法,一步步走直到参数最优。这种方法,我们会在逻辑斯提回归中介绍。
回来看,这有一个问题:XTX这个矩阵需要可逆,在练习情况下该矩阵通常是可逆的,因为样本数远远大于属性数。而在实际情况中该矩阵往往不可逆,属性数远远大于样本数,这样该矩阵不可逆,存在多个解。这时需要引入正则化,如岭回归,lasso回归。我会在线性回归之后进行介绍。
还有一个问题:线性回归往往出现欠拟合,在已知数据集上很难达到很好的性能,局部加权回归可以缓解这种情况。
实战演练
线性回归模型很简单,可以自己试着去实现。这里不再介绍,直接使用sklearn。
超参数:fit_intercept:是否需要偏置项b,默认true
normalize:是否需要归一化。当 fit_intercept
False,忽略该参数 。归一化方法为:减去均值,除以l2范数。如果需要 标准化, 使用 sklearn.preprocessing.StandardScaler
并设置 normalize=False
.
copy_X:是否需要复制X
n_jobs:cpu使用数,-1代表全部使用。
属性:coef_:权重系数w
intercept_:偏置项b
代码走起:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
'''
创建数据集
'''
#rand(100,1)产生100*1的矩阵,数为0到1的随机数
X = 2 * np.random.rand(100,1)
#randn(100,1)产生100*1的矩阵,数为正态分布随机数
y = 4 + 3*X + np.random.randn(100,1)
plt.figure()
plt.xlim(0,2)
plt.ylim(0,14)
plt.xlabel('x')
plt.ylabel('y')
plt.scatter(X,y)
plt.show()
结果:
lin_reg = LinearRegression()
lin_reg.fit(X,y)
print(lin_reg.coef_,lin_reg.intercept_)
结果:
在数据集上可视化:
'''
可视化
'''
plt.figure()
plt.xlim(0,2)
plt.ylim(0,14)
plt.xlabel('x')
plt.ylabel('y')
plt.plot([0,2],
[lin_reg.intercept_[0],2*lin_reg.coef_[0][0]+lin_reg.intercept_[0]],
c='black')
plt.scatter(X,y)
plt.title('LinearRegression')
plt.show()
结果:
还不错吧。
多项式回归
如果数据比简单的直线复杂,我们也可以用线性模型来进行拟合。一个简单的方法就是将每个特征的幂次方添加为一个新的特征,然后在这个拓展过的训练集上训练线性模型,这种方法称为多项式回归。
'''
创建数据集
'''
np.random.seed(42)
#rand(100,1)产生100*1的矩阵,数为0到1的随机数
X = 6 * np.random.rand(100,1) - 3
#randn(100,1)产生100*1的矩阵,数为正态分布随机数
y = 0.5 * X**2 + X + 2 + np.random.randn(100,1)
'''
可视化
'''
plt.figure()
plt.xlim(-3,3)
plt.ylim(0,10)
plt.xlabel('x')
plt.ylabel('y')
plt.scatter(X,y)
plt.show()
结果:
显然,直线不可能很好的拟合它。
'''
训练
'''
#利用PolynomialFeatures对数据进行转换,将每个属性的平方作为新特征加入数据集
poly_features = PolynomialFeatures(degree=2,include_bias=False)
X_poly = poly_features.fit_transform(X)
lin_reg = LinearRegression()
lin_reg.fit(X_poly,y)
'''
可视化
'''
plt.figure()
plt.xlim(-3,3)
plt.ylim(0,10)
plt.xlabel('x')
plt.ylabel('y')
x = np.arange(-3,3,0.1)
Y = lin_reg.coef_[0][1]*x**2 + lin_reg.coef_[0][0]*x + lin_reg.intercept_
plt.plot(x,Y,c='black')
plt.scatter(X,y)
plt.show()
结果:
岭回归
前面我们提到过,有时XTX矩阵不可逆时可以使用岭回归。简单来说,岭回归就是在XTX上加上一个lamda*I使得矩阵非奇异。
矩阵I是m*m的单位矩阵(单位矩阵,值1贯穿对角线,其余全是0,像不像一条‘岭’)。这样闭式解变成:
岭回归最初就是为了解决矩阵奇异的情况,现在也用于在估计中加入偏差,引入惩罚项相当于l2范数,降低过拟合。
这里引入了方差和偏差的概念:在机器学习领域,一个模型的泛化误差可以表示成三种误差的和
1)偏差 该误差的原因在于模型欠拟合
2)方差 该误差的原因在于使用的模型复杂,导致过拟合
3)不可避免的误差:数据本身的噪声
我们经常回去权衡偏差和方差,增加模型的复杂度就会提升方差,减小偏差。反过来,降低模型的复杂度就会降低方差,增大偏差。
参数:
alpha:正则化强度; 必须是正浮点数。 较大的值指定较强的正则化。
copy_X:与线性回归相同
fit_intercept:与线性回归相同
max_iter:int,可选,共轭梯度求解器的最大迭代次数。 对于’sparse_cg’和’lsqr’求解器,默认值由scipy.sparse.linalg确定。 对于’sag’求解器,默认值为1000。
normalize:与线性回归相同
solver:{‘auto’,’svd’,’cholesky’,’lsqr’,’sparse_cg’,’sag’,‘saga’}
用于计算的求解方法:
‘auto’根据数据类型自动选择求解器。
‘svd’使用X的奇异值分解来计算Ridge系数。对于奇异矩阵比’cholesky’更稳定。
‘cholesky’使用标准的scipy.linalg.solve函数来获得闭合形式的解,即上述的闭式解的一种变体
‘sparse_cg’使用在scipy.sparse.linalg.cg中找到的共轭梯度求解器。作为迭代算法,这个求解器比大规模数据的“cholesky”更合适。
‘lsqr’使用专用的正则化最小二乘常数scipy.sparse.linalg.lsqr。它是最快的,使用迭代过程。
‘sag’使用随机平均梯度下降。‘saga’是它的改进。都使用迭代过程,并且当n_samples和n_feature都很大时,通常比其他求解器更快。注意,“sag”快速收敛仅在具有近似相同尺度的特征上被保证。您可以使用sklearn.preprocessing的缩放器预处理数据。
所有最后五个求解器支持密集和稀疏数据。但是,当fit_intercept为True时,只有’sag’,‘saga’支持稀疏输入。
新版本0.17支持:sag。
新版本0.19支持:saga。
tol:float解的精度。
random_state:随机种子发生器。当为int时,生成固定数据,只适合‘sag’求解器
返回值
coef_:与线性回归相同
intercept_:与线性回归相同
n_iter_:每个目标的实际迭代次数。 仅适用于sag和lsqr求解器。 其他求解器将返回None。在版本0.17中出现。
'''
训练
'''
#利用PolynomialFeatures对数据进行转换,将每个属性的平方作为新特征加入数据集
poly_features = PolynomialFeatures(degree=2,include_bias=False)
X_poly = poly_features.fit_transform(X)
lin_reg = LinearRegression()
lin_reg.fit(X_poly,y)
#岭回归
ridge_reg = Ridge(alpha=100,solver='cholesky')
ridge_reg.fit(X_poly,y)
'''
可视化
'''
plt.figure()
plt.xlim(-3,3)
plt.ylim(0,10)
plt.xlabel('x')
plt.ylabel('y')
x = np.arange(-3,3,0.1)
Y1 = lin_reg.coef_[0][1]*x**2 + lin_reg.coef_[0][0]*x + lin_reg.intercept_
plt.plot(x,Y1,c='black',label='lin_reg')
Y2 = ridge_reg.coef_[0][1]*x**2 + ridge_reg.coef_[0][0]*x + ridge_reg.intercept_
plt.plot(x,Y2,c='red',label='ridge_reg')
plt.scatter(X,y)
plt.legend(loc='best')
plt.show()
结果:
alpha越大,越接近线性模型。
lasso回归
与岭回归一样,它也能缓解过拟合,引入惩罚项,相当于L1范数。Lasso回归有一个重要的特点,它会倾向于完全消除掉那些最不重要的权重,我们因此能更好的了解到哪些特征是无用的。
alpha:正则化程度,较大的值指定较强的正则化。
fit_intercept:与线性回归相同
normalize:与线性回归相同
precompute:是否使用预计算的 Gram 矩阵来加速计算。如果设置为 ‘auto’ 则机器决定。Gram 矩阵也可以 pass。对于 sparse input 这个选项永远为 True。
max_iter:最大循环次数
tol:优化误差率
warm_start:为 True 时, 重复使用上一次学习作为初始化,否则直接清除上次方案
positive:设为 True 时,强制使系数为正。
random_state:随机种子生成器
selection:若设为 ‘random’, 每次循环会随机更新参数,而按照默认设置则会依次更新。设为随机通常会极大地加速交点(convergence)的产生,尤其是 tol 比 1e-4 大的情况下。
返回值
coef_:同线性回归
intercept_:同线性回归
n_iter_:达到误差率的循环次数
sparse_coef_:
拟合系数的稀疏表示
#Lasso回归
Lasso_reg = Lasso(alpha=2)
Lasso_reg.fit(X_poly,y)
'''
可视化
'''
plt.figure()
plt.xlim(-3,3)
plt.ylim(0,10)
plt.xlabel('x')
plt.ylabel('y')
x = np.arange(-3,3,0.1)
Y1 = lin_reg.coef_[0][1]*x**2 + lin_reg.coef_[0][0]*x + lin_reg.intercept_
plt.plot(x,Y1,c='black',label='lin_reg')
Y2 = Lasso_reg.coef_[1]*x**2 + Lasso_reg.coef_[0]*x + Lasso_reg.intercept_
plt.plot(x,Y2,c='red',label='Lasso_reg')
plt.scatter(X,y)
plt.legend(loc='best')
plt.show()
结果:
小结
本节主要介绍线性模型中的线性回归,并引出了多项式回归来解决非线性数据问题。还介绍了岭回归和Lasso回归来解决过拟合问题。那如何利用线性模型进行分类学习呢?
下节我们将要介绍逻辑斯提回归。虽说它有回归字样,却叛变为分类学习方法。