第十四周:机器学习笔记

news2024/9/29 15:15:57

第十四周周报

  • 摘要
  • Abstract
  • 一、机器学习——Transformer(下)
    • 1. Transformer decoder
      • 1.1 autoregressive decoder(自回归解码器)
      • 1.2 Transformer decoder结构图
      • 1.3 non-autoregressive decoder(非自回归解码器)
    • 2. encoder-decoder attention(编码器-解码器注意力)
    • 3. Transformer 的训练过程
    • 4. 序列到序列模型训练常用的Tips
  • 二、Pytorch学习
    • 1. 完整的模型训练套路
  • 三、数学拓展
    • 1. 梯度(gradient)
  • 总结

摘要

本周周报在机器学习的理论内容中,概述了Transformer里面Decoder的内容,其中包括Decoder的结构、autoregressive decoder(自回归解码器)以及non-autoregressive decoder(非自回归解码器),此外还讲述了Transformer 的训练过程,并在最后提供了序列到序列模型训练常用的Tips。在Pytorch代码学习中,本周报详细的讲述了训练模型的一些常用手段和流程,并通过代码实现了包括数据收集、模型搭建、模型训练、结果展示等过程。最后,在数学理论的学习中,本周周报描述了梯度的几何意义以及数学意义,并在最后解释了为什么梯度永远指向最大的方向。

Abstract

In the field of machine learning, the theoretical content of this week’s report has been outlined, focusing on the Decoder component within the Transformer architecture. The structure of the Decoder has been detailed, with particular emphasis on autoregressive and non-autoregressive decoding strategies. Additionally, the training process of the Transformer has been discussed, and a set of tips commonly used in the training of sequence-to-sequence models has been provided.In the context of PyTorch code learning, this week’s report has detailed the common methods and procedures for training models. A comprehensive demonstration has been provided, which includes the processes of data collection, model construction, model training, and result presentation through code.Finally, in the study of mathematical theory, the report has described the geometric and mathematical significance of gradients. An explanation has been offered at the end of the report as to why gradients are always directed towards the direction of the greatest increase.

一、机器学习——Transformer(下)

Transformer 是一个基于自注意力的序列到序列(Sequence to Sequence)的模型
Transformer 与基于循环神经网络的序列到序列模型不同,其可以能够并行计算。

我们上一周学习了Transformer encoder,了解了其结构
具体如下:
在这里插入图片描述
在这里插入图片描述

1. Transformer decoder

1.1 autoregressive decoder(自回归解码器)

Transformer decoder中解码器比较常见的称为自回归的(autoregressive)解码器
以语音识别为例:其功能就是输入一段声音,输出一串文字。
如下图所示,步骤如下
1、 把一段声音(“机器学习”)的声音信号输入给编码器,输出会变成一排向量。
2、 接下来解码器产生语音识别的结果,解码器把编码器的输出先“读”进去。
3、 要让解码器产生输出文字,首先要先给它一个代表开始的特殊符号
< BOS >,即 Begin Of Sequence,这是一个特殊的词元(token)。 在词表(vocabulary)里面,在本来解码器可能产生的文字里面多加一个特殊的符号 < BOS >。
在机器学习里面,假设要处理自然语言处理的问题,每一个词元都可以用one-hot vector来表示(即一个向量表示一个文字,其中一维是 1,其他都是 0) 所以 < BOS > 也是用one-hot vector来表示,其中一维是 1,其他是 0。比如:三维,向量100表示 你 ;向量010表示 我,001表示< BOS >等等。
4、 接下来解码器会“吐”出一个向量,该向量的长度跟词表的大小是一样的。
在产生这个向量之前,跟做分类一样,通常会先进行一个 softmax 操作(目的是为了控制在0~1之间,生成概率。这个向量里面的分数是一个分布,该向量里面的值全部加起来,总和是 1)。
这个向量会给每一个中文字一个分,分数最高的中文字就是最终的输出。
“机”的分数最高,所以“机”就当做是解码器的第一个输出。
在这里插入图片描述
Decoder的输出单位是什么呢?
不同的语言,输出的单位不见不会不一样,这由我们对各个语言的理解来决定
比如英语,选择输出英语的字母。但字母作为单位可能太小了,有人可能会选择输出英语的词汇,英语的词汇是用空白作为间隔的。但如果都用词汇当作输出又太多了,有一些方法可以把英语的字首、字根切出来,拿字首、字根当作单位。
中文通常用中文的一个字来当作单位,这个向量的长度就跟机器可以输出的方块字的数量是一样多的。每一个中文的字都会对应到一个数值。

5、 接下来把“机”当成解码器新的输入。
根据两个输入:特殊符号 < BOS >和 “机” 的输入,decoder输出一个蓝色的向量。
蓝色的向量里面会给出每一个中文字的分数,假设“器”的分数最高,“器”就是输出。
解码器接下来会拿“器”当作输入,其看到了 < BOS >、“机”、“器”,可能就输出“学”。
解码器看到 < BOS >、“机”、“器”、“学”,它会输出一个向量。
这个向量里面“习”的分数最高的,所以它就输出“习”。
这个过程就反复地持续下去。
在这里插入图片描述
这种输出方式会存在一个问题就是——一步错步步错
解码器的输入是它在前一个时间点的输出,它会将自己的输出视为下一个输入,因此当解码器生成一个句子时,它可能会看到一些错误。
如图所示:
如果解码器出现语音识别错误,它会将机器的 “器” 误认为天气的 “气” 。
然后,解码器将根据不正确的识别结果生成预期的正确输出,这可能会导致错误传播问题。
一步错误会导致一步一步的错误,后面可能无法生成正确的词汇。
在这里插入图片描述

1.2 Transformer decoder结构图

Transformer 里面的解码器内部的结构如图所示:
类似于编码器,解码器也有多头注意力、残差连接和层归一化、前馈神经网络(全连接)。
解码器最后再做一个 softmax,使其输出变成一个概率。
在这里插入图片描述
从上图中,我们可以发现decoder中也有暗藏玄机的地方:
其使用了——masked self-attention(掩蔽自注意力)
掩蔽自注意力可以通过一个掩码(mask)来阻止每个位置选择其后面的输入信息。
在这里插入图片描述
下图更加直观的表示了计算过程:
在这里插入图片描述
那为什么要使用masked self-attention?
因为解码器的输出是一个一个产生的,所以只能考虑其左边的东西,没有办法考虑其右边的东西。

关键的问题——输出如何停止?
实际应用中输入跟输出长度的关系是非常复杂的,我们无法从输入序列的长度知道输出序列的长度,因此解码器必须决定输出的序列的长度。

给定一个输入序列,机器可以自己学到输出序列的长度。
但在目前的解码器运作的机制里面,机器不知道什么时候应该停下来。

如图所示:
机器产生完“习”以后,还可以继续重复一模一样的过程,把“习”当做输入,解码器可能就会输出“惯”,接下来就一直持续下去,永远都不会停下来。
在这里插入图片描述
要让解码器停止运作,需要特别准备一个特别的符号 < EOS >。
如下图所示:
在这里插入图片描述
产生完“习”以后,再把“习”当作解码器的输入以后,解码器就要能够输出 < EOS >
解码器看到编码器输出的嵌入、< BOS >、“机”、“器”、“学”、“习”以后,其产生出来的向量里面 < EOS > 的概率必须是最大的,于是输出 < EOS >,整个解码器产生序列的过程就结束了。

1.3 non-autoregressive decoder(非自回归解码器)

自回归模型首先以< BOS >为输入,输出w1,然后以w1为输入,并输出w2,直到输出< EOS >

假设生成的句子是中文的,非自回归不会一次生成一个单词,而是一次生成整个句子
非自回归解码器可以输入整行< BOS >词法元素,一次生成一行词法元素,例如:
将4个< BOS >词汇元素输入非自回归解码器,该解码器将产生4个汉字。
由于输出长度未知,用作非自回归解码器的< BOS >输入数量也未知。
因此,有两种方法:

1、用分类器来解决这个问题。
分类器中的输入是编码器(encoder)的输入,输出是一个数字,该数字代表解码器应该要输出的长度。
比如:分类器输出 4,非自回归的解码器就将输入4 个 < BOS >的词元,产生 4 个中文的字。

2、给编码器一堆 < BOS > 的词元。
假设输出的句子的长度有上限,绝对不会超过 300 个字。
给编码器 300 个 < BOS >,就会输出 300 个字,输出 < EOS > 右边的的输出就当它没有输出。
在这里插入图片描述
非自回归的解码器(non-autoregressive decoder)的优点如下:
1、并行化
当自回归解码器输出一个句子时,它是逐字生成的。
假设要输出100个单词的句子,则需要解码100次。
然而,非自回归解码器在一步中不用管句子的长度就生成完整的句子。
因此,非自回归解码器将比自回归解码器运行得更快。
非自回归解码器的想法是在有 Transformer 以后,有这种自注意力的解码器以后才有的。以前,如果使用长短期记忆网络(LSTM)或RNN并给定一行< BOS >,它将无法同时生成所有输出,其输出是一个一个产生的。

2、非自回归解码器更能够控制其输出的长度
在语音合成中,非自回归解码器非常常用。
非自回归解码器可以控制输出的长度,分类器可用于确定非自回归译码器应输出的长度。
在进行语音合成时,如果你想让系统说得更快,把分类器的输出除以2,系统就会说得快一倍。
如果你想放慢你的语音速度,把分类器输出的长度乘以2倍,解码器的语音速度就会加倍。
因此,非自回归解码器可以控制解码器输出的长度并进行各种更改。

总之,并行化是非自回归解码器的最大优势,**但非自回归译码器的性能往往不如自回归译码器。**许多研究试图提高非自回归解码器的性能,并接近自回归解码器。为了使非自回归解码器的性能与自回归解码器一样好,必须使用许多技术。

2. encoder-decoder attention(编码器-解码器注意力)

编码器和解码器通过编码器-解码器注意力(encoder-decoder attention)传递信息
编码器-解码器注意力是连接编码器跟解码器之间的桥梁。
如下图所示:
encoder-decoder attention的key和value来自encoder的输出,查询来自decoder中前一个层的输出。
在这里插入图片描述
接下来开始学习encoder-decoder attention的运作过程
在这里插入图片描述
我们之前学习过self-attention,为了加深学习的影响,我们可以对比学习。
比较self-attention 与 cross-attention的区别
在这里插入图片描述
在这里插入图片描述
v 接下来会“丢”给fully connected network,这个步骤 q 来自于decoder,k 跟 v 来自于encoder,该步骤就叫做encoder-decoder attention。
所以decoder就是凭借着产生一个 q,去encoder这边抽取信息出来,然后将抽取出来信息当做接下来的decoder的fully connected network的输入。
在这里插入图片描述
假设产生了 “机” ,随后输入 < BOS > 、“机” ,产生一个向量。
这个向量一样乘上一个线性变换得到一个查询 q′。
q′ 会跟 k1、k2、k3 计算attention score。
接着用attention score跟 v1、v2、v3 做加权和,加起来得到 v′,最后交给全连接网络处理。
在这里插入图片描述

3. Transformer 的训练过程

Transformer 应该要学到听到“机器学习”的声音信号,它的输出就是“机器学习”这四个字。
把 < BOS > 丢给编码器的时候,其第一个输出应该要跟“机”越接近越好。
而decoder的输出是一个概率的分布,这个概率分布跟“机”的one-hot vector越接近越好。
因此我们会去计算标准答案(Ground Truth)跟分布之间的cross entropy(交叉熵),交叉熵的值越小越好。
每一次解码器在产生一个中文的时候做了一次分类的问题。
假设中文字有四千个,就是做有四千个类别的分类的问题。
实际训练的时候,输出应该是“机器学习”。解码器第一次的输出、第二次的输出、第三次的输出、第四次输出应该分别就是“机”、“器”、“学”、“习”
这四个中文的one-hot vector,输出跟这四个字的one-hot vector越接近越好。
在这里插入图片描述
在训练的时候,每一个输出跟其对应的正确答案都有一个cross entropy
每次输出的时候相当于做了四次分类,希望这些分类的问题得到的cross entropy的总和越小越好。
训练的时候,decoder输出的不止“机器学习”这四个字,还要输出 < EOS >。
所以解码器的最终第五个位置输出的向量跟 < EOS > 的独热向量的交叉熵越小越好。
我们把标准答案给解码器,希望decoder的输出跟正确答案越接近越好。
在训练的时候,我们要告诉decoder要输出什么,比如:
有< BOS >、“机” 的情况下,要告诉其输出 “器” ;
有 < BOS >、“机” 、“器” 的情况下,要告诉其输出 “学” ;
有 < BOS >、“机”、“器”、“学”的情况下,要告诉其输出 “习” ;
有 < BOS >、“机” 、“器”、“学”、“习” 的情况下,要告诉其输出 < EOS >。
在decoder训练的时候,在输入的时候告诉机器正确的答案,这称为教师强制(teacher forcing)
在这里插入图片描述

4. 序列到序列模型训练常用的Tips

Tips 1:Copy mechanism(复制机制)
对于许多任务,decoder不需要创造自己的输出,它可以从输入中复制一些东西。
以聊天机器人为例
用户对机器说:“你好,我是库洛洛”。
机器应该回答:“库洛洛你好,很高兴认识你”。
机器其实没有必要创造“库洛洛”这个词,因为“库洛洛”对机器来说可能是一个生僻的词,所以它可能很难在训练数据里面出现,甚至一次也没有出现过,因此机器不太可能正确地产生输出。
但是假设机器在学的时候,学到的并不是它要产生“库洛洛”,它学到的是看到输入的时候说“我是某某某”,就直接把“某某某”复制出来,说“某某某你好”。
如下图所示:
在这里插入图片描述
这种机器的训练会比较容易,显然比较有可能得到正确的结果,所以复制对于对话任务可能是一个需要的技术。
机器只要复述这一段它听不懂的话,它不需要从头去创造这一段文字,它要学的是从用户的输入去复制一些词当做输出。
如下图所示:
在这里插入图片描述
在做摘要时,我们需要用到Copy mechanism。
做摘要需要搜集大量的文章,每一篇文章都要有摘要,这样训练一个Seq2Seq的模型就结束了。
但是要训练机器产生合理通顺的句子,通常需要百万篇文章,这些文章都要有人为标注的摘要。
在做摘要的时候,很多的词汇就是直接从原来的文章里面复制出来的(类似于我们人总结一篇文章一样,不会自己创造太多词语,大多数是从文章中复制词语出来)
所以对摘要任务而言,从文章里面直接复制一些信息出来是一个很关键的能力

最早有从输入复制内容到输出能力的模型叫做指针网络(pointer network)
后来还有一个变形叫做复制网络(copy network)

在这里插入图片描述

Tips 2:Guided attention(引导注意力)
序列到序列模型在训练时有时会产生奇怪的结果。
以语音合成为例,如果一台机器说“发财”四次并重复四次,就没有问题,但如果它只说一次“发财”,它就会省略“发”,只说“0财”。
也许在训练数据中,这些非常短的句子很少见,所以机器无法处理它们。
类似于语音识别、语音合成这种任务最适合使用Guided attention(引导注意力)
Guided attention要求机器在做注意力的时候有固定的方式
对语音合成或语音识别,我们人认为的attention的顺序应该就是由左向右。
如下图所示:
红色曲线代表attention score,曲线越高,attention score越大
以语音合成为例,输入是一串文本,在合成声音时,它明显是从左向右的顺序发音的。机器应该首先通过查看最左侧的输入词汇表来产生声音,然后查看中间的词汇表来发出声音,最后查看右侧的词汇表以发出声音。
如果机器在进行语音合成时机器的attention是颠三倒四的,比如,它先看后面,然后看前面,然后随机看整个句子。这种attention显然是有问题的,所以不能产生好的结果。
Guided attention会迫使attention具有固定的形式。
如果我们对这个问题本身就已经有理解,知道对于语音合成这样的问题,注意力的位置都应该由左向右,不如就直接把这个限制放进训练里面,要求机器学到注意力就应该要由左向右。
在这里插入图片描述
Tips 3:Beam Search(束搜索)

