神经网络的学习 求梯度

import sys, os

sys.path.append(os.pardir)
import numpy as np

from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient


# simpleNet类
class simpleNet:
    def __init__(self):
        self.W = np.random.rand(2, 3)  # 随机形状为2*3的权重参数

    def predict(self, x):  # 方法一:预测
        return np.dot(x, self.W) #表示输入 x 与权重 W 之间的点积,这相当于神经网络的前向传播。

    def loss(self, x, t):  # 方法二:求损失函数值 x接受输入数据,t为真实值标签
        z = self.predict(x)
        y = softmax(z)  # softmax的输出总和=1,可将softmax函数输出的结果称为概率
        loss = cross_entropy_error(y, t)  # 交叉熵损失函数,值越小,表示越接近真实值
        return loss


net = simpleNet()#生成
print(net.W)  #输出权重参数
x = np.array([0.6, 0.9])   
p = net.predict(x)
print(p)

print(np.argmax(p))#最大的索引值

t=np.array([0,0,1])#正确标签为2

print(net.loss(x,t))#求损失函数值

#求梯度:
def f(W): #f(W)是为参数W可以在求权重函数中使用的兼容参数,类似于将W重构成f(W)?
    return net.loss(x,t)
dW=numerical_gradient(f,net.W)
print(dW)

common中用到的的function和gradient

import numpy as np

def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T 

    x = x - np.max(x) # 溢出对策
    return np.exp(x) / np.sum(np.exp(x))

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
    # 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
    if t.size == y.size:
        t = t.argmax(axis=1)
             
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        tmp_val = x[idx]
        x[idx] = float(tmp_val) + h
        fxh1 = f(x) # f(x+h)
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val # 还原值
        it.iternext()   
        
    return grad

 代码详解:

第一段:simpleNet

这段代码实现了一个简单的神经网络(simpleNet 类),并展示了如何计算神经网络的预测、损失函数值以及权重的梯度。接下来,我将逐行解释代码的作用和它的核心概念。

1. 导入模块

import sys, os
sys.path.append(os.pardir)
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient
  • import sys, os: 导入 sysos 模块,允许你在程序中操作系统路径、环境变量等。
  • sys.path.append(os.pardir): 将父目录添加到模块搜索路径中,以便可以访问父目录中的模块。os.pardir 是上级目录的路径。
  • import numpy as np: 导入 NumPy 库,常用于数组操作和数学计算。
  • from common.functions import softmax, cross_entropy_error: 从 common.functions 模块中导入 softmaxcross_entropy_error 函数。softmax 将网络的输出转化为概率分布,cross_entropy_error 用于计算交叉熵损失。
  • from common.gradient import numerical_gradient: 从 common.gradient 模块中导入 numerical_gradient 函数,用于计算梯度。

2. simpleNet

class simpleNet:
    def __init__(self):
        self.W = np.random.rand(2, 3)  # 随机形状为2*3的权重参数
  • class simpleNet: 定义了一个名为 simpleNet 的类,这个类是简单的神经网络模型。
  • def __init__(self): 构造函数初始化模型参数。
  • self.W = np.random.rand(2, 3): 初始化权重 W,它是一个 2×32 \times 3 的随机矩阵,表示有 2 个输入和 3 个输出神经元。

3. predict 方法

def predict(self, x):  # 方法一:预测
    return np.dot(x, self.W)  # 表示输入 x 与权重 W 之间的点积,这相当于神经网络的前向传播。
  • def predict(self, x): 定义了一个方法 predict,用于计算神经网络的输出。
  • return np.dot(x, self.W): 计算输入 x 和权重矩阵 W 的点积。点积相当于神经网络的前向传播过程,得出每个神经元的激活值。

4. loss 方法

def loss(self, x, t):  # 方法二:求损失函数值 x 接受输入数据,t 为真实标签
    z = self.predict(x)
    y = softmax(z)  # softmax的输出总和=1,可将 softmax 函数输出的结果称为概率
    loss = cross_entropy_error(y, t)  # 交叉熵损失函数,值越小,表示越接近真实值
    return loss
  • def loss(self, x, t): 定义了一个计算损失的函数。x 是输入数据,t 是真实标签。
  • z = self.predict(x): 调用 predict 方法计算输入 x 的预测值 z
  • y = softmax(z): 使用 softmax 函数将输出 z 转化为概率分布。softmax 函数将模型的原始输出转化为每个类别的概率。
  • loss = cross_entropy_error(y, t): 使用交叉熵损失函数计算预测概率 y 和真实标签 t 之间的差异。交叉熵损失值越小,表示预测越接近真实值。
  • return loss: 返回损失值。

