深度学习笔记1:自动微分与神经网络实现(附代码)

news2024/11/26 8:39:01

第 1 阶段:自动微分

步骤1:作为“箱子”的变量

1.1什么是变量

变量是存储数据的容器,用于在程序中传递和修改数据。变量是在程序中用于存储和操作数据的基本单元,它们可以随计算过程而改变其值。

1.2 实现Variable 类

Variable 类不仅存储了数据,还能跟踪梯度信息,并记录其创建者。

import numpy as np
 
class Variable:
    def __init__(self, data, grad=None):
        self.data = data
        self.grad = grad if grad is not None else np.zeros_like(data)
        self.creator = None  # 用于记录创建该变量的函数

1.3 NumPy的多维数组

NumPy提供了强大的多维数组对象ndarray,支持高效的数组和矩阵运算。

步骤2:创建变量的函数

2.1 什么是函数

函数是一段封装了特定逻辑的可重用代码块,接受输入并产生输出。

函数是执行特定任务的代码块,可以接受输入并返回输出。

2.2 Function类的实现

通过定义静态方法 forward 和 backward 来分别处理前向和反向传播

class Function:
    @staticmethod
    def forward(x):
        # 前向传播逻辑
        raise NotImplementedError
    
    @staticmethod
    def backward(x, gy):
        # 反向传播逻辑
        raise NotImplementedError

2.3 使用Function 类

通过继承Function类并实现其静态方法forwardbackward来创建具体的函数。

#通过继承 Function 类并实现其方法来创建具体的函数,如 Exp 函数class Exp(Function):
    @staticmethod
    def forward(x):
        return np.exp(x)
​
    @staticmethod
    def backward(x, gy):
        return gy * np.exp(x)

步骤3:函数的连续调用

通过依次调用多个函数,可以构建复杂的计算图,实现更复杂的计算逻辑。

3.1 Exp函数的实现

class Exp(Function):
    @staticmethod
    def forward(x):
        return np.exp(x)
    
    @staticmethod
    def backward(x, gy):
        return gy * np.exp(x)

3.2 函数的连续调用

通过链式调用多个函数,可以构建复杂的计算图。

# 3.2 函数的连续调用
# 通过链式调用多个函数,可以构建复杂的计算图。

class Square(Function):
    @staticmethod
    def forward(x):
        return x ** 2

    @staticmethod
    def backward(x, gy):
        return 2 * x * gy

class Exp(Function):
    @staticmethod
    def forward(x):
        return np.exp(x)

    @staticmethod
    def backward(x, gy):
        return np.exp(x) * gy

# 连续调用示例
def function_chain_call():
    """
    此函数展示了函数的连续调用
    """
    x = Variable(np.array(2.0))
    y = Square.forward(x)
    z = Exp.forward(y)
    print(z.data)

步骤4:数值微分

4.1 什么是导数

导数是函数在某一点的变化率,用于描述函数在该点的切线斜率。导数表示函数在某一点处的变化率,反映了函数的局部变化趋势。

4.2 数值微分的实现

通过数值方法(如有限差分法)近似计算导数。使用有限差分等数值方法来近似计算导数。


def numerical_derivative(f, x, epsilon=1e-4):
    """
    数值微分函数,通过有限差分法近似计算导数

    参数:
    f: 要计算导数的函数
    x: 计算导数的点
    epsilon: 微小增量,默认为 1e-4

    返回:
    函数在 x 点的近似导数
    """
    return (f(x + epsilon) - f(x - epsilon)) / (2 * epsilon)

# 示例函数
def example_function(x):
    return x ** 2 + 3 * x + 1

# 计算数值导数
def numerical_derivative_example():
    """
    此函数计算示例函数在特定点的数值导数
    """
    x = 5
    derivative = numerical_derivative(example_function, x)
    print(derivative)

4.3 复合函数的导数

复合函数的导数可以通过链式法则计算。根据链式法则,复合函数的导数是各层函数导数的乘积。

4.4 数值微分存在的问题

