上一篇文章讲述了梯度下降法的数学思想,趁热打铁,这篇博客笔者将使用梯度下降法完成多元线性回归,话不多说,直接开始。
我们假设我们的目标函数是长这样的:
其中的是我们认为对输出产生影响的输入值,而则是我们要求的参数,也就是各个x的权值。需要指出的是,取值为1,也就是式中的第一项代表的是偏置值。是我们根据我们的输入值计算得到的预测输出值。我们如何能找到能使这个预测值最贴近实际值的参数呢?我们引入cost function(代价函数),这里使用mse:
这个式子中的分别指的是我们的已知的输入数据(矢量,一个对应了),对应的输出数据,m是数据的条数,则是输入为情况下按照我们假设的目标函数计算得到的预测值。这个式子的含义就是求得预测值与实际值差的平方和后再求其均值,2则是为了求导后得式子更加简洁而引入的,并不影响结果。很显然,代价函数值越小代表我们的预测值与实际值就越接近,代价函数值取得最小时我们所取的参数就是我们要找的最优参数。
接着我们需要创建迭代序列:
式中的决定了我们的迭代步长,也就是学习率。这个式子要求我们要对代价函数中的每个参数都求偏导,得到下一次我们应该取的参数,这样迭代下去,代价函数会逐渐减小逼近局部极小值或者全局最小值,我们需要给这个迭代序列设置一个迭代次数,以免出现一直迭代的情况。
接下来我们按照代价函数求出对各的偏导,并代入上式:
这样我们就得到了每个参数的迭代方程。到此理论分析结束,笔者将python实现代码贴出,供交流学习。
本次用到的训练集是广告收入场景:当我们已知广告投入(一共三种渠道,分别是TV,radio,newspaper)的情况下,预测销售量。
数据文件格式如图:
import numpy as np
import pandas as pd
# 读入数据
data = pd.read_csv('D:/Advertising.csv')
# 学习率alpha
lr = 0.00001
# 参数
theta0 = 0
theta1 = 0
theta2 = 0
theta3 = 0
# 最大迭代次数
epochs = 1000
#假设目标函数
def h_predict(theta0, theta1, theta2, theta3, x_data, i):
h_pre=(theta0 + theta1 * x_data.iloc[i,0] + theta2*x_data.iloc[i,1] + theta3*x_data.iloc[i,2])
return h_pre
# 最小二乘法
def compute_error(theta0, theta1, theta2, theta3, x_data, y_data):
totalError = 0
for i in range(0, len(x_data)):
totalError += (h_predict(theta0, theta1, theta2, theta3, x_data, i)-y_data[i]) ** 2
return totalError / float(len(x_data))
def gradient_descent_runner(x_data, y_data, theta0, theta1, theta2, theta3, lr, epochs):
# 计算总数据量
m = float(len(x_data))
# 循环epochs次
for i in range(epochs):
theta0_grad = 0
theta1_grad = 0
theta2_grad = 0
theta3_grad = 0
# 求偏导
for j in range(0, len(x_data)):
theta0_grad += (1/m) * (h_predict(theta0, theta1, theta2, theta3, x_data, j) - y_data.iloc[j])
theta1_grad += (1/m) * x_data.iloc[j,0] * (h_predict(theta0, theta1, theta2, theta3, x_data, j) - y_data.iloc[j])
theta2_grad += (1/m) * x_data.iloc[j,1] * (h_predict(theta0, theta1, theta2, theta3, x_data, j) - y_data.iloc[j])
theta3_grad += (1/m) * x_data.iloc[j,2] * (h_predict(theta0, theta1, theta2, theta3, x_data, j) - y_data.iloc[j])
# 更新b和k
theta0 = theta0 - (lr*theta0_grad)
theta1 = theta1 - (lr*theta1_grad)
theta2 = theta2 - (lr*theta2_grad)
theta3 = theta3 - (lr*theta3_grad)
#print(compute_error(theta0, theta1, theta2, theta3, x_data, y_data))
return theta0, theta1, theta2 ,theta3
#计算初始时代价函数值
print("Starting theta0 = {0}, theta1 = {1}, theta2 = {2}, theta3 = {3}, error = {4}".
format(theta0, theta1, theta2, theta3, compute_error(theta0, theta1, theta2, theta3, x_data, y_data)))
print("Running...")
theta0, theta1, theta2, theta3 = gradient_descent_runner(x_data, y_data, theta0, theta1, theta2, theta3, lr, epochs)
#经过epochs次迭代后代价函数值
print("After {0} iterations theta0 = {1}, theta1 = {2}, theta2 = {3}, theta3 = {4}, error = {5}".
format(epochs, theta0, theta1, theta2, theta3, compute_error(theta0, theta1, theta2, theta3, x_data, y_data)))
运行结果如图:
可以看到error值从最开始的223变成了4,最后我将第一行数据带入验证:
与原数据的22.1几乎一样。
值得一提的是,笔者在写好程序后一度以为自己的公式实现出了问题,因为学习率选取过大,导致步长过大,梯度下降发散,error值不断增大,后来经过仔细分析发现是学习率导致的,在改小后程序就运行正常了。