• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

【深度学习实战】【nlp-beginner】基于深度学习的文本分类

人工智能 喵木木 1222次浏览 0个评论

任务说明:NLP-Beginner:自然语言处理入门练习 任务二   数据下载:Sentiment Analysis on Movie Reviews   参考资料:  

  1. Convolutional Neural Networks for Sentence Classificatio
  2. PyTorch官方文档
  3. 关于深度学习与自然语言处理的一些基础知识:【深度学习实战】从零开始深度学习(四):RNN与自然语言处理
  4. TorchText文本数据集读取操作

  代码:NLP-Beginner-Exercise 该任务主要利用torchtext这个包来进行。  

目录

 

    • 导入需要的包
    • 创建Field对象
    • 从文件中读取数据——TabularDataset
    • 构建词表
    • 生成迭代器
    • 创建网络模型
    • 模型训练
    • 模型结果(一)
    • 模型改进——使用预训练好的词向量
    • 模型结果(二)
    • 模型再改进——使用LSTM

 

导入需要的包

 

import torch
from torch import nn, optim
import torch.nn.functional as F
from torchtext import data
from torchtext.data import Field, TabularDataset, BucketIterator
import time

  这里主要用到torchtext的四个模块:  

  1. Field——用来定义文本预处理的模式
  2. Example——数据样本,“文本+标签”
  3. TabularDataset——用于从文件中读取数据
  4. BucketIterator——迭代器,生成Batch

  整体思路如下图所示(摘自参考资料4)  
【深度学习实战】【nlp-beginner】基于深度学习的文本分类  

创建Field对象

 

fix_length = 40
# 创建Field对象
TEXT = Field(sequential = True, lower=True, fix_length = fix_length)
LABEL = Field(sequential = False, use_vocab = False)

  Field参数说明(摘自参考资料4):  

  • sequential——是否把数据表示成序列,如果是False, 不能使用分词 默认值: True.
  • use_vocab——是否使用词典对象. 如果是False 数据的类型必须已经是数值类型. 默认值: True.
  • init_token——每一条数据的起始字符 默认值: None. eos_token——每条数据的结尾字符 默认值: None.
  • fix_length——修改每条数据的长度为该值,不够的用pad_token补全. 默认值: None.
  • tensor_type——把数据转换成的tensor类型 默认值: torch.LongTensor.
  • preprocessing——在分词之后和数值化之前使用的管道,默认值: None.
  • postprocessing——数值化之后和转化成tensor之前使用的管道,默认值: None.
  • lower——是否把数据转化为小写。默认值: False.
  • tokenize——分词函数. 默认值: string.split().
  • include_lengths——是否返回一个已经补全的最小batch的元组和和一个包含每条数据长度的列表 . 默认值: False.
  • batch_first——Whether to produce tensors with the batch dimension
  • first. 默认值: False. pad_token——用于补全的字符. 默认值: “<pad>”.
  • unk_token——不存在词典里的字符. 默认值: “<unk>”.
  • pad_first——是否补全第一个字符. 默认值:False.

  Field几个重要的方法(摘自参考资料4):  

  • pad(minibatch)——在一个batch对齐每条数据
  • build_vocab()—— 建立词典
  • numericalize()——把文本数据数值化,返回tensor

 

从文件中读取数据——TabularDataset

  torchtext的TabularDataset可以用于从CSV、TSV或者JSON文件中读取数据。   TabularDataset参数说明  

  • path——文件存储路径
  • format——读取文件的格式(CSV/TSV/JSON)
  • fields——tuple(str, Field)。如果使用列表,格式必须是CSV或TSV,列表的值应该是(name, Field)的元组。字段的顺序应该与CSV或TSV文件中的列相同,而(name, None)的元组表示将被忽略的列。如果使用dict,键应该是JSON键或CSV/TSV列的子集,值应该是(名称、字段)的元组。不存在于输入字典中的键将被忽略。这允许用户从JSON/CSV/TSV键名重命名列,还允许选择要加载的列子集。
  • skip_header——是否要跳过表的第一行(表头)
  • csv_reader_params——读取csv或者tsv所需要的一些参数。具体可以参考csv.reader

  我们可以先看一下数据的样子  
