免费、绿色、专业的手机游戏中心下载安装平台-游家吧

当前位置: 首页 > 教程攻略 > 从零实现深度学习框架 基础框架的构建

从零实现深度学习框架 基础框架的构建

更新时间:2026-02-15 12:56:44

爱情和生活模拟rpg手机版
  • 类型:体育竞技
  • 大小:87.5mb
  • 语言:简体中文
  • 评分:
查看详情

从零实现深度学习框架 基础框架的构建

本文介绍了从零开始实现深度学习框架的方法。借鉴了飞桨框架的学习活动和相关书籍。首先,解释了深度学习框架是能够自动求导的库,然后详细说明如何通过计算图来实现这个过程,包括节点类的设计及前向和反向传播的逻辑。以一个吃鸡排名预测挑战赛为例,演示如何使用简易框架处理数据、构建网络模型、进行训练,并进行预测。

从零实现深度学习框架

飞桨框架学习(LearnDL)是一个由Mr. Sun发起的活动,主旨在于以简单易懂的方式了解深度学习框架、构造深度学习框架乃至于改写深度学习框架。整体内容包括了入门级的名词解释乃至后续的框架实现工作,推荐新入门深度学习、对神经网络有些困惑、不知道如何给Paddle提PR、不知道如何参加黑客松、觉得平台上的交流充满“黑话”的同学一起参与学习~

本项目受到上述活动的启发,并利用了Python来实现深度学习框架。通过名词解释和代码示例的形式,对深度学习的基础概念进行了简要介绍,包括神经网络、反向传播算法以及一个简单模型的实现。

强烈推荐大家看上面那本书,对于新手入门很不错~

什么是深度学习框架

深度学习框架本质上是可视为一种库、包或简单的Python文件,专注于提供所需的工具、函数和模块以便用户能够高效地构建和训练深度学习模型即神经网络。例如,PaddlePaddle正是这样的一种框架。

更具体地说,当未采用深度学习框架时,用户需自行编写模型训练中的求导和梯度反馈逻辑。然而,一旦使用了深度学习框架,只需构建模型架构即可,而无需深入理解其内部的求导过程与梯度计算方法。例如考虑一个简单的函数表达式:\[ y = \tan(g\sin(kx\cos(x^))) \]其中 \( x \) 是输入变量,\( k, g, h \) 是待拟合参数,而 \( y \) 是输出结果。在不使用深度学习框架的情况下,必须手动设计并计算这些参数的导数(即梯度),才能完成参数拟合任务。相反地,当采用深度学习框架时,只需告知模型有这么一个公式,框架会自动进行梯度计算,并提供所需的学习迭代过程,从而大幅减少用户的时间和精力投入。由此可见,深度学习框架简化了复杂的数据处理与分析任务的实现过程,特别是对于那些需要反复训练和参数调整的非线性函数。

如何实现深度学习框架

按照之前的指导,构建深度学习框架只需编写一个包含了多种高效功能的Py文件。

我们可以手动计算特定函数的导数,如tan(x)、tantanx、tantatanx等,但这远远不够应对复杂多变的世界。为满足用户多样化需求,我们需要一个高效的代码架构来处理各类数学函数的导数。这样我们既能在少量代码基础上实现广泛的计算功能,又能够快速适应新出现的需求。

为此,我们引入“计算图”的概念。

计算图

计算图是深度学习框架中的一个重要组成部分,通常用流程图或数据流图来表示。它由节点构成,这些节点代表数据单元或运算单元,并且通过连接线表示了它们之间的运算关系。举个例子,假设输入为xxx值,经过一系列计算后得到结果c,并与标签y进行均方损失(MSE)求解过程。这个图展示了从原始输入到最终输出结果的数据流动路径,是理解和实现深度学习模型不可或缺的一部分。

