多元线性回归及其算法实现(梯度下降法)

上一篇文章讲述了梯度下降法的数学思想,趁热打铁,这篇博客笔者将使用梯度下降法完成多元线性回归,话不多说,直接开始。

我们假设我们的目标函数是长这样的:

h_\Theta (x)=\Theta _0x_0+\Theta _1x_1+\Theta _2x_2+\cdot \cdot \cdot +\Theta _nx_n

其中的x_0\cdot \cdot \cdot x_n是我们认为对输出产生影响的输入值,而\Theta _0\cdot \cdot \cdot \Theta _n则是我们要求的参数,也就是各个x的权值。需要指出的是,x_0取值为1,也就是式中的第一项代表的是偏置值h_\Theta (x)是我们根据我们的输入值计算得到的预测输出值。我们如何能找到能使这个预测值最贴近实际值的参数呢?我们引入cost function(代价函数),这里使用mse:

J(\Theta _0,\Theta _1,\cdot \cdot \cdot ,\Theta _n ) = \frac{1}{2m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)})^2

这个式子中的x^{(i)},y^{(i)}分别指的是我们的已知的输入数据(矢量,一个x^{(i)}对应了x_0\cdot \cdot \cdot x_n),对应的输出数据,m是数据的条数,h_\Theta (x^{(i)})则是输入为x_i情况下按照我们假设的目标函数计算得到的预测值。这个式子的含义就是求得预测值与实际值差的平方和后再求其均值,2则是为了求导后得式子更加简洁而引入的,并不影响结果。很显然,代价函数值越小代表我们的预测值与实际值就越接近,代价函数值取得最小时我们所取的参数就是我们要找的最优参数。

接着我们需要创建迭代序列:

\Theta _{j_{new}}=\Theta _{j_{old}}-\alpha \frac{\partial }{\partial \Theta _{j_{old}} }J(\Theta _0,\cdot \cdot \cdot ,\Theta _n)

式中的\alpha决定了我们的迭代步长,也就是学习率。这个式子要求我们要对代价函数中的每个参数\Theta_{j_{old}}都求偏导,得到下一次我们应该取的参数\Theta_{j_{new}},这样迭代下去,代价函数会逐渐减小逼近局部极小值或者全局最小值,我们需要给这个迭代序列设置一个迭代次数,以免出现一直迭代的情况。

接下来我们按照代价函数求出对各\Theta _j的偏导,并代入上式:

\Theta _{0_{new}}=\Theta _{0_{old}}-\alpha\frac{1}{m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)})x_0^{(i)}

\Theta _{1_{new}}=\Theta _{1_{old}}-\alpha\frac{1}{m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)})x_1^{(i)}

\cdot \cdot \cdot \cdot \cdot \cdot

\Theta _{n_{new}}=\Theta _{n_{old}}-\alpha\frac{1}{m}\sum_{i=1}^{m}(h_\Theta (x^{(i)})-y^{(i)})x_n^{(i)}

这样我们就得到了每个参数的迭代方程。到此理论分析结束,笔者将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值不断增大,后来经过仔细分析发现是学习率导致的,在改小后程序就运行正常了。

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值