束搜索(beam search) 方法是解决优化问题的一种启发式方法
它是在分枝定界方法基础上发展起来的,它使用启发式方法估计 k 个最好的路径
仅从这 k 个路径出发向下搜索,即每一层只有满意的结点会被保留,其它的结点则被永久抛弃
从而比分枝定界法能大大节省运行时间。

假设Decoder就只能产生两个字 A 和 B,即词表 V = {A, B}。
对decoder而言,每一次在第一个时间步(time step),它在 A、B 里面决定一个,比如decoder可能选 B 当作输入,再从 A、B 中选一个。
在之前的描述中,decoder每一次都是选分数最高的那一个。
假设 A 的分数是 0.6,B 的分数是 0.4,解码器的第一次就会输出 A。
接下来假设 B 的分数为 0.6,A 的分数为 0.4,解码器就会输出 B。
再假设把 B 当做输入,现在输入已经有 A、B,接下来 A 的分数是 0.4,B 的分数是 0.6,解码器就会选择输出 B。
因此输出就是 A 、B 、B。
这种每次找分数最高的词元来当做输出的方法称为贪心搜索(greedy search),其也被称为贪心解码(greedy decoding)。
红色路径就是通过贪心解码得到的路径。但贪心搜索不一定是最好的方法
绿色路径虽然第一步选了一个较差的输出,但是接下来的结果是好的,例如第一步可以先稍微舍弃一点东西,第一步虽然 B 是 0.4,但先选 B 第二步时 B 的可能性就大增就变成 0.9。到第三步时,B 的可能性也是0.9。
比较下红色路径与绿色路径,红色路径第一步好,但全部乘起来是比较差的,绿色路径一开始比较差,但最终结果其实是比较好的。
如何找到最好的结果是一个值得考虑的问题。
穷举搜索(exhaustive search) 是最容易想到的方法,但实际上并没有办法穷举所有可能的路径,因为每一个转折点的选择太多了。
对中文而言,中文有 4000 个字,所以树每一个地方的分叉都是 4000 个可能的路径,走两三步以后,就会无法穷举。
在这里插入图片描述
接下来介绍下束搜索(beam search):束搜索经常也称为集束搜索或柱搜索。
束搜索是用比较优的方法求一个近似解

但在某些情况下效果不好。
比如论文“The Curious Case Of Neural Text Degeneration”。这个任务要做的事情是完成句子(sentence completion),也就是机器先读一段句子,接下来它要把这个句子的后半段完成。
如果用束搜索,会发现说机器不断讲重复的话。
如果不用束搜索,加一些随机性,虽然结果不一定完全好,但是看起来至少是比较正常的句子。
有时候对decoder来说,没有找出分数最高的路,反而结果是比较好的,这个就是要看任务本身的特性。
总而言之:
假设任务的答案非常明确识别的结果就只有一个可能。对这种任务而言,通常束搜索就会帮助比较大。
但如果任务需要机器发挥一点创造力,束搜索的效果就比较差

Tips 4:Add noise(加入噪声)
在做语音合成的时候,decoder加噪声,在我们的直觉上这是完全违背正常的机器学习的做法。
但是在训练的时候会加噪声,让机器看过更多不同的可能性,这会让模型比较鲁棒,比较能够对抗它在测试的时候没有看过的状况。

我们有个疑问:在测试的时候居然还要加一些噪声,这不是把测试的状况弄得更困难,结果更差吗?
但语音合成神奇的地方是,模型训练好以后。测试的时候要加入一些噪声,合出来的声音才会好。
用正常的解码的方法产生出来的声音听不太出来是人声,产生出比较好的声音是需要一些随机性的。
对于语音合成或句子完成任务,decoder找出最好的结果不一定是人类觉得最好的结果,反而是奇怪的结果,加入一些随机性的结果反而会是比较好的。

Tips 5:Training with reinforcement learning(使用强化学习训练)

强化学习是人工智能领域中的一种学习方式,其核心思想是通过一系列的试错过程,让智能体逐步学习如何在一个复杂的环境中进行最优的决策。
这种学习方式的特点在于,智能体需要通过与环境的交互来获取奖励信号,从而逐步调整自己的行动策略,以期在长期的时间尺度下获得最大的总奖励。
与其他的机器学习算法相比,强化学习最大的特点在于其能够处理连续的、实时的、具有不确定性的环境,因此在许多实际的应用场景中具有很高的实用价值。
例如,在机器人控制、游戏策略、自然语言处理等领域中,强化学习已经取得了一系列的重要成果,成为了人工智能领域中不可或缺的一部分。

在机器翻译中我们评估的标准用的是 BLEU(BiLingual Evaluation Under study,双语替换测评)分数。

