TensorFlow 2.x - MNIST 圖像分類(3)

這一節(jié)將使用 TensorFlow 官方教程 的一個例子——Basic classification: Classify images of clothing——基礎(chǔ)分類:衣物圖片分類,訓(xùn)練一個神經(jīng)網(wǎng)絡(luò)模型來分類像運動鞋和襯衫這樣的衣服的圖像。

有了《TensorFlow 2.x - MNIST 圖像分類(1)》
《TensorFlow 2.x - MNIST 圖像分類(2)》這兩節(jié)的鋪墊,本節(jié)將顯得比較簡單。

一、加載數(shù)據(jù)

在之前的文章中,我們都是使用 MNIST,但是 MNIST 太簡單了,只需要幾分鐘的訓(xùn)練,準(zhǔn)確率就可以達(dá)到 99% 以上,并且大部分圖片可能只需要幾個像素點就可以區(qū)分開來。真實世界千變?nèi)f化,我們需要更真實的圖片。

本次使用的數(shù)據(jù)集是 Fashion MNIST。該數(shù)據(jù)集由 70,000 張黑白圖片構(gòu)成,每張圖片大小為 28x28,圖片數(shù)量和大小與經(jīng)典 MNIST 完全相同,但 Fashion MNIST 是由十類服飾圖片構(gòu)成,MNIST 是手寫數(shù)字。Fashion MNIST 更有挑戰(zhàn)性,更適合用來驗證算法。Fashion-MNIST 的目的是要代替 MNIST。

Fashion-MNIST

廢話不多說,先下載四個數(shù)據(jù)集:

  1. train-images-idx3-ubyte.gz,訓(xùn)練集圖片,60,000張,26 MBytes;
  2. train-labels-idx1-ubyte.gz,訓(xùn)練集標(biāo)簽,60,000張,29 KBytes;
  3. t10k-images-idx3-ubyte.gz,測試集圖片,10,000張,4.3 MBytes;
  4. t10k-labels-idx1-ubyte.gz,測試集標(biāo)簽,10,000張,5.1 KBytes。

下載后,加載數(shù)據(jù):

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import os
import gzip
import numpy as np

def load_data(data_folder):
    files = ['train-labels-idx1-ubyte.gz', 'train-images-idx3-ubyte.gz',
             't10k-labels-idx1-ubyte.gz', 't10k-images-idx3-ubyte.gz']
    paths = []
    for f_name in files:
        paths.append(os.path.join(data_folder, f_name))

    with gzip.open(paths[0], 'rb') as lb_path:
        y_train = np.frombuffer(lb_path.read(), np.uint8, offset=8)

    with gzip.open(paths[1], 'rb') as img_path:
        x_train = np.frombuffer(img_path.read(), np.uint8, offset=16).reshape(len(y_train), 28, 28)

    with gzip.open(paths[2], 'rb') as lb_path:
        y_test = np.frombuffer(lb_path.read(), np.uint8, offset=8)

    with gzip.open(paths[3], 'rb') as img_path:
        x_test = np.frombuffer(img_path.read(), np.uint8, offset=16).reshape(len(y_test), 28, 28)

    return (x_train, y_train), (x_test, y_test)

(train_images, train_labels), (test_images, test_labels) = load_data(os.path.dirname(__file__) + '/data/fashion')

這里的數(shù)據(jù)加載方法與 TensorFlow 官方教程上的不同,因為官方教程加載的數(shù)據(jù)需要科學(xué)上網(wǎng),如果你能科學(xué)上網(wǎng),可以使用如下官方教程,替代上述方法:

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

二、數(shù)據(jù)預(yù)處理

數(shù)據(jù)格式如下:

train_images.shape # (60000, 28, 28)
len(train_labels) # 60000
train_labels # ([9, 0, 0, ..., 3, 0, 5], dtype=uint8)
test_images.shape # (10000, 28, 28)
len(test_labels) # 10000

然后我們查看第一張圖片:

plt.figure()
plt.imshow(train_images[0])
plt.show()
print(train_labels[0])  # 9
Ankle boot

圖片大小 28x28,每個像素值取值范圍 0-255。標(biāo)簽是整數(shù),取值范圍 0-9,與實際的服飾類別對應(yīng)關(guān)系如下。

Label Class
0 T-shirt/top (T恤)
1 Trouser(褲子)
2 Pullover(套衫)
3 Dress (裙子)
4 Coat(外套)
5 Sandal(涼鞋)
6 Shirt(汗衫)
7 Sneaker(運動鞋)
8 Bag(包)
9 Ankle boot(短靴)

每個圖像都映射到一個標(biāo)簽。上圖的標(biāo)簽是 9,所以圖片中應(yīng)該是 Ankle boot (短靴),雖然沒看出哪里短。

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

圖片的每個像素值在 0-255 之間,需要轉(zhuǎn)為 0-1。訓(xùn)練集和測試集都需要經(jīng)過相同的處理。

train_images = train_images / 255.0
test_images = test_images / 255.0

顯示前 25 張圖片與標(biāo)簽的對應(yīng)關(guān)系:

