手把手教您使用LSTM生成唐詩

參考博客:《安娜卡列尼娜》文本生成——利用TensorFlow構建LSTM模型

一、引入安裝包
import time
from collections import namedtuple
import numpy as np
import tensorflow as tf
二、將全部故事轉變成數字
# 處理輸入數據
with open(r'./tangshi.txt','r',encoding='utf-8') as f:
    text=f.read()
#set就是將所有的文字全部轉變成爲一個數字集合
vocab=set(text)
vocab_to_int={c:i for i,c in enumerate(vocab)}
# 把字轉變成爲對應的數字
int_to_vocab=dict(enumerate(vocab))
# 把數字轉變成對應的字
encoded=np.array([vocab_to_int[c] for c in text] ,dtype=np.int32)
#將全部古詩轉變成爲數字,np.array()需要指出數據的類型

進行一個小驗證,觀察一下輸出是不是挺合理

###進行一些輸出的驗證
print("len(encoded):",len(encoded))
print("len(vocab):",len(vocab))
"""
len(encoded): 3451603
len(vocab): 7649
挺合理
"""
三、創建mini-batch
batch_size=64
time_steps=50
# 創建mini-batch,投給輸入以及輸出
def generate_batch(encoded,batch_size,time_steps):
    num_batch=len(encoded)//batch_size//time_steps
    # num_batch表示batch的個數,我們只保留整除的結果
    arr=encoded[:num_batch*batch_size*time_steps].reshape(batch_size,-1)
    x=[]
    y=[]
    for i in range(0,arr.shape[1],time_steps):
        x=arr[:,i:i+time_steps]
        y=np.zeros_like(x)
        # 複製一份
        y[:,:-1]=x[:,1:]
        # 說明已經到頭了,隨便賦予一個數值就行
        if (i+time_steps)==arr.shape[1]:
            y[:,-1]=y[:,0]
        else:
            y[:,-1]=arr[:,i+time_steps]
        yield x,y  
"""
使用yield可以怎麼使用
batches=generate_batch(encoded,batch_size,time_steps)
x,y=next(batches)
"""

四、構建計算圖

# 開始設置各種變量,構建計算圖
tf.reset_default_graph()
batch_size=64
input_dim=len(vocab)
time_steps=50
learning_rate=0.01
num_units=128
num_layers=2
output_dim=len(vocab)
grad_clip=5
#隱藏層單元數128
inputs=tf.placeholder(name="inputs",shape=[None,time_steps],dtype=tf.int32)
# 對應batch_x
targets=tf.placeholder(name="targets",shape=[None,time_steps],dtype=tf.int32)
# 對應batch_y
# 輸入以及輸出都是二維的
input_data=tf.one_hot(inputs,len(vocab))
print("input_data.shape:",input_data.shape)
# 將輸入轉變成三維的one-hot編碼
# 構建lstm單元
# lstm_cell=tf.contrib.rnn.LayerNormBasicLSTMCell(num_units)
cells=tf.contrib.rnn.MultiRNNCell([tf.contrib.rnn.LayerNormBasicLSTMCell(num_units) for _ in range(num_layers)])
initial_state=cells.zero_state(batch_size,dtype=tf.float32)
"""
這裏的state就是初始化了state.c以及state.h分別表示長時記憶以及短時記憶
"""
out,state=tf.nn.dynamic_rnn(cells, input_data, initial_state=initial_state)
# 創建輸出層,輸出的數據是三維的,展開
out=tf.reshape(out,[-1,num_units])
print("out.shape:",out.shape)
out_weights=tf.get_variable(name="out_weights",shape=[num_units,output_dim],dtype=tf.float32)
out_bias=tf.get_variable(name="out_bias",shape=[output_dim],dtype=tf.float32)
logits=tf.add(tf.matmul(out,out_weights),out_bias)
print("logits.shape:",logits.shape)
predictions=tf.nn.softmax(logits,name="predictions")# 每隔所佔的標準比例
# 計算損失
outputs=tf.one_hot(targets,len(vocab))
print("outputs.shape:",outputs.shape)
outputs=tf.reshape(outputs,[-1,len(vocab)])
print("outputs.shape:",outputs.shape)
loss=tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=outputs)
print("loss.shape:",loss.shape)
loss=tf.reduce_mean(loss)
# 確定優化函數
tvars=tf.trainable_variables()##找出需要訓練的參數變量
grads,_=tf.clip_by_global_norm(tf.gradients(loss,tvars),grad_clip)
train_op=tf.train.AdamOptimizer(learning_rate)
optimizer=train_op.apply_gradients(zip(grads,tvars))

