0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

PyTorch文本分类任务的基本流程

jf_78858299 来源:天宏NLP 作者: tianhongzxy 2023-02-22 14:23 次阅读

文本分类是NLP领域的较为容易的入门问题,本文记录文本分类任务的基本流程,大部分操作使用了torchtorchtext两个库。

1. 文本数据预处理

首先数据存储在三个csv文件中,分别是train.csv,valid.csv,test.csv,第一列存储的是文本数据,例如情感分类问题经常是用户的评论 review ,例如imdb或者amazon数据集。第二列是情感极性 polarity ,N分类问题的话就有N个值,假设值的范围是0~N-1。

下面是很常见的文本预处理流程,英文文本的话不需要分词,直接按空格split就行了,这里只会主要说说第4点。

1.去除非文本部分

2.分词

3.去除停用词

4.对英文单词进行 词干提取 (stemming)和 词型还原 (lemmatization)

5.转为小写

6.特征处理

•Bag of Words•Tf-idf•N-gram•Word2vec词干提取和词型还原

from nltk.stem import SnowballStemmer
stemmer = SnowballStemmer("english") # 选择语言
from nltk.stem import WordNetLemmatizer 
wnl = WordNetLemmatizer()

SnowballStemmer较为激进,转换有可能出现错误,这里较为推荐使用WordNetLemmatizer,它一般只在非常肯定的情况下才进行转换,否则会返回原来的单词。

stemmer.stem('knives')
# knive
wnl.lemmatize('knives')
# knife

因为我没有系统学习和研究过NLTK的代码,所以就不在这里展开说了,有兴趣的可以自己去看NLTK源代码。

2. 使用torchtext加载文本数据

本节主要是用的模块是torchtext里的data模块,处理的数据同上一节所描述。

首先定义一个tokenizer用来处理文本,比如分词,小写化,如果你已经根据上一节的词干提取和词型还原的方法处理过文本里的每一个单词后可以直接分词就够了。

tokenize = lambda x: x.split()

或者也可以更保险点,使用spacy库,不过就肯定更耗费时间了。

import spacy


spacy_en = spacy.load('en')
def tokenizer(text):
    return [toke.text for toke in spacy_en.tokenizer(text)]

然后要定义 Field ,至于Field是啥,你可以简单地把它理解为一个能够加载、预处理和存储文本数据和标签的对象。我们可以用它根据训练数据来建立词表,加载预训练的Glove词向量等等。

def DataLoader():
    tokenize = lambda x: x.split()
        # 用户评论,include_lengths设为True是为了方便之后使用torch的pack_padded_sequence
    REVIEW = data.Field(sequential=True,tokenize=tokenize, include_lengths=True)
    # 情感极性
    POLARITY = data.LabelField(sequential=False, use_vocab=False, dtype = torch.long)
        # 假如train.csv文件并不是只有两列,比如1、3列是review和polarity,2列是我们不需要的数据,
    # 那么就要添加一个全是None的元组, fields列表存储的Field的顺序必须和csv文件中每一列的顺序对应,
    # 否则review可能就加载到polarity Field里去了
    fields = [('review', REVIEW), (None, None), ('polarity', POLARITY)]


    # 加载train,valid,test数据
    train_data, valid_data, test_data = data.TabularDataset.splits(
                                    path = 'amazon',
                                    train = 'train.csv',
                                                                  validation = 'valid.csv',
                                                                  test = 'test.csv',
                                    format = 'csv',
                                    fields = fields,
                                    skip_header = False # 是否跳过文件的第一行
    )
    return REVIEW, POLARITY, train_data

加载完数据可以开始建词表。如果本地没有预训练的词向量文件,在运行下面的代码时会自动下载到当前文件夹下的'.vector_cache'文件夹内,如果本地已经下好了,可以用Vectors指定文件名name,路径cache,还可以使用Glove。

from torchtext.vocab import Vectors, Glove
import torch


REVIEW, POLARITY, train_data = DataLoader()
# vectors = Vectors(name='glove.6B.300d.txt', cache='.vector_cache')
REVIEW.build_vocab(train_data, # 建词表是用训练集建,不要用验证集和测试集
                  max_size=400000, # 单词表容量
                  vectors='glove.6B.300d', # 还有'glove.840B.300d'已经很多可以选
                  unk_init=torch.Tensor.normal_ # 初始化train_data中不存在预训练词向量词表中的单词
)


