从下至上保持严格的分层逻辑:
1、ndarr包:核心类 NdArray,底层线性代数的简单实现,目前只实现CPU版本,GPU版本需要依赖庞大的三方库。
2、func包:核心类Function与Variable 分别是抽象的数学函数与变量的抽象,用于在前向传播时自动构建计算图,实现自动微分功能,其中Variable对应PyTorch的tensor。
3、nnet包:核心类Layer与Block表示神经网络的层和块,任何复杂的深度网络都是依赖这些Layer与Block的堆叠而层。实现了一些常用的cnn层rnn层norm层以及encode与decode的seq2seq架构等等。
4、mlearning 包:机器学习的通用组件的表示,深度学习是机器学习的一个分支,对应更广泛的机器学习有一套通用的组件,包括数据集,损失函数,优化算法,训练器,推导器,效果评估器等。
5、modality 包:属于应用层的范畴,目前深度学习主要应用任务图形图像的视觉,自然语言处理以及强化学习三部分,暂时还没有相应的领域的实现,希望在0.02版中实现GPT-2等原型。
6、example包:一些简单的能跑通的例子,主要包括机器学习的分类和回归两类问题,有曲线的拟合,螺旋曲线的分类,手写数字的识别以及序列数据的预测。
接下来就从下至上,全栈式地简答串一下每层涉及的核心概念和简单实现。
二、线性代数与张量运算
首先进入深度学习的第一层:张量操作层。张量(多维数组)的操作和计算是深度学习的基础,所有运算几乎都是基于张量的运算(单个数字称为标量,一维数称为向量,二维数组称为矩阵,三维以及以上数组称为N维张量)。这些操作通常使用高效的数值计算库来实现,通过C/C 语言在特定的计算硬件上实现的,提供了各种张量操作,如矩阵乘法、卷积、池化等。
这一部分主要分三块,首先关于线性代数的一些基本知识,然后是基于CPU的最小化实现,最后对比为什么深度学习重依赖一种全新的计算范型GPU。
1. 基础的线性代数
先上一张图来直接劝退下大家,虽然当初考研的时候,这些只算最基本的八股练习题:
然后是一些线性代数的常见概念:
向量:向量是一个有大小和方向的量,在线性代数中,向量通常用一列数表示。
矩阵:矩阵是一个二维数组,由行和列组成,它可以用于表示线性方程组或者线性变换。
向量空间:向量空间是由一组向量构成的集合,满足一些特定的性质,如封闭性、加法和数量乘法的结合性等。
线性变换:线性变换是一种将一个向量空间映射到另一个向量空间的操作。它保留线性组合和共线关系。
线性方程组:线性方程组是一组线性方程的集合,其中每个方程都满足变量的次数为1,并且具有线性关系。
特征值和特征向量:在矩阵中,特征值是一个标量,特征向量是一个非零向量,满足矩阵与该向量的乘积等于特征值乘以该向量。
内积和外积:内积是向量之间的一种运算,用于度量它们之间的夹角和长度,外积是向量之间的一种运算,用于生成一个新的向量,该向量垂直于原始向量。
行列式:行列式是一个标量值,由一个方阵的元素按照特定的规则组合而成,它用于计算矩阵的逆、判断矩阵的奇偶性等。
有没有点头大?但是如果你看到CPU版本的简单实现[6],你也会瞬间觉得如此简单(目前只支持标量&向量&矩阵,暂不支持更高维度的张量)。
2. CPU版本的简单实现
/**
* 支持,1,标量;2.向量;3,矩阵,
* <p>
* 暂不支持,更高纬度的张量,更高维的通过Tensor来继承简单实现
*/
public class NdArray {
protected Shape shape;
/**
* 真实存储数据,使用float32
*/
private float[][] matrix;
}
/**
* 表示矩阵或向量的形状
*/
public class Shape {
/**
* 表示多少行
*/
public int row = 1;
/**
* 表示多少列
*/
public int column = 1;
public int[] dimension;
}
其实都是一些对二维数组的操作:大致分三类,首先是一些简单初始化函数;其次是基本的四则运算加减乘除,还有一些运算会对矩阵改变形状,其中内积是最长的。
3. 为什么需要GPU
通过上面在CPU上实现的的矩阵操作,特别是一个简单内积运算需要多层for循环嵌套,大家就知道,在CPU上这种为逻辑控制转移设计的架构,其实并不能很好地实现并行运算。而矩阵运算的行列其实是可以并行的,所以深度学习依赖的矩阵运算在CPU上是极其低效的。为了更直观地对比可以参考下图,相比于CPU,GPU的控制逻辑单元较弱(蓝色单元),但是具有大量的ALU(算术逻辑 绿色单元)。
大部分深度学习框架(如TensorFlow、PyTorch等)都提供了对GPU的支持,可以方便地利用GPU进行并行计算。随着今年chatGPT的爆发,GPU已经成为AI的基础设施,快速成为一种全新的主流计算范式。
三、计算图与自动微分
现在来到深度学习框架的第二层:func层,主要实现深度学习框架非常重要的特性,计算图与自动微分。
1)计算图是一种图形化表示方式,用于描述计算过程中数据的流动和操作的依赖关系。在深度学习中,神经网络的前向传播和反向传播过程可以通过计算图来表示。
2)自动微分是一种计算导数的技术,用于计算函数的导数或梯度。在深度学习中,反向传播算法就是一种自动微分的方法,用于计算神经网络中每个参数对于损失函数的梯度。
通过计算图和自动微分的结合,可以有效地计算复杂神经网络中大量参数的梯度,从而实现模型的训练和优化。
1. 数值与解析微分
1.1. 数值微分导数是函数图像在某一点处的斜率,也就是纵坐标增量(Δy)和横坐标增量(Δx)在Δx->0时的比值。微分是指函数图像在某一点处的切线在横坐标取得增量Δx以后,纵坐标取得的增量,一般表示为dy。
数值微分是一种用数值方法来近似计算函数的导数的方法,其目的是通过计算函数在某个点附近的有限差分来估计函数的导数值。求解使用比较多的是中心差分, 通过近似计算函数在某个点的导数,使用函数在该点前后一个点的函数值来计算,公式如下:f'(x) ≈ (f(x h) - f(x - h)) / (2h)。其中,h是差分的步长,步长越小,计算结果越精确。数值微分是一种近似计算方法,计算结果与真实的导数值存在一定误差。
1.2. 解析微分解析微分是微积分中的另一种方法,用于精确计算函数在某个点的导数值。它通过应用导数的定义和基本的微分规则来求解导数。可以根据函数的定义,确定函数表达式。例如,给定一个函数 f(x),需要确定它的表达式,如 f(x) = x^2 2x 1。例如以下是一些常用函数的解析微分: