深度学习笔记:误差反向传播(2)

news2025/7/28 0:08:57

反向传播上一章内容可见http://t.csdn.cn/SHW3m

1 激活函数的反向传播

ReLU

ReLU函数在x > 0的时候返回x,在X <= 0的时候返回0。对该函数求导可得∂y/∂x 在x > 0时为1,在x <= 0时为0
在这里插入图片描述

class Relu:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0

        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout

        return dx

在该程序中,mask为一个boolean数组。self.mask = (x <= 0)将x中所有小于0的元素位置对应为True,将x大于0的元素位置对应False

Sigmoid
在这里插入图片描述
sigmoid函数求导结果为(1 + exp(-x)) ^ -2 * exp(-x),这一结果可以进一步简化为y(1 - y)
在这里插入图片描述

class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        out = sigmoid(x)
        self.out = out
        return out

    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out

        return dx

Affine

Affine层即为矩阵乘法。作用是将输入值乘以权重再加上偏置
在这里插入图片描述
反向传播输出矩阵形状要和输入矩阵形状一致,如X形状为(N,2),∂L/∂X 的形状也要为(N,2)。通过这点我们可以推出图中的反向传播计算公式。如要计算∂L/∂X,我们要将反向传播的输入(N,3)乘以W的同时确保输出形状为(N,2)。要实现这一点,我们可以将(N,3)* (3, 2)得到(N,2),对应过来即为∂L/∂Y x WT

WT为转置矩阵,其作用是将矩阵横纵颠倒
在这里插入图片描述

对于偏置量,由于在正向传播中将X * W结果的每一维都加上了偏置。如对[[1, 1, 1], [2, 2, 2], [3, 3, 3]]加上偏置[1, 2, 3]的结果为[[2, 3, 4], [3, 4, 5], [4, 5, 6]]。在加法反向传播中,导数结果∂L/∂Y,这里需要将∂L/∂Y关于第0维加和结果作为∂L/∂B。在例子中即为[6, 6, 6]

class Affine:
    def __init__(self, W, b):
        self.W =W
        self.b = b
        
        self.x = None
        self.original_x_shape = None
        # 权重和偏置参数的导数
        self.dW = None
        self.db = None

    def forward(self, x):
        # 对应张量
        self.original_x_shape = x.shape
        x = x.reshape(x.shape[0], -1)
        self.x = x

        out = np.dot(self.x, self.W) + self.b

        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)
        
        dx = dx.reshape(*self.original_x_shape)  # 还原输入数据的形状(对应张量)
        return dx

softmax-with-loss

在分类问题的输出层,我们一般使用softmax函数。这里我们将softmax和cross-entropy层相连直接得到损失函数,因此被称为softmax-with-loss层。该层计算图如下:
在这里插入图片描述
这里由计算图一步步推得导数结果过程过于复杂。这里我们直接给出反向传播结果为y - t,即为预测值和标签值的差值。

class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None # softmax的输出
        self.t = None # 监督数据

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        
        return self.loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        if self.t.size == self.y.size: # 监督数据是one-hot-vector的情况
            dx = (self.y - self.t) / batch_size
        else:
            dx = self.y.copy()
            dx[np.arange(batch_size), self.t] -= 1
            dx = dx / batch_size
        
        return dx

利用误差反向传播搭建的完整神经网络

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
from common.layers import *
from common.gradient import numerical_gradient
from collections import OrderedDict


class TwoLayerNet:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
        # 初始化权重
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) 
        self.params['b2'] = np.zeros(output_size)

        # 生成层
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.lastLayer = SoftmaxWithLoss()
        
    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        
        return x
        
    # x:输入数据, t:监督数据
    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)
    
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1 : t = np.argmax(t, axis=1)
        
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
        
    # x:输入数据, t:监督数据
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)
        
        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
        
        return grads
        
    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 设定
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads

1

    def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
        # 初始化权重
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) 
        self.params['b2'] = np.zeros(output_size)

        # 生成层
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.lastLayer = SoftmaxWithLoss()

创建网络,使用随机正态初始化权重和偏置。创建了一个OrderedDict来保存各层。OrderedDict可以保存加入字典的元素顺序

2

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        
        return x

逐层正向传播得到输出

3

    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)

利用sofmax with loss函数计算损失函数

4

    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1 : t = np.argmax(t, axis=1)
        
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy

将网络输出和标签值比较得到预测准确率

5

    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 设定
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads

使用反向传播发计算梯度。创建layers将各层保存为列表,再将列表翻转得到反向传播的函数顺序。对输出进行反向传播即可得到各层的梯度

梯度确认

数值微分计算梯度是很低效的方法,但是其实现简单,一般不会出错。因此,我们一般利用数值微分的结果对反向传播的结果进行验算,如果验算误差很小,我们可以确定反向传播的方法结果正确。

