如果说朴素贝叶斯是解决分类任务的好起点,那么线性回归模型就是解决回归任务的好起点。
#简单线性回归
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns;sns.set()
rng = np.random.RandomState(42)
x = 10 * rng.rand(50)
y = 2 * x + 5 + rng.randn(50)
#使用Scikit-Learn的LinearRegression评估器来拟合数据,并获得最佳拟合直线
from sklearn.linear_model import LinearRegression
model = LinearRegression(fit_intercept=True)
model.fit(x.reshape(-1,1), y)
x_test = np.linspace(-1, 11, 50)
y_test = model.predict(x_test.reshape(-1, 1))
plt.scatter(x, y)
plt.plot(x_test, y_test)
LinearRegression评估器除了简单的直线拟合,它还可以处理多维度的线性回归模型: y=a0+a1x1+a2x2+... y = a 0 + a 1 x 1 + a 2 x 2 + . . . 里面有多个 x x 变量。从几何学的角度看,这个模型是拟合三维空间中的一个平面,或者是更多维度的数据点的一个超平面。
rng = np.random.RandomState(1)
x = 10 * rng.rand(100, 3)
y = 0.5 + np.dot(x, [1.5, -2, 1.])
model.fit(x, y)
#数据的斜率和截距都在模型的拟合参数中
print(model.intercept_)
print(model.coef_)
#输出结果:
0.5000000000000144
[ 1.5 -2. 1. ]
通过基函数对原始数据进行变换可以将变量间的线性回归模型转换为非线性回归模型。这个方法的多维模型是: y=a0+a1x1+a2x2+a3x3+... y = a 0 + a 1 x 1 + a 2 x 2 + a 3 x 3 + . . . ,其中一维的输入变量转换成三维变量 x1、x2 x 1 、 x 2 和 x3 x 3 。让 xn=fn(x) x n = f n ( x ) ,这里的 fn() f n ( ) 是转换数据的函数。假如 fn(x)=xn f n ( x ) = x n ,那么模型就会变成多项式回归:
y=a0+a1x1+a2x22+a3x33+... y = a 0 + a 1 x 1 + a 2 x 2 2 + a 3 x 3 3 + . . .
需要注意的是,这个模型仍然是一个线性模型,也就是说系数 an a n 彼此不会相乘或者相除。我们其实是将一维的 x x 投影到高位空间,因此通过线性模型就可以拟合出 x x 和 y y 间更复杂的关系。
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
poly_model = make_pipeline(PolynomialFeatures(7), LinearRegression())
rng = np.random.RandomState(1)
x = 10 * rng.rand(50)
y = np.sin(x) + 0.1 * rng.randn(50)
poly_model.fit(x[:, np.newaxis], y)
x_fit = np.linspace(0, 10, 1000)
y_fit = poly_model.predict(x_fit[:, np.newaxis])
plt.scatter(x, y)
plt.plot(x_fit, y_fit)
#写一个转化器来创建高斯基函数
from sklearn.base import BaseEstimator, TransformerMixin
class GaussianFeatures(BaseEstimator, TransformerMixin):
"""一维输入均匀分布的高斯特征"""
def __init__(self, N, width_factor=2):
self.N = N
self.width_factor = width_factor
@staticmethod
def _gauss_basis(x, y, width, axis=None):
arg = (x - y) / width
return np.exp(-0.5 * np.sum(arg ** 2, axis))
def fit(self, X, y=None):
#在数据区间内创建N个高斯分布中心
self.centers_ = np.linspace(X.min(), X.max(), self.N)
self.width_ = self.width_factor * (self.centers_[1] - self.centers_[0])
return self
def transform(self, X):
return self._gauss_basis(X[:, :, np.newaxis], self.centers_,
self.width_, axis=1)
gauss_model = make_pipeline(GaussianFeatures(20), LinearRegression())
gauss_model.fit(x[:, np.newaxis], y)
y_fit = gauss_model.predict(x_fit[:, np.newaxis])
plt.scatter(x, y)
plt.plot(x_fit, y_fit)
plt.xlim(0, 10)
当每个位置上的基函数的系数变化幅度很大时,通常来说拟合函数出现了过拟合的现象:相邻基函数的系数相互抵消。这显然是有问题的,如果对较大的模型参数进行惩罚,从而抑制模型剧烈波动,应该就可以解决这个问题了。这个惩罚机制成为正则化,有几种不同的表现形式
岭回归的处理方法是对模型系数平方和( L2范数 L 2 范 数 )进行惩罚,模型拟合的惩罚项为: P=α∑Nn−1θ2n P = α ∑ n − 1 N θ n 2 ,其中, α α 是一个自由参数,用来控制惩罚的力度。
#写一个绘图的函数
def basis_plot(model, title=None):
fig, ax = plt.subplots(2, sharex=True)
model.fit(x[:, np.newaxis], y)
ax[0].scatter(x, y)
ax[0].plot(x_fit, model.predict(x_fit[:, np.newaxis]))
ax[0].set(xlabel='x', ylabel='y', ylim=(-1.5, 1.5))
if title:
ax[0].set_title(title)
ax[1].plot(model.steps[0][1].centers_, model.steps[1][1].coef_)
ax[1].set(xlabel='basis location', ylabel='coefficient', xlim=(0, 10))
#岭回归
from sklearn.linear_model import Ridge
model = make_pipeline(GaussianFeatures(30), Ridge(alpha=0.1))
basis_plot(model, title='Ridge Regression')
参数 α α 是控制最终模型复杂度的关键。如果 α→0 α → 0 ,那么模型就恢复的到标准线性回归结果,如果 α→∞ α → ∞ 那么所有模型响应都会被压制。岭回归的一个重要优点是,它可以非常高效的计算——因此相比原始的线性回归模型,机会没有消耗更多的计算资源。
另外一种常用的正则化被称为Lasso,其处理方法是对模型系数绝对值的和( L1范数 L 1 范 数 )进行惩罚:
P=α∑Nn−1|θn| P = α ∑ n − 1 N | θ n | ,虽然它在形式上非常接近岭回归,但是其结果与岭回归差别很大。例如,由于其几何特性,Lasso正则化倾向于构建稀疏模型,也就说,它更喜欢将模型系数设置为0.
from sklearn.linear_model import Lasso
model = make_pipeline(GaussianFeatures(30), Lasso(alpha=0.001))
basis_plot(model, title='Lasso Regression')
通过Lasso回归惩罚, 大多数基函数的系数都变成了0,所以模型变成了原来基函数的一小部分。与岭回归正则化类似,参数 α α 控制惩罚力度,可以通过交叉检验来确定。