数值微分可能受到舍入误差的影响,而且对于复杂函数计算量较大。

步骤5:反向传播的理论知识

5.1 链式法则

链式法则是计算复合函数导数的基本法则。是计算复合函数导数的重要法则,通过依次相乘各层的导数来得到最终的导数。

5.2 反向传播的推导

反向传播通过链式法则从输出层向输入层逐层计算梯度。从输出层开始,依据链式法则逐步向输入层计算梯度。

5.3 用计算图表示

计算图是一种表示函数计算过程的图形化工具,可以帮助理解反向传播。以图形的方式直观展示函数的计算流程和依赖关系,有助于理解反向传播。

步骤6:手动进行反向传播

6.1 Variable 类的功能扩展

Variable类中添加用于记录梯度传播路径的属性。添加属性来记录梯度的传播路径和相关信息。

# 6.1 Variable 类的功能扩展
class Variable:
    def __init__(self, data, grad=None, creator=None):
        self.data = data
        self.grad = grad if grad is not None else np.zeros_like(data)
        self.creator = creator

6.2 Function类的功能扩展

Function类中添加用于记录输入变量的属性。记录输入变量,以便在反向传播时使用。

# 6.2 Function 类的功能扩展
class Function:
    def __init__(self):
        self.inputs = []

    def forward(self, *inputs):
        self.inputs = inputs
        # 前向传播逻辑
        raise NotImplementedError

    def backward(self, gy):
        # 反向传播逻辑
        raise NotImplementedError

6.3 Square类和Exp类的功能扩展

为这些具体的函数类添加完整的反向传播实现。

# 6.3 Square 类和 Exp 类的功能扩展
class Square(Function):
    @staticmethod
    def forward(x):
        y = x.data ** 2
        output = Variable(y)
        output.creator = Square
        return output

    @staticmethod
    def backward(x, gy):
        gx = 2 * x.data * gy.data
        return Variable(gx)

class Exp(Function):
    @staticmethod
    def forward(x):
        y = np.exp(x.data)
        output = Variable(y)
        output.creator = Exp
        return output

    @staticmethod
    def backward(x, gy):
        gx = np.exp(x.data) * gy.data
        return Variable(gx)

6.4 反向传播的实现

通过递归地调用函数类的 backward 方法,实现梯度的反向传播。

# 6.4 反向传播的实现
def backward(v):
    """
    反向传播函数

    参数:
    v: 最终的输出变量
    """
    funcs = []
    while v.creator is not None:
        funcs.append(v.creator)
        v = v.creator.inputs[0]

    gy = Variable(np.ones_like(v.data))
    for func in funcs[::-1]:
        gy = func.backward(func.inputs[0], gy)

步骤7:反向传播的自动化

7.1 为反向传播的自动化创造条件

修改 Variable 和 Function 类的结构和方法,使其能够自动记录和处理计算图。

7.2 尝试反向传播

进行初步的测试和验证,确保自动化反向传播的基本功能正常。

7.3 增加backward方法

在 Variable 类中添加 backward 方法,以方便触发反向传播过程。

# 7.3 增加 backward 方法
class Variable:
    def backward(self):
        """
        触发反向传播
        """
        backward(self)

步骤8:从递归到循环

8.1 现在的Variable 类

分析当前Variable类的实现,具体的结构和功能,找出可能存在的性能瓶颈或可优化点。

8.2 使用循环实现

使用循环代替递归,以提高性能。将递归方式改为循环方式,以提高计算效率和避免递归深度限制。

8.3 代码验证

编写测试用例来验证循环实现的正确性和性能优

# 9.2 简化 backward 方法
class Variable:
    def backward(self):
        """
        简化 backward 方法的调用
        """
        backward(self)

势。

步骤9:让函数更易用

9.1 作为Python函数使用

修改Function类,使其能够像Python函数一样调用,提高代码的简洁性和可读性。

# 9.1 作为 Python 函数使用
""" 
使 Function 类能够像函数一样被调用
参数:*inputs: 输入参数
返回:计算结果
"""

