赞
踩
h t = RNN ( x t , h t − 1 ) h_t = \text{RNN}(x_t, h_{t-1}) ht=RNN(xt,ht−1)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KKlgGZVV-1631697478974)(./assets/sentiment1.png)]
TorchText 的主要概念之一是Field
,这些定义了应如何处理数据, TEXT
【tokenize=‘spacy’】字段来定义应该如何处理评论,并使用 LABEL
字段来处理情绪
如果没有设置 tokenize
参数,默认值是使用空格拆分字符串。我们还需要指定一个 tokenizer_language
来告诉 torchtext 使用哪个 spaCy 模型。我们使用 en_core_web_sm
模型。
下载
en_core_web_sm
模型的方法:python -m spacy download en_core_web_sm
LABEL
由 LabelField
定义,它是专门用于处理标签的 Field
类的特殊子集。
TorchText
的另一个方便的功能是它支持自然语言处理 (NLP) 中使用的常见数据集。
import torch
from torchtext.legacy import data
# 设置随机种子数,该数可以保证随机数是可重复的
SEED = 1234
# 设置种子
torch.manual_seed(SEED)
torch.backends.cudnn.deterministic = True
# 读取数据和标签
TEXT = data.Field(tokenize = 'spacy', tokenizer_language = 'en_core_web_sm')
LABEL = data.LabelField(dtype = torch.float)
# 自动下载 IMDb 数据集并将其拆分为规范的训练集和测试集
from torchtext.legacy import datasets
train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)
# view
print(f'Number of training examples: {len(train_data)}')
print(f'Number of testing examples: {len(test_data)}')
print(vars(train_data.examples[0]))
Number of training examples: 25000
Number of testing examples: 25000
{'text': ['elvira', 'mistress', 'of', 'the', 'dark', 'is', 'one', 'of', 'my', 'fav', 'movies', ',', 'it', 'has', 'every', 'thing', 'you', 'would', 'want', 'in', 'a', 'film', ',', 'like', 'great', 'one', 'liners', ',', 'sexy', 'star', 'and', 'a', 'Outrageous', 'story', '!', 'if', 'you', 'have', 'not', 'seen', 'it', ',', 'you', 'are', 'missing', 'out', 'on', 'one', 'of', 'the', 'greatest', 'films', 'made', '.', 'i', 'ca', "n't", 'wait', 'till', 'her', 'new', 'movie', 'comes', 'out', '!'], 'label': 'pos'}
import random
train_data, valid_data = train_data.split(split_ratio=0.8 , random_state = random.seed(SEED))
须构建一个 词汇表。 这是一个查找表,其中数据集中的每个单词都有唯一对应的 index(整数)
每个 index 用于为每个词构造一个 one-hot 向量,通常用 VV 表示。
取前n个出现次数最多的单词作为one-hot的基
忽略出现次数小于m个的单词
<unk>
来编码未编码单词
MAX_VOCAB_SIZE = 25000
TEXT.build_vocab(train_data, max_size = MAX_VOCAB_SIZE)
LABEL.build_vocab(train_data)
print(f"Unique tokens in TEXT vocabulary: {len(TEXT.vocab)}")
print(f"Unique tokens in LABEL vocabulary: {len(LABEL.vocab)}")
# 额外token ---- <unk>和<pad>
我们一次输入一个batch,并且批次中的所有句子都需要具有相同的长度
print(TEXT.vocab.freqs.most_common(20))
print(TEXT.vocab.itos[:10])
创建验证集,测试集,以及训练集的迭代器, 每一次的迭代都会返回一个batch的数据。
BATCH_SIZE = 64
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
(train_data, valid_data, test_data),
batch_size = BATCH_SIZE,
device = device)
创建RNN ----- nn.module的子类。
三层模型分别是嵌入层,RNN层,最后还有全连接层
squeeze
方法, 可以消除维度为1的维度
import torch.nn as nn class RNN(nn.Module): def __init__(self, input_dim, embedding_dim, hidden_dim, output_dim): super().__init__() self.embedding = nn.Embedding(input_dim, embedding_dim) self.rnn = nn.RNN(embedding_dim, hidden_dim) self.fc = nn.Linear(hidden_dim, output_dim) def forward(self, text): #text = [sent len, batch size] embedded = self.embedding(text) #embedded = [sent len, batch size, emb dim] output, hidden = self.rnn(embedded) #output = [sent len, batch size, hid dim] #hidden = [1, batch size, hid dim] assert torch.equal(output[-1,:,:], hidden.squeeze(0)) return self.fc(hidden.squeeze(0))
# 设置
INPUT_DIM = len(TEXT.vocab)
EMBEDDING_DIM = 100
HIDDEN_DIM = 256
OUTPUT_DIM = 1
model = RNN(INPUT_DIM, EMBEDDING_DIM, HIDDEN_DIM, OUTPUT_DIM)
def count_parameters(model):
return sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f'The model has {count_parameters(model):,} trainable parameters')
# The model has 2,592,105 trainable parameters
import torch.optim as optim
optimizer = optim.SGD(model.parameters(), lr=1e-3)
# BCEWithLogitsLoss一般用来做二分类
criterion = nn.BCEWithLogitsLoss()
# GPU 适用
model = model.to(device)
criterion = criterion.to(device)
# 将sigmoid层输出的预测结果输入到计算准确率的函数, 取整到最近的整数.大于0.5,就取1。反之取0。计算出预测的结果和label一致的值,在除以所有的值,就可以得到准确率。
def binary_accuracy(preds, y):
"""
Returns accuracy per batch, i.e. if you get 8/10 right, this returns 0.8, NOT 8
"""
#round predictions to the closest integer
rounded_preds = torch.round(torch.sigmoid(preds))
correct = (rounded_preds == y).float() #convert into float for division
acc = correct.sum() / len(correct)
return acc
train
函数迭代所有的样本,每次都是一个batch。
model.train()
将model处于 “training 模式”, 也会打开 dropout 和 batch normalization. 在每一次的batch, 先将梯度清0. 模型的每一个参数都有一个 grad
属性, 存储着损失函数计算的梯度值. PyTorch 不会自动删除(或“归零”)从上次梯度计算中计算出的梯度,因此必须手动将其归零。
每次输入, batch.text
, 到模型中. 只需要调用模型即可.
用loss.backward()
计算梯度,更新参数使用的是 optimizer.step()
。
损失值和准确率在整个 epoch 中累积, .item()
抽取张量中只含有一个值的张量中的值。
最后,我们返回损失和准确率,在整个 epoch 中取平均值. len
可以得到epoch中的batch数
当然在计算的时候,要记得将LongTensor
转化为 torch.float
。这是因为 TorchText 默认将张量设置为 LongTensor
。
def train(model, iterator, optimizer, criterion): epoch_loss = 0 epoch_acc = 0 model.train() for batch in iterator: optimizer.zero_grad() predictions = model(batch.text).squeeze(1) loss = criterion(predictions, batch.label) acc = binary_accuracy(predictions, batch.label) loss.backward() optimizer.step() epoch_loss += loss.item() epoch_acc += acc.item() return epoch_loss / len(iterator), epoch_acc / len(iterator)
evaluate
和 train
相似, 只要将train函数稍微进行修改即可。
model.eval()
将模型置于"evaluation 模式", 这会关掉 dropout 和 batch normalization.
在with no_grad()
下,不会进行梯度计算. 这会导致使用更少的内存并加快计算速度.
其他函数在train
中类似,在evaluate中移除了 optimizer.zero_grad()
, loss.backward()
and optimizer.step()
, 因为不再需要更新参数了
def evaluate(model, iterator, criterion): epoch_loss = 0 epoch_acc = 0 model.eval() with torch.no_grad(): for batch in iterator: predictions = model(batch.text).squeeze(1) loss = criterion(predictions, batch.label) acc = binary_accuracy(predictions, batch.label) epoch_loss += loss.item() epoch_acc += acc.item() return epoch_loss / len(iterator), epoch_acc / len(iterator)
import time
def epoch_time(start_time, end_time):
elapsed_time = end_time - start_time
elapsed_mins = int(elapsed_time / 60)
elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
return elapsed_mins, elapsed_secs
# 训练 N_EPOCHS = 5 best_valid_loss = float('inf') for epoch in range(N_EPOCHS): start_time = time.time() train_loss, train_acc = train(model, train_iterator, optimizer, criterion) valid_loss, valid_acc = evaluate(model, valid_iterator, criterion) end_time = time.time() epoch_mins, epoch_secs = epoch_time(start_time, end_time) if valid_loss < best_valid_loss: best_valid_loss = valid_loss torch.save(model.state_dict(), 'tut1-model.pt') print(f'Epoch: {epoch+1:02} | Epoch Time: {epoch_mins}m {epoch_secs}s') print(f'\tTrain Loss: {train_loss:.3f} | Train Acc: {train_acc*100:.2f}%') print(f'\t Val. Loss: {valid_loss:.3f} | Val. Acc: {valid_acc*100:.2f}%')
# 结果
model.load_state_dict(torch.load('tut1-model.pt'))
test_loss, test_acc = evaluate(model, test_iterator, criterion)
print(f'Test Loss: {test_loss:.3f} | Test Acc: {test_acc*100:.2f}%')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。