环境搭建可参考Django环境搭建及开发的环境搭建部分也可参照TensorFlow官网
pip install --upgrade tensorflow # 其它所需包 pip install --upgrade numpy pandas matplotlib
本文主要内容
TensorFlow的基础知识
TensorFlow经典的数据流图
TensorFlow的Hello World
TensorBoard
TensorFlow的操作(Operations)
TensorFlow的使用案例
梯度下降解决线性回归
激活函数(Activation Function)
实现CNN(Convolution Neural Network)卷积神经网络
RNN-LSTM循环神经网络
TensorFlow的基础知识
TensorFlow经典的数据流图
图中的各个节点是操作(Operation)中间的线是张量(Tensor)
TensorFlow的Hello World
import tensorflow as tf # 创建一个常量Operation hw = tf.constant("Hello World!") # 启动会话 sess = tf.Session() # 运行Graph计算图 print(sess.run(hw)) # 关闭会话 sess.close()
Tensor有以下几种constant(常量), Variable(变量), placeholder(占位符),SparseTensor(稀疏张量)
TensorBoard
# 1.用TensorFlow保存图的消息到日志中 tf.summary.FileWriter(path, sess.graph) # 2.用TensorBoard读取并展示日志命令行 tensorboard --logdir=日志所在路径
如以下是后面卷积神经网络示例的TensorBoard
TensorFlow的操作(Operations)
TensorFlow的使用案例
梯度下降解决线性回归
import numpy as np import matplotlib.pyplot as plt import tensorflow as tf # 构建数据 points_num = 100 vectors = [] # 用Numpy的正态随机分页函数生成100个点 # 这些点的(x,y)坐标值对应线性方程 y = 0.1 * x + 0.2 for i in range(points_num): x1 = np.random.normal(0.0, 0.66) y1 = x1 * 0.1 + 0.2 + np.random.normal(0.0, 0.04) vectors.append([x1, y1]) x_data = [v[0] for v in vectors] # 真实点的x坐标 y_data = [v[1] for v in vectors] # 真实点的y坐标 # 图像1展示100个随机数据点 plt.plot(x_data, y_data, 'r*', label="Original data") # 红色星形的点 plt.title("Linear Regression using Gradient Descent") plt.legend() plt.show() # 构建线性回归模型 W = tf.Variable(tf.random_uniform([1], -1.0, 1.0)) # 初始化 Weight b = tf.Variable(tf.zeros([1])) # 初始化 Bias y = W * x_data + b # 模型计算出来的y # 定义 loss function(损失函数) 或 cost function(代价函数) # 对 Tensor的所有维度计算 ((y - y_data) ^ 2)之和 / N loss = tf.reduce_mean(tf.square(y - y_data)) # 用梯度下降的优化器来优化我们的 loss function optimizer = tf.train.GradientDescentOptimizer(0.5) # 设置学习率0.5 train = optimizer.minimize(loss) # 创建会话 sess = tf.Session() # 初始化数据流图中的所有变量 init = tf.global_variables_initializer() sess.run(init) # 训练 20 步 for step in range(20): # 优化每一步 sess.run(train) # 打印出每一步的损失、权重和偏差 print("Step=%d, Loss=%f, [Weight=%f Bias=%f]" % (step, sess.run(loss), sess.run(W), sess.run(b)) ) # 图像2 绘制所有的点并且绘制出最佳拟合的直线 plt.plot(x_data, y_data, 'r*', label="Original data") # 红色星形的点 plt.title("Linear Regression using Gradient Descent") plt.plot(x_data, sess.run(W) * x_data + sess.run(b), label="Fitted line") # 拟合的线 plt.legend() plt.xlabel('x') plt.ylabel('y') plt.show() # 关闭会话 sess.close()
激活函数(Activation Function)
import numpy as np import matplotlib.pyplot as plt import tensorflow as tf # 创建输入数据 x = np.linspace(-7, 7, 180) # (-7, 7)之间等间隔的180个点 #激活函数的原始实现 def sigmoid(inputs): y = [1 / float(1 + np.exp(-x)) for x in inputs] return y def relu(inputs): y = [x * (x>0) for x in inputs] return y def tanh(inputs): y = [(np.exp(x) - np.exp(-x)) / float(np.exp(x) + np.exp(-x)) for x in inputs] return y def softplus(inputs): y = [np.log(1 + np.exp(x)) for x in inputs] return y # 经过 TensorFlow的激活函数处理的各个 Y值 y_sigmoid = tf.nn.sigmoid(x) y_relu = tf.nn.relu(x) y_tanh = tf.nn.tanh(x) y_softplus = tf.nn.softplus(x) # 创建会话 sess = tf.Session() # 运行 y_sigmoid, y_relu, y_tanh, y_softplus = sess.run([y_sigmoid, y_relu, y_tanh, y_softplus]) # 创建各个激活函数的图像 plt.subplot(221) plt.plot(x, y_sigmoid, c="red", label="Sigmoid") plt.ylim(-0.2, 1.2) plt.legend(loc="best") plt.subplot(222) plt.plot(x, y_relu, c="red", label="Relu") plt.ylim(-1, 6) plt.legend(loc="best") plt.subplot(223) plt.plot(x, y_tanh, c="red", label="Tanh") plt.ylim(-1.3, 1.3) plt.legend(loc="best") plt.subplot(224) plt.plot(x, y_softplus, c="red", label="Softplus") plt.ylim(-1, 6) plt.legend(loc="best") # 显示图像 plt.show() #关闭会话 sess.close()
深度学习的三大模型
CNNConvolution Neural Network卷积神经网络
RNNRecurrent Neural Network循环神经网络
DBNDeep Belief Network深度信念网络
实现CNN(Convolution Neural Network)卷积神经网络
import numpy as np import tensorflow as tf # 下载并载入 MNIST 手写数字库(55000张28*28像素的图片) from tensorflow.examples.tutorials.mnist import input_data # mnist_data可自定义的数据存储目录one_hot是独热码的编码形式0-9的表示形式为0: 1000000000, 1: 0100000000... mnist = input_data.read_data_sets('mnist_data', one_hot=True) # None 表示张量(Tensor)的第一个维度可以是任何长度 input_x = tf.placeholder(tf.float32, [None, 28 * 28]) / 255 # 除255是因为有0-255的灰度值 output_y = tf.placeholder(tf.int32, [None, 10]) # 输出: 10个数字的标签 input_x_images = tf.reshape(input_x, [-1, 28, 28 ,1]) # 改变形状之后的输入 # 从 Test测试数据集中选取3000全手写数字的图片和对应标签 test_x = mnist.test.images[:3000] # 图片 test_y = mnist.test.labels[:3000] # 标签 # 构建卷积神经网络 # 第 1 层卷积 # output_size =1+ (input_size+2*padding-kernel_size)/stride conv1 = tf.layers.conv2d( inputs=input_x_images, # 形状 [28, 28, 1] filters=32, # 32个过滤器输出的深度是32 kernel_size=[5, 5], # 过滤器在二维的大小是5 * 5 strides=1, # 步长是1 padding='same', # same表示输出的大小不变需要在外围补0需补0两圈 activation=tf.nn.relu # 激活函数使用 Relu ) # 形状 [28, 28, 32] # 第 1 层池化亚采样 pool1 = tf.layers.max_pooling2d( inputs=conv1, # 形状 [28, 28, 32] pool_size=[2, 2], # 过滤器在二维的大小是2 * 2 strides=2, # 步长是2 ) # 形状 [14, 14, 32] # 第 2 层卷积 conv2 = tf.layers.conv2d( inputs=pool1, # 形状 [14, 14, 32] filters=64, # 64个过滤器输出的深度是64 kernel_size=[5, 5], # 过滤器在二维的大小是5 * 5 strides=1, # 步长是1 padding='same', # same表示输出的大小不变需要在外围补0需补0两圈 activation=tf.nn.relu # 激活函数使用 Relu ) # 形状 [14, 14, 64] # 第 2 层池化亚采样 pool2 = tf.layers.max_pooling2d( inputs=conv2, # 形状 [14, 14, 64] pool_size=[2, 2], # 过滤器在二维的大小是2 * 2 strides=2, # 步长是2 ) # 形状 [7, 7, 64] # 平坦化Flat flat = tf.reshape(pool2, [-1, 7 * 7 * 64]) # 形状 7 * 7 * 64 # 1024 个神经元的全连接层 dense = tf.layers.dense(inputs=flat, units=1024, activation=tf.nn.relu) # Dropout: 丢弃 50%, rate=0.5 dropout = tf.layers.dropout(inputs=dense, rate=0.5) # 10 个神经元的全连接层此处无需激活函数来做非线性化 logits = tf.layers.dense(inputs=dropout, units=10) # 输入形状 1 * 1 * 10 # 计算误差计算 Cross entropy交叉熵再用Softmax计算百分比概率 loss = tf.losses.softmax_cross_entropy( onehot_labels=output_y, logits=logits ) # Adam优化器来最小化误差学习率0.001 train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss) # 精度计算预测值和实际标签的匹配程度 # 返回(accuracy, update_op),会创建两个局部变量 accuracy = tf.metrics.accuracy( labels=tf.argmax(output_y, axis=1), predictions=tf.argmax(logits, axis=1),)[1] # 创建会话 sess = tf.Session() # 初始化全局变量和局部变量 init = tf.group(tf.global_variables_initializer(),tf.local_variables_initializer()) sess.run(init) for i in range(20000): batch = mnist.train.next_batch(50) # 从 Train训练数据集取一组50个样本 train_loss, train_op_ = sess.run([loss, train_op], {input_x: batch[0], output_y: batch[1]}) if i % 100 == 0: test_accuracy = sess.run(accuracy, {input_x: test_x, output_y: test_y}) print("Step=%d, Train loss=%.4f, [Test accuracy=%.2f]" % (i, train_loss, test_accuracy)) # 测试打印20个预测值和真实值对 test_output = sess.run(logits, {input_x: test_x[:20]}) inferenced_y = np.argmax(test_output, 1) print(inferenced_y, 'Inferenced numbers') # 推测的数字 print(np.argmax(test_y[:20], 1), 'Real numbers') # 真实的数字
训练结果如下
Step=0, Train loss=2.3087, [Test accuracy=0.22] Step=100, Train loss=0.1605, [Test accuracy=0.57] ... Step=19800, Train loss=0.0000, [Test accuracy=0.98] Step=19900, Train loss=0.0067, [Test accuracy=0.98] [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5 9 7 3 4] Inferenced numbers [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5 9 7 3 4] Real numbers
RNN-LSTM循环神经网络
RNN问题梯度消失、梯度爆炸
LSTM: Long Short-Term Memory
扩展阅读Understanding LSTM Networks
LSTM神经元的“三重门”机制
PTB数据集
wget http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz
本例中使用data文件夹下的ptb.test.txtptb.train.txtptb.valid.txt三个文件
utils.py
import os, sys import argparse import datetime import collections import numpy as np import tensorflow as tf """ ==== 超参数Hyper parameter==== init_scale : 权重参数Weights的初始取值跨度一开始取小一些比较利于训练 learning_rate : 学习率训练时初始为 1.0 num_layers : LSTM 层的数目默认是 2 num_steps : LSTM 展开的步step数相当于每个批次输入单词的数目默认是 35 hidden_size : LSTM 层的神经元数目也是词向量的维度默认是 650 max_lr_epoch : 用初始学习率训练的 Epoch 数目默认是 10 dropout : 在 Dropout 层的留存率默认是 0.5 lr_decay : 在过了 max_lr_epoch 之后每一个 Epoch 的学习率的衰减率训练时初始为 0.93。让学习率逐渐衰减是提高训练效率的有效方法 batch_size : 批次(样本)数目。一次迭代Forword 运算用于得到损失函数以及 BackPropagation 运算用于更新神经网络参数所用的样本数目 batch_size 默认是 20。取比较小的 batch_size 更有利于 Stochastic Gradient Descent随机梯度下降防止被困在局部最小值 """ # 数据集目录 data_path = "/Users/alan/Desktop/demo/simple-examples/data" # 保存训练所得的模型参数文件的目录 save_path = "./save" # 测试时读取模型参数文件的名称 load_file = "train-checkpoint-69" parser = argparse.ArgumentParser() parser.add_argument('--data_path', type=str, default=data_path, help='Path to data for training and testing') parser.add_argument('--load_file', type=str, default=load_file, help='Path to checkpoint file of model variables during training') args = parser.parse_args() # 判断是否 Python3 版本 Py3 = sys.version_info[0] == 3 # 将文件根据语句结束标识符(<eos>)来分割 def read_words(filename): with tf.gfile.GFile(filename, "r") as f: if Py3: return f.read().replace("\n", "<eos>").split() else: return f.read().decode("utf-8").replace("\n", "<eos>").split() # 构造从单词到唯一整数值的映射 def build_vocab(filename): data = read_words(filename) # 用counter统计单词出现次数并进行排序如the最多对应整数为0依此类推 counter = collections.Counter(data) count_pairs = sorted(counter.items(), key= lambda x: (-x[1], x[0])) words, _ = list(zip(*count_pairs)) # 单词到整数的映射 word_to_id = dict(zip(words, range(len(words)))) return word_to_id # 将文件里的单词都替换成独一的整数 def file_to_word_ids(filename, word_to_id): data = read_words(filename) return [word_to_id[word] for word in data if word in word_to_id] # 加载所有数据读取所有单词把将转成唯一对应的整数值 def load_data(data_path): # 三个数据集的路径 train_path = os.path.join(data_path, "ptb.train.txt") valid_path = os.path.join(data_path, "ptb.valid.txt") test_path = os.path.join(data_path, "ptb.test.txt") # 建立词汇表将所有单词转为唯一对应的整数值 word_to_id = build_vocab(train_path) train_data = file_to_word_ids(train_path, word_to_id) valid_data = file_to_word_ids(valid_path, word_to_id) test_data = file_to_word_ids(test_path, word_to_id) # 所胡独五词汇的个数 vocab_size = len(word_to_id) # 反转一个词汇表以便之后从整数转为单词 id_to_word = dict(zip(word_to_id.values(), word_to_id.keys())) print(word_to_id) print("=====================") print(vocab_size) print("=====================") print(train_data[:10]) print("=====================") print(" ".join([id_to_word[x] for x in train_data[:10]])) return train_data, valid_data, test_data, vocab_size, id_to_word # 生成批次样本 def generate_batches(raw_data, batch_size, num_steps): # 将数据转为 Tensor 类型 raw_data = tf.convert_to_tensor(raw_data, name="raw_data", dtype=tf.int32) data_len = tf.size(raw_data) batch_len = data_len // batch_size # 将数据形状转为 [batch_size, batch_len] data = tf.reshape(raw_data[0: batch_size * batch_len], [batch_size, batch_len]) epoch_size = (batch_len - 1) // num_steps # range_input_producer 可以用多线程异步的方式从数据集里提取数据 # 用多线程可以加快训练因为 feed_dict 的赋值方式效率不高 # shuffle 为 False 表示不打乱数据而按照队列先进先出的方式提取数据 i = tf.train.range_input_producer(epoch_size, shuffle=False).dequeue() # 假设一句话是这样 “我爱我的祖国和人民” # 那么如果 x 是类似这样 “我爱我的祖国” x = data[:, i * num_steps:(i + 1) * num_steps] x.set_shape([batch_size, num_steps]) # y 就是类似这样正好是 x 的时间步长 + 1 “爱我的祖国和” # 因为我们的模型就是要预测一句话中每一个单词的下一个单词 # 当然这边的例子很简单实际的数据不止一个维度 y = data[:, i * num_steps + 1: (i + 1) * num_steps + 1] y.set_shape([batch_size, num_steps]) return x, y # 输入数据 class Input(object): def __init__(self, batch_size, num_steps, data): self.batch_size = batch_size self.num_steps = num_steps self.epoch_size = ((len(data) // batch_size) - 1) // num_steps # input_data 是输入targets 是期望的输出 self.input_data, self.targets = generate_batches(data, batch_size, num_steps)
network.py
import tensorflow as tf # 神经网络的模型 class Model(object): def __init__(self, input, is_training, hidden_size, vocab_size, num_layers, dropout=0.5, init_scale=0.05): self.is_training = is_training self.input_obj = input self.batch_size = input.batch_size self.num_steps = input.num_steps self.hidden_size = hidden_size # 此处操作和变量用 CPU 来计算暂无 GPU 的实现 with tf.device("/cpu:0"): # 创建 词向量Word EmbeddingEmbedding 表示 Dense Vector密集向量 # 词向量本质上是一种单词聚类Clustering的方法 embedding = tf.Variable(tf.random_uniform([vocab_size, self.hidden_size], -init_scale, init_scale)) inputs = tf.nn.embedding_lookup(embedding, self.input_obj.input_data) # 如果是训练并且dropout率小于1使输入经过一个Dropout层 # Dropout 防止过拟合 if is_training and dropout < 1: inputs = tf.nn.dropout(inputs, dropout) # 状态state的存储和提取 # 第二维是 2 是因为对每一个 LSTM 单元有两个来自上一单元的输入 # 一个是 前一时刻 LSTM 的输出 h(t-1) # 一个是 前一时刻的单元状态 C(t-1) # 这个 C 和 h 是用于构建之后的 tf.contrib.rnn.LSTMStateTuple self.init_state = tf.placeholder(tf.float32, [num_layers, 2, self.batch_size, self.hidden_size]) # 每一层的状态 state_per_layer_list = tf.unstack(self.init_state, axis=0) # 初始的状态包含 前一时刻 LSTM 的输出 h(t-1) 和 前一时刻的单元状态 C(t-1)用于之后的 dynamic_rnn rnn_tuple_state = tuple( [tf.contrib.rnn.LSTMStateTuple(state_per_layer_list[idx][0], state_per_layer_list[idx][1]) for idx in range(num_layers)] ) # 创建一个 LSTM 层其中的神经元数目是 hidden_size 个默认 650 个 cell = tf.contrib.rnn.LSTMCell(hidden_size) # 如果是训练时 并且 Dropout 率小于 1给 LSTM 层加上 Dropout 操作 # 这里只给 输出 加了 Dropout 操作留存率(output_keep_prob)是 0.5 # 输入则是默认的 1所以相当于输入没有做 Dropout 操作 if is_training and dropout < 1: cell = tf.contrib.rnn.DropoutWrapper(cell, output_keep_prob=dropout) # 如果 LSTM 的层数大于 1, 则总计创建 num_layers 个 LSTM 层 # 并将所有的 LSTM 层包装进 MultiRNNCell 这样的序列化层级模型中 # state_is_tuple=True 表示接受 LSTMStateTuple 形式的输入状态 if num_layers > 1: cell = tf.contrib.rnn.MultiRNNCell([cell for _ in range(num_layers)], state_is_tuple=True) # dynamic_rnn动态 RNN让不同迭代传入的 Batch 可以是长度不同的数据 # 但同一次迭代中一个 Batch 内部的所有数据长度仍然是固定的 # dynamic_rnn 能更好处理 padding补零的情况节约计算资源 # 返回两个变量 # 第一个是一个 Batch 里在时间维度默认是 35上展开的所有 LSTM 单元的输出形状默认为 [20, 35, 650]之后会经过扁平层处理 # 第二个是最终的 state状态包含 当前时刻 LSTM 的输出 h(t) 和 当前时刻的单元状态 C(t) output, self.state = tf.nn.dynamic_rnn(cell, inputs, dtype=tf.float32, initial_state=rnn_tuple_state) # 扁平化处理改变输出形状为 (batch_size * num_steps, hidden_size)形状默认为 [700, 650] output = tf.reshape(output, [-1, hidden_size]) # -1 表示自动推导维度大小 # Softmax的权重 softmax_w = tf.Variable(tf.random_uniform([hidden_size, vocab_size], -init_scale, init_scale)) # Softmax的偏置 softmax_b = tf.Variable(tf.random_uniform([vocab_size], -init_scale, init_scale)) # logits 是 Logistic Regression用于分类模型线性方程 y = W * x + b 计算的结果分值 # 这个 logits分值之后会用 Softmax 来转成百分比概率 # output 是输入x softmax_w 是 权重Wsoftmax_b 是偏置b # 返回 W * x + b 结果 logits = tf.nn.xw_plus_b(output, softmax_w, softmax_b) # 将 logits 转化为三维的 Tensor为了 sequence loss 的计算 # 形状默认为 [20, 35, 10000] logits = tf.reshape(logits, [self.batch_size, self.num_steps, vocab_size]) # 计算 logits 的序列的交叉熵Cross-Entropy的损失loss loss = tf.contrib.seq2seq.sequence_loss( logits, # 形状默认为 [20, 35, 10000] self.input_obj.targets, # 期望输出形状默认为 [20, 35] tf.ones([self.batch_size, self.num_steps], dtype=tf.float32), average_across_timesteps=False, average_across_batch=True ) # 更新代价Cost self.cost = tf.reduce_sum(loss) # Softmax算出来的概率 self.softmax_out = tf.nn.softmax(tf.reshape(logits, [-1, vocab_size])) # 取最大概率的那个值作为预测 self.predict = tf.cast(tf.argmax(self.softmax_out, axis=1), tf.int32) # 预测值和真实值目标对比 correct_prediction = tf.equal(self.predict, tf.reshape(self.input_obj.targets, [-1])) # 计算预测的精度 self.accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) # 如果是 测试则直接退出 if not is_training: return # 学习率,trainable=False表示“不可被训练” self.learning_rate = tf.Variable(0.0, trainable=False) # 返回所有可被训练trainable=True。如果不设定 trainable=False默认的 Variable 都是可以被训练的 # 也就是除了不可被训练的 学习率 之外的其他变量 tvars = tf.trainable_variables() # tf.clip_by_global_norm实现 Gradient Clipping梯度裁剪是为了防止梯度爆炸 # tf.gradients 计算 self.cost 对于 tvars 的梯度求导返回一个梯度的列表 grads, _ = tf.clip_by_global_norm(tf.gradients(self.cost, tvars), 5) # 优化器用 GradientDescentOptimizer梯度下降优化器 optimizer = tf.train.GradientDescentOptimizer(self.learning_rate) # apply_gradients应用梯度将之前用Gradient Clipping梯度裁剪过的梯度 应用到可被训练的变量上去做梯度下降 # apply_gradients 其实是 minimize 方法里面的第二步第一步是 计算梯度 self.train_op = optimizer.apply_gradients( zip(grads, tvars), global_step=tf.train.get_or_create_global_step() ) # 用于更新学习率 self.new_lr = tf.placeholder(tf.float32, shape=[]) self.lr_update = tf.assign(self.learning_rate, self.new_lr) # 更新 学习率 def assign_lr(self, session, lr_value): session.run(self.lr_update, feed_dict={self.new_lr: lr_value})
train.py
""" 训练神经网络模型 大家之后可以加上各种的 name_scope命名空间 用 TensorBoard 来可视化 ==== 一些术语的概念 ==== # Batch size : 批次(样本)数目。一次迭代Forword 运算用于得到损失函数以及 BackPropagation 运算用于更新神经网络参数所用的样本数目。Batch size 越大所需的内存就越大 # Iteration : 迭代。每一次迭代更新一次权重网络参数每一次权重更新需要 Batch size 个数据进行 Forward 运算再进行 BP 运算 # Epoch : 纪元/时代。所有的训练样本完成一次迭代 # 假如 : 训练集有 1000 个样本Batch_size=10 # 那么 : 训练完整个样本集需要 100 次 Iteration1 个 Epoch # 但一般我们都不止训练一个 Epoch ==== 超参数Hyper parameter==== init_scale : 权重参数Weights的初始取值跨度一开始取小一些比较利于训练 learning_rate : 学习率训练时初始为 1.0 num_layers : LSTM 层的数目默认是 2 num_steps : LSTM 展开的步step数相当于每个批次输入单词的数目默认是 35 hidden_size : LSTM 层的神经元数目也是词向量的维度默认是 650 max_lr_epoch : 用初始学习率训练的 Epoch 数目默认是 10 dropout : 在 Dropout 层的留存率默认是 0.5 lr_decay : 在过了 max_lr_epoch 之后每一个 Epoch 的学习率的衰减率训练时初始为 0.93。让学习率逐渐衰减是提高训练效率的有效方法 batch_size : 批次(样本)数目。一次迭代Forword 运算用于得到损失函数以及 BackPropagation 运算用于更新神经网络参数所用的样本数目 batch_size 默认是 20。取比较小的 batch_size 更有利于 Stochastic Gradient Descent随机梯度下降防止被困在局部最小值 """ from utils import * from network import * def train(train_data, vocab_size, num_layers, num_epochs, batch_size, model_save_name, learning_rate=1.0, max_lr_epoch=10, lr_decay=0.93, print_iter=50): # 训练的输入 training_input = Input(batch_size=batch_size, num_steps=35, data=train_data) # 创建训练的模型 m = Model(training_input, is_training=True, hidden_size=650, vocab_size=vocab_size, num_layers=num_layers) # 初始化变量的操作 init_op = tf.global_variables_initializer() # 初始的学习率learning rate的衰减率 orig_decay = lr_decay with tf.Session() as sess: sess.run(init_op) # 初始化所有变量 # Coordinator协调器用于协调线程的运行 coord = tf.train.Coordinator() # 启动线程 threads = tf.train.start_queue_runners(coord=coord) # 为了用 Saver 来保存模型的变量 saver = tf.train.Saver() # max_to_keep 默认是 5, 只保存最近的 5 个模型参数文件 # 开始 Epoch 的训练 for epoch in range(num_epochs): # 只有 Epoch 数大于 max_lr_epoch设置为 10后才会使学习率衰减 # 也就是说前 10 个 Epoch 的学习率一直是 1, 之后每个 Epoch 学习率都会衰减 new_lr_decay = orig_decay ** max(epoch + 1 - max_lr_epoch, 0) m.assign_lr(sess, learning_rate * new_lr_decay) # 当前的状态 # 第二维是 2 是因为对每一个 LSTM 单元有两个来自上一单元的输入 # 一个是 前一时刻 LSTM 的输出 h(t-1) # 一个是 前一时刻的单元状态 C(t-1) current_state = np.zeros((num_layers, 2, batch_size, m.hidden_size)) # 获取当前时间以便打印日志时用 curr_time = datetime.datetime.now() for step in range(training_input.epoch_size): # train_op 操作计算被修剪clipping过的梯度并最小化 cost误差 # state 操作返回时间维度上展开的最后 LSTM 单元的输出C(t) 和 h(t)作为下一个 Batch 的输入状态 if step % print_iter != 0: cost, _, current_state = sess.run([m.cost, m.train_op, m.state], feed_dict={m.init_state: current_state}) else: seconds = (float((datetime.datetime.now() - curr_time).seconds) / print_iter) curr_time = datetime.datetime.now() cost, _, current_state, acc = sess.run([m.cost, m.train_op, m.state, m.accuracy], feed_dict={m.init_state: current_state}) # 每 print_iter默认是 50打印当下的 Cost误差/损失和 Accuracy精度 print("Epoch {}, 第 {} 步, 损失: {:.3f}, 精度: {:.3f}, 每步所用秒数: {:.2f}".format(epoch, step, cost, acc, seconds)) # 保存一个模型的变量的 checkpoint 文件 saver.save(sess, save_path + '/' + model_save_name, global_step=epoch) # 对模型做一次总的保存 saver.save(sess, save_path + '/' + model_save_name + '-final') # 关闭线程 coord.request_stop() coord.join(threads) if __name__ == "__main__": if args.data_path: data_path = args.data_path train_data, valid_data, test_data, vocab_size, id_to_word = load_data(data_path) train(train_data, vocab_size, num_layers=2, num_epochs=70, batch_size=20, model_save_name='train-checkpoint') if __name__ == "__main__": if args.data_path: data_path = args.data_path train_data, valid_data, test_data, vocab_size, id_to_word = load_data(data_path) train(train_data, vocab_size, num_layers=2, num_epochs=70, batch_size=20, model_save_name='train-checkpoint')
test.py
from utils import * from network import * def test(model_path, test_data, vocab_size, id_to_word): # 测试的输入 test_input = Input(batch_size=20, num_steps=35, data=test_data) # 创建测试的模型基本的超参数需要和训练时用的一致例如 # hidden_sizenum_stepsnum_layersvocab_sizebatch_size 等等 # 因为我们要载入训练时保存的参数的文件如果超参数不匹配 TensorFlow 会报错 m = Model(test_input, is_training=False, hidden_size=650, vocab_size=vocab_size, num_layers=2) # 为了用 Saver 来恢复训练时生成的模型的变量 saver = tf.train.Saver() with tf.Session() as sess: # Coordinator协调器用于协调线程的运行 coord = tf.train.Coordinator() # 启动线程 threads = tf.train.start_queue_runners(coord=coord) # 当前的状态 # 第二维是 2 是因为测试时指定只有 2 层 LSTM # 第二维是 2 是因为对每一个 LSTM 单元有两个来自上一单元的输入 # 一个是 前一时刻 LSTM 的输出 h(t-1) # 一个是 前一时刻的单元状态 C(t-1) current_state = np.zeros((2, 2, m.batch_size, m.hidden_size)) # 恢复被训练的模型的变量 saver.restore(sess, model_path) # 测试30个批次 num_acc_batches = 30 # 打印预测单词和实际单词的批次数 check_batch_idx = 25 # 超过5个批次才开始累加精度 acc_check_thresh = 5 # 初始精度的和用于之后算平均精度 accuracy = 0 for batch in range(num_acc_batches): if batch == check_batch_idx: true, pred, current_state, acc = sess.run([m.input_obj.targets, m.predict, m.state, m.accuracy], feed_dict={m.init_state: current_state}) pred_words = [id_to_word[x] for x in pred[:m.num_steps]] true_words = [id_to_word[x] for x in true[0]] print("True words (1st line) vs. predicted words (2nd line)") print(" ".join(true_words)) # 真实的单词 print(" ".join(pred_words)) # 预测的单词 else: acc, current_state = sess.run([m.accuracy, m.state],feed_dict={m.init_state: current_state}) if batch >= accuracy: accuracy += acc # 打印平均精度 print("Average accuracy: {:.3f}".format(accuracy / (num_acc_batches - acc_check_thresh))) # 关闭线程 coord.request_stop() coord.join(threads) if __name__ == "__main__": if args.data_path: data_path = args.data_path if args.load_file: load_file = args.load_file train_data, valid_data, test_data, vocab_size, id_to_word = load_data(data_path) trained_model = save_path + '/' + load_file test(trained_model, test_data, vocab_size, id_to_word)
在Mac上进行一次训练需耗时30多分钟写这篇文章时训练仍在进行以下是第二次训练完成后执行test.py的结果可以看到精确度还是很低的
True words (1st line) vs. predicted words (2nd line)
stock market is headed many traders were afraid to trust stock prices quoted on the big board <eos> the futures halt was even <unk> by big board floor traders <eos> it <unk> things up said
is market is n't by of said <unk> to be <eos> prices <eos> at a dollar board <eos> the dollar index that a a in a board in in said the is a that a
Average accuracy: 0.236
课程链接https://coding.imooc.com/class/176.html
本文首次发表于 Alan Hou 的个人博客