class Function:
    def __call__(self, *inputs):
        
        outputs = self.forward(*inputs)
        return outputs

9.2 简化backward方法

简化backward方法的调用方式。优化 backward 方法的调用接口,使其更易于使用和理解。

# 9.2 简化 backward 方法
class Variable:
    def backward(self):
        """
        简化 backward 方法的调用
        """
        backward(self)

9.3 只支持ndarray

限制输入数据类型为ndarray,确保计算的一致性和高效性,以提高性能。

步骤10:测试

10.1 Python的单元测试

利用 Python 的 unittest 框架编写单元测试用例,对各个功能模块进行测试。

# 10.1 Python 的单元测试
import unittest

class TestFunctions(unittest.TestCase):
    def test_square_forward(self):
        """
        测试 Square 函数的前向传播
        """
        x = Variable(np.array(2.0))
        y = Square.forward(x)
        self.assertEqual(y.data, 4.0)

    def test_square_backward(self):
        """
        测试 Square 函数的反向传播
        """
        x = Variable(np.array(2.0))
        y = Square.forward(x)
        y.backward()
        self.assertEqual(x.grad, 4.0)

if __name__ == '__main__':
    unittest.main()

10.2 square函数反向传播的测试

专门针对 square 函数的反向传播进行详细的测试,确保其正确性。

import numpy as np
import unittest

class Square(Function):
    @staticmethod
    def forward(x):
        return x ** 2

    @staticmethod
    def backward(x, gy):
        return 2 * x * gy

class TestSquareBackpropagation(unittest.TestCase):
    def test_square_backpropagation(self):
        x = Variable(np.array(3.0))
        y = Square.forward(x)
        y.backward()
        expected_grad = 6.0  
        self.assertAlmostEqual(x.grad, expected_grad, places=5)

if __name__ == '__main__':
    unittest.main()

10.3 通过梯度检验来自动测试

使用梯度检验方法自动验证反向传播计算的准确性。

import numpy as np

class Variable:
    def __init__(self, data, grad=None):
        self.data = data
        self.grad = grad if grad is not None else np.zeros_like(data)

class Function:
    def forward(self, x):
        raise NotImplementedError

    def backward(self, x, gy):
        raise NotImplementedError

class Square(Function):
    def forward(self, x):
        return x ** 2

    def backward(self, x, gy):
        return 2 * x * gy

def gradient_check(f, x, epsilon=1e-4):
    x.grad = None
    f(x).backward()
    analytic_grad = x.grad

    num_grad = np.zeros_like(x.data)
    for i in range(x.data.size):
        tmp_val = x.data[i]
        x.data[i] = tmp_val + epsilon
        f_pos = f(x).data
        x.data[i] = tmp_val - epsilon
        f_neg = f(x).data
        x.data[i] = tmp_val
        num_grad[i] = (f_pos - f_neg) / (2 * epsilon)

    return np.allclose(analytic_grad, num_grad, atol=1e-5)

x = Variable(np.random.rand(5))
if gradient_check(Square, x):
    print("梯度检验通过")
else:
    print("梯度检验未通过")

10.4 测试小结

总结测试结果,分析是否通过测试,找出可能存在的问题和改进方向。

import numpy as np

# 假设之前的测试结果存储在一个字典中
test_results = {
    "square_backpropagation": True,
    "gradient_check": True
}

# 总结测试通过情况
passed_tests = [test_name for test_name, passed in test_results.items() if passed]
failed_tests = [test_name for test_name, passed in test_results.items() if not passed]

# 打印通过和未通过的测试
if passed_tests:
    print("通过的测试:")
    for test in passed_tests:
        print(f"- {test}")

if failed_tests:
    print("未通过的测试:")
    for test in failed_tests:
        print(f"- {test}")

# 分析可能存在的问题和提出改进方向
print("可能的问题:")
if failed_tests:
    print(" - 未通过测试的函数实现可能有误或未处理好边界情况。")
else:
    print(" - 复杂场景和极端输入下可能有潜在问题,测试用例覆盖可能不全。")

