欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

简单神经网络的求梯度介绍

程序员文章站 2022-05-21 22:40:13
...

先构造一个如下图所示的简单神经网络

简单神经网络的求梯度介绍

图中的直线均表示从左到右的箭头。

我们的目标是求字典型变量grads,grads['W']表示损失函数关于权重W的梯度,grads['b']表示损失函数关于偏置b的梯度。如下图所示

简单神经网络的求梯度介绍

我们构造一个神经网络类simpleNet来实现求梯度的的目标,代码如下所示

import sys,os
sys.path.append(os.pardir)
import numpy as np
from ch03.softmax import softmax
from cross_entropy_error import cross_entropy_error 
from numerical_gradient import numerical_gradient

class simpleNet:
	def __init__(self):
		self.W = np.random.randn(2,3) # 利用高斯分布初始化2x3大小的权重数组
		self.b = np.zeros(3)          # 初始化长度为且元素全为0的一维偏置数组
		
	def predict(self, x):             # 推理函数
		z = np.dot(x,self.W) + self.b # 计算加权和z
		y = softmax(z)                # 将z通过输出层的**函数转化得到推理结果y
		return y
		
	def loss(self, x, t):             # 损失函数
		y = self.predict(x)           # 求得推理结果y
		loss = cross_entropy_error(y,t)   # 调用交叉熵误差函数求出损失函数值
		return loss
	
	def gradient(self, x, t):         # 梯度函数
		FX = lambda X: self.loss(x,t) # 创建匿名函数FX,入口参数为X,函数体内容实际上就是return self.loss(x,t)
					      # 我们要求的是损失函数关于权重参数的梯度。由于已实现的求梯度函数numerical_gradient(f,x)要求
					      # f的入口参数显示包含x,而已经实现的损失函数loss没有将权重参数作为入口参数,所以构造了形式损失函数FX。
					      # 之后便可以将FX和相应权重参数X作为numerical_gradient的入口参数了。
		
		grads = {} # 定义字典型变量grads
		grads['W'] = numerical_gradient(FX, self.W) # 求损失函数关于W的梯度
		grads['b'] = numerical_gradient(FX, self.b) # 求损失函数关于b的梯度
		return grads
		
network = simpleNet()      # 创建简单神经网络对象network
x = np.array([0.6,0.9])    # 设定输入参数x和正确解标签t
t = np.array([0,0,1])
print(network.W)           # 打印权重参数W和偏置参数b
print(network.b)
grads = network.gradient(x,t)  # 求W和b的梯度,并打印
print(grads['W'])
print(grads['b'])

上边所用的求梯度函数numerical_gradient与之前讲的稍稍有所不同,多考虑了维度的变化(权重参数W现在是二维的)。代码如下所示

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
	
def numerical_gradient(f, X):
    if X.ndim == 1:                      # 权重参数为一维时,直接调用一维梯度函数numerical_gradient
        return numerical_gradient_1d(f, X)
    else:                                # 权重参数大于一维时
        grad = np.zeros_like(X)          # 定义一个与权重矩阵形状相同的梯度矩阵grad      
        
        for idx, x in enumerate(X):      # 逐行调用一维梯度函数numerical_gradient,求出梯度矩阵grad
            grad[idx] = numerical_gradient_1d(f, x)
        
        return grad

运行结果如下所示

简单神经网络的求梯度介绍

这样就求出了我们所需要的梯度,为接下来的更新梯度等操作做了准备。

 

# 本博客参考了《深度学习入门——基于Python的理论与实现》(斋藤康毅著,陆宇杰译),特在此声明。

 

相关标签: python与深度学习