前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >用于情感分析的Transformers

用于情感分析的Transformers

作者头像
代码医生工作室
发布2020-03-20 11:16:38
发布2020-03-20 11:16:38
3.4K00
代码可运行
举报
文章被收录于专栏:相约机器人相约机器人
运行总次数:0
代码可运行

本文首次介绍的Transformers模型。具体来说,将使用本文中的BERT(来自Transformers的双向编码器表示)模型。

Transformers模型比这些教程中涵盖的任何其他模型都大得多。因此将使用Transformers库来获取经过预训练的transformers,并将其用作嵌入层。 将冻结(而不是训练)Transformers,仅训练从Transformers产生的表示中学习的模型的其余部分。在这种情况下,将使用多层双向GRU,但是任何模型都可以从这些表示中学习。

介绍

“NLP’s ImageNet moment has arrived.”

                 – Sebastian Ruder

想象一下我们有能力构建支持谷歌翻译的自然语言处理(NLP)模型,并且在Python中仅需几行代码来完成,这听起来是不是让人非常兴奋。

而现在我们就可以坐在自己的机器前实现这个了!借助于被HuggingFace称为PyTorch-Transformers目前最先进的NLP工具。

https://github.com/huggingface/pytorch-transformers

我们可以简单地用Python导入它并进行实验。

我对现在NLP的研发速度感到非常惊讶,每一篇新论文、每一个框架和库都在推动着这个不可思议的强大领域的发展。由于围绕人工智能的研究的开放文化和大量免费可用的文本数据,几乎没有什么是我们今天不能做的。

无论我再怎么强调PyTorch-Transformers对研究社区和NLP行业的影响也不为过。我相信这有可能彻底改变我们所知的自然语言处理领域。

揭开NLP的神秘面纱

本质上,自然语言处理是教计算机理解人类语言的复杂性。

在讨论PyTorch-Transformers的技术细节之前,让我们快速回顾一下该库构建的概念——NLP。我们还将理解最先进的(state-of-the-art)意味着什么。

在我们开始PyTorch-Transformers的讲解之前,这里有一些你需要了解的东西:

  • 最先进的(state-of-the-art)是指目前对于某项任务“最好的”算法或技术。当我们说“最好的”时,我们的意思是这些算法是由像谷歌、Facebook、微软和亚马逊这样的巨头所倡导的。
  • NLP有许多定义明确的任务,研究人员正在研究创建智能技术来解决这些问题。一些最受欢迎的任务像机器翻译、文本摘要、问答系统等。
  • 深度学习技术如Recurrent Neural Networks (RNNs), Sequence2Sequence, Attention,还有Word Embeddings(Glove, Word2Vec)对NLP任务来说曾是最先进的。
  • 然而这些技术被一个叫Transformers的框架取代了,其背后是几乎所有的当前最先进的NLP模型。

注意 这篇文章将多处提及Transformers ,所以我强烈建议你阅读下面的指南,以对Transformers有个理解

https://www.analyticsvidhya.com/blog/2019/06/understanding-transformers-nlp-state-of-the-art-models/?utm_source=blog&utm_medium=pytorch-transformers-nlp-python

PyTorch-Transformers是什么?

PyTorch-Transformers是一个最先进的自然语言处理预训练模型库。

我从PyTorch-Transformers的文档中选取了这一部分。这个库目前包含PyTorch实现、预训练的模型权重、使用脚本和用于以下模型的转换工具:

  • BERT(来自谷歌) 与论文BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 一起发布
  • GPT(来自OpenAI) 与论文 Improving Language Understanding by Generative Pre-Training 一起发布
  • GPT-2(来自OpenAI) 与论文 Language Models are Unsupervised Multitask Learners 一起发布
  • Transformer-XL(来自谷歌/CMU) 与论文 Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context 一起发布
  • XLNet (来自谷歌/CMU) 与论文 XLNet: Generalized Autoregressive Pretraining for Language Understanding 一起发布
  • XLM (来自Facebook) 与论文 Cross-lingual Language Model Pretraining一起发布

上述所有模型都是适用于各种NLP任务的最佳模型。

大多数最先进的模型需要大量的训练数据和花费数天时间在昂贵的GPU硬件上进行训练,而这些只有大型技术公司和研究实验室才能负担得起。但随着PyTorch-Transformers的推出,现在任何人都可以利用这些最先进的模型!

在你的机器上安装PyTorch-Transformers

在Python中 Pytorch-Transformers非常简单。你可以只使用pip安装:

代码语言:javascript
代码运行次数:0
运行
复制
pip install pytorch-transformers

或者在Colab上使用以下命令:

代码语言:javascript
代码运行次数:0
运行
复制
!pip install pytorch-transformers

由于大多数这些模型都是GPU密集型的,因此我建议按照本文使用谷歌Colab。

准备资料

首先,像往常一样,为确定性结果设置随机种子。

代码语言:javascript
代码运行次数:0
运行
复制
import torch
import randomimport numpy as np
SEED = 1234
random.seed(SEED)np.random.seed(SEED)torch.manual_seed(SEED)torch.backends.cudnn.deterministic = True

transformer已经使用特定的词汇进行了训练,这意味着需要使用完全相同的词汇进行训练,并且还需要以与transformer最初训练时相同的方式标记数据。

幸运的是,transformer库为提供的每个transformer模型提供了标记器。在这种情况下,使用的是BERT模型,该模型会忽略大小写(即每个单词都小写)。通过加载预训练的基于bert-base-uncase的令牌生成器来获得此功能。

代码语言:javascript
代码运行次数:0
运行
复制
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')I1106 14:55:11.110527 139759243081536 file_utils.py:39] PyTorch version 1.3.0 available.I1106 14:55:11.917650 139759243081536 tokenization_utils.py:374] loading file https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-vocab.txt from cache at /home/ben/.cache/torch/transformers/26bc1ad6c0ac742e9b52263248f6d0f00068293b33709fae12320c0e35ccfbbb.542ce4285a40d23a559526243235df47c5f75c197f04f37d1a0c124c32c9a084

标记器具有vocab属性,其中包含将要使用的实际词汇。可以通过检查其长度来检查其中有多少个令牌。

代码语言:javascript
代码运行次数:0
运行
复制
len(tokenizer.vocab)

30522

使用令牌生成器就像在字符串上调用tokenizer.tokenize一样简单。这将以与预训练的transformer模型一致的方式标记和小写数据。

代码语言:javascript
代码运行次数:0
运行
复制
tokens = tokenizer.tokenize('Hello WORLD how ARE yoU?')
print(tokens)['hello', 'world', 'how', 'are', 'you', '?']

可以使用词汇表tokenizer.convert_tokens_to_ids对令牌进行数字化。

代码语言:javascript
代码运行次数:0
运行
复制
indexes = tokenizer.convert_tokens_to_ids(tokens)
print(indexes)

[7592, 2088, 2129, 2024, 2017, 1029]

transformer还接受了特殊标记的训练,以标记句子的开头和结尾,此处有详细说明。 以及标准填充和未知令牌。 也可以从令牌生成器中获取这些。

注意:分词器确实具有序列的开始和序列的结束属性(bos_token和eos_token),但未设置这些属性,因此不应将其用于此transformer。

代码语言:javascript
代码运行次数:0
运行
复制
init_token = tokenizer.cls_tokeneos_token = tokenizer.sep_tokenpad_token = tokenizer.pad_tokenunk_token = tokenizer.unk_token
print(init_token, eos_token, pad_token, unk_token)

[CLS] [SEP] [PAD] [UNK]

可以通过使用词汇表来转换特殊标记的索引...

代码语言:javascript
代码运行次数:0
运行
复制
init_token_idx = tokenizer.convert_tokens_to_ids(init_token)eos_token_idx = tokenizer.convert_tokens_to_ids(eos_token)pad_token_idx = tokenizer.convert_tokens_to_ids(pad_token)unk_token_idx = tokenizer.convert_tokens_to_ids(unk_token)
print(init_token_idx, eos_token_idx, pad_token_idx, unk_token_idx)

