Я познакомился с более влиятельной моделью в области Inference — ESIM. В то же время шерсть Colab была зачищена.
Введение в модель ESIM
Enhanced LSTM for Natural Language InferenceВ этой статье предлагается модель для вычисления подобия двух предложений. Модель состоит из 3-х частей:
Input Encoding
Во-первых, два входных предложения, словесный вектор предпосылки и гипотезы.иПосле процесса BiLSTM получается новое представление вектора слов.и.
Local Inference
В документе говорится, что лучший способ вычислить степень корреляции между двумя словами — это вычислить скалярное произведение вектора слов, которое равно. Таким образом, вычислив сходство (внимание) между всеми парами слов двух предложений, можно получить матрицу
Затем возникает очень интересная мысль: поскольку мы хотим судить о сходстве двух предложений, нам нужно посмотреть, могут ли они представлять друг друга. То есть используйте векторы слов в посылке и гипотезе соответственно.иПредставляет вектор слов противника.
Формула в статье следующая:
Перевод такой, потому что модель не знает, какая пара должна бытьиЭто похоже или относительно, поэтому операция перечисления выполняется для выражения всех ситуаций. Матрица подобия, рассчитанная ранее, используется для взвешивания. Вес в каждой позиции — это текущая строка матрицы весов (для вычисления, для вычисленийзначение Softmax столбца).
В целях усиления рассуждений (улучшение информации для вывода) в статье собраны промежуточные результаты, полученные ранее.
Inference Composition
Вектор слова, используемый в комбинации вывода, получен в предыдущей части.иили используйте BiLSTM для получения контекстной информации двух наборов векторов слов.
После объединения всей информации она отправляется на полносвязный слой для завершения окончательного смешивания.
Импорт необходимых библиотек
import os
import time
import logging
import pickle
from tqdm import tqdm_notebook as tqdm
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchtext
from torchtext import data, datasets
from torchtext.vocab import GloVe
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import nltk
from nltk import word_tokenize
import spacy
from keras_preprocessing.text import Tokenizer
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
cuda
Смонтировать Google Диск
from google.colab import drive
drive.mount('/content/drive')
Go to this URL in a browser: https://accounts.google.com/o/oauth2/xxxxxxxx
Enter your authorization code:
··········
Mounted at /content/drive
!nvidia-smi
Fri Aug 9 04:45:35 2019
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.67 Driver Version: 410.79 CUDA Version: 10.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 Tesla K80 Off | 00000000:00:04.0 Off | 0 |
| N/A 60C P0 62W / 149W | 6368MiB / 11441MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
+-----------------------------------------------------------------------------+
Подготовьте данные с помощью torchtext
Использование torchtext относится к ссылке:GitHub.com/py torch/ отвратительно…
GloVe в torchtext можно использовать напрямую, но поскольку он не предоставляет функции прямого чтения исходных файлов, аналогичной torchvision, он может только читать кэш, поэтому лучше всего:
- Сначала загрузите GloVe локально
- Откройте терминал в каталоге загрузки, а затем используйте torchtext для создания кеша в терминале.
- При использовании GloVe в будущем увеличьте параметр cache, чтобы torchtext читался из кеша, а не загружал огромный GloVe в локальный
Но если это шерсть Colab, это легко (~ ̄▽ ̄)~
torchtext также может напрямую загружать набор данных SNLI, но структура каталога загрузки набора данных выглядит следующим образом:
- root
- snli_1.0
- snli_1.0_train.jsonl
- snli_1.0_dev.jsonl
- snli_1.0_test.jsonl
- snli_1.0
TEXT = data.Field(batch_first=True, lower=True, tokenize="spacy")
LABEL = data.Field(sequential=False)
# 分离训练、验证、测试集
tic = time.time()
train, dev, test = datasets.SNLI.splits(TEXT, LABEL)
print(f"Cost: {(time.time() - tic) / 60:.2f} min")
# 加载GloVe预训练向量
tic = time.time()
glove_vectors = GloVe(name='6B', dim=100)
print(f"Creat GloVe done. Cost: {(time.time() - tic) / 60:.2f} min")
# 创建词汇表
tic = time.time()
TEXT.build_vocab(train, dev, test, vectors=glove_vectors)
LABEL.build_vocab(train)
print(f"Build vocab done. Cost: {(time.time() - tic) / 60:.2f} min")
print(f"TEXT.vocab.vectors.size(): {TEXT.vocab.vectors.size()}")
num_words = int(TEXT.vocab.vectors.size()[0])
# 保存分词和词向量的对应字典
if os.path.exists("/content/drive/My Drive/Colab Notebooks"):
glove_stoi_path = "/content/drive/My Drive/Colab Notebooks/vocab_label_stoi.pkl"
else:
glove_stoi_path = "./vocab_label_stoi.pkl"
pickle.dump([TEXT.vocab.stoi, LABEL.vocab.stoi], open(glove_stoi_path, "wb"))
batch_sz = 128
train_iter, dev_iter, test_iter = data.BucketIterator.splits(
datasets=(train, dev, test),
batch_sizes=(batch_sz, batch_sz, batch_sz),
shuffle=True,
device=device
)
Cost: 7.94 min
Creat GloVe done. Cost: 0.00 min
Build vocab done. Cost: 0.12 min
TEXT.vocab.vectors.size(): torch.Size([34193, 100])
Общая конфигурация параметров
При совершенствовании алхимии лучше всего иметь глобальную формулу, которую легко настроить.
class Config:
def __init__(self):
# For data
self.batch_first = True
try:
self.batch_size = batch_sz
except NameError:
self.batch_size = 512
# For Embedding
self.n_embed = len(TEXT.vocab)
self.d_embed = TEXT.vocab.vectors.size()[-1]
# For Linear
self.linear_size = self.d_embed
# For LSTM
self.hidden_size = 300
# For output
self.d_out = len(LABEL.vocab) # 表示输出为几维
self.dropout = 0.5
# For training
self.save_path = r"/content/drive/My Drive/Colab Notebooks" if os.path.exists(
r"/content/drive/My Drive/Colab Notebooks") else "./"
self.snapshot = os.path.join(self.save_path, "ESIM.pt")
self.device = device
self.epoch = 64
self.scheduler_step = 3
self.lr = 0.0004
self.early_stop_ratio = 0.985 # 可以提早结束训练过程
args = Config()
Реализация кода модели ESIM
Ссылка на код:GitHub.com/Пэн Шуан/…
Использование nn.BatchNorm1d
Регуляризация данных может устранить проблему различного распределения данных в разных измерениях.Геометрическое понимание состоит в том, чтобы упорядочить «эллипсоид» в n-мерном пространстве в «сферу», что может упростить сложность обучения модели и улучшить скорость обучения.
Однако, если все входные данные упорядочиваются, это потребует много времени.Пакетная нормализация — это компромиссный метод, который упорядочивает только входные данные размера_пакета. Вероятностное понимание заключается в оценке распределения всех выборок на основе распределения выборок размера партии.
ПиТорчnn.BatchNorm1dКак вы можете понять из названия, это пакетная регуляризация одномерных данных, поэтому здесь есть два ограничения:
- обучение (т.е. открытое
model.train()
), предоставьте размер пакета не менее 2; для тестирования используйте (model.eval()
) когда нет ограничения на размер пакета - Предпоследним параметром по умолчанию является «пакет».
И форма каждого пакета данных, полученного моей предыдущей обработкой данных после векторного отображения слов,batch * seq_len * embed_dim
, так что здесь 3 измерения. и после тортекстаdata.BucketIterator.splits
обработка, за партиюseq_len
является динамическим (той же длины, что и самое длинное предложение в текущем пакете). Таким образом, если никакая обработка не добавляется, она непосредственно вводится вBatchNorm1d
, вы обычно увидите следующую ошибку:
RuntimeError: running_mean should contain xxx elements not yyy
Нужно ли добавлять слой BatchNorm1d после внедрения
Реализация эталонного кода очень красивая, видно мастерство автора кода. Однако автор, похоже, не использует предварительно обработанный вектор слов в качестве вектора встраивания, но я использую предварительно обученный вектор слов GloVe и не буду обучать Glove, поэтому необходимо увеличиватьnn.BatchNorm1d
?
Поскольку слепое увеличение количества слоев в сети не даст хорошего эффекта, лучше всего сначала посмотреть, является ли вектор слов GloVe «регуляризованным» в каждом измерении.
glove = TEXT.vocab.vectors
means, stds = glove.mean(dim=0).numpy(), glove.std(dim=0).numpy()
dims = [i for i in range(glove.shape[1])]
plt.scatter(dims, means)
plt.scatter(dims, stds)
plt.legend(["mean", "std"])
plt.xlabel("Dims")
plt.ylabel("Features")
plt.show()
print(f"mean(means)={means.mean():.4f}, std(means)={means.std():.4f}")
print(f"mean(stds)={stds.mean():.4f}, std(stds)={stds.std():.4f}")
mean(means)=0.0032, std(means)=0.0809
mean(stds)=0.4361, std(stds)=0.0541
Из рисунка видно, что распределение каждого измерения относительно стабильно, поэтому его не предполагается использовать после слоя Embedding.nn.BatchNorm1d
.
Использование nn.LSTM
nn.LSTM(
input_size, hidden_size, num_layers, bias=True, batch_first=False, dropout=0, bidirectional=False)
)
nn.LSTM
Параметр по умолчанию для batch_first:False
, мне это будет очень неудобно, кто привык к формату данных CV, так что лучше поставить егоTrue
.
Ниже приведен формат ввода/вывода LSTM. Входные данные могут быть опущеныh_0
иc_0
, в это время LSTM автоматически сгенерирует все 0h_0
иc_0
.
- Inputs: input, (h_0, c_0)
- Outputs: output, (h_n, c_n)
- input: (seq_len, batch, input_size)
- output: (seq_len, batch, num_directions * hidden_size)
- h / c: (num_layers * num_directions, batch, hidden_size)
class ESIM(nn.Module):
def __init__(self, args):
super(ESIM, self).__init__()
self.args = args
self.embedding = nn.Embedding(
args.n_embed, args.d_embed) # 参数的初始化可以放在之后
# self.bn_embed = nn.BatchNorm1d(args.d_embed)
self.lstm1 = nn.LSTM(args.d_embed, args.hidden_size,
num_layers=1, batch_first=True, bidirectional=True)
self.lstm2 = nn.LSTM(args.hidden_size * 8, args.hidden_size,
num_layers=1, batch_first=True, bidirectional=True)
self.fc = nn.Sequential(
nn.BatchNorm1d(args.hidden_size * 8),
nn.Linear(args.hidden_size * 8, args.linear_size),
nn.ELU(inplace=True),
nn.BatchNorm1d(args.linear_size),
nn.Dropout(args.dropout),
nn.Linear(args.linear_size, args.linear_size),
nn.ELU(inplace=True),
nn.BatchNorm1d(args.linear_size),
nn.Dropout(args.dropout),
nn.Linear(args.linear_size, args.d_out),
nn.Softmax(dim=-1)
)
def submul(self, x1, x2):
mul = x1 * x2
sub = x1 - x2
return torch.cat([sub, mul], -1)
def apply_multiple(self, x):
# input: batch_size * seq_len * (2 * hidden_size)
p1 = F.avg_pool1d(x.transpose(1, 2), x.size(1)).squeeze(-1)
p2 = F.max_pool1d(x.transpose(1, 2), x.size(1)).squeeze(-1)
# output: batch_size * (4 * hidden_size)
return torch.cat([p1, p2], 1)
def soft_attention_align(self, x1, x2, mask1, mask2):
'''
x1: batch_size * seq_len * dim
x2: batch_size * seq_len * dim
'''
# attention: batch_size * seq_len * seq_len
attention = torch.matmul(x1, x2.transpose(1, 2))
# mask的作用:防止计算Softmax的时候出现异常值
mask1 = mask1.float().masked_fill_(mask1, float('-inf'))
mask2 = mask2.float().masked_fill_(mask2, float('-inf'))
# weight: batch_size * seq_len * seq_len
weight1 = F.softmax(attention + mask2.unsqueeze(1), dim=-1)
x1_align = torch.matmul(weight1, x2)
weight2 = F.softmax(attention.transpose(
1, 2) + mask1.unsqueeze(1), dim=-1)
x2_align = torch.matmul(weight2, x1)
# x_align: batch_size * seq_len * hidden_size
return x1_align, x2_align
def forward(self, sent1, sent2):
"""
sent1: batch * la
sent2: batch * lb
"""
mask1, mask2 = sent1.eq(0), sent2.eq(0)
x1, x2 = self.embedding(sent1), self.embedding(sent2)
# x1, x2 = self.bn_embed(x1), self.bn_embed(x2)
# batch * [la | lb] * dim
o1, _ = self.lstm1(x1)
o2, _ = self.lstm1(x2)
# Local Inference
# batch * [la | lb] * hidden_size
q1_align, q2_align = self.soft_attention_align(o1, o2, mask1, mask2)
# Inference Composition
# batch_size * seq_len * (8 * hidden_size)
q1_combined = torch.cat([o1, q1_align, self.submul(o1, q1_align)], -1)
q2_combined = torch.cat([o2, q2_align, self.submul(o2, q2_align)], -1)
# batch_size * seq_len * (2 * hidden_size)
q1_compose, _ = self.lstm2(q1_combined)
q2_compose, _ = self.lstm2(q2_combined)
# Aggregate
q1_rep = self.apply_multiple(q1_compose)
q2_rep = self.apply_multiple(q2_compose)
# Classifier
similarity = self.fc(torch.cat([q1_rep, q2_rep], -1))
return similarity
def take_snapshot(model, path):
"""保存模型训练结果到Drive上,防止Colab重置后丢失"""
torch.save(model.state_dict(), path)
print(f"Snapshot has been saved to {path}")
def load_snapshot(model, path):
model.load_state_dict(torch.load(path))
print(f"Load snapshot from {path} done.")
model = ESIM(args)
# if os.path.exists(args.snapshot):
# load_snapshot(model, args.snapshot)
# Embedding向量不训练
model.embedding.weight.data.copy_(TEXT.vocab.vectors)
model.embedding.weight.requires_grad = False
model.to(args.device)
ESIM(
(embedding): Embedding(34193, 100)
(lstm1): LSTM(100, 300, batch_first=True, bidirectional=True)
(lstm2): LSTM(2400, 300, batch_first=True, bidirectional=True)
(fc): Sequential(
(0): BatchNorm1d(2400, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(1): Linear(in_features=2400, out_features=100, bias=True)
(2): ELU(alpha=1.0, inplace)
(3): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(4): Dropout(p=0.5)
(5): Linear(in_features=100, out_features=100, bias=True)
(6): ELU(alpha=1.0, inplace)
(7): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(8): Dropout(p=0.5)
(9): Linear(in_features=100, out_features=4, bias=True)
(10): Softmax()
)
)
фаза обучения
Вот несколько деталей:
форма партии.этикетка
batch.label
представляет собой одномерный вектор формы (пакет); иY_pred
это форма2D вектор , используя.topk(1).indices
Это все еще двумерный вектор после извлечения максимального значения.
Так что, если вы не расширяетеbatch.label
измерение, которое PyTorch автоматически транслируетbatch.label
, окончательный результат больше не, но, то итоговая расчетная точность будет запредельной. Вот что означает приведенный ниже код:
(Y_pred.topk(1).indices == batch.label.unsqueeze(1))
Разделение тензора и скаляра
В Python 3.6 символ деления/
Результатом по умолчанию является float, но это не относится к PyTorch, и это еще одна деталь, которую легко упустить из виду.
(Y_pred.topk(1).indices == batch.label.unsqueeze(1))
Результат приведенного выше кода можно рассматривать как тип bool (на самом делеtorch.uint8
). перечислить.sum()
Тип результата после суммированияtorch.LongTensor
. Но целочисленное деление в PyTorch не приведет к числам с плавающей запятой.
# 就像下面的代码会得到0一样
In [2]: torch.LongTensor([1]) / torch.LongTensor([5])
Out[2]: tensor([0])
Переменная acc накапливает правильное количество выборок в каждой партии.Из-за автоматического преобразования типов acc теперь указывает наtorch.LongTensor
тип, поэтому его необходимо использовать при расчете показателя точности в конце..item()
Извлеките целочисленное значение. Если эту деталь игнорировать, итоговая степень точности равна 0.
def training(model, data_iter, loss_fn, optimizer):
"""训练部分"""
model.train()
data_iter.init_epoch()
acc, cnt, avg_loss = 0, 0, 0.0
for batch in data_iter:
Y_pred = model(batch.premise, batch.hypothesis)
loss = loss_fn(Y_pred, batch.label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
avg_loss += loss.item() / len(data_iter)
# unsqueeze是因为label是一维向量,下同
acc += (Y_pred.topk(1).indices == batch.label.unsqueeze(1)).sum()
cnt += len(batch.premise)
return avg_loss, (acc.item() / cnt) # 如果不提取item,会导致accuracy为0
def validating(model, data_iter, loss_fn):
"""验证部分"""
model.eval()
data_iter.init_epoch()
acc, cnt, avg_loss = 0, 0, 0.0
with torch.set_grad_enabled(False):
for batch in data_iter:
Y_pred = model(batch.premise, batch.hypothesis)
avg_loss += loss_fn(Y_pred, batch.label).item() / len(data_iter)
acc += (Y_pred.topk(1).indices == batch.label.unsqueeze(1)).sum()
cnt += len(batch.premise)
return avg_loss, (acc.item() / cnt)
def train(model, train_data, val_data):
"""训练过程"""
optimizer = optim.Adam(model.parameters(), lr=args.lr)
loss_fn = nn.CrossEntropyLoss()
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
optimizer, mode='min', factor=0.5, patience=args.scheduler_step, verbose=True)
train_losses, val_losses, train_accs, val_accs = [], [], [], []
# Before train
tic = time.time()
train_loss, train_acc = validating(model, train_data, loss_fn)
val_loss, val_acc = validating(model, val_data, loss_fn)
train_losses.append(train_loss)
val_losses.append(val_loss)
train_accs.append(train_acc)
val_accs.append(val_acc)
min_val_loss = val_loss
print(f"Epoch: 0/{args.epoch}\t"
f"Train loss: {train_loss:.4f}\tacc: {train_acc:.4f}\t"
f"Val loss: {val_loss:.4f}\tacc: {val_acc:.4f}\t"
f"Cost time: {(time.time()-tic):.2f}s")
try:
for epoch in range(args.epoch):
tic = time.time()
train_loss, train_acc = training(
model, train_data, loss_fn, optimizer)
val_loss, val_acc = validating(model, val_data, loss_fn)
train_losses.append(train_loss)
val_losses.append(val_loss)
train_accs.append(train_acc)
val_accs.append(val_acc)
scheduler.step(val_loss)
print(f"Epoch: {epoch + 1}/{args.epoch}\t"
f"Train loss: {train_loss:.4f}\tacc: {train_acc:.4f}\t"
f"Val loss: {val_loss:.4f}\tacc: {val_acc:.4f}\t"
f"Cost time: {(time.time()-tic):.2f}s")
if val_loss < min_val_loss: # 即时保存
min_val_loss = val_loss
take_snapshot(model, args.snapshot)
# Early-stop:
# if len(val_losses) >= 3 and (val_loss - min_val_loss) / min_val_loss > args.early_stop_ratio:
# print(f"Early stop with best loss: {min_val_loss:.5f}")
# break
# args.early_stop_ratio *= args.early_stop_ratio
except KeyboardInterrupt:
print("Interrupted by user")
return train_losses, val_losses, train_accs, val_accs
train_losses, val_losses, train_accs, val_accs = train(
model, train_iter, dev_iter)
Epoch: 0/64 Train loss: 1.3871 acc: 0.3335 Val loss: 1.3871 acc: 0.3331 Cost time: 364.32s
Epoch: 1/64 Train loss: 1.0124 acc: 0.7275 Val loss: 0.9643 acc: 0.7760 Cost time: 998.41s
Snapshot has been saved to /content/drive/My Drive/Colab Notebooks/ESIM.pt
Epoch: 2/64 Train loss: 0.9476 acc: 0.7925 Val loss: 0.9785 acc: 0.7605 Cost time: 1003.32s
Epoch: 3/64 Train loss: 0.9305 acc: 0.8100 Val loss: 0.9204 acc: 0.8217 Cost time: 999.49s
Snapshot has been saved to /content/drive/My Drive/Colab Notebooks/ESIM.pt
Epoch: 4/64 Train loss: 0.9183 acc: 0.8227 Val loss: 0.9154 acc: 0.8260 Cost time: 1000.97s
Snapshot has been saved to /content/drive/My Drive/Colab Notebooks/ESIM.pt
Epoch: 5/64 Train loss: 0.9084 acc: 0.8329 Val loss: 0.9251 acc: 0.8156 Cost time: 996.99s
....
Epoch: 21/64 Train loss: 0.8236 acc: 0.9198 Val loss: 0.8912 acc: 0.8514 Cost time: 992.48s
Epoch: 22/64 Train loss: 0.8210 acc: 0.9224 Val loss: 0.8913 acc: 0.8514 Cost time: 996.35s
Epoch 22: reducing learning rate of group 0 to 5.0000e-05.
Epoch: 23/64 Train loss: 0.8195 acc: 0.9239 Val loss: 0.8940 acc: 0.8485 Cost time: 1000.48s
Epoch: 24/64 Train loss: 0.8169 acc: 0.9266 Val loss: 0.8937 acc: 0.8490 Cost time: 1006.78s
Interrupted by user
Нарисуйте кривую потери-точности
iters = [i + 1 for i in range(len(train_losses))]
# 防止KeyboardInterrupt的打断导致两组loss不等长
min_len = min(len(train_losses), len(val_losses))
# 绘制双纵坐标图
fig, ax1 = plt.subplots()
ax1.plot(iters, train_losses[: min_len], '-', label='train loss')
ax1.plot(iters, val_losses[: min_len], '-.', label='val loss')
ax1.set_xlabel("Epoch")
ax1.set_ylabel("Loss")
# 创建子坐标轴
ax2 = ax1.twinx()
ax2.plot(iters, train_accs[: min_len], ':', label='train acc')
ax2.plot(iters, val_accs[: min_len], '--', label='val acc')
ax2.set_ylabel("Accuracy")
# 为双纵坐标图添加图例
handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
plt.legend(handles1 + handles2, labels1 + labels2, loc='center right')
plt.show()
предсказывать
Помимо результатов обучения, модель также должна иметь возможность использования на практике.
nlp = spacy.load("en")
# 重新加载之前训练结果最棒的模型参数
load_snapshot(model, args.snapshot)
# 小规模数据还是cpu跑得快
model.to(torch.device("cpu"))
with open(r"/content/drive/My Drive/Colab Notebooks/vocab_label_stoi.pkl", "rb") as f:
vocab_stoi, label_stoi = pickle.load(f)
Load snapshot from /content/drive/My Drive/Colab Notebooks/ESIM.pt done.
def sentence2tensor(stoi, sent1: str, sent2: str):
"""将两个句子转化为张量"""
sent1 = [str(token) for token in nlp(sent1.lower())]
sent2 = [str(token) for token in nlp(sent2.lower())]
tokens1, tokens2 = [], []
for token in sent1:
tokens1.append(stoi[token])
for token in sent2:
tokens2.append(stoi[token])
delt_len = len(tokens1) - len(tokens2)
if delt_len > 0:
tokens2.extend([1] * delt_len)
else:
tokens1.extend([1] * (-delt_len))
tensor1 = torch.LongTensor(tokens1).unsqueeze(0)
tensor2 = torch.LongTensor(tokens2).unsqueeze(0)
return tensor1, tensor2
def use(model, premise: str, hypothsis: str):
"""使用模型测试"""
label_itos = {0: '<unk>', 1: 'entailment',
2: 'contradiction', 3: 'neutral'}
model.eval()
with torch.set_grad_enabled(False):
tensor1, tensor2 = sentence2tensor(vocab_stoi, premise, hypothsis)
predict = model(tensor1, tensor2)
top1 = predict.topk(1).indices.item()
print(f"The answer is '{label_itos[top1]}'")
prob = predict.cpu().squeeze().numpy()
plt.bar(["<unk>", "entailment", "contradiction", "neutral"], prob)
plt.ylabel("probability")
plt.show()
После ввода двух предложений выведите наиболее вероятные предположения и покажите вероятность каждого предположения в виде гистограммы.
# 蕴含
use(model,
"A statue at a museum that no seems to be looking at.",
"There is a statue that not many people seem to be interested in.")
# 对立
use(model,
"A land rover is being driven across a river.",
"A sedan is stuck in the middle of a river.")
# 中立
use(model,
"A woman with a green headscarf, blue shirt and a very big grin.",
"The woman is young.")
The answer is 'entailment'
The answer is 'contradiction'
The answer is 'neutral'