python梯度下降法实现线性回归_机器学习系列——利用梯度下降法及Python手动实现线性回归...

1. 引言

线性回归是机器学习的基础,在日常使用中,我们经常调用函数来求解参数,这样虽然方便,但在学习初期不能加深对模型及代码的理解。本文则讲述了梯度下降法(包括Adagrad法)求解线性回归的理论以及如何用Python实现,最后应用到 diabetes 数据集,并将结果与 sklearn 中的API进行对比。

2. 理论基础

2.1 最小二乘法

线性回归模型可表示为:

(严格地讲,这里的$w$应该用$\hat{w}$)

其中

维列向量。

维的列向量,之所以多了一个维度,是因为我们把常数项参数

也放在了

里。即

。相应的,

也多了一个维度,该矩阵的第一列全为1。如果无法理解这种形式的表达,可以将矩阵在草稿纸上写出来,就看得明白了,这样表达能方便后续求解。

求解线性回归最简单的算法是最小二乘法,即找到一组参数,使模型的损失函数(残差平方和)最小化,用数学模型表示就是:

写成矩阵的形式能够方便求梯度,可能下面这种形式是大家更熟悉的最小二乘法形式:

其中

是第

个个体的自变量向量,

是向量的内积。这种形式是大家更为熟悉的最小二乘法形式。

2.2 梯度

上文提到,最小二乘法的损失函数为:

这是一个标量,而

是一个向量,标量对向量求导,其实就是该标量对向量每一个元素求导,再组合成列向量的形式。用数学表达就是:

equation?tex=%5Cnabla+%5Cmathrm%7BL%7D%3D%5Cnabla+%5Cmathrm%7BL%7D%5Cleft%28w_%7B0%7D%2C+w_%7B1%7D%2C+w_%7B2%7D%2C+%5Cldots%2C+w_%7Bm%7D%5Cright%29%3D%5Cleft%28%5Cbegin%7Barray%7D%7Bc%7D+%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%7D%7B%5Cpartial+w_%7B0%7D%7D+%5C%5C+%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%7D%7B%5Cpartial+w_%7B1%7D%7D+%5C%5C+%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%7D%7B%5Cpartial+w_%7B2%7D%7D+%5C%5C+%5Cdots+%5C%5C+%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%7D%7B%5Cpartial+w_%7Bm%7D%7D+%5Cend%7Barray%7D%5Cright%29

如何求解

呢?将损失函数

展开:

equation?tex=%5Cbegin%7Baligned%7D+%5Cmathrm%7BL%7D%28w%29%26%3D%28Y%5ET-w%5E%7BT%7DX%5E%7BT%7D%29%28Y-Xw%29+%5C%5C+%26%3DY%5E%7BT%7DY-Y%5E%7BT%7DXw-w%5E%7BT%7DX%5E%7BT%7DY%2Bw%5E%7BT%7DX%5E%7BT%7DXw+%5C%5C+%26%3DY%5E%7BT%7DY-Y%5E%7BT%7DXw-%28Y%5E%7BT%7DXw%29%5E%7BT%7D%2Bw%5E%7BT%7DX%5E%7BT%7DXw+%5C%5C+%26%3DY%5E%7BT%7DY-2Y%5E%7BT%7DXw%2Bw%5E%7BT%7DX%5E%7BT%7DXw+%5Cend%7Baligned%7D

利用矩阵求导法则:

equation?tex=%5Cbegin%7Baligned%7D+%5Cnabla+%5Cmathrm%7BL%7D%28w%29%26%3D%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%28w%29%7D%7B%5Cpartial%7Bw%7D%7D+%5C%5C+%26%3D%5Cfrac%7B%5Cpartial+Y%5E%7BT%7DY%7D%7B%5Cpartial%7Bw%7D%7D-%5Cfrac%7B%5Cpartial+2Y%5E%7BT%7DXw%7D%7B%5Cpartial%7Bw%7D%7D%2B%5Cfrac%7B%5Cpartial+w%5E%7BT%7DX%5E%7BT%7DXw%7D%7B%5Cpartial%7Bw%7D%7D+%5C%5C+%26%3D0-2X%5E%7BT%7DY%2B2X%5E%7BT%7DXw+%5C%5C+%26%3D2X%5E%7BT%7DXw-2X%5E%7BT%7DY+%5Cend%7Baligned%7D