101 102 0 100

或通过从令牌生成器中明确获取它们。

代码语言:javascript
代码运行次数:0
运行
复制
init_token_idx = tokenizer.cls_token_ideos_token_idx = tokenizer.sep_token_idpad_token_idx = tokenizer.pad_token_idunk_token_idx = tokenizer.unk_token_id
print(init_token_idx, eos_token_idx, pad_token_idx, unk_token_idx)

101 102 0 100

需要处理的另一件事是,模型是在具有定义的最大长度的序列上进行训练的-它不知道如何处理序列的时间要比在其上进行训练的时间长。可以通过检查要使用的 transformer版本的max_model_input_sizes来获得这些输入大小的最大长度。在这种情况下,它是512个令牌。

代码语言:javascript
代码运行次数:0
运行
复制
max_input_length = tokenizer.max_model_input_sizes['bert-base-uncased']
print(max_input_length)

512

以前,已经使用spaCy标记器对示例进行标记。但是,现在需要定义一个函数,该函数将传递给TEXT字段,该函数将处理所有标记处理。它还将令牌的数量减少到最大长度。请注意,最大长度比实际的最大长度小2。这是因为需要向每个序列附加两个标记,一个标记添加到开始,一个标记添加到结束。

代码语言:javascript
代码运行次数:0
运行
复制
def tokenize_and_cut(sentence):    tokens = tokenizer.tokenize(sentence)    tokens = tokens[:max_input_length-2]    return tokens

现在定义字段。 transforme期望批次尺寸为第一,因此将batch_first = True设置为True。因为已经有了由 transforme提供的文本词汇,所以将use_vocab = False设置为告诉torchtext将处理事物的词汇方面。将tokenize_and_cut函数作为令牌生成器传递。预处理参数是一个函数,该函数在标记了示例之后将其用于示例,这是我们将标记转换为其索引的地方。最后,定义特殊标记-请注意,将它们定义为它们的索引值而不是它们的字符串值,即100而不是[UNK],这是因为序列已经转换为索引。

像以前一样定义标签字段。

代码语言:javascript
代码运行次数:0
运行
复制
from torchtext import data
TEXT = data.Field(batch_first = True,                  use_vocab = False,                  tokenize = tokenize_and_cut,                  preprocessing = tokenizer.convert_tokens_to_ids,                  init_token = init_token_idx,                  eos_token = eos_token_idx,                  pad_token = pad_token_idx,                  unk_token = unk_token_idx)
LABEL = data.LabelField(dtype = torch.float)

像以前一样加载数据并创建验证拆分。

代码语言:javascript
代码运行次数:0
运行
复制
from torchtext import datasets
train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)
train_data, valid_data = train_data.split(random_state = random.seed(SEED))In [13]:print(f"Number of training examples: {len(train_data)}")print(f"Number of validation examples: {len(valid_data)}")print(f"Number of testing examples: {len(test_data)}")

Number of training examples: 17500

Number of validation examples: 7500

Number of testing examples: 25000

可以检查一个示例,并确保文本已被数字化。

代码语言:javascript
代码运行次数:0
运行
复制
print(vars(train_data.examples[6]))

{'text': [5949, 1997, 2026, 2166, 1010, 1012, 1012, 1012, 1012, 1996, 2472, 2323, 2022, 10339, 1012, 2339, 2111, 2514, 2027, 2342, 2000, 2191, 22692, 5691, 2097, 2196, 2191, 3168, 2000, 2033, 1012, 2043, 2016, 2351, 2012, 1996, 2203, 1010, 2009, 2081, 2033, 4756, 1012, 1045, 2018, 2000, 2689, 1996, 3149, 2116, 2335, 2802, 1996, 2143, 2138, 1045, 2001, 2893, 10339, 3666, 2107, 3532, 3772, 1012, 11504, 1996, 3124, 2040, 2209, 9895, 2196, 4152, 2147, 2153, 1012, 2006, 2327, 1997, 2008, 1045, 3246, 1996, 2472, 2196, 4152, 2000, 2191, 2178, 2143, 1010, 1998, 2038, 2010, 3477, 5403, 3600, 2579, 2067, 2005, 2023, 10231, 1012, 1063, 1012, 6185, 2041, 1997, 2184, 1065], 'label': 'neg'}