# show first 25 images and labels
plt.figure(figsize=(10, 10))
for i in range(25):
    plt.subplot(5, 5, i + 1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    plt.xlabel(class_names[train_labels[i]])
plt.show()

三、搭建神經(jīng)網(wǎng)絡(luò)

搭建神經(jīng)網(wǎng)絡(luò)分為兩步,首先配置模型的每一層,然后編譯模型。

配置模型層

神經(jīng)網(wǎng)絡(luò)的基本構(gòu)成是網(wǎng)絡(luò)層 (layer),大部分深度學(xué)習(xí)網(wǎng)絡(luò)都由多個簡單的 layers 構(gòu)成。

model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(10, activation='softmax')
])

網(wǎng)絡(luò)的輸入層 tf.keras.layers.Flatten 將輸入的圖片從 28 × 28 的二維數(shù)組(28 × 28 像素)轉(zhuǎn)為 784 的一維數(shù)組,這一層的作用僅僅是將每一行值平鋪在一行,沒有參數(shù)需要學(xué)習(xí)。

接下來是 2 層 tf.keras.layers.Dense,即全連接層 (Fully Connected, FC),第一層 Dense 有 128 個神經(jīng)元。第二層有 10 個神經(jīng)元,經(jīng)過 softmax 后,返回了和為 1 長度為 10 的概率數(shù)組,每一個數(shù)分別代表當(dāng)前圖片屬于分類 0-9 的概率。

編譯模型

在開始訓(xùn)練前,模型需要編譯 (Compile) 并設(shè)置一些參數(shù):

  1. Loss function - 損失函數(shù),訓(xùn)練時評估模型的正確率,希望最小化這個函數(shù),往正確的方向訓(xùn)練模型。
  2. Optimizer - 優(yōu)化器算法,更新模型參數(shù)的算法。
  3. Metrics - 指標(biāo),用來監(jiān)視訓(xùn)練和測試步數(shù),下面的例子中使用 accuracy,即圖片被正確分類的比例。
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

四、訓(xùn)練模型

訓(xùn)練神經(jīng)網(wǎng)絡(luò),通常有以下幾個步驟。

  1. 傳入訓(xùn)練集數(shù)據(jù),train_imagestrain_labels
  2. 訓(xùn)練模型,關(guān)聯(lián)圖片和標(biāo)簽。
  3. 使用測試集 test_images 預(yù)測,并用標(biāo)簽 test_labels 進行驗證。

Feed the model

調(diào)用 model.fit 方法開始訓(xùn)練。

model.fit(train_images, train_labels, epochs=10)
Epoch 1/10
1875/1875 [==============================] - 5s 3ms/step - loss: 0.5025 - accuracy: 0.8243
..............................
Epoch 7/10
1875/1875 [==============================] - 5s 3ms/step - loss: 0.2696 - accuracy: 0.9001
Epoch 8/10
1875/1875 [==============================] - 5s 3ms/step - loss: 0.2576 - accuracy: 0.9035
Epoch 9/10
1875/1875 [==============================] - 6s 3ms/step - loss: 0.2491 - accuracy: 0.9075
Epoch 10/10
1875/1875 [==============================] - 6s 3ms/step - loss: 0.2396 - accuracy: 0.9112

經(jīng)過 10 輪訓(xùn)練后,準(zhǔn)確率達(dá)到了 91.12%。

評估準(zhǔn)確率

使用測試集數(shù)據(jù)看看模型訓(xùn)練的結(jié)果:

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)
print('\nTest accuracy:', test_acc)  # Test accuracy: 0.8865000009536743

測試集的準(zhǔn)確率低于訓(xùn)練集,訓(xùn)練集和測試集準(zhǔn)確率之間的差距代表模型過擬合 (overfitting)。即對于訓(xùn)練中沒有見過的新數(shù)據(jù),模型表現(xiàn)差。如果你對過擬合不熟悉,可以看看《過擬合(Overfitting) 與 Dropout》,沒錯,也是我的文章。

預(yù)測

使用 predict 函數(shù)進行預(yù)測,并顯示圖片,打印預(yù)測結(jié)果及標(biāo)簽。

# 預(yù)測
predictions = model.predict(test_images)

idx = 20
plt.figure()
plt.imshow(train_images[idx])  # 顯示圖片
plt.show()
print(predictions[idx])
predict = int(np.argmax(predictions[idx]))
print('predict: ' + class_names[predict])  # 預(yù)測結(jié)果
print('label: ' + class_names[test_labels[idx]])  # 標(biāo)簽

看一下第一個預(yù)測結(jié)果。

[3.1960328e-09 1.2830121e-11 1.0000000e+00 1.5766790e-18 1.2614347e-16
 7.3473108e-24 1.2678178e-11 2.2807704e-21 2.7815026e-11 3.0496564e-14]
predict: Pullover
label: Pullover

結(jié)果是一個長度為 10 的數(shù)組,數(shù)組的每個值代表模型認(rèn)為是對應(yīng)標(biāo)簽的概率。上述例子中 9 號標(biāo)簽的概率最大 1.0000000e+00,即模型認(rèn)為該圖片是 9 號標(biāo)簽的概率是 100%。

Pullover

預(yù)測結(jié)果和標(biāo)簽結(jié)果相同,都是 Pullover。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容