【机器学习】线性回归:动手实现一个线性回归模型

2019-03-04  本文已影响3人  John_Tsemin

已有知识:

  1. 线性回归(统计学)
  2. 线性回归(机器学习)

回归问题简单说就是给出一组特征,模型需要给出一个回答,这个回答是个连续的数值,也就是说模型的输出应该是个实数。
因此回归问题的模型是一个实函数。

线性回归则是用线性函数 f(x_1,x_2,...,x_n) = x_1 w_1 + x_2 w_2+... +x_n w_n + b 来建立模型。

写成向量形式就是(一维向量均是列向量):
f(\boldsymbol{X}) = \boldsymbol{X}^T W + \boldsymbol{b} \tag{1}

回归本身是个估计问题,没有对错之分,所以需要一个评价标准来评价一个回归模型的好坏。常见的评价标准有MSE,SAE,SSE等

误差度量标准 优缺点
SSE(误差平方和) 样本多时数值会累加到很大, 处理起来不便
SAE(绝对误差和) 绝对值性质很差,难以进行数学运算
MSE(均方误差) 数值不爆炸,数学性质好(凸性)

记训练集第i个样本为X_i, 对应标签为Y_i
因此,此处选择MSE作为模型好坏的度量标准。

MSE = \sum_{i \in 样本集} {(W X_i + b - Y_i)}^2

于是问题转化为优化问题:
\min_{W, b \in \mathcal{R}} MSE

要求多元函数MSE(W, b )的最小值,一个必要不充分条件是求出某点使得其各个方向偏导数均为零,又由于这个函数性质良好(参见凸优化相关内容),于是这就成了充要条件。因此根据这个条件可以求得方程组并且解之,实际上一元的线性回归就是这么做的,初中课本上还有一元线性回归的公式,多元的情况类似,但是运算更复杂,在此不赘述。

为了简化运算,将式(1)写成完全的矩阵形式:
f(\boldsymbol{X}) =( \boldsymbol{X}^T, 1 ) \begin{bmatrix} W \\ \boldsymbol{b} \end{bmatrix}

根据线性代数的结论,倘若有n个样本, label对应为Y,上式成为

AW = Y
其中A为:
W为:

参见wikipedia,或者其他文献,keywords:广义逆矩阵

它的误差的二范数(就是MSE)极小的解为A = A^{\dagger}Y
A^{\dagger}A的广义逆矩阵。

首先导入需要用到的库:


import numpy as np

# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D  # noqa: F401 unused import

import matplotlib.pyplot as plt
import numpy as np

然后随机生成一些数据作为样本集:

#特征数量和样本数量
feature_num = 2
num_examples = 100

#预设的w和b
preset_w =np.array([2.2, 7.5])

preset_b = 2.8

#假数据
fake_data_X = np.random.rand(num_examples, feature_num)


#通过预设的w和b算出对应的labels

labels = fake_data_X.dot( preset_w.reshape(2,1) )+ preset_b



#给labels加个随机误差
labels += np.random.normal(loc=0, scale=0.01,size=(num_examples, 1))



#开始训练模型
#对应上述几个公式来看
#
fake_data_X_with_one = np.column_stack( (fake_data_X,  np.ones((num_examples, 1))) )

persudo_invert_X_with_one = np.linalg.pinv(fake_data_X_with_one)

model_params = persudo_invert_X_with_one.dot(labels)

#输出模型参数
model_params
array([[2.20471427],
       [7.50252716],
       [2.79889165]])

对比可以发现算出的模型参数和预设值相差不算太大。

绘制一下原始数据和回归结果的图形直观感受一下:


output_8_0.png

这3D图太难操作了,画出来还这么抽象........可以看到我们的样本点分布在回归曲面的附近就够了。



fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.scatter(fake_data_X[:,0], fake_data_X[:,1], labels, color='r')


u = np.linspace(0, 1, 15)
v = np.linspace(0, 1, 15)
x, y = np.meshgrid(u, v )
xy1 = np.column_stack((x.reshape(15*15, 1), y.reshape(15*15, 1), np.ones((15*15, 1)) ))
#通过模型算出回归结果
z = x*model_params[0] + y*model_params[1] + model_params[2]
ax.plot_surface(x, y , z , alpha=0.5, color='b')
ax.view_init(elev=15, 
             azim=45 )

plt.show()
回归结果和原始数据对比

可以看到计算的结果和我们预设的值非常接近。可见我们的结果是有效的。

有些读者肯定不服了,你这怕是是假机器学习,直接用公式就算出来了也算数?训练过程呢?梯度下降呢?

这种方法的确和现代机器学习的原理大相径庭,它通过数学原理直接算出了模型需要的参数,但是它的缺陷也是显然的:它需要所有的数据来参与运算,如果有新的数据到来,它只能推倒重算,无法利用已有的结果。另外它只适用于性质很好(在数学领域研究透彻了)的函数。

作为入门的第一课,我只是想借助这个例子说明机器学习的本质:

你假设一个模型 f(x,\sigma) 能够解决问题,但是参数 \sigma 是未知的,你只要用某种方法求得一个误差可接受的参数 \hat{\sigma} 即可。

如果使用成熟的机器学习框架来训练,它的结果会是多少呢?

from sklearn import  linear_model
#创建一个随机梯度下降的线性回归模型并且训练
regress = linear_model.SGDRegressor(max_iter=2000)
regress.fit(fake_data_X,  labels.reshape(1,num_examples)[0])
#输出训练结果
print("线性模型系数W为", regress.coef_)
print("线性模型常数项b为", regress.intercept_)
线性模型系数W为 [2.20029992 7.49111373]
线性模型常数项b为 [2.80727725]

可以看出,随机梯度下降法(SGD)在迭代2000次的情况下得到了一个还算精确的结果。

ref:
[1] 监督学习-线性模型(sklearn)

上一篇 下一篇

猜你喜欢

热点阅读