引言
在上一篇博客中,介紹了各種Python的第三方庫的安裝,本周將要使用Tensorflow完成第一個神經網絡,BP神經網絡的編寫。由於之前已經介紹過了BP神經網絡的內部結構,本文將直接介紹Tensorflow編程常用的一些方法。
正文
神經網絡的內容
一般,一個神經網絡程序包含以下幾部分內容。
1.數據表達和特征提取。對於一個非深度學習神經網絡,主要影響其模型准確度的因素就是數據表達和特征提取。同樣的一組數據,在歐式空間和非歐空間,就會有着不同的分布。有時候換一種思考問題的思路就會使得問題變得簡單。所以選擇合適的數據表達可以極大的降低解決問題的難度。同樣,在機器學習中,特征的提取也不是一種簡單的事。在一些復雜問題上,要通過人工的方式設計有效的特征集合,需要很多的時間和精力,有時甚至需要整個領域數十年的研究投入。例如,PCA獨立成分分析就是特征提取中常用的手段之一。但是很多情況下,人為都很難提取出合適的特征。
由於不同問題下,可以提取不同的特征向量,這里將不做具體介紹。事實上深度學習解決的核心問題之一就是自動地將簡單的特征組合成更加復雜的特征,並使用這些組合特征解決問題。
2.定義神經網絡的結構。由神經網絡發展的歷史可知,不同結構的神經網絡在不同的問題下得到的效果不同。因此分析問題,選擇與問題合適的神經網絡結構也同樣重要。
3.訓練神經網絡的參數。使用訓練數據集訓練神經網絡。主要是利用神經網絡輸出誤差反向傳播修正神經網絡中的參數,甚至結構。反向傳播過程中,步長選擇對神經網絡的訓練有着重要的影響,在此基礎上產生了多種訓練方法。將在后面介紹。
4.使用訓練好的神經網絡預測未知數據。訓練神經網絡的目的就是對未知數據預測。
具體過程可以由如下流程圖表示:
BP神經網絡
這里將按照流程圖,詳細構造BP神經網絡。
1.數據預處理
這里將使用自造的數據模擬實際數據,通過對自造的數據仿真,驗證BP神經網絡的擬合能力。模型將使用一個單輸入單輸出一階慣性傳遞函數模型。模型結構如下所示:
為了很好的激勵出模型的特性,X將用隨機數來表示。具體制造模型,例子:
導入第三方庫,后面將不再展示。
import tensorflow as tf import numpy as np import matplotlib.pyplot as plt
導入完成后就需要生成輸入與輸出的數據了。由於現實中各種數據之間的單位不同,直接使用時,由於數量級的問題往往會導致神經網絡建模出現一些奇怪的問題。所以建模數據是需要歸一化的。而這里為了方便,將直接生成0~1的數據。例子:
#生成輸入輸出數據 input_size = 1#輸入變量個數 output_size = 1#輸出變量個數 data_size = 2000#樣本個數 k,T,x_state = 0.5,200,0#比例增益,時間常數,初始狀態 x_data = np.random.rand(data_size,input_size)#輸入數據0~1之間 y_data = np.zeros((data_size,output_size))#初始化輸出數據 t_conv = np.e**(-1/T)#零階保持器,采樣周期1s for i in range(data_size): x_state = t_conv*x_state + k*(1-t_conv)*x_data[i] y_data[i] = x_state
這里使用零階保持的方法,將傳遞函數離散化后生成了共2000點的數據。這樣就完成了數據預處理。
2.神經網絡結構
由於已經確定要使用BP神經網絡為傳遞函數建模。所以直接按照一層隱含層的BP神經網絡建立結構就可以了。而在建立前,需要學習Tensorflow中張量的相關操作。
首先,在Tensorflow中定義張量的方法是。例子:
a = tf.constant([1,2,3]) print(a)
從運行結果可以發現,Tensorflow中的張量與numpy中的向量不同,張量中包含着名字,維度,和類型三種屬性。張量是建立在計算圖上的,通過使用會話,就可以計算不同的數據。具體計算圖將在之后Tensorflow可視化中介紹,這里暫時就可以理解為一個函數。Tensorflow中還含有不同的隨機數常數生成函數,可以幫助建立神經網絡中的權值和閾值。例子:
tf.random_normal()#正太分布,可設置平均值、標准差、取值類型 tf.truncated_normal()#正態分布,但隨機值偏離平均值2個標准差以內 tf.random_uniform()#平均分布,可設置最小值、最大值、取值類型 tf.random_gamma()#gamma分布,可設置形狀參數,尺度參數,取值類型 tf.zeros()#產生全0數組 tf.ones()#產生全1數組 tf.fill()#產生全部為給定數字的數組
特殊的,當不指導輸入數據的長度時,可以用一個占位來定義。例子:
a = tf.placeholder(tf.float32,[None,1])
定義好節點以后,就需要進行計算,常用的計算中加減乘除與之前相同,下面將介紹一些不同的。例子:
a = tf.Variable(tf.ones((1,3))) b = tf.Variable(tf.ones((3,1))) c = tf.matmul(a,b)#向量乘法 d = tf.nn.relu(c)#激活函數ReLU e = tf.nn.sigmoid(c)#激活函數sigmoid f = tf.nn.tanh(c)#激活函數tanh
依靠以上內容,就可以建立BP神經網絡的結構。例子:
#神經網絡結構 batch_size = 50#訓練batch的大小 hide_size = 5#隱藏神經元個數 #預留每個batch中輸入與輸出的空間 x = tf.placeholder(tf.float32,shape = (None,input_size))#None隨batch大小變化 y_pred = tf.placeholder(tf.float32,shape = (None,output_size)) w_hidden = tf.Variable(tf.random_normal([input_size,hide_size],stddev = 1,seed = 1)) b_hidden = tf.Variable(tf.zeros([1,hide_size],dtype = tf.float32)) w_output = tf.Variable(tf.random_normal([hide_size,output_size],stddev = 1,seed = 1)) #定義前向傳播過程 h = tf.nn.tanh(tf.matmul(x,w_hidden)+b_hidden) y = tf.nn.sigmoid(tf.matmul(h,w_output))
之后反向傳播Tensorflow可以自動進行,我們只需要定義損失函數和反向計算算法就可以了。神經網絡模型的效果以及優化的目標是通過損失函數來定義的。這就是使用Tensorflow的優勢。根據不同的用途,損失函數有着不同的定義方法。對於分類問題,可以使用交叉熵來定義。交叉熵的計算公式如下:
編程例子:
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y,1e-10,1.0)))#定義值與真實值間的交叉熵
這里tf.clip_by_value可以將一個張量中的數值限制在一個范圍內,避免運算錯誤。由於交叉熵經常與softmax回歸一起使用,Tensorflow將它們進行了封裝。例子:
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(y,y_)
與分類問題不同,本次例子解決的是回歸問題。對於具體數值的預測,應該使用MSE均方誤差作為損失函數。例子:
cross_entropy = tf.reduce_mean(tf.square(y_ - y))#定義損失函數
並且,損失函數還可以自己定義,這里不再具體詳述。
3.訓練神經網絡
訓練神經網絡時,要用到Tensorflow中另一個重要的內容,會話。會話擁有Tensorflow程序運行時的所有資源,並且可以對其進行管理。會話的打開與關閉與文件的讀取類似。例子:
#方法1 sess=tf.Session()#創建會話 sess.run()#運行會話——類比打開文件 #具體會話執行內容 sess.close#關閉會話——類比關閉文件 #方法2 with tf.Session() as sess: sess.run() #通過縮進自動關閉
在使用sess.run( )運行計算圖時,我們可以傳入fetches,用於取回某些操作或tensor的輸出內容。fetches可以是list,tuple,namedtuple,dict中的任意一個。例子:
x = tf.constant([1]) y = tf.constant([2]) a = x+y with tf.Session() as sess: z = sess.run(a) print(z)
同樣在使用sess.run( )運行計算圖時,我們可以傳入feed,用於臨時替代計算圖中任意op操作的輸入張量。例子:
x = numpy.array([2]) y = numpy.array([3]) input1 = tf.placeholder(tf.int32) input2 = tf.placeholder(tf.int32) output = input1+input2 with tf.Session() as sess: print(sess.run(output, feed_dict = {input1:x, input2:y}))
4.訓練集,測試集。
一般情況下需要將數據分為2類,一部分用來訓練模型,一部分用來測試模型。這樣就完成了最簡單的BP神經網絡的建立。
作業
使用普通BP神經網絡擬合傳遞函數
#-*- coding:utf-8 -*- #BP neural network #Author:Kai Z import tensorflow as tf import numpy as np import matplotlib.pyplot as plt #生成輸入輸出數據 input_size = 1#輸入變量個數 output_size = 1#輸出變量個數 data_size = 2000#樣本個數 x_data = np.random.rand(data_size,input_size)#輸入數據0~1之間 y_data = np.zeros((data_size,output_size))#初始化輸出數據 k,T,x_state = 0.5,200,0 t_conv = np.e**(-1/T)#零階保持器,采樣周期1s for i in range(data_size): x_state = t_conv*x_state + k*(1-t_conv)*x_data[i] y_data[i] = x_state #神經網絡結構 batch_size = 50#訓練batch的大小 hide_size = 5#隱藏神經元個數 #預留每個batch中輸入與輸出的空間 x = tf.placeholder(tf.float32,shape = (None,input_size))#None隨batch大小變化 y = tf.placeholder(tf.float32,shape = (None,output_size)) w_hidden = tf.Variable(tf.random_normal([input_size,hide_size],stddev = 1,seed = 1)) b_hidden = tf.Variable(tf.zeros([1,hide_size],dtype = tf.float32)) w_output = tf.Variable(tf.random_normal([hide_size,output_size],stddev = 1,seed = 1)) #定義前向傳播過程 h = tf.nn.tanh(tf.matmul(x,w_hidden)+b_hidden) y_pred = tf.nn.sigmoid(tf.matmul(h,w_output)) #反向損失函數 learning_rate = 2e-3#學習速率 cross_entropy = tf.reduce_mean(tf.square(y_pred - y))#定義損失函數 train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)#定義反向傳播優化方法 #創建會話 with tf.Session() as sess: init_op = tf.global_variables_initializer() sess.run(init_op)#初始化變量 #設定訓練次數 STEPS = 10000#訓練次數 for i in range(STEPS): #選取訓練batch start = max((i * batch_size) % 1000,20) end = min(start + batch_size,1000)#取前1000點訓練 #計算 sess.run(train_step,feed_dict = {x:x_data[start:end],y:y_data[start:end]}) #顯示誤差 if i % 100 == 0: total_cross_entropy = sess.run(cross_entropy,feed_dict = {x:x_data[1000:1500],y:y_data[1000:1500]})#1000~1500測試 print('訓練%d次后,誤差為%f'%(i,total_cross_entropy)) if total_cross_entropy <= 1e-3: break else: print('未達到訓練目標') exit() #保存結果 saver = tf.train.Saver() file_path = 'D:/Study/Project/Hobby/Python/03 Study/02 Learn/test' save_path = saver.save(sess,file_path) predict = sess.run(y_pred,feed_dict={x:x_data}) predict = predict.ravel()#轉換為向量 orange = y_data.ravel() #建立時間軸 t = np.arange(2000) plt.plot(t,predict) plt.plot(t,orange) plt.show()
后記
可以看出,普通BP神經網絡對動態傳遞函數的擬合效果並不是很好。這是由於神經網絡是靜態的,而傳遞函數是動態的。要想辨識動態系統,也得使用動態的神經網絡。如最簡單的NARX神經網絡。或者更復雜的RNN。之后將進行RNN的實現。