深度学习第七篇---Pytorch 循环神经网络-实现情感极性判定

之前所学的全连接神经网络(DNN)和卷积神经网络(CNN),他们的前一个输入和后一个输入是没有关系的(从输入层到隐含层再到输出层,层与层之间是全连接的,每层之间的节点是无连接的)。如下面这样,输出层X,经过隐藏层,到输出层Y,通过调节权重Win和Wout就可以实现学习的效果。

但是当我们处理序列信息的时候,某些前面的输入和后面的输入是有关系的,比如:当我们在理解一句话意思时,孤立的理解这句话的每个词是不够的,我们需要处理这些词连接起来的整个序列;这个时候我们就需要使用到循环神经网络(Recurrent Neural Network)。比如:手机坏了,我要买一个256g苹果,结合前面的手机坏了,这个苹果含义是一台手机,而不是不是吃的苹果。

1 基本循环神经网络RNN

X是输入向量,O是输出,S是隐藏层,U是输入到隐藏层的权重矩阵,V是隐藏层到输出层的权重矩阵,循环神经网络的隐藏层的值s不仅仅取决于当前这次的输入x,还取决于上一次隐藏层的值s。权重矩阵w就是隐藏层上一次的值作为这一次的输入的权重。上面的图可以在时间维度进行展开成一个链式的结构。

这个网络在t时刻接收到输入Xt之后,隐藏层的值是St,输出值是Ot。关键一点是,St的值不仅仅取决于Xt,还取决于St-1。

隐藏层计算公式为:St=f(UXt + W * St-1),其中f为激活函数。
输出层计算公式为:Ot = g(V
St),其中g为激活函数,V是权重矩阵。

结合前面的例子,“手机坏了,我要买一个256g苹果”,被分词之后,成一组向量[X1,X2,...,X6]


循环神经网络从左到右阅读这个句子,不断调用相同的RNN CELL来处理。但是上面方法有一个明显的缺陷,当阅读的句子很长的时候,网络会变得复杂甚至无效。当前面的信息在传递到后面的同时,信息的权重会下降(梯度爆炸和梯度消失),导致预测不准。比如下面两句话,was和were要根据前面的student的单复数来确定。句子过长的情况下,就难以判定了,因此RNN这种网络被称为短时记忆网络(Short Term Memory)。

he student,who got A+ in the exam,was excellent.
The students,who got A+ in the exam,were excellent.

2 LSTM循环神经网络

 为了解决上面记忆信息不足的问题,引入一种长短时记忆网络(Long Short Term Memory,LSTM)。原始RNN的隐藏层只有一个状态,即h,它对于短期的输入非常敏感。那么如果我们再增加一个门(gate)机制用于控制特征的流通和损失,即c,让它来保存长期的状态。

左边是不同时刻的X,中间黄色球是隐藏层H,右边绿色是输出Y,和基本RNN相比,除了黄色的链条(Short Term Memory),LSTM增加了一个新的红色链条,用C来表示,叫LongTerm Memory,且两个链条相互作用,相关更新,将这两条线“拍平”后如下:


我们在学习的时候,会经常看到下面这幅图,比较难以理解,主要是二维的图难以想象成三维的结构。理解上面三维结构的两条线,就知道下面的二维图两条线是怎么进行数据更新的。

现在正式介绍LSTM中三个重要的门结构。

2.1 遗忘门

函数f1是sigmoid函数,可以把矩阵的值压缩到0-1之间,矩阵元素相乘的时候,因为任何数乘以 0 都得 0,这部分信息就会剔除掉。同样的,任何数乘以 1 都得到它本身,这部分信息就会完美地保存下来。这样网络就能了解哪些数据是需要遗忘,哪些数据是需要保存。


数据更新公式:


与基本RNN的内部结构计算非常相似,首先将当前时间步输入x(t)与上一个时间步隐含状态h(t—1)拼接,得到[x(t),h(t—1)],然后通过一个全连接层做变换,最后通过sigmoid函数进行激活得到f1(t),我们可以将f1(t)看作是门值,好比一扇门开合的大小程度,门值都将作用在通过该扇门的张量,遗忘门门值将作用的上一层的细胞状态上,代表遗忘过去的多少信息,又因为遗忘门门值是由x(t),h(t—1)计算得来的,因此整个公式意味着根据当前时间步输入和上一个时间步隐含状态h(t—1)来决定遗忘多少上一层的细胞状态所携带的过往信息。