5. 实例化并测试网络

net = simpleNet()  # 生成 simpleNet 类的实例
print(net.W)  # 输出权重参数
x = np.array([0.6, 0.9])   
p = net.predict(x)
print(p)
print(np.argmax(p))  # 打印最大值的索引
  • net = simpleNet(): 创建一个 simpleNet 类的实例,初始化网络的权重。
  • print(net.W): 打印权重 W,它是一个 2×32 \times 3 的随机矩阵。
  • x = np.array([0.6, 0.9]): 定义输入数据 x,它是一个包含两个元素的数组。
  • p = net.predict(x): 使用 predict 方法计算输入 x 的预测结果 p
  • print(p): 打印预测结果 p
  • print(np.argmax(p)): 打印 p 中最大的值的索引。np.argmax(p) 返回数组 p 中最大元素的索引,通常用于分类任务,表示预测的类别。

6. 计算损失

t = np.array([0, 0, 1])  # 正确标签为2(即第三类)
print(net.loss(x, t))  # 求损失函数值
  • t = np.array([0, 0, 1]): 定义真实标签 t,这里标签为 [0, 0, 1],表示类别 2 的独热编码。
  • print(net.loss(x, t)): 计算输入数据 x 与标签 t 之间的交叉熵损失,并打印出来。

7. 计算梯度

def f(W):  # f(W) 是一个可以在求权重函数中使用的兼容参数,类似于将 W 重构成 f(W)?
    return net.loss(x, t)

dW = numerical_gradient(f, net.W)
print(dW)
  • def f(W): 定义了一个函数 f(W),该函数用于计算给定权重 W 下的损失值。这个函数将 net.loss(x, t) 封装在其中,并接受 W 作为参数。
  • dW = numerical_gradient(f, net.W): 使用 numerical_gradient 函数计算损失函数 f(W) 对权重 W 的数值梯度。numerical_gradient 会通过数值差分方法计算梯度。
  • print(dW): 打印计算得到的梯度 dW,它表示每个权重参数对于损失函数的敏感程度。

总结

这段代码展示了一个简单的神经网络的实现,包括了:

  1. 网络的初始化(权重的随机生成)。
  2. 前向传播过程(通过点积和 softmax 函数得到预测)。
  3. 损失函数的计算(使用交叉熵损失)。
  4. 计算损失对权重的梯度(使用数值梯度)。

通过这些步骤,代码展示了如何用 Python 构建一个简单的神经网络,并计算其梯度,从而为后续的优化(如梯度下降)做好准备。

function:激活函数softmax和求交叉熵误差

这段代码实现了两个常用的函数:softmaxcross_entropy_error,它们在神经网络中用于分类任务。接下来我会逐行解释这两个函数的作用和实现细节。

1. Softmax 函数

def softmax(x):
    if x.ndim == 2:
        x = x.T  # 转置,使得每一列代表一个样本
        x = x - np.max(x, axis=0)  # 减去每列的最大值,避免溢出
        y = np.exp(x) / np.sum(np.exp(x), axis=0)  # 对每列应用 softmax
        return y.T  # 转置回原来的形状

    x = x - np.max(x)  # 溢出对策,减去最大值避免指数溢出
    return np.exp(x) / np.sum(np.exp(x))  # 计算softmax
解释:
  • softmax 函数将一个向量或者矩阵(代表每个类的分数)转换成概率分布。它常用于神经网络的输出层,将原始的网络输出(称为“logits”)转换为类的概率。

  • if x.ndim == 2::检查输入 x 的维度。如果 x 是二维数组(形状为 batch_size x class_num),即处理的是多个样本(一个小批量的数据),则执行以下代码:

    • x = x.T:转置矩阵,使得每一列代表一个样本的数据。
    • x = x - np.max(x, axis=0):减去每列的最大值,防止在计算指数时溢出。因为大数的指数值会导致计算中的溢出。
    • y = np.exp(x) / np.sum(np.exp(x), axis=0):对每列的值应用 softmax 函数,得到每个类别的概率。np.exp(x) 对每个元素求指数,np.sum(np.exp(x), axis=0) 是对每列进行求和。
    • return y.T:最后将矩阵转置回原来的形状。
  • x = x - np.max(x):如果 x 是一个一维数组(单个样本),则直接减去最大值,避免指数计算时的溢出。

  • return np.exp(x) / np.sum(np.exp(x)):计算 softmax 输出,返回每个类别的概率。