# coding: utf-8
import sys, os
sys.path.append('D:\AI learning source code')  # 为了导入父目录的文件而进行的设定
import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

x_batch = x_train[:3]
t_batch = t_train[:3]

grad_numerical = network.numerical_gradient(x_batch, t_batch)
grad_backprop = network.gradient(x_batch, t_batch)

for key in grad_numerical.keys():
    diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )
    print(key + ":" + str(diff))

这里我们引入mnist数据集进行测试,结果如下:

W1:4.0712457921539956e-10
b1:2.5670347708844996e-09
W2:5.31776417291195e-09
b2:1.4023633097548504e-07

可以看到两种方法得到的梯度基本一致,反向传播程序正确

利用反向传播发对mnist手写数字集进行分类

# coding: utf-8
import sys, os
sys.path.append('D:\AI learning source code')

import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    # 梯度
    #grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch)
    
    # 更新
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
    
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print(train_acc, test_acc)

程序和上一章程序(http://t.csdn.cn/tDD17)完全相同,只是方法由数值微分改为反向传播,故不做详解

测试结果

0.11295 0.1126
0.9041 0.9066
0.92065 0.9222
0.9335333333333333 0.935
0.9434166666666667 0.9433
0.9489333333333333 0.9461
0.9547833333333333 0.9521
0.9597666666666667 0.956
0.9631666666666666 0.9579
0.9662666666666667 0.9584
0.9684666666666667 0.9631
0.97145 0.9656
0.9720833333333333 0.9655
0.9738666666666667 0.9659
0.9752833333333333 0.9691
0.9768833333333333 0.9681
0.9780666666666666 0.9679

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

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

相关文章

【架构师】零基础到精通——架构演进

博客昵称&#xff1a;架构师Cool 最喜欢的座右铭&#xff1a;一以贯之的努力&#xff0c;不得懈怠的人生。 作者简介&#xff1a;一名Coder&#xff0c;软件设计师/鸿蒙高级工程师认证&#xff0c;在备战高级架构师/系统分析师&#xff0c;欢迎关注小弟&#xff01; 博主小留言…

Pycharm远程连接服务器配置常见问题

说明&#xff1a;配置过程中两个地方容易出错&#xff01;&#xff01;&#xff01; pycharm远程连接服务器完整教程_hehedadaq的博客-CSDN博客_pycharm远程 可参考该网址&#xff0c;但是在该过程中注意&#xff1a; 其他功能&#xff1a; 打开服务器的terminal(可开多个)&…

Spring源码分析:创建 BeanDefinition 流程

一、前期准备1.1 环境依赖<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.7.RELEASE</version></dependency><dependency><groupId&…

CentOs8 安装Jenkins一系列问题总结

CentOs8 安装 Jenkins 总结 文章目录CentOs8 安装 Jenkins 总结一、版本&安装二、Jenkins配置1. 配置 Java 路径2. 配置用户名称(一定要改成root&#xff01;&#xff01;&#xff01;)3. 启动 &停止& 重启 Jenkins 命令1. 启动2. 停止3. 重启三、成功四、问题记录…

同事每天早下班,原来是用了这8个开发工具

引言 工欲善其事必先利其器&#xff0c;说的就是工匠要想更加高效的做事情&#xff0c;就得先将工具变得锋利。那么对于程序员来说同样也是如此&#xff0c;如果要想每天早点下班&#xff0c;就必须借助于一些开发工具来提高自己的工作效率&#xff0c;今天慕枫就给大家总结一…

【手撕源码】vue2.x双向数据绑定原理

&#x1f431; 个人主页&#xff1a;不叫猫先生 &#x1f64b;‍♂️ 作者简介&#xff1a;前端领域新星创作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab;系列专栏&#xff1a;vue3从入门…

Teradata在华落幕,国产化崛起,袋鼠云数栈会是更好的选择吗?

2月15日&#xff0c;数仓软件巨头Teradata宣布根据其对中国当前和未来商业环境的慎重评估&#xff0c;将逐步结束在中国的直接运营&#xff0c;后续进入中国公司的关闭程序。 一石激起千层浪&#xff0c;这一消息&#xff0c;在国内的To B市场引起了广泛关注。Teradata这家进入…

mongoDB的安装与使用

MongoDB安装MongoDB官方网站&#xff1a;https://www.mongodb.com/try/download/community-kubernetes-operator2软件安装权限不足&#xff1a;https://www.javaclub.cn/database/56541.htmlstep1:打开安装包直接点击Nextstep2&#xff1a;继续点击Nextstep3&#xff1a;点击自…

Oracle 12C以上统计信息收集CDB、PDB执行时间不一致问题

文章目录前言一、统计信息窗口期调查二、时区调查三、查询alert记录四、why Database Statistic Collection Job is running two times inside a Maintenance Window?五、Default Scheduler Timezone Value In PDB$SEED Different Than CDB六、总结前言 在实际工作中发现一个…

Tina_Linux_功耗管理_开发指南

Tina Linux 功耗管理开发指南 1 概述 1.1 编写目的 简要介绍tina 平台功耗管理机制&#xff0c;为关注功耗的开发者&#xff0c;维护者和测试者提供使用和配置参考。 1.2 适用范围 表1-1: 适用产品列表产品名称内核版本休眠类型参与功耗管理的协处理器R328Linux-4.9NormalS…

ESP32设备驱动-MLX90393磁场传感器驱动

MLX90393磁场传感器驱动 文章目录 MLX90393磁场传感器驱动1、MLX90393介绍2、硬件准备3、软件准备4、驱动实现1、MLX90393介绍 MLX90393 磁场传感器可以在运行时重新编程为不同的模式和不同的设置。 该传感器使用 Melexis 专有的 Triaxis 技术提供与沿 XYZ 轴感应的磁通密度成…

Matplotlib之画图模块

目录 matplotlib简介 条形图 折线图 散点图 matplotlib简介 Matplotlib 是 Python 的绘图库&#xff0c;它能让使用者很轻松地将数据图形化&#xff0c;并且提供多样化的输出格式。 Matplotlib 可以用来绘制各种静态&#xff0c;动态&#xff0c;交互式的图表。 Matplotli…

OpenCV-PyQT项目实战(9)项目案例04:视频播放

欢迎关注『OpenCV-PyQT项目实战 Youcans』系列&#xff0c;持续更新中 OpenCV-PyQT项目实战&#xff08;1&#xff09;安装与环境配置 OpenCV-PyQT项目实战&#xff08;2&#xff09;QtDesigner 和 PyUIC 快速入门 OpenCV-PyQT项目实战&#xff08;3&#xff09;信号与槽机制 …

mars3d对geojson图层分属性设置样式

开发中可能会遇到如下需求&#xff0c;在全省的数据中按某个属性⾼亮展示某市区。此时就需要使⽤分属性样式的api了。⽂档如下。GeoJsonLayer - Mars3D API文档属性是根据⽮量数据的属性进⾏匹配。可以通过 layer.graphics[0]?.attr ⽅式获取。 指导有哪些属性之后先设置…

Spark3 新特性之AQE

文章目录Spark3 AQE一、 背景二、 Spark 为什么需要AQE? (Why)三、 AQE 到底是什么&#xff1f;(What)四、AQE怎么用&#xff1f;(How)4.1 自动分区合并4.2 自动数据倾斜处理4.3 Join 策略调整五、对比验证5.1 执行耗时5.2 自动分区合并5.3 自动数据倾斜处理六、结论Spark3 AQ…

电脑录屏怎么操作,操作步骤详解,2023新版

在日常的学习、生活和工作中&#xff0c;当小伙伴想要分享一段游戏视频或者教学视频时&#xff0c;电脑录屏就显得尤为重要了。但是小伙伴你是否知道电脑录屏怎么操作&#xff1f;今天小编就分享电脑录屏操作步骤的详细教程&#xff0c;一起来看看吧。 电脑录屏怎么操作1&#…

【剑指Offer】重建二叉树(递归+迭代)

重建二叉树一、递归法二、迭代法题目链接 题目描述&#xff1a; 输入某二叉树的前序遍历和中序遍历的结果&#xff0c;请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 示例 1: Input: preorder [3,9,20,15,7], inorder [9,3,15,…

C进阶:7.程序环境和预处理

目录 1.程序的翻译环境和执行环境 2.详解编译 链接 2.1翻译环境 2.2编译本身也分为几个阶段&#xff1a; 2.3运行环境 3.预处理详解 3.1预定义符号 3.2 #define 3.2.1 #define 定义标识符 3.2.2 #define 定义宏 3.2.3 #define 替换规则 3.2.4 # 和 ## 3.2.5带副…

大规模食品图像识别:T-PAMI 2023论文解读

美团基础研发平台视觉智能部与中科院计算所展开科研课题合作&#xff0c;共同构建大规模数据集Food2K&#xff0c;并提出渐进式区域增强网络用于食品图像识别&#xff0c;相关研究成果已发表于T-PAMI 2023。本文主要介绍了数据集特点、方法设计、性能对比&#xff0c;以及基于该…

Unreal Engine 虚幻引擎,性能分析,优化(二)

一、CPU 性能分析 如渲染线程中出现 CPU 受限&#xff0c;原因可能是绘制调用过多。这是一个常见问题&#xff0c;美术师通常会将绘制调用进行组合&#xff0c;从而减少消耗&#xff08;如&#xff1a;将多个墙壁组合为一个网格体&#xff09;。实际消耗存在于多个区域中&…