为了简化计算过程,我们可以采取以下措施:首先,明确所有的运算都应是单一操作或基本运算,避免复杂度提升。例如,对于这个图像的例子,当我们需要处理三个点的位置和连接方式时,我们可以通过将其分解为两个二元运算来完成。通过这种拆分的方式,任何多元运算都能够转化为由简单的加、减、乘、除等基本运算组合而成的表达式。请记住,这只是一个简化计算过程的方法,实际应用中可能还需要结合具体问题进行灵活调整和优化。

当然,tantaantananx也是由三个tana组成。我们的重点应该是处理一元或二元运算。至于多变量运算的扩展支持,我们会在将来讨论。

以图举例,所有的节点拥有最多两个输入和一个输出,并且包含特殊的计算过程。例如,+运算是相加,而×则是相乘。所有这些节点可以归属于同一类,其成员包括:- 父节点:如xw“×”运算的父节点,若该节点是根节点,则记作None - 值:每个节点都具有一个特定的值 - 计算:根据父节点所指定的计算流程进行操作

下面简单实现一下~ In [1]

class Node(object): def __init__(self, Papa = None, Mama = None, Value = 0): # 通常使用Father表示父节点,这里使用Papa和Mama纯粹因为更有趣一些 self.Papa = Papa self.Mama = Mama self.value = Value def forward(self): self.value = self.value登录后复制

上述构造了一个基础的节点类,其forward是一个恒等映射,下面分别派生对应的加法节点,乘法节点,和MSE节点 In [2]

# 加法节点class AddNode(Node): def forward(self): if self.Papa != None: self.Papa.forward() # 基础节点的父节点不需要计算,但是非基础节点的父节点需要保证有值 if self.Mama != None: self.Mama.forward() self.value = self.Papa.value + self.Mama.value# 乘法节点class MulNode(Node): def forward(self): if self.Papa != None: self.Papa.forward() if self.Mama != None: self.Mama.forward() self.value = self.Papa.value * self.Mama.value# 损失函数节点class MSENode(Node): def forward(self): if self.Papa != None: self.Papa.forward() if self.Mama != None: self.Mama.forward() self.value = (self.Papa.value - self.Mama.value)**2登录后复制

只需要对上述几个Node节点进行线性组合,即可完成一次前向计算。 In [3]

x1 = Node(Value = 1) x2 = Node(Value = 2) x3 = Node(Value = 3) w1 = Node(Value = 1) w2 = Node(Value = 1) w3 = Node(Value = 1) m1 = MulNode(Papa = x1, Mama = w1) m2 = MulNode(Papa = x2, Mama = w2) m3 = MulNode(Papa = x3, Mama = w3) a1 = AddNode(Papa = m1, Mama = m2) a2 = AddNode(Papa = a1, Mama = m3) y = Node(Value = 6) # 1+2+3 = 6, 这样MSE的结果为0result = MSENode(Papa = a2, Mama = y, Value = 20)print('计算前 result.value = ', result.value) result.forward()print('计算后 result.value = ', result.value)登录后复制

计算前 result.value = 20 计算后 result.value = 0登录后复制

观察之下,一旦基础内容被明确设定,用户只需聚焦于构建节点及连接方式,类似于在使用Paddle时需继承nn.Layer后,主要关注不同组件(如线性、卷积等)间的衔接操作。

梯度反馈

计算图的双重功能:不仅用于前向过程,还用于计算梯度反馈计算图不仅能帮助我们执行前向计算过程,还能在需要时为模型提供计算梯度的反馈信息。例如,当我们讨论m对于xw梯度时,每个子节点都非常清楚自己的贡献大小。比如mx影响是通过参数w生的。然而,在计算图中,这些具体的梯度值对于参与计算的节点来说并不都是已知的。因此,当网络完成前向计算后,我们需要利用反向传播算法来进一步细化每个参数对最终结果的影响。通过这种方式,我们能够准确地评估模型在不同条件下可能的行为,并据此进行优化和调整。

简单来说,子节点还需要增加一些属性: 父节点的梯度 从子节点收到的梯度

