TensorFlow 深度学习第二版:6~10(1)

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: TensorFlow 深度学习第二版:6~10(1)

六、RNN 和梯度消失或爆炸问题

较深层的梯度计算为多层网络中许多激活函数梯度的乘积。当这些梯度很小或为零时,它很容易消失。另一方面,当它们大于 1 时,它可能会爆炸。因此,计算和更新变得非常困难。

让我们更详细地解释一下:

  • 如果权重较小,则可能导致称为消失梯度的情况,其中梯度信号变得非常小,以至于学习变得非常慢或完全停止工作。这通常被称为消失梯度。
  • 如果该矩阵中的权重很大,则可能导致梯度信号太大而导致学习发散的情况。这通常被称为爆炸梯度。

因此,RNN 的一个主要问题是消失或爆炸梯度问题,它直接影响表现。事实上,反向传播时间推出了 RNN,创建了一个非常深的前馈神经网络。从 RNN 获得长期背景的不可能性正是由于这种现象:如果梯度在几层内消失或爆炸,网络将无法学习数据之间的高时间距离关系。

下图显示了发生的情况:计算和反向传播的梯度趋于在每个时刻减少(或增加),然后,在一定数量的时刻之后,成本函数趋于收敛到零(或爆炸到无穷大) )。

我们可以通过两种方式获得爆炸梯度。由于激活函数的目的是通过压缩它们来控制网络中的重大变化,因此我们设置的权重必须是非负的和大的。当这些权重沿着层次相乘时,它们会导致成本的大幅变化。当我们的神经网络模型学习时,最终目标是最小化成本函数并改变权重以达到最优成本。

例如,成本函数是均方误差。它是一个纯凸函数,目的是找到凸起的根本原因。如果你的权重增加到一定量,那么下降的时刻就会增加,我们会反复超过最佳状态,模型永远不会学习!

在上图中,我们有以下参数:

  • θ表示隐藏的循环层的参数
  • θ[x]表示隐藏层的输入参数
  • θ[y]表示输出层的参数
  • σ表示隐藏层的激活函数
  • 输入表示为X[t]
  • 隐藏层的输出为h[t]
  • 最终输出为o[t]
  • t(时间步长)

注意,上图表示下面给出的循环神经网络模型的时间流逝。现在,如果你回忆一下图 1,输出可以表示如下:

现在让E代表输出层的损失:E = f(O[t])。然后,上述三个方程告诉我们E取决于输出O[t]。输出O[t]相对于层的隐藏状态(h[t])的变化而变化。当前时间步长(h[t])的隐藏状态取决于先前时间步长(h[t-1])的神经元状态。现在,下面的等式将清除这个概念。

相对于为隐藏层选择的参数的损失变化率= ∂E/∂θ,这是一个可以表述如下的链规则:

(I)

在前面的等式中,项∂h[t]/∂h[k]不仅有趣而且有用。

(II)

现在,让我们考虑t = 5k = 1然后

(III)

微分方程(II)相对于(h[t-1])给出了:

(IV)

现在,如果我们将方程(III)和(IV)结合起来,我们可以得到以下结果:

在这些情况下,θ也随着时间步长而变化。上面的等式显示了当前状态相对于先前状态的依赖性。现在让我们解释这两个方程的解剖。假设您处于时间步长 5(t = 5),那么k的范围从 1 到 5(k = 1到 5),这意味着您必须为以下内容计算k):

现在来看上面的每一个等式(II)

而且,它取决于循环层的参数θ。如果在训练期间你的权重变大,那么由于每个时间步长的等式(I)(II)的乘法,它们将会出现梯度爆炸的问题。

为了克服消失或爆炸问题,已经提出了基本 RNN 模型的各种扩展。将在下一节介绍的 LSTM 网络就是其中之一。

LSTM 网络

一种 RNN 模型是 LSTM。 LSTM 的精确实现细节不在本书的范围内。 LSTM 是一种特殊的 RNN 架构,最初由 Hochreiter 和 Schmidhuber 于 1997 年构思。

最近在深度学习的背景下重新发现了这种类型的神经网络,因为它没有消失梯度的问题,并且提供了出色的结果和表现。基于 LSTM 的网络是时间序列的预测和分类的理想选择,并且正在取代许多传统的深度学习方法。

这个名称意味着短期模式不会被遗忘。 LSTM 网络由彼此链接的单元(LSTM 块)组成。每个 LSTM 块包含三种类型的门:输入门,输出门和遗忘门,它们分别实现对单元存储器的写入,读取和复位功能。这些门不是二元的,而是模拟的(通常由映射在[0, 1]范围内的 Sigmoid 激活函数管理,其中 0 表示总抑制,1 表示总激活)。

如果你认为 LSTM 单元是一个黑盒子,它可以像基本单元一样使用,除了它会表现得更好;训练将更快地收敛,它将检测数据中的长期依赖性。在 TensorFlow 中,您只需使用BasicLSTMCell代替BasicRNNCell

lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(num_units=n_neurons)

LSTM 单元管理两个状态向量,并且出于表现原因,它们默认保持独立。您可以通过在创建BasicLSTMCell时设置state_is_tuple=False来更改此默认行为。那么,LSTM 单元如何工作?基本 LSTM 单元的架构如下图所示:

图 11:LSTM 单元的框图

现在,让我们看看这个架构背后的数学符号。如果我们不查看 LSTM 框内的内容,LSTM 单元本身看起来就像常规存储单元,除了它的状态被分成两个向量,h(t)c(t)

  • h(t)是短期状态
  • c(t)是长期状态

现在,让我们打开盒子吧!关键的想法是网络可以学习以下内容:

  • 在长期的状态中存储什么
  • 扔掉什么
  • 怎么读它

由于长期c(t)从左到右穿过网络,你可以看到它首先通过一个遗忘门,丢弃一些内存,然后它添加一些新的存储器通过加法运算(增加了输入门选择的存储器)。结果c(t)直接发送,没有任何进一步的变换

因此,在每个时间步骤,都会丢弃一些内存并添加一些内存。此外,在加法运算之后,长期状态被复制并通过 tanh 函数,该函数产生[-1, +1]范围内的输出。

然后输出门过滤结果。这会产生短期h(t)(等于此时间步的单元输出y(t))。现在,让我们来看看新记忆的来源以及大门如何运作。首先,当前输入x(t)和之前的短路h(t-1)被馈送到四个不同的完全连接。这些门的存在允许 LSTM 单元无限期地记住信息:事实上,如果输入门低于激活阈值,单元格将保持先前的状态,如果启用当前状态,它将与输入值组合。顾名思义,遗忘门重置单元的当前状态(当其值被清除为 0 时),输出门决定是否必须执行单元的值。

以下等式用于对单个实例的单元的长期状态,其短期状态及其在每个时间步的输出进行 LSTM 计算:

在前面的方程中,W[xi]W[xf]W[xo]W[xg]是四个层中每个层的权重矩阵,用于与输入向量x(t)连接。另一方面,W[hi]W[hf]W[ho],和W[hg]是四层中每一层的权重矩阵,它们与先前的短期状态有关。b[i]b[f]b[o]b[g]是四层中每一层的偏差项。 TensorFlow 初始化它们为一个全 1 的向量而不是全 0 的向量。这可以防止它在训练开始时遗忘一切。

GRU 单元

LSTM 单元还有许多其他变体。一种特别流行的变体是门控循环单元(GRU)。 Kyunghyun Cho 和其他人在 2014 年的论文中提出了 GRU 单元,该论文还介绍了我们前面提到的自编码器网络。

从技术上讲,GRU 单元是 LSTM 单元的简化版本,其中两个状态向量合并为一个称为h(t)的向量。单个门控制器控制遗忘门和输入门。如果门控制器的输出为 1,则输入门打开并且遗忘门关闭。