2.2 输入门

我们看到输入门的计算公式有两个,第一个就是产生输入门门值的公式,它和遗忘门公式几乎相同,区别只是在于它们之后要作用的目标上,这个公式意味着输入信息有多少需要进行过滤。输入门的第二个公式是与传统RNN的内部结构计算相同。对于LSTM来讲,它得到的是当前的细胞状态,而不是像经典RNN一样得到的是隐含状态。最后,第一个公式f1与上一次的Ct-1做全连接然后,加上f2之后的结果,更新给当前的Ct,这个过程被称为LSTM的细胞状态更新。

2.3 输出门


输出门部分的公式也是两个,第一个即是计算输出门的门值,它和遗忘门,输入门计算方式相同。第二个即是使用这个门值产生隐含状态h(t),他将作用在更新后的细胞状态C(t)上,并做tanh激活,最终得到h(t)作为下一时间步输入的一部分.整个输出门的过程,就是为了产生隐含状态h(t)。

上面的过程用代码表示就是

# 定义一个LSTM模型
class LSTM(nn.Module):
   def __init__(self, input_size, hidden_size, num_layers, output_size):
       super(LSTM, self).__init__()
       self.hidden_size = hidden_size
       self.num_layers = num_layers
       self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
       self.fc = nn.Linear(hidden_size, output_size)
       
   def forward(self, x):
       # 初始化隐藏状态h0, c0为全0向量
       h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
       c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
       # 将输入x和隐藏状态(h0, c0)传入LSTM网络
       out, _ = self.lstm(x, (h0, c0))
       # 取最后一个时间步的输出作为LSTM网络的输出
       out = self.fc(out[:, -1, :])
       return out

3 BiLSTM神经网络

双向循环神经网络(Bi-Directional Long Short-Term Memory,BiLSTM)是一种特殊的循环神经网络(RNN)架构,它包含一个正向LSTM 层和一个反向LSTM层。这两个LSTM层分别对序列中的元素进行正向和反向传递,并在最后的隐藏层中进行合并。这样,BiLSTM可以同时考虑序列中的历史信息和未来信息,使得它在处理序列数据任务中(如文本分类和序列标注)有着良好的表现。

前向的LSTML依次输入“我”,“爱”,“你”得到三个向量{h0,h1,h2}。后向的LSTMR依次输入“你”,“爱”,“我”得到三个向量{h5,h4,h3}。最后将前向和后向的隐向量进行拼接得到{[h0,h5],[h1,h4],[h2,h3]},即{A,B,C},对于情感分类任务来说,我们采用的句子表示往往是[h2,h5],因为这其中包含了前向和后向的所有信息。

4 电影评价的极性分析实践

4.1 划分训练集、测试集

import torch
import torch.nn.functional as F
from torchtext import data
from torchtext import datasets
from torchtext.legacy import data, datasets
import time
import random
torch.backends.cudnn.deterministic = True

# 定义超参数
RANDOM_SEED = 123
torch.manual_seed(RANDOM_SEED)

VOCABULARY_SIZE = 20000
LEARNING_RATE = 1e-3
BATCH_SIZE = 128
NUM_EPOCHS = 15
DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')


if __name__ == '__main__':
   # 注意:由于 RNN 只能处理序列中的非 padded 元素(即非0数据)
   # 对于任何 padded 元素输出都是 0 。所以在准备数据的时候将include_length设置为True
   # 以获得句子的实际长度。
   TEXT = data.Field(tokenize='spacy', include_lengths=True, tokenizer_language='en_core_web_sm')
   LABEL = data.LabelField(dtype=torch.float)
   train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)
   datasets.IMDB.splits(TEXT,LABEL)

   # 从训练集中选取部分做验证集
   train_data, valid_data = train_data.split(random_state=random.seed(RANDOM_SEED), split_ratio=0.8)

   print(f'Num Train: {len(train_data)}')
   print(f'Num Valid: {len(valid_data)}')
   print(f'Num Test: {len(test_data)}')
   print("train_data[0:200]", test_data.examples[0].text[0:100])
Num Train: 20000
Num Valid: 5000
Num Test: 25000
train_data[0:200] ['Based', 'on', 'an', 'actual', 'story', ',', 'John', 'Boorman', 'shows', 'the', 'struggle', 'of', 'an', 'American', 'doctor', ',', 'whose', 'husband', 'and', 'son', 'were', 'murdered', 'and', 'she', 'was', 'continually', 'plagued', 'with', 'her', 'loss', '.', 'A', 'holiday', 'to', 'Burma', 'with', 'her', 'sister', 'seemed', 'like', 'a', 'good', 'idea', 'to', 'get', 'away', 'from', 'it', 'all', ',', 'but', 'when', 'her', 'passport', 'was', 'stolen', 'in', 'Rangoon', ',', 'she', 'could', 'not', 'leave', 'the', 'country', 'with', 'her', 'sister', ',', 'and', 'was', 'forced', 'to', 'stay', 'back', 'until', 'she', 'could', 'get', 'I.D.', 'papers', 'from', 'the', 'American', 'embassy', '.', 'To', 'fill', 'in', 'a', 'day', 'before', 'she', 'could', 'fly', 'out', ',', 'she', 'took', 'a']

4.2 创建词向量

TEXT.build_vocab(train_data, max_size=VOCABULARY_SIZE)
LABEL.build_vocab(train_data)

print(f'Vocabulary size: {len(TEXT.vocab)}')
print(f'Number of classes: {len(LABEL.vocab)}')

输出:

Vocabulary size: 20002
Number of classes: 2

TEXT.build_vocab表示从预训练的词向量中,将当前训练数据中的词汇的词向量抽取出来,构成当前训练集的 Vocab(词汇表)。对于当前词向量语料库中没有出现的单词(记为UNK)。

4.3 创建数据迭代器

BATCH_SIZE = 64

# 根据当前环境选择是否调用GPU进行训练
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# 创建数据迭代器
train_loader, valid_loader, test_loader = data.BucketIterator.splits(
   (train_data, valid_data, test_data),
   batch_size=BATCH_SIZE,
   sort_within_batch=True,  # 为了 packed_padded_sequence
   device=device)

4.4 定义RNN模型

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_length):
       embedded = self.embedding(text)
       # pack_padded_sequence 技术的应用
       packed = torch.nn.utils.rnn.pack_padded_sequence(embedded, text_length)
       output, hidden = self.rnn(packed)
       #squeeze(0)的作用是将张量中维度大小为1的维度进行压缩,减少张量的维度数量和大小。
       #如果张量中没有维度大小为1的维度,那么squeeze(0)函数不会对张量进行任何修改,它会返回与原始张量相同的张量
       # view(-1)将张量重塑为一维形状
       return self.fc(hidden.squeeze(0)).view(-1)

关于pack_padded_sequence(处理Pad问题)的解释:
"Pad问题"是指填充操作中的一个常见问题,即如何处理填充元素(通常用特殊的占位符,如<pad>)对模型训练和推理的影响。我们需要对电影评论进行情感分类,这些评论往往具有不同长度的单词数量。当我们将这些评论句子作为输入传递给循环神经网络(RNN)进行处理时,由于RNN的输入需要是固定长度的张量,我们需要对序列进行填充(padding)操作,使得每个评论都具有相同的长度。

假设我们有三个电影评论,分别是"这是一部很好看的电影","这个电影一般般"和"我不喜欢这部电影"。我们可以将这些评论编码为以下张量:

评论1: [这, 是, 一部, 很, 好看, 的, 电影]
评论2: [这个, 电影, 一般般]
评论3: [我, 不喜欢, 这部, 电影]

在这个例子中,我们有3个电影评论。它们的长度分别是7、3和4。我们需要将它们填充到相同的长度,以便能够将它们作为一个批次输入到模型中。填充后的序列是:

评论1: [这, 是, 一部, 很, 好看, 的, 电影]
评论2: [这个, 电影, 一般般, <pad>, <pad>, <pad>, <pad>]
评论3: [我, 不喜欢, 这部, 电影, <pad>, <pad>, <pad>]

4.5 RNN模型训练

INPUT_DIM = len(TEXT.vocab)
EMBEDDING_DIM = 128
HIDDEN_DIM = 256
OUTPUT_DIM = 1

