当前位置: 首页 > news >正文

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通常在训练之前,随机初始化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 n1i=1n(yiyi)2 或 L = 1 n ( y ′ − y ) 2 \frac{1}{n}(y′ - y)^2 n1(yy)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 n1i=1n(yiyi)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 WMSE=yMSEWy=n2i=1n(yiyi)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求导:ω0y=ω0a0ω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求导:ω1y=ω1a0ω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求导:ωny=ωna0ω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 因此Wy=[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 ) bMSE=n2i=1n(yiyi)

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

权重梯度调整: W = W − η ∂ M S E ∂ W 权重梯度调整:W = W - η \frac{\partial MSE}{\partial W} 权重梯度调整:W=WηWMSE
偏置梯度调整: b = b − η ∂ M S E ∂ b 偏置梯度调整:b = b - η \frac{\partial MSE}{\partial b} 偏置梯度调整:b=bηbMSE
η表示学习率(步长),η越大,收敛速度越快,但是越容易错过最佳值,η越小收敛速度越慢。

通过多轮训练优化,最终得到能够准确预测的权重矩阵。
看公式可能很头疼,码农肯定看代码更容易理解:

 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)) / ngradient_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+ex1

特点: 输出范围(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 函数主要用来生成概率分布,在自注意力机制中也有用到,到时再单独做简要说明。

五、完整示例

下面通过一个简单的四层模型完成分类任务

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
class FourLayerNN(nn.Module):def __init__(self, input_dim, hidden_dim1, hidden_dim2, hidden_dim3, num_classes):super(FourLayerNN, self).__init__()#四层网络,将输入数据的维度,通过网络输出num_classes维数据,也就是num_classes个分类self.fc1 = nn.Linear(input_dim, hidden_dim1)self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)self.fc3 = nn.Linear(hidden_dim2, hidden_dim3)self.fc4 = nn.Linear(hidden_dim3, num_classes)#使用ReLU激活函数self.relu = nn.ReLU() def forward(self, x):x = self.relu(self.fc1(x))x = self.relu(self.fc2(x))x = self.relu(self.fc3(x))x = self.fc4(x)return x#定义一个训练方法       
def train(model, dataloader, loss_fn, optimizer, device):#开启训练模式model.train() for batch_X, batch_y in dataloader:batch_X, batch_y = batch_X.to(device), batch_y.to(device)# 前向传播y_pred = model(batch_X)# 计算损失loss = loss_fn(y_pred, batch_y)# 反向传播#反向传播前,先将之前的梯度清零optimizer.zero_grad()loss.backward()optimizer.step()
#定义一个评估方法
def evaluate(model, dataloader, loss_fn, device):model.eval()total_loss = 0correct = 0total = 0with torch.no_grad():for batch_X, batch_y in dataloader:batch_X, batch_y = batch_X.to(device), batch_y.to(device)# 前向传播y_pred = model(batch_X)# 计算损失loss = loss_fn(y_pred, batch_y)total_loss += loss.item()# 计算准确率,概率最大的就是预测值_, predicted = torch.max(y_pred, 1)total += batch_y.size(0)correct += (predicted == batch_y).sum().item()accuracy = correct / totalaverage_loss = total_loss / len(dataloader)print(f"loss: {average_loss}, Accuracy: {accuracy}")

前面就定义好了一个四层神经网络,同时也定义好了训练方法和评估方法,接下来,模拟数据完成模型训练

np.random.seed(42)
torch.manual_seed(42)# 模拟数据参数
input_dim = 100
num_classes = 10
num_samples = 1000# 生成模拟数据,1000个x,每个x有100维,也就是100列,对应1000个y,每个y取值范围从0-9。
X = np.random.randn(num_samples, input_dim).astype(np.float32)
y = np.random.randint(0, num_classes, size=num_samples).astype(np.int64)
print(X.shape)
print(y.shape)
# 转换为 PyTorch 张量
X_tensor = torch.tensor(X)
y_tensor = torch.tensor(y)# 创建数据集和数据加载器
dataset = TensorDataset(X_tensor, y_tensor)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)# 模型参数
hidden_dim1 = 128
hidden_dim2 = 64
hidden_dim3 = 32# 实例化模型,将100维的数据,转成128维然后转64维,32维,最终分成10类
model = FourLayerNN(input_dim, hidden_dim1, hidden_dim2, hidden_dim3, num_classes)
# 定义损失函数,使用的交叉熵损失函数
loss_fn = nn.CrossEntropyLoss()# 定义优化器,使用adam优化器完成梯度优化
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练和验证
#训练轮数
num_epochs = 20device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)for epoch in range(num_epochs):print(f"Epoch {epoch + 1}/{num_epochs}")train(model, dataloader, loss_fn, optimizer, device)evaluate(model, dataloader, loss_fn, device)

以上就完成一个分类模型训练,接下来在新模拟一些数据,验证模型。

# 生成新的模拟数据
num_new_samples = 100
X_new = np.random.randn(num_new_samples, input_dim).astype(np.float32)
X_new_tensor = torch.tensor(X_new).to(device)
def predict(model, X, device):model.eval()with torch.no_grad():X = X.to(device)y_pred = model(X)_, predicted = torch.max(y_pred, 1)return predicted.cpu().numpy()# 使用训练好的模型进行分类
predictions = predict(model, X_new_tensor, device)
print(predictions)

http://www.mrgr.cn/news/78342.html

相关文章:

  • 【前端】JavaScript中的柯里化(Currying)详解及实现
  • 白光干涉仪:表面粗糙度形貌台阶高测量解决方案
  • Cyberchef 辅助网络安全运营-数据格式转换
  • hping3工具介绍及使用方法
  • uniapp跨域问题解决方案
  • 微信小程序+Vant-自定义选择器组件(多选
  • mini-spring源码分析
  • Leetcode(快慢指针习题思路总结,持续更新。。。)
  • 【halcon】Metrology工具系列之 get_metrology_object_model_contour
  • Leetcode 51 N Queens Leetcode N Queens II
  • Qt程序发布及打包成exe安装包
  • Windows Server 2019 虚拟机 安装Oracle19c,图文详情(超详细)
  • Chrome和edge浏览器如何为任何网站强制暗模式
  • git 学习笔记
  • VTK中对于相机camera的设置
  • 机载视频流回传+编解码方案
  • 分布式调用 - 服务间的远程调用RPC
  • Linux系统硬件老化测试脚本:自动化负载与监控
  • Github 基本使用学习笔记
  • 老旧前端项目如何升级工程化的项目
  • 【大模型】从零样本到少样本学习:一文读懂 Zero-shot、One-shot 和 Few-shot 的核心原理与应用!
  • 【Zookeeper】四,Zookeeper节点类型、通知、仲裁、会话
  • 去哪儿大数据面试题及参考答案
  • 使用Compose Multiplatform开发跨平台的Android调试工具
  • 小程序 - 个人简历
  • VUE练习