图 12:GRU 单元

另一方面,如果输出为 0,则相反。每当必须存储存储器时,首先擦除存储它的位置,这实际上是 LSTM 单元本身的常见变体。第二种简化是因为在每个时间步输出满状态向量,所以没有输出门。但是,新的门控制器控制先前状态的哪一部分将显示给主层。

以下等式用于为单个实例,在每个时间步计算 GRU 单元的长期状态,其短期状态及其输出的:

在 TensorFlow 中创建 GRU 单元非常简单。这是一个例子:

gru_cell = tf.nn.rnn_cell.GRUCell(num_units=n_neurons)

这些简化并不是这种架构的弱点;它似乎成功地执行。 LSTM 或 GRU 单元是近年来 RNN 成功背后的主要原因之一,特别是在 NLP 中的应用。

我们将在本章中看到使用 LSTM 的示例,但下一节将介绍使用 RNN 进行垃圾邮件/火腿文本分类的示例。

实现 RNN 进行垃圾邮件预测

在本节中,我们将看到如何在 TensorFlow 中实现 RNN 来预测文本中的垃圾邮件。

数据描述和预处理

将使用来自 UCI ML 仓库的流行垃圾数据集,可从此链接下载amcollection.zip

该数据集包含来自多个电子邮件的文本,其中一些被标记为垃圾邮件。在这里,我们将训练一个模型,该模型将学习仅使用电子邮件文本区分垃圾邮件和非垃圾邮件。让我们开始导入所需的库和模型:

import os
import re
import io
import requests
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from zipfile import ZipFile
from tensorflow.python.framework import ops
import warnings

另外,如果您需要,我们可以停止打印由 TensorFlow 产生的警告:

warnings.filterwarnings("ignore")
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
ops.reset_default_graph()

现在,让我们为图创建 TensorFlow 会话:

sess = tf.Session()

下一个任务是设置 RNN 参数:

epochs = 300
batch_size = 250
max_sequence_length = 25
rnn_size = 10
embedding_size = 50
min_word_frequency = 10
learning_rate = 0.0001
dropout_keep_prob = tf.placeholder(tf.float32)

让我们手动下载数据集并将其存储在temp目录的text_data.txt文件中。首先,我们设置路径:

data_dir = 'temp'
data_file = 'text_data.txt'
if not os.path.exists(data_dir):
    os.makedirs(data_dir)

现在,我们直接以压缩格式下载数据集:

if not os.path.isfile(os.path.join(data_dir, data_file)):
    zip_url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip'
    r = requests.get(zip_url)
    z = ZipFile(io.BytesIO(r.content))
    file = z.read('SMSSpamCollection')

我们仍然需要格式化数据:

text_data = file.decode()
    text_data = text_data.encode('ascii',errors='ignore')
    text_data = text_data.decode().split('\n')

现在,在文本文件中存储前面提到的目录:

with open(os.path.join(data_dir, data_file), 'w') as file_conn:
        for text in text_data:
            file_conn.write("{}\n".format(text))
else:
    text_data = []
    with open(os.path.join(data_dir, data_file), 'r') as file_conn:
        for row in file_conn:
            text_data.append(row)
    text_data = text_data[:-1]

让我们分开单词长度至少为 2 的单词:

text_data = [x.split('\t') for x in text_data if len(x)>=1]
[text_data_target, text_data_train] = [list(x) for x in zip(*text_data)]

现在我们创建一个文本清理函数:

def clean_text(text_string):
    text_string = re.sub(r'([^\s\w]|_|[0-9])+', '', text_string)
    text_string = " ".join(text_string.split())
    text_string = text_string.lower()
    return(text_string)

我们调用前面的方法来清理文本:

text_data_train = [clean_text(x) for x in text_data_train]

现在我们需要做一个最重要的任务,即创建单词嵌入 - 将文本更改为数字向量:

vocab_processor = tf.contrib.learn.preprocessing.VocabularyProcessor(max_sequence_length, min_frequency=min_word_frequency)
text_processed = np.array(list(vocab_processor.fit_transform(text_data_train)))

现在让我们随意改变数据集的平衡:

text_processed = np.array(text_processed)
text_data_target = np.array([1 if x=='ham' else 0 for x in text_data_target])
shuffled_ix = np.random.permutation(np.arange(len(text_data_target)))
x_shuffled = text_processed[shuffled_ix]
y_shuffled = text_data_target[shuffled_ix]

现在我们已经改组了数据,我们可以将数据分成训练和测试集:

ix_cutoff = int(len(y_shuffled)*0.75)
x_train, x_test = x_shuffled[:ix_cutoff], x_shuffled[ix_cutoff:]
y_train, y_test = y_shuffled[:ix_cutoff], y_shuffled[ix_cutoff:]
vocab_size = len(vocab_processor.vocabulary_)
print("Vocabulary size: {:d}".format(vocab_size))
print("Training set size: {:d}".format(len(y_train)))
print("Test set size: {:d}".format(len(y_test)))

以下是上述代码的输出:

>>>
Vocabulary size: 933
Training set size: 4180
Test set size: 1394

在我们开始训练之前,让我们为 TensorFlow 图创建占位符:

x_data = tf.placeholder(tf.int32, [None, max_sequence_length])
y_output = tf.placeholder(tf.int32, [None])

让我们创建嵌入:

embedding_mat = tf.get_variable("embedding_mat", shape=[vocab_size, embedding_size], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)
embedding_output = tf.nn.embedding_lookup(embedding_mat, x_data)

现在是构建我们的 RNN 的时候了。以下代码定义了 RNN 单元:

cell = tf.nn.rnn_cell.BasicRNNCell(num_units = rnn_size)
output, state = tf.nn.dynamic_rnn(cell, embedding_output, dtype=tf.float32)
output = tf.nn.dropout(output, dropout_keep_prob)

现在让我们定义从 RNN 序列获取输出的方法:

output = tf.transpose(output, [1, 0, 2])
last = tf.gather(output, int(output.get_shape()[0]) - 1)

接下来,我们定义 RNN 的权重和偏置:

weight = bias = tf.get_variable("weight", shape=[rnn_size, 2], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)
bias = tf.get_variable("bias", shape=[2], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)

然后定义logits输出。它使用前面代码中的权重和偏置:

logits_out = tf.nn.softmax(tf.matmul(last, weight) + bias)

现在我们定义每个预测的损失,以便稍后,它们可以为损失函数做出贡献:

losses = tf.nn.sparse_softmax_cross_entropy_with_logits_v2(logits=logits_out, labels=y_output)

然后我们定义损失函数:

loss = tf.reduce_mean(losses)

我们现在定义每个预测的准确率:

accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(logits_out, 1), tf.cast(y_output, tf.int64)), tf.float32))

然后我们用RMSPropOptimizer创建training_op

optimizer = tf.train.RMSPropOptimizer(learning_rate)
train_step = optimizer.minimize(loss)

现在让我们使用global_variables_initializer()方法初始化所有变量 :

init_op = tf.global_variables_initializer()
sess.run(init_op)

此外,我们可以创建一些空列表来跟踪每个周期的训练损失,测试损失,训练准确率和测试准确率:

train_loss = []
test_loss = []
train_accuracy = []
test_accuracy = []

现在我们已准备好进行训练,让我们开始吧。训练的工作流程如下:

  • 打乱训练数据
  • 选择训练集并计算周期
  • 为每个批次运行训练步骤
  • 运行损失和训练的准确率
  • 运行评估步骤。

以下代码包括上述所有步骤:

shuffled_ix = np.random.permutation(np.arange(len(x_train)))
    x_train = x_train[shuffled_ix]
    y_train = y_train[shuffled_ix]
    num_batches = int(len(x_train)/batch_size) + 1
    for i in range(num_batches):
        min_ix = i * batch_size
        max_ix = np.min([len(x_train), ((i+1) * batch_size)])
        x_train_batch = x_train[min_ix:max_ix]
        y_train_batch = y_train[min_ix:max_ix]
        train_dict = {x_data: x_train_batch, y_output: \
y_train_batch, dropout_keep_prob:0.5}
        sess.run(train_step, feed_dict=train_dict)
        temp_train_loss, temp_train_acc = sess.run([loss,\ 
                         accuracy], feed_dict=train_dict)
    train_loss.append(temp_train_loss)
    train_accuracy.append(temp_train_acc)
    test_dict = {x_data: x_test, y_output: y_test, \ 
dropout_keep_prob:1.0}
    temp_test_loss, temp_test_acc = sess.run([loss, accuracy], \
                    feed_dict=test_dict)
    test_loss.append(temp_test_loss)
    test_accuracy.append(temp_test_acc)
    print('Epoch: {}, Test Loss: {:.2}, Test Acc: {:.2}'.format(epoch+1, temp_test_loss, temp_test_acc))
print('\nOverall accuracy on test set (%): {}'.format(np.mean(temp_test_acc)*100.0))

以下是前面代码的输出:

>>>
Epoch: 1, Test Loss: 0.68, Test Acc: 0.82
Epoch: 2, Test Loss: 0.68, Test Acc: 0.82
Epoch: 3, Test Loss: 0.67, Test Acc: 0.82
Epoch: 997, Test Loss: 0.36, Test Acc: 0.96
Epoch: 998, Test Loss: 0.36, Test Acc: 0.96
Epoch: 999, Test Loss: 0.35, Test Acc: 0.96
Epoch: 1000, Test Loss: 0.35, Test Acc: 0.96
Overall accuracy on test set (%): 96.19799256324768

做得好! RNN 的准确率高于 96%,非常出色。现在让我们观察损失如何在每次迭代中传播并随着时间的推移:

epoch_seq = np.arange(1, epochs+1)
plt.plot(epoch_seq, train_loss, 'k--', label='Train Set')
plt.plot(epoch_seq, test_loss, 'r-', label='Test Set')
plt.title('RNN training/test loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend(loc='upper left')
plt.show()

图 13:a)每个周期的 RNN 训练和测试损失 b)每个周期的测试精度

我们还随时间绘制准确率:

plt.plot(epoch_seq, train_accuracy, 'k--', label='Train Set')
plt.plot(epoch_seq, test_accuracy, 'r-', label='Test Set')
plt.title('Test accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='upper left')
plt.show()

下一个应用使用时间序列数据进行预测建模。我们还将看到如何开发更复杂的 RNN,称为 LSTM 网络。

开发时间序列数据的预测模型

RNN,特别是 LSTM 模型,通常是一个难以理解的主题。由于数据中的时间依赖性,时间序列预测是 RNN 的有用应用。时间序列数据可在线获取。在本节中,我们将看到使用 LSTM 处理时间序列数据的示例。我们的 LSTM 网络将能够预测未来的航空公司乘客数量。

数据集的描述

我将使用的数据集是 1949 年至 1960 年国际航空公司乘客的数据。该数据集可以从[此链接](https://datamarket.com/data/set/22u3/international-airlinepassengers- monthly-totals-in#!ds=22u3&display=line)。以下屏幕截图显示了国际航空公司乘客的元数据:

图 14:国际航空公司乘客的元数据(来源:https://datamarket.com/

您可以通过选择“导出”选项卡,然后在“导出”组中选择 CSV 来下载数据。您必须手动编辑 CSV 文件以删除标题行以及其他页脚行。我已经下载并保存了名为international-airline-passengers.csv的数据文件。下图是时间序列数据的一个很好的图:

图 15:国际航空公司乘客:1 月 49 日至 12 月 60 日的月度总数为千人

预处理和探索性分析

现在让我们加载原始数据集并查看一些事实。首先,我们加载时间序列如下(见time_series_preprocessor.py):

import csv
import numpy as np

在这里,我们可以看到load_series()的签名,它是一个用户定义的方法,可以加载时间序列并对其进行正则化:

def load_series(filename, series_idx=1):
    try:
        with open(filename) as csvfile:
            csvreader = csv.reader(csvfile)
            data = [float(row[series_idx]) for row in csvreader if len(row) > 0]
            normalized_data = (data - np.mean(data)) / np.std(data)
        return normalized_data
    except IOError:
       Print("Error occurred")
        return None

现在让我们调用前面的方法加载时间序列并打印(在终端上发出$ python3 plot_time_series.py)数据集中的序列号:

import csv
import numpy as np
import matplotlib.pyplot as plt
import time_series_preprocessor as tsp
timeseries = tsp.load_series('international-airline-passengers.csv')
print(timeseries)

以下是前面代码的输出:

>>>
[-1.40777884 -1.35759023 -1.24048348 -1.26557778 -1.33249593 -1.21538918
 -1.10664719 -1.10664719 -1.20702441 -1.34922546 -1.47469699 -1.35759023
…..
 2.85825285  2.72441656  1.9046693   1.5115252   0.91762667  1.26894693]
print(np.shape(timeseries))
>>>
144

这意味着时间序列中有144条目。让我们绘制时间序列:

plt.figure()
plt.plot(timeseries)
plt.title('Normalized time series')
plt.xlabel('ID')
plt.ylabel('Normalized value')
plt.legend(loc='upper left')
plt.show()

以下是上述代码的输出:

>>>

图 16:时间序列(y 轴,标准化值与 x 轴,ID)

加载时间序列数据集后,下一个任务是准备训练集。由于我们将多次评估模型以预测未来值,因此我们将数据分为训练和测试。更具体地说,split_data()函数将数据集划分为两个部分,用于训练和测试,75% 用于训练,25% 用于测试:

def split_data(data, percent_train):
    num_rows = len(data)
    train_data, test_data = [], []
    for idx, row in enumerate(data):
        if idx < num_rows * percent_train:
            train_data.append(row)
        else:
            test_data.append(row)
    return train_data, test_data

LSTM 预测模型

一旦我们准备好数据集,我们就可以通过以可接受的格式加载数据来训练预测器。在这一步中,我编写了一个名为TimeSeriesPredictor.py的 Python 脚本,它首先导入必要的库和模块(在此脚本的终端上发出$ python3 TimeSeriesPredictor.py命令):

import numpy as np
import tensorflow as tf
from tensorflow.python.ops import rnn, rnn_cell
import time_series_preprocessor as tsp
import matplotlib.pyplot as plt

接下来,我们为 LSTM 网络定义超参数(相应地调整它):

input_dim = 1
seq_size = 5
hidden_dim = 5

我们现在定义权重变量(无偏差)和输入占位符:

W_out = tf.get_variable("W_out", shape=[hidden_dim, 1], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None) 
b_out = tf.get_variable("b_out", shape=[1], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)
x = tf.placeholder(tf.float32, [None, seq_size, input_dim])
y = tf.placeholder(tf.float32, [None, seq_size])

下一个任务是构建 LSTM 网络。以下方法LSTM_Model()采用三个参数,如下所示:

  • x:大小为[T, batch_size, input_size]的输入
  • W:完全连接的输出层权重矩阵
  • b:完全连接的输出层偏置向量

现在让我们看一下方法的签名:

def LSTM_Model():
        cell = rnn_cell.BasicLSTMCell(hidden_dim)
        outputs, states = rnn.dynamic_rnn(cell, x, dtype=tf.float32)
        num_examples = tf.shape(x)[0]
        W_repeated = tf.tile(tf.expand_dims(W_out, 0), [num_examples, 1, 1])
        out = tf.matmul(outputs, W_repeated) + b_out
        out = tf.squeeze(out)
        return out

此外,我们创建了三个空列表来存储训练损失,测试损失和步骤:

train_loss = []
test_loss = []
step_list = []

下一个名为train()的方法用于训练 LSTM 网络:

def trainNetwork(train_x, train_y, test_x, test_y):
        with tf.Session() as sess:
            tf.get_variable_scope().reuse_variables()
            sess.run(tf.global_variables_initializer())
            max_patience = 3
            patience = max_patience
            min_test_err = float('inf')
            step = 0
            while patience > 0:
                _, train_err = sess.run([train_op, cost], feed_dict={x: train_x, y: train_y})
                if step % 100 == 0:
                    test_err = sess.run(cost, feed_dict={x: test_x, y: test_y})
                    print('step: {}\t\ttrain err: {}\t\ttest err: {}'.format(step, train_err, test_err))
                    train_loss.append(train_err)
                    test_loss.append(test_err)
                    step_list.append(step)
                    if test_err < min_test_err:
                        min_test_err = test_err
                        patience = max_patience
                    else:
                        patience -= 1
                step += 1
            save_path = saver.save(sess, 'model.ckpt')
            print('Model saved to {}'.format(save_path))

接下来的任务是创建成本优化器并实例化training_op

cost = tf.reduce_mean(tf.square(LSTM_Model()- y))
train_op = tf.train.AdamOptimizer(learning_rate=0.003).minimize(cost)

另外,这里有一个叫做保存模型的辅助op

saver = tf.train.Saver()

现在我们已经创建了模型,下一个方法称为testLSTM(),用于测试模型在测试集上的预测能力:

def testLSTM(sess, test_x):
        tf.get_variable_scope().reuse_variables()
        saver.restore(sess, 'model.ckpt')
        output = sess.run(LSTM_Model(), feed_dict={x: test_x})
        return output

为了绘制预测结果,我们有一个名为plot_results()的函数。签名如下:

def plot_results(train_x, predictions, actual, filename):
    plt.figure()
    num_train = len(train_x)
    plt.plot(list(range(num_train)), train_x, color='b', label='training data')
    plt.plot(list(range(num_train, num_train + len(predictions))), predictions, color='r', label='predicted')
    plt.plot(list(range(num_train, num_train + len(actual))), actual, color='g', label='test data')
    plt.legend()
    if filename is not None:
        plt.savefig(filename)
    else:
        plt.show()

模型评估

为了评估模型,我们有一个名为main()的方法,它实际上调用前面的方法来创建和训练 LSTM 网络。代码的工作流程如下:

  1. 加载数据
  2. 在时间序列数据中滑动窗口以构建训练数据集
  3. 执行相同的窗口滑动策略来构建测试数据集
  4. 在训练数据集上训练模型
  5. 可视化模型的表现

让我们看看方法的签名:

def main():
    data = tsp.load_series('international-airline-passengers.csv')
    train_data, actual_vals = tsp.split_data(data=data, percent_train=0.75)
    train_x, train_y = [], []
    for i in range(len(train_data) - seq_size - 1):
        train_x.append(np.expand_dims(train_data[i:i+seq_size], axis=1).tolist())
        train_y.append(train_data[i+1:i+seq_size+1])
    test_x, test_y = [], []
    for i in range(len(actual_vals) - seq_size - 1):
        test_x.append(np.expand_dims(actual_vals[i:i+seq_size], axis=1).tolist())
        test_y.append(actual_vals[i+1:i+seq_size+1])
    trainNetwork(train_x, train_y, test_x, test_y)
    with tf.Session() as sess:
        predicted_vals = testLSTM(sess, test_x)[:,0]
        # Following prediction results of the model given ground truth values
        plot_results(train_data, predicted_vals, actual_vals, 'ground_truth_predition.png')
        prev_seq = train_x[-1]
        predicted_vals = []
        for i in range(1000):
            next_seq = testLSTM(sess, [prev_seq])
            predicted_vals.append(next_seq[-1])
            prev_seq = np.vstack((prev_seq[1:], next_seq[-1]))
        # Following predictions results where only the training data was given
        plot_results(train_data, predicted_vals, actual_vals, 'prediction_on_train_set.png')
>>>

最后,我们将调用main()方法来执行训练。训练完成后,它进一步绘制模型的预测结果,包括地面实况值与预测结果,其中只给出了训练数据:

>>>

图 17:模型对地面实况值的结果

下图显示了训练数据的预测结果。此过程可用的信息较少,但它仍然可以很好地匹配数据中的趋势:

图 18:训练集上模型的结果

以下方法帮助我们绘制训练和测试误差:

def plot_error():
    # Plot training loss over time
    plt.plot(step_list, train_loss, 'r--', label='LSTM training loss per iteration', linewidth=4)
    plt.title('LSTM training loss per iteration')
    plt.xlabel('Iteration')
    plt.ylabel('Training loss')
    plt.legend(loc='upper right')
    plt.show()
    # Plot test loss over time
    plt.plot(step_list, test_loss, 'r--', label='LSTM test loss per iteration', linewidth=4)
    plt.title('LSTM test loss per iteration')
    plt.xlabel('Iteration')
    plt.ylabel('Test loss')
    plt.legend(loc='upper left')
    plt.show()

现在我们调用上面的方法如下:

plot_error()
>>>

图 19:a)每次迭代的 LSTM 训练损失,b)每次迭代的 LSTM 测试损失

我们可以使用时间序列预测器来重现数据中的实际波动。现在,您可以准备自己的数据集并执行其他一些预测分析。下一个示例是关于产品和电影评论数据集的情感分析。我们还将了解如何使用 LSTM 网络开发更复杂的 RNN。

用于情感分析的 LSTM 预测模型

情感分析是 NLP 中使用最广泛的任务之一。 LSTM 网络可用于将短文本分类为期望的类别,即分类问题。例如,一组推文可以分为正面或负面。在本节中,我们将看到这样一个例子。

网络设计

实现的 LSTM 网络将具有三层:嵌入层,RNN 层和 softmax 层。从下图可以看到对此的高级视图。在这里,我总结了所有层的功能:

  • 嵌入层:我们将在第 8 章中看到一个示例,显示文本数据集不能直接馈送到深度神经网络(DNN),因此一个名为嵌入层是必需的。对于该层,我们将每个输入(k 个单词的张量)变换为 k 个 N 维向量的张量。这称为字嵌入,其中 N 是嵌入大小。每个单词都与在训练过程中需要学习的权重向量相关联。您可以在单词的向量表示中更深入地了解单词嵌入。
  • RNN 层:一旦我们构建了嵌入层,就会有一个名为 RNN 层的新层,它由带有压降包装的 LSTM 单元组成。在训练过程中需要学习 LSTM 权重,如前几节所述。动态展开 RNN 层(如图 4 所示),将 k 个字嵌入作为输入并输出 k 个 M 维向量,其中 M 是 LSTM 单元的隐藏大小。
  • Softmax 或 Sigmoid 层:RNN 层的输出在k个时间步长上平均,获得大小为M的单个张量。最后,例如,softmax 层用于计算分类概率。

    图 20:用于情感分析的 LSTM 网络的高级视图

稍后我们将看到交叉熵如何用作损失函数,RMSProp是最小化它的优化器。

LSTM 模型训练

UMICH SI650 - 情感分类数据集(删除了重复)包含有关密歇根大学捐赠的产品和电影评论的数据,可以从此链接下载。在获取令牌之前,已经清除了不需要的或特殊的字符(参见data.csv文件)。

以下脚本还会删除停用词(请参阅data_preparation.py)。给出一些标记为阴性或阳性的样本(1 为正面,0 为负面):

情感 情感文本
1 达芬奇密码书真棒。
1 我很喜欢达芬奇密码。
0 天哪,我讨厌断背山。
0 我讨厌哈利波特。

表 1:情感数据集的样本

现在,让我们看一下为此任务训练 LSTM 网络的分步示例。首先,我们导入必要的模块和包(执行train.py文件):

from data_preparation import Preprocessing
from lstm_network import LSTM_RNN_Network
import tensorflow as tf
import pickle
import datetime
import time
import os
import matplotlib.pyplot as plt

在前面的导入声明中,data_preparationlstm_network是两个辅助 Python 脚本,用于数据集准备和网络设计。我们稍后会看到更多细节。现在让我们为 LSTM 定义参数:

data_dir = 'data/' # Data directory containing 'data.csv'
stopwords_file = 'data/stopwords.txt' # Path to stopwords file
n_samples= None # Set n_samples=None to use the whole dataset
# Directory where TensorFlow summaries will be stored'
summaries_dir= 'logs/'
batch_size = 100 #Batch size
train_steps = 1000 #Number of training steps
hidden_size= 75 # Hidden size of LSTM layer
embedding_size = 75 # Size of embeddings layer
learning_rate = 0.01
test_size = 0.2
dropout_keep_prob = 0.5 # Dropout keep-probability
sequence_len = None # Maximum sequence length
validate_every = 100 # Step frequency to validate

我相信前面的参数是不言自明的。下一个任务是准备 TensorBoard 使用的摘要:

summaries_dir = '{0}/{1}'.format(summaries_dir, datetime.datetime.now().strftime('%d_%b_%Y-%H_%M_%S'))
train_writer = tf.summary.FileWriter(summaries_dir + '/train')
validation_writer = tf.summary.FileWriter(summaries_dir + '/validation')

现在让我们准备模型目录:

model_name = str(int(time.time()))
model_dir = '{0}/{1}'.format(checkpoints_root, model_name)
if not os.path.exists(model_dir):
    os.makedirs(model_dir)

接下来,让我们准备数据并构建 TensorFlow 图(参见data_preparation.py文件):

data_lstm = Preprocessing(data_dir=data_dir,
                 stopwords_file=stopwords_file,
                 sequence_len=sequence_len,
                 test_size=test_size,
                 val_samples=batch_size,
                 n_samples=n_samples,
                 random_state=100)

在前面的代码段中,Preprocessing是一个继续的类(详见data_preparation.py)几个函数和构造器,它们帮助我们预处理训练和测试集以训练 LSTM 网络。在这里,我提供了每个函数及其功能的代码。

该类的构造器初始化数据预处理器。此类提供了一个接口,用于将数据加载,预处理和拆分为训练,验证和测试集。它需要以下参数:

  • data_dir:包含数据集文件data.csv的数据目录,其中包含名为SentimentTextSentiment的列。
  • stopwords_file:可选。如果提供,它将丢弃原始数据中的每个停用词。
  • sequence_len:可选。如果m是数据集中的最大序列长度,则需要sequence_len >= m。如果sequence_lenNone,则会自动分配给m
  • n_samples:可选。它是从数据集加载的样本数(对大型数据集很有用)。如果n_samplesNone,则将加载整个数据集(注意;如果数据集很大,则可能需要一段时间来预处理每个样本)。
  • test_size:可选。0 < test_size < 1。它表示要包含在测试集中的数据集的比例(默认值为0.2)。
  • val_samples:可选但可用于表示验证样本的绝对数量(默认为100)。
  • random_state:这是随机种子的可选参数,用于将数据分成训练,测试和验证集(默认为0)。
  • ensure_preprocessed:可选。如果ensure_preprocessed=True,它确保数据集已经过预处理(默认为False)。

构造器的代码如下:

def __init__(self, data_dir, stopwords_file=None, sequence_len=None, n_samples=None, test_size=0.2, val_samples=100, random_state=0, ensure_preprocessed=False):
        self._stopwords_file = stopwords_file
        self._n_samples = n_samples
        self.sequence_len = sequence_len
        self._input_file = os.path.join(data_dir, 'data.csv')
        self._preprocessed_file=os.path.join(data_dir,"preprocessed_"+str(n_samples)+ ".npz")
        self._vocab_file = os.path.join(data_dir,"vocab_" + str(n_samples) + ".pkl")
        self._tensors = None
        self._sentiments = None
        self._lengths = None
        self._vocab = None
        self.vocab_size = None
        # Prepare data
        if os.path.exists(self._preprocessed_file)and os.path.exists(self._vocab_file):
            print('Loading preprocessed files ...')
            self.__load_preprocessed()
        else:
            if ensure_preprocessed:
                raise ValueError('Unable to findpreprocessed files.')
            print('Reading data ...')
            self.__preprocess()
        # Split data in train, validation and test sets
        indices = np.arange(len(self._sentiments))
        x_tv, self._x_test, y_tv, self._y_test,tv_indices, test_indices = train_test_split(
            self._tensors,
            self._sentiments,
            indices,
            test_size=test_size,
            random_state=random_state,
            stratify=self._sentiments[:, 0])
            self._x_train,self._x_val,self._y_train,self._y_val,train_indices,val_indices= train_test_split(x_tv, y_tv, tv_indices, test_size=val_samples,random_state = random_state,
               stratify=y_tv[:, 0])
        self._val_indices = val_indices
        self._test_indices = test_indices
        self._train_lengths = self._lengths[train_indices]
        self._val_lengths = self._lengths[val_indices]
        self._test_lengths = self._lengths[test_indices]
        self._current_index = 0
        self._epoch_completed = 0

现在让我们看看前面方法的签名。我们从_preprocess()方法开始,该方法从data_dir / data.csv加载数据,预处理每个加载的样本,并存储中间文件以避免以后进行预处理。工作流程如下:

  1. 加载数据
  2. 清理示例文本
  3. 准备词汇词典
  4. 删除最不常见的单词(它们可能是语法错误),将样本编码为张量,并根据sequence_len用零填充每个张量
  5. 保存中间文件
  6. 存储样本长度以备将来使用

现在让我们看看下面的代码块,它代表了前面的工作流程:

def __preprocess(self):
    data = pd.read_csv(self._input_file, nrows=self._n_samples)
    self._sentiments = np.squeeze(data.as_matrix(columns=['Sentiment']))
    self._sentiments = np.eye(2)[self._sentiments]
    samples = data.as_matrix(columns=['SentimentText'])[:, 0]
    samples = self.__clean_samples(samples)
    vocab = dict()
    vocab[''] = (0, len(samples))  # add empty word
    for sample in samples:
        sample_words = sample.split()
        for word in list(set(sample_words)):  # distinct words
            value = vocab.get(word)
            if value is None:
                vocab[word] = (-1, 1)
            else:
                encoding, count = value
                vocab[word] = (-1, count + 1)
      sample_lengths = []
      tensors = []
      word_count = 1
      for sample in samples:
          sample_words = sample.split()
          encoded_sample = []
          for word in list(set(sample_words)):  # distinct words 
              value = vocab.get(word)
              if value is not None:
                  encoding, count = value
                  if count / len(samples) > 0.0001:
                      if encoding == -1:
                          encoding = word_count
                          vocab[word] = (encoding, count)
                          word_count += 1
                      encoded_sample += [encoding]
                  else:
                      del vocab[word]
          tensors += [encoded_sample]
          sample_lengths += [len(encoded_sample)]
      self.vocab_size = len(vocab)
      self._vocab = vocab
      self._lengths = np.array(sample_lengths)
      self.sequence_len, self._tensors = self.__apply_to_zeros(tensors, self.sequence_len)
      with open(self._vocab_file, 'wb') as f:
          pickle.dump(self._vocab, f)
      np.savez(self._preprocessed_file, tensors=self._tensors, lengths=self._lengths, sentiments=self._sentiments)

接下来,我们调用前面的方法并加载中间文件,避免数据预处理:

def __load_preprocessed(self):
    with open(self._vocab_file, 'rb') as f:
        self._vocab = pickle.load(f)
    self.vocab_size = len(self._vocab)
    load_dict = np.load(self._preprocessed_file)
    self._lengths = load_dict['lengths']
    self._tensors = load_dict['tensors']
    self._sentiments = load_dict['sentiments']
    self.sequence_len = len(self._tensors[0])

一旦我们预处理数据集,下一个任务就是清理样本。工作流程如下:

  1. 准备正则表达式模式。
  2. 清理每个样本。
  3. 恢复 HTML 字符。
  4. 删除@users和 URL。
  5. 转换为小写。
  6. 删除标点符号。
  7. C替换C+(连续出现两次以上的字符)
  8. 删除停用词。

现在让我们以编程方式编写上述步骤。为此,我们有以下函数:

def __clean_samples(self, samples):
    print('Cleaning samples ...')
    ret = []
    reg_punct = '[' + re.escape(''.join(string.punctuation)) + ']'
    if self._stopwords_file is not None:
        stopwords = self.__read_stopwords()
        sw_pattern = re.compile(r'\b(' + '|'.join(stopwords) + r')\b')
    for sample in samples:
        text = html.unescape(sample)
        words = text.split()
        words = [word for word in words if not word.startswith('@') and not word.startswith('http://')]
        text = ' '.join(words)
        text = text.lower()
        text = re.sub(reg_punct, ' ', text)
        text = re.sub(r'([a-z])\1{2,}', r'\1', text)
        if stopwords is not None:
            text = sw_pattern.sub('', text)
        ret += [text]
    return ret

__apply_to_zeros()方法返回使用的padding_length和填充张量的 NumPy 数组。首先,它找到最大长度m,并确保m>=sequence_len。然后根据sequence_len用零填充列表:

def __apply_to_zeros(self, lst, sequence_len=None):
    inner_max_len = max(map(len, lst))
    if sequence_len is not None:
        if inner_max_len > sequence_len:
            raise Exception('Error: Provided sequence length is not sufficient')
        else:
            inner_max_len = sequence_len
result = np.zeros([len(lst), inner_max_len], np.int32)
for i, row in enumerate(lst):
    for j, val in enumerate(row):
        result[i][j] = val
return inner_max_len, result

下一个任务是删除所有停用词(在data / StopWords.txt file中提供)。此方法返回停用词列表:

def __read_stopwords(self):
    if self._stopwords_file is None:
        return None
    with open(self._stopwords_file, mode='r') as f:
        stopwords = f.read().splitlines()
    return stopwords

next_batch()方法将batch_size>0作为包含的样本数,在完成周期后返回批量大小样本(text_tensortext_targettext_length),并随机抽取训练样本:

def next_batch(self, batch_size):
    start = self._current_index
    self._current_index += batch_size
    if self._current_index > len(self._y_train):
        self._epoch_completed += 1
        ind = np.arange(len(self._y_train))
        np.random.shuffle(ind)
        self._x_train = self._x_train[ind]
        self._y_train = self._y_train[ind]
        self._train_lengths = self._train_lengths[ind]
        start = 0
        self._current_index = batch_size
    end = self._current_index
    return self._x_train[start:end], self._y_train[start:end], self._train_lengths[start:end]

然后使用称为get_val_data()的下一个方法来获取在训练期间使用的验证集。它接受原始文本并返回验证数据。默认情况下,它返回original_textoriginal_samplestext_tensortext_targettext_length),否则返回text_tensortext_targettext_length