"""
輸出:
input_data.shape: (?, 50, 7649)
out.shape: (3200, 128)
logits.shape: (3200, 7649)
outputs.shape: (?, 50, 7649)
outputs.shape: (?, 7649)
loss.shape: (3200,)
"""

【在這個過程中遇到的小問題:】

在Tensorboard中展示計算圖:
在這裏插入圖片描述

五、進行訓練
# 開始進行訓練以及測試
init=tf.global_variables_initializer()
epochs=20
save_every_n=400
batches=generate_batch(encoded,batch_size,time_steps)
saver=tf.train.Saver(max_to_keep=10)
with tf.Session() as sess:
    sess.run(init)
    sess.run(initial_state)
    count=0
    for index in range(epochs):
        for x,y in generate_batch(encoded,batch_size,time_steps):
            count+=1
            start=time.time()
            feed={inputs:x,targets:y}
            sess.run(optimizer,feed_dict=feed)
            loss_=sess.run(loss,feed_dict=feed)
            end=time.time()
            if count%500==0:
                print("輪數:{}/{}".format(index,epochs),
                      "訓練步數:{}".format(count),
                      "損失:{:4f}".format(loss_),
                      "時間{:.4f}".format((end-start)),
                     )
            if count%save_every_n==0:
                saver.save(sess,"./save/{}_{}_{}.ckpt".format(count,index,epochs))
    saver.save(sess,"./save/{}_{}_{}.ckpt".format(count,index,epochs))
 

在進行訓練的過程中,由於特殊情況停止了訓練,然後需要從頭開始訓練,非常麻煩,所以在有了下邊的代碼,加入了斷電之後可以繼續生成的功能。

# 開始進行訓練以及測試
init=tf.global_variables_initializer()
epochs=2
save_every_n=200
batches=generate_batch(encoded,batch_size,time_steps)
saver=tf.train.Saver(max_to_keep=10)
with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess:
    sess.run(init)
    sess.run(initial_state)
    # 加入斷點續訓功能
    ckpt = tf.train.get_checkpoint_state("./save/")
    print(ckpt)
    if ckpt and ckpt.model_checkpoint_path:
        saver.restore(sess, ckpt.model_checkpoint_path)
    count=0
    for index in range(epochs):
        for x,y in generate_batch(encoded,batch_size,time_steps):
            count+=1
            start=time.time()
            feed={inputs:x,targets:y}
            sess.run(optimizer,feed_dict=feed)
            loss_=sess.run(loss,feed_dict=feed)
            gs=tf.train.global_step(sess, global_steps)
            #gs=sess.run(global_steps) 這條語句也可以
            end = time.time()
            if count%10==0:
                print("輪數:{}/{}".format(index,epochs),
                      "訓練步數:{}".format(gs),
                      "計數次數:{}".format(count),
                      "損失:{:.4f}".format(loss_),
                      "{:.4f}".format((end-start)))
            if count%save_every_n==0:
                saver.save(sess,"./save/{}_{}_{}.ckpt".format(count,index,epochs))
    saver.save(sess,"./save/{}_{}_{}.ckpt".format(count,index,epochs))

部分輸出展示:
輪數:0/2 訓練步數:210 計數次數:10 損失:7.1789 0.6681
輪數:0/2 訓練步數:220 計數次數:20 損失:6.7573 0.6814
輪數:0/2 訓練步數:230 計數次數:30 損失:6.6701 0.6787
輪數:0/2 訓練步數:240 計數次數:40 損失:6.7215 0.6784
輪數:0/2 訓練步數:250 計數次數:50 損失:6.6892 0.6803
輪數:0/2 訓練步數:260 計數次數:60 損失:6.6715 0.6945
輪數:0/2 訓練步數:270 計數次數:70 損失:6.7109 0.6776
輪數:0/2 訓練步數:280 計數次數:80 損失:6.6895 0.6671
輪數:0/2 訓練步數:290 計數次數:90 損失:6.7066 0.6769
輪數:0/2 訓練步數:300 計數次數:100 損失:6.6703 0.7228
輪數:0/2 訓練步數:310 計數次數:110 損失:6.7029 0.6847
輪數:0/2 訓練步數:320 計數次數:120 損失:6.6948 0.6739
輪數:0/2 訓練步數:330 計數次數:130 損失:6.6475 0.6792
輪數:0/2 訓練步數:340 計數次數:140 損失:6.7281 0.6739
輪數:0/2 訓練步數:350 計數次數:150 損失:6.6740 0.6796
輪數:0/2 訓練步數:360 計數次數:160 損失:6.6269 0.7110
輪數:0/2 訓練步數:370 計數次數:170 損失:6.6863 0.6875
輪數:0/2 訓練步數:380 計數次數:180 損失:6.6472 0.6754
輪數:0/2 訓練步數:390 計數次數:190 損失:6.6592 0.7117
輪數:0/2 訓練步數:400 計數次數:200 損失:6.7368 0.6814
輪數:0/2 訓練步數:410 計數次數:210 損失:6.7022 0.6690
輪數:0/2 訓練步數:420 計數次數:220 損失:6.7272 0.6770
輪數:0/2 訓練步數:430 計數次數:230 損失:6.6623 0.6487
輪數:0/2 訓練步數:440 計數次數:240 損失:6.6454 0.6741
輪數:0/2 訓練步數:450 計數次數:250 損失:6.6999 0.6757
輪數:0/2 訓練步數:460 計數次數:260 損失:6.6248 0.6642
輪數:0/2 訓練步數:470 計數次數:270 損失:6.6507 0.6734
輪數:0/2 訓練步數:480 計數次數:280 損失:6.5726 0.6740
輪數:0/2 訓練步數:490 計數次數:290 損失:6.5282 0.6848
輪數:0/2 訓練步數:500 計數次數:300 損失:6.5218 0.6756
輪數:0/2 訓練步數:510 計數次數:310 損失:6.4808 0.6770
輪數:0/2 訓練步數:520 計數次數:320 損失:6.4407 0.6653
輪數:0/2 訓練步數:530 計數次數:330 損失:6.4833 0.6680
輪數:0/2 訓練步數:540 計數次數:340 損失:6.4142 0.6830
輪數:1/2 訓練步數:550 計數次數:350 損失:6.3838 0.6533
輪數:1/2 訓練步數:560 計數次數:360 損失:6.3213 0.6808
輪數:1/2 訓練步數:570 計數次數:370 損失:6.2740 0.6749
輪數:1/2 訓練步數:580 計數次數:380 損失:6.2192 0.6863
輪數:1/2 訓練步數:590 計數次數:390 損失:6.1916 0.6983
輪數:1/2 訓練步數:600 計數次數:400 損失:6.1218 0.6693
輪數:1/2 訓練步數:610 計數次數:410 損失:6.0958 0.6999
輪數:1/2 訓練步數:620 計數次數:420 損失:6.0795 0.6878

補充知識:

1、np.random.choice(a,b,p)
表示的含義:從a中按照概率p選擇b個數字。
2、tensorflow如何實現斷點續訓:參考博客
3、參考鏈接:https://github.com/NELSONZHAO/zhihu/blob/master/anna_lstm/anna_lstm-tf1.0.ipynb

歡迎批評指指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章