torch.manual_seed(RANDOM_SEED)
model = RNN(INPUT_DIM, EMBEDDING_DIM, HIDDEN_DIM, OUTPUT_DIM)
model = model.to(DEVICE)
#选择Adam优化器
optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)

start_time = time.time()

for epoch in range(NUM_EPOCHS):
   model.train()
   for batch_idx, batch_data in enumerate(train_loader):
       text, text_lengths = batch_data.text
       logits = model(text, text_lengths)
       cost = F.binary_cross_entropy_with_logits(logits, batch_data.label)
       optimizer.zero_grad()
       cost.backward()
       optimizer.step()

       if not batch_idx % 50:
           print(f'Epoch: {epoch + 1:03d}/{NUM_EPOCHS:03d} | '
                 f'Batch {batch_idx:03d}/{len(train_loader):03d} | '
                 f'Cost: {cost:.4f}')

4.6 RNN模型评估

def compute_binary_accuracy(model, data_loader, device):
   model.eval()
   correct_pred, num_examples = 0, 0
   with torch.no_grad():
       for batch_idx, batch_data in enumerate(data_loader):
           text, text_lengths = batch_data.text
           logits = model(text, text_lengths)
           predicted_labels = (torch.sigmoid(logits) > 0.5).long()
           num_examples += batch_data.label.size(0)
           correct_pred += (predicted_labels == batch_data.label.long()).sum()
       return correct_pred.float() / num_examples * 100


def predict_sentiment(model, sentence):
   model.eval()
   tokenized = [tok.text for tok in nlp.tokenizer(sentence)]
   indexed = [TEXT.vocab.stoi[t] for t in tokenized]
   length = [len(indexed)]
   tensor = torch.LongTensor(indexed).to(DEVICE)
   tensor = tensor.unsqueeze(1)
   length_tensor = torch.LongTensor(length)
   prediction = torch.sigmoid(model(tensor, length_tensor))
   return prediction.item()
with torch.set_grad_enabled(False):
    print(f'training accuracy: '
                 f'{compute_binary_accuracy(model, train_loader, DEVICE):.2f}%'
                 f'\nvalid accuracy: '
                 f'{compute_binary_accuracy(model, valid_loader, DEVICE):.2f}%')

print(f'Time elapsed: {(time.time() - start_time) / 60:.2f} min')
print(f'Total Training Time: {(time.time() - start_time) / 60:.2f} min')
print(f'Test accuracy: {compute_binary_accuracy(model, test_loader, DEVICE):.2f}%')

nlp = spacy.load('en_core_web_sm')
ret = predict_sentiment(model, "I really love this movie. This movie is so great!")
print("ret=", ret)

输出:

Num Train: 20000
Num Valid: 5000
Num Test: 25000
train_data[0:200] ['Based', 'on', 'an', 'actual', 'story', ',', 'John', 'Boorman', 'shows', 'the', 'struggle', 'of', 'an', 'American', 'doctor', ',', 'whose', 'husband', 'and', 'son', 'were', 'murdered', 'and', 'she', 'was', 'continually', 'plagued', 'with', 'her', 'loss', '.', 'A', 'holiday', 'to', 'Burma', 'with', 'her', 'sister', 'seemed', 'like', 'a', 'good', 'idea', 'to', 'get', 'away', 'from', 'it', 'all', ',', 'but', 'when', 'her', 'passport', 'was', 'stolen', 'in', 'Rangoon', ',', 'she', 'could', 'not', 'leave', 'the', 'country', 'with', 'her', 'sister', ',', 'and', 'was', 'forced', 'to', 'stay', 'back', 'until', 'she', 'could', 'get', 'I.D.', 'papers', 'from', 'the', 'American', 'embassy', '.', 'To', 'fill', 'in', 'a', 'day', 'before', 'she', 'could', 'fly', 'out', ',', 'she', 'took', 'a']
Vocabulary size: 20002
Number of classes: 2
Epoch: 001/004 | Batch 000/313 | Cost: 0.7078
Epoch: 001/004 | Batch 050/313 | Cost: 0.6911
Epoch: 001/004 | Batch 100/313 | Cost: 0.6901
Epoch: 001/004 | Batch 150/313 | Cost: 0.6965
Epoch: 001/004 | Batch 200/313 | Cost: 0.6274
Epoch: 001/004 | Batch 250/313 | Cost: 0.6855
Epoch: 001/004 | Batch 300/313 | Cost: 0.6413
training accuracy: 66.27%
valid accuracy: 65.26%
Time elapsed: 6.34 min
Epoch: 002/004 | Batch 000/313 | Cost: 0.6546
Epoch: 002/004 | Batch 050/313 | Cost: 0.6024
Epoch: 002/004 | Batch 100/313 | Cost: 0.6676
Epoch: 002/004 | Batch 150/313 | Cost: 0.6437
Epoch: 002/004 | Batch 200/313 | Cost: 0.6236
Epoch: 002/004 | Batch 250/313 | Cost: 0.6862
Epoch: 002/004 | Batch 300/313 | Cost: 0.5634
training accuracy: 54.29%
valid accuracy: 52.32%
Time elapsed: 12.72 min
Epoch: 003/004 | Batch 000/313 | Cost: 0.6892
Epoch: 003/004 | Batch 050/313 | Cost: 0.6420
Epoch: 003/004 | Batch 100/313 | Cost: 0.6250
Epoch: 003/004 | Batch 150/313 | Cost: 0.6815
Epoch: 003/004 | Batch 200/313 | Cost: 0.5970
Epoch: 003/004 | Batch 250/313 | Cost: 0.6502
Epoch: 003/004 | Batch 300/313 | Cost: 0.5945
training accuracy: 68.32%
valid accuracy: 61.98%
Time elapsed: 19.41 min
Epoch: 004/004 | Batch 000/313 | Cost: 0.5901
Epoch: 004/004 | Batch 050/313 | Cost: 0.3887
Epoch: 004/004 | Batch 100/313 | Cost: 0.6483
Epoch: 004/004 | Batch 150/313 | Cost: 0.5912
Epoch: 004/004 | Batch 200/313 | Cost: 0.5973
Epoch: 004/004 | Batch 250/313 | Cost: 0.4288
Epoch: 004/004 | Batch 300/313 | Cost: 0.4574
training accuracy: 75.49%
valid accuracy: 65.98%
Time elapsed: 25.52 min
Total Training Time: 25.52 min
Test accuracy: 65.84%
ret= 0.9203115105628967

0.9大于0.5 代表是积极的观点,但是测试集上准确率只有65.84%。

4.7 定义LSTM模型

class LSTM(nn.Module):
   def __init__(self, input_dim, embedding_dim, hidden_dim, output_dim):
       super().__init__()
       self.embedding = nn.Embedding(input_dim, embedding_dim)
       self.lstm = nn.LSTM(embedding_dim, hidden_dim)
       self.fc = nn.Linear(hidden_dim, output_dim)

   def forward(self, text, text_length):
       embedded = self.embedding(text)
       packed = torch.nn.utils.rnn.pack_padded_sequence(embedded, text_length)
       packed_output, (hidden, cell) = self.lstm(packed)

       return self.fc(hidden.unsqueeze(0)).view(-1)

4.8 LSTM 模型训练和评估


model2 = LSTM(INPUT_DIM, EMBEDDING_DIM, HIDDEN_DIM, OUTPUT_DIM).to(DEVICE)
optimizer2 = torch.optim.Adam(model2.parameters(), lr=LEARNING_RATE)
start_time = time.time()
for epoch in range(NUM_EPOCHS):
   model2.train()
   for batch_idx, batch_data in enumerate(train_loader):
       text, text_lengths = batch_data.text
       logits = model2(text, text_lengths)
       cost2 = F.binary_cross_entropy_with_logits(logits, batch_data.label)
       optimizer2.zero_grad()
       cost2.backward()
       optimizer2.step()
       
       if not batch_idx % 50:
           print (f'Epoch: {epoch+1:03d}/{NUM_EPOCHS:03d} | '
                  f'Batch {batch_idx:03d}/{len(train_loader):03d} | '
                  f'Cost: {cost2:.4f}')

   with torch.set_grad_enabled(False):
       print(f'training accuracy: '
             f'{compute_binary_accuracy(model2, train_loader, DEVICE):.2f}%'
             f'\nvalid accuracy: '
             f'{compute_binary_accuracy(model2, valid_loader, DEVICE):.2f}%')
       
   print(f'Time elapsed: {(time.time() - start_time)/60:.2f} min')
   