def get_val_data(self, original_text=False):
    if original_text:
        data = pd.read_csv(self._input_file, nrows=self._n_samples)
        samples = data.as_matrix(columns=['SentimentText'])[:, 0]
        return samples[self._val_indices], self._x_val, self._y_val, self._val_lengths
        return self._x_val, self._y_val, self._val_lengths

最后, 是一个名为get_test_data()的附加方法,用于准备将在模型评估期间使用的测试集:

def get_test_data(self, original_text=False):
        if original_text:
            data = pd.read_csv(self._input_file, nrows=self._n_samples)
            samples = data.as_matrix(columns=['SentimentText'])[:, 0]
            return samples[self._test_indices], self._x_test, self._y_test, self._test_lengths
        return self._x_test, self._y_test, self._test_lengths

现在我们准备数据,以便 LSTM 网络可以提供它:

lstm_model = LSTM_RNN_Network(hidden_size=[hidden_size],
                              vocab_size=data_lstm.vocab_size,
                              embedding_size=embedding_size,
                              max_length=data_lstm.sequence_len,
                              learning_rate=learning_rate)

在前面的代码段中,LSTM_RNN_Network是一个包含多个函数和构造器的类,可帮助我们创建 LSTM 网络。即将推出的构造器构建了 TensorFlow LSTM 模型。它需要以下参数:

  • hidden_size:一个数组,保存 rnn 层的 LSTM 单元中的单元数
  • vocab_size:样本中的词汇量大小
  • embedding_size:将使用此大小的向量对单词进行编码
  • max_length:输入张量的最大长度
  • n_classes:类别的数量
  • learning_rate:RMSProp 算法的学习率
  • random_state:丢弃的随机状态

