pytorch transformers finetune 疑惑总结
optimizer = AdamW(model.parameters(), lr=LR, correct_bias=False)
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=WARMUP_STEPS, num_training_steps=t_total)
for epoch in range(EPOCHS):
loss.backward()
optimizer.step()
scheduler.step()
当保存和加载模型时,需要熟悉三个核心功能: torch.save:将序列化对象保存到磁盘。此函数使用Python的pickle模块进行序列化。使用此函数可以保存如模型、tensor、字典等各种对象。 torch.load:使用pickle的unpickling功能将pickle对象文件反序列化到内存。此功能还可以有助于设备加载数据。 torch.nn.Module.load_state_dict:使用反序列化函数 state_dict 来加载模型的参数字典。 1.什么是状态字典:state_dict? 在PyTorch中,torch.nn.Module模型的可学习参数(即权重和偏差)包含在模型的参数中,(使用model.parameters()可以进行访问)。 state_dict是Python字典对象,它将每一层映射到其参数张量。注意,只有具有可学习参数的层(如卷积层,线性层等)的模型 才具有state_dict这一项。目标优化torch.optim也有state_dict属性,它包含有关优化器的状态信息,以及使用的超参数。
当保存成 Checkpoint 的时候,可用于推理或者是继续训练,保存的不仅仅是模型的 state_dict 。保存优化器的 state_dict 也很重要, 因为它包含作为模型训练更新的缓冲区和参数。你也许想保存其他项目,比如最新记录的训练损失,外部的torch.nn.Embedding
层等等。
要保存多个组件,请在字典中组织它们并使用torch.save()
来序列化字典。PyTorch 中常见的保存checkpoint 是使用 .tar 文件扩展名。
要加载项目,首先需要初始化模型和优化器,然后使用torch.load()
来加载本地字典。这里,你可以非常容易的通过简单查询字典来访问你所保存的项目。
请记住在运行推理之前,务必调用model.eval()
去设置 dropout 和 batch normalization 为评估。如果不这样做,有可能得到不一致的推断结果。 如果你想要恢复训练,请调用model.train()
以确保这些层处于训练模式。
input_ids_method1 = torch.tensor(tokenizer.encode(sentence, add_special_tokens=True))
- 从例子中可以看出,encode方法可以一步到位地生成对应模型的输入。
- 相比之下,tokenize只是用于分词,可以分成WordPiece的类型,并且在分词之后还要手动使用convert_tokens_to_ids方法,比较麻烦
Puts each data field into a tensor with outer dimension batch size 即用于对单个样本生成batch的函数,如果没有特殊需求其实不用自己写collate_fn方法,有默认的default_collate方法。
如果模型中有BN层(Batch Normalization)和Dropout,需要在训练时添加model.train(),在测试时添加model.eval()。其中model.train()是保证BN层用每一批数据的均值和方差,而model.eval()是保证BN用全部训练数据的均值和方差;而对于Dropout,model.train()是随机取一部分网络连接来训练更新参数,而model.eval()是利用到了所有网络连接。
因为验证集的时候,我们只是想看一下训练的效果,并不是想通过验证集来更新网络时,就可以使用with torch.no_grad()。最终,torch.save就是保存的训练集的训练模型。
pytorch 的数据加载到模型的操作顺序是这样的: ① 创建一个 Dataset 对象
为面部数据集创建一个数据集类。我们将在__init__
中读取csv的文件内容,在__getitem__
中读取图片。这么做是为了节省内存 空间。只有在需要用到图片的时候才读取它而不是一开始就把图片全部存进内存里。 ② 创建一个 DataLoader 对象 ③ 循环这个 DataLoader 对象,将img, label加载到模型中进行训练
test_loader 没有打乱数据。train_loader有打乱数据
model.parameters()与model.state_dict()是Pytorch中用于查看网络参数的方法。一般来说,前者多见于优化器的初始化,后者多见于模型的保存
总结来说:梯度累加就是,每次获取1个batch的数据,计算1次梯度,梯度不清空,不断累加,累加一定次数后,根据累加的梯度更新网络参数,然后清空梯度,进行下一次循环。一定条件下,batchsize越大训练效果越好,梯度累加则实现了batchsize的变相扩大,如果accumulation_steps为8,则batchsize '变相' 扩大了8倍,是我们这种乞丐实验室解决显存受限的一个不错的trick,使用时需要注意,学习率也要适当放大。 算一个batch计算一次梯度,然后进行一次梯度更新。这里梯度值就是对应偏导数的计算结果。显然,我们进行下一次batch梯度计算的时候,前一个batch的梯度计算结果,没有保留的必要了。所以在下一次梯度更新的时候,先使用optimizer.zero_grad把梯度信息设置为0。
Bert模型类,继承torch.nn.Module,实例化对象时使用from_pretrained()函数初始化模型权重,参数config用于配置模型参数 模型输入是: input_ids,token_type_ids(可选),attention_mask(可选),position_ids(可选), head_mask(可选):0表示head无效,1表示head有效。 inputs_embeds (可选)如果不使用input_ids,可以直接输入token的embedding表示。 encoder_hidden_states(可选):encoder最后一层的隐含状态序列,模型配置为decoder时,需要此输入。 encoder_attention_mask(可选):encoder最后一层隐含状态序列是否参与attention计算,防止padding部分参与,模型配置为decoder时,需要此输入. 返回类型tuple(torch.FloatTensor): last_hidden_state:模型最后一层输出的隐含层状态序列 pooler_output :最后一层隐含层状态序列经过一层全连接和Tanh激活后,第一个toekn对应位置的输出。
last_hidden_state:模型最后一层输出的隐含层状态序列
pooler_output :最后一层隐含层状态序列经过一层全连接和Tanh激活后,第一个toekn对应位置的输出。
hidden_states(可选,当output_hidden_states=True或者config.output_hidden_states=True):每一层和初始embedding层输出的隐含状态
attentions(可选,当output_attentions=True或者config.output_attentions=True):attention softmax后的attention权重,用于在自注意力头中计算权重平均值。
BertForSequenceClassification 这个类用于句子分类或回归任务,继承torch.nn.Module,实例化依然使用from_pretrained+ config配置。 输入相比BertModel多了一个label 输出主要是loss,logits(softmax之前的分类分数)等tuple(torch.FloatTensor)
pretrained_model_name_or_path :这个参数可以是一个需要下载或缓存中的与训练模型的快捷名称,如果本地的缓存中没有这个模型,就自己去官网下载,缓存到本地.cache中,速度较慢;也可以是使用save_pretrained()保存的模型权重字典的路径
PyTorch的核心是两个主要特征:
- 一个n维张量,类似于numpy,但可以在GPU上运行
- 搭建和训练神经网络时的自动微分/求导机
PyTorch自动求导看起来非常像TensorFlow:这两个框架中,我们都定义计算图,使用自动微分来计算梯度。两者最大的不同就是TensorFlow的计算图是静态的,而PyTorch使用动态的计算图。
在TensorFlow中,我们定义计算图一次,然后重复执行这个相同的图,可能会提供不同的输入数据。而在PyTorch中,每一个前向通道定义一个新的计算图。
静态图的好处在于你可以预先对图进行优化。例如,一个框架可能要融合一些图的运算来提升效率,或者产生一个策略来将图分布到多个GPU或机器上。如果重复使用相同的图,那么在重复运行同一个图时,,前期潜在的代价高昂的预先优化的消耗就会被分摊开。
静态图和动态图的一个区别是控制流。对于一些模型,我们希望对每个数据点执行不同的计算。例如,一个递归神经网络可能对于每个数据点执行不同的时间步数,这个展开(unrolling)可以作为一个循环来实现。对于一个静态图,循环结构要作为图的一部分。因此,TensorFlow提供了运算符(例如tf.scan
)来把循环嵌入到图当中。对于动态图来说,情况更加简单:既然我们为每个例子即时创建图,我们可以使用普通的命令式控制流来为每个输入执行不同的计算。