可以使用convert_ids_to_tokens将这些索引转换回可读的令牌。

代码语言:javascript
代码运行次数:0
运行
复制
tokens = tokenizer.convert_ids_to_tokens(vars(train_data.examples[6])['text'])
print(tokens)

['waste', 'of', 'my', 'life', ',', '.', '.', '.', '.', 'the', 'director', 'should', 'be', 'embarrassed', '.', 'why', 'people', 'feel', 'they', 'need', 'to', 'make', 'worthless', 'movies', 'will', 'never', 'make', 'sense', 'to', 'me', '.', 'when', 'she', 'died', 'at', 'the', 'end', ',', 'it', 'made', 'me', 'laugh', '.', 'i', 'had', 'to', 'change', 'the', 'channel', 'many', 'times', 'throughout', 'the', 'film', 'because', 'i', 'was', 'getting', 'embarrassed', 'watching', 'such', 'poor', 'acting', '.', 'hopefully', 'the', 'guy', 'who', 'played', 'heath', 'never', 'gets', 'work', 'again', '.', 'on', 'top', 'of', 'that', 'i', 'hope', 'the', 'director', 'never', 'gets', 'to', 'make', 'another', 'film', ',', 'and', 'has', 'his', 'pay', '##che', '##ck', 'taken', 'back', 'for', 'this', 'crap', '.', '{', '.', '02', 'out', 'of', '10', '}']

尽管已经处理了文本的词汇表,但是仍然需要为标签构建词汇表。

代码语言:javascript
代码运行次数:0
运行
复制
LABEL.build_vocab(train_data)
代码语言:javascript
代码运行次数:0
运行
复制
print(LABEL.vocab.stoi)

defaultdict(None, {'neg': 0, 'pos': 1})

和以前一样,创建迭代器。 理想情况下,希望使用最大的批量,因为发现这可以为transformers提供最佳的结果。

代码语言:javascript
代码运行次数:0
运行
复制
BATCH_SIZE = 128
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)

建立模型

接下来,将加载预训练的模型,并确保加载与令牌化程序相同的模型。

代码语言:javascript
代码运行次数:0
运行
复制
from transformers import BertTokenizer, BertModel
bert = BertModel.from_pretrained('bert-base-uncased')

I1106 14:57:06.877642 139759243081536 configuration_utils.py:151] loading configuration file https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-config.json from cache at /home/ben/.cache/torch/transformers/4dad0251492946e18ac39290fcfe91b89d370fee250efe9521476438fe8ca185.bf3b9ea126d8c0001ee8a1e8b92229871d06d36d8808208cc2449280da87785c

I1106 14:57:06.878792 139759243081536 configuration_utils.py:168] Model config {

"attention_probs_dropout_prob": 0.1,

"finetuning_task": null,

"hidden_act": "gelu",

"hidden_dropout_prob": 0.1,

"hidden_size": 768,

"initializer_range": 0.02,

"intermediate_size": 3072,

"layer_norm_eps": 1e-12,

"max_position_embeddings": 512,

"num_attention_heads": 12,

"num_hidden_layers": 12,

"num_labels": 2,

"output_attentions": false,

"output_hidden_states": false,

"output_past": true,

"pruned_heads": {},

"torchscript": false,

"type_vocab_size": 2,

"use_bfloat16": false,

"vocab_size": 30522

}

I1106 14:57:07.421291 139759243081536 modeling_utils.py:337] loading weights file https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-pytorch_model.bin from cache at /home/ben/.cache/torch/transformers/aa1ef1aede4482d0dbcd4d52baad8ae300e60902e88fcb0bebdec09afd232066.36ca03ab34a1a5d5fa7bc3d03d55c4fa650fed07220e2eeebc06ce58d0e9a157