【深度学习实战】【nlp-beginner】基于深度学习的文本分类   实际上我们只需要知道文本内容(即Phrase列)和标签(即Sentiment列)就可以了,设置fields读取器如下:  

fields = [('PhraseId', None), ('SentenceId', None), ('Phrase', TEXT), ('Sentiment', LABEL)]

  设置好读取器后,可以利用TabularDataset读取数据:  

# 从文件中读取数据
fields = [('PhraseId', None), ('SentenceId', None), ('Phrase', TEXT), ('Sentiment', LABEL)]

dataset, test = TabularDataset.splits(path = './', format = 'tsv',
                                      train = 'train.tsv', test = 'test.tsv',
                                      skip_header = True, fields = fields)
train, vali = dataset.split(0.7)

  这里需要注意的是,我们读进来的train.tsv文件其实是包含训练集和测试集的,利用dataset.split(0.7)将其分成训练集和测试集,数据样本数的比例的是7:3。  

构建词表

  torchtext.data.Field对象提供了build_vocab方法来帮助构建词表。build_vocab()方法的参数参考Vocab   从训练集中构建词表的代码如下。文本数据的词表最多包含10000个词,并删除了出现频次不超过10的词。  

这里需要注意的是,我们读进来的train.tsv文件其实是包含训练集和测试集的,利用dataset.split(0.7)将其分成训练集和测试集,数据样本数的比例的是7:3。

构建词表

torchtext.data.Field对象提供了build_vocab方法来帮助构建词表。build_vocab()方法的参数参考Vocab

从训练集中构建词表的代码如下。文本数据的词表最多包含10000个词,并删除了出现频次不超过10的词。

 

生成迭代器

  trochtext提供了BucketIterator,可以帮助我们批处理所有文本并将词替换成词的索引。如果序列的长度差异很大,则填充将消耗大量浪费的内存和时间。BucketIterator可以将每个批次的相似长度的序列组合在一起,以最小化填充。  

# 生成向量的批数据
bs = 64
train_iter, vali_iter = BucketIterator.splits((train, vali), batch_size = bs, 
                                              device= torch.device('cpu'), 
                                              sort_key=lambda x: len(x.Phrase),
                                              sort_within_batch=False,
                                              shuffle = True,
                                              repeat = False)

 

创建网络模型

 

class EmbNet(nn.Module):
    def __init__(self,emb_size,hidden_size1,hidden_size2=400):
        super().__init__()
        self.embedding = nn.Embedding(emb_size,hidden_size1)
        self.fc = nn.Linear(hidden_size2,3)
        self.log_softmax = nn.LogSoftmax(dim = -1)
        
    def forward(self,x):
        embeds = self.embedding(x).view(x.size(0),-1)
        out = self.fc(embeds)
        out = self.log_softmax(out)
        return out

model = EmbNet(len(TEXT.vocab.stoi), 20)
model = model.cuda()
optimizer = optim.Adam(model.parameters(),lr=0.001)

  模型包括三层,首先是一个embedding层,它接收两个参数,即词表的大小和希望为每个单词创建的word embedding的维度。对于一个句子来说,所有的词的word embedding向量收尾相接(扁平化),通过一个线性层和一个log_softmax层得到最后的分类。   这里的优化器选择Adam,在实践中的效果比SGD要好很多。  

模型训练

 

# 训练模型
def fit(epoch, model, data_loader, phase = 'training'):
    if phase == 'training':
        model.train()
    if phase == 'validation':
        model.eval()
    running_loss = 0.0
    running_correct = 0.0
            
    for batch_idx, batch in enumerate(data_loader):
        text, target = batch.Phrase, batch.Sentiment
