python梯度下降算法算法及注意的细节

首先要明白梯度下降算法我们能干什么?什么时候可以用到梯度下降?
我们先进行两个例子,一个二维函数问题,一个三维的现实问题:
二维函数:比如我们要求

在这里插入图片描述
三维现实问题:你站在一个一个的山中,你想要的去谷底,那怎么样办才能最快的到达局部谷底呢?要沿着哪个方向进行走动呢?
然后带着问题去思考梯度下降算法。

首先你会有一个函数,比如这个函数有两个参数的函数J(θ_1,θ_2),我们需要一种算法去最小化这个函数
这里我们就需要用到梯度下降算法。
梯度下降算法的步骤:
1.我们开始会有初始数值θ_1,θ_2,一般来说这两个数值为多少没关系,一般可以为0
2.对损失函数求所有 θ 的偏导(梯度): ∂J(θ)/∂θ_j
3.使用样本数据更新假设函数的不停的同时更新θ_1,θ_2,一定要同时更新,后边会说为什么要同时更更新这个两个变量,更新的参数公式为:在这里插入图片描述(α是为更新步长(调整参数的灵敏度,灵敏度太高容易振荡,灵敏度过低收敛缓慢))
4.直到找到J函数的最小值或者局部最小值

在这里插入图片描述
比如我们在上图这个点,我想要快速下山,我要怎么办呢?我会根据这个点的变化率最大的方向(也就是偏导数)出发,走一步就再判断一次,最终会走到下图所示的地方在这里插入图片描述
但是有一个有趣的事情我们会发现,如果我们开始的位置不同的到结果会不同,正如下图所示
在这里插入图片描述
这就是梯度下降算法的特点,可以尽快的找到局部最优解或者全体最优解
注意的细节:
我们需要同时更新我们的θ_1,θ_2
算法中应该如下图的步骤
在这里插入图片描述
而不是这样:
在这里插入图片描述
然后我们思考更新θ_1,θ_2这两个变量的公式,很明显我们要求最低点的时候,偏导数求导是一个正数,但是我们要向下走因此要加负号,第二个求导是负的,我们本身就是要向下走,因此不用加符号,所有最终结果都是在这里插入图片描述
在这里插入图片描述
然后我们再考虑α这个步长:
α太小:如下图所示,如果太小了 就会导致我们的步数越来越多,我们进行迭代的次数就很大,就会很浪费时间
α太大:如下图所示,如果太大了就会导致我们可能会超过最低点进而无法收敛越来越偏离正确的道路,因此我们需要选定合适的α
在这里插入图片描述
大致基本思路就是这样,之后会写一些实际的应用问题

梯度下降算法用于线性回归

批量梯度下降法(BGD):使用所有样本进行计算,慢但准确度好
首先要导入数据和定义初始的参数以及选择合适的步长,如下图

import numpy as np
import matplotlib.pyplot as plt
import random
spaces = [150, 200, 250, 300, 350, 400, 600]
prices = [6450, 7450, 8450, 9450, 11450, 15450,18450]
#prices=[]
#for i in range(0,len(spaces)):
     #prices.append(spaces[i]*prices1[i]/10000)         

spaces, prices = np.array(spaces), np.array(prices)



## theta 初始值
theta0 = 0
theta1 = 0

## 如果步长选择不对,则 theta 参数更新结果会不对
step = 0.0000001

#np.ones()生产一个长度跟spaces一样的数组,且数组元素为1,因为theta0为常数
x_i0 = np.ones((len(spaces)))

定义损失函数和theta 0的损失函数和theta 1的损失函数

# 假设函数
def h(x) :
  return theta0 + theta1 * x

# 损失函数
def calc_error() :
  return np.sum(np.power((h(spaces) - prices),2)) / len(spaces)

# 损失函数偏导数( theta 0)
def calc_delta0() :
  return step * np.sum((h(spaces) - prices) * x_i0) / len(spaces)

# 损失函数偏导数( theta 1)
def calc_delta1() :
  return step * np.sum((h(spaces) - prices) * spaces) / len(spaces)

利用梯度下降算法进行动态更新

def min1():
    return h(spaces[0])