双语互译质量评估辅助工具(双语替换测评)。它是用来评估机器翻译质量的工具。BLEU的设计思想:机器翻译结果越接近专业人工翻译结果越好。BLEU算法实际上用于确定两个句子之间的相似性。了解翻译前后句子含义是否一致的直接方法是比较该句子的标准人工翻译和机器翻译的结果。如果它们非常相似,这意味着我的翻译是成功的。

虽然 BLEU 最先是用于评估机器翻译的结果,但现在它已经被广泛用于评价许多应用输出序列的质量
Decoder先产生一个完整的句子,再去跟正确的答案一整句做比较,拿两个句子之间做比较算出 BLEU 分数。但训练的时候,每一个词汇是分开考虑的,最小化的是交叉熵,最小化交叉熵不一定可以最大 BLEU 分数。
在做验证的时候,并不是挑交叉熵最低的模型,而是挑 BLEU 分数最高的模型。

在了解了 BLEU 后,那么我们有一种猜想:训练的损失设置成 BLEU 分数乘一个负号,最小化损失等价于最大化 BLEU 分数。

但 BLEU 分数很复杂,如果要计算两个句子之间的 BLEU 分数,损失根本无法做微分。
我们之所以采用交叉熵,而且是每一个中文的字分开来算,就是因为这样才有办法处理。

遇到优化无法解决的问题,可以用强化学习训练
遇到无法优化的损失函数,把损失函数当成强化学习的奖励,把Decoder当成智能体

Tips 5:Plan sampling(计划采样)
测试的时候,decoder看到的是自己的输出,因此它会看到一些错误的内容。
但是在训练的时候,decoder看到的是完全正确的内容。
这种不一致的现象叫做曝光偏差(exposure bias)

假设解码器在训练的时候永远只看过正确的东西,在测试的时候,只要有一个错,就会一步错步步错。因为解码器从来没有看过错的东西,它看到错的东西会奇怪,所以接下来它产生的结果可能都会出错。

有一个解决方案就是——给解码器的输入加一些错误的东西
不要总是给的都是正确的答案,偶尔给它一些错的答案,它反而会学得更好
这一技巧称为计划采样(scheduled sampling)

其实计划采样很早就有了,在还没有 Transformer、只有 LSTM 的时候,就已存在了。

但是计划采样会伤害到 Transformer 的平行化的能力,所以 Transformer 的计划采样另有招数
其跟原来最早提在这个 LSTM 上被提出来的招数也不太一样(这个后续再继续学习)
在这里插入图片描述

二、Pytorch学习

1. 完整的模型训练套路

经过前面的学习,我们把Pytorch的基础部分已经学的七七八八了,所以接下来我们需要融会贯通,结合前面的知识,然后加上一些训练时常用的方法,对模型进行训练。
接下来,我们将会以CIFAR-10数据集作为我们的数据来源,对模型进行训练

第一步:准备CIFAR-10数据集
代码如下:

import torchvision

# 准备数据集
train_data = torchvision.datasets.CIFAR10(root='./datasets', train=True, download=True,
                                          transform=torchvision.transforms.ToTensor())

test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, download=True,
                                         transform=torchvision.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))

可以看到,我们的数据集长度输出成功,证明我们的数据集获取成功。
其中训练集为50000张图片,测试集为10000张图片
在这里插入图片描述
第二步:使用DataLoader加载数据集

# 第二步:利用dataloader 加载训练数据集
# 加载训练集
train_dataloader = torch.utils.data.DataLoader(train_data, batch_size=64)
# 加载测试集
test_dataloader = torch.utils.data.DataLoader(test_data, batch_size=64)

第三步:搭建神经网络
创建一个model.py的python文件(到时候import即可)在这里插入图片描述
在这里插入图片描述
按照上图,搭建一个CIFAR-10的模型

代码如下:

import torch
from torch import nn


# 第三步:搭建神经网络
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, 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
        

if __name__ == '__main__':
    Net = Net()
    # torch.ones([]) 是 PyTorch 中用于生成标量张量的函数之一。
    # 它会创建一个包含一个元素且值为1的标量张量,通常用于表示单个数值,比如损失函数的值或者模型的参数初始化值。
    input = torch.ones((64, 3, 32, 32))
    output = Net(input)
    print(output.shape)

第五步:引入模型,加入损失函数和优化器,设置训练网络的一些参数(训练的轮数、记录测试的次数)
代码如下:

import torch
import torchvision
from p23_model import *

# 第一步:准备数据集
# 训练集
train_data = torchvision.datasets.CIFAR10(root='./datasets', train=True, download=True,
                                          transform=torchvision.transforms.ToTensor())
# 测试集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, download=True,
                                         transform=torchvision.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_size=64)
# 加载测试集
test_dataloader = torch.utils.data.DataLoader(test_data, batch_size=64)

# 加载模型
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(), lr=learning_rate)

# 设置训练网络的一些参数
# 训练的轮数
total_train_step = 0
# 记录测试的次数
total_test_step = 0

# 训练的轮数
epoch = 10

for i in range(epoch):
    print('------第 {}轮训练开始-------'.format(i+1))
    # 训练步骤开始
    for data in train_dataloader:
        images, labels = data
        optimizer.zero_grad()
        outputs = MCifar(images)
        loss = loss_func(outputs, labels)
        loss.backward()
        optimizer.step()
        # 训练完一次,数训练的次数+1
        total_train_step += 1
        print('训练次数{},Loss:{}'.format(total_train_step,loss.item()))
        