print("改进方向:")
print(" - 调试未通过测试的函数。")
print(" - 补充更多样化的测试用例。")
print(" - 优化数值计算方法和算法。")
print(" - 定期重构和优化代码。")

本篇系统梳理了 DeZero 框架,涵盖从自动微分到神经网络测试的全流程,包括变量与函数的相关操作数值微分与反向传播函数类扩展与优化、易用性提升及全面测试环节。

由于篇幅较长且整理过程较为繁琐,我计划逐步整理并发布后续内容。我深信,科技应当服务于大众,我希望可以为促进知识的共享与学习,贡献自己绵薄之力,根据我的整理节省后来人的时间。如果对自动化定位感兴趣,可以看之前相关博客 

关于python自动化定位的9种函数方法-CSDN博客

python自动登录跳转获取信息等_自动登录网站查找信息做记录-CSDN博客

整理不易,诚望各位看官点赞 收藏 评论 予以支持,这将成为我持续更新的动力源泉。若您在阅览时存有异议或建议,敬请留言指正批评,让我们携手共同学习,共同进取,吾辈自当相互勉励!

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

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

相关文章

dubbo-go框架介绍

框架介绍 什么是 dubbo-go Dubbo-go 是 Apache Dubbo 的 go 语言实现,它完全遵循 Apache Dubbo 设计原则与目标,是 go 语言领域的一款优秀微服务开发框架。dubbo-go 提供: API 与 RPC 协议:帮助解决组件之间的 RPC 通信问题&am…

不只是请求和响应:使用Fiddler抓包URL和Method全指南(中)

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持! 不只是请求和响应:使用Fiddler抓包HTTP协议全指南(上)-CSDN博客https://blog.csdn.net/Chunfeng6yugan/article/details/144005872?spm1001.2014.3001.5502 🙉在(上)篇博客中&#xf…

Linux操作系统学习---初识环境变量

目录 ​编辑 环境变量的概念: 小插曲:main函数的第一、二个参数 获取环境变量信息: 1.main函数的第三个参数 2.查看单个环境变量 3.c语言库函数getenv() 和环境变量相关的操作指令: 1.export---导出环境变量: 2.unse…

跨平台应用开发框架(1)----Qt(组件篇)

目录 1.Qt 1.Qt 的主要特点 2.Qt的使用场景 3.Qt的版本 2.QtSDK 1.Qt SDK 的组成部分 2.安装 Qt SDK 3.Qt SDK 的优势 3.Qt初识 1.快速上手 widget.cpp mian.cpp widget.h Helloworld.pro 2.对象树 3.坐标系 4.信号和槽 1. 信号和槽的基本概念 2. 信号和槽的…

mysql索引失效的五种情况

第一种 违反最左前缀法则 这个是针对联合索引的。 假设有个tb_seller表,现在给三个字段建立联合索引,建立的时候字段顺序不可随便设置,字段顺序: name, status, address。下图Seq_in_index对应的是联合索引顺序。 判断索引失效用…

H3C OSPF 多区域实验

