zoukankan      html  css  js  c++  java
  • Pytorch-情感分类实战(基于LSTM,调用torchtext)

    提前安装torchtext和scapy,运行下面语句(压缩包地址链接:https://pan.baidu.com/s/1_syic9B-SXKQvkvHlEf78w 提取码:ahh3):

    pip install torchtext

    pip install scapy

    pip install 你的地址en_core_web_md-2.2.5.tar.gz  

    • en_core_web_md安装成功了,但spyder还是没法调用,把F:AnacondaLibsite-packages这个目录下的en_core_web_md复制一份放到当前.py文件相同目录下。
    • 在torchtext中使用spacy时,由于field的默认属性是tokenizer_language='en',当使用en_core_web_md时要改field.py文件中创建的field属性为tokenizer_language='en_core_web_md',且data.Field()中的参数也要改为tokenizer_language='en_core_web_md'。

    1.加载数据

     1 import numpy as np
     2 import torch
     3 from torch import nn, optim
     4 from torchtext import data, datasets
     5 
     6 #为CPU设置随机种子
     7 torch.manual_seed(123)
     8 
     9 #两个Field对象定义字段的处理方法(文本字段、标签字段)
    10 TEXT = data.Field(tokenize='spacy', tokenizer_language='en_core_web_md')
    11 LABEL = data.LabelField(dtype=torch.float)
    12 
    13 #IMDB共50000影评,包含正面和负面两个类别。数据被前面的Field处理
    14 train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)  
    15 
    16 print('len of train data:', len(train_data))        #25000
    17 print('len of test data:', len(test_data))          #25000
    18 
    19 print(train_data.examples[15].text)
    20 print(train_data.examples[15].label)

    ['Like', 'one', 'of', 'the', 'previous', 'commenters', 'said', ',', 'this', 'had', 'the', 'foundations', 'of', 'a', 'great', 'movie', 'but', 'something', 'happened', 'on', 'the', 'way', 'to', 'delivery', '.', 'Such', 'a', 'waste', 'because', 'Collette', "'s", 'performance', 'was', 'eerie', 'and', 'Williams', 'was', 'believable', '.', 'I', 'just', 'kept', 'waiting', 'for', 'it', 'to', 'get', 'better', '.', 'I', 'do', "n't", 'think', 'it', 'was', 'bad', 'editing', 'or', 'needed', 'another', 'director', ',', 'it', 'could', 'have', 'just', 'been', 'the', 'film', '.', 'It', 'came', 'across', 'as', 'a', 'Canadian', 'movie', ',', 'something', 'like', 'the', 'first', 'few', 'seasons', 'of', 'X', '-', 'Files', '.', 'Not', 'cheap', ',', 'just', 'hokey', '.', 'Also', ',', 'it', 'needed', 'a', 'little', 'more', 'suspense', '.', 'Something', 'that', 'makes', 'you', 'jump', 'off', 'your', 'seat', '.', 'The', 'movie', 'reached', 'that', 'moment', 'then', 'faded', 'away', ';', 'kind', 'of', 'like', 'a', 'false', 'climax', '.', 'I', 'can', 'see', 'how', 'being', 'too', 'suspenseful', 'would', 'have', 'taken', 'away', 'from', 'the', '"', 'reality', '"', 'of', 'the', 'story', 'but', 'I', 'thought', 'that', 'part', 'was', 'reached', 'when', 'Gabriel', 'was', 'in', 'the', 'hospital', 'looking', 'for', 'the', 'boy', '.', 'This', 'movie', 'needs', 'to', 'have', 'a', 'Director', "'s", 'cut', 'that', 'tries', 'to', 'fix', 'these', 'problems', '.']

    pos

    当我们把句子传进模型的时候,是按照一个个batch传进去的,而且每个batch中的句子必须是相同的长度。为了确保句子的长度相同,TorchText会把短的句子pad到和最长的句子等长。

    创建vocabulary

    vocabulary把每个单词一一映射到一个数字。使用10k个单词来构建单词表(用max_size这个参数可以设定),所有其他的单词都用<unk>来表示。

    词典中应当有10002个单词,且有两个label,可以通过TEXT.vocab和TEXT.label查询,可以直接用stoi(stringtoint) 或者itos(inttostring) 来查看单词表。

    1 TEXT.build_vocab(train_data, max_size=10000, vectors='glove.6B.100d')
    2 LABEL.build_vocab(train_data)
    3 print(len(TEXT.vocab))                     #10002
    4 print(TEXT.vocab.itos[:12])                #['<unk>', '<pad>', 'the', ',', '.', 'and', 'a', 'of', 'to', 'is', 'in', 'I']
    5 print(TEXT.vocab.stoi['and'])              #5
    6 print(LABEL.vocab.stoi)                    #defaultdict(None, {'neg': 0, 'pos': 1})

    创建iteratiors

    每个iterator中各有两部分:词(.text)和标签(.label),其中text全部转换成数字了。BucketIterator会把长度差不多的句子放到同一个batch中,确保每个batch中不出现太多的padding。这里因为pad比较少,所以把<pad>也当做了模型的输入进行训练。如果有GPU,还可以指定每个iteration返回的tensor都在GPU上。

    1 batchsz = 30
    2 train_iterator, test_iterator = data.BucketIterator.splits(
    3                                 (train_data, test_data),
    4                                 batch_size = batchsz,
    5                                )

    2.定义模型

     1 class RNN(nn.Module):
     2     
     3     def __init__(self, vocab_size, embedding_dim, hidden_dim):
     4        
     5         super(RNN, self).__init__()
     6         
     7         # [0-10001] => [100]
     8         self.embedding = nn.Embedding(vocab_size, embedding_dim)        #单词数,嵌入向量维度
     9         # [100] => [256]
    10         self.rnn = nn.LSTM(embedding_dim, hidden_dim, num_layers=2,     #双向RNN,所以下面使用hidden_dim*2
    11                            bidirectional=True, dropout=0.5)
    12         # [256*2] => [1]
    13         self.fc = nn.Linear(hidden_dim*2, 1)
    14         self.dropout = nn.Dropout(0.5)
    15         
    16     
    17     def forward(self, x):
    18         """
    19         x: [seq_len, b] vs [b, 3, 28, 28]
    20         """
    21         # [seq_len, b, 1] => [seq_len, b, 100]
    22         embedding = self.dropout(self.embedding(x))
    23         
    24         # output: [seq, b, hidden_len*2]
    25         # hidden/h: [num_layers*2, b, hidden_len]
    26         # cell/c: [num_layers*2, b, hidden_len]
    27         output, (hidden, cell) = self.rnn(embedding)          #[h0,c0]随机初始化
    28         
    29         # [num_layers*2, b, hidden_len] => 2 of [b, hidden_len] => [b, hidden_len*2]
    30         hidden = torch.cat([hidden[-2], hidden[-1]], dim=1)   #双向,所以要把最后两个输出连接
    31         
    32         # [b, hidden_len*2] => [b, 1]
    33         hidden = self.dropout(hidden)
    34         out = self.fc(hidden)
    35         
    36         return out

    使用预训练过的embedding来替换随机初始化(Tip:.copy_()这种带着下划线的函数均代表替换inplace)

    1 rnn = RNN(len(TEXT.vocab), 100, 256)                          #词个数,词嵌入维度,输出维度
    2 
    3 pretrained_embedding = TEXT.vocab.vectors
    4 print('pretrained_embedding:', pretrained_embedding.shape)    #torch.Size([10002, 100])
    5 rnn.embedding.weight.data.copy_(pretrained_embedding)
    6 print('embedding layer inited.')

    3.训练模型

    首先定义模型和损失函数。

    1 optimizer = optim.Adam(rnn.parameters(), lr=1e-3)
    2 criteon = nn.BCEWithLogitsLoss()                  #BCEWithLogitsLoss是针对二分类的CrossEntropy

    定义一个函数用于计算准确率

    1 def binary_acc(preds, y):
    2 
    3      preds = torch.round(torch.sigmoid(preds))
    4      correct = torch.eq(preds, y).float()
    5      acc = correct.sum() / len(correct)
    6      return acc  

    定义一个训练函数

     1 def train(rnn, iterator, optimizer, criteon):
     2     
     3     avg_acc = []
     4     rnn.train()        #表示进入训练模式
     5     
     6     for i, batch in enumerate(iterator):
     7         
     8         # [seq, b] => [b, 1] => [b]
     9         pred = rnn(batch.text).squeeze(1)            #batch.text 就是上面forward函数的参数text,压缩维度是为了和batch.label维度一致
    10         
    11         loss = criteon(pred, batch.label)
    12         acc = binary_acc(pred, batch.label).item()   #计算每个batch的准确率
    13         avg_acc.append(acc)
    14         
    15         optimizer.zero_grad()
    16         loss.backward()
    17         optimizer.step()                             #不断训练,pred的值会越来越接近真实的label值
    18         
    19         if i%10 == 0:
    20             print(i, acc)
    21         
    22     avg_acc = np.array(avg_acc).mean()
    23     print('avg acc:', avg_acc)

    4.评估模型

    定义一个评估函数,和训练函数高度重合,区别是要把rnn.train()改为rnn.val(),不需要反向传播过程。

     1 def evaluate(rnn, iterator, criteon):    
     2     avg_acc = []    
     3     rnn.eval()         #表示进入测试模式
     4     
     5     with torch.no_grad():
     6         for batch in iterator:
     7             
     8             pred = rnn(batch.text).squeeze(1)      #[b, 1] => [b]
     9             loss = criteon(pred, batch.label)
    10             acc = binary_acc(pred, batch.label).item()
    11             avg_acc.append(acc)
    12         
    13     avg_acc = np.array(avg_acc).mean()
    14     
    15     print('test acc:', avg_acc)

    运行

    1 for epoch in range(10):
    2     
    3     train(rnn, train_iterator, optimizer, criteon)
    4     evaluate(rnn, test_iterator, criteon)

    渣渣本实在是跑不动,结果就先不放了。

  • 相关阅读:
    layui 3种导航栏
    SQL语句内做除法得出百分比
    JS 日期比较方法
    JDK 13 的 12 个新特性,真心涨姿势了
    怎么实现单点登录?面试必问!
    厉害了,Apache架构师们遵循的 30 条设计原则
    面试官问线程安全的List,看完再也不怕了!
    Java 类在 Tomcat 中是如何加载的?
    Redis 21问,你接得住不?
    String hashCode 这个数字,很多人不知道!
  • 原文地址:https://www.cnblogs.com/cxq1126/p/13358147.html
Copyright © 2011-2022 走看看