Softmax 特点
  • 输入值经过 softmax 函数后,输出的概率值总和为 1。
  • 它将每个输出值转换为一个介于 0 和 1 之间的值,表示该类的预测概率。

2. 交叉熵损失函数(Cross-Entropy Error)

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)  # 如果标签是1D,转换为2D
        y = y.reshape(1, y.size)  # 如果输出是1D,转换为2D
        
    if t.size == y.size:
        t = t.argmax(axis=1)  # 将标签转换为类索引(对于one-hot编码)

    batch_size = y.shape[0]  # 获取批次大小
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size  # 计算平均交叉熵损失
解释:
  • if y.ndim == 1::检查 y 是否是一维数组。如果 y 是一维数组,表示只有一个样本,接着将标签和预测的 y 重塑为二维数组,便于处理。

  • if t.size == y.size::检查 ty 的尺寸。如果标签 t 和预测概率 y 的尺寸相同,则说明标签是 one-hot 编码。例如,标签为 [0, 0, 1],表示类别 2。argmax(axis=1) 将标签从 one-hot 编码转换为类别索引。即 t.argmax(axis=1) 变为 2

  • batch_size = y.shape[0]:获取样本的批次大小,即 y 的第一维的大小。

  • return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

    • np.arange(batch_size):生成批次大小的数组(从 0 到 batch_size-1),表示样本的索引。
    • y[np.arange(batch_size), t]:从预测概率 y 中选取对应类别 t 的概率值。t 是每个样本的类别索引,y 是一个矩阵,y[i, t[i]] 会返回样本 i 在类别 t[i] 上的预测概率。
    • np.log(y[np.arange(batch_size), t] + 1e-7):对每个样本的预测概率取对数,1e-7 是防止概率值为 0,导致对数函数计算出无穷大。
    • np.sum(...)/batch_size:求和并计算平均值,返回批次的平均交叉熵损失。
交叉熵损失

交叉熵损失函数衡量了预测概率分布与实际标签之间的差异,特别适用于分类问题。它的值越小,表示模型的预测越准确。对于二分类任务和多分类任务,交叉熵是常用的损失函数。

总结:

  • Softmax 函数将神经网络的原始输出转化为概率分布,用于分类问题。
  • Cross-Entropy Error 计算模型的输出概率与实际标签之间的差异,用于量化模型的预测误差。

这两个函数常常一起使用,尤其是在多类分类任务中,softmax 用于生成分类概率,交叉熵损失用于衡量预测与真实标签的差异。

求函数的梯度值

这段代码实现了 数值梯度 的计算。数值梯度是通过有限差分方法来近似计算梯度的,常用于验证反向传播算法的正确性。接下来我将详细解释这段代码的每一部分。

1. numerical_gradient(f, x) 函数的作用

  • 函数输入:

    • f: 目标函数。它接受一个输入 x,并返回该输入对应的损失值。
    • x: 参数 x,是我们要计算梯度的输入,通常是模型的参数(如权重和偏置)。
  • 函数输出:

    • grad: 数值梯度,表示目标函数对每个参数 x 的导数,形状与 x 相同。

2. 初始化和设置

h = 1e-4  # 设定一个小的值,用于计算有限差分
grad = np.zeros_like(x)  # 创建一个与x相同形状的零矩阵,用于存储计算出来的梯度
  • h = 1e-4: 设定一个很小的值 h,用于在计算梯度时做微小的偏移。h 是差分方法中的步长,用来近似导数。
  • grad = np.zeros_like(x): 创建一个与 x 形状相同的零矩阵 grad,用来存储计算得到的梯度。

3. 使用 np.nditer 迭代 x

it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
  • np.nditer(x) 是 NumPy 中的一个迭代器,用于遍历 x 中的每一个元素。
  • flags=['multi_index']: 允许获取每个元素的多维索引。
  • op_flags=['readwrite']: 允许对 x 中的元素进行读取和修改。

4. 计算每个元素的数值梯度

