Pytorch官方实验的食用方法
pytorch官网上关于NLP的实验有两类,https://pytorch.org/tutorials/index.html#text和https://pytorch.org/tutorials/beginner/deep_learning_nlp_tutorial.html,两份教程的风格比较类似,倾向于快速原型,不需要下载,预处理数据集,而是通过几个简单的样例介绍网络的搭建和训练。
但是我认为,如果因为它们简单易上手,就直接复制黏贴看结果,会一无所获,正确的食用方法是,看过一遍实验原理之后自己着手构建网络,训练模型,查看结果。
如果这样的话,就会发现有很多实作中的疑点隐藏在code背后,接下来几篇文章的目的就是在讲述实验的过程中解答这些疑点。
这篇博客是对https://pytorch.org/tutorials/beginner/nlp/deep_learning_tutorial.html#sphx-glr-beginner-nlp-deep-learning-tutorial-py中一些问题的解惑。
词袋分类器
词袋分类
该实验是上文链接Ⅱ中第一个网络实验,其目的是学习一个bag of word->language的映射。
例如:
data = [("me gusta comer en la cafeteria".split(), "SPANISH"),
("Give it to me".split(), "ENGLISH"),
("No creo que sea una buena idea".split(), "SPANISH"),
("No it is not a good idea to get lost at sea".split(), "ENGLISH")]
词袋就是一个固定长度(单词表大小)的数组B,代表第i个单词在句中的出现频度,例如,有单词表{hello:0,world:1,iSika:2},则句子<hello world>的词袋表示就是[1,1,0],显然,这种表示方法忽视词与词之间的顺序关系,仅仅保留词频。
该实验似乎是没有意义的,想要判断语言为什么不直接抽出来一个单词查表呢?的确,这个实验是没有意义的,但是真正有意义的是词袋分类器这个模型,该模型不仅可以用于分类语言,也可以用于做各种各样的文本分类,例如垃圾邮件检测,情感检测等。
网络模型
该模型是单层linear+softmax结构,是一个标准的分类模型。
class bowcls(nn.Module):
def __init__(self,V,N):
super(bowcls,self).__init__()
#在init函数中取得类时,自动取得参数
self.linear=nn.Linear(V,N)
def forward(self,bow):
#
return nn.functional.log_softmax(self.linear(bow),dim=1)
在搭建网络中,有这么几个问题:
__init__里写什么,forward里写什么?
init里写带参层,forward里写不带参层,一旦在init里定义带参层,model会自动记录参数,当然,声明softmax,dropout等不带参层也是可以的,但是没必要,一般在forward里直接调用。
init中网络size如何确定?
单个样例原则,以单个样例被投入该层时的输入size和输出size设置。
例如,在上文单层网络中,最小单位是一个词袋,即一个长度=vocal的向量,得到一个分类score,即长度=N的向量,所以linear层的参数设置为(V,N)。
forward中input size如何确定?
单次训练原则,以单次训练所使用的输入size和输出size设置,因为在train的过程中,模型的一次调用实际上是forward函数的一次执行。
这个原则在本网络中体现的不明显,因为不需要手动设置size。
训练
在训练之前需要确定损失函数和优化器。
本实验使用log(softmax(x))和NLLLOSS(负对数似然函数),功能等价于softmax+cross_entropy损失函数。
本实验使用SGD优化器。
model=bowcls(VOCAB_SIZE,NUM_LABELS)
loss_function=nn.NLLLoss()
optimizer=opt.SGD(model.parameters(),lr=0.1)
for epoch in range(100):
for instance,label in data:
bow_vec=make_bow_vector(instance,word_to_ix)
target=make_target(label,label_to_ix)
model.zero_grad()
log_p=model(bow_vec)
loss=loss_function(log_p,target)
loss.backward()
optimizer.step()
一个epoch内的训练可以分为5步
- 准备训练数据
- 如果不是以batch的形式(本次所有实验都不是),则遍历单个样例,预处理样例(tensor转换等)
- 也可以以batch的形式并行训练
- (大多数情况下)需要清空累计的梯度
- 将样例投入网络
- 根据最后一层的输出计算loss,借助于pytorch强大的计算图机制,将梯度backforward到所有可更新的参数
- 使用optimizer更新参数