当前位置: 首页 > news >正文

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

第 1 阶段:自动微分

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

1.1什么是变量

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

1.2 实现Variable 类

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

import numpy as npclass Variable:def __init__(self, data, grad=None):self.data = dataself.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:@staticmethoddef forward(x):# 前向传播逻辑raise NotImplementedError@staticmethoddef backward(x, gy):# 反向传播逻辑raise NotImplementedError

2.3 使用Function 类

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

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

步骤3:函数的连续调用

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

3.1 Exp函数的实现

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

3.2 函数的连续调用

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

# 3.2 函数的连续调用
# 通过链式调用多个函数,可以构建复杂的计算图。class Square(Function):
    @staticmethoddef forward(x):return x ** 2    @staticmethoddef backward(x, gy):return 2 * x * gyclass Exp(Function):
    @staticmethoddef forward(x):return np.exp(x)    @staticmethoddef 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 = 5derivative = 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 = dataself.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 NotImplementedErrordef backward(self, gy):# 反向传播逻辑raise NotImplementedError

6.3 Square类和Exp类的功能扩展

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

# 6.3 Square 类和 Exp 类的功能扩展
class Square(Function):
    @staticmethoddef forward(x):y = x.data ** 2output = Variable(y)output.creator = Squarereturn output    @staticmethoddef backward(x, gy):gx = 2 * x.data * gy.datareturn Variable(gx)class Exp(Function):
    @staticmethoddef forward(x):y = np.exp(x.data)output = Variable(y)output.creator = Expreturn output    @staticmethoddef backward(x, gy):gx = np.exp(x.data) * gy.datareturn 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 unittestclass 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 unittestclass Square(Function):
    @staticmethoddef forward(x):return x ** 2    @staticmethoddef backward(x, gy):return 2 * x * gyclass 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 npclass Variable:def __init__(self, data, grad=None):self.data = dataself.grad = grad if grad is not None else np.zeros_like(data)class Function:def forward(self, x):raise NotImplementedErrordef backward(self, x, gy):raise NotImplementedErrorclass Square(Function):def forward(self, x):return x ** 2def backward(self, x, gy):return 2 * x * gydef gradient_check(f, x, epsilon=1e-4):x.grad = Nonef(x).backward()analytic_grad = x.gradnum_grad = np.zeros_like(x.data)for i in range(x.data.size):tmp_val = x.data[i]x.data[i] = tmp_val + epsilonf_pos = f(x).datax.data[i] = tmp_val - epsilonf_neg = f(x).datax.data[i] = tmp_valnum_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.mrgr.cn/news/78046.html

相关文章:

  • Springboot 整合 Java DL4J 搭建智能问答系统
  • 小米C++ 面试题及参考答案下(120道面试题覆盖各种类型八股文)
  • Django 自定义路由转换器
  • 免费下载 | 2025中国5G产业全景图谱报告
  • Leetcode647. 回文子串(HOT100)
  • 【可解释性机器学习】基于SHAP进行特征选择和贡献度计算
  • AI-Talk开发板之Camera
  • OpenCV基础(3)
  • 优化Docker镜像:提升部署效率与降低资源消耗
  • Spring Boot 与 Java 决策树:构建智能分类系统
  • 数字逻辑(一)——导论
  • 241125学习日志——[CSDIY] [ByteDance] 后端训练营 [18]
  • 车载摄像camera基础知识和评估
  • 2024年底-Arch linux或转为0BSD许可证!
  • 打造智能扩容新纪元:Kubernetes Custom Metrics深度解析
  • Vue 修饰符的作用与应用,应用场景详细介绍
  • 栈、队列、链表
  • 241124学习日志——[CSDIY] [ByteDance] 后端训练营 [14]
  • 传输控制协议(TCP)和用户数据报协议(UDP)
  • WebSocket 常见问题及解决方案