网站关键词格式,注册域名后怎么建站,网页登录界面制作,郑州网站加工第十五周周报 摘要Abstract一、机器学习1. 各式各样神奇的自注意力机制1.1 Local Attention/Truncated Attention#xff08;截断注意力机制#xff09;1.2 Stride Attention#xff08;跨步注意力机制#xff09;1.3 Global Attention#xff08;全局注意力机制#xff… 第十五周周报 摘要Abstract一、机器学习1. 各式各样神奇的自注意力机制1.1 Local Attention/Truncated Attention截断注意力机制1.2 Stride Attention跨步注意力机制1.3 Global Attention全局注意力机制1.4 Clustering聚类1.5 Sinkhorn Sorting NetworkSinkhorn分拣网络1.6 Linformer 二、Pytorch学习1. 完整的模型训练套路下2. 利用GPU训练 三、数学拓展——通过矩阵运算加速self-attention的过程总结 摘要
本周周报在机器学习的理论内容中详细描述了attention matrix计算的各种优化形式其中包括用人类自身的理解而诞生的Local Attention、Stride Attention、Global Attention、Clustering也有通过机器自身通过学习而诞生Sinkhorn Sorting Network最后还有简化attention matrix格式的Linformer。此外周报在Pytorch学习中还补充了模型训练的细节和描述了如何利用GPU训练的过程。最后在数学拓展部分对self-attention的计算过程优化进行了数学推导解释了如何通过矩阵乘法结合律来优化self-attention的计算速度。
Abstract
In this week’s report, the theoretical aspects of machine learning are explored, with a detailed exposition on the diverse optimization techniques for calculating the attention matrix. Various forms of attention mechanisms are discussed, including Local Attention, Stride Attention, Global Attention, and Clustering, which have been conceptualized through human comprehension. The Sinkhorn Sorting Network, a novel approach that has been independently developed through machine learning algorithms, is also presented. Moreover, the Linformer, which streamlines the attention matrix format, is introduced.Furthermore, the report provides additional insights into the nuances of model training within the PyTorch framework and elucidates the process of leveraging GPU resources for training purposes. In the mathematical extension section, the report delves into the optimization of the self-attention computation process, offering a mathematical derivation that explains how the associative property of matrix multiplication can be utilized to enhance the computational efficiency of self-attention mechanisms.
一、机器学习
1. 各式各样神奇的自注意力机制
之前的self-attention学习中我们接触到了各种各样的Transformer的变形。 我们都知道self-attention机制是Transformer里面重要的一环 所以有些情况改进self-attention对优化Transformer有很大的帮助 所以我们今天来探究一下如何对self-attention进行优化 不过在说优化self-attention之前我们需要知道一个非常重要的东西 self-attention只是Transformer中的一个module 这就是为什么我上面要强调“有些情况优化self-attention” 而不是 “任何时候优化self-attention” 是因为有时候我们优化self-attention未必对整个Transformer的优化有很好的效果。 我们都知道Transformer是一个Seq 2 Seq的模型Sequence的长度决定了self-attention的运算复杂度。 如果我们的Sequence 长度记为N并不是很大时整个Transformer的运算复杂程度可能就是由fully connected 层主导的。所以彼时我们优化self-attention其实并没有太大的效果。 但是我们在做图像识别的时候N往往很大的。 比如我们有一张256 * 256 size的图片我们把图片中的每个pixel展开为一列就是 256 * 256 个pixel即Sequence length为2562 假设Sequence的长度为N那么我们计算其αattention score时就要进行N2次运算(如下图矩阵所示每个key与query相乘)。 如果 N 的值为256*256就要进行2564次运算这个运算量是非常庞大的。 所以在图像识别中Transformer主要的运算集中在self-attention中因此我们此时优化self-attention模块是非常有必要的 那么我们有什么优化self-attention的方式呢
1.1 Local Attention/Truncated Attention截断注意力机制
self-attention之所以运算复杂是因为我们要处理N2次运算即处理q与k的matrix 如果我们对self-attention实际情况有所理解是不是就可以不用计算N2次那么多呢 如下图所示 我们的输入只与其相邻的有关我们就不用关注全局的输入 因此我们计算α时只需计算相邻的。 那这样就可以加速运算因为灰色的部分不用计算 解释如下 但这显然有一个问题 就是每次你在做self-attention的时候你只看得到某一个小范围之内的信息 如果attention都是在一个很小的范围内那其实他就是CNN。 所以用Truncated Attention那就等于是做CNN那不如直接用CNN就好了 所以它是一个可以加快self-attention的运算的方法。但不一定可以有非常好的结果。
1.2 Stride Attention跨步注意力机制
那有那除了local attention以外还有各式各样的变形 只看相邻的信息不好那我们就看比较远一点的信息 比如说在每一个位置我们不是看下一个位置的信息和上一个位置的信息 而是先跳两格看第三个位置之后的信息和三个位置之前的信息这种方法叫做Stride Attention 当用Stride Attention的时候在下图灰色的部分就是直接填零就我们只计算有颜色的部分。
当然可以根据你现在处理的问题决定可以只空一格也可以空三格。
1.3 Global Attention全局注意力机制
不管是刚才看过的local attention还是stride attention都是以某一个位置作为中心的看这个位置看左右两边的信息。 如果我们想要知道一整个attention的信息就可以使用Global Attention。 在做Global Attention的时候会在原来的Sequence里面加上一个特殊的token。
假设是处理NLP的问题在文字的加入一个特殊的符号。这个特殊的符号代表这个位置要做global attention。 他会从里面的每一个token去收集信息来了解一下这整个句子里面整个里面发生了什么样的事情。这些special token他们会attend到所有的token也会被所有的token attend global attention的具体操作有两种 ①在你原来的token里面就直接选择一些token作为special token。 举个例子一般用Transformer来处理这个文字的时候会放一个开头的token当做special token或者是把句号当做special token就是你从原来就有的token选一些当做 special token ②另外一种做法是外加额外的token 不管句子是什么内容都直接插如两个符号这两个符号代表是special token。
如下图所示 这一个句子里面这一个input sequence里面头两个位置。 头两个位置就是special token叫做global attention的special token 这两个special token会看到所有其他的token做其他的token也都会看到这两个token。 凭借着人的理解决定的这一种self-attention的有local attention、stride attention、global attention。 那哪一种最好呢 真正好的结果就是全部都有 在multi-head attention中我们可以设定多个不同的head。就让每一个head做不同的事 有的是local attention、有的是stride attention、有的是global attention等这样你就不需要做选择了。 以一些比较知名的transformer这个的变形就是这么做的比如 Longformer就是把local attentionstride attentionglobal attention。 BigBird则使用了random attention即随机选一些token让它们彼此之间是有联系的
1.4 Clustering聚类
刚刚我们是用人的理解直接告诉你说哪一些位置就不需要算哪些位置。也许人类设计的attention并不能得到最好的结果。 那我们能不能够不要用人力的方法呢 举例来说在一个self-attention的吗matrix里可能某些位置他的value特别大某一些位置他的attention的value特别小。那这个时候其实你可以把特别小的位置直接补零因为特别小的位置本来他可能本来就很接近零也许有变成零跟没有变成零结果是不会差距太大的。 那如果我们用这样子的方法来加快的运算那我也那也许我们就是就是简化以后的跟原来也不会有太大的差距。 如下图 我们可以直接估计在一个里面哪一些位置有可能会有比较大的值我们只计算那些有可能会有比较大的值的位置。 如果有些位置它的attention的位置的value就会很小直接就不进行任何的计算直接把它当做是零。 问题是怎么快速的估算出哪些位置可能有大的、哪些位置可能有小到直接就可以无视呢 这里有一个技术叫做——Clustering聚类 step 1 先把query与key拿出来根据query与key他们相近的程度做clustering。 比较近的就分配在一起比较远的就属于不同的cluster 如下图所示 这个例子里边框红色为cluster 1 、边框紫色为cluster 2、边框绿色为cluster 3、边框黄色为cluster 4。 clustering的目的是要让相近的vector就属于同一个cluster不相近的cluster就属于不同的vector。 **那clustering会不会也耗费很大的运算量呢**如果clustering的complexity也跟sequence的平方有关的话那根本没有办法加速的运算。 但是实际上clustering有很多可以加速的方法 其会采取一个可能是估测不是非常准确但非常快速的方法来将query与key进行分区以后再补充这个方法
step 2 把query跟key归在同一群里面以后接下来我们才去计算的weight 如下图 红色的query跟这三个key是在同一个cluster里面那我们就计算一下红色的attention weight 红色的query跟跟其他的这五个key不在同一个cluster里面就代表他们的距离其实是偏远的直接把他们的attention weight设为0。 其他颜色同理 所以整个attention matrix里面会有很多位置都不需要去计算这样就可以加快这个attention matrix的计算
1.5 Sinkhorn Sorting NetworkSinkhorn分拣网络
我们怎么决定哪一些位置要计算哪些位置和不要计算都是凭着人类对这个问题的理解去解决的因为clustering是假设比较近的vector我们才去计算它的attention 所以到目前为止要不要计算attention往往还是基于人类对这个问题的理解。
那有没有办法把要不要计算attention这件事情直接把它learn出来呢 我们要计算的的时候你要先产生一个attention matrix这个attention matrix里有一些位置是1有一些位置是0。 如下图示 涂深色的代表1涂浅色的代表0。只有涂深色的地方需要去计算attention浅色的则不需要计算。 哪个query要与对应的key做点积我们需要用下图的matrix来决定。matrix而这个怎么来呢在到目前为止这部分计算的都是人决定的。 但是在Sinkhorn Sorting Network里它决定直接learn一个network让另外一个network来决定哪些地方需要计算attention 那要怎么做呢 如下图示 绿色部分是你input sequence。 然后把input sequence的每一个position通过一个network产生一个vectorvector的长度要跟sequence一致 然后input sequence每一个vector通过一个network就产生另外一排vector。 把这些vector全部拼起来以后它的大小也要是N × N跟attention matrix一样 那我们现在的目标就是要把这么多排的vector变成attention matrix。 计算出来那么多排vector不是binary的而是连续的即其值不止0和1而我们的attention matrix是binary只有0和1其用一个很特别的技术来解决vector排到matrix转换这个问题而且这一个过程是可以微分的所以最后你在train这个nn的时候是跟整个network一起train出来的。
那有个疑问难道input sequence 的 vector 通过 NN 产生另外一个vector就不耗时了吗 其实这里面是有技巧的即好几个input vector会共用同一个产生出来的vector 例如 假设input一个很长的sequence假设100个vector。将其分成10段每段10个vector10个vector一起通过network以后产生另外一个vector那10个vector要共用同一个产生初出来的vector。 可以理解为假设本来要算100×100的matrix即100×100的attention 在Sinkhorn Sorting Network是解析度比较低的它其实做10 × 10 的attention然后再放大为100 × 100
1.6 Linformer
到目前为止都想产生一个N × N的matrix但是真的需要N × N的matrix吗 如下图所示 在我们计算attention matrix中其实很多grid的值都是一样的颜色一致 即很多信息都是重复的那我们为何不把这些重复的信息拿掉产生一个比较小的attention matrix。 那要怎么实现呢 来有N个key从N个k里面是挑大K个出来当做key的代表。 把这个attention的matrix变瘦我们只算一个n×k个attention matrix。 那产生attention matrix后接下来你要怎么用attention matrix这个产生self-attention layer的output呢 你有n个value你一样要挑出k个具代表性的value 有k个key vector就有k个value vector k个key对第一个行query算出来的attention weight对K个value vector做运算得到第一个位置的output。 其他以此类推 为什么我们只选有代表性的key而不选有代表性的query呢 如果我们把query变为原来的一半那么我们的output sequence也会变为原来的一半 假设你今天的任务是input sequence里面的每一个位置都需要output一个label那么如果我们选择减少query的数量输出结果就会出错。
那么我们的代表性的key是如何选出来的呢 方式一直接将input sequence 它通过CNN将N个key变为K个key
方式二我们input sequence 的 vector 维度是 d那么有 N 个 vector 整个就为d × N 的矩阵 我们可以用这d × N的矩阵 跟一个 N × K的 矩阵相乘变为一个 d × K的矩阵 实际上就是一个线性结合的过程选出来代表性的key 都是结合了之前N个vector信息的。
二、Pytorch学习
1. 完整的模型训练套路下
上一周我们学习了完整的训练套路从数据集的准备与处理、模型的搭建、模型的训练、结果的输出、模型的测试等各方面做了一次完整的学习。 但是我们训练的时候不单只有回归的模型。在我们日常生活中分类问题也是很重要的 但是分类很重要的一点就是要需要展示正确率例如我们测试集中有100张图片我在这100张图片到底预测正确了多少张。这就是所谓的正确率。 然而在我们上一周的模型中却没有对分类这一特定的问题展示模型预测的正确率。所以这一周我们要实现这个功能。 思路如下图所示 argmax就是帮我们选取模型预测后概率最大的那个类别的下标。 我们可以用一个demo来感受一下
import torchoutput torch.tensor([[0.1, 0.2],[0.05, 0.3]])print(output.argmax(1))可以看到其结果是[1,1]跟我们想象的一样是横向排01的。 当argmax取0时结果如下
import torchoutput torch.tensor([[0.1, 0.2],[0.05, 0.3]])print(output.argmax(0))
可以看到其结果是[0,1]因为是横向排01的。 整个demo代码如下
import torch# 假设这个是模型预测的结果只有两个类别 0 与 1
output torch.tensor([[0.1, 0.2],[0.05, 0.3]])# 输出argmax的结果
print(output.argmax(1))# 预测值
preds output.argmax(1)# 真实值
input_targert torch.tensor([[0, 1]])# 比较真实值与预测值输出 每一个预测的true与false
print(preds input_targert)# 输出全部预测的结果
print((preds input_targert).sum())
可以看到跟我们手写的结果差不多 接下来我们要将其运用到我们的上一周的CIFAR-10 的 Model 中 主要修改的地方就是测试模块部分 需要加上我们上述的代码 看看最终的预测结果 代码如下
import torch
import torchvision
from torch.utils.tensorboard import SummaryWriterfrom p23_model import *# 第一步准备数据集
# 训练集
train_data torchvision.datasets.CIFAR10(root./datasets, trainTrue, downloadTrue,transformtorchvision.transforms.ToTensor())
# 测试集
test_data torchvision.datasets.CIFAR10(root./datasets, trainFalse, downloadTrue,transformtorchvision.transforms.ToTensor())
# length 用于展示训练集和测试集的长度
train_data_size len(train_data)
test_data_size len(test_data)
# format的用法{}.format(变量) ,变量的值会替换{} -》例如train_data_size 10,那么{}就会被替换成10
print(训练数据集的长度为:{}.format(train_data_size))
print(测试数据集的长度为:{}.format(test_data_size))# 第二步利用dataloader 加载训练数据集
# 加载训练集
train_dataloader torch.utils.data.DataLoader(train_data, batch_size64)
# 加载测试集
test_dataloader torch.utils.data.DataLoader(test_data, batch_size64)# 加载模型
MCifar MCifar()# 定义损失函数cross entropy
loss_func torch.nn.CrossEntropyLoss()# 优化器
# learning_rate 0.1
# 1e-2 1 * (10)^-2
learning_rate 1e-2
optimizer torch.optim.SGD(MCifar.parameters(), lrlearning_rate)# 设置训练网络的一些参数
# 训练的轮数
total_train_step 0
# 记录测试的次数
total_test_step 0# 训练的轮数
epoch 10# 添加tensorboard
writer SummaryWriter(./logs_train)for i in range(epoch):print(------第 {}轮训练开始-------.format(i 1))# 训练步骤开始for data in train_dataloader:images, labels dataoptimizer.zero_grad()outputs MCifar(images)loss loss_func(outputs, labels)loss.backward()optimizer.step()# 训练完一次数训练的次数1total_train_step 1if total_train_step % 100 0:print(训练次数{},Loss:{}.format(total_train_step, loss.item()))# 将train_loss的数值加入到tensorboard中writer.add_scalar(train_loss, loss.item(), total_train_step)# 测试步骤开始total_test_loss 0# 测试的正确率total_test_accuracy 0with torch.no_grad():for data in test_dataloader:images, labels dataoutputs MCifar(images)loss loss_func(outputs, labels)total_test_step 1total_test_loss total_test_loss loss# 正确率accuracy (outputs.argmax(1) labels).sum()total_test_accuracy total_test_accuracy accuracyprint(整体的loss为{}.format(total_test_loss))print(整体的正确率为{}.format(total_test_accuracy / test_data_size))# 将test_loss的数值加入到tensorboard中writer.add_scalar(test_loss, total_test_loss, total_test_step)# 保存模型# 每训练一轮保存一次torch.save(MCifar, Mcifar_{}.pth.format(i))print(模型已保存)# 关闭tensorboard
writer.close()
可以看到随着训练轮数的增加正确率也在慢慢上升 此外我们还要注意一些小细节。 例如我们在训练的时候可以加上.train()表示训练开始 # 训练步骤开始MCifar.train()for data in train_dataloader:images, labels dataoptimizer.zero_grad()outputs MCifar(images)loss loss_func(outputs, labels)loss.backward()optimizer.step()# 训练完一次数训练的次数1total_train_step 1if total_train_step % 100 0:print(训练次数{},Loss:{}.format(total_train_step, loss.item()))# 将train_loss的数值加入到tensorboard中writer.add_scalar(train_loss, loss.item(), total_train_step).train()就是将模块设置为训练模式。 这个操作或设置只对特定的模块有影响。例如像Dropout和Batch Normalization这样的模块可能会受到这些操作或设置的影响。
同样在测试集中我们可以使用.eval()
# 测试步骤开始MCifar.eval()total_test_loss 0# 测试的正确率total_test_accuracy 0with torch.no_grad():for data in test_dataloader:images, labels dataoutputs MCifar(images)loss loss_func(outputs, labels)total_test_step 1total_test_loss total_test_loss loss# 正确率accuracy (outputs.argmax(1) labels).sum()total_test_accuracy total_test_accuracy accuracyprint(整体的loss为{}.format(total_test_loss))print(整体的正确率为{}.format(total_test_accuracy / test_data_size))# 将test_loss的数值加入到tensorboard中writer.add_scalar(test_loss, total_test_loss, total_test_step).eval()将模块设置为评估模式。 这种设置只对某些模块有影响。如果这些模块受到影响可以在特定模块的文档中查看它们在训练/评估模式下的行为细节例如Dropout随机失活和BatchNorm批量归一化。 补充了.trian()与.eval()后我们的代码实战的细节就比较完整了
2. 利用GPU训练
方式一 cuda就是利用我们的独显训练可以加快运行效率极大提升运行速度 这是我们用cpu训练的速度训练一轮需要25秒非常耗时 所以我们如果有一台好的电脑需要发挥它强大的GPU 以下内容可以使用cuda即使用GPU 接下来我们加入 计时器 以及 模型、训练数据集和测试数据集的cuda
# 计时器
import time
# 开始时间
start_time time.time()
# 结束时间
end_time time.time()# cuda的使用
# 加载模型
mCigar MCifar()
if torch.cuda.is_available():mCigar mCigar.cuda()# 训练数据集的cuda使用
if torch.cuda.is_available():images images.cuda()labels labels.cuda()# 测试数据集的cuda使用
if torch.cuda.is_available():images images.cuda()labels labels.cuda()整体代码如下 只训练一轮
import torch
import torchvision
from torch import nn
from torch.utils.tensorboard import SummaryWriter
import time# 第一步准备数据集
# 训练集
train_data torchvision.datasets.CIFAR10(root./datasets, trainTrue, downloadTrue,transformtorchvision.transforms.ToTensor())
# 测试集
test_data torchvision.datasets.CIFAR10(root./datasets, trainFalse, downloadTrue,transformtorchvision.transforms.ToTensor())
# length 用于展示训练集和测试集的长度
train_data_size len(train_data)
test_data_size len(test_data)
# format的用法{}.format(变量) ,变量的值会替换{} -》例如train_data_size 10,那么{}就会被替换成10
print(训练数据集的长度为:{}.format(train_data_size))
print(测试数据集的长度为:{}.format(test_data_size))# 第二步利用dataloader 加载训练数据集
# 加载训练集
train_dataloader torch.utils.data.DataLoader(train_data, batch_size64)
# 加载测试集
test_dataloader torch.utils.data.DataLoader(test_data, batch_size64)# 模型
class MCifar(torch.nn.Module):def __init__(self):super(MCifar, self).__init__()self.model nn.Sequential(nn.Conv2d(3, 32, 5, 1, 2),nn.MaxPool2d(2, 2),nn.Conv2d(32, 32, 5, 1, 2),nn.MaxPool2d(2, 2),nn.Conv2d(32, 64, 5, 1, 2),nn.MaxPool2d(2, 2),nn.Flatten(),nn.Linear(1024, 64),nn.Linear(64, 10),)def forward(self, x):x self.model(x)return x# 加载模型
mCigar MCifar()
if torch.cuda.is_available():mCigar mCigar.cuda()# 定义损失函数cross entropy
loss_func torch.nn.CrossEntropyLoss()
if torch.cuda.is_available():loss_func loss_func.cuda()
# 优化器
# learning_rate 0.1
# 1e-2 1 * (10)^-2
learning_rate 1e-2
optimizer torch.optim.SGD(mCigar.parameters(), lrlearning_rate)# 设置训练网络的一些参数
# 训练的轮数
total_train_step 0
# 记录测试的次数
total_test_step 0# 训练的轮数
epoch 1# 添加tensorboard
writer SummaryWriter(./logs_train_gpu1)
start_time time.time()
for i in range(epoch):print(------第 {}轮训练开始-------.format(i 1))# 训练步骤开始mCigar.train()for data in train_dataloader:images, labels dataif torch.cuda.is_available():images images.cuda()labels labels.cuda()optimizer.zero_grad()outputs mCigar(images)loss loss_func(outputs, labels)loss.backward()optimizer.step()# 训练完一次数训练的次数1total_train_step 1if total_train_step % 100 0:print(训练次数{},Loss:{}.format(total_train_step, loss.item()))# 将train_loss的数值加入到tensorboard中writer.add_scalar(train_loss, loss.item(), total_train_step)# 测试步骤开始mCigar.eval()total_test_loss 0# 测试的正确率total_test_accuracy 0with torch.no_grad():for data in test_dataloader:images, labels dataif torch.cuda.is_available():images images.cuda()labels labels.cuda()outputs mCigar(images)loss loss_func(outputs, labels)total_test_step 1total_test_loss total_test_loss loss# 正确率accuracy (outputs.argmax(1) labels).sum()total_test_accuracy total_test_accuracy accuracyprint(整体的loss为{}.format(total_test_loss))print(整体的正确率为{}.format(total_test_accuracy / test_data_size))# 将test_loss的数值加入到tensorboard中writer.add_scalar(test_loss, total_test_loss, total_test_step)# 保存模型# 每训练一轮保存一次torch.save(mCigar, mCigar_{}.pth.format(i))print(模型已保存)end_time time.time()
print(end_time - start_time)
# 关闭tensorboard
writer.close()
可以看到我们训练一轮使用了10秒左右比原来的25秒快乐15秒速度得到极大的提升 方式二——.to(device)
# 引入设备意思即使cuda可用时用cuda否则用cpu
device torch.device(cuda:0 if torch.cuda.is_available() else cpu)
# 加载模型
mCigar MCifar()
mCigar.to(device)# 训练数据集的cuda使用
images, labels images.to(device), labels.to(device)# 测试数据集的cuda使用
images, labels images.to(device), labels.to(device)整体代码如下
import torch
import torchvision
from torch import nn
from torch.utils.tensorboard import SummaryWriter
import time# 引入设备
device torch.device(cuda:0 if torch.cuda.is_available() else cpu)
# 第一步准备数据集
# 训练集
train_data torchvision.datasets.CIFAR10(root./datasets, trainTrue, downloadTrue,transformtorchvision.transforms.ToTensor())
# 测试集
test_data torchvision.datasets.CIFAR10(root./datasets, trainFalse, downloadTrue,transformtorchvision.transforms.ToTensor())
# length 用于展示训练集和测试集的长度
train_data_size len(train_data)
test_data_size len(test_data)
# format的用法{}.format(变量) ,变量的值会替换{} -》例如train_data_size 10,那么{}就会被替换成10
print(训练数据集的长度为:{}.format(train_data_size))
print(测试数据集的长度为:{}.format(test_data_size))# 第二步利用dataloader 加载训练数据集
# 加载训练集
train_dataloader torch.utils.data.DataLoader(train_data, batch_size64)
# 加载测试集
test_dataloader torch.utils.data.DataLoader(test_data, batch_size64)# 模型
class MCifar(torch.nn.Module):def __init__(self):super(MCifar, self).__init__()self.model nn.Sequential(nn.Conv2d(3, 32, 5, 1, 2),nn.MaxPool2d(2, 2),nn.Conv2d(32, 32, 5, 1, 2),nn.MaxPool2d(2, 2),nn.Conv2d(32, 64, 5, 1, 2),nn.MaxPool2d(2, 2),nn.Flatten(),nn.Linear(1024, 64),nn.Linear(64, 10),)def forward(self, x):x self.model(x)return x# 加载模型
mCigar MCifar()
mCigar.to(device)# 定义损失函数cross entropy
loss_func torch.nn.CrossEntropyLoss()
loss_func.to(device)
# 优化器
# learning_rate 0.1
# 1e-2 1 * (10)^-2
learning_rate 1e-2
optimizer torch.optim.SGD(mCigar.parameters(), lrlearning_rate)# 设置训练网络的一些参数
# 训练的轮数
total_train_step 0
# 记录测试的次数
total_test_step 0# 训练的轮数
epoch 10# 添加tensorboard
writer SummaryWriter(./logs_train_gpu1)
start_time time.time()
for i in range(epoch):print(------第 {}轮训练开始-------.format(i 1))# 训练步骤开始mCigar.train()for data in train_dataloader:images, labels data# 训练数据集的cuda使用images, labels images.to(device), labels.to(device)optimizer.zero_grad()outputs mCigar(images)loss loss_func(outputs, labels)loss.backward()optimizer.step()# 训练完一次数训练的次数1total_train_step 1if total_train_step % 100 0:print(训练次数{},Loss:{}.format(total_train_step, loss.item()))# 将train_loss的数值加入到tensorboard中writer.add_scalar(train_loss, loss.item(), total_train_step)# 测试步骤开始mCigar.eval()total_test_loss 0# 测试的正确率total_test_accuracy 0with torch.no_grad():for data in test_dataloader:images, labels data# 测试数据集的cuda使用images, labels images.to(device), labels.to(device)outputs mCigar(images)loss loss_func(outputs, labels)total_test_step 1total_test_loss total_test_loss loss# 正确率accuracy (outputs.argmax(1) labels).sum()total_test_accuracy total_test_accuracy accuracyprint(整体的loss为{}.format(total_test_loss))print(整体的正确率为{}.format(total_test_accuracy / test_data_size))# 将test_loss的数值加入到tensorboard中writer.add_scalar(test_loss, total_test_loss, total_test_step)# 保存模型# 每训练一轮保存一次torch.save(mCigar, cifar10_{}.pth.format(i))print(模型已保存)end_time time.time()
print(end_time - start_time)
# 关闭tensorboard
writer.close()结果如下
三、数学拓展——通过矩阵运算加速self-attention的过程
我们来回顾一下从Input Sequence 到 Output Sequence的过程如下图所示 其实attention的这个process其实就是一连串矩阵的相乘的过程 这个矩阵相乘的过程中有没有可以减少运算量的部分呢 如下图所示在我们的矩阵乘法中是有结合律的。 不同的结合顺序他们的运算复杂度就不同 举个具体的例子 然后代入到我们的self-attention运算当中 可以看到先算V×KT × Q 明显比V ×KT× Q的运算量要小 那我们在self-attention的运算中要如何想让V与KT先相乘呢 我们先来复习一下self-attention的计算过程 换算过程如下 拿计算b1举例 整个过程图示如下 拿b1举例 算出的是分子分母的运算就不用赘述了。 exp ( q ⋅ k ) ≈ ϕ ( q ) ⋅ ϕ ( k ) \begin{aligned} \exp (\boldsymbol{q} \cdot \boldsymbol{k}) \\\approx \phi(\boldsymbol{q}) \cdot \phi(\boldsymbol{k})\end{aligned} ≈exp(q⋅k)ϕ(q)⋅ϕ(k) 以上公式的转换有很多方法具体可以参考以下论文 其实现在的transformer中attention matrix甚至都不需要训练其直接将其作为参数放入到network中训练的效果也是大差不差的因此人们对attention matrix是否留用又产生了进一步的思考
总结
本周的进度比较缓慢希望下一周再接再厉。 本周在机器学习理论部分学习了的变形。自注意力机制是深度学习中的一种关键技术它允许模型在序列的不同位置关注不同的信息。本周的报告涵盖了以下自注意力机制的优化形式1、 Local Attention/Truncated Attention截断注意力机制这种机制限制了注意力的范围只关注输入序列中的局部区域从而减少了计算复杂度。2、Stride Attention跨步注意力机制通过在计算注意力时采用步长这种机制可以有效地处理长序列同时保持对重要信息的关注。3 、Global Attention全局注意力机制与局部注意力相反全局注意力机制考虑整个输入序列捕捉全局依赖关系。4、Clustering聚类通过将输入序列分组到不同的簇中这种机制可以有效地处理具有相似特征的序列部分。5、Sinkhorn Sorting NetworkSinkhorn分拣网络这是一种新颖的自注意力机制它通过Sinkhorn排序算法来优化注意力权重的分配。6、LinformerLinformer通过简化注意力矩阵的格式减少了计算量同时保持了模型的性能。 在Pytorch学习中针对分类问题利用argmax()加入了准确率的输出才外还补充训练的一些细节比如在训练模块加上.train()在测试模块加上.eval()。此外还并详细描述了如何利用GPU加速模型训练过程。 在数学拓展部分探讨了如何通过矩阵运算来加速self-attention的计算过程通过重新排列矩阵乘法的顺序可以减少计算量并提高计算效率。 下一周计划学习生成式模型然后完结Pytorch课程学习跑一次完整的模型。