【NLP】【十一】基於RNN和tf.keras 實現文字生成
【一】宣告
本文源自TensorFlow官方指導(https://tensorflow.google.cn/tutorials/sequences/text_generation),增加了部分細節說明。
【二】綜述
1. tf.keras與keras有如下三個較大的不同點
1):opt必須是tf.train模組下的opt,不能是keras下的opt
2):tf.keras模型預設的儲存格式時check-point,不是h5.
3): tf.keras進行模型訓練、推理時,input_data可以直接傳遞tf.data.Dataset
2. TensorFlow的官方案例,是依據字元進行文字生成的,基本流程是,給定一個句子,預測其下一個字元。因此該模型不知道單詞是怎麼拼寫的,不知道字成詞,因為他是字元級的,它只知道預測下一個字元。因此,可能生成了不存在的單詞或者詞語。
3. 該模型只有三層(char-embedding、GRU、FC),但引數巨大,訓練十分緩慢(i7CPU訓練一個epoch差不多半個小時)。而且這裡,char-embedding是直接訓練出來了,而不是通過fasttext或者gensim訓練出來,然後在做fine-tuning的。
【三】程式碼如下:
# -*- coding:utf-8 -*- import tensorflow as tf import numpy as np import os import time tf.enable_eager_execution() # 1. 資料下載 path = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt') #2. 資料預處理 with open(path) as f: # text 是個字串 text = f.read() # 3. 將組成文字的字元全部提取出來,注意 vocab是個list vocab = sorted(set(text)) # 4. 建立text-->int的對映關係 char2idx = {u:i for i, u in enumerate(vocab)} idx2char = np.array(vocab) text_as_int = np.array([char2idx[c] for c in text]) # 5. 借用dataset的batch方法,將text劃分為定長的句子 seq_length = 100 examples_per_epoch = len(text)//seq_length char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int) # 這裡batch_size 加1的原因在於,下面對inputs和labels的生成。labels比inputs多一個字元 sequences = char_dataset.batch(seq_length+1, drop_remainder=True) # 6. 將每個句子劃分為inputs和labels。例如:hello,inputs = hell,label=ello def split_input_target(chunk): input_text = chunk[:-1] target_text = chunk[1:] return input_text, target_text dataset = sequences.map(split_input_target) # 7. 將句子劃分為一個個batch BATCH_SIZE = 64 steps_per_epoch = examples_per_epoch//BATCH_SIZE BUFFER_SIZE = 10000 # drop_remainder 一般需要設定為true,表示當最後一組資料不夠劃分為一個batch時,將這組資料丟棄 dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True) # 8. 模型搭建 # Length of the vocabulary in chars vocab_size = len(vocab) # The embedding dimension embedding_dim = 256 # Number of RNN units rnn_units = 1024 model = tf.keras.Sequential() # 這裡是字元embedding,所以是字符集大小*embedding_dim model.add(tf.keras.layers.Embedding(input_dim=vocab_size,output_dim=embedding_dim, batch_input_shape=[BATCH_SIZE,None])) model.add(tf.keras.layers.GRU(units=rnn_units, return_sequences=True, recurrent_initializer='glorot_uniform', stateful=True)) model.add(tf.keras.layers.Dense(units=vocab_size)) model.summary() # 9. 模型配置 # optimizer 必須為 tf.train 下的opt,不能是keras下的opt model.compile(optimizer=tf.train.AdamOptimizer(),loss=tf.losses.sparse_softmax_cross_entropy) # 10 .設定回撥函式 checkpoint_dir = './training_checkpoints' checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}") checkpoint_callback=tf.keras.callbacks.ModelCheckpoint( filepath=checkpoint_prefix, save_weights_only=True) tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir='./logs/') # 11. 訓練模型,repeat() 表示dataset無限迴圈,不然這裡資料可能不夠30個epochs model.fit(dataset.repeat(),epochs=30, steps_per_epoch=steps_per_epoch, callbacks=[checkpoint_callback,tensorboard_callback]) # 12 .模型儲存 # 儲存為keras模型格式 model.save_weights(filepath='./models/gen_text_with_char_on_rnn.h5',save_format='h5') # 儲存為TensorFlow的格式 model.save_weights(filepath='./models/gen_text_with_char_on_rnn_check_point') # 13. 模型生成文字 def generate_text(model, start_string): # Evaluation step (generating text using the learned model) # Number of characters to generate num_generate = 1000 # You can change the start string to experiment start_string = 'ROMEO' # Converting our start string to numbers (vectorizing) input_eval = [char2idx[s] for s in start_string] input_eval = tf.expand_dims(input_eval, 0) # Empty string to store our results text_generated = [] # Low temperatures results in more predictable text. # Higher temperatures results in more surprising text. # Experiment to find the best setting. temperature = 1.0 # Here batch size == 1 model.reset_states() for i in range(num_generate): predictions = model(input_eval) # remove the batch dimension predictions = tf.squeeze(predictions, 0) # using a multinomial distribution to predict the word returned by the model predictions = predictions / temperature predicted_id = tf.multinomial(predictions, num_samples=1)[-1, 0].numpy() # We pass the predicted word as the next input to the model # along with the previous hidden state input_eval = tf.expand_dims([predicted_id], 0) text_generated.append(idx2char[predicted_id]) return (start_string + ''.join(text_generated)) print(generate_text(model, start_string="ROMEO: "))
【四】總結
1. 關於tf.keras更多的內容,可以參考官方網站(https://tensorflow.google.cn/guide/keras)
2. 關於tf.dataset的更多內容,可以參考官方網站(https://tensorflow.google.cn/guide/datasets)和另外一篇部落格(https://my.oschina.net/u/3800567/blog/1637798)
3. 可以完全使用tf.keras,不再使用keras。二者功能與介面一致,tf.keras提供了更多的對TensorFlow的支援