可以看到训练的损失函数,其loss越来越小了。
在这里插入图片描述
第六步:加入测试模块

 # 测试步骤开始
    total_test_loss = 0
    # 让梯度为0,防止在计算loss时造成影响
    with torch.no_grad():
        for data in test_dataloader:
            images, labels = data
            outputs = MCifar(images)
            loss = loss_func(outputs, labels)
            total_test_step += 1
            total_test_loss = total_test_loss + loss
    # 输出整体的loss
    print('整体的loss为{}'.format(total_test_loss))

结合上述代码,运行结果如下:
在这里插入图片描述
可以看得到,我们的每一轮的训练次数高达782次(782*64 = 50048,batch_size = 64,训练集共50000张图片)
但是,我们发现一个问题,我们的训练次数的loss输出过于密集,导致我们遗漏掉了test_loss的信息,所以我们需要对训练loss输出做一下改良,让其训练100次输出一次loss

        if total_train_step % 100 == 0:
            print('训练次数{},Loss:{}'.format(total_train_step, loss.item()))

train.py的整体代码如下:

import torch
import torchvision
from p23_model import *

# 第一步:准备数据集
# 训练集
train_data = torchvision.datasets.CIFAR10(root='./datasets', train=True, download=True,
                                          transform=torchvision.transforms.ToTensor())
# 测试集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, download=True,
                                         transform=torchvision.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_size=64)
# 加载测试集
test_dataloader = torch.utils.data.DataLoader(test_data, batch_size=64)

# 加载模型
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(), lr=learning_rate)

# 设置训练网络的一些参数
# 训练的轮数
total_train_step = 0
# 记录测试的次数
total_test_step = 0

# 训练的轮数
epoch = 10

for i in range(epoch):
    print('------第 {}轮训练开始-------'.format(i + 1))
    # 训练步骤开始
    for data in train_dataloader:
        images, labels = data
        optimizer.zero_grad()
        outputs = MCifar(images)
        loss = loss_func(outputs, labels)
        loss.backward()
        optimizer.step()
        # 训练完一次,数训练的次数+1
        total_train_step += 1
        if total_train_step % 100 == 0:
            print('训练次数{},Loss:{}'.format(total_train_step, loss.item()))

    # 测试步骤开始
    total_test_loss = 0
    with torch.no_grad():
        for data in test_dataloader:
            images, labels = data
            outputs = MCifar(images)
            loss = loss_func(outputs, labels)
            total_test_step += 1
            total_test_loss = total_test_loss + loss
    print('整体的loss为{}'.format(total_test_loss))

运行结果如下:
可以看到,我们的输出清晰了很多,更便于我们观察。
在这里插入图片描述
第七步:加入tensorboard

import torch
import torchvision
from torch.utils.tensorboard import SummaryWriter

from p23_model import *

# 第一步:准备数据集
# 训练集
train_data = torchvision.datasets.CIFAR10(root='./datasets', train=True, download=True,
                                          transform=torchvision.transforms.ToTensor())
# 测试集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, download=True,
                                         transform=torchvision.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_size=64)
# 加载测试集
test_dataloader = torch.utils.data.DataLoader(test_data, batch_size=64)

# 加载模型
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(), lr=learning_rate)

# 设置训练网络的一些参数
# 训练的轮数
total_train_step = 0
# 记录测试的次数
total_test_step = 0

# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("./logs_train")


            # 将loss的数值加入到tensorboard种
            writer.add_scalar('train_loss', loss.item(), total_train_step)


    # 添加测试集的loss到tensorboard中
    writer.add_scalar('test_loss', total_test_loss, total_test_step)

# 关闭tensorboard
writer.close()

运行结果如下:
在这里插入图片描述
第八步:保存模型

    # 保存模型
    # 每训练一轮保存一次
    torch.save(MCifar, "Mcifar_{}.pth".format(i))
    print('模型已保存')

可以看到,训练一轮,模型都保存一次。
在这里插入图片描述
至此,一个模型的搭建、训练、测试就基本上完成了。
整体代码如下:

import torch
import torchvision
from torch.utils.tensorboard import SummaryWriter

from p23_model import *

# 第一步:准备数据集
# 训练集
train_data = torchvision.datasets.CIFAR10(root='./datasets', train=True, download=True,
                                          transform=torchvision.transforms.ToTensor())
# 测试集
test_data = torchvision.datasets.CIFAR10(root='./datasets', train=False, download=True,
                                         transform=torchvision.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_size=64)
# 加载测试集
test_dataloader = torch.utils.data.DataLoader(test_data, batch_size=64)

# 加载模型
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(), lr=learning_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 = data
        optimizer.zero_grad()
        outputs = MCifar(images)
        loss = loss_func(outputs, labels)
        loss.backward()
        optimizer.step()
        # 训练完一次,数训练的次数+1
        total_train_step += 1
        if 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
    with torch.no_grad():
        for data in test_dataloader:
            images, labels = data
            outputs = MCifar(images)
            loss = loss_func(outputs, labels)
            total_test_step += 1
            total_test_loss = total_test_loss + loss
    print('整体的loss为{}'.format(total_test_loss))
    # 将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()

三、数学拓展

1. 梯度(gradient)

本周我们就来研究一下梯度(gradient)到底是个什么东西?
要学习梯度,首先我们需要先了解 方向导数
再说方向导数之前,我们需要先复习一下偏导数
在这里插入图片描述
但是这个点肯定不止往x y这两个方向上移动那么简单,它可以往随意的方向进行移动
这就引申出了方向导数
在此,我们还需要复习一个与方向相关的知识点——方向余弦
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述

总结

本周的进度还算可以,希望下一周再接再厉。
在机器学习的理论部分中,这一周主要对上一周的Transformer学习进行了完善,上一周学习的是encoder(编码器),这一周的主要内容就是decoder。在decoder的学习的内容中又主要包括了autoregressive decoder(自回归解码器)与non-autoregressive decoder(非自回归解码器)。其中autoregressive decoder跟是根据参考之前的全部的输出作为输入,再一个进行结果的输出的,这种输出方式会存在一个问题就是一步错步步错,例如,如果解码器出现语音识别错误,一步错误会导致一步一步的错误,后面可能无法生成正确的词汇。然后,还引申出了masked self-attention(掩蔽自注意力),掩蔽自注意力可以通过一个掩码(mask)来阻止每个位置选择其后面的输入信息。使用这个技术是因为解码器的输出是一个一个产生的,所以只能考虑其左边的东西,没有办法考虑其右边的东西。在non-autoregressive decoder中,假设生成的句子是中文的,非自回归不会一次生成一个单词,而是一次生成整个句子。要让解码器停止运作,需要特别准备一个特别的符号 < EOS >。由于输出长度未知,用作非自回归解码器的< BOS >输入数量也未知。因此,有两种方法:1、用分类器来解决这个问题。分类器中的输入是编码器(encoder)的输入,输出是一个数字,该数字代表解码器应该要输出的长度。2、给编码器一堆 < BOS > 的词元,输出 < EOS > 右边的的输出就当它没有输出。non-autoregressive decoder优点是支持并行化、能够控制输出的长度。但非自回归译码器的性能往往不如自回归译码器。此外,还了解了Transformer 的训练过程,实际上就是分类问题,就是计算cross entropy之和,使用teacher forcing技术完成训练。最后还学会了序列到序列模型训练常用的Tips,例如,Copy mechanism(复制机制)、Guided attention(引导注意力)、Beam Search(束搜索)、Add noise(加入噪声)、 reinforcement learning(强化学习)、Plan sampling(计划采样)等在Pytorch代码实战中,这一周对之前的内容融会贯通,做了一个CIFAR-10的完整训练模型,学会了一些在训练中的套路。在数学理论学习部分,这一周对梯度进行了学习。因为一开始接触的时候,对梯度的概念完全是懵懵懂懂的状态,所以决定把这个概念搞清楚。
下一周计划计划继续对机器学习理论部分进行按部就班的学习,然后尽量完成Pytorch代码实战的基础内容,数学理论部分,计划研究Transformer或者概率论知识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2177129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

24年最新 idea 插件开发教程,面试鸭插件技术实现!

大家好&#xff0c;我是松柏。今天给大家分享下这个JetBrains插件开发教程。 学习过程中可以参考开源项目&#xff1a;https://github.com/yuyuanweb/mianshiya-plugin 首先贴一下官方文档&#xff1a;https://plugins.jetbrains.com/docs/intellij/welcome.html 虽然这个文档…

【最新发布】Win10 22H2 19045.4957 正式版免费下载!

今日系统之家小编给大家分享2024年9月第二次更新的Windows10 22H2正式版系统&#xff0c;该版本系统基于最新Windows10 22H2 19045.4957 64位专业版进行离线制作&#xff0c;安全无毒&#xff0c;修复了使用某些环绕声技术时某些媒体的播放可能会停止等多项问题&#xff0c;系统…

基于C#开发的(CS界面)图书管理系统

图书管理系统项目开发说明书 项目简介 项目背景&#xff08;选择这个项目的原因、前景&#xff0c;面向的用户&#xff0c;优势&#xff09;&#xff1b; 根据温州理工学院需要希望能够充分利用现代科技来提高图书管理的效率&#xff0c;在原有的办公系统基础上进行扩展&…

1. 如何在服务器上租GPU跑实验 (以AutoDL为例) - 深度学习·科研实践·从0到1

目录 前言 1. 在AutoDL上注册账号 2. 在算力市场选择GPU 3. 创建实例 4. 控制台-容器实例界面&#xff08;核心&#xff09; 4.1 无卡模式&#xff08;常用&#xff09; 5. 帮助文档 前言 好记性不如烂笔头&#xff0c;本专栏将详细记录下本人学习深度学习工程实践&…

Python通过Sqlalchemy框架实现增删改查

目录 简介 什么是SQLAlchemy&#xff1f; SQLAlchemy可以分为两个部分&#xff1a;Core和ORM。 一、首先安装sqlalchemy 二、在配置文件中添加数据库连接信息&#xff0c;我这里是Mysql 三、 创建数据库连接类&#xff0c;我这里是动态读取数据库的表字段&#xff0c;自动…

神器!GPT让大学生也能轻松实现架构师级的系统设计图