while not it.finished:
    idx = it.multi_index  # 获取当前元素的多维索引
    tmp_val = x[idx]  # 保存当前元素的值
    x[idx] = float(tmp_val) + h  # 将当前元素加上h
    fxh1 = f(x)  # 计算 f(x + h)
    
    x[idx] = tmp_val - h  # 将当前元素减去h
    fxh2 = f(x)  # 计算 f(x - h)
    
    grad[idx] = (fxh1 - fxh2) / (2 * h)  # 通过中心差分法计算梯度
    
    x[idx] = tmp_val  # 还原当前元素的值
    it.iternext()  # 移动到下一个元素
  • while not it.finished:: 这是一个循环,直到迭代器遍历完 x 中的所有元素。

  • idx = it.multi_index: 获取当前元素的索引。

  • tmp_val = x[idx]: 保存当前元素的原始值,以便在计算后将其还原。

  • x[idx] = float(tmp_val) + h: 将当前元素的值加上 h,然后调用目标函数 f(x) 计算其值 fxh1

  • fxh1 = f(x): 计算函数在 x + h 处的值。

  • x[idx] = tmp_val - h: 将当前元素的值减去 h,然后计算函数 f(x)x - h 处的值 fxh2

  • fxh2 = f(x): 计算函数在 x - h 处的值。

  • grad[idx] = (fxh1 - fxh2) / (2 * h): 使用中心差分法计算梯度。中心差分法通过 (f(x+h) - f(x-h)) / (2 * h) 近似计算导数。

  • x[idx] = tmp_val: 还原当前元素的值,以便继续计算其他元素的梯度。

  • it.iternext(): 移动到下一个元素,继续计算梯度。

5. 返回结果

return grad
  • grad 是一个与 x 形状相同的矩阵,包含了 x 中每个元素的数值梯度。

数值梯度的原理

数值梯度通过有限差分方法来近似计算。对于给定的函数 f(x),某个元素 x_i 的导数可以通过以下公式来近似:

其中:

  • x + hx - h 分别表示对 x_i 添加和减去微小偏移量 h 后的值。
  • (f(x + h) - f(x - h)) / (2h) 是使用中心差分法近似计算的梯度。

使用场景

数值梯度主要用于验证反向传播算法的正确性。在训练神经网络时,计算梯度是一个关键步骤。反向传播算法是基于链式法则计算的梯度,而数值梯度可以作为一种“手工”计算梯度的方式,帮助我们检查反向传播是否实现正确。

总结

  • 这个 numerical_gradient 函数通过对 x 中每个元素添加和减去一个小的 h 来计算数值梯度,采用了中心差分法。
  • 数值梯度对于调试和验证梯度计算的正确性非常有用,特别是在训练神经网络时。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/970399.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

架构——Nginx功能、职责、原理、配置示例、应用场景

