From 3d7c502c688faabb2395ee8b6be41bf03b3f8950 Mon Sep 17 00:00:00 2001 From: Cheng Lai Date: Thu, 9 Mar 2023 16:21:00 +0800 Subject: [PATCH] Update programming model (#425) --- .../c_python_interaction.md | 2 +- .../development_history.md | 19 +++---- chapter_programming_interface/index.md | 5 +- .../ml_programming_paradigm.md | 55 +++++++++++++++++++ chapter_programming_interface/ml_workflow.md | 12 ++-- .../neural_network_layer.md | 29 +++------- 6 files changed, 81 insertions(+), 41 deletions(-) create mode 100644 chapter_programming_interface/ml_programming_paradigm.md diff --git a/chapter_programming_interface/c_python_interaction.md b/chapter_programming_interface/c_python_interaction.md index 946cdf4..f295f6e 100644 --- a/chapter_programming_interface/c_python_interaction.md +++ b/chapter_programming_interface/c_python_interaction.md @@ -1,6 +1,6 @@ ## C/C++编程接口 -在上述小节中,我们讨论了开发者如何利用Python来定义机器学习的整个工作流,以及如何定义复杂的深度神经网络。然而,在很多时候,用户也需要添加自定义的算子来帮助实现新的模型,优化器,数据处理函数等。这些自定义算子需要通过C和C++实现,从而获得最优性能。但是为了帮助这些算子被用户使用,他们也需要暴露为Python函数,从而方便用户整合入已有的Python为核心编写的工作流和模型。在这一小节中,我们讨论这一过程是如何实现的。 +在2.2和2.3节中,分别讨论了开发者如何利用Python来定义机器学习的整个工作流,以及如何定义复杂的深度神经网络。然而,在很多时候,开发者也需要添加自定义的算子来帮助实现新的模型,优化器,数据处理函数等。这些自定义算子需要通过C和C++实现,从而获得最优性能。但是为了帮助这些算子被开发者使用,他们也需要暴露为Python函数,从而方便开发者整合入已有的Python为核心编写的工作流和模型。在这一小节中,我们讨论这一过程是如何实现的。 ### 在Python中调用C/C++函数的原理 diff --git a/chapter_programming_interface/development_history.md b/chapter_programming_interface/development_history.md index b7ebf28..78c9afb 100644 --- a/chapter_programming_interface/development_history.md +++ b/chapter_programming_interface/development_history.md @@ -4,23 +4,18 @@ :width:`800px` :label:`img_framedh` -随着机器学习系统的诞生,如何设计易用且高性能的编程接口就一直成为了框架设计者首要解决的问题。在早期的机器学习框架中(如 :numref:`img_framedh`所示),人们选择用Lua(Torch)和Python(Theano)等高层次编程语言来编写机器学习程序。这些早期的机器学习框架提供了机器学习必须的模型定义,自动微分等功能,其适用于编写小型和科研为导向的机器学习应用。 +随着机器学习系统的诞生,如何设计易用且高性能的API接口就一直成为了系统设计者首要解决的问题。在早期的机器学习框架中(如 :numref:`img_framedh`所示),人们选择用Lua(Torch)和Python(Theano)等高层次编程语言来编写机器学习程序。这些早期的机器学习框架提供了机器学习必须的模型定义,自动微分等功能,其适用于编写小型和科研为导向的机器学习应用。 -在2011年,深度神经网络快速崛起,并很快在各个AI应用领域(计算机视觉,语音识别,自然语言处理等)取得了最先进的性能。训练深度神经网络需要消耗大量的算力,而这些算力无法被以Lua和Python所主导开发的Torch和Theano所满足。与此同时,计算加速卡(如英伟达GPU)的通用编程接口(例如CUDA -C)日趋成熟,而构建于CPU多核技术之上的多线程库(POSIX -Threads)也被广大开发者所接受。因此,许多的机器学习用户希望基于C和C++来开发高性能的深度学习应用。这一类需求被Caffe等一系列以C和C++作为核心编程接口的框架所满足。 +深度神经网络在2011年来快速崛起,很快在各个AI应用领域(计算机视觉、语音识别、自然语言处理等)取得了突破性的成绩。训练深度神经网络需要消耗大量的算力,而以Lua和Python为主导开发的Torch和Theano无法发挥这些算力的最大性能。与此同时,计算加速卡(如英伟达GPU)的通用API接口(例如CUDA C)日趋成熟,且构建于CPU多核技术之上的多线程库(POSIX Threads)也被广大开发者所接受。因此,许多的机器学习用户希望基于C和C++来开发高性能的深度学习应用。这一类需求被Caffe等一系列以C和C++作为核心API的框架所满足。 -然而,机器学习模型往往需要针对部署场景,数据类型,识别任务等需求进行深度定制,而这类定制任务需要被广大的AI应用领域的开发者所实现。这类开发者的背景多样,其往往不具有熟练使用C和C++的背景,因此Caffe这一类库与C和C++深度绑定的编程模型快速成为了制约这一类框架快速推广的巨大瓶颈。 +然而,机器学习模型往往需要针对部署场景、数据类型、识别任务等需求进行深度定制,而这类定制任务需要被广大的AI应用领域开发者所实现。这类开发者的背景多样,往往不能熟练使用C和C++。因此Caffe这一类与C和C++深度绑定的编程框架,成为了制约框架快速推广的巨大瓶颈。 -在2016年,谷歌率先推出了TensorFlow。相比于传统的Caffe,Torch和Theano,TensorFlow提出利用高层次编程语言:Python作为面向用户的主要前端语言,而利用C和C++实现高性能后端。大量基于Python的前端API确保了TensorFlow可以被大量的数据科学家和机器学习科学家接受,同时帮助TensorFlow能够快速融入Python为主导的大数据生态(大量的大数据开发库如Numpy,Pandas,SciPy, -Matplotlib和PySpark)。同时,Python具有出色的和C语言的互操作性,这种互操作性已经在多个Python库中得到验证。因此,TensorFlow兼有Python的灵活性和生态,同时也通过C/C++后端得以实现高性能。这种设计在日后崛起的PyTorch,MXNet和CNTK的机器学习框架得到传承。 +在2015年底,谷歌率先推出了TensorFlow。相比于传统的Torch,TensorFlow提出前后端分离相对独立的设计,利用高层次编程语言Python作为面向用户的主要前端语言,而利用C和C++实现高性能后端。大量基于Python的前端API确保了TensorFlow可以被大量的数据科学家和机器学习科学家接受,同时帮助TensorFlow能够快速融入Python为主导的大数据生态(大量的大数据开发库如Numpy、Pandas、SciPy、Matplotlib和PySpark)。同时,Python具有出色的和C/C++语言的互操作性,这种互操作性已经在多个Python库中得到验证。因此,TensorFlow兼有Python的灵活性和生态,同时也通过C/C++后端得以实现高性能。这种设计在日后崛起的PyTorch、MindSpore和PaddlePaddle等机器学习框架得到传承。 -随着多个机器学习框架的出现,Keras和TensorLayer等高层次机器学习开发库提供了更高层次的Python -API从而可以快速导入已有的模型, -这些高层次API进一步屏蔽了底层框架的实现细节,因此Keras和TensorLayer可以运行在不同的机器学习框架之上。 +随着各国大型企业开源机器学习框架的出现,为了更高效地开发机器学习应用,基于开源机器学习框架为后端的高层次库Keras和TensorLayerX应运而生,它们提供Python API 可以快速导入已有的模型,这些高层次API进一步屏蔽了机器学习框架的实现细节,因此Keras和TensorLayerX可以运行在不同的机器学习框架之上。 -随着深度神经网络的进一步发展,对于机器学习框架编程接口的挑战也日益增长。因此在2020年前后,新型的机器学习框架如MindSpore和JAX进一步出现。其中,MindSpore在继承了TensorFlow,PyTorch的Python和C/C++的混合接口的基础上,进一步拓展了机器学习编程模型从而可以高效支持多种AI后端芯片(如华为Ascend,英伟达GPU和ARM芯片),实现了机器学习应用在海量异构设备上的快速部署。 +随着深度神经网络的进一步发展,对于机器学习框架编程接口的挑战也日益增长。因此在2020年前后,新型的机器学习框架如MindSpore和JAX进一步出现。其中,MindSpore在继承了TensorFlow、PyTorch的Python和C/C++的混合接口的基础上,进一步拓展了机器学习编程模型从而可以高效支持多种AI后端芯片(如华为Ascend、英伟达GPU和ARM芯片),实现了机器学习应用在海量异构设备上的快速部署。 -同时,超大型数据集和超大型深度神经网络崛起让分布式执行成为了机器学习框架编程模型的核心设计需求。为了实现分布式执行,TensorFlow和PyTorch的使用者需要进行大量编程来将数据集和神经网络分配到分布式节点上,而大量的AI开发人员并不具有分布式编程的能力。因此MindSpore进一步完善了机器学习框架的分布式编程模型的能力,从而让单节点的MindSpore程序可以无缝地运行在海量节点上。 +同时,超大型数据集和超大型深度神经网络崛起让分布式执行成为了机器学习编程框架的核心设计需求。为了实现分布式执行,TensorFlow和PyTorch的使用者需要花费大量代码来将数据集和神经网络分配到分布式节点上,而大量的AI开发人员并不具有分布式编程的能力。因此MindSpore进一步完善了机器学习框架的分布式编程模型的能力,从而让单节点的MindSpore程序可以无缝地运行在海量节点上。 在本小节中,我们将以MindSpore作为例子讲解一个现代机器学习框架的Python前端API和C/C++后端API的设计原则。这些设计原则和PyTorch,TensorFlow相似。 diff --git a/chapter_programming_interface/index.md b/chapter_programming_interface/index.md index f0d79d1..2daefd5 100644 --- a/chapter_programming_interface/index.md +++ b/chapter_programming_interface/index.md @@ -1,8 +1,8 @@ # 编程接口 -现代机器学习框架包含大量的组件。这些组件使得用户得以高效开发机器学习算法,处理数据,部署模型,性能调优和使用硬件加速器。在设计这些组件的编程接口时,一个核心的诉求是:如何平衡框架性能和易用性?为了达到最优的性能,开发者需要利用硬件亲和的编程语言如:C和C++来进行开发。这是因为:C和C++的使用使得机器学习框架可以高效调用硬件的底层API,从而最大限度发挥硬件性能。同时,现代操作系统(如Linux和Windows)提供丰富的基于C和C++的编程接口(如文件系统,网络编程,多线程管理等),通过直接调用操作系统API,可以降低框架运行的开销。 +现代机器学习框架包含大量的组件,辅助用户高效开发机器学习算法、处理数据、部署模型、性能调优和调用硬件加速器。在设计这些组件的应用编程接口(Application Programming Interface,API)时,一个核心的诉求是:如何平衡框架性能和易用性?为了达到最优的性能,开发者需要利用硬件亲和的编程语言如:C和C++来进行开发。这是因为C和C++可以帮助机器学习框架高效地调用硬件底层API,从而最大限度发挥硬件性能。同时,现代操作系统(如Linux和Windows)提供丰富的基于C和C++的API接口(如文件系统、网络编程、多线程管理等),通过直接调用操作系统API,可以降低框架运行的开销。 -从易用性的角度分析,机器学习框架的使用者往往具有丰富的行业背景(如数据科学家,生物学家,化学家,物理学家等)。他们常用的编程语言是高层次脚本语言:Python,Matlab,R和Julia。相比于C和C++,这些语言在提供编程的易用性的同时,丧失了C和C++对底层硬件和操作系统进行深度优化的能力。因此,机器学习框架的核心设计目标是:其要具有易用编程接口来支持用户用高层次语言如Python来实现机器学习算法,同时其也要具备以C和C++为核心的低层次编程接口,使得框架开发者可以用C和C++实现大量高性能组件,从而在硬件上高效执行。在本章中,我们将会讲述如何达到这个设计目标。 +从易用性的角度分析,机器学习框架的使用者往往具有丰富的行业背景(如数据科学家、生物学家、化学家、物理学家等)。他们常用的编程语言是高层次脚本语言:Python、Matlab、R和Julia。相比于C和C++,这些语言在提供编程易用性的同时,丧失了C和C++对底层硬件和操作系统进行深度优化的能力。因此,机器学习框架的核心设计目标是:具有易用的编程接口来支持用户使用高层次语言,如Python实现机器学习算法;同时也要具备以C和C++为核心的低层次编程接口来帮助框架开发者用C和C++实现大量高性能组件,从而在硬件上高效执行。在本章中,将讲述如何达到这个设计目标。 本章的学习目标包括: @@ -21,5 +21,6 @@ development_history ml_workflow neural_network_layer c_python_interaction +ml_programming_paradigm summary ``` \ No newline at end of file diff --git a/chapter_programming_interface/ml_programming_paradigm.md b/chapter_programming_interface/ml_programming_paradigm.md new file mode 100644 index 0000000..d59971d --- /dev/null +++ b/chapter_programming_interface/ml_programming_paradigm.md @@ -0,0 +1,55 @@ +## 机器学习框架的编程范式 +### 机器学习框架编程需求 +机器学习的训练是其任务中最为关键的一步,训练依赖于优化器算法来描述。目前大部分机器学习任务都使用一阶优化器,因为一阶方法简单易用。随着机器学习的高速发展,软硬件也随之升级,越来越多的研究者开始探索收敛性能更好的高阶优化器。常见的二阶优化器如牛顿法、拟牛顿法、AdaHessians,均需要计算含有二阶导数信息的Hessian矩阵,Hessian矩阵的计算带来两方面的问题,一方面是计算量巨大如何才能高效计算,另一方面是高阶导数的编程表达。 + +同时,近年来,工业界发布了非常多的大模型,从2020年OpenAI GTP-3 175B参数开始,到2021年盘古大模型100B、鹏程盘古-$\alpha$ 200B、谷歌switch transformer 1.6T、智源悟道 1.75T参数,再到2022年百度ERNIE3.0 280M、Facebook NLLB-200 54B,越来越多的超大规模模型训练需求使得单纯的数据并行难以满足,而模型并行需要靠人工来模型切分耗时耗力,如何自动并行成为未来机器学习框架所面临的挑战。最后,构建机器学习模型本质上是数学模型的表示,如何简洁表示机器学习模型也成为机器学习框架编程范式的设计的重点。 + +为了解决机器学习框架在实际应用中的一些困难,研究人员发现函数式编程能很好地提供解决方案。在计算机科学中,函数式编程是一种编程范式,它将计算视为数学函数的求值,并避免状态变化和数据可变,这是一种更接近于数学思维的编程模式。神经网络由连接的节点组成,每个节点执行简单的数学运算。通过使用函数式编程语言,开发人员能够用一种更接近运算本身的语言来描述这些数学运算,使得程序的读取和维护更加容易。同时,函数式语言的函数都是相互隔离的,使得并发性和并行性更容易管理。 + +因此,机器学习框架使用函数式编程设计具有以下优势: +- 支持高效的科学计算和机器学习场景。 +- 易于开发并行。 +- 简洁的代码表示能力。 + +### 机器学习框架编程范式现状 +本小节将从目前主流机器学习框架发展历程来看机器学习框架对函数式编程的支持现状。谷歌在2015年发布了TensorFlow1.0其代表的编程特点包括计算图(Computational Graphs)、会话(Session)、张量(Tensor)它是一种声明式编程风格。2017年Facebook发布了PyTorch其编程特点为即时执行,它是一种命令式编程风格。2018年谷歌发布了JAX它不是存粹为了机器学习而编写的框架,而是针对GPU和TPU做高性能数据并行计算的框架;与传统的机器学习框架相比其核心能力是神经网络计算和数值计算的融合,在接口上兼容了NumPy、Scipy等Python原生的数据科学接口,而且在此基础上扩展分布式、向量化、高阶求导、硬件加速,其编程风格是函数式,主要体现在无副作用、Lambda闭包等。2020年华为发布了MindSpore,其函数式可微分编程架构可以让用户聚焦机器学习模型数学的原生表达。2022年PyTorch推出functorch,受到谷歌JAX的极大启发,functorch是一个向PyTorch添加可组合函数转换的库,包括可组合的vmap(向量化)和autodiff转换,可与PyTorch模块和PyTorch autograd一起使用,并具有良好的渴望模式(Eager-Mode)性能,functorch可以说是弥补了PyTorch静态图的分布式并行需求。 + +从主流的机器学习框架发展历程来看,未来机器学习框架函数式编程风格将会日益得到应用,因为函数式编程能更直观地表达机器学习模型,同时对于自动微分、高阶求导、分布式实现也更加方便。另一方面,未来的机器学习框架在前端接口层次也趋向于分层解耦,其设计不直接为了机器学习场景,而是只提供高性能的科学计算和自动微分算子,更高层次的应用如机器学习模型开发则是通过封装这些高性能算子实现。 + +### 函数式编程案例 +在上一小节介绍了机器学习框架编程范式的现状,不管是JAX、MindSpore还是functorch都提到了函数式编程,其在科学计算、分布式方面有着独特的优势。然而在实际应用中纯函数式编程几乎没有能够成为主流开发范式,而现代编程语言几乎不约而同的选择了接纳函数式编程特性。以MindSpore为例,MindSpore选择将函数式和面向对象编程融合,兼顾用户习惯,提供易用性最好,编程体验最佳的混合编程范式。MindSpore采用混合编程范式道理也很简单,纯函数式会让学习曲线陡增,易用性变差;面向对象构造神经网络的编程范式深入人心。 + +下面中提供了使用MindSpore编写机器学习模型训练的全流程。其网络构造,满足面向对象编程习惯,函数式编程主要体现在模型训练的反向传播部分;MindSpore使用函数式,将前向计算构造成function,然后通过函数变换,获得grad function,最后通过执行grad function获得权重对应的梯度。 + +```python +# Class definition +class Net(nn.Cell): + def __init__(self): + ...... + def construct(self, inputs): + ...... + +# Object instantiation +net = Net() # network +loss_fn = nn.CrossEntropyLoss() # loss function +optimizer = nn.Adam(net.trainable_params(), lr) # optimizer + +# define forward function +def forword_fn(inputs, targets): + logits = net(inputs) + loss = loss_fn(logits, targets) + return loss, logits + +# get grad function +grad_fn = value_and_grad(forward_fn, None, optim.parameters, has_aux=True) + +# define train step function +def train_step(inputs, targets): + (loss, logits), grads = grad_fn(inputs, targets) # get values and gradients + optimizer(grads) # update gradient + return loss, logits + +for i in range(epochs): + for inputs, targets in dataset(): + loss = train_step(inputs, targets) +``` \ No newline at end of file diff --git a/chapter_programming_interface/ml_workflow.md b/chapter_programming_interface/ml_workflow.md index 6ad47de..799d011 100644 --- a/chapter_programming_interface/ml_workflow.md +++ b/chapter_programming_interface/ml_workflow.md @@ -1,19 +1,19 @@ ## 机器学习工作流 -机器学习系统编程模型的首要设计目标是:对开发者的整个工作流进行完整的编程支持。一个常见的机器学习任务一般包含如 :numref:`img_workflow`所示的流程。这个工作流完成了训练数据集的读取,模型的训练,测试和调试。通过归纳,我们可以将这一工作流中用户所需要自定义的部分通过定义以下API来支持(我们这里假设用户的高层次API以Python函数的形式提供): +机器学习系统编程模型的首要设计目标是:对开发者的整个工作流进行完整的编程支持。一个常见的机器学习任务一般包含如 :numref:`img_workflow`所示的工作流。这个工作流完成了训练数据集的读取,模型的训练,测试和调试。通过归纳,我们可以将这一工作流中用户所需要自定义的部分通过定义以下API来支持(我们这里假设用户的高层次API以Python函数的形式提供): - **数据处理:** 首先,用户需要数据处理API来支持将数据集从磁盘读入。进一步,用户需要对读取的数据进行预处理,从而可以将数据输入后续的机器学习模型中。 -- **模型结构:** - 完成数据的读取后,用户需要模型定义API来定义机器学习模型。这些模型带有模型参数,可以对给定的数据进行推理。 +- **模型定义:** + 完成数据的预处理后,用户需要模型定义API来定义机器学习模型。这些模型带有模型参数,可以对给定的数据进行推理。 -- **损失函数和优化算法:** +- **优化器定义:** 模型的输出需要和用户的标记进行对比,这个对比差异一般通过损失函数(Loss function)来进行评估。因此,优化器定义API允许用户定义自己的损失函数,并且根据损失来引入(Import)和定义各种优化算法(Optimisation algorithms)来计算梯度(Gradient),完成对模型参数的更新。 -- **训练过程:** +- **训练:** 给定一个数据集,模型,损失函数和优化器,用户需要训练API来定义一个循环(Loop)从而将数据集中的数据按照小批量(mini-batch)的方式读取出来,反复计算梯度来更新模型。这个反复的过程称为训练。 - **测试和调试:** @@ -175,7 +175,7 @@ train_net(args, model, train_epoch, mnist_path, dataset_size, ckpoint, False) ### 测试和验证 -测试是模型运行测试数据集得到的结果,通常在训练过程中,每训练一定的数据量后就会测试一次,以验证模型的泛化能力。MindSpore使用model.eval接口读入测试数据集。 +测试是将测试数据集输入到模型,运行得到输出的过程。通常在训练过程中,每训练一定的数据量后就会测试一次,以验证模型的泛化能力。MindSpore使用model.eval接口读入测试数据集。 ```python def test_net(model, data_path): """定义验证的方法""" diff --git a/chapter_programming_interface/neural_network_layer.md b/chapter_programming_interface/neural_network_layer.md index f74f911..c10aea7 100644 --- a/chapter_programming_interface/neural_network_layer.md +++ b/chapter_programming_interface/neural_network_layer.md @@ -1,13 +1,13 @@ ## 定义深度神经网络 -在上一节我们使用MindSpore构建了一个多层感知机的网络结构,随着深度神经网络的飞速发展,各种深度神经网络结构层出不穷,但是不管结构如何复杂,神经网络层数量如何增加,构建深度神经网络结构始终遵循最基本的规则:1.承载计算的节点;2.可变化的节点权重(节点权重可训练);3.允许数据流动的节点连接。因此在机器学习编程库中神经网络是以层为核心,它提供了各类神经网络层基本组件;将神经网络层组件按照网络结构进行堆叠、连接就能构造出神经网络模型。 +在上一节我们使用MindSpore构建了一个多层感知机的网络结构,随着深度神经网络的飞速发展,各种深度神经网络结构层出不穷,但是不管结构如何复杂,神经网络层数量如何增加,构建深度神经网络结构始终遵循最基本的元素:1.承载计算的节点;2.可变化的节点权重(节点权重可训练);3.允许数据流动的节点连接。因此在机器学习编程库中深度神经网络是以层为核心,它提供了各类深度神经网络层基本组件;将神经网络层组件按照网络结构进行堆叠、连接就能构造出神经网络模型。 ### 以层为核心定义神经网络 神经网络层包含构建机器学习网络结构的基本组件,如计算机视觉领域常用到卷积(Convolution)、池化(Pooling)、全连接(Fully Connected);自然语言处理常用到循环神经网络(Recurrent Neural Network,RNN);为了加速训练,防止过拟合通常用到批标准化(BatchNorm)、Dropout等。 **全连接**是将当前层每个节点都和上一层节点一一连接,本质上是特征空间的线性变换;可以将数据从高维映射到低维,也能从低维映射到高维度。 - :numref:`fc_layer`展示了全连接的过程,对输入的n个数据变换到另一个大小为m的特征空间,再从大小为m的特征空间变换到大小为p的特征空间;可见全连接层的参数量巨大,两次变换所需的参数大小为$n \times m$和$m \times p$。 + :numref:`fc_layer`展示了全连接的过程,对输入的n个数据变换到大小为m的特征空间,再从大小为m的特征空间变换到大小为p的特征空间;可见全连接层的参数量巨大,两次变换所需的参数大小为$n \times m$和$m \times p$。 ![全连接层](../img/ch02/fc_layer_1.svg) :width:`800px` @@ -56,7 +56,7 @@ :width:`800px` :label:`nn_network` -有了上述基础知识,我们对卷积神经网络所需组件接口和模型构建使用伪代码描述如下: +有了上述基础知识,对卷积神经网络模型构建过程使用伪代码描述如下: ```python # 构建卷积神经网络的组件接口定义: 全连接层接口:fully_connected(input, weights) @@ -94,26 +94,15 @@ Transformer又是BERT模型架构的重要组成。随着深度神经网络的 ### 神经网络层的实现原理 -3.3.1中使用伪代码定义了一些卷积神经网络接口和模型构建过程,整个构建过程,需要创建训练变量和构建连接过程; -随着网络层数的增加,手动管理训练变量是一个繁琐的过程,因此2.3.1中描述的接口在机器学习库中属于低级API。 -机器学习编程库大都提供了更高级用户友好的API,它将神经网络层抽象成一个基类,所有的神经网络层实现都继承基类调用低级API。 -如MindSpore提供的mindspore.nn.Cell、mindspore.nn.Conv2d、mindspore.dataset; -PyTorch提供的torch.nn.Module、torch.nn.Conv2d、torch.utils.data.Dataset。 +2.3.1中使用伪代码定义了一些卷积神经网络接口和模型构建过程,整个构建过程需要创建训练变量和构建连接过程。随着网络层数的增加,手动管理训练变量是一个繁琐的过程,因此2.3.1中描述的接口在机器学习库中属于低级API。机器学习编程库大都提供了更高级用户友好的API,它将神经网络层抽象出一个基类,所有的神经网络层都继承基类来实现,如MindSpore提供的mindspore.nn.Cell;PyTorch提供的torch.nn.Module。基于基类他们都提供了高阶API,如MindSpore 提供的mindspore.nn.Conv2d、mindspore.nn.MaxPool2d、mindspore.dataset;PyTorch提供的torch.nn.Conv2d、torch.nn.MaxPool2d、torch.utils.data.Dataset。 - :numref:`model_build`描述了神经网络构建过程中的基本细节。 -神经网络层需要的功能有该层的训练参数(变量,包括初始化方法和训练状态)以及计算过程; -神经网络模型需要的功能是对神经网络层管理和神经网络层参数的管理。 -在机器学习编程库中,承担此功能有MindSpore的Cell、PyTorch的Module。 -Cell和Module是模型抽象方法也是所有网络的基类。 -现有模型抽象方案有两种。 -一种是抽象出两个方法分别为Layer(负责单个神经网络层的参数构建和前向计算),Model(负责对神经网络层进行连接组合和神经网络层参数管理); -另一种是将Layer和Model抽象成一个方法,该方法既能表示单层神经网络层也能表示包含多个神经网络层堆叠的模型,Cell和Module就是这样实现的。 + :numref:`model_build`描述了神经网络构建过程中的基本细节。基类需要初始化训练参数、管理参数状态以及定义计算过程;神经网络模型需要实现对神经网络层和神经网络层参数管理的功能。在机器学习编程库中,承担此功能有MindSpore的Cell、PyTorch的Module。Cell和Module是模型抽象方法也是所有网络的基类。现有模型抽象方案有两种,一种是抽象出两个方法分别为Layer(负责单个神经网络层的参数构建和前向计算),Model(负责对神经网络层进行连接组合和神经网络层参数管理);另一种是将Layer和Model抽象成一个方法,该方法既能表示单层神经网络层也能表示包含多个神经网络层堆叠的模型,Cell和Module就是这样实现的。 ![神经网络模型构建细节](../img/ch02/model_build.svg) :width:`800px` :label:`model_build` - :numref:`cell_abs`展示了设计神经网络层抽象方法的通用表示。通常在构造器会选择使用Python中collections模块的OrderedDict来初始化神经网络层和神经网络层参数的存储;它的输出是一个有序的,相比与Dict更适合深度学习这种模型堆叠的模式。参数和神经网络层的管理是在\_\_setattr\_\_中实现的,当检测到属性是属于神经网络层及神经网络层参数时就记录起来。神经网络模型比较重要的是计算连接过程,可以在\_\_call\_\_里重载,实现神经网络层时在这里定义计算过程。训练参数的返回接口是为了给优化器传所有训练参数。神经网络层返回为了遍历各层神经网络得到各个神经网络层的参数。这里只列出了一些重要的方法,在自定义方法中,通常需要实现参数插入删除方法、神经网络层插入删除、神经网络模型信息等。 + :numref:`cell_abs`展示了设计神经网络层抽象方法的通用表示。通常在构造器会选择使用Python中collections模块的OrderedDict来初始化神经网络层和神经网络层参数的存储;它的输出是一个有序的,相比与Dict更适合深度学习这种模型堆叠的模式。参数和神经网络层的管理是在\_\_setattr\_\_中实现的,当检测到属性是属于神经网络层及神经网络层参数时就记录起来。神经网络模型比较重要的是计算连接过程,可以在\_\_call\_\_里重载,实现神经网络层时在这里定义计算过程。训练参数的返回接口给优化器传所有训练参数,这些参数是基类遍历了所有网络层后得到的。这里只列出了一些重要的方法,在自定义方法中,通常需要实现参数插入删除、神经网络层插入删除、神经网络模型信息返回等方法。 ![神经网络基类抽象方法](../img/ch02/cell_abstract.svg) :width:`800px` @@ -123,7 +112,7 @@ Cell和Module是模型抽象方法也是所有网络的基类。 ### 自定义神经网络层 -2.3.1中使用伪代码定义机器学习库中低级API,有了实现的神经网络基类抽象方法,那么就可以设计更高层次的接口解决手动管理参数的繁琐。假设已经有了神经网络模型抽象方法Cell,构建Conv2D将继承Cell,并重构\_\_init\_\_和\_\_call\_\_方法,在\_\_init\_\_里初始化训练参数和输入参数,在\_\_call\_\_里调用低级API实现计算逻辑。同样使用伪代码接口描述自定义卷积层的过程。 +2.3.1中使用伪代码定义机器学习库中低级API,有了实现的神经网络基类抽象方法,那么就可以设计更高层次的接口解决手动管理参数的繁琐。假设已经有了神经网络模型抽象方法Cell,构建Conv2D将继承Cell,并重构\_\_init\_\_和\_\_call\_\_方法,在\_\_init\_\_里初始化训练参数和输入参数,在\_\_call\_\_里调用低级API实现计算逻辑。同样使用伪代码描述自定义卷积层的过程。 ```python # 接口定义: @@ -153,7 +142,7 @@ conv = Conv2D(in_channel=10, out_channel=20, filter_size=3, stride=2, padding=0) output = conv(input) ``` -其执行过程为,在初始化Conv2D时,\_\_setattr\_\_会判断属性,属于Cell把神经网络层Conv2D记录到self.\_cells,filters属于parameter把参数记录到self.\_params。查看神经网络层参数使用conv.parameters_and_names;查看神经网络层列表使用conv.cells_and_names;执行操作使用conv(input)。 +在执行过程中,初始化Conv2D时,\_\_setattr\_\_会判断属性,属于Cell把神经网络层Conv2D记录到self.\_cells,属于parameter的filters记录到self.\_params。查看神经网络层参数使用conv.parameters_and_names;查看神经网络层列表使用conv.cells_and_names;执行操作使用conv(input)。 ### 自定义神经网络模型 @@ -191,4 +180,4 @@ class CNN(Cell): net = CNN() ``` -上述卷积模型进行实例化,其执行将从\_\_init\_\_开始,第一个是Conv2D,Conv2D也是Cell的子类,会进入到Conv2D的\_\_init\_\_,此时会将第一个Conv2D的卷积参数收集到self.\_params,之后回到Conv2D,将第一个Conv2D收集到self.\_cells;第二个的组件是MaxPool2D,因为其没有训练参数,因此将MaxPool2D收集到self.\_cells;依次类推,分别收集第二个卷积参数和卷积层,三个全连接层的参数和全连接层。实例化之后可以调用net.parameters_and_names来返回训练参数;调用net.cells_and_names查看神经网络层列表。 +上述卷积模型进行实例化,其执行将从\_\_init\_\_开始,第一个是Conv2D,Conv2D也是Cell的子类,会进入到Conv2D的\_\_init\_\_,此时会将第一个Conv2D的卷积参数收集到self.\_params,之后回到Conv2D,将第一个Conv2D收集到self.\_cells;第二个的组件是MaxPool2D,因为其没有训练参数,因此将MaxPool2D收集到self.\_cells;依次类推,分别收集第二个卷积层的参数和层信息以及三个全连接层的参数和层信息。实例化之后可以调用net.parameters_and_names来返回训练参数;调用net.cells_and_names查看神经网络层列表。