文章目录 零、前言一、实现架构师级的系统设计图操作指导系统背景功能细化 画用例图画系统架构设计图划分html页面画实体类图画服务层类图画时序图画数据库ER图 二、感受 零、前言 粉丝做毕业设计时&#xff0c;不会画架构图&#xff0c;问虚竹哥会不会画&#xff5e; 虽然这…

基于微信小程序的空巢老人健康管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

STM32引脚PB3、PB4、PA15作为输入输出的特殊配置

一、问题描述 简单描述&#xff1a; 最近做的一个项目中&#xff0c;PB3端口配置为输入&#xff0c;不管外部输入是高电平还是低电平&#xff0c;一直读取到的是低电平。 调试过程&#xff1a;在撰写代码过程中&#xff0c;又发现新的问题&#xff0c;Enter按键无法控制屏幕数…

电脑显示缺失msvcp140_1.dll怎样修复,5种快速修复方法让你快速修复

1. msvcp140_1.dll 定义 1.1 Microsoft Visual C 2015 Redistributable组件 msvcp140_1.dll 是 Microsoft Visual C 2015 Redistributable 的关键组件之一&#xff0c;扮演着至关重要的角色。以下是对 msvcp140_1.dll 的详细分析&#xff1a; 组件功能&#xff1a;msvcp140_…

《中国电子报》报道: 安宝特AR为产线作业者的“秘密武器

近日&#xff0c;中国电子报在其文章《下一代工业智能终端重新定义制造业》中对安宝特的增强现实&#xff08;AR&#xff09;解决方案给予了高度评价&#xff0c;称其为产线作业者的“秘密武器”。这一创新技术改变了传统制造业的作业方式&#xff0c;使得操作人员能够在生产过…

Ubuntu中交叉编译armadillo库

网上关于交叉编译armadillo库比较少&#xff0c;借鉴了一些但是在前几天编译时总是磕磕绊绊&#xff0c;于是写一个详细的编译过程。 交叉编译armadillo库包含两个步骤&#xff1a;交叉编译依赖库和交叉编译armadillo。armadillo官网介绍依赖库如下图所示&#xff1a; 需要注意…

【高性能内存池】page cache 5

page cache 1 page cache的框架2 central cache从page cache申请n页span的过程3 page cache 的结构3.1 page cache类框架3.2 central cache向page cache申请span3.3 获取k页的span page cache的结构和central cache是一样的&#xff0c;都是哈希桶的结构&#xff0c;并且挂载的…

JAVA全球互联同城速达国际版同城跑腿快递代取帮买帮送一体化服务系统源码

全球互联&#xff0c;便捷生活新篇章&#xff01; &#x1f31f; 开篇&#xff1a;跨越国界的即时服务革命 在这个快节奏的时代&#xff0c;你是否也曾为忙碌的生活而烦恼&#xff1f;购物、取件、送物……这些日常琐事似乎总在不经意间占据了我们宝贵的时间。但现在&#xf…

003集—— CAD批量划线和text文字(CAD—C#二次开发入门)

本例通过for循环创建255条线&#xff0c;颜色不同&#xff0c;并在线的右端点处注记文字。 效果如下: 本文有个事务的封装函数&#xff0c;如下&#xff1a; private ObjectId AppendEntity(Entity entity) { ObjectId objectId; Database db HostApplication…

❤Node实现接口增删改查(文章为例)

❤Node实现接口增删改查&#xff08;文章为例&#xff09; 1、文章表的创建​ 接下来我们新建一个文章数据表article&#xff0c;实现对于文章部分的管理功能接口 根据文章我们创建一个对应的 SQL 数据表 javascript CREATE TABLE articles (id INT AUTO_INCREMENT PRIMAR…

亚马逊卖家如何利用自养号测评策略低成本提升销量?

在跨境电商的平台上&#xff0c;随着市场逐渐成熟与竞争的白热化&#xff0c;众多卖家正面临流量增长乏力与转化率提升困难的双重挑战。为了在这日益激烈的竞争环境中脱颖而出&#xff0c;卖家们纷纷加大投入&#xff0c;探索多样化的推广策略&#xff0c;但往往因策略不够精准…

探索Kombo:AI与API的完美结合

文章目录 探索Kombo&#xff1a;AI与API的完美结合背景介绍库的定义安装指南简单函数使用场景应用常见问题及解决方案总结 探索Kombo&#xff1a;AI与API的完美结合 背景介绍 在当今快速发展的人工智能领域&#xff0c;Kombo库以其独特的优势脱颖而出。Kombo是一个专注于AI的…

2024年7月大众点评全国爱车前百名城市分析

在做一些城市分析、学术研究分析、商业选址、商业布局分析等数据分析挖掘时&#xff0c;大众点评的数据参考价值非常大&#xff0c;截至2024年7月&#xff0c;大众点评美食店铺剔除了暂停营业、停止营业后的最新数据情况分析如下。 分析研究的字段维度包括大众点评数字id、字母…

微信商城小程序怎么弄_重塑购物体验

在数字化浪潮的推动下&#xff0c;微信商城小程序正逐步成为消费者购物的新宠儿。它不仅打破了传统电商的界限&#xff0c;更以其便捷性、高效性和个性化服务&#xff0c;重新定义了购物体验。今天&#xff0c;让我们一同探索微信商城小程序如何以独特魅力&#xff0c;引领未来…