此时,如果我们令

就可以求得

的解析解,即:

但是在梯度下降法中,我们当然不能直接带这个公式算,因为这个公式只对线性回归模型成立,对其他模型不成立,而梯度下降则是对各种模型都能使用。下面讲述梯度下降法的原理。

2.3 梯度下降

什么是梯度?从几何上解释,沿着梯度方向,函数上升的最快。可以类比二次函数的导数,二次函数在某一点上升最快的方向就是那一点切线的方向。这段话另一面就是说逆着梯度方向,函数下降的最快。梯度下降法的逻辑就是:我任意给定一个初始参数向量

,算出损失函数值,这个值肯定不是最小的,但是如果我算出

这一点的梯度

,那么我反着这个梯度走,就可以减小损失函数值,如果我不停重复这个过程,就能达到一个最小值。

这一段可以结合台湾大学李宏毅老师的机器学习课程理解。也可以参考这个博文。 用数学过程表示为:

equation?tex=%5Cbegin%7Baligned%7D+%26w%5E1%3Dw%5E0-%5Ceta+%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%28w%29%7D%7B%5Cpartial%7Bw%7D%7D_%7Bw%3Dw%5E0%7D+%5C%5C+%26w%5E2%3Dw%5E1-%5Ceta+%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%28w%29%7D%7B%5Cpartial%7Bw%7D%7D_%7Bw%3Dw%5E1%7D+%5C%5C+%26w%5E3%3Dw%5E2-%5Ceta+%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%28w%29%7D%7B%5Cpartial%7Bw%7D%7D_%7Bw%3Dw%5E2%7D+%5C%5C+%26...+%5C%5C%5C%5C+%26w%5E%7Bi%2B1%7D%3Dw%5Ei-%5Ceta+%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%28w%29%7D%7B%5Cpartial%7Bw%7D%7D%7C_%7Bw%3Dw%5Ei%7D+%5C%5C+%26if+%5C+%5C+%28%5Cfrac%7B%5Cpartial+%5Cmathrm%7BL%7D%28w%29%7D%7B%5Cpartial%7Bw%7D%7D_%7Bw%3Dw%5Ei%7D%3D%3D0%29+%5C+%5C+then+%5C+%5C+stop%3B+%5Cend%7Baligned%7D

其中

是学习率,该值是手动设置的参数,如果过大,可能会学习过快错过最低点,如果过小,会使学习速度太慢。实际操作中,当梯度等于零的时候循环结束不太现实,因为不可能达到零,因此更多地是大量地迭代,去逼近解析值。

这就是最经典的梯度下降法,可以看到梯度下降法的一大缺点就是固定的学习率,但一个常识是:通常是随着参数的更新越来越小的。这很容易理解,学习越好的同学越可以少努力学习,学习越差的同学则应该更努力学习。有没有什么办法能让我们随着迭代更新

呢?接下来我们介绍Adagrad。

2.4 Adagrad

Adagrad的过程表示如下:

equation?tex=%5Cbegin%7Barray%7D%7Bll%7D+w%5E%7B1%7D+%5Cleftarrow+w%5E%7B0%7D-%5Cfrac%7B%5Ceta%5E%7B0%7D%7D%7B%5Csigma%5E%7B0%7D%7D+g_%7B0%7D%5E%7B0%7D+%5Cquad+%5Csigma%5E%7B0%7D%3D%5Csqrt%7B%5Cleft%28g%5E%7B0%7D%5Cright%29%5E%7B2%7D%7D+%5C%5C+w%5E%7B2%7D+%5Cleftarrow+w%5E%7B1%7D-%5Cfrac%7B%5Ceta%5E%7B1%7D%7D%7B%5Csigma%5E%7B1%7D%7D+g%5E%7B1%7D+%5Cquad+%5Csigma%5E%7B1%7D%3D%5Csqrt%7B%5Cfrac%7B1%7D%7B2%7D%5Cleft%5B%5Cleft%28g%5E%7B0%7D%5Cright%29%5E%7B2%7D%2B%5Cleft%28g%5E%7B1%7D%5Cright%29%5E%7B2%7D%5Cright%5D%7D+%5C%5C+w%5E%7B3%7D+%5Cleftarrow+w%5E%7B2%7D-%5Cfrac%7B%5Ceta%5E%7B2%7D%7D%7B%5Csigma%5E%7B2%7D%7D+g%5E%7B2%7D+%5Cquad+%5Csigma%5E%7B2%7D%3D%5Csqrt%7B%5Cfrac%7B1%7D%7B3%7D%5Cleft%5B%5Cleft%28g%5E%7B0%7D%5Cright%29%5E%7B2%7D%2B%5Cleft%28g%5E%7B1%7D%5Cright%29%5E%7B2%7D%2B%5Cleft%28g%5E%7B2%7D%5Cright%29%5E%7B2%7D%5Cright%5D%7D+%5C%5C+%5Cdots+%5C%5C+w%5E%7Bt%2B1%7D+%5Cleftarrow+w%5E%7Bt%7D-%5Cfrac%7B%5Ceta%5E%7Bt%7D%7D%7B%5Csigma%5E%7Bt%7D%7D+g%5E%7Bt%7D+%5Cquad+%5Csigma%5E%7Bt%7D%3D%5Csqrt%7B%5Cfrac%7B1%7D%7Bt%2B1%7D+%5Csum_%7Bi%3D0%7D%5E%7Bt%7D%5Cleft%28g%5E%7Bi%7D%5Cright%29%5E%7B2%7D%7D+%5Cend%7Barray%7D

其中

是参数向量,

表示第$t$次更新参数,

是之前所有梯度均方根。

表示损失函数对

的偏微分,即梯度

需要注意,由于

是一个向量,式子中

或者

都是对每向量每一个元素进行计算。

都有

,因此可以约分,化解后的更新过程为:

在计算机编程中,为了防止出现分母为零的情况,更新过程下的分母一般会加上一个非常小的值

,即:

3. 代码实现

本部分展示梯度下降法求解多元线性回归的Python代码,结合sklearn中的diabetes数据集为例进行简单的训练,同时与用sklearn提供的linear_model得到的结果进行对比。

import numpy as np

import pandas as pd

def grad_desc(X, y, learning_rate=0.01, epochs=1000, eps=0.0000000001, measure='gd'):

'''This funchtion is used to calculate parameters of linear regression by gradient descend method\n:param X: sample matrix, shape(n_samples, n_features)\n:param y: matrix-like, shape (n_samples, 1)\n:param learning_rate: learning rate, 0.01 by default\n:param epochs: times of iteration, 1000 by default\n:param eps: the small positive number used in Adagrad prevent zero denominator\n:param return: the parameters of linear regression\n:param gd_type: measures to do gradient descend, can choose 'gd' or 'Adagrad'\n'''

n = X.shape[0] # 样本数量

dim = X.shape[1] + 1 # 特征数量,+1是因为有常数项

x = np.concatenate((np.ones([n, 1]), X), axis = 1).astype(float)

# 同样由于有常数项,x矩阵需要多加一列1

y = np.matrix(y).reshape(-1, 1).astype(float) # y转化为列向量,方便矩阵运算

w = np.zeros([dim, 1]) # 初始化参数

## 常规的梯度下降法

if measure == 'gd':

for i in range(epochs):

loss = np.sum(np.power(np.dot(x, w) - y, 2))/n

if (i % 100 == 0):

print(str(i) + ":" + str(loss))

gradient = 2 * np.dot(x.transpose(), np.dot(x, w) - y)/n

