transformer学习笔记-神经网络原理

在深度学习领域,transformer可以说是在传统的神经网络的基础上发展而来,着重解决传统神经网络长距离关联、顺序处理、模型表达能力等问题。

在学习transformer之前,我想,有必要先对传统的神经网络做简要的了解。

一、神经网络基本结构


输入层: 神经网络的第一层,直接接收原始输入数据,传递到隐藏层。
隐藏层: 位于输入层和输出层之间,可以有一个或多个隐藏层,多层隐藏层逐步提取更高层次的抽象特征。
输出层: 最后一层,负责生成最终的预测结果。

下面以CNN(卷积神经网络)为例,大致说明下各层间的关系:

1.1、从原始数据到输入层

假设一张经过灰度处理的数字6、大小N * N个像素的图片,每个像素点的取值经过归一化处理(取值0-1),将N * N个像素点展开成神经网络输入层的每个神经元。

1.2、隐藏层特征提取

  • 当输入层的各个节点数据传输到第一个隐藏层时,提取多个最基础的形状特征的神经元,
  • 这些神经元在传输给第一个隐藏层后,第二个隐藏层提取更高层次的形状或物体结构。也就是更高层次的特征。
  • 最后,第二个隐藏层的神经元通过权重计算,给每个输出层的特征节点赋值,取值最高的特征节点,就是输入图片最终匹配的预定特征。

那么,每层之间的数据传递到底是怎样的?我们以输入层到第一个隐藏层为例:

  • 设n = N*N ,那么输入层就有a0-an 个神经元,每个连线代表输入层每个输入对下一层某个神经元的权重,n个输入就有n个权重(全连接的情况,全连接就是每个输入都对下一层的每个节点有影响)。
  • 那么下一层的单个神经元就等于上一层所有神经元的加权和,即 : a0ω0 + a1ω1 + ... +an*ωn
  • 在识别特征过程中,可能需要对不明显的特征进行过滤,这也就要求加权和需要大于某个数,这个值我们称之为偏置量,在线性表达上,偏置可以是模型更好拟合数据,可以表示不仅仅穿过原点的线性关系。 (a0ω0 + a1ω1 + ... +an*ωn ) +(-b)
  • 当然,有时为了优化模型的表达,比如提高收敛速度、避免梯度消失,需要将下一层神经元的取值限制在有界范围,这时候需要通过激活函数做一些非线性的操作,比如使用SIGMOD函数,将值域限定在(0,1): σ((a0ω0 + a1ω1 + ... +an*ωn ) +(-b)) f = σ(...)为激活函数

上面仅仅是表示了第二层的一个神经元的计算,那第二层所有的神经元该如何表达:

加上偏置和激活函数表示如下:

后面隐藏层到输出层,结构也类似。通过线性表达则是:

y = W* a + b ; 其中y 为输出矩阵,W为权重矩阵,a 为输入矩阵,b为偏置矩阵

那么W和b 如何而来,当然是通过大量的数据训练而来,后面我们将逐步学习如何通过损失函数训练,

二、损失函数

以最后一个隐藏层到输出层为例:假定这个过程的权重矩阵初始化为W,偏置初始化为b ,预测值为y′ 。那么真实值y 与预测值之间的误差就可以如下表示(以均方差为例):
假设10个样本:

更一般的表示: MSE= 1 n ∑ i = 1 n ( y ′ i − y i ) 2 \frac{1}{n} ∑_{i=1}^{n} (y′_i - y_i )^2 n1∑i=1n(y′i−yi)2 或 L = 1 n ( y ′ − y ) 2 \frac{1}{n}(y′ - y)^2 n1(y′−y)2

n为样本数量

除了均方差损失函数,常用的损失函数还有交叉熵损失函数,通常用于多分类概率分布:

二分类可以看做多分类的一种特例:

三、梯度下降法与反向传播

3.1 利用损失函数求梯度

得到损失函数后,我们得根据损失函数反向推导哪个权重影响比较大,权重矩阵就重点关注影响损失函数较大的权重:

那回到损失函数MSE= 1 n ∑ i = 1 n ( y ′ i − y i ) 2 \frac{1}{n} ∑_{i=1}^{n} (y′_i - y_i )^2 n1∑i=1n(y′i−yi)2 ,则是对其关于W和b求偏导以获取梯度,梯度越大,斜率越大,对误差影响越大。