# print(REVIEW.vocab.freqs.most_common(20)) 数据集里最常出现的20个单词
# print(REVIEW.vocab.itos[:10])                       列表 index to word
# print(REVIEW.vocab.stoi)                               字典 word to index

接着就是把预训练词向量加载到model的embedding weight里去了。

pretrained_embeddings = REVIEW.vocab.vectors
model.embedding.weight.data.copy_(pretrained_embeddings)
UNK_IDX = REVIEW.vocab.stoi[REVIEW.unk_token]
PAD_IDX = REVIEW.vocab.stoi[REVIEW.pad_token]
# 因为预训练的权重的unk和pad的词向量不是在我们的数据集语料上训练得到的,所以最好置零
model.embedding.weight.data[UNK_IDX] = torch.zeros(EMBEDDING_DIM)
model.embedding.weight.data[PAD_IDX] = torch.zeros(EMBEDDING_DIM)

然后用torchtext的迭代器来批量加载数据,torchtext.data里的BucketIterator非常好用,它可以把长度相近的文本数据尽量都放到一个batch里,这样最大程度地减少padding,数据就少了很多无意义的0,也减少了矩阵计算量,也许还能对最终准确度有帮助(误)?

sort_within_batch设为True的话,一个batch内的数据就会按sort_key的排列规则降序排列,sort_key是排列的规则,这里使用的是review的长度,即每条用户评论所包含的单词数量。

train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
                                                (train_data, valid_data, test_data),
                                                batch_size=32,
                                                sort_within_batch=True,
                                                sort_key = lambda x:len(x.review),
                                                device=torch.device('cpu'))

最后就是加载数据喂给模型了。

for batch in train_iterator:
  # 因为REVIEW Field的inclue_lengths为True,所以还会包含一个句子长度的Tensor
  review, review_len = batch.review  
  # review.size = (seq_length, batch_size) , review_len.size = (batch_size, )
  polarity = batch.polarity
  # polarity.size = (batch_size, )
  predictions = model(review, review_lengths)
  loss = criterion(predictions, polarity) # criterion = nn.CrossEntropyLoss()

3. 使用pytorch写一个LSTM情感分类器

下面是我简略写的一个模型,仅供参考

import torch.nn as nn
import torch.nn.functional as F
from torch.nn.utils.rnn import pack_padded_sequence
import torch




class LSTM(nn.Module):


    def __init__(self, vocab_size, embedding_dim, hidden_dim, output_dim,
                 n_layers, bidirectional, dropout, pad_idx):
        super(LSTM, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=pad_idx)
        self.lstm = nn.LSTM(embedding_dim, hidden_dim, num_layers=n_layers,
                            bidirectional=bidirectional, dropout=dropout)
        self.Ws = nn.Parameter(torch.Tensor(hidden_dim, output_dim))
        self.bs = nn.Parameter(torch.zeros((output_dim, )))
        nn.init.uniform_(self.Ws, -0.1, 0.1)
        nn.init.uniform_(self.bs, -0.1, 0.1)
        self.dropout = nn.Dropout(p=0.5)


    def forward(self, x, x_len):
        x = self.embedding(x)
        x = pack_padded_sequence(x, x_len)
        H, (h_n, c_n) = self.lstm(x)
        h_n = self.dropout(h_n)
        h_n = torch.squeeze(h_n)
        res = torch.matmul(h_n, self.Ws) + self.bs
        y = F.softmax(res, dim=1)
        # y.size(batch_size, output_dim)
        return y

训练函数

def train(model, iterator, optimizer, criterion):
    epoch_loss = 0
    num_sample = 0
    correct = 0


    model.train()
    for batch in iterator:
        optimizer.zero_grad()
        review, review_lengths = batch.review
        polarity = batch.polarity
        predictions = model(review, review_lengths)
        correct += torch.sum(torch.argmax(preds, dim=1) == polarity)
        loss = criterion(predictions, polarity)
        loss.backward()
        epoch_loss += loss.item()
        num_sample += len(batch)
        optimizer.step()


    return epoch_loss / num_sample, correct.float() / num_sample


