一、回歸神經網絡
1、神經網絡結構
定義一個簡單的回歸神經網絡結構:
- 數據集為(xi,yi),數據的特征數為1,所以x的維度為1。
- 輸入層1個神經元。
- 隱藏層數為1,4個神經元。
- 輸出層1個神經元。
- 隱藏層的激活函數為f(x)=x,輸出層的激活函數為ReLU
結構圖如下:
2、代碼示例
相關函數說明:
- tf.random_normal :用於生成正太分布隨機數矩陣的tensor,tensorFlow有很多隨機數函數,可以查找官方文檔獲得。
- tf.zeros :用於生成0矩陣的tensor,tf.ones可以用來獲得單位矩陣。
- tf.nn.relu :tensorflow定義的用來實現ReLU激活函數的方法。
- tf.reduce_sum :求和函數,通過axis來控制在哪個方向上求和,axis=[0]表示按行求和,axis=[1]表示按列求和。
- tf.train.GradientDescentOptimizer(learning_rate).minimize(loss) :梯度下降優化函數,learning_rate表示學習率,minimize表示最小化,loss是優化的損失函數。tensorFlow有很多優化函數,可以查找官方文檔獲得。
代碼:
import tensorflow as tf import numpy as np import matplotlib.pyplot as plt # 創建數據訓練數據集, x_data = np.linspace(-1, 1, 500).reshape(500, 1) noise = np.random.normal(0, 0.05, [500, 1]) # 制作噪音 y_data = np.square(x_data) + 0.5 + noise # 創建占位符用於minibatch的梯度下降訓練,建議數據類型使用tf.float32、tf.float64等浮點型數據 x_in = tf.placeholder(tf.float32, [None, 1]) y_in = tf.placeholder(tf.float32, [None, 1]) # 定義一個添加層的函數 def add_layer(input_, in_size, out_size, activation_funtion=None): ''' :param input_: 輸入的tensor :param in_size: 輸入的維度,即上一層的神經元個數 :param out_size: 輸出的維度,即當前層的神經元個數即當前層的 :param activation_funtion: 激活函數 :return: 返回一個tensor ''' weight = tf.Variable(tf.random_normal([in_size, out_size])) # 權重,隨機的in_size*out_size大小的權重矩陣 biase = tf.Variable(tf.zeros([1, out_size]) + 0.01) # 偏置,1*out_size大小的0.01矩陣,不用0矩陣避免計算出錯 if not activation_funtion: # 根據是否有激活函數決定輸出 output = tf.matmul(input_, weight) + biase else: output = activation_funtion(tf.matmul(input_, weight) + biase) return output # 定義隱藏層,輸入為原始數據,特征為1,所以輸入為1個神經元,輸出為4個神經元 layer1 = add_layer(x_in, 1, 4, tf.nn.relu) # 定義輸出層,輸入為layer1返回的tensor,輸入為4個神經元,輸出為1個神經元,激活函數為ReLU predict = add_layer(layer1, 4, 1) # 定義損失函數 loss = tf.reduce_mean(tf.reduce_sum(tf.square(y_in - predict), axis=[1])) # tf.reduce_sum的axis=[1]表示按列求和 # 定義訓練的優化方式為梯度下降 train = tf.train.GradientDescentOptimizer(0.1).minimize(loss) # 學習率為0.1 init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) # 訓練1000次 for step in range(1000): # 執行訓練,因為有占位符所以要傳入字典,占位符的好處是可以用來做minibatch訓練,這里數據量小,直接傳入全部數據來訓練 sess.run(train, feed_dict={x_in: x_data, y_in: y_data}) # 每50步輸出一次loss if step % 49 == 0: print(sess.run(loss, feed_dict={x_in: x_data, y_in: y_data})) # 最后畫出實際的散點圖與擬合的折線圖進行對比 predict_value = sess.run(predict, feed_dict={x_in: x_data}) # 先要獲得預測值 plt.figure() plt.scatter(x_data, y_data, c='r', marker='o') plt.plot(x_data, predict_value, '--', lw=2, c='b') plt.show()
二、分類神經網絡——mnist手寫字數據
1、網絡結構與數據來源
數據的詳細說明請查看:tensorflow中文社區。
結構說明:
- 數據集(xi,yi):數據共有60000張圖。其中xi是表示的是每一個圖片的數據,長度為28x28 = 784,即一張圖片特征為784列;yi有0-9共10種結果,由於是分類,所以使用softmax函數,則yi最后對應的輸出層需要有10個神經元對應,有幾個類就有幾個神經元。
- 隱藏層:個數為0,定義隱藏層擬合效果較差,所以這里不定義。
- 輸出層:為10個神經元,激活函數為softmax。
- 損失函數loss:交叉熵損失函數。(交叉熵損失請查閱:詳解機器學習中的熵、條件熵、相對熵和交叉熵)
- 迭代方法:由於數據集過大,使用minbtach方法。
- 准確度的計算 :tensorflow中文社區中有詳細說明。
2、代碼示例
相關函數說明:
-
input_data.read_data_sets(train_dir='MNIST_data',one_hot=True) :函數作用說明,讀取數據,代碼會自動下載數據(若網絡原因可自行下載),下載的是數據文件夾,在當前工作目錄下,里面包含訓練集mnist.train(包含特征mnist.train.images與標簽mnist.train.labels)與測試集mnist.test(包含特征mnist.test.images與標簽mnist.test.labels)。參數說明,train_dir:數據相對路徑,one_hot:是否為獨熱編碼。mnist.train.next_batch。
- tf.argmax(input,dimension) :返回input張量的最大值所在的位置,dimension=1為列最大。
- tf.equal(x,y) :返回一個bool數組,當x==y則值為True。
-
mnist.train.next_batch(batch_size) :用來獲取mnist每批次的數據,每次使用會自動獲取下一批batch_size大小的數據集。
代碼:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 讀取數據,代碼會自動下載數據,下載的是數據文件夾,在當前工作目錄下,里面包含訓練集與測試集,train_dir:數據路徑,one_hot:是否為獨熱編碼。
mnist = input_data.read_data_sets(train_dir='MNIST_data', one_hot=True)
# 數據較大,因此用minbatch,batch_size每個批次的數據個數,batch_num為批次個數
batch_size = 1000
batch_num = mnist.train.num_examples // batch_size
# 創建占位符用於minibatch的梯度下降訓練,建議數據類型使用tf.float32、tf.float64等浮點型數據
x_in = tf.placeholder(tf.float32, [None, 784])
y_in = tf.placeholder(tf.float32, [None, 10])
# 定義一個添加層的函數
def add_layer(input_, in_size, out_size, activation_funtion=None):
'''
:param input_: 輸入的tensor
:param in_size: 輸入的維度,即上一層的神經元個數
:param out_size: 輸出的維度,即當前層的神經元個數即當前層的
:param activation_funtion: 激活函數
:return: 返回一個tensor
'''
weight = tf.Variable(tf.random_normal([in_size, out_size])) # 權重,隨機的in_size*out_size大小的權重矩陣
biase = tf.Variable(tf.zeros([1, out_size]) + 0.01) # 偏置,1*out_size大小的0.01矩陣,不用0矩陣避免計算出錯
if not activation_funtion: # 根據是否有激活函數決定輸出
output = tf.matmul(input_, weight) + biase
else:
output = activation_funtion(tf.matmul(input_, weight) + biase)
return output
# 定義輸出層,輸入為layer1返回的tensor,輸入為784個神經元,輸出為10個神經元,激活函數為softmax。
prediction = add_layer(x_in, 784, 10) # 這個對應下一步定義交叉熵損失函數的method1,不需要激活函數softmax
# 定義交叉熵損失函數,這里用method1
# method1:用自帶的函數,這個函數會自動對prediction進行softmax操作所以不需要前面定義prediction不需要激活函數
cross_entropy = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=y_in, logits=prediction))
# #method2:手動計算,這里tf.clip_by_value函數的作用是為了避免輸入為負數或者0,log函數的定義域是大於0的,不這么做會出現LOSS=NAN且模型准確度不變的情況,這個坑會在代碼示例后面說明。
# cross_entropy = -tf.reduce_sum(y_in * tf.log(tf.clip_by_value(prediction,1e-8,1.0)))
# 定義訓練的優化方式為梯度下降
train = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy) # 學習率為0.1
# 准確度,先得到bool型的數組correct_prediction ,再計算值為True的平均值即為准確率
correct_prediction = tf.equal(tf.argmax(y_in, 1), tf.argmax(prediction, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
# 訓練20代
for epoch in range(20):
# 每代對數據進行一輪minibatch
for batch in range(batch_num):
batch_x, batch_y = mnist.train.next_batch(batch_size) # 每個循環讀取batch_size大小批次的數據
sess.run(train, feed_dict={x_in: batch_x, y_in: batch_y})
acc = sess.run(accuracy, feed_dict={x_in: mnist.test.images, y_in: mnist.test.labels}) # 用測試數據計算准確度
print('第%d代%d批次,准確率為%.6f' % (epoch + 1, batch + 1, acc))
3、 使用交叉商的坑
第一個坑:
使用method1計算較差熵時,定義prediction不能有激活函數tf.nn.softmax,因為tf.nn.softmax_cross_entropy_with_logits函數會自動對prediction進行softmax處理,所以正確的代碼如下:
#定義輸出層 prediction = add_layer(x_in, 784, 10) #定義交叉熵 cross_entropy = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=y_in, logits=prediction))
可以嘗試使用method1計算交叉熵,並定義prediction的時候傳入激活函數,會發現擬合效果變差,代碼如下
#定義輸出層 prediction = add_layer(x_in, 784, 10, tf.nn.softmax) #定義交叉熵 cross_entropy = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=y_in, logits=prediction))
第二個坑:
先來看一下,當使用method2手動計算交叉熵時,不使用tf.clip_by_value時候的代碼的時候運行是什么樣子的,代碼修改如下:
#method2:手動計算,這里tf.clip_by_value函數的作用是為了避免輸入為負數或者0,log函數的定義域是大於0的,不這么做會出現LOSS=NAN且模型准確度不變的情況,這個坑會在代碼示例后面說明。 cross_entropy = -tf.reduce_sum(y_in * tf.log(prediction))
運行結果為:
C:\Users\EDZ\PycharmProjects\DY\Scripts\python.exe C:/Users/EDZ/.PyCharm2019.1/config/scratches/tf.py Extracting MNIST_data\train-images-idx3-ubyte.gz Extracting MNIST_data\train-labels-idx1-ubyte.gz Extracting MNIST_data\t10k-images-idx3-ubyte.gz Extracting MNIST_data\t10k-labels-idx1-ubyte.gz 第1代1批次,准確率為0.098000 第1代2批次,准確率為0.098000 第1代3批次,准確率為0.098000 第1代4批次,准確率為0.098000 第1代5批次,准確率為0.098000 第1代6批次,准確率為0.098000 第1代7批次,准確率為0.098000
可以看出准確率完全沒有變化,准確率沒有變化說明cross_entropy沒有變,沒有進行訓練,於是輸出每次迭代cross_entropy的值,代碼修改如下:
# print('第%d代%d批次,准確率為%.6f' % (epoch + 1, batch + 1, acc)) loss = sess.run(cross_entropy, feed_dict={x_in: mnist.test.images, y_in: mnist.test.labels}) print('第%d代%d批次,loss為%.6f' % (epoch + 1, batch + 1, loss))
運行結果為:
C:\Users\EDZ\PycharmProjects\DY\Scripts\python.exe C:/Users/EDZ/.PyCharm2019.1/config/scratches/tf.py Extracting MNIST_data\train-images-idx3-ubyte.gz Extracting MNIST_data\train-labels-idx1-ubyte.gz Extracting MNIST_data\t10k-images-idx3-ubyte.gz Extracting MNIST_data\t10k-labels-idx1-ubyte.gz 第1代1批次,loss為nan 第1代2批次,loss為nan 第1代3批次,loss為nan 第1代4批次,loss為nan 第1代5批次,loss為nan 第1代6批次,loss為nan 第1代7批次,loss為nan
出現這樣的結果是因為,log函數的定義域是x>0,但是傳入的數據predicttion可能含有0或者負數,此時就會出現計算結果為NAN的情況,為了避免這個情況就需要用到tf.clip_by_value函數將數據限制在0-1之間,下面是這個函數的介紹:
tf.clip_by_value(t,clip_value_min,clip_value_max,name=None)
t
:Tensor
orIndexedSlices
.clip_value_min
: A 0-D (scalar)Tensor
, or aTensor
with the same shape ast
. The minimum value to clip by.clip_value_max
: A 0-D (scalar)Tensor
, or aTensor
with the same shape ast
. The maximum value to clip by.name
: A name for the operation (optional).
作用:截斷tensor數據的值,讓數據的值在區間 [clip_value_min , clip_value_max] 內。若tensor的值value<clip_value_min,則返回value=clip_value_min;若clip_value_min<=value<=clip_value_max,則返回value;若clip_value_max<value,則返回clip_value_max。