本文使用pytohn3,jupyter notebook
本文禁止转载!

一、一元函数的梯度下降

已知一个一元函数:

$$ f(x)=3x^2+2x-1 $$
求f(x)最低点。
实际上用初中知识我们就可以求出最低点坐标,但是我们今天来使用梯度下降的方法。

1.什么是梯度下降

对于该函数,我们想求出最低点,但是我们不知道最低点在哪里,于是我们瞎蒙一个:x=5
求得f(5)=84。
但是我们不知道它是不是最低点,但是我们会求导:
$$ f^’(x)=6x+2 $$
$$ f^’(5) = 32 $$
很显然,x=5处不是最低点,而且此处导数为正,函数单调上升。
那么我们可以尝试把x变小一点,这样就会更接近最低点。
令x=4。
得f(4)=55,比刚刚更接近最低点,这样我们就可以不断接近最低点了。
梯度下降
但是为什么让x减小到4?减小到3可以吗?减小到4.5可以吗?
我们把x的变化称之为步长,那么这个步长应该怎么选取?
我们知道如果导数的绝对值越大,离极值点也就越远,那么我们应该把步子迈得大一些。而导数越接近于0,离极值点就越近,那么我们应该小心翼翼的前进。所以步长是与改点的导数成正相关。他们的比例系数我们称之为学习率。

如果学习率选取的不合理就会出现梯度消失梯度爆炸
梯度消失:
梯队消失
可以看到,步长如果比较大,就会在极值点附近来回震荡,而误差也减小得很慢。

梯度爆炸:
梯度爆炸
可以看到,步长非常大时,不仅没有靠近极值点,反而越来越远,我们称之为梯度爆炸
下面,我们来实现梯度下降的过程。

2.导入必要的包

import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np

3.画出函数图像

f = lambda x:3*x**2+2*x-1
x = np.linspace(-6,5,1000)
y = np.array(list(map(f, x)))
plt.plot(x,y)

y=3x^2+2x-1

4.求导

d = lambda x:6*x+2

5.梯度下降

# 设置学习率
learning_rate = 0.1
# 精确度
precision = 0.0001
# 随机生成一个初始x
min_x = random.random()*11-6
# 计数
count = 0
while True:
    # 求出该点导数
    dx = d(min_x)
    print('count:{},x:{}'.format(count,min_x))
    # 沿导数方向前进
    min_x -= learning_rate * dx
    count += 1
    if abs(learning_rate * dx) < precision or count > 5000:
        break
print('count:{},x:{}'.format(count,min_x))

6.画图

plt.figure(figsize=(12,7))
x = np.linspace(-6,5,1000)
y = np.array(list(map(f, x)))
plt.plot(x,y)
x = np.array(x_list)
y = np.array(list(map(f, x)))
plt.plot(x,y,color='red')

在这里插入图片描述

二、二元函数梯度下降

有二元函数:
$$f(x,y)=x^2+y^2+2x$$
求最低点。

1.导入必要的包

from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

2.画出三维图像

fig = plt.figure(figsize=(12,7))
ax = Axes3D(fig)
X = np.linspace(-100, 100, 50)
Y = np.linspace(-100, 100, 50)
X, Y = np.meshgrid(X, Y)
# R = np.sqrt(X**2 + Y**2)
Z = X**2 + Y**2 + 2*X 
ax.plot_surface(X, Y, Z,cmap='rainbow')

在这里插入图片描述

3.导函数

二元函数存在两个方向上的导数

dx = lambda x,y:2*x+2
dy = lambda x,y:2*y

4.梯度下降

# 设置学习率
learning_rate = 0.2
# 精确度
precision = 0.0001
# 随机生成一个初始x,y
min_x = random.random()*200-100
min_y = random.random()*200-100
# 计数
count = 0
# 记录过程
xy_list = []
while True:
    xy_list.append([min_x,min_y])
    # 求出该点导数
    dx_ = dx(min_x,min_y)
    dy_ = dy(min_x,min_y)
    print('count:{},x:{},y:{}'.format(count,min_x,min_y))
    # 沿导数方向前进
    step_x = learning_rate * dx_
    step_y = learning_rate * dy_
    min_x -= step_x
    min_y -= step_y
    count += 1
    if step_x**2+step_y**2 < precision**2 or count > 5000:
        xy_list.append([min_x,min_y])
        break
print('count:{},x:{},y:{}'.format(count,min_x,min_y))

5.画图

fig = plt.figure(figsize=(12,7))
ax = Axes3D(fig)
X = np.linspace(-100, 100, 50)
Y = np.linspace(-100, 100, 50)
X, Y = np.meshgrid(X, Y)
Z = X**2 + Y**2 + 2*X 
ax.plot_surface(X, Y, Z,cmap='rainbow',alpha=0.3)

xy_np = np.array(xy_list)
X = xy_np[:,0].reshape([-1])
Y = xy_np[:,1].reshape([-1])
Z = X**2 + Y**2 + 2*X
ax.plot(X, Y, Z,color='red')

在这里插入图片描述
可以看到一条逐渐滑向谷底的线。


下一篇:纯python实现线性回归

Last modification:August 14th, 2020 at 11:50 am
如果觉得我的文章对你有用,请随意赞赏