TensorFlow從1到2(三)數據預處理和卷積神經網絡


數據集及預處理

從這個例子開始,相當比例的代碼都來自於官方新版文檔的示例。開始的幾個還好,但隨后的程序都將需要大量的算力支持。Google Colab是一個非常棒的雲端實驗室,提供含有TPU/GPU支持的Python執行環境(需要在Edit→Notebook Settings設置中打開)。速度比不上配置優良的本地電腦,但至少超過平均的開發環境。
所以如果你的電腦運行速度不理想,建議你嘗試去官方文檔中,使用相應代碼的對應鏈接進入Colab執行試一試。
Colab還允許新建Python筆記,來嘗試自己的實驗代碼。當然這一切的前提,是需要你科學上網。

上一個例子已經完全使用了TensorFlow 2.0的庫來實現。但數據集仍然沿用了TensorFlow 1.x講解時所使用的樣本。
TensorFlow 2.0默認使用Keras的datasets類來管理數據集,包括Keras內置模型已經訓練好的生產數據集,和類似MNIST這種學習項目所用到的練習數據集。
使用Keras載入數據集同樣只是一行代碼:

(train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()

Keras.datasets默認是從谷歌網站下載數據集,以MNIST為例,數據下載地址是:https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz。文件下載之后,放置到~/.keras/datasets文件夾,以后執行程序的時候,會自動從本地讀取數據。數據保存路徑Linux/Mac都是如此,Windows同樣是在用戶主目錄,比如:c:\Users\Administrator.keras\datasets。

接着是數據預處理的問題,主要是從原始的圖片、標注,轉換為機器學習所需要的規范化之后的數據。我們在TensorFlow 1.x中所使用的數據實際是已經規范化之后的。我們可以使用Python3的交互模式,載入數據之后,查看一下數據:

$ python3
Python 3.7.3 (default, Mar 27 2019, 09:23:39) 
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import input_data
>>> mnist = input_data.read_data_sets("data/", one_hot=True)
Extracting data/train-images-idx3-ubyte.gz
Extracting data/train-labels-idx1-ubyte.gz
Extracting data/t10k-images-idx3-ubyte.gz
Extracting data/t10k-labels-idx1-ubyte.gz
>>> mnist.train.images[0]
array([0.        , 0.        , 0.        , 0.        , 0.        ,
   ...省略部分...
       0.        , 0.        , 0.        , 0.        , 0.        ,
       0.9215687 , 0.9215687 , 0.9215687 , 0.9215687 , 0.9215687 ,
       0.9843138 , 0.9843138 , 0.9725491 , 0.9960785 , 0.9607844 ,
       0.9215687 , 0.74509805, 0.08235294, 0.        , 0.        ,
   ...省略部分...
       0.        , 0.        , 0.        , 0.        , 0.        ,
       0.        , 0.        , 0.        , 0.        ], dtype=float32)
>>> mnist.train.labels[0]
array([0., 0., 0., 0., 0., 0., 0., 1., 0., 0.])

對於圖,就是28x28的二維數組,其中每一個數據,代表一個點,數據的值越接近1,代表這個點的顏色越接近白色;反之,則顏色越接近黑色。借用原文第四篇中的一幅圖來幫你回憶一下這個關系(上一篇中,圖片顯示部分的代碼,功能也是還原這組數據):

對於標簽,因為我們是識別為0-9共10個數字,是10個輸出的分類器。所以標簽組某一個值為1,表示圖像代表的手寫數字屬於該分類。同樣借用一下原圖:

如果想將這樣的分類數據轉成我們習慣的0-9數字,可以使用TensorFlow中內置的函數argmax:

   ...接着上面的交互模式繼續執行...
>>> import tensorflow as tf
>>> train_labels = tf.argmax(mnist.train.labels, 1)
>>> train_labels
<tf.Tensor: id=2, shape=(55000,), dtype=int64, numpy=array([7, 3, 4, ..., 5, 6, 8])>

現在的數據看起來很習慣了吧?更幸福的是,使用Keras的的分類器模型訓練,已經可以直接使用這樣的標簽數據了。
keras.datasets.mnist.load_data()所載入的樣本數據,跟TensorFlow 1.0所使用的數據有一些區別。其中的圖像數據並未做規范化,仍然是通常BMP圖像中的0-255的字節數據方式。標簽數據,也直接是我們更熟悉的0-9數字標簽。這兩個微小的變化提現了TensorFlow理念的轉變,TensorFlow更貼近真實的工作環境了。
我們同樣在交互模式來看一下這兩組數據:

$ python3
Python 3.7.3 (default, Mar 27 2019, 09:23:39) 
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from tensorflow import keras
>>> (train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()
>>> train_images[0]
array([[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
   ...省略部分...
       [  0,   0,   0,   0,   0,   0,   0,   0,  30,  36,  94, 154, 170,
        253, 253, 253, 253, 253, 225, 172, 253, 242, 195,  64,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,  49, 238, 253, 253, 253, 253,
        253, 253, 253, 253, 251,  93,  82,  82,  56,  39,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,  18, 219, 253, 253, 253, 253,
        253, 198, 182, 247, 241,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,  80, 156, 107, 253, 253,
        205,  11,   0,  43, 154,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,  14,   1, 154, 253,
         90,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
   ...省略部分...
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0]], dtype=uint8)
>>> train_labels
array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)

就像從上一個系列中我就一直強調的,TensorFlow的使用越來越容易,成熟的模型越來越多。難度更多的會集中在樣本的選取和預處理,所以一定要多關注對原始數據的理解。
TensorFlow 2.0可以直接處理如上所示的標簽數據。圖像的數據則仍然需要規范化,圖像數據的取值范圍我們很清楚是0-255,規范化也很簡單:

# 數據規范化為0-1范圍的浮點數
train_images = train_images / 255.0
test_images1 = test_images / 255.0

下面看看完整的代碼:

#!/usr/bin/env python3

# tensorflow庫
import tensorflow as tf
# tensorflow 已經內置了keras
from tensorflow import keras

# 引入繪圖庫
import matplotlib.pyplot as plt

# 第一次使用會自動從網上下載mnist的訓練樣本
(train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()


def plot_image(i, imgs, labels, predictions):
	# TensorFlow 2.x的數據已經是0-255范圍,無需再次還原
    image = imgs[i]
    label = labels[i]
    prediction = tf.argmax(predictions[i])
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])
    # 繪制樣本圖
    plt.imshow(image)
    # 顯示標簽值,對比顯示預測值和實際標簽值
    plt.xlabel("predict:{} label:{}".format(prediction, label))


def show_samples(num_rows, num_cols, images, labels, predictions):
    num_images = num_rows*num_cols
    plt.figure('Predict Samples', figsize=(2*num_cols, 2*num_rows))
    # 循環顯示前num_rows*num_cols副樣本圖片
    for i in range(num_images):
        plt.subplot(num_rows, num_cols, i+1)
        plot_image(i, images, labels, predictions)
    plt.show()

# 數據規范化為0-1范圍的浮點數
train_images = train_images / 255.0
test_images1 = test_images / 255.0

# 定義神經網絡模型
model = keras.Sequential([
    # 輸入層把28x28的2維矩陣轉換成1維
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(10, activation='softmax')
])
# 編譯模型
model.compile(optimizer='adam', 
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
# 使用訓練集數據訓練模型
model.fit(train_images, train_labels, epochs=5)

# 使用測試集樣本驗證識別准確率
test_loss, test_acc = model.evaluate(test_images1, test_labels)
print('\nTest accuracy:', test_acc)

# 完整預測測試集樣本
predictions = model.predict(test_images1)

# 顯示測試樣本預測結果
show_samples(4, 6, test_images, test_labels, predictions)

代碼中,圖片顯示的部分也對應取消了把規范化的數據還原為0-255原始圖像數據的過程。其它部分則並沒有什么變化。
現在,MNIST已經是完整的TensorFlow 2.0的原生代碼了。繞了這么遠,希望能幫你更深刻理解這些代碼背后的工作。

卷積和池化

在前一個系列中,卷積和池化部分據很多反饋說是一個很嚴重的門檻。有讀者說完全算不清每一層和相連接的層之間的數據關系。
在TensorFlow 2.0中,就像前面說過的,這種層與層之間的數據維度模型完全是無需自己計算的,Keras會自動匹配這種數據關系。因此單純從這一點上說,在TensorFlow 2.0中,無論多復雜的模型構建都不會再成為問題。只是會多一點其它的擔心,那就是這樣隱藏起來機器學習本質上的數學模型,究竟對程序員來說是好事還是壞事?
TensorFlow 1.x中使用卷積神經網絡解決MNIST問題的講解在前系列第六篇。篇幅很長,這里就不重貼了。在TensorFlow 2.0中,則只是一個函數幾行代碼(請盡量跟TensorFlow 1.x版本的代碼對應着看。對卷積、池化的概念已經忘記的也強烈建議去前系列復習一下):

# 定義卷積池化神經網絡模型
model = keras.Sequential([
    keras.layers.Conv2D(32, (5, 5), strides=(1, 1),
                        padding='same', activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(2, 2),
                              strides=(2, 2), padding='same'),
    keras.layers.Conv2D(64, (5, 5), strides=(1, 1),
                        padding='same', activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(2, 2),
                              strides=(2, 2), padding='same'),
    keras.layers.Flatten(),  # 下面的神經網絡需要1維的數據
    keras.layers.Dense(1024, activation='relu'),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(10, activation='softmax')
])

Keras讓模型構建的過程變得極其容易。
從原理上說,卷積是對圖像的二維數據做掃描,還需要指定圖像的色深。所以在樣本預處理的階段,我們還要對其做一個變形:

# 卷積需要2維數據,還需要指定色深,因此是(樣本數,長,寬,色深)
train_images = train_images.reshape(train_labels.shape[0], 28, 28, 1)
test_images1 = test_images1.reshape(test_labels.shape[0], 28, 28, 1)

訓練集的樣本我們直接用變形后的數據替代了原始樣本。測試集則另外使用了一個變量保留了原始的測試集,這是因為我們顯示測試集圖片的時候,使用原始數據集顯然更方便。
實際上整個代碼只有這么兩點區別,不過為了你練習的時候方便,還是把完整代碼貼一遍:

#!/usr/bin/env python3

# tensorflow庫
import tensorflow as tf
# tensorflow 已經內置了keras
from tensorflow import keras

# 引入繪圖庫
import matplotlib.pyplot as plt

# 第一次使用會自動從網上下載mnist的訓練樣本
(train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()
# 數據路徑:~/.keras/datasets/mnist.npz


def plot_image(i, imgs, labels, predictions):
    image = imgs[i]
    label = labels[i]
    prediction = tf.argmax(predictions[i])
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])
    # 繪制樣本圖
    plt.imshow(image)
    # 顯示標簽值
    plt.xlabel("predict:{} label:{}".format(prediction, label))


def show_samples(num_rows, num_cols, images, labels, predictions):
    num_images = num_rows*num_cols
    plt.figure('Predict Samples', figsize=(2*num_cols, 2*num_rows))
    # 循環顯示前4*6副訓練集樣本圖片
    for i in range(num_images):
        plt.subplot(num_rows, num_cols, i+1)
        plot_image(i, images, labels, predictions)
    plt.show()

# 數據規范化為0-1范圍的浮點數
train_images = train_images / 255.0
test_images1 = test_images / 255.0

# 卷積需要2維數據,還需要指定色深,因此是(樣本數,長,寬,色深)
train_images = train_images.reshape(train_labels.shape[0], 28, 28, 1)
test_images1 = test_images1.reshape(test_labels.shape[0], 28, 28, 1)
# 定義卷積池化神經網絡模型
model = keras.Sequential([
    keras.layers.Conv2D(32, (5, 5), strides=(1, 1),
                        padding='same', activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(2, 2),
                              strides=(2, 2), padding='same'),
    keras.layers.Conv2D(64, (5, 5), strides=(1, 1),
                        padding='same', activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(2, 2),
                              strides=(2, 2), padding='same'),
    keras.layers.Flatten(),  # 下面的神經網絡需要1維的數據
    keras.layers.Dense(1024, activation='relu'),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(10, activation='softmax')
])
# 編譯模型
model.compile(optimizer='adam', 
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
# 使用訓練集數據訓練模型
model.fit(train_images, train_labels, epochs=3)

# 使用測試集樣本驗證識別准確率
test_loss, test_acc = model.evaluate(test_images1, test_labels)
print('\nTest accuracy:', test_acc)

# 完整預測測試集樣本
predictions = model.predict(test_images1)

# 顯示測試樣本預測結果
show_samples(4, 6, test_images, test_labels, predictions)

程序執行的時候,在控制台的輸出信息類似下面:

$ chmod +x mnist-conv-maxpool-v2.py
$ ./mnist-conv-maxpool-v2.py
Epoch 1/3
60000/60000 [==============================] - 141s 2ms/sample - loss: 0.1139 - accuracy: 0.9643
Epoch 2/3
60000/60000 [==============================] - 143s 2ms/sample - loss: 0.0417 - accuracy: 0.9869
Epoch 3/3
60000/60000 [==============================] - 138s 2ms/sample - loss: 0.0312 - accuracy: 0.9904
10000/10000 [==============================] - 7s 659us/sample - loss: 0.0289 - accuracy: 0.9903

Test accuracy: 0.9903

在樣本集的測試上,卷積神經網絡的版本可以達到超過99%的正確率。
這個正確率,只進行了3次的訓練迭代,當然因為卷積神經網絡模型的復雜,這3次的訓練就遠遠比上一例中的5次訓練速度更慢。

(待續...)


免責聲明!

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



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