由于 y′ = W* a + b ,因此关于W和b求偏导,是个复合函数求偏导:

关于W的梯度: ∂ M S E ∂ W = ∂ M S E ∂ y ′ ∗ ∂ y ′ ∂ W = 2 n ∑ i = 1 n ( y ′ i − y i ) ∗ a T \frac{\partial MSE}{\partial W} =\frac{\partial MSE}{\partial y′} * \frac{\partial y′}{\partial W} = \frac{2}{n} ∑_{i=1}^{n} (y′_i - y_i ) * a^T ∂W∂MSE=∂y′∂MSE∗∂W∂y′=n2i=1∑n(y′i−yi)∗aT

对于单个y'(即单行W与单列a的内积,忽略偏置) 基于W求偏导,得到的应该是1行n列的矩阵,由于a是n行一列的矩阵,因此需要对a做转置处理,例如:
对 ω 0 求导: ∂ y ′ ∂ ω 0 = ∂ a 0 ∗ ω 0 + a 1 ∗ ω 1 + . . . . . . + a n ∗ ω n ω 0 = a 0 对ω0求导:\frac{\partial y′}{\partial ω0} =\frac{\partial a0*ω0 + a1*ω1 + ...... +an*ωn}{ω0} = a0 对ω0求导:∂ω0∂y′=ω0∂a0∗ω0+a1∗ω1+......+an∗ωn=a0
对 ω 1 求导: ∂ y ′ ∂ ω 1 = ∂ a 0 ∗ ω 0 + a 1 ∗ ω 1 + . . . . . . + a n ∗ ω n ω 1 = a 1 对ω1求导:\frac{\partial y′}{\partial ω1} =\frac{\partial a0*ω0 + a1*ω1 + ...... +an*ωn}{ω1} = a1 对ω1求导:∂ω1∂y′=ω1∂a0∗ω0+a1∗ω1+......+an∗ωn=a1
对 ω n 求导: ∂ y ′ ∂ ω n = ∂ a 0 ∗ ω 0 + a 1 ∗ ω 1 + . . . . . . + a n ∗ ω n ω n = a n 对ωn求导:\frac{\partial y′}{\partial ωn} =\frac{\partial a0*ω0 + a1*ω1 + ...... +an*ωn}{ωn} = an 对ωn求导:∂ωn∂y′=ωn∂a0∗ω0+a1∗ω1+......+an∗ωn=an
因此 ∂ y ′ ∂ W = [ a 0 , a 1 , . . . , a n ] = a T 因此\frac{\partial y′}{\partial W} = [ a0,a1,...,an] = a^T 因此∂W∂y′=[a0,a1,...,an]=aT

多行W与多列a以此类推。
关于b的梯度:
∂ M S E ∂ b = 2 n ∑ i = 1 n ( y ′ i − y i ) \frac{\partial MSE}{\partial b} = \frac{2}{n} ∑_{i=1}^{n} (y′_i - y_i ) ∂b∂MSE=n2i=1∑n(y′i−yi)

因此根据求解的梯度,反向传播,使用梯度下降法修改权重与偏置矩阵

权重梯度调整: W = W − η ∂ M S E ∂ W 权重梯度调整:W = W - η \frac{\partial MSE}{\partial W} 权重梯度调整:W=W−η∂W∂MSE
偏置梯度调整: b = b − η ∂ M S E ∂ b 偏置梯度调整:b = b - η \frac{\partial MSE}{\partial b} 偏置梯度调整:b=b−η∂b∂MSE

η表示学习率(步长),η越大,收敛速度越快,但是越容易错过最佳值,η越小收敛速度越慢。

通过多轮训练优化,最终得到能够准确预测的权重矩阵。

看公式可能很头疼,码农肯定看代码更容易理解:

python 复制代码
 import numpy as np

# 定义均方差损失函数
def mean_squared_error(y_true, y_pred):
    return np.mean((y_true - y_pred) ** 2)

# 定义均方差损失函数的梯度
def mse_gradient(y_true, y_pred, X):
    n = len(y_true)
    gradient_w = 2 * np.dot(X.T, (y_true - y_pred)) / n
    gradient_b = 2 * np.mean(y_true - y_pred)
    return gradient_w, gradient_b

# 示例数据
X = np.array([[1, 2], [2, 3], [3, 4], [4, 5]])  # 输入特征
y_true = np.array([2, 3, 4, 5])  # 真实值
w = np.array([0.5, 0.5])  # 初始权重
b = 0.0  # 初始偏置

# 计算预测值
y_pred = np.dot(X, w) + b

# 计算均方差损失
mse = mean_squared_error(y_true, y_pred)
print("Mean Squared loss:", mse)

# 计算梯度
gradient_w, gradient_b = mse_gradient(y_true, y_pred, X)
print("Gradient of weights:", gradient_w)
print("Gradient of bias:", gradient_b)

3.2 交叉熵损失函数

均方差相比比较好理解,但是对于概率等较小的预测值,反应不灵敏,有时不能有效反应误差,如何对较小的值如(0,1)更好的做损失分析呢,聪明的你肯定可以想到用log(x):

但从取现观察看,在x∈(0,1)时,x越小,斜率的绝对值越大,说明log(x)对越小的值越敏感,从概率考虑,也就说,概率越小的事件,越不该发生,发生了越应该被注意到,越应该调整它的权重,减少它对误差的影响。

交叉熵损失函数,就是应用了这一原理。交叉熵损失函数应用在softmax()处理后形成了概率分布的输出层,softmax处理后的输出即为预测的概率分布。这里不对交叉熵损失函数求梯度做进一步学习。

3.3优化器

在我们实际的开发过程中,可以直接使用torch提供的优化器完成反向传播梯度优化。

常见的优化器有如下SGD(随机梯度)、动量(Momentum)、RMSProp、Adam等几种,极大方便模型开发训练工作,后续有机会再认真学习优化器的原理。再第五章的示例代码中,也将简单应用优化器完成模型训练。

四、激活函数

激活函数主要非线性的操作,用来对神经元的输出限制值域,以求模型有更好的表现力,能够更好学习拟合复杂的线性关系,解决梯度消失,平滑分布,矩阵稀疏等问题。

以下是常见的一些激活函数和使用场景:

4.1 SIGMOD函数

σ(x) = 1 1 + e − x \frac{1}{1+e^{-x}} 1+e−x1

特点: 输出范围(0,1),可用于表示二分类概率,也可用于平滑梯度,但在两端附近的值梯度趋零,易导致梯度消失,因此深层网络通常采用ReLU函数。

4.2 ReLU函数

f(x)=max(0,x)

特点: 输出范围在 [0, ∞) 之间,对于正输入值保持不变,有助于缓解梯度下降;而对于负输入值输出为0。可以产生稀疏激活,对于负输入值,输出为0,减少模型的复杂度和过拟合风险,但也会导致输出为0的神经元,后续不会再被激活。因此可引入ReLU函数的变体,如:

f(x)=max(αx,x); α通常取非常小的正数,如0.01

当然还有如Tanh 函数、Softmax 函数、ELU 函数等激活函数,这里就不一一介绍了。Softmax 函数主要用来生成概率分布,在自注意力机制中也有用到,到时再单独做简要说明。

五、完整示例

相关推荐
万物复苏10123 分钟前
c++-用c++解决简单数学问题
开发语言·c++·笔记·青少年编程
宇寒风暖39 分钟前
软件工程之动态建模
笔记·学习·软件工程
宇寒风暖40 分钟前
软件工程之静态建模
笔记·学习·软件工程
2402_871321951 小时前
Matlab与python数据处理对比
python·gpt·学习·算法·matlab
Re.不晚1 小时前
Java图书管理系统(简易&保姆级)
java·linux·服务器·开发语言·学习·算法·intellij-idea
楚疏笃1 小时前
鸿蒙学习使用模拟器运行应用(开发篇)
学习·华为·harmonyos
一位小说男主1 小时前
深度学习中的梯度下降算法:详解与实践
人工智能·深度学习·神经网络·算法
机器学习之心1 小时前
多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测
神经网络·matlab·lstm·长短期记忆神经网络·tcn-lstm·时间卷积神经网络
suzh1qian1 小时前
Unity类银河战士恶魔城学习总结(P146 Delete Save file-P147 Encryption of save data删除数据和加密数据)
学习·unity·c#·游戏引擎
一坨阿亮2 小时前
Zookeeper学习心得
分布式·学习·zookeeper