if __name__ == '__main__':
    for epoch in range(N_EPOCHS):
    train_loss, acc = train(model, train_iter, optimizer, criterion)
    print(f'\\tTrain Loss: {train_loss:.3f} | Train Acc: {acc* 100:.2f}%')

4. 注意事项和遇到的一些坑

1.文本情感分类需不需要去除停用词?

•应该是不用的,否则acc有可能下降。

2.data.TabularDataset.splits虽然好用,但是如果你只想加载训练集,这时候如果直接不给validation和test参数赋值,那么其他代码和原来一样,比如这样

train_data = data.TabularDataset.splits(
                                       path = '',
                                       train = 'train.csv',
                                       format = 'csv',
                                       fields = fields,
                                       skip_header = False # 是否跳过文件的第一行
   )

那么底下你一定会报错,因为data.TabularDataset.splits返回的是一个元组,也就是如果是训练验证测试三个文件都给了函数,就返回(train_data, valid_data, test_data),这时候你用三个变量去接受函数返回值当然没问题,元组会自动拆包。

当只给函数一个文件train.csv时,函数返回的是(train_data)而非train_data,因此正确的写法应该如下

train_data = data.TabularDataset.splits(
                                       path = '',
                                       train = 'train.csv',
                                       format = 'csv',
                                       fields = fields,
                                       skip_header = False # 是否跳过文件的第一行
   )[0] # 注意这里的切片,选择元组的第一个也是唯一一个元素赋给train_data

3.同理data.BucketIterator.splits也有相同的问题,它不但返回的是元组,它的参数datasets要求也是以元组形式,即(train_data, valid_data, test_data)进行赋值,否则在下面的运行中也会出现各种各样奇怪的问题。

如果你要生成两个及以上的迭代器,那么没问题,直接照上面写就完事了。

如果你只要生成train_iterator,那么正确的写法应该是下面这样

train_iter = data.BucketIterator(
               train_data,
               batch_size=32,
               sort_key=lambda x:len(x.review),
               sort_within_batch=True,
               shuffle=True # 训练集需要shuffle,但因为验证测试集不需要
                                                  # 可以生成验证和测试集的迭代器直接用data.iterator.Iterator类就足够了
   )

4.出现的问题 x = pack_padded_sequence(x, x_len) 当数据集有长度为0的句子时, 就会后面报错

5.当vocab size较大而训练数据不多的情况下,我在实验时发现Adagrad效果比Adam好,如果数据较多,可以尝试使用RMSProp和Adam

5. 总结

不仅仅是NLP领域,在各大顶会中,越来越多的学者选择使用Pytorch而非TensorFlow,主要原因就是因为它的易用性,torchtext和pytorch搭配起来是非常方便的NLP工具,可以大大缩短文本预处理,加载数据的时间。

我本人之前用过tf 1.x以及keras,最终拥抱了Pytorch,也是因为它与Numpy极其类似的用法,更Pythonic的代码,清晰的源码让我在遇到bug时能一步一步找到问题所在,动态图让人能随时看到输出的Tensor的全部信息,这些都是Pytorch的优势。

现在tf 2.0也在不断改进,有人称tf越来越像pytorch了,其实pytorch也在不断向tf学习,在工业界,tf仍然处于王者地位,不知道未来pytorch能不能在工业界也与tf平分秋色,甚至更胜一筹呢?

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据预处理
    +关注

    关注

    1

    文章

    20

    浏览量

    2758
  • nlp
    nlp
    +关注

    关注

    1

    文章

    488

    浏览量

    22034
  • pytorch
    +关注

    关注

    2

    文章

    808

    浏览量

    13219