接下来,将定义实际模型。

将使用预训练的transformer模型,而不是使用嵌入层来获取文本的嵌入。然后,将这些嵌入内容输入到GRU中,以生成对输入句子的情感的预测。通过其config属性从transformer获取嵌入尺寸大小(称为hidden_size)。其余的初始化是标准的。

在前向遍历中,将transformers包装在no_grad中,以确保在模型的这一部分上没有计算出任何梯度。transformer实际上返回整个序列的嵌入以及合并的输出。文档指出,合并的输出“通常不是输入语义内容的良好总结,通常最好对整个输入序列的隐藏状态序列进行平均或合并”,因此将不使用它。正向传递的其余部分是递归模型的标准实现,在该模型中,我在最后的时间步中获取隐藏状态,然后将其通过线性层以进行预测。

代码语言:javascript
代码运行次数:0
运行
复制
import torch.nn as nn class BERTGRUSentiment(nn.Module):    def __init__(self,                 bert,                 hidden_dim,                 output_dim,                 n_layers,                 bidirectional,                 dropout):                super().__init__()                self.bert = bert                embedding_dim = bert.config.to_dict()['hidden_size']                self.rnn = nn.GRU(embedding_dim,                          hidden_dim,                          num_layers = n_layers,                          bidirectional = bidirectional,                          batch_first = True,                          dropout = 0 if n_layers < 2 else dropout)                self.out = nn.Linear(hidden_dim * 2 if bidirectional else hidden_dim, output_dim)                self.dropout = nn.Dropout(dropout)            def forward(self, text):                #text = [batch size, sent len]                        with torch.no_grad():            embedded = self.bert(text)[0]                        #embedded = [batch size, sent len, emb dim]                _, hidden = self.rnn(embedded)                #hidden = [n layers * n directions, batch size, emb dim]                if self.rnn.bidirectional:            hidden = self.dropout(torch.cat((hidden[-2,:,:], hidden[-1,:,:]), dim = 1))        else:            hidden = self.dropout(hidden[-1,:,:])                        #hidden = [batch size, hid dim]                output = self.out(hidden)                #output = [batch size, out dim]                return output

接下来,使用标准超参数创建模型的实例。

代码语言:javascript
代码运行次数:0
运行
复制
HIDDEN_DIM = 256OUTPUT_DIM = 1N_LAYERS = 2BIDIRECTIONAL = TrueDROPOUT = 0.25 model = BERTGRUSentiment(bert,                         HIDDEN_DIM,                         OUTPUT_DIM,                         N_LAYERS,                         BIDIRECTIONAL,                         DROPOUT)

可以检查模型有多少个参数。 标准模型有5M以下,但这个有112M!幸运的是,这些参数中的110M来自transformer,将不再对其进行训练。

代码语言:javascript
代码运行次数:0
运行
复制
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')

该模型具有112,241,409个可训练参数

为了冻结参数(不训练它们),需要将其require_grad属性设置为False。为此,只需要遍历模型中的所有named_parameters,如果它们是berttransformer模型的一部分,则可以将设置为require_grad = False

代码语言:javascript
代码运行次数:0
运行
复制
for name, param in model.named_parameters():                    if name.startswith('bert'):        param.requires_grad = False

现在可以看到模型具有3M可训练的参数,几乎可以与FastText模型相提并论。但是,文本仍必须通过transformer传播,这会使训练花费更长的时间。

代码语言:javascript
代码运行次数:0
运行
复制
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')

该模型具有2,759,169个可训练参数

可以仔细检查可训练参数的名称,以确保它们有意义。 它们都是GRU(rnn)和线性层(out)的所有参数。