以下是关于 Nginx 的功能、职责、原理、配置示例、应用场景及其高性能原因的详细说明: 一、Nginx 的核心功能 1. 静态资源服务 功能:直接返回静态文件(如 HTML、CSS、JS、图片、视频等)。配置示例:server {listen 80…

如何在 Mac 上解决 Qt Creator 安装后应用程序无法找到的问题

在安装Qt时,遇到了一些问题,尤其是在Mac上安装Qt后,发现Qt Creator没有出现在应用程序中。通过一些搜索和操作,最终解决了问题。以下是详细的记录和解决方法。 1. 安装Qt后未显示Qt Creator 安装完成Qt后,启动应用程…

优选算法《位运算》

在本篇当中我们将会复习之前在C语言阶段学习的各种位运算,并且在复习当中将再补充一些在算法题当中没有进行总结的位运算的使用方法,再总结完常见的位运算使用方法之和接下来还是和之前的算法篇章一样通过几道算法题来对这些位运算的方法技巧进行巩固。在…

应对DeepSeek总是服务器繁忙的解决方法

最近由于访问量过大,DeepSeek服务器官网经常弹出:“服务器繁忙,请稍后再试”的提示,直接卡成PPT怎么办?服务器繁忙直接看到视觉疲劳: 解决DeepSeek卡顿问题 DeepSeek使用卡顿问题,是因为访问量…

docker容器部署jar应用导入文件时候报缺少字体错误解决

如题,在导入文件时候报错如下: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager 经查是缺少对应字体,解决办法有两张: 第一种:…

工作室如何实现一机一IP

对于工作室而言,多开游戏账号却是其运营模式的核心需求。他们通过大量囤积金币、资源,再将其变现来获取利润。在这种运营模式下,账号数量直接关系到工作室的收益,所以解决 IP 问题就成了手游工作室发展道路上的首要难题&#xff0…

使用grafana v11 建立k线(蜡烛图)仪表板

先看实现的结果 沪铜主力合约 2025-02-12 的1分钟k线图 功能介绍: 左上角支持切换主力合约,日期,实现动态加载数据. 项目背景: 我想通过前端展示期货指定品种某1天的1分钟k线,类似tqsdk 的web_gui 生成图形化界面— TianQin Python SDK 3.7.8 文档 项目架构: 后端: fastap…

20250214在ubuntu20.04下使用obs studio录制外挂的1080p的USB摄像头【下载安装】

20250214在ubuntu20.04下使用obs studio录制外挂的1080p的USB摄像头 2025/2/14 9:10 缘起:笔记本电脑在ubuntu20.04下使用Guvcview录制自带的摄像头,各种问题。 1、降帧率。WIN10/11自带的相机应用可以满速30fps,马上重启到ubuntu20.04&#…

【开源免费】基于Vue和SpringBoot的旅游管理系统(附论文)

本文项目编号 T 229 ,文末自助获取源码 \color{red}{T229,文末自助获取源码} T229,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

基于Python的Optimal Interpolation (OI) 方法实现

前言 Optimal Interpolation (OI) 方法概述与实现 Optimal Interpolation (OI) 是一种广泛应用于气象学、海洋学等领域的空间数据插值方法。该方法通过结合观测数据与模型预测数据,最小化误差方差,从而实现对空间数据的最优插值。以下是OI方法的一般步骤…

从无序到有序:上北智信通过深度数据分析改善会议室资源配置

当前企业普遍面临会议室资源管理难题,预约机制不完善和临时会议多导致资源调度不合理,既有空置又有过度拥挤现象。 针对上述问题,上北智信采用了专业数据分析手段,巧妙融合楼层平面图、环形图、折线图和柱形图等多种可视化工具&a…

CAS单点登录(第7版)9.属性

如有疑问,请看视频:CAS单点登录(第7版) 属性 属性定义 概述 属性定义 从身份验证或属性存储库源获取和解析 CAS 中属性的定义时,往往使用其名称进行定义和引用,而无需任何其他元数据或修饰。例如&#…

Halo 配置QQ邮箱验证教程

一、准备工作 获取QQ邮箱授权码 登录QQ邮箱网页版(https://mail.qq.com)。 点击顶部导航栏 "设置" → "账户" → 找到 "POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务"。 点击 "生成授权码",按页面…

双轴伺服电机驱动控制器AGV、AMR专用双伺服电机驱动控制器解决方案

工业机器人数控机床XY机械手双轴机器人堆垛机专用双轴伺服电机驱动控制器48V 14ARMS带有STO功能,隔离高压CAN/RS485/USB通讯支持编码器和霍尔输入 双伺服电机驱动控制器TMCM2611功能介绍 集成2个伺服电机的控制和驱动于一体供电电压48V,驱动电流14A RM…

Springboot中使用Elasticsearch(部署+使用+讲解 最完整)

目录 引言 一、docker中安装Elasticsearch 1、创建es专有的网络 2、开放端口 3、在es-net网络上安装es和kibana 4、可能出现的问题 5、测试 6、安装IK分词器 7、测试IK分词器 二、结合业务实战 1、准备依赖 2、配置yml 3、读取yml配置 4、准备es配置类 5、编写测…

NVIDIA Jetson Orin Nano 刷机过程

1. 背景 新到手 NVIDIA Jetson Orin Nano 插上显示屏,显示如下: 这是UEFI Shell,UEFI Shell(统一可扩展固件接口外壳程序)是一种基于UEFI规范的交互式命令行工具,它运行在UEFI固件环境中,为用…

buu-jarvisoj_level2_x64-好久不见37

覆盖缓冲区和 RBP: 使用 128 8 字节覆盖 buf 和 rbp。 构造 ROP 链: pop rdi; ret 地址: 将 pop rdi; ret 指令的地址写入返回地址位置。 /bin/sh 地址: 将 /bin/sh 字符串的地址压入栈顶,作为 system 函数的参数。…

UE求职Demo开发日志#32 优化#1 交互逻辑实现接口、提取Bag和Warehouse的父类

1 定义并实现交互接口 接口定义: // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "UObject/Interface.h" #include "MyInterActInterface.generated.h…

java在文本追加内容时候发现数据丢失问题

最近在做清洗数据的工作,使用BufferedWriter类用来追加文本内容,测试时候发现没有问题,拿到生成时候发现过大时丢失数据。 核心代码如下: FileOutputStream fos new FileOutputStream(saveFile, true); OutputStreamWriter osw …

Unity学习part3

此为b站视频【【Unity教程】零基础带你从小白到超神】 https://www.bilibili.com/video/BV1gQ4y1e7SS/?p55&share_sourcecopy_web&vd_source6e7a3cbb802eb986578ad26fae1eeaab的笔记 1、反向动力学 打开ik处理 public class PlayerMoveController : MonoBehaviour {…