tensorflow框架學習(三)—— 兩個簡單的神經網絡示例,回歸與分類


一、回歸神經網絡

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 or IndexedSlices.
  • clip_value_min: A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by.
  • clip_value_max: A 0-D (scalar) Tensor, or a Tensor with the same shape as t. 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。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM