偏导数
详细介绍参考百度百科https://baike.baidu.com/item/%E5%81%8F%E5%AF%BC%E6%95%B0/5536984?fr=aladdin
求f(x0,x1,x2)关于x0的偏导数,就是将x1,x2看作常数再对x0求导。
利用定义求偏导可用如下数学式表示
这也就是数值微分。数值微分就是利用微小的差分求导数的过程。
梯度
详细介绍参考百度百科https://baike.baidu.com/item/%E6%A2%AF%E5%BA%A6/13014729?fr=aladdin
求f(x0,x1,x2)的梯度,就是求由x0,x1,x2(全部变量)的偏导数汇总而成的向量,即如下图所示的向量
根据高等数学,我们可知梯度指示的方向是各点处的函数值减小最多的方向。(这有利于我们找到使损失函数值最小的权重参数)
求梯度函数的实现如下:
import numpy as np
# 计算梯度的函数
def numerical_gradient_1d(f,x): # 入口参数为待求导函数f和变量数组x (所求梯度就是一个由函数f关于各个变量的偏导数组成的数组)
h = 1e-4 # 定义微小值h为0.0001(便于直接使用定义求偏导数)
grad = np.zeros_like(x) # 生成和x形状相同且所有元素都为0的数组grad
for idx in range(x.size): # 依次取每一个变量的数组下标
tmp_val = x[idx] # 记录第idx个元素的数值,存放在tmp_val变量中
x[idx] = tmp_val + h # 将x[idx]修改为tmp_val+h
fxh1 = f(x) # 对当前x数组求f函数值,得到函数值fxh1(表示f(x+h),实际上这里的x+h表示仅x[idx]加了h后得到的数组)
x[idx] = tmp_val - h # 将x[idx]修改为tmp_val-h
fxh2 = f(x) # 对当前x数组求f函数值,得到函数值fxh2(表示f(x-h),实际上这里的x-h表示仅x[idx]减了h后得到的数组)
grad[idx] = (fxh1 - fxh2)/(2*h) # 求得x[idx]的偏导数,放入梯度数组第idx单元
x[idx] = tmp_val # 将x[idx]恢复原值,为求下一个变量的偏导数做准备
return grad
梯度法就是通过巧妙地使用梯度来寻找函数取得最小值(或者尽可能小的值)时对应的参数。
具体而言使用梯度法就是指通过不断地沿梯度方向前进,逐渐减小函数值的过程。(由于梯度指示方向不一定是函数最小值,所以需要不断重复(根据参数求梯度,根据梯度更新参数)这个过程)
梯度法的数学式表示
其中的η表示学习率,它决定在一次学习中应该学习多少(即在多大程度上更新参数)。学习率是个超参数,是需要人为设定的。
梯度下降法的实现如下:
from numerical_gradient import numerical_gradient_1d
def gradient_descent(f, init_x, lr=0.01, step_num=100): # 入口参数中,f表示待求梯度函数,init_x表示参数初始值,lr=0.01表示学习率默认设定为0.01,step_num=100表示参数学习次数默认设定为100
x = init_x
for i in range(step_num): # i从0到99,进行100次循环(学习)
grad = numerical_gradient_1d(f,x) # 根据参数,求梯度
x -= lr*grad # 根据梯度,更新参数
return x #返回学习到的参数
举个例子:请用梯度法求使f(x0+x1) = x0^2 + x1^2取到最小值时的参数x0,x1
代码实现如下:
import numpy as np
from gradient_descent import gradient_descent
def function1(x): # 入口参数为一个含两个元素的一维数组,该函数表示x0^2+x1^2
return x[0]**2 + x[1]**2
init_x = np.array([-3.0,4.0]) # 定义参数初始值
x = gradient_descent(function1,init_x=init_x,lr=0.1,step_num=100) # 利用梯度下降法,得到学习到的参数
print(x)
运行结果如下:
最终结果是(-6.1e-10,8.1e-10),即x0为-6.1乘以10的-10次,x1为8.1乘以10的-10次,非常接近正确结果(0,0)了。
# 本博客参考了《深度学习入门——基于Python的理论与实现》(斋藤康毅著,陆宇杰译),特在此声明。