专栏首页相约机器人用于情感分析的Transformers

用于情感分析的Transformers

本文首次介绍的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安装:

pip install pytorch-transformers

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

!pip install pytorch-transformers

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

准备资料

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

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的令牌生成器来获得此功能。

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属性,其中包含将要使用的实际词汇。可以通过检查其长度来检查其中有多少个令牌。

len(tokenizer.vocab)

30522

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

tokens = tokenizer.tokenize('Hello WORLD how ARE yoU?')
print(tokens)['hello', 'world', 'how', 'are', 'you', '?']

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

indexes = tokenizer.convert_tokens_to_ids(tokens)
print(indexes)

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

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

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

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]

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

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

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

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个令牌。

max_input_length = tokenizer.max_model_input_sizes['bert-base-uncased']
print(max_input_length)

512

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

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],这是因为序列已经转换为索引。

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

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)

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

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

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

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将这些索引转换回可读的令牌。

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', '}']

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

LABEL.build_vocab(train_data)
print(LABEL.vocab.stoi)

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

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

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)

建立模型

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

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实际上返回整个序列的嵌入以及合并的输出。文档指出,合并的输出“通常不是输入语义内容的良好总结,通常最好对整个输入序列的隐藏状态序列进行平均或合并”,因此将不使用它。正向传递的其余部分是递归模型的标准实现,在该模型中,我在最后的时间步中获取隐藏状态,然后将其通过线性层以进行预测。

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

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

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,将不再对其进行训练。

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

for name, param in model.named_parameters():                    if name.startswith('bert'):        param.requires_grad = False

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

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)的所有参数。

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

训练模型

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

import torch.optim as optim optimizer = optim.Adam(model.parameters())
criterion = nn.BCEWithLogitsLoss()

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

model = model.to(device)criterion = criterion.to(device)

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

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
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)
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

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

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%

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

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%

推理

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

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()
predict_sentiment(model, tokenizer, "This film is terrible")

0.02264496125280857

predict_sentiment(model, tokenizer, "This film is great")

0.9411056041717529

本文分享自微信公众号 - 相约机器人(xiangyuejiqiren),作者:代码医生

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • TensorFlow 2.0入门

    谷歌于2019年3月6日和7日在其年度TensorFlow开发者峰会上发布了最新版本的TensorFlow机器学习框架。这一新版本使用TensorFlow的方式...

    代码医生工作室
  • 强化学习的三种范例(Three Paradigms of Reinforcement Learning)

    “基于模型的方法比没有模型的方法更具样本效率。”近年来,这种经常重复的格言在几乎所有基于模型的RL论文(包括Jacob论文)中都引起关注。如此常识,没有人甚至不...

    代码医生工作室
  • 使用Flask部署ML模型

    https://github.com/schmidtbri/using-ml-model-abc?source=post_page---------------...

    代码医生工作室
  • 针对分布式或集群session同步问题,改用jwt的续期解决方案

    在前后分离场景下,越来越多的项目使用token作为接口的安全机制,APP端或者WEB端(使用VUE、REACTJS等构建)使用token与后端接口交互,以达到安...

    梦_之_旅
  • 数据科学行业的8个关键角色:职责与技能

    第二届世界互联网大会的召开,将大数据战略推向了又一高潮,许多与数据相关的职位如雨后春笋般涌现,数据科学家、数据分析师、数据架构师、统计学家、数据库管理员、商业分...

    华章科技
  • 数据科学行业的8个关键角色:职责与技能

    大数据文摘
  • Qt5.2中使用ping命令实现Ip扫描功能

    在实现类似于Free IP Scanner 2.1的Ip扫描器软件中,会用到ping命令。如果使用Qt编程实现,主要会用QThread、QProcess这两个...

    ccf19881030
  • 腾讯云网站备案号是什么?

    腾讯云网站备案号是什么及如何使用?国内网站备案时都会听到一个名词:备案号,大家都是初次备案所以老魏分享相关知识和遇到问题如何解决。

    魏艾斯博客www.vpsss.net
  • Reddit热议:一道看似简单的分类基础问题,为何难倒一大片人?

    在机器学习领域,二元分类器问题可以说是一个非常基础的领域,基于二元分类器的模型,已经成为目前多个常见分类模型应用构建的基础。但就是这样一个基础问题,最近却难住了...

    新智元
  • 【TS 演化史 -- 15】可选的 catch 语句变量 和 JSX 片段语法

    TypeScript 2.4 为标识符实现了拼写纠正机制。即使咱们稍微拼错了一个变量、属性或函数名,TypeScript 在很多情况下都可以提示正确的拼写。

    前端小智@大迁世界

扫码关注云+社区

领取腾讯云代金券