#        if torch.cuda.is_available():
#            text, target = text.cuda(), target.cuda()
        if phase == 'training':
            optimizer.zero_grad()
        output = model(text)
        
        loss = F.cross_entropy(output, target)
        
        running_loss += F.cross_entropy(output, target, reduction='sum').item()
        preds = output.data.max(dim=1, keepdim = True)[1]
        running_correct += preds.eq(target.data.view_as(preds)).cpu().sum()
        if phase == 'training':
            loss.backward()
            optimizer.step()
    
    loss = running_loss/len(data_loader.dataset)
    accuracy = 100. * running_correct/len(data_loader.dataset)
    
    print(f'{phase} loss is {loss:{5}.{4}} and {phase} accuracy is {running_correct}/{len(data_loader.dataset)}{accuracy:{10}.{4}}')
    return loss, accuracy

train_losses , train_accuracy = [],[]
val_losses , val_accuracy = [],[]

t0 = time.time()

for epoch in range(1, 100):
    print('epoch no. {} :'.format(epoch) + '-'* 15)
    epoch_loss, epoch_accuracy = fit(epoch, model, train_iter,phase='training')
    val_epoch_loss, val_epoch_accuracy = fit(epoch, model, vali_iter,phase='validation')
    train_losses.append(epoch_loss)
    train_accuracy.append(epoch_accuracy)
    val_losses.append(val_epoch_loss)
    val_accuracy.append(val_epoch_accuracy)

tend = time.time()
print('总共用时:{} s'.format(tend-t0))

 

模型结果(一)

 
【深度学习实战】【nlp-beginner】基于深度学习的文本分类   模型一共训练了100轮,在val的准确率大致在60%左右。  

模型改进——使用预训练好的词向量

  很多时候,在处理特定领域的NLP任务时,使用预训练好的词向量会非常有用。通常使用预训练的词向量包括下面三个步骤。   下载词向量  

TEXT.build_vocab(train, vectors=GloVe(name='6B', dim=300), max_size=10000, min_freq=20)

  在模型中加载词向量  

model = EmbNet(len(TEXT.vocab.stoi),300, 300*fix_length)
# 利用预训练好的词向量
model.embedding.weight.data = TEXT.vocab.vectors

  冻结embedding层  

# 冻结embedding层的权重
model.embedding.weight.requires_grad = False
optimizer = optim.Adam([ param for param in model.parameters() if param.requires_grad == True],lr=0.001)

 

模型结果(二)

 
【深度学习实战】【nlp-beginner】基于深度学习的文本分类  

模型再改进——使用LSTM

 

## 创建模型
class LSTM(nn.Module):
    def __init__(self, vocab, hidden_size, n_cat, bs = 1, nl = 2):
        super().__init__()
        self.hidden_size = hidden_size
        self.bs = bs
        self.nl = nl
        self.n_vocab = len(vocab)
        self.n_cat = n_cat
        self.e = nn.Embedding(self.n_vocab, self.hidden_size)
        self.rnn = nn.LSTM(self.hidden_size, self.hidden_size, self.nl)
        self.fc2 = nn.Linear(self.hidden_size, self.n_cat)
        self.sofmax = nn.LogSoftmax(dim = -1)
    
    def forward(self, x):
        bs = x.size()[1]
        if bs != self.bs:
            self.bs = bs
        e_out = self.e(x)
        h0, c0 = self.init_paras()
        rnn_o, _ = self.rnn(e_out, (h0, c0))
        rnn_o = rnn_o[-1]
        fc = self.fc2(rnn_o)
        out = self.sofmax(fc)
        return out
    
    def init_paras(self):
        h0 = Variable(torch.zeros(self.nl, self.bs, self.hidden_size))
        c0 = Variable(torch.zeros(self.nl, self.bs, self.hidden_size))
        return h0, c0
        

model = LSTM(TEXT.vocab, hidden_size = 300, n_cat = 5, bs = bs)
# 利用预训练好的词向量
model.e.weight.data = TEXT.vocab.vectors

# 冻结embedding层的权重
model.e.weight.requires_grad = False
optimizer = optim.Adam([ param for param in model.parameters() if param.requires_grad == True],lr=0.001)

 


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明【深度学习实战】【nlp-beginner】基于深度学习的文本分类
喜欢 (0)

您必须 登录 才能发表评论!

加载中……