w = w - learning_rate * gradient

## Adagrad法

if measure == 'Adagrad':

adagrad = np.zeros([dim, 1])

for i in range(epochs):

loss = np.sum(np.power(np.dot(x, w) - y, 2))/n

if (i % 100 == 0):

print(str(i) + ":" + str(loss))

gradient = 2 * np.dot(x.transpose(), np.dot(x, w) - y)/n

adagrad += np.square(gradient)

w = w - learning_rate * gradient / np.sqrt(adagrad + eps)

return w

def predict(w, test_X, test_y):

'''This function is use to calculate the MSE of a given linear regression model\n:param w: matrix-like, shape (n_features+1, 1)\n:test_X: test sample matrix, shape(n_samples, n_features)\n:test_y: true targets of test set, matrix-like, shape (n_samples, 1)\n'''

test_X = np.concatenate((np.ones([test_X.shape[0], 1]), test_X), axis = 1).astype(float)

test_y = np.matrix(test_y).reshape(-1, 1).astype(float)

predict_y = np.dot(test_X, w)

mse = np.sqrt(np.average(np.square(predict_y - test_y)))

return mse, predict_y

if __name__ == '__main__':

# 导入deabetes数据集

from sklearn.datasets import load_diabetes

from sklearn.utils import shuffle

diabetes = load_diabetes()

X = diabetes.data

y = diabetes.target

# 划分训练集和测试集

offset = int(X.shape[0] * 0.9)

X_train, y_train = X[:offset], y[:offset]

X_test, y_test = X[offset:], y[offset:]

y_train = y_train.reshape((-1,1))

y_test = y_test.reshape((-1,1))

print('X_train=', X_train.shape)

print('X_test=', X_test.shape)

print('y_train=', y_train.shape)

print('y_test=', y_test.shape)

# 使用grad_desc()函数求解

w = grad_desc(X_train, y_train, learning_rate=0.03,epochs=10000, measure='gd')

print('使用grad_desc()函数的MSE:', predict(w, X_test, y_test)[0])

# 利用sklearn的线性回归api来做对比

import sklearn.linear_model as sl

import sklearn.metrics as sm

model = sl.LinearRegression()

model.fit(X_train, y_train)

pred_y = model.predict(X_test)

print('使用sklearn线性模型函数的MSE:', sm.mean_absolute_error(y_test, pred_y))

最终得到结果:

使用grad_desc()函数的MSE:41.768875313581226

使用sklearn线性模型函数的MSE: 32.085174947765125

可以看到,我们手动实现的梯度下降法表现明显不如api提供的好,这也是很正常的事情,因为本文所采用的梯度下降法是最简单的两种:普通的GD法和Adagrad。而梯度下降的更新方法有许多提高措施,如随机梯度下降法(SGD),使用动量的随机梯度下降法,RMSProp法等,详见博文梯度下降。有兴趣读者可在本文基础上加上这些提高措施,改善性能。

4. 总结

本文主要讲解了梯度下降法求解线性回归模型的理论与Python代码实现,最终效果一般,但是梯度下降的思想和方法是最重要的,因为深度学习的基础就是梯度下降。

作为一个金融科班出身的学生,我深知光学金融学或者经济学的知识是远远不够的,现在各个学科都在鼓励交叉,最近几年金融科技也逐渐收到社会的关注,因此我在硕士课程之余经常自学机器学习、深度学习的内容。一开始是只企图掌握API的使用(即掉包、调参侠),但我逐渐发现,许多有创造的论文都是对经典的模型有一些改动,加上在金融领域的应用,才能成为优秀的论文。因此我又从头开始,试图能从源码上理解基础模型,既能加深对模型的印象,也会对未来创作有帮助。

希望这篇博文能帮助到,欢迎对深度学习、机器学习感兴趣的金融、经济学领域同学与我交(P)流(Y)。

你可能感兴趣的:(python梯度下降法实现线性回归_机器学习系列——利用梯度下降法及Python手动实现线性回归...)