收藏 人收藏

    评论

    相关推荐

    pyhanlp文本分类与情感分析

    语料库本文语料库特指文本分类语料库,对应IDataSet接口。而文本分类语料库包含两个概念:文档和类目。一个文档只属于一个类目,一个类目可能含有多个文档。比如搜狗文本分类语料库迷你版.zip,下载前
    发表于 02-20 15:37

    TensorFlow的CNN文本分类

    在TensorFlow中实现CNN进行文本分类(译)
    发表于 10-31 09:27

    NLPIR平台在文本分类方面的技术解析

    文本分类问题就是将一篇文档归入预先定义的几个类别中的一个或几个,而文本的自动分类则是使用计算机程序来实现这种文本分类,即根据事先指定的规则和示例样本,自动从海量文档中识别并训练
    发表于 11-18 17:46

    基于文章标题信息的汉语自动文本分类

    文本分类文本挖掘的一个重要组成部分,是信息搜索领域的一项重要研究课题。该文提出一种基于文章标题信息的汉语自动文本分类方法,在HNC理论的领域概念框架下,通过标题
    发表于 04-13 08:31 10次下载

    textCNN论文与原理——短文本分类

    前言 之前书写了使用pytorch进行短文本分类,其中的数据处理方式比较简单粗暴。自然语言处理领域包含很多任务,很多的数据向之前那样处理的话未免有点繁琐和耗时。在pytorch中众所周
    的头像 发表于 12-31 10:08 2529次阅读
    textCNN论文与原理——短<b class='flag-5'>文本分类</b>

    文本分类的一个大型“真香现场”来了

    任何标注数据啦!哇,真香! 当前的文本分类任务需要利用众多标注数据,标注成本是昂贵的。而半监督文本分类虽然减少了对标注数据的依赖,但还是需要领域专家手动进行标注,特别是在类别数目很大的情况下。 试想一下,我们人类是如何对新闻
    的头像 发表于 02-05 11:02 1859次阅读
    <b class='flag-5'>文本分类</b>的一个大型“真香现场”来了

    基于深度神经网络的文本分类分析

      随着深度学习技术的快速发展,许多研究者尝试利用深度学习来解决文本分类问题,特别是在卷积神经网络和循环神经网络方面,出现了许多新颖且有效的分类方法。对基于深度神经网络的文本分类问题进行分析,介绍
    发表于 03-10 16:56 37次下载
    基于深度神经网络的<b class='flag-5'>文本分类</b>分析

    融合文本分类和摘要的多任务学习摘要模型

    文本摘要应包含源文本中所有重要信息,传统基于编码器-解码器架构的摘要模型生成的摘要准确性较低。根据文本分类文本摘要的相关性,提出一种多任务
    发表于 04-27 16:18 11次下载
    融合<b class='flag-5'>文本分类</b>和摘要的多<b class='flag-5'>任务</b>学习摘要模型

    基于不同神经网络的文本分类方法研究对比

    海量文本分析是实现大数据理解和价值发现的重要手段,其中文本分类作为自然语言处理的经典问题受到研究者广泛关注,而人工神经网络在文本分析方面的优异表现使其成为目前的主要研究方向。在此背景下,介绍卷积
    发表于 05-13 16:34 49次下载

    基于双通道词向量的卷积胶囊网络文本分类算法

    的词向量与基于特定文本分类任务扩展的语境词向量作为神经网络的2个输入通道,并采用具有动态路由机制的卷积胶囊网络模型进行文本分类。在多个英文数据集上的实验结果表明,双通道的词向量训练方式优于单通道策略,与LSTM、RAE、 M
    发表于 05-24 15:07 6次下载

    基于LSTM的表示学习-文本分类模型

    文本表示和分类是自然语言理解领域的研究热点。目前已有很多文本分类方法,包括卷积网络、递归网络、自注意力机制以及它们的结合。但是,复杂的网络并不能从根本上提高文本分类的性能,好的
    发表于 06-15 16:17 18次下载

    基于注意力机制的新闻文本分类模型

    基于注意力机制的新闻文本分类模型
    发表于 06-27 15:32 30次下载

    带你从头构建文本分类

    文本分类是 NLP 中最常见的任务之一, 它可用于广泛的应用或者开发成程序,例如将用户反馈文本标记为某种类别,或者根据客户文本语言自动归类。另外向我们平时见到的邮件垃圾过滤器也是
    的头像 发表于 03-22 10:49 3574次阅读

    PyTorch教程4.3之基本分类模型

    电子发烧友网站提供《PyTorch教程4.3之基本分类模型.pdf》资料免费下载
    发表于 06-05 15:43 0次下载
    <b class='flag-5'>PyTorch</b>教程4.3之基<b class='flag-5'>本分类</b>模型

    卷积神经网络在文本分类领域的应用

    显著成就后,也逐渐被引入到文本分类任务中。卷积神经网络通过模拟人类视觉系统的信息处理方式,能够有效地提取文本中的局部特征,进而实现高精度的文本分类。本文将对卷积神经网络在
    的头像 发表于 07-01 16:25 717次阅读