1.WAVM源码解析 —— WASI接口定义、主题找主内部实例初始化及实例链接
2.PyTorch 源码分析(一):torch.nn.Module
3.Pytorch - Module
4.Pytorch源码剖析:nn.Module功能介绍及实现原理
5.Pytorch深入剖析 | 1-torch.nn.Module方法及源码
WAVM源码解析 —— WASI接口定义、源码内部实例初始化及实例链接
从前面文章中,题源我们知道WAVM执行WASM程序的主题找主流程。本文着重解析第三、源码四、题源ios日历源码五部分:生成内部实例、主题找主调用接口与实例链接。源码
生成内部实例的题源关键在于调用接口,接口参数是主题找主Intrinsics::Module类型的列表。内部实例不基于WASM程序,源码仅关注导入导出段内容,题源因此Intrinsics::Module类仅包含Function、主题找主Global、源码Table、题源Memory等元素。宏定义WAVM_INTRINSIC_MODULE_REF(wasi)生成一个Intrinsics::Module对象,其实际实现对应WASI标准接口。
初始化Intrinsics::Module对象通过宏函数WAVM_DEFINE_INTRINSIC_FUNCTION完成,这个宏定义接口并将其赋值给Intrinsics::Module对象。以sched_yield为例,宏定义后生成一个静态的Intrinsics::Function对象,通过构造函数自动赋值到Intrinsics::Module中。
Intrinsics::instantiateModule()函数执行步骤包括:将moduleRefs转化为IR::Module,ar场景源码编译生成的IR::Module,调用实例化接口函数生成内部实例。关键步骤为将外部接口函数转化为WASM格式的thunks函数,并将thunks导出。最终,通过实例化创建出内部实例,与普通实例的主要区别在于导入段内容的获取方式。
链接器实现实例化的一大功能,即提供查询导出项的接口。核心逻辑简单,具体实现则较为复杂,本文不展开解析。关于实例化细节,后续文章将深入探讨。
PyTorch 源码分析(一):torch.nn.Module
nn.Module是PyTorch中最核心和基础的结构,它是操作符/损失函数的基类,同时也是组成各种网络结构的基类(实际上是由多个module组合而成的一个module)。
在Python侧,2.1回调函数注册,2.2 module类定义中,有以下几个重点函数:
重点函数一:将模型的参数移动到CUDA上,内部会遍历其子module。
重点函数二:将模型的仿soul源码参数移动到CPU上,内部会遍历其子module。
重点函数三:将模型的参数转化为fp或者fp等,内部会遍历其子module。
重点函数四:forward函数调用。
重点函数五:返回该net的所有layer。
在类图中,PyTorch的算子都是module的子类,包括自定义算子和整网定义。
在C++侧,3.1 module.to("cuda")详细分析中,本质是将module的parameter&buffer等tensor移动到CUDA上,最终调用的是tensor.to(cuda)。
3.2 module.load/save逻辑中,PyTorch模型保存分为两种,一种是纯参数,一种是带模型结构(PyTorch中的模型结构,本质上是由module、sub-module构造的一个计算图)。
parameter、buffer是通过key-value的形式来存储和检索的,key为module的.name,value为存储具体数据的tensor。
InputArchive/OutputArchive的shadow源码解释write和read逻辑。
通过Module,PyTorch将op/loss/opt等串联起来,类似于一个计算图。基于PyTorch构建的ResNet等模型,是逐个算子进行计算的,tensor在CPU和GPU之间来回流动,而不是整个计算都在GPU上完成(即中间计算结果不出GPU)。实际上,在进行推理时,可以构建一个计算图,让整个计算图的计算都在GPU上完成,不知道是否可行(如果GPU上有一个CPU就可以完成这个操作,不知道tensorrt是否是这样的操作)。
Pytorch - Module
本文聚焦于剖析Pytorch的源码,选取了torch.nn.Module作为研究对象。Module作为模型模块的封装父类,负责封装逻辑或模型的一层或多层区块。在Pytorch中,我们只需继承Module并保存模型参数,定义forward方法以实现前向传播,而后向传播则由tensor的自动微分机制处理。
让我们以多层感知器(MLP)为例,演示如何通过继承nn.Module自定义模型。OA源码完美MLP包含两个子模块:隐藏层和输出层。
Module内部仅有两个非内部变量:dump_patches和training,前者通常无作用,后者指示模块状态,是否处于训练中。其他变量分为三部分:模块信息,用于进行前向和反向的钩子,以及用于生成或加载状态字典的钩子。
模块信息包含了模型参数(_parameters)、缓存(_buffers 和 _non_persistent_buffers_set)以及内部模块(_modules)。这些信息由Module的__dict__记录,通过重写__getattr__、__setattr__和__delattr__方法访问。
每个模块拥有的钩子可以分为全局钩子和本地钩子。全局钩子适用于所有模块,主要用于调试;本地钩子仅作用于该模块本身,进行输入、输出和梯度处理,或用于调试目的。
为保存模块状态,我们还提供了状态字典(state_dict)功能,用于生成或恢复模块的状态信息,其中包含参数、持久状态缓存以及其他额外状态。避免保存值为None的参数和缓存。
Module中的变量状态保存在训练标志字段training中,它指示模块当前是否处于训练状态。确保在进行推断前使用eval()函数调整dropout和批量归一化层为评估模式,以获得一致的推断结果。
当模块调用前向、后向函数或各类钩子时,前向与后向过程以及各种钩子则被自动调用。
Module还具备钩子机制,包含前向、后向处理和状态字典操作。进一步,用户可以指定设备和数据类型迁移模块中的参数与缓存。
Pytorch源码剖析:nn.Module功能介绍及实现原理
nn.Module作为Pytorch的核心类,是构建模型的基础。它提供了一系列功能,包括记录模型的参数,实现网络的前向传播,加载和保存模型数据,以及进行设备和数据类型转换等。这些功能在模型的训练和应用中起到关键作用。
在训练与评估模式间切换,模块的行为会有所不同,如rrelu、dropout、batchnorm等操作在两种模式下表现不同。可学习的参数,如权重和偏置,需要通过梯度下降进行更新。非学习参数,比如batchnorm的running_mean,是训练过程中的统计结果。_buffers包含的Tensor不作为模型的一部分保存。
模块内部包含一系列钩子(hook)函数,用于在特定的前向传播或反向传播阶段执行自定义操作。子模块列表用于存储模型中的所有子模块。
魔术函数__init__在声明对象时自动调用,优化性能的关键在于使用super().__setattr__而非直接赋值。super调用父类的方法,避免不必要的检查,提高效率。使用register_buffer为模块注册可变的中间结果,例如BatchNorm的running_mean。register_parameter用于注册需要梯度下降更新的参数。
递归应用函数用于对模型进行操作,如参数初始化。可以将模型移动到指定设备,转换数据类型,以及注册钩子函数以实现对网络的扩展和修改。
调用魔术方法__call__执行前向传播。nn.Module未实现forward函数,子类需要提供此方法的具体实现。对于线性层等,forward函数定义了特定的运算流程。从检查点加载参数时,模块自动处理兼容性问题,确保模型结构与参数值的兼容。
模块的__setattr__方法被重写,以区别对待Parameter、Module和Buffer。当尝试设置这些特定类型的属性时,执行注册或更新操作。其他属性的设置遵循标准的Python行为。
模块的save方法用于保存模型参数和状态,确保模型结构和参数值在不同设备间转移时的一致性。改变训练状态(如将模型切换到训练或评估模式)是模块管理过程的重要组成部分。
Pytorch深入剖析 | 1-torch.nn.Module方法及源码
torch.nn.Module是神经网络模型的基础类,大部分自定义子模型(如卷积、池化或整个网络)均是其子类。torch.nn.Parameter是继承自torch.tensor的子类,用以表示可训练参数。定义Module时,可以使用个内置方法,例如add_module用于添加子模块,children和named_children用于获取子模块,modules和named_modules用于获取所有模块,register_parameter用于注册参数,parameters和named_parameters用于获取参数,get_parameter用于获取指定参数等。Module还支持数据格式转换,如float、double、half和bfloat,以及模型的设备移动,如cpu、cuda和xpu。训练模式调整可以通过train和eval方法实现。模型参数的梯度可以使用zero_grad方法清零。
模型的前向传播由forward方法定义,而apply方法允许应用特定函数到模型的所有操作符上。模型状态可以通过state_dict和load_state_dict方法进行保存和加载,常用于保存模型参数。此外,模型可以设置为训练模式或评估模式,影响特定模块如Dropout和BatchNorm的行为。
在PyTorch中,hook方法用于在前向和反向传播过程中捕获中间变量。注册hook时,可以使用torch.Tensor.register_hook针对张量注册后向传播函数,torch.nn.Module.register_forward_hook针对前向传播函数,torch.nn.Module.register_forward_pre_hook用于在前向传播之前修改输入张量,以及torch.nn.Module.register_backward_hook用于捕获中间层的梯度输入和输出。
通过这些方法,开发者可以灵活地调整、监控和优化神经网络模型的行为,从而实现更高效、更精确的模型训练和应用。利用hook方法,用户可以访问中间变量、修改输入或输出,以及提取特征图的梯度,为模型的定制化和深入分析提供了强大的工具。