进一步地,当我们接收到梯度之后,还需对梯度进行学习,即调整参数。在此过程中,我们还需要以下三个属性:参数指示符:如果设置为True,表示当前的参数需要随着梯度的变化而更新;例如,w指示符为True,则表明我们需要根据梯度来更新这个参数;x不需要进行这种更新。梯度更新函数:通过这个函数,我们可以确定我们该在哪个方向上调整我们的参数。这意味着如果某个参数的梯度是负值,那么我们可能需要增加它的权重,反之亦然。学习率:这里的“学习”并不是指对算法本身的“学习”,而是指的是根据当前的梯度来调整我们的更新速度。它是一个控制我们迭代过程中的步长的重要因素,通常设置在间。

结合以上几点,对上述Node类进行改写如下 In [4]

class Node(object): def __init__(self, Papa = None, Mama = None, Value = 0, Flag = 0, lr = 0.01): # 通常使用Father表示父节点,这里使用Papa和Mama纯粹因为更有趣一些 self.Papa = Papa self.Mama = Mama self.value = Value self.Flag = Flag self.Papa_grad = 0 self.Mama_grad = 0 self.grad = 1 self.lr = lr def updata(self): # 参数更新 if self.Flag == 1: self.value = self.value - self.lr*self.grad def forward(self): self.value = self.value def backward(self): self.updata()# 加法节点class AddNode(Node): def forward(self): if self.Papa != None: self.Papa.forward() # 基础节点的父节点不需要计算,但是非基础节点的父节点需要保证有值 if self.Mama != None: self.Mama.forward() self.value = self.Papa.value + self.Mama.value def backward(self): if self.Papa != None: self.Papa.grad = self.grad * 1 self.Papa.backward() if self.Mama != None: self.Mama.grad = self.grad * 1 self.Mama.backward() self.updata()# 乘法节点class MulNode(Node): def forward(self): if self.Papa != None: self.Papa.forward() if self.Mama != None: self.Mama.forward() self.value = self.Papa.value * self.Mama.value def backward(self): if self.Papa != None: self.Papa.grad = self.grad * self.Mama.value self.Papa.backward() if self.Mama != None: self.Mama.grad = self.grad * self.Papa.value self.Mama.backward() self.updata()# 损失函数节点class MSENode(Node): def forward(self): if self.Papa != None: self.Papa.forward() if self.Mama != None: self.Mama.forward() self.value = (self.Papa.value - self.Mama.value)**2 def backward(self): if self.Papa != None: self.Papa.grad = self.grad * 2 * (self.Papa.value - self.Mama.value) * 1 self.Papa.backward() if self.Mama != None: self.Mama.grad = self.grad * 2 * (self.Papa.value - self.Mama.value) * -1 self.Mama.backward() self.updata()登录后复制

下面改一下x1的初始值,看看w1会发生什么变化 In [5]

在给定的示例中,通过逐步调整参数xw并观察学习结果的变化,展示了如何使用PyTorch进行优化和训练。这包括创建节点(如加法和乘法操作)、计算MSE并更新参数以最小化误差。最终,显示了每次更新后参数值的变化及其对目标变量y的影响。

计算前 result.value = 计算后 result.value = 第一次更新后 wvalue = 第一次更新后 result.value = 第二次更新后 wvalue = 第二次更新后 result.value = 第三次更新后 wvalue = 第三次更新后 result.value = 第四次更新后 wvalue = 第四次更新后 result.value =

可以看到搭建的网络确实能够随着不断迭代贴近目标值~

基于飞桨常规赛的框架实战

飞桨学习赛:吃鸡排名预测挑战赛属于回归类型。让我们尝试构建最基础的全连接层模型并参与此赛事!

框架封装

最简单的方法是创建一个Python文件,并把上面定义的类封装进去。用户可以通过`import`的方式调用我们提供的接口。不妨给框架命名为`OurDL(我们的深度学习)`,只需要建立一个名为`OurDL.py`的文件,然后将几个节点类的声明复制粘贴到其中即可。