代码语言:javascript
代码运行次数:0
运行
复制
for name, param in model.named_parameters():                    if param.requires_grad:        print(name)rnn.weight_ih_l0rnn.weight_hh_l0rnn.bias_ih_l0rnn.bias_hh_l0rnn.weight_ih_l0_reversernn.weight_hh_l0_reversernn.bias_ih_l0_reversernn.bias_hh_l0_reversernn.weight_ih_l1rnn.weight_hh_l1rnn.bias_ih_l1rnn.bias_hh_l1rnn.weight_ih_l1_reversernn.weight_hh_l1_reversernn.bias_ih_l1_reversernn.bias_hh_l1_reverseout.weightout.bias

训练模型

按照标准,定义优化器和标准(损失函数)。

代码语言:javascript
代码运行次数:0
运行
复制
import torch.optim as optim optimizer = optim.Adam(model.parameters())
代码语言:javascript
代码运行次数:0
运行
复制
criterion = nn.BCEWithLogitsLoss()

将模型和标准放置到GPU上(如果可用)

代码语言:javascript
代码运行次数:0
运行
复制
model = model.to(device)criterion = criterion.to(device)

接下来,将定义以下功能:计算准确性,执行训练时间,执行评估时间以及计算训练/评估时间需要多长时间。

代码语言:javascript
代码运行次数:0
运行
复制
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
代码语言:javascript
代码运行次数:0
运行
复制
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)
代码语言:javascript
代码运行次数:0
运行
复制
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)
代码语言:javascript
代码运行次数:0
运行
复制
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

最后,将训练模型。 由于transformer的尺寸,与以前的任何型号相比,所需时间要长得多。即使没有训练任何变压器参数,仍然需要通过模型传递数据,这在标准GPU上花费了大量时间。

代码语言:javascript
代码运行次数:0
运行
复制
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(), 'tut6-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}%')

Epoch: 01 | Epoch Time: 7m 27s

Train Loss: 0.286 | Train Acc: 88.16%

Val. Loss: 0.247 | Val. Acc: 90.26%

Epoch: 02 | Epoch Time: 7m 27s

Train Loss: 0.234 | Train Acc: 90.77%

Val. Loss: 0.229 | Val. Acc: 91.00%

Epoch: 03 | Epoch Time: 7m 27s

Train Loss: 0.209 | Train Acc: 91.83%

Val. Loss: 0.225 | Val. Acc: 91.10%

Epoch: 04 | Epoch Time: 7m 27s

Train Loss: 0.182 | Train Acc: 92.97%

Val. Loss: 0.217 | Val. Acc: 91.98%

Epoch: 05 | Epoch Time: 7m 27s

Train Loss: 0.156 | Train Acc: 94.17%

Val. Loss: 0.230 | Val. Acc: 91.76%

将加载带来最大验证损失的参数,并在测试集上进行尝试-到目前为止,带来了最好的结果!

代码语言:javascript
代码运行次数:0
运行
复制
model.load_state_dict(torch.load('tut6-model.pt')) test_loss, test_acc = evaluate(model, test_iterator, criterion) print(f'Test Loss: {test_loss:.3f} | Test Acc: {test_acc*100:.2f}%')

Test Loss: 0.198 | Test Acc: 92.31%

推理

然后,将使用该模型来测试某些序列的情绪。 对输入序列进行标记化,将其修剪到最大长度,在任一侧添加特殊标记,将其转换为张量,添加伪造的批次尺寸,然后将其传递给模型。

代码语言:javascript
代码运行次数:0
运行
复制
def predict_sentiment(model, tokenizer, sentence):    model.eval()    tokens = tokenizer.tokenize(sentence)    tokens = tokens[:max_input_length-2]    indexed = [init_token_idx] + tokenizer.convert_tokens_to_ids(tokens) + [eos_token_idx]    tensor = torch.LongTensor(indexed).to(device)    tensor = tensor.unsqueeze(0)    prediction = torch.sigmoid(model(tensor))    return prediction.item()
代码语言:javascript
代码运行次数:0
运行
复制
predict_sentiment(model, tokenizer, "This film is terrible")

0.02264496125280857

代码语言:javascript
代码运行次数:0
运行
复制
predict_sentiment(model, tokenizer, "This film is great")

0.9411056041717529

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档