# 循环更新 theta 值并计算误差,停止条件为
#  1. 误差小于某个值
#  2. 循环次数控制
k = 0
min11 = 100
while True :
  delta0 = calc_delta0()
  delta1 = calc_delta1()
  theta0 = theta0 - delta0
  theta1 = theta1 - delta1
  plt.figure(1)
  plt.scatter(theta0, theta1, c='y')
  plt.xlabel('theta0')
  plt.ylabel('theta1')
  plt.figure(3)
  error = calc_error()
  plt.scatter(k,error,c='y')
  plt.xlabel('k')
  plt.ylabel('error')
  # print("delta [%f, %f], theta [%f, %f], error %f" % (delta0, delta1, theta0, theta1, error))
  k = k + 1
  if (k > 500 ) : 
    break


print(" h(x) = %f + %f * x" % (theta0, theta1))
    
# 使用假设函数计算出来的价格,用于画拟合曲线
y_out = h(spaces)
plt.figure(2)
plt.scatter(spaces, prices, c='g')
plt.plot(spaces, y_out, c='b')
plt.xlabel('house space')
plt.ylabel('house price')
plt.show()

细节处理
我们并不是说得到了结果是一条直线就觉得完成了相对应的线性回归,我们需要对损失函数的损失数值进行对比,如果得到如下的图,表示拟合度很高
在这里插入图片描述
在此可以看到损失函数的数值趋近于0代表k只需要200就可以了,当然k越大其误差越小
然后我们可以继续编写随机梯度算法

随机梯度下降法(SGD):每次使用1个样本进行计算,快但准确度欠缺
然后伪代码如下

## theta 初始值
theta0 = 0
theta1 = 0

## 如果步长选择不对,则 theta 参数更新结果会不对
step = 0.0000015

x_i0 = np.ones((len(spaces)))

# 假设函数

def h(x) :
  return theta0 + theta1 * x

# 损失函数
def calc_error() :
  return np.sum(np.power((h(spaces) - prices),2)) / len(spaces)

# 损失函数偏导数( theta 0)
def calc_delta0() :
  return step * (h(i) - prices[i])

# 损失函数偏导数( theta 1)
def calc_delta1() :
  return step * (h(i) - prices[i]) * i 

# 循环更新 theta 值并计算误差,停止条件为
#  1. 误差小于某个值
#  2. 循环次数控制
k = 0

while True :
  i = random.randint(0,6)
  delta0 = calc_delta0()
  delta1 = calc_delta1()
  theta0 = theta0 - delta0
  theta1 = theta1 - delta1
  plt.figure(1)
  plt.scatter(theta0, theta1,c='g')
  plt.xlabel('theta0')
  plt.ylabel('theta1')
  error = calc_error()
  plt.figure(4)
  plt.scatter(k,error,c='y')
  plt.xlabel('k')
  plt.ylabel('error')
  # print("delta [%f, %f], theta [%f, %f], error %f" % (delta0, delta1, theta0, theta1, error))
  k = k + 1
  if (k > 500 ) : 
    break


print(" h(x) = %f + %f * x" % (theta0, theta1))
    
# 使用假设函数计算出来的价格,用于画拟合曲线
y_out1 = h(spaces)
plt.figure(2)
plt.plot(spaces, y_out1)
plt.show()

随机梯度算法的核心就是 用单个样本的数值代替整体样本的数值,优点就是速度快但是精度很欠缺,所以需要多k次的运行
我们依旧画出损失函数跟k的关系,如下图在这里插入图片描述
我们很明显的可以看出来用批量梯度算法是循环200次,而利用随机梯度算法需要进行500次,但是进行的次数快,每次都是选一个样本进行估计,当然我在这次编程中其步数没有统一,因此其参考价值并没有太大的意义。

最后完整的代码如下

import numpy as np
import matplotlib.pyplot as plt
import random
spaces = [150, 200, 250, 300, 350, 400, 600]
prices = [6450, 7450, 8450, 9450, 11450, 15450,18450]
#prices=[]
#for i in range(0,len(spaces)):
     #prices.append(spaces[i]*prices1[i]/10000)         

spaces, prices = np.array(spaces), np.array(prices)



## theta 初始值
theta0 = 0
theta1 = 0