数据预处理

即使拥有深度学习框架,同样需要大量的训练数据。接下来,我们将详细说明如何处理这些数据。

# 提取压缩包! unzip /home/aistudio/data/data137263/pubg_train.csv.zip! unzip /home/aistudio/data/data137263/pubg_test.csv.zip登录后复制 In [1]

```python import pandas as pd# 读取数据 df = pd.read_csv('pubg_train.csv')# 去除具有nan信息的行和列 df = df.dropna(axis= how='any')# 提取需要的特征信息 data = df.iloc[:, ].valuesmax_value = data.max(axis=# 简单归一化数据 data = data / max_valueprint(data.shape) ```

(635716, 14)登录后复制

构造网络

因为数据集包含维度,其中一个维度是目标值,我们需要构建一个从第第全连接层。通过设计网络结构并配置好输入数据后,只需调用`result.forward`进行推理,并使用`result.backward`完成梯度更新。

from OurDL import * x = [] # 数据输入节点w = [] # 参数节点m = [] # 乘法节点a = [] # 加法节点for i in range(13): x.append(Node()) w.append(Node(Flag=1))for i in range(13): m.append(MulNode(Papa = x[i], Mama = w[i]))for i in range(12): if i == 0: a.append(AddNode(Papa = m[0], Mama = m[1])) else: a.append(AddNode(Papa = a[i-1], Mama = m[i+1])) y = Node() result = MSENode(Papa = a[11], Mama = y)登录后复制

训练

In [3]

max_epochs = 1now_step = 0for epoch in range(max_epochs): for sample in data: # 填充输入数据 for i in range(13): x[i].value = sample[i] y.value = sample[-1] result.forward() result.backward() now_step = now_step + 1 print('\rEpoch:{}/{}, Step:{}'.format(epoch,max_epochs,now_step),end="")登录后复制

Epoch:0/1, Step:635716登录后复制

训练后可以简单查看一下模型学习到的参数内容 In [8]

for i in range(13): print('第{}个参数的系数w{}是{}'.format(i,i,w[i].value))登录后复制

第参数的系数w 第参数的系数w 第参数的系数w- 第参数的系数w 第参数的系数w- 第参数的系数w- 第参数的系数w- 第参数的系数w- 第参数的系数w 第参数的系数w- 第参数的系数w 第参数的系数w 第参数的系数w

预测

In [9]

``` df = pd.read_csv('pubg_test.csv') test_data = df.iloc[:,].values# 划分测试集,去除最后一维并归一化,保证最大值不会溢出 test_data = test_data / max_value[:-mean_value = data.mean(axis= # 训练集数据的均值用于填充缺失值# 数据预处理完成,下一步是训练模型 ```

预测import numpy as np predict_result = [] # 保存预测信息for i in range(len(test_data)): sample = test_data[i] # 替换缺失值 for j in range(len(sample)): if np.isnan(sample[j]): sample[j] = mean_value[j] # 填充输入数据 for i in range(: x[i].value = sample[i] y.value = sample[- # 需要注意,result节点只是用于求损失,真正的结果输出其实在a[节点 # 这里既可以运行result也可以运行a[节点,只要最后从a[节点取数据即可 a[-.forward # 记录结果,并且去归一化 out = int(a[-.value * max_value[-) if out<= out = 1 if out>=max_value[-: out = max_value[- predict_result.append(out)登录后复制

# 打包提交predict_df = pd.DataFrame(predict_result, columns = ['team_placement']) predict_df.to_csv('submission.csv',index = None) ! zip submission.zip submission.csv登录后复制

updating: submission.csv (deflated 75%)登录后复制

以上就是从零实现深度学习框架 基础框架的构建的详细内容,更多请关注其它相关文章!

精品推荐

相关文章

最新资讯

热门文章

更多

最新推荐

更多

最新更新

更多