侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

使用pytorch和torchtext进行文本分类的实例

Python  /  管理员 发布于 5年前   231

文本分类是NLP领域的较为容易的入门问题,本文记录我自己在做文本分类任务以及复现相关论文时的基本流程,绝大部分操作都使用了torch和torchtext两个库。

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 SnowballStemmerstemmer = SnowballStemmer("english") # 选择语言from nltk.stem import WordNetLemmatizer wnl = WordNetLemmatizer()

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

stemmer.stem('knives')# knivewnl.lemmatize('knives')# knife

因为我没有系统学习和研究过NLTK的代码,所以就不多说了,有兴趣的可以自己去阅读NLTK的源码。

2. 使用torchtext加载文本数据

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

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

tokenize = lambda x: x.split()

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

import spacyspacy_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, Gloveimport torchREVIEW, 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.vectorsmodel.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 nnimport torch.nn.functional as Ffrom torch.nn.utils.rnn import pack_padded_sequenceimport torchclass 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_sampleif __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}%')

注意事项和遇到的一些坑

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

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

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

同理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类就足够了)

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

Adagrad效果比Adam好的多

4. 总结

不仅仅是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平分秋色,甚至更胜一筹呢?

以上这篇使用pytorch和torchtext进行文本分类的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


  • 上一条:
    pytorch实现用CNN和LSTM对文本进行分类方式
    下一条:
    Pytorch实现基于CharRNN的文本分类与生成示例
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客