print(f'Total Training Time: {(time.time() - start_time)/60:.2f} min')
print(f'Test accuracy: {compute_binary_accuracy(model2, test_loader, DEVICE):.2f}%')

输出:

Num Train: 20000
Num Valid: 5000
Num Test: 25000
train_data[0:200] ['Based', 'on', 'an', 'actual', 'story', ',', 'John', 'Boorman', 'shows', 'the', 'struggle', 'of', 'an', 'American', 'doctor', ',', 'whose', 'husband', 'and', 'son', 'were', 'murdered', 'and', 'she', 'was', 'continually', 'plagued', 'with', 'her', 'loss', '.', 'A', 'holiday', 'to', 'Burma', 'with', 'her', 'sister', 'seemed', 'like', 'a', 'good', 'idea', 'to', 'get', 'away', 'from', 'it', 'all', ',', 'but', 'when', 'her', 'passport', 'was', 'stolen', 'in', 'Rangoon', ',', 'she', 'could', 'not', 'leave', 'the', 'country', 'with', 'her', 'sister', ',', 'and', 'was', 'forced', 'to', 'stay', 'back', 'until', 'she', 'could', 'get', 'I.D.', 'papers', 'from', 'the', 'American', 'embassy', '.', 'To', 'fill', 'in', 'a', 'day', 'before', 'she', 'could', 'fly', 'out', ',', 'she', 'took', 'a']
Vocabulary size: 20002
Number of classes: 2
Epoch: 001/010 | Batch 000/313 | Cost: 0.6930
Epoch: 001/010 | Batch 050/313 | Cost: 0.6436
Epoch: 001/010 | Batch 100/313 | Cost: 0.6402
Epoch: 001/010 | Batch 150/313 | Cost: 0.5405
Epoch: 001/010 | Batch 200/313 | Cost: 0.6803
Epoch: 001/010 | Batch 250/313 | Cost: 0.6905
Epoch: 001/010 | Batch 300/313 | Cost: 0.6695
training accuracy: 56.28%
valid accuracy: 56.62%
Time elapsed: 73.09 min
Epoch: 002/010 | Batch 000/313 | Cost: 0.6772
Epoch: 002/010 | Batch 050/313 | Cost: 0.6866
Epoch: 002/010 | Batch 100/313 | Cost: 0.6674
Epoch: 002/010 | Batch 150/313 | Cost: 0.6037
Epoch: 002/010 | Batch 200/313 | Cost: 0.6808
Epoch: 002/010 | Batch 250/313 | Cost: 0.6685
Epoch: 002/010 | Batch 300/313 | Cost: 0.6927
training accuracy: 50.33%
valid accuracy: 50.40%
Time elapsed: 148.10 min
Epoch: 003/010 | Batch 000/313 | Cost: 0.7443
Epoch: 003/010 | Batch 050/313 | Cost: 0.6509
Epoch: 003/010 | Batch 100/313 | Cost: 0.6160
Epoch: 003/010 | Batch 150/313 | Cost: 0.6501
Epoch: 003/010 | Batch 200/313 | Cost: 0.5341
Epoch: 003/010 | Batch 250/313 | Cost: 0.4378
Epoch: 003/010 | Batch 300/313 | Cost: 0.4366
training accuracy: 84.29%
valid accuracy: 81.76%
Time elapsed: 218.03 min
Epoch: 004/010 | Batch 000/313 | Cost: 0.3864
Epoch: 004/010 | Batch 050/313 | Cost: 0.2678
Epoch: 004/010 | Batch 100/313 | Cost: 0.2225
Epoch: 004/010 | Batch 150/313 | Cost: 0.3614
Epoch: 004/010 | Batch 200/313 | Cost: 0.2415
Epoch: 004/010 | Batch 250/313 | Cost: 0.1816
Epoch: 004/010 | Batch 300/313 | Cost: 0.2577
training accuracy: 91.74%
valid accuracy: 86.48%
Time elapsed: 285.13 min
Test accuracy: 90.72%
ret= 0.9601113557815552 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容