在学习斯坦福大学的 cs231遇到了不少问题,这一篇主要是来自己对于神经网络中的梯度计算和其实现的python代码。
目前我所学习到的有关梯度的计算的方法有两种,第一种是根据网络的反向传播,可以得到网络输出的导数dout与输入dx,dw,db的关系(这里所说的神经网络是比较简单的全连接神经网络,激活层为sotmax或者relu等函数),这一部分将在下篇blog里进行推导计算;第二种就是利用基本公式(一个点号代表求一阶导数):
一个简单的神经网络结构如下图所示(具体的不多解释):
简单神经网络结构下面贴出代码:
import numpy as np
from random import randrange
def eval_numerical_gradient(f,x,verbose=True,h=0.0001):
#计算f在x的梯度,其中f是接收一个参数的函数,返回一个数字,如loss
fx = f(x)
grad = np.zeros_like(x)
it = np.nditer(x,flags=['multi_index'],op_flags=['readwrite'])
#当迭代没有结束的时候
while not it.finished:
ix = it.multi_index
oldval = x[ix]
x[ix] = oldval+h
f1 = f(x)
x[ix] = oldval-h
f2 = f(x)
grad[ix] = (f1-f2)/(2*h)
if verbose:
print(ix,grad[ix])
it.iternext()
return grad
def eval_numerical_gradient_array(f,x,df,h=1e-5):
#f为接受numpy数组并返回numpy数组的函数,并求f在x的梯度
grad = np.zeros_like(x)
it = np.nditer(x,flags=['multi_index'],op_flags=['readwrite'])
while not it.finished:
ix = it.multi_index
oldval = x[ix]
x[ix] = oldval + h
pos = f(x).copy()
x[ix] = oldval - h
neg = f(x).copy()
x[ix] = oldval
grad[ix] = np.sum((pos - neg) * df) / (2 * h)
it.iternext()
return grad
上述的两个函数都是计算导数,其中第一个函数eval_numerical_gradient是用来计算简单函数的导数,第二个函数eval_numerical_gradient_array则是用来计算矩阵中对应元素的导数。第一个函数好理解,现在来看第二个函数,原理一样,但是会发现在计算导数的时候分子多乘了一个df,这个是为什么?
#eval_numerical_gradien
grad[ix] = (f1-f2)/(2*h)
#eval_numerical_gradient_array
grad[ix] = np.sum((pos - neg) * df) / (2 * h)
下面给出矩阵求导的公式公式来源:https://blog.csdn.net/xiezongsheng1990/article/details/86709575
个人觉得这个公式不太好理解,接下来详细说明矩阵对元素的求导
矩阵中某个元素的梯度,分为两个方向:x和y,因此矩阵某个元素的梯度也就由两部分相加而成。
在这个例子里,上面矩阵导数的公式可以简化为:即为该元素在y1和y2方向上的导数和。
grad[ix] = np.sum((pos - neg) * df) / (2 * h)
在这条执行语句中,公式中的即为元素在x和y方向上所对应的行向量和列向量的变化,因为只有一个元素变化了,其他元素都没有变化,所以,则为。
个人理解:
矩阵对梯度的求导本质上是一个复合求导,即矩阵先对行/列向量求导,行/列向量最后再对该元素进行求导。
下篇预告:神经网络导数的推导并计算与本篇公式所得到的导数之间的误差
网友评论