神经网络的学习 求梯度

news2025/2/21 13:38:51
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 来计算数值梯度,采用了中心差分法。
  • 数值梯度对于调试和验证梯度计算的正确性非常有用,特别是在训练神经网络时。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI向量数据库之LanceDB快速介绍

LanceDB LanceDB 是一个开源的向量搜索数据库,具备持久化存储功能,极大地简化了嵌入向量的检索、过滤和管理。 LanceDB的主要特点 LanceDB 的主要特点包括: 生产级向量搜索:无需管理服务器。 存储、查询和过滤向量、元数据以…

CentOS7 安装配置FTP服务

CentOS7 安装配置FTP服务 CentOS7 安装配置FTP服务1. FTP简介2. 先行准备2.1 关闭防火墙2.2 关闭 SELinux 3.安装FTP软件包4. 创建 FTP 用户及目录4.1 创建 FTP 目录并设置权限4.2 防止 FTP 用户登录 Linux 终端4.3 创建 FTP 用户组及用户4.4 创建 FTP 可写目录 5. 配置ftp服务…

【设计模式】03-理解常见设计模式-行为型模式(专栏完结)

前言 前面我们介绍完创建型模式和创建型模式,这篇介绍最后的行为型模式,也是【设计模式】专栏的最后一篇。 一、概述 行为型模式主要用于处理对象之间的交互和职责分配,以实现更灵活的行为和更好的协作。 二、常见的行为型模式 1、观察者模…

编程题-最大子数组和(中等-重点【贪心、动态规划、分治思想的应用】)

题目: 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组是数组中的一个连续部分。 解法一(枚举法-时间复杂度超限): …

本地通过隧道连接服务器的mysql

前言 服务器上部署了 mysql,本地希望能访问该 mysql,但是又不希望 mysql 直接暴露在公网上 那么可以通过隧道连接 ssh 端口的方式进行连接 从外网看,服务器只开放了一个 ssh 端口,并没有开放 3306 监听端口 设置本地免密登录 …

2. grafana插件安装并接入zabbix

一、在线安装 如果不指定安装位置,则默认安装位置为/var/lib/grafana/plugins 插件安装完成之后需要重启grafana 命令在上一篇讲到过 //查看相关帮助 [rootlocalhost ~]# grafana-cli plugins --help //从列举中的插件过滤zabbix插件 [rootlocalhost ~]# grafana…

Linux第107步_Linux之PCF8563实验

