- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章使用pytorch和torchtext进行文本分类的实例由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
文本分类是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 。
词干提取和词型还原 。
1
2
3
4
|
from
nltk.stem
import
SnowballStemmer
stemmer
=
SnowballStemmer(
"english"
)
# 选择语言
from
nltk.stem
import
WordNetLemmatizer
wnl
=
WordNetLemmatizer()
|
SnowballStemmer较为激进,转换有可能出现错误,这里较为推荐使用WordNetLemmatizer,它一般只在非常肯定的情况下才进行转换,否则会返回原来的单词.
1
2
3
4
|
stemmer.stem(
'knives'
)
# knive
wnl.lemmatize(
'knives'
)
# knife
|
因为我没有系统学习和研究过NLTK的代码,所以就不多说了,有兴趣的可以自己去阅读NLTK的源码.
2. 使用torchtext加载文本数据 。
本节主要是用的模块是torchtext里的data模块,处理的数据同上一节所描述.
首先定义一个tokenizer用来处理文本,比如分词,小写化,如果你已经根据上一节的词干提取和词型还原的方法处理过文本里的每一个单词后可以直接分词就够了.
1
|
tokenize
=
lambda
x: x.split()
|
或者也可以更保险点,使用spacy库,不过就肯定更耗费时间了.
1
2
3
4
5
|
import
spacy
spacy_en
=
spacy.load(
'en'
)
def
tokenizer(text):
return
[toke.text
for
toke
in
spacy_en.tokenizer(text)]
|
然后要定义Field,至于Field是啥,你可以简单地把它理解为一个能够加载、预处理和存储文本数据和标签的对象。我们可以用它根据训练数据来建立词表,加载预训练的Glove词向量等等.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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里去了.
1
2
3
4
5
6
7
|
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的长度,即每条用户评论所包含的单词数量.
1
2
3
4
5
6
|
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'
))
|
最后就是加载数据喂给模型了.
1
2
3
4
5
6
7
8
|
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情感分类器 。
下面是我简略写的一个模型,仅供参考 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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
|
训练函数 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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}%'
)
|
注意事项和遇到的一些坑 。
文本情感分类需不需要去除停用词?
应该是不用的,否则acc有可能下降.
data.TabularDataset.splits虽然好用,但是如果你只想加载训练集,这时候如果直接不给validation和test参数赋值,那么其他代码和原来一样,比如这样 。
1
2
3
4
5
6
7
|
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,因此正确的写法应该如下 。
1
2
3
4
5
6
7
|
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,那么正确的写法应该是下面这样 。
1
2
3
4
5
6
7
8
|
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进行文本分类的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://blog.csdn.net/qq_40367479/article/details/102772334 。
最后此篇关于使用pytorch和torchtext进行文本分类的实例的文章就讲到这里了,如果你想了解更多关于使用pytorch和torchtext进行文本分类的实例的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!