线性回归算法
- 解决回归问题
- 形式简单、易于建模
- 蕴含着机器学习中一些重要的基本思想
- 许多强大的非线性模型的基础
线性模型
给定由 d d d 个属性描述的示例
x = ( x 1 ; x 2 ; x 3 ; . . . ; x d ) x = (x_1;x_2;x_3;...;x_d) x=(x1;x2;x3;...;xd)
其中 x i x_i xi 是 x x x 在第 i i i 个属性上的取值
线性模型试图学得一个通过属性的线性组合来进行预测的函数,即
f ( x ) = a 1 x 1 + a 2 x 2 + . . . + a d x d + b f(x) = a_1x_1 + a_2x_2 + ... + a_dx_d + b f(x)=a1x1+a2x2+...+adxd+b
一般用向量形式写成
f ( x ) = a T x + b f(x) = a^Tx + b f(x)=aTx+b
其中 a = ( a 1 ; a 2 ; . . . ; a d ) a = (a_1;a_2;...;a_d) a=(a1;a2;...;ad). a a a 和 b b b 学得之后,模型就得以确定
数据集 : D = ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x 3 , y 3 ) , . . . . , ( x m , y m ) D={(x_1, y_1),(x_2,y_2),(x_3,y_3),....,(x_m,y_m)} D=(x1,y1),(x2,y2),(x3,y3),....,(xm,ym),
x i = ( x i 1 ; x i 2 ; x i 3 ; . . . ; x i d ) x_i = (x_{i1};x_{i2};x_{i3};...;x_{id}) xi=(xi1;xi2;xi3;...;xid), y i = R y_i=\mathbb{R} yi=R .
其中 x i x_i xi 是 x x x 在第 i i i 个属性上的取值.
简单线性回归
我们先考虑一种最简单的情形:输入属性的数目只有一个。即
D = ( x i , y i ) i = 1 m D = {(x_i, y_i)}_{i=1} ^{m} D=(xi,yi)i=1m
x i ∈ R x_i\in \mathbb{R} xi∈R
线性回归试图学得
f ( x i ) = a x i + b f(x_i) = ax_i + b f(xi)=axi+b, 使得 f ( x i ) ≃ y i f(x_i) \simeq y_i f(xi)≃yi
其中 f ( x i ) f(x_i) f(xi) 可写成 y i ^ \hat{y_i} yi^
如图:
如何确定 a a a 和 b b b 呢? 关键在与如何衡量 y i ^ \hat{y_i} yi^ 与 y y y 之间的差别。如图:
上图所示,
我们先考虑让 y i ^ \hat{y_i} yi^ 与 y y y 之间的差距,当值为 0 时,直线完全拟合数据。我们希望如此,但这是不可能的。
我们又考虑让 y i ^ \hat{y_i} yi^ 与 y y y 之间的绝对值,绝对值的函数并不是处处可导的,所以也不取。
最后,我们选择 y i ^ \hat{y_i} yi^ 与 y y y 差的平方,考虑到全部样本。
目标:找到 a a a 和 b b b,使得 E ( a , b ) = ∑ i = 1 m ( y i − a x i − b ) 2 E(a, b) = \sum_{i=1}^{m}(y_i - ax_i - b)^2 E(a,b)=∑i=1m(yi−axi−b)2 尽可能地小
令损失函数 E ( a , b ) E(a, b) E(a,b) 导后为 0,就能得到最优解
最小二乘法解决:
a = ∑ i = 1 m ( x i − x ˉ ) ( y i − y ˉ ) ∑ i = 1 m ( x i − x ˉ ) 2 a = \frac{\sum_{i=1}^{m}(x_i - \bar{x})(y_i - \bar{y})}{\sum _{i=1}^{m}(x_i - \bar{x})^2} a=∑i=1m(xi−xˉ)2∑i=1m(xi−xˉ)(yi−yˉ)
b = y ˉ − a x ˉ b = \bar{y} - a\bar{x} b=yˉ−axˉ
其中 x ˉ = 1 m ∑ i = 1 m x i \bar{x} = \frac{1}{m}\sum_{i=1}^{m}x_i xˉ=m1∑i=1mxi 为 x x x 的均值
代码实现:简单线性回归
import numpy as np
import matplotlib.pyplot as plt
class LinearRegression1(object):
def __init__(self):
self.a_ = None
self.b_ = None
def fit(self, x_train, y_train):
# 简单线性回归器只能求解单一特征训练数据
assert x_train.ndim == 1, \
"Simple Linear Regressor can only solve single feature training data"
assert len(x_train) == len(y_train), \
"the size of x_train must be to the szie of equal y_train"
x_mean = np.mean(x_train)
y_mean = np.mean(y_train)
num = 0.0
d = 0.0
for x, y in zip(x_train, y_train):
num += (x - x_mean) * (y - y_mean)
d += (x - x_mean) ** 2
self.a_ = num / d
self.b_ = y_mean - self.a_ * x_mean
return self
def predict(self, x_predict):
assert x_predict.ndim == 1, \
"Simple Linear Regressor can only solve single feature training data"
assert self.a_ is not None and self.b_ is not None, \
"the size of x_train must be to the szie of equal y_train"
return np.array([self._predict(x) for x in x_predict])
def _predict(self, x_single):
return self.a_ * x_single + self.b_
def __repr__(self):
return "Simple Linear Regression1()"
if __name__ == '__main__':
x = np.array([1., 2., 3., 4., 5.])
y = np.array([1., 3., 2., 3., 5.])
lrg = LinearRegression1()
lrg.fit(x, y)
y_hat = lrg.predict(x)
plt.scatter(x, y)
plt.plot(x, y_hat, c='r')
plt.show()
如图:
对于上面代码所使用的 for 对单个变量循环来说,我们使用一种更为常用且快速的方法 向量运算。
对于 ∑ i = 1 m ( y i − a x i − b ) 2 \sum_{i=1}^{m}(y_i - ax_i - b)^2 ∑i=1m(yi−axi−b)2
a = ∑ i = 1 m ( x i − x ˉ ) ( y i − y ˉ ) ∑ i = 1 m ( x i − x ˉ ) 2 a = \frac{\sum_{i=1}^{m}(x_i - \bar{x})(y_i - \bar{y})}{\sum _{i=1}^{m}(x_i - \bar{x})^2} a=∑i=1m(xi−xˉ)2∑i=1m(xi−xˉ)(yi−yˉ)
b = y ˉ − a x ˉ b = \bar{y} - a\bar{x} b=yˉ−axˉ
令 ∑ i = 1 m w i ⋅ v i \sum_{i=1}^{m}w_i \cdot v_i ∑i=1mwi⋅vi
其中 w i = ( w 1 ; w 2 ; w 3 ; . . . w m ) w_i = (w_1;w_2;w_3;...w_m) wi=(w1;w2;w3;...wm)
v i = ( v 1 ; v 2 ; v 3 ; . . . v m ) v_i = (v_1;v_2;v_3;...v_m) vi=(v1;v2;v3;...vm)
w ⋅ v w \cdot v w⋅v
num = (x_train - x_mean).dot(y_train - y_mean)
d = (x_train - x_mean).dot(x_train - x_mean)
多元线性回归
更为常见的情形是如开头的数据集 D D D,样本由 d d d 个属性来描述,此时我们试图学得
y i ^ = θ 1 x 1 + θ 2 x 2 + . . . + θ d X d + b \hat{y_i} = \theta_1x_1 + \theta_2x_2 + ... + \theta_dX_d + b yi^=θ1x1+θ2x2+...+θdXd+b
b b b 为 截距 , ( θ 1 ; . . . ; θ d ) (\theta_1;...;\theta_d) (θ1;...;θd) 为 系数 ,将 b b b 写为 θ 0 \theta_0 θ0,并将 x 0 x_0 x0 设为 1,得到
y i ^ = θ 0 x 0 + θ 1 x 1 + θ 2 x 2 + . . . + θ d X d \hat{y_i} = \theta_0x_0 + \theta_1x_1 + \theta_2x_2 + ... + \theta_dX_d yi^=θ0x0+θ1x1+θ2x2+...+θdXd
所以我们的目标为
找到 θ 0 , θ 1 , . . . , θ d \theta_0, \theta_1, ..., \theta_d θ0,θ1,...,θd,使得 ∑ i = 0 m ( y i − y i ^ ) 2 \sum_ {i=0} ^{m}(y_i - \hat{y_i})^2 ∑i=0m(yi−yi^)2,尽可能地小
将 θ = ( θ 0 ; θ 1 ; . . . ; θ d ) \theta = (\theta_0;\theta_1;...;\theta_d) θ=(θ0;θ1;...;θd), θ \theta θ 是一个一维向量,我们习惯把一维向量看为列向量 表示
对此,我们想要使得 θ \theta θ 点乘 X X X ,得到
f ( x i ) = θ T x i f(x_i) = \theta^Tx_i f(xi)=θTxi
y ^ = θ T ⋅ X \hat{y} = \theta^T \cdot X y^=θT⋅X
那么对于多元函数公式有
θ = ( y − X θ ) T ( y − X θ ) \theta = (y - X\theta)^T(y - X\theta) θ=(y−Xθ)T(y−Xθ)
令 ( y − X θ ) T ( y − X θ ) (y - X\theta)^T(y - X\theta) (y−Xθ)T(y−Xθ) 尽可能地小,得到
θ = ( X T X ) − 1 X T y \theta = (X^TX)^{-1}X^Ty θ=(XTX)−1XTy
问题:时间复杂度高: O ( n 3 ) ( 优 化 O ( n 2.4 ) ) O(n^3)(优化 O(n^{2.4})) O(n3)(优化O(n2.4))
优点:不需要对数据做归一化处理
代码:
import numpy as np
from blog_metrics import r2_score
import matplotlib.pyplot as plt
import sklearn.datasets as datasets
from sklearn.model_selection import train_test_split
class LinearRegression(object):
def __init__(self):
self.coef_ = None
self.interception_ = None
self._theta = None
def fit_normal(self, X_train, y_train):
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must be equal to the size of y_train"
X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
# np.c_[np.ones((len(X), 1)), X]
self._theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train)
self.interception_ = self._theta[0]
self.coef_ = self._theta[1:]
return self
def predict(self, X_predict):
assert self.interception_ is not None and self.coef_ is not None, \
"must fit before predict"
assert X_predict.shape[1] == len(self.coef_), \
"the feature number of X_predict must be equal to X_predict"
X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
return X_b.dot(self._theta)
def score(self, X_test, y_test):
y_predict = self.predict(X_test)
return r2_score(y_test, y_predict)
def __repr__(self):
return "Linear Regression"
if __name__ == '__main__':
bost = datasets.load_boston()
x = bost.data
y = bost.target
X = x[y < 50]
y = y[y < 50]
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
lrg = LinearRegression()
lrg.fit_normal(X_train, y_train)
# 系数
print(lrg.coef_)
# 截距
print(lrg.interception_)
# R^2 值
print(lrg.score(X_test, y_test))
拓展
一类机器学习算法的基本思路
我们所谓的建模就是指 找到一个模型最大程度地拟合我们的数据,在线性模型中就是直线方程。最大拟合数据其本质就是找到一个函数。在 ∑ i = 1 m ( y i − a x i − b ) 2 \sum_{i=1}^{m}(y_i - ax_i - b)^2 ∑i=1m(yi−axi−b)2 中就是指 损失函数(loss function),度量出模型没有拟合出我们的样本的一部分。另一种时度量出拟合的部分,称 效用函数(utility function)
近乎所有参数学习算法都是这样的套路,如
线性回归、多项式回归、路基回归、SVM、神经网络…
性能度量
对模型的泛化性能进行评估,不仅需要有效可行的实验估计方法,还要有衡量模型泛化能力的评价标准,这就是性能度量(performance measure)
性能度量反映了任务需求,再对比不同的模型的能力时,使用不同的性能度量往往会导致不同的评判结果。这意味着模型的好坏是相对的,什么样的模型是好的,不取决于算法和数据,还决定于任务需求。
在预测任务中:
数据集: D = ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x 3 , y 3 ) , . . . . , ( x m , y m ) D={(x_1, y_1),(x_2,y_2),(x_3,y_3),....,(x_m,y_m)} D=(x1,y1),(x2,y2),(x3,y3),....,(xm,ym)
y i y_i yi 是样本 x i x_i xi 的真实标记。要评估模型的 性能,就要把预测结果 y ^ \hat{y} y^ 与 y y y 进行比较。
回归任务最常用的性能度量是 均方误差(MSE)
均方误差(MSE)
1 m ∑ i = 1 m ( y i − y i ^ ) 2 \frac{1}{m}\sum_{i=1}^{m}(y_i - \hat{y_i})^2 m1∑i=1m(yi−yi^)2
均方根误差
1 m ∑ i = 1 m ( y i − y i ^ ) 2 = M S E \sqrt{\frac{1}{m}\sum_{i=1}^{m}(y_i - \hat{y_i})^2} = \sqrt{MSE} m1∑i=1m(yi−yi^)2=MSE
平方绝对误差
1 m ∑ i = 1 m ∣ y i − y i ^ ∣ \frac{1}{m}\sum_{i=1}^{m}\left | y_i - \hat{y_i} \right | m1∑i=1m∣yi−yi^∣
虽然平方绝对误差不能用来进行预测,但是它可以用来评测算法的好坏。
R Squared 广泛使用
R 2 = 1 − ∑ i ( y i ^ − y i ) 2 ∑ i ( y ˉ − y i ) 2 R^2 = 1 - \frac{\sum_i (\hat{y_i} - y_i)^2}{\sum_i(\bar{y} - y_i)^2} R2=1−∑i(yˉ−yi)2∑i(yi^−yi)2
∑ i ( y i ^ − y i ) 2 \sum_i (\hat{y_i} - y_i)^2 ∑i(yi^−yi)2 使用我们的模型预测产生的错误
∑ i ( y ˉ − y i ) 2 \sum_i(\bar{y} - y_i)^2 ∑i(yˉ−yi)2 使用 y = y ˉ y = \bar{y} y=yˉ 预测产生的错误, y y y 的方差
-
R 2 R^2 R2 <= 1
R 2 R^2 R2 越大越好,当我们的预测模型不犯任何错误时, R 2 R^2 R2 得到最大值 1
-
R 2 = 0 R^2 = 0 R2=0
当我们的模型等于基准模型时, R 2 R^2 R2 为 0
-
R 2 R^2 R2 < 0
我们的数据很有可能不存在任何线性关系
def mean_squared_error(y_true, y_predict):
assert len(y_true) == len(y_predict), "the size of y_true must be equal to the size of y_predict"
return np.sum((y_true - y_predict) ** 2) / len(y_true)
def root_mean_squared_error(y_true, y_predict):
return sqrt(mean_squared_error(y_true, y_predict))
def mean_absolute_error(y_true, y_predict):
assert len(y_true) == len(y_predict), "the size of y_true must be equal to the size of y_predict"
return np.sum(np.absolute(y_true - y_predict)) / len(y_true)
def r2_score(y_true, y_predict):
return 1 - mean_squared_error(y_true, y_predict) / np.var(y_true)
参考:
- 西瓜书-第三章 线性模型
- imooc python3入门机器学习https://coding.imooc.com/class/chapter/169.html#Anchor