构造器的代码如下:

def __init__(self, hidden_size, vocab_size, embedding_size, max_length, n_classes=2, learning_rate=0.01, random_state=None):
    # Build TensorFlow graph
    self.input = self.__input(max_length)
    self.seq_len = self.__seq_len()
    self.target = self.__target(n_classes)
    self.dropout_keep_prob = self.__dropout_keep_prob()
    self.word_embeddings = self.__word_embeddings(self.input, vocab_size, embedding_size, random_state)
    self.scores = self.__scores(self.word_embeddings, self.seq_len, hidden_size, n_classes, self.dropout_keep_prob,
                                random_state)
        self.predict = self.__predict(self.scores)
        self.losses = self.__losses(self.scores, self.target)
        self.loss = self.__loss(self.losses)
        self.train_step = self.__train_step(learning_rate, self.loss)
        self.accuracy = self.__accuracy(self.predict, self.target)
        self.merged = tf.summary.merge_all()

下一个函数被称为_input(),它采用一个名为max_length的参数,它是输入张量的最大长度。然后它返回一个输入占位符,其形状为[batch_size, max_length],用于 TensorFlow 计算:

def __input(self, max_length):
        return tf.placeholder(tf.int32, [None, max_length], name='input')

接下来,_seq_len()函数返回一个形状为[batch_size]的序列长度占位符。它保持给定批次中每个张量的实际长度,允许动态序列长度:

def __seq_len(self):
    return tf.placeholder(tf.int32, [None], name='lengths')

下一个函数称为_target()。它需要一个名为n_classes的参数,它包含分类类的数量。最后,它返回形状为[batch_size, n_classes]的目标占位符:

def __target(self, n_classes):
    return tf.placeholder(tf.float32, [None, n_classes], name='target')

_dropout_keep_prob()返回一个持有丢弃的占位符保持概率以减少过拟合:

def __dropout_keep_prob(self):
    return tf.placeholder(tf.float32, name='dropout_keep_prob')

_cell()方法用于构建带有压差包装器的 LSTM 单元。它需要以下参数:

  • hidden_size:它是 LSTM 单元中的单元数
  • dropout_keep_prob:这表示持有丢弃保持概率的张量
  • seed:它是一个可选值,可确保丢弃包装器的随机状态计算的可重现性。

最后,它返回一个带有丢弃包装器的 LSTM 单元:

def __cell(self, hidden_size, dropout_keep_prob, seed=None):
    lstm_cell = tf.nn.rnn_cell.LSTMCell(hidden_size, state_is_tuple=True)
    dropout_cell = tf.nn.rnn_cell.DropoutWrapper(lstm_cell, input_keep_prob=dropout_keep_prob, output_keep_prob = dropout_keep_prob, seed=seed)
        return dropout_cell

一旦我们创建了 LSTM 单元格,我们就可以创建输入标记的嵌入。为此,__word_embeddings()可以解决这个问题。它构建一个形状为[vocab_size, embedding_size]的嵌入层,输入参数如x,它是形状[batch_size, max_length]的输入。vocab_size是词汇量大小,即可能出现在样本中的可能单词的数量。embedding_size是将使用此大小的向量表示的单词,种子是可选的,但确保嵌入初始化的随机状态。

最后,它返回具有形状[batch_size, max_length, embedding_size]的嵌入查找张量:

def __word_embeddings(self, x, vocab_size, embedding_size, seed=None):
    with tf.name_scope('word_embeddings'):
        embeddings = tf.get_variable("embeddings",shape=[vocab_size, embedding_size], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)
        embedded_words = tf.nn.embedding_lookup(embeddings, x)
    return embedded_words

__rnn_layer ()方法创建 LSTM 层。它需要几个输入参数,这里描述:

  • hidden_size:这是 LSTM 单元中的单元数
  • x:这是带形状的输入
  • seq_len:这是具有形状的序列长度张量
  • dropout_keep_prob:这是持有丢弃保持概率的张量
  • variable_scope:这是变量范围的名称(默认层是rnn_layer
  • random_state:这是丢弃包装器的随机状态

最后,它返回形状为[batch_size, max_seq_len, hidden_size]的输出:

def __rnn_layer(self, hidden_size, x, seq_len, dropout_keep_prob, variable_scope=None, random_state=None):
    with tf.variable_scope(variable_scope, default_name='rnn_layer'):
        lstm_cell = self.__cell(hidden_size, dropout_keep_prob, random_state)
        outputs, _ = tf.nn.dynamic_rnn(lstm_cell, x, dtype=tf.float32, sequence_length=seq_len)
    return outputs

_score()方法用于计算网络输出。它需要几个输入参数,如下所示:

  • embedded_words:这是具有形状[batch_size, max_length, embedding_size]的嵌入查找张量
  • seq_len:这是形状[batch_size]的序列长度张量
  • hidden_size:这是一个数组,其中包含每个 RNN 层中 LSTM 单元中的单元数
  • n_classes:这是类别的数量
  • dropout_keep_prob:这是持有丢弃保持概率的张量
  • random_state:这是一个可选参数,但它可用于确保丢弃包装器的随机状态

最后,_score()方法返回具有形状[batch_size, n_classes]的每个类的线性激活:

def __scores(self, embedded_words, seq_len, hidden_size, n_classes, dropout_keep_prob, random_state=None):
    outputs = embedded_words
    for h in hidden_size:
        outputs = self.__rnn_layer(h, outputs, seq_len, dropout_keep_prob)
    outputs = tf.reduce_mean(outputs, axis=[1])
    with tf.name_scope('final_layer/weights'):
        w = tf.get_variable("w", shape=[hidden_size[-1], n_classes], dtype=tf.float32, initializer=None, regularizer=None, trainable=True, collections=None)
        self.variable_summaries(w, 'final_layer/weights')
    with tf.name_scope('final_layer/biases'):
        b = tf.get_variable("b", shape=[n_classes], dtype=tf.float32, initializer=None, regularizer=None,trainable=True, collections=None)
        self.variable_summaries(b, 'final_layer/biases')
        with tf.name_scope('final_layer/wx_plus_b'):
            scores = tf.nn.xw_plus_b(outputs, w, b, name='scores')
            tf.summary.histogram('final_layer/wx_plus_b', scores)
        return scores

_predict()方法将得分作为具有形状[batch_size, n_classes]的每个类的线性激活,并以形状[batch_size, n_classes]返回 softmax(以[0, 1]的比例标准化得分)激活:

def __predict(self, scores):
    with tf.name_scope('final_layer/softmax'):
        softmax = tf.nn.softmax(scores, name='predictions')
        tf.summary.histogram('final_layer/softmax', softmax)
    return softmax

_losses()方法返回具有形状[batch_size]的交叉熵损失(因为 softmax 用作激活函数)。它还需要两个参数,例如得分,作为具有形状[batch_size, n_classes]的每个类的线性激活和具有形状[batch_size, n_classes]的目标张量:

def __losses(self, scores, target):
        with tf.name_scope('cross_entropy'):
            cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(logits=scores, labels=target, name='cross_entropy')
        return cross_entropy

_loss()函数计算并返回平均交叉熵损失。它只需要一个参数,称为损耗,它表示形状[batch_size]的交叉熵损失,并由前一个函数计算:

def __loss(self, losses):
    with tf.name_scope('loss'):
        loss = tf.reduce_mean(losses, name='loss')
        tf.summary.scalar('loss', loss)
    return loss

现在,_train_step()计算并返回RMSProp训练步骤操作。它需要两个参数,learning_rate,这是RMSProp优化器的学习率;和前一个函数计算的平均交叉熵损失:

def __train_step(self, learning_rate, loss):
    return tf.train.RMSPropOptimizer(learning_rate).minimize(loss)

评估表现时,_accuracy()函数计算分类的准确率。它需要三个参数,预测,softmax 激活具有哪种形状[batch_size, n_classes];和具有形状[batch_size, n_classes]的目标张量和当前批次中获得的平均精度:

def __accuracy(self, predict, target):
    with tf.name_scope('accuracy'):
        correct_pred = tf.equal(tf.argmax(predict, 1), tf.argmax(target, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32), name='accuracy')
        tf.summary.scalar('accuracy', accuracy)
    return accuracy

下一个函数被称为initialize_all_variable(),正如您可能猜到的那样,它初始化所有变量:

def initialize_all_variables(self):
    return tf.global_variables_initializer()

最后,我们有一个名为variable_summaries()的静态方法,它将大量摘要附加到 TensorBoard 可视化的张量上。它需要以下参数:

var: is the variable to summarize
mean: mean of the summary name.

签名如下:

@staticmethod
    def variable_summaries(var, name):
        with tf.name_scope('summaries'):
            mean = tf.reduce_mean(var)
            tf.summary.scalar('mean/' + name, mean)
            with tf.name_scope('stddev'):
                stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
            tf.summary.scalar('stddev/' + name, stddev)
            tf.summary.scalar('max/' + name, tf.reduce_max(var))
            tf.summary.scalar('min/' + name, tf.reduce_min(var))
            tf.summary.histogram(name, var)

现在我们需要在训练模型之前创建一个 TensorFlow 会话:

sess = tf.Session()

让我们初始化所有变量:

init_op = tf.global_variables_initializer()
sess.run(init_op)

然后我们保存 TensorFlow 模型以备将来使用:

saver = tf.train.Saver()

现在让我们准备训练集:

x_val, y_val, val_seq_len = data_lstm.get_val_data()

现在我们应该编写 TensorFlow 图计算的日志:

train_writer.add_graph(lstm_model.input.graph)

此外,我们可以创建一些空列表来保存训练损失,验证损失和步骤,以便我们以图形方式查看它们:

train_loss_list = []
val_loss_list = []
step_list = []
sub_step_list = []
step = 0

现在我们开始训练。在每个步骤中,我们记录训练误差。验证误差记录在每个子步骤中:

for i in range(train_steps):
    x_train, y_train, train_seq_len = data_lstm.next_batch(batch_size)
    train_loss, _, summary = sess.run([lstm_model.loss, lstm_model.train_step, lstm_model.merged],
                                      feed_dict={lstm_model.input: x_train,
                                                 lstm_model.target: y_train,
                                                 lstm_model.seq_len: train_seq_len,
                                                 lstm_model.dropout_keep_prob:dropout_keep_prob})
    train_writer.add_summary(summary, i)  # Write train summary for step i (TensorBoard visualization)
    train_loss_list.append(train_loss)
    step_list.append(i)
        print('{0}/{1} train loss: {2:.4f}'.format(i + 1, FLAGS.train_steps, train_loss))
    if (i + 1) %validate_every == 0:
        val_loss, accuracy, summary = sess.run([lstm_model.loss, lstm_model.accuracy, lstm_model.merged],
                                               feed_dict={lstm_model.input: x_val,
                                                          lstm_model.target: y_val,
                                                          lstm_model.seq_len: val_seq_len,
                                                          lstm_model.dropout_keep_prob: 1})
        validation_writer.add_summary(summary, i)  
        print('   validation loss: {0:.4f} (accuracy {1:.4f})'.format(val_loss, accuracy))
        step = step + 1
        val_loss_list.append(val_loss)
        sub_step_list.append(step)

以下是上述代码的输出:

>>>
1/1000 train loss: 0.6883
2/1000 train loss: 0.6879
3/1000 train loss: 0.6943
99/1000 train loss: 0.4870
100/1000 train loss: 0.5307
validation loss: 0.4018 (accuracy 0.9200)
199/1000 train loss: 0.1103
200/1000 train loss: 0.1032
validation loss: 0.0607 (accuracy 0.9800)
299/1000 train loss: 0.0292
300/1000 train loss: 0.0266
validation loss: 0.0417 (accuracy 0.9800)
998/1000 train loss: 0.0021
999/1000 train loss: 0.0007
1000/1000 train loss: 0.0004
validation loss: 0.0939 (accuracy 0.9700)

上述代码打印了训练和验证误差。训练结束后,模型将保存到具有唯一 ID 的检查点目录中:

checkpoint_file = '{}/model.ckpt'.format(model_dir)
save_path = saver.save(sess, checkpoint_file)
print('Model saved in: {0}'.format(model_dir))

TensorFlow 深度学习第二版:6~10(2)https://developer.aliyun.com/article/1426802

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
相关文章
|
11天前
|
机器学习/深度学习 TensorFlow 算法框架/工具
深度学习之格式转换笔记(三):keras(.hdf5)模型转TensorFlow(.pb) 转TensorRT(.uff)格式
将Keras训练好的.hdf5模型转换为TensorFlow的.pb模型,然后再转换为TensorRT支持的.uff格式,并提供了转换代码和测试步骤。
42 3
深度学习之格式转换笔记(三):keras(.hdf5)模型转TensorFlow(.pb) 转TensorRT(.uff)格式
|
1月前
|
机器学习/深度学习 人工智能 算法
鸟类识别系统Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+ResNet50算法模型+图像识别
鸟类识别系统。本系统采用Python作为主要开发语言,通过使用加利福利亚大学开源的200种鸟类图像作为数据集。使用TensorFlow搭建ResNet50卷积神经网络算法模型,然后进行模型的迭代训练,得到一个识别精度较高的模型,然后在保存为本地的H5格式文件。在使用Django开发Web网页端操作界面,实现用户上传一张鸟类图像,识别其名称。
86 12
鸟类识别系统Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+ResNet50算法模型+图像识别
|
8天前
|
机器学习/深度学习 人工智能 算法
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
玉米病害识别系统,本系统使用Python作为主要开发语言,通过收集了8种常见的玉米叶部病害图片数据集('矮花叶病', '健康', '灰斑病一般', '灰斑病严重', '锈病一般', '锈病严重', '叶斑病一般', '叶斑病严重'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。再使用Django搭建Web网页操作平台,实现用户上传一张玉米病害图片识别其名称。
23 0
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
|
1月前
|
机器学习/深度学习 算法 TensorFlow
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
交通标志识别系统。本系统使用Python作为主要编程语言,在交通标志图像识别功能实现中,基于TensorFlow搭建卷积神经网络算法模型,通过对收集到的58种常见的交通标志图像作为数据集,进行迭代训练最后得到一个识别精度较高的模型文件,然后保存为本地的h5格式文件。再使用Django开发Web网页端操作界面,实现用户上传一张交通标志图片,识别其名称。
74 6
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
|
11天前
|
机器学习/深度学习 移动开发 TensorFlow
深度学习之格式转换笔记(四):Keras(.h5)模型转化为TensorFlow(.pb)模型
本文介绍了如何使用Python脚本将Keras模型转换为TensorFlow的.pb格式模型,包括加载模型、重命名输出节点和量化等步骤,以便在TensorFlow中进行部署和推理。
45 0
|
1月前
|
机器学习/深度学习 供应链 TensorFlow
深度学习实战营:TensorFlow+Python,打造你的数据驱动决策引擎
【9月更文挑战第13天】在数据爆炸时代,企业日益依赖精准分析进行决策。深度学习凭借其卓越的特征提取与模式识别能力,成为构建数据驱动决策引擎的关键技术。本项目通过TensorFlow和Python,利用LSTM构建零售业销量预测模型,优化库存管理和营销策略。首先确保安装TensorFlow,然后使用Keras API搭建模型,并通过训练、评估和部署流程,展示深度学习在数据驱动决策中的强大应用潜力,助力企业提升经营效率。
38 3
|
1月前
|
机器学习/深度学习 数据挖掘 TensorFlow
解锁Python数据分析新技能,TensorFlow&PyTorch双引擎驱动深度学习实战盛宴
在数据驱动时代,Python凭借简洁的语法和强大的库支持,成为数据分析与机器学习的首选语言。Pandas和NumPy是Python数据分析的基础,前者提供高效的数据处理工具,后者则支持科学计算。TensorFlow与PyTorch作为深度学习领域的两大框架,助力数据科学家构建复杂神经网络,挖掘数据深层价值。通过Python打下的坚实基础,结合TensorFlow和PyTorch的强大功能,我们能在数据科学领域探索无限可能,解决复杂问题并推动科研进步。
51 0
|
1月前
|
机器学习/深度学习 数据挖掘 TensorFlow
从数据小白到AI专家:Python数据分析与TensorFlow/PyTorch深度学习的蜕变之路
【9月更文挑战第10天】从数据新手成长为AI专家,需先掌握Python基础语法,并学会使用NumPy和Pandas进行数据分析。接着,通过Matplotlib和Seaborn实现数据可视化,最后利用TensorFlow或PyTorch探索深度学习。这一过程涉及从数据清洗、可视化到构建神经网络的多个步骤,每一步都需不断实践与学习。借助Python的强大功能及各类库的支持,你能逐步解锁数据的深层价值。
54 0
|
2月前
|
持续交付 测试技术 jenkins
JSF 邂逅持续集成,紧跟技术热点潮流,开启高效开发之旅,引发开发者强烈情感共鸣
【8月更文挑战第31天】在快速发展的软件开发领域,JavaServer Faces(JSF)这一强大的Java Web应用框架与持续集成(CI)结合,可显著提升开发效率及软件质量。持续集成通过频繁的代码集成及自动化构建测试,实现快速反馈、高质量代码、加强团队协作及简化部署流程。以Jenkins为例,配合Maven或Gradle,可轻松搭建JSF项目的CI环境,通过JUnit和Selenium编写自动化测试,确保每次构建的稳定性和正确性。
53 0
|
2月前
|
测试技术 数据库
探索JSF单元测试秘籍!如何让您的应用更稳固、更高效?揭秘成功背后的测试之道!
【8月更文挑战第31天】在 JavaServer Faces(JSF)应用开发中,确保代码质量和可维护性至关重要。本文详细介绍了如何通过单元测试实现这一目标。首先,阐述了单元测试的重要性及其对应用稳定性的影响;其次,提出了提高 JSF 应用可测试性的设计建议,如避免直接访问外部资源和使用依赖注入;最后,通过一个具体的 `UserBean` 示例,展示了如何利用 JUnit 和 Mockito 框架编写有效的单元测试。通过这些方法,不仅能够确保代码质量,还能提高开发效率和降低维护成本。
47 0