## 如果步长选择不对,则 theta 参数更新结果会不对
step = 0.0000001

#np.ones()生产一个长度跟spaces一样的数组,且数组元素为1,因为theta0为常数
x_i0 = np.ones((len(spaces)))

# 假设函数
def h(x) :
  return theta0 + theta1 * x

# 损失函数
def calc_error() :
  return np.sum(np.power((h(spaces) - prices),2)) / len(spaces)

# 损失函数偏导数( theta 0)
def calc_delta0() :
  return step * np.sum((h(spaces) - prices) * x_i0) / len(spaces)

# 损失函数偏导数( theta 1)
def calc_delta1() :
  return step * np.sum((h(spaces) - prices) * spaces) / len(spaces)

def min1():
    return h(spaces[0])

# 循环更新 theta 值并计算误差,停止条件为
#  1. 误差小于某个值
#  2. 循环次数控制
k = 0
min11 = 100
while True :
  delta0 = calc_delta0()
  delta1 = calc_delta1()
  theta0 = theta0 - delta0
  theta1 = theta1 - delta1
  plt.figure(1)
  plt.scatter(theta0, theta1, c='y')
  plt.xlabel('theta0')
  plt.ylabel('theta1')
  plt.figure(3)
  error = calc_error()
  plt.scatter(k,error,c='y')
  plt.xlabel('k')
  plt.ylabel('error')
  # print("delta [%f, %f], theta [%f, %f], error %f" % (delta0, delta1, theta0, theta1, error))
  k = k + 1
  if (k > 500 ) : 
    break


print(" h(x) = %f + %f * x" % (theta0, theta1))
    
# 使用假设函数计算出来的价格,用于画拟合曲线
y_out = h(spaces)
plt.figure(2)
plt.scatter(spaces, prices, c='g')
plt.plot(spaces, y_out, c='b')
plt.xlabel('house space')
plt.ylabel('house price')
plt.show()


## theta 初始值
theta0 = 0
theta1 = 0

## 如果步长选择不对,则 theta 参数更新结果会不对
step = 0.0000015

x_i0 = np.ones((len(spaces)))

# 假设函数

def h(x) :
  return theta0 + theta1 * x

# 损失函数
def calc_error() :
  return np.sum(np.power((h(spaces) - prices),2)) / len(spaces)

# 损失函数偏导数( theta 0)
def calc_delta0() :
  return step * (h(i) - prices[i])

# 损失函数偏导数( theta 1)
def calc_delta1() :
  return step * (h(i) - prices[i]) * i 

# 循环更新 theta 值并计算误差,停止条件为
#  1. 误差小于某个值
#  2. 循环次数控制
k = 0

while True :
  i = random.randint(0,6)
  delta0 = calc_delta0()
  delta1 = calc_delta1()
  theta0 = theta0 - delta0
  theta1 = theta1 - delta1
  plt.figure(1)
  plt.scatter(theta0, theta1,c='g')
  plt.xlabel('theta0')
  plt.ylabel('theta1')
  error = calc_error()
  plt.figure(4)
  plt.scatter(k,error,c='y')
  plt.xlabel('k')
  plt.ylabel('error')
  # print("delta [%f, %f], theta [%f, %f], error %f" % (delta0, delta1, theta0, theta1, error))
  k = k + 1
  if (k > 500 ) : 
    break


print(" h(x) = %f + %f * x" % (theta0, theta1))
    
# 使用假设函数计算出来的价格,用于画拟合曲线
y_out1 = h(spaces)
plt.figure(2)
plt.plot(spaces, y_out1)
plt.show()

结果分析:
在这里插入图片描述
蓝色直线是利用批量梯度算法进行的拟合,另一条直线是随机梯度算法进行的拟合
其每次变化的快慢可以通过theta0和theta1的关系进行展示在这里插入图片描述
当然这仅仅只是参考,对于不同的步数(学习率)会有不同的结果
以下是三种梯度下降算法的特点:

批量梯度下降法(BGD):使用所有样本进行计算,慢但准确度好
随机梯度下降法(SGD):每次使用1个样本进行计算,快但准确度欠缺
小批量梯度下降法:每次使用a个样本进行计算,是BGD和SGD的折中方案

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值