使用PCF8563代替内核的RTC,可以降低功耗,提高时间的精度。同时有助于进一步熟悉I2C驱动的编写。 1、了解rtc_time64_to_tm()和rtc_tm_to_time64() 打开“drivers/rtc/lib.c” /* * rtc_time64_to_tm - Converts time64_t to rtc_time. * Convert seco…

功能说明并准备静态结构

功能说明并准备静态结构 <template><div class"card-container"><!-- 搜索区域 --><div class"search-container"><span class"search-label">车牌号码&#xff1a;</span><el-input clearable placeho…

[免费]SpringBoot公益众筹爱心捐赠系统【论文+源码+SQL脚本】

大家好&#xff0c;我是老师&#xff0c;看到一个不错的SpringBoot公益众筹爱心捐赠系统&#xff0c;分享下哈。 项目介绍 公益捐助平台的发展背景可以追溯到几十年前&#xff0c;当时人们已经开始通过各种渠道进行公益捐助。随着互联网的普及&#xff0c;本文旨在探讨公益事业…

ML.Net二元分类

ML.Net二元分类 文章目录 ML.Net二元分类前言项目的创建机器学习模型的创建添加模型选择方案训练环境的选择训练数据的添加训练数据的选择训练数据的格式要预测列的选择模型评估模型的使用总结前言 ‌ML.NET‌是由Microsoft为.NET开发者平台创建的免费、开源、跨平台的机器学习…

visutal studio 2022使用qcustomplot基础教程

编译 下载&#xff0c;2.1.1版支持到Qt6.4 。 拷贝qcustomplot.h和qcustomplot.cpp到项目源目录&#xff08;Qt project&#xff09;。 在msvc中将它俩加入项目中。 使用Qt6.8&#xff0c;需要修改两处代码&#xff1a; L6779 # if QT_VERSION > QT_VERSION_CHECK(5, 2, …

本地搭建自己的专属客服之OneApi关联Ollama部署的大模型并创建令牌《下》

这里写目录标题 OneApi1、渠道设置2、令牌创建 配置文件修改修改配置文件docker-compose.yml修改config.json到此结束 上文讲了如何本地docker部署fastGtp&#xff0c;相信大家也都已经部署成功了&#xff01;&#xff01;&#xff01; 今天就说说怎么让他们连接在一起 创建你的…

【C】初阶数据结构4 -- 双向循环链表

之前学习的单链表相比于顺序表来说&#xff0c;就是其头插和头删的时间复杂度很低&#xff0c;仅为O(1) 且无需扩容&#xff1b;但是对于尾插和尾删来说&#xff0c;由于其需要从首节点开始遍历找到尾节点&#xff0c;所以其复杂度为O(n)。那么有没有一种结构是能使得头插和头删…

小爱音箱控制手机和电视听歌的尝试

最近买了小爱音箱pro&#xff0c;老婆让我扔了&#xff0c;吃灰多年的旧音箱。当然舍不得&#xff0c;比小爱还贵&#xff0c;刚好还有一台红米手机&#xff0c;能插音箱&#xff0c;为了让音箱更加灵活&#xff0c;买了个2元的蓝牙接收模块Type-c供电3.5接口。这就是本次尝试起…

Kotlin Lambda

Kotlin Lambda 在探索Kotlin Lambda之前&#xff0c;我们先回顾下Java中的Lambda表达式&#xff0c;Java 的 Lambda 表达式是 Java 8 引入的一项强大的功能&#xff0c;它使得函数式编程风格的代码更加简洁和易于理解。Lambda 表达式允许你以一种更简洁的方式表示实现接口&…

Java 设计模式之备忘录模式

文章目录 Java 设计模式之备忘录模式概述UML代码实现 Java 设计模式之备忘录模式 概述 备忘录(Memento)&#xff1a;在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态。方便对该对象恢复到原先保存的状态。 UML Originnato…

vue3搭建实战项目笔记二

vue3搭建实战项目笔记二 2.1.git管理项目2.2.隐藏tabBar栏2.2.1 方案一&#xff1a;在路由元信息中设置一个参数是否显示tabBar2.2.2 方案二&#xff1a;通过全局设置相对定位样式 2.3.项目里封装axios2.3.1 发送网络请求的两种做法2.3.2 封装axios并发送网络请求2.3.2.1 对axi…

【原创】解决vue-element-plus-admin无法实现下拉框动态控制表单功能,动态显隐输入框

前言 目前使用vue-element-plus-admin想要做一个系统定时任务功能&#xff0c;可以选择不同的定时任务类型&#xff0c;比如使用cron表达式、周期执行、指定时间执行等。每种类型对应不同的输入框&#xff0c;需要动态显隐输入框才行&#xff0c;但是这个vue-element-plus-adm…

大疆无人机需要的kml文件如何制作kml导出(大疆KML文件)

大疆无人机需要的轨迹kml文件&#xff0c;是一种专门的格式&#xff0c;这个kml里面只有轨迹点&#xff0c;其它的属性信息都不需要。 BigemapPro提供了专门的大疆格式输出&#xff0c; 软件这里下载 www.bigemap.com 安装后&#xff0c;kml导入如下图&#xff1a; 然后选择…

免费deepseek的API获取教程及将API接入word或WPS中

免费deepseek的API获取教程: 1 https://cloud.siliconflow.cn/中注册时填写邀请码&#xff1a;GAejkK6X即可获取2000 万 Tokens; 2 按照图中步骤进行操作 将API接入word或WPS中 1 打开一个word&#xff0c;文件-选项-自定义功能区-勾选开发工具-左侧的信任中心-信任中心设置…