TensorFlow 机器学习秘籍第二版:9~11(1)https://developer.aliyun.com/article/1426842
- 接下来,我们获取数据。如果数据文件不存在,我们下载并保存莎士比亚文本。如果确实存在,我们加载数据:
if not os.path.exists(full_model_dir): os.makedirs(full_model_dir) # Make data directory if not os.path.exists(data_dir): os.makedirs(data_dir) print('Loading Shakespeare Data') # Check if file is downloaded. if not os.path.isfile(os.path.join(data_dir, data_file)): print('Not found, downloading Shakespeare texts from www.gutenberg.org') shakespeare_url = 'http://www.gutenberg.org/cache/epub/100/pg100.txt' # Get Shakespeare text response = requests.get(shakespeare_url) shakespeare_file = response.content # Decode binary into string s_text = shakespeare_file.decode('utf-8') # Drop first few descriptive paragraphs. s_text = s_text[7675:] # Remove newlines s_text = s_text.replace('\r\n', '') s_text = s_text.replace('\n', '') # Write to file with open(os.path.join(data_dir, data_file), 'w') as out_conn: out_conn.write(s_text) else: # If file has been saved, load from that file with open(os.path.join(data_dir, data_file), 'r') as file_conn: s_text = file_conn.read().replace('\n', '')
- 我们通过删除标点符号和额外的空格来清理莎士比亚的文本:
s_text = re.sub(r'[{}]'.format(punctuation), ' ', s_text) s_text = re.sub('s+', ' ', s_text ).strip().lower()
- 我们现在处理创建要使用的莎士比亚词汇。我们创建一个函数,它将返回两个字典(单词到索引和索引到单词),其中的单词出现的频率超过指定的频率:
def build_vocab(text, min_word_freq): word_counts = collections.Counter(text.split(' ')) # limit word counts to those more frequent than cutoff word_counts = {key:val for key, val in word_counts.items() if val>min_word_freq} # Create vocab --> index mapping words = word_counts.keys() vocab_to_ix_dict = {key:(ix+1) for ix, key in enumerate(words)} # Add unknown key --> 0 index vocab_to_ix_dict['unknown']=0 # Create index --> vocab mapping ix_to_vocab_dict = {val:key for key,val in vocab_to_ix_dict.items()} return ix_to_vocab_dict, vocab_to_ix_dict ix2vocab, vocab2ix = build_vocab(s_text, min_word_freq) vocab_size = len(ix2vocab) + 1
请注意,在处理文本时,我们必须小心索引值为零的单词。我们应该保存填充的零值,也可能保存未知单词。
- 现在我们有了词汇量,我们将莎士比亚的文本变成了一系列索引:
s_text_words = s_text.split(' ') s_text_ix = [] for ix, x in enumerate(s_text_words): try: s_text_ix.append(vocab2ix[x]) except: s_text_ix.append(0) s_text_ix = np.array(s_text_ix)
- 在本文中,我们将展示如何在类对象中创建模型。这对我们很有帮助,因为我们希望使用相同的模型(具有相同的权重)来批量训练并从示例文本生成文本。如果没有采用内部抽样方法的类,这将很难做到。理想情况下,此类代码应位于单独的 Python 文件中,我们可以在此脚本的开头导入该文件:
class LSTM_Model(): def __init__(self, rnn_size, batch_size, learning_rate, training_seq_len, vocab_size, infer =False): self.rnn_size = rnn_size self.vocab_size = vocab_size self.infer = infer self.learning_rate = learning_rate if infer: self.batch_size = 1 self.training_seq_len = 1 else: self.batch_size = batch_size self.training_seq_len = training_seq_len self.lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(rnn_size) self.initial_state = self.lstm_cell.zero_state(self.batch_size, tf.float32) self.x_data = tf.placeholder(tf.int32, [self.batch_size, self.training_seq_len]) self.y_output = tf.placeholder(tf.int32, [self.batch_size, self.training_seq_len]) with tf.variable_scope('lstm_vars'): # Softmax Output Weights W = tf.get_variable('W', [self.rnn_size, self.vocab_size], tf.float32, tf.random_normal_initializer()) b = tf.get_variable('b', [self.vocab_size], tf.float32, tf.constant_initializer(0.0)) # Define Embedding embedding_mat = tf.get_variable('embedding_mat', [self.vocab_size, self.rnn_size], tf.float32, tf.random_normal_initializer()) embedding_output = tf.nn.embedding_lookup(embedding_mat, self.x_data) rnn_inputs = tf.split(embedding_output, num_or_size_splits=self.training_seq_len, axis=1) rnn_inputs_trimmed = [tf.squeeze(x, [1]) for x in rnn_inputs] # If we are inferring (generating text), we add a 'loop' function # Define how to get the i+1 th input from the i th output def inferred_loop(prev, count): prev_transformed = tf.matmul(prev, W) + b prev_symbol = tf.stop_gradient(tf.argmax(prev_transformed, 1)) output = tf.nn.embedding_lookup(embedding_mat, prev_symbol) return output decoder = tf.nn.seq2seq.rnn_decoder outputs, last_state = decoder(rnn_inputs_trimmed, self.initial_state, self.lstm_cell, loop_function=inferred_loop if infer else None) # Non inferred outputs output = tf.reshape(tf.concat(1, outputs), [-1, self.rnn_size]) # Logits and output self.logit_output = tf.matmul(output, W) + b self.model_output = tf.nn.softmax(self.logit_output) loss_fun = tf.contrib.legacy_seq2seq.sequence_loss_by_example loss = loss_fun([self.logit_output],[tf.reshape(self.y_output, [-1])], [tf.ones([self.batch_size * self.training_seq_len])], self.vocab_size) self.cost = tf.reduce_sum(loss) / (self.batch_size * self.training_seq_len) self.final_state = last_state gradients, _ = tf.clip_by_global_norm(tf.gradients(self.cost, tf.trainable_variables()), 4.5) optimizer = tf.train.AdamOptimizer(self.learning_rate) self.train_op = optimizer.apply_gradients(zip(gradients, tf.trainable_variables())) def sample(self, sess, words=ix2vocab, vocab=vocab2ix, num=10, prime_text='thou art'): state = sess.run(self.lstm_cell.zero_state(1, tf.float32)) word_list = prime_text.split() for word in word_list[:-1]: x = np.zeros((1, 1)) x[0, 0] = vocab[word] feed_dict = {self.x_data: x, self.initial_state:state} [state] = sess.run([self.final_state], feed_dict=feed_dict) out_sentence = prime_text word = word_list[-1] for n in range(num): x = np.zeros((1, 1)) x[0, 0] = vocab[word] feed_dict = {self.x_data: x, self.initial_state:state} [model_output, state] = sess.run([self.model_output, self.final_state], feed_dict=feed_dict) sample = np.argmax(model_output[0]) if sample == 0: break word = words[sample] out_sentence = out_sentence + ' ' + word return out_sentence
- 现在我们将声明 LSTM 模型以及测试模型。我们将在变量范围内执行此操作,并告诉范围我们将重用测试 LSTM 模型的变量:
with tf.variable_scope('lstm_model', reuse=tf.AUTO_REUSE) as scope: # Define LSTM Model lstm_model = LSTM_Model(rnn_size, batch_size, learning_rate, training_seq_len, vocab_size) scope.reuse_variables() test_lstm_model = LSTM_Model(rnn_size, batch_size, learning_rate, training_seq_len, vocab_size, infer=True)
- 我们创建一个保存操作,并将输入文本拆分为相等的批量大小的块。然后我们初始化模型的变量:
saver = tf.train.Saver() # Create batches for each epoch num_batches = int(len(s_text_ix)/(batch_size * training_seq_len)) + 1 # Split up text indices into subarrays, of equal size batches = np.array_split(s_text_ix, num_batches) # Reshape each split into [batch_size, training_seq_len] batches = [np.resize(x, [batch_size, training_seq_len]) for x in batches] # Initialize all variables init = tf.global_variables_initializer() sess.run(init)
- 我们现在可以遍历我们的周期,在每个周期开始之前对数据进行混洗。我们数据的目标只是相同的数据,但是移动了 1(使用
numpy.roll()
函数):
train_loss = [] iteration_count = 1 for epoch in range(epochs): # Shuffle word indices random.shuffle(batches) # Create targets from shuffled batches targets = [np.roll(x, -1, axis=1) for x in batches] # Run a through one epoch print('Starting Epoch #{} of {}.'.format(epoch+1, epochs)) # Reset initial LSTM state every epoch state = sess.run(lstm_model.initial_state) for ix, batch in enumerate(batches): training_dict = {lstm_model.x_data: batch, lstm_model.y_output: targets[ix]} c, h = lstm_model.initial_state training_dict[c] = state.c training_dict[h] = state.h temp_loss, state, _ = sess.run([lstm_model.cost, lstm_model.final_state, lstm_model.train_op], feed_dict=training_dict) train_loss.append(temp_loss) # Print status every 10 gens if iteration_count % 10 == 0: summary_nums = (iteration_count, epoch+1, ix+1, num_batches+1, temp_loss) print('Iteration: {}, Epoch: {}, Batch: {} out of {}, Loss: {:.2f}'.format(*summary_nums)) # Save the model and the vocab if iteration_count % save_every == 0: # Save model model_file_name = os.path.join(full_model_dir, 'model') saver.save(sess, model_file_name, global_step = iteration_count) print('Model Saved To: {}'.format(model_file_name)) # Save vocabulary dictionary_file = os.path.join(full_model_dir, 'vocab.pkl') with open(dictionary_file, 'wb') as dict_file_conn: pickle.dump([vocab2ix, ix2vocab], dict_file_conn) if iteration_count % eval_every == 0: for sample in prime_texts: print(test_lstm_model.sample(sess, ix2vocab, vocab2ix, num=10, prime_text=sample)) iteration_count += 1
- 这产生以下输出:
Loading Shakespeare Data Cleaning Text Building Shakespeare Vocab Vocabulary Length = 8009 Starting Epoch #1 of 10\. Iteration: 10, Epoch: 1, Batch: 10 out of 182, Loss: 10.37 Iteration: 20, Epoch: 1, Batch: 20 out of 182, Loss: 9.54 ... Iteration: 1790, Epoch: 10, Batch: 161 out of 182, Loss: 5.68 Iteration: 1800, Epoch: 10, Batch: 171 out of 182, Loss: 6.05 thou art more than i am a to be or not to the man i have wherefore art thou art of the long Iteration: 1810, Epoch: 10, Batch: 181 out of 182, Loss: 5.99
- 最后,以下是我们如何绘制历史上的训练损失:
plt.plot(train_loss, 'k-') plt.title('Sequence to Sequence Loss') plt.xlabel('Generation') plt.ylabel('Loss') plt.show()
This results in the following plot of our loss values:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EskYj5GW-1681566911075)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/678ff2b5-2732-4bc7-b85f-3fee578fb962.png)]
图 4:模型所有代的序列到序列损失
工作原理
在这个例子中,我们基于莎士比亚词汇构建了一个带有 LSTM 单元的 RNN 模型来预测下一个单词。可以采取一些措施来改进模型,可能会增加序列大小,具有衰减的学习率,或者训练模型以获得更多的周期。
更多
为了抽样,我们实现了一个贪婪的采样器。贪婪的采样器可能会一遍又一遍地重复相同的短语;例如,他们可能会卡住for the for the
for the....
为了防止这种情况,我们还可以实现一种更随机的采样方式,可能是根据输出的对数或概率分布制作加权采样器。
堆叠多个 LSTM 层
正如我们可以增加神经网络或 CNN 的深度,我们可以增加 RNN 网络的深度。在这个秘籍中,我们应用了一个三层深度的 LSTM 来改进我们的莎士比亚语言生成。
准备
我们可以通过将它们叠加在一起来增加循环神经网络的深度。从本质上讲,我们将获取目标输出并将其输入另一个网络。
要了解这对于两层的工作原理,请参见下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cBAzUNr-1681566911075)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/89cc9e5c-d9d8-403c-8656-6d2a23f69f83.png)]
图 5:在上图中,我们扩展了单层 RNN,使它们具有两层。对于原始的单层版本,请参阅上一章简介中的绘图。左侧架构说明了使用多层 RNN 预测输出序列中的一个输出的方法。正确的架构显示了使用多层 RNN 预测输出序列的方法,该输出序列使用输出作为输入
TensorFlow 允许使用MultiRNNCell()
函数轻松实现多个层,该函数接受 RNN 单元列表。有了这种行为,很容易用MultiRNNCell([rnn_cell(num_units) for n in num_layers])
单元格从 Python 中的一个单元格创建多层 RNN。
对于这个秘籍,我们将执行我们在之前的秘籍中执行的相同的莎士比亚预测。将有两个变化:第一个变化将是具有三个堆叠的 LSTM 模型而不是仅一个层,第二个变化将是进行字符级预测而不是单词。进行字符级预测会将我们潜在的词汇量大大减少到只有 40 个字符(26 个字母,10 个数字,1 个空格和 3 个特殊字符)。
操作步骤
我们将说明本节中的代码与上一节的不同之处,而不是重新使用所有相同的代码。有关完整代码,请参阅 GitHub 仓库或 Packt 仓库。
- 我们首先需要设置模型的层数。我们将此作为参数放在脚本的开头,并使用其他模型参数:
num_layers = 3 min_word_freq = 5
rnn_size = 128 epochs = 10
- 第一个主要变化是我们将按字符加载,处理和提供文本,而不是按字词加载。为了实现这一点,在清理文本之后,我们可以使用 Python 的
list()
命令逐个字符地分隔整个文本:
s_text = re.sub(r'[{}]'.format(punctuation), ' ', s_text) s_text = re.sub('s+', ' ', s_text ).strip().lower() # Split up by characters char_list = list(s_text)
- 我们现在需要更改 LSTM 模型,使其具有多个层。我们接受
num_layers
变量并使用 TensorFlow 的MultiRNNCell()
函数创建一个多层 RNN 模型,如下所示:
class LSTM_Model(): def __init__(self, rnn_size, num_layers, batch_size, learning_rate, training_seq_len, vocab_size, infer_sample=False): self.rnn_size = rnn_size self.num_layers = num_layers self.vocab_size = vocab_size self.infer_sample = infer_sample self.learning_rate = learning_rate ... self.lstm_cell = tf.contrib.rnn.BasicLSTMCell(rnn_size) self.lstm_cell = tf.contrib.rnn.MultiRNNCell([self.lstm_cell for _ in range(self.num_layers)]) self.initial_state = self.lstm_cell.zero_state(self.batch_size, tf.float32) self.x_data = tf.placeholder(tf.int32, [self.batch_size, self.training_seq_len]) self.y_output = tf.placeholder(tf.int32, [self.batch_size, self.training_seq_len])
请注意,TensorFlow 的
MultiRNNCell()
函数接受 RNN 单元列表。在这个项目中,RNN 层都是相同的,但您可以列出您希望堆叠在一起的任何 RNN 层。
- 其他一切基本相同。在这里,我们可以看到一些训练输出:
Building Shakespeare Vocab by Characters Vocabulary Length = 40 Starting Epoch #1 of 10 Iteration: 9430, Epoch: 10, Batch: 889 out of 950, Loss: 1.54 Iteration: 9440, Epoch: 10, Batch: 899 out of 950, Loss: 1.46 Iteration: 9450, Epoch: 10, Batch: 909 out of 950, Loss: 1.49 thou art more than the to be or not to the serva wherefore art thou dost thou Iteration: 9460, Epoch: 10, Batch: 919 out of 950, Loss: 1.41 Iteration: 9470, Epoch: 10, Batch: 929 out of 950, Loss: 1.45 Iteration: 9480, Epoch: 10, Batch: 939 out of 950, Loss: 1.59 Iteration: 9490, Epoch: 10, Batch: 949 out of 950, Loss: 1.42
- 以下是最终文本输出的示例:
thou art more fancy with to be or not to be for be wherefore art thou art thou
- 最后,以下是我们如何绘制几代的训练损失:
plt.plot(train_loss, 'k-') plt.title('Sequence to Sequence Loss') plt.xlabel('Generation') plt.ylabel('Loss') plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uehlZWKg-1681566911076)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/tf-ml-cookbook-2e-zh/img/c9aa6cf7-c514-4cbe-bbfb-d815dbbb07b1.png)]
图 6:多层 LSTM 莎士比亚模型的训练损失与世代的关系图
工作原理
TensorFlow 只需一个 RNN 单元列表即可轻松将 RNN 层扩展到多个层。对于这个秘籍,我们使用与上一个秘籍相同的莎士比亚数据,但是用字符而不是单词处理它。我们通过三层 LSTM 模型来生成莎士比亚文本。我们可以看到,在仅仅 10 个周期之后,我们就能够以文字的形式产生古老的英语。
创建序列到序列模型
由于我们使用的每个 RNN 单元也都有输出,我们可以训练 RNN 序列来预测其他可变长度的序列。对于这个秘籍,我们将利用这一事实创建一个英语到德语的翻译模型。
准备
对于这个秘籍,我们将尝试构建一个语言翻译模型,以便从英语翻译成德语。
TensorFlow 具有用于序列到序列训练的内置模型类。我们将说明如何在下载的英语 - 德语句子上训练和使用它。我们将使用的数据来自 www.manythings.org 的编译 zip 文件,该文件汇编了 Tatoeba 项目 的数据。这些数据是制表符分隔的英语 - 德语句子翻译;例如,一行可能包含句子hello. /t hallo
。该数据包含数千种不同长度的句子。
此部分的代码已升级为使用 TensorFlow 官方仓库提供的神经机器翻译模型。
该项目将向您展示如何下载数据,使用,修改和添加到超参数,以及配置您自己的数据以使用项目文件。
虽然官方教程向您展示了如何通过命令行执行此操作,但本教程将向您展示如何使用提供的内部代码从头开始训练您自己的模型。
操作步骤
- 我们首先加载必要的库:
import os import re import sys import json import math import time import string import requests import io import numpy as np import collections import random import pickle import string import matplotlib.pyplot as plt import tensorflow as tf from zipfile import ZipFile from collections import Counter from tensorflow.python.ops import lookup_ops from tensorflow.python.framework import ops ops.reset_default_graph() local_repository = 'temp/seq2seq'
- 以下代码块将整个 NMT 模型仓库导入
temp
文件夹:
if not os.path.exists(local_repository): from git import Repo tf_model_repository = 'https://github.com/tensorflow/nmt/' Repo.clone_from(tf_model_repository, local_repository) sys.path.insert(0, 'temp/seq2seq/nmt/') # May also try to use 'attention model' by importing the attention model: # from temp.seq2seq.nmt import attention_model as attention_model from temp.seq2seq.nmt import model as model from temp.seq2seq.nmt.utils import vocab_utils as vocab_utils import temp.seq2seq.nmt.model_helper as model_helper import temp.seq2seq.nmt.utils.iterator_utils as iterator_utils import temp.seq2seq.nmt.utils.misc_utils as utils import temp.seq2seq.nmt.train as train
- 接下来,我们设置一些关于词汇量大小,我们将删除的标点符号以及数据存储位置的参数:
# Model Parameters vocab_size = 10000 punct = string.punctuation # Data Parameters data_dir = 'temp' data_file = 'eng_ger.txt' model_path = 'seq2seq_model' full_model_dir = os.path.join(data_dir, model_path)
- 我们将使用 TensorFlow 提供的超参数格式。这种类型的参数存储(在外部
json
或xml
文件中)允许我们以编程方式迭代不同类型的架构(在不同的文件中)。对于本演示,我们将使用提供给我们的wmt16.json
并进行一些更改:
# Load hyper-parameters for translation model. (Good defaults are provided in Repository). hparams = tf.contrib.training.HParams() param_file = 'temp/seq2seq/nmt/standard_hparams/wmt16.json' # Can also try: (For different architectures) # 'temp/seq2seq/nmt/standard_hparams/iwslt15.json' # 'temp/seq2seq/nmt/standard_hparams/wmt16_gnmt_4_layer.json', # 'temp/seq2seq/nmt/standard_hparams/wmt16_gnmt_8_layer.json', with open(param_file, "r") as f: params_json = json.loads(f.read()) for key, value in params_json.items(): hparams.add_hparam(key, value) hparams.add_hparam('num_gpus', 0) hparams.add_hparam('num_encoder_layers', hparams.num_layers) hparams.add_hparam('num_decoder_layers', hparams.num_layers) hparams.add_hparam('num_encoder_residual_layers', 0) hparams.add_hparam('num_decoder_residual_layers', 0) hparams.add_hparam('init_op', 'uniform') hparams.add_hparam('random_seed', None) hparams.add_hparam('num_embeddings_partitions', 0) hparams.add_hparam('warmup_steps', 0) hparams.add_hparam('length_penalty_weight', 0) hparams.add_hparam('sampling_temperature', 0.0) hparams.add_hparam('num_translations_per_input', 1) hparams.add_hparam('warmup_scheme', 't2t') hparams.add_hparam('epoch_step', 0) hparams.num_train_steps = 5000 # Not use any pretrained embeddings hparams.add_hparam('src_embed_file', '') hparams.add_hparam('tgt_embed_file', '') hparams.add_hparam('num_keep_ckpts', 5) hparams.add_hparam('avg_ckpts', False) # Remove attention hparams.attention = None
TensorFlow 机器学习秘籍第二版:9~11(3)https://developer.aliyun.com/article/1426844