Pytorch官方实验的食用方法

pytorch官网上关于NLP的实验有两类,https://pytorch.org/tutorials/index.html#texthttps://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,NLPIR 下载 nlp官网_损失函数代表第i个单词在句中的出现频度,例如,有单词表{hello:0,world:1,iSika:2},则句子<hello world>的词袋表示就是[1,1,0],显然,这种表示方法忽视词与词之间的顺序关系,仅仅保留词频。

该实验似乎是没有意义的,想要判断语言为什么不直接抽出来一个单词查表呢?的确,这个实验是没有意义的,但是真正有意义的是词袋分类器这个模型,该模型不仅可以用于分类语言,也可以用于做各种各样的文本分类,例如垃圾邮件检测,情感检测等。

网络模型

NLPIR 下载 nlp官网_NLPIR 下载_02


该模型是单层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步

  1. 准备训练数据
  1. 如果不是以batch的形式(本次所有实验都不是),则遍历单个样例,预处理样例(tensor转换等)
  2. 也可以以batch的形式并行训练
  1. (大多数情况下)需要清空累计的梯度
  2. 将样例投入网络
  3. 根据最后一层的输出计算loss,借助于pytorch强大的计算图机制,将梯度backforward到所有可更新的参数
  4. 使用optimizer更新参数