目录 前言 实验拓扑 实验需求 实验解析 路由器配置 测试 前言 此篇文章为 OSPF多区域试验,建议先食用OSPF单区域实验,理解实验原理 学习基本配置,再来使用此篇,效果更佳!(当然如果你已经了解原理与基…

算法 Java实现

一.查找算法 1.分块查找 二.排序算法 1.冒泡排序

优先算法 —— 双指针系列 - 快乐数

1. 快乐数 题目链接: 202. 快乐数 - 力扣(LeetCode)https://leetcode.cn/problems/happy-number/description/ 2. 题目解析 示例1: 示例2: 3. 算法原理 两种情况:我们可以把两种情况都看作为循环&#xff0…

【机器学习】——卷积与循环的交响曲:神经网络模型在现代科技中的协奏

🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大二学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL&#xff0…

php 导出excel 一个单元格 多张图片

public function dumpData(){error_reporting(0); // 禁止错误信息输出ini_set(display_errors, 0); // 不显示错误$limit $this->request->post(limit, 20, intval);$offset $this->request->post(offset, 0, intval);$page floor($offset / $limit) 1 ;$wh…

几天游记啊

绿灯常亮,黄灯闪,就是没有上线状态 一 2024.11.24 青浦圆通信息中心:vsphere client IDC运维专员可能就这项技能稀缺 二 2024.11.25 1 字节服务器外包单位有孚网路 什么互联网交换中心 不了解的人还以为是国家火炬计划呢!实际…

【C++动态规划 子集状态压缩】2002. 两个回文子序列长度的最大乘积|1869

本文涉及知识点 C动态规划 位运算、状态压缩、枚举子集汇总 LeetCode2002. 两个回文子序列长度的最大乘积 给你一个字符串 s ,请你找到 s 中两个 不相交回文子序列 ,使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符&…

记录:从.Net程序的内存转储文件中提取内存数据过程

1.准备材料:xxx.dump转储文件,VS2022 2.提取过程 使用VS打开xxx.dump文件VS中点击 调试托管内存 按钮查找需要导出的变量,注:通过类型查找时基础变量类型跟原类型不一样,如string对应String,bool对应Bool…

Nacos学习文档

目录 1、Nacos是什么2、Nacos名词介绍3、Nacos中的data id是如何组装的?4、Nacos 融合 Spring Cloud,成为注册配置中心4.1、Maven依赖作用4.2、启动配置管理4.2.1、添加依赖4.2.2、在 bootstrap.yml(也支持properties格式) 中添加…

QT简易项目 数据库可视化界面 数据库编程SQLITE QT5.12.3环境 C++实现

案例需求&#xff1a; 完成数据库插入&#xff0c;删除&#xff0c;修改&#xff0c;查看操作。 分为 插入&#xff0c;删除&#xff0c;修改&#xff0c;查看&#xff0c;查询 几个模块。 代码&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget…

【Linux学习】【Ubuntu入门】2-3 make工具和makefile引入

1.使用命令新建三个.c文件vi main.c&#xff0c;vi input.c&#xff0c;vi caclcu.c&#xff0c;两个.h文件vi input.h&#xff0c;vi caclcu.h 2.vi Makefile&#xff1a;新建Makefile文件&#xff0c;输入一下内容 注意&#xff1a;命令列表中每条命令前用TAB键&#xff0c;不…

Gazebo仿真实现无人机+Apriltag码动态跟踪

目录 演示 一、环境 二、配置 创建模型 首先相机创建 添加相机 Apriltag创建 地图添加apriltag码 Apriltag_ros配置 三、代码运行 四、问题 修改相机模型的参数 演示 一、环境 ROSgazebo配置 Px4Mavros Apriltag_ros编译 二、配置 在默认的mavros_posix_sitl.l…

H.265流媒体播放器EasyPlayer.js播放器提示MSE不支持H.265解码可能的原因

随着人工智能和机器学习技术的应用&#xff0c;流媒体播放器将变得更加智能&#xff0c;能够根据用户行为和偏好提供个性化的内容推荐。总体而言&#xff0c;流媒体播放器的未来发展将更加注重技术创新和用户互动&#xff0c;以适应不断变化的市场需求和技术进步。 提示MSE不支…

加菲工具 - 好用免费的在线工具集合

加菲工具 https://orcc.online AI 工具 集合了目前主流的&#xff0c;免费可用的ai工具 文档处理 pdf转word、office与pdf互转等等工具都有链接 图片图标 统计了好用免费的在线工具 编码解码 base64编码解码、url编码解码、md5计算、进制转换等等 其它 还有其他好用的…

【linux学习指南】初识Linux进程信号与使用

文章目录 &#x1f4dd;信号快速认识&#x1f4f6;⽣活⻆度的信号&#x1f4f6; 技术应⽤⻆度的信号&#x1f309; 前台进程&#xff08;键盘&#xff09;&#x1f309;⼀个系统函数 &#x1f4f6;信号概念&#x1f4f6;查看信号 &#x1f320; 信号处理&#x1f309; 忽略此信…