從TensorFlow2.0開始集成了Keras,變爲了tensorflow.keras模塊,其中,原Keras框架的一些方法已經丟棄,爲了快速上手Keras,實現工程部署,從本篇文章開始更新基於TensorFlow 2.1.0的Keras使用系列教程。教程以小白視角編寫,重要語句都做了註釋。本文是該系列教程的第一篇。通過 Fashion MNIST 數據集實現圖像分類,熟悉TensorFlow和Keras的建模流程和基本用法。
代碼環境:
python version: 3.7.6 (default, Jan 8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)]
tensorflow version: 2.1.0
注:本文所有代碼在 jupyter notebook
中編寫並測試通過。
文章目錄
1.Fashion MNIST 數據集
Fashion MNIST數據集包含10個類別的70,000張灰度圖像。下圖顯示了低分辨率(28 x 28像素)的單個衣物:
Fashion MNIST旨在替代經典MNIST數據集,通常被用作計算機視覺機器學習程序的“ Hello,World”。這兩個數據集用於驗證算法是否按預期工作,是測試和調試代碼的benchmark。本文例子使用60,000張圖像訓練網絡,使用10,000張圖像評估網絡學習圖像分類的準確率。直接從TensorFlow導入和加載Fashion MNIST數據:
導入表要的包:
import sys
import tensorflow as tf
from tensorflow import keras
print('python version:',sys.version)
print('tensorflow version:',tf.__version__)
加載數據集:
fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
# load_data()方法的返回值爲:Tuple of Numpy arrays: (x_train, y_train), (x_test, y_test)
Fashion MNIST 中的圖像是28x28 NumPy數組,像素值範圍是0到255。標籤是整數數組,範圍是0到9,分別對應於圖像表示的衣服真實標籤爲:
Label Class
0 T-shirt/top
1 Trouser
2 Pullover
3 Dress
4 Coat
5 Sandal
6 Shirt
7 Sneaker
8 Bag
9 Ankle boot
原數據集中不包含真實標籤,爲方便繪圖,首先用一個列表保存真實標籤:
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
注意:在下載數據集的時候,可能會出現下載不全導致報錯:【EOFError: Compressed file ended before the end-of-stream marker was reached】。解決方法:刪除路徑下對應的數據集文件之後,重新下載即可,比如我的在:【C:\Users\34123.keras\datasets】。無需科學上網,否則報SSL錯誤。下載比較慢或者卡死的話,直接從github下載,然後放到該路徑下:
2. 數據可視化與預處理
1.基本信息
train_images.shape
#(60000,28,28)
train_labels
#array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)
len(train_labels)
# 60000
len(test_labels)
# 10000
2.在訓練網絡之前,必須對數據進行預處理。檢查訓練集中第一張圖像的像素值:
plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.grid(False)
plt.show()
可以看到,其像素值落在0到255之間。
[1] matplotlib.pyplot.colorbar 官方文檔
3.將這些值除以255,縮放到0到1,然後再將其輸入神經網絡模型。
train_images = train_images / 255.0
test_images = test_images / 255.0
4.爲了驗證數據的格式正確,顯示訓練集中的前25個圖像,並在每個圖像下方顯示類別名稱:
plt.figure(figsize=(10,10), dpi=200)
for i in range(25):
plt.subplot(5,5,i+1)
plt.xticks([]) # 屏蔽座標顯示,下同
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.binary)
plt.xlabel(class_names[train_labels[i]])
plt.show()
3. 建模
3.1 定義模型
model = keras.Sequential([
keras.layers.Flatten(input_shape=(28, 28)),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dense(10)
])
該網絡的第一層 tf.keras.layers.Flatten
將圖像格式從二維數組(28 x 28像素)轉換爲一維數組(28 * 28 = 784像素)。可以將這一層看作是堆疊圖像中的像素行並將它們排成一行。該層沒有學習參數,只會格式化數據。
像素展平後,添加兩層 tf.keras.layers.Dense
層,即全連接層。第一個Dense層有128個節點(或神經元)。第二層(也是最後一層,即輸出層)返回長度爲 10
的logits數組。每個節點包含一個得分,指示當前圖像屬於10個類別中的哪一類。
3.2 編譯模型
在訓練模型之前,需要進行一些其他設置。這些是在模型的編譯步驟中添加的:
- 優化器:基於模型看到的數據及其損失函數來更新模型的方式,即權重更新方式。
- 損失函數:衡量模型在訓練過程中的準確性。訓練過程即爲最小化損失函數的過程。
- 指標:用於監視訓練和測試過程。本例使用準確率爲指標(正確分類的圖像分數)。
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
3.3 訓練模型
訓練神經網絡模型需要執行以下步驟:
- 將訓練數據輸入模型(fit)。本例中,訓練數據爲train_images和train_labels組成的數組。
- 模型學習關聯圖像和標籤。
- 配置模型對測試集進行預測(本例爲test_images數組)。
- 驗證預測是否與test_labels數組中的標籤匹配。
model.fit(train_images, train_labels, epochs=10)
輸出:
Train on 60000 samples
Epoch 1/10
60000/60000 [==============================] - 8s 128us/sample - loss: 0.4929 - accuracy: 0.8267
Epoch 2/10
60000/60000 [==============================] - 5s 89us/sample - loss: 0.3701 - accuracy: 0.8675
Epoch 3/10
60000/60000 [==============================] - 5s 86us/sample - loss: 0.3331 - accuracy: 0.8773
Epoch 4/10
60000/60000 [==============================] - 5s 91us/sample - loss: 0.3099 - accuracy: 0.8868
Epoch 5/10
60000/60000 [==============================] - 5s 87us/sample - loss: 0.2930 - accuracy: 0.8926
Epoch 6/10
60000/60000 [==============================] - 5s 83us/sample - loss: 0.2792 - accuracy: 0.8964
Epoch 7/10
60000/60000 [==============================] - 5s 88us/sample - loss: 0.2673 - accuracy: 0.9010
Epoch 8/10
60000/60000 [==============================] - 5s 88us/sample - loss: 0.2565 - accuracy: 0.9046
Epoch 9/10
60000/60000 [==============================] - 5s 87us/sample - loss: 0.2461 - accuracy: 0.9075
Epoch 10/10
60000/60000 [==============================] - 5s 90us/sample - loss: 0.2392 - accuracy: 0.9112
<tensorflow.python.keras.callbacks.History at 0x17b19ed3948>
模型訓練時,會打印損失和準確率指標。該模型在訓練數據上達到約91%(0.9112)的精度。
3.4 評估模型
接下來,比較模型在測試數據集上的表現:
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
print('\nTest accuracy:', test_acc)
輸出:
10000/10000 - 1s - loss: 0.3307 - accuracy: 0.8846
Test accuracy: 0.8846
事實證明,測試數據集的準確性略低於訓練數據集的準確性。訓練準確性和測試準確性之間的差距表示出現了過擬合。當機器學習模型在新的輸入數據上的表現比訓練數據上的表現差時,即認爲發生了過擬合。過擬合的模型“存儲”訓練數據集中的噪聲和細節,從而對新數據的模型性能產生負面影響。有關更多欠擬合和過擬抑制策略,下篇介紹。
4. 查看預測結果
訓練模型可以預測某些圖像。模型的輸出爲線性的logits。通過softmax層可以將logits轉換爲更容易解釋的概率。【注:對數 (logits):分類模型生成的原始(非標準化)預測向量,通常會傳遞給標準化函數。如果模型要解決多類別分類問題,則對數通常變成 softmax 函數的輸入。之後,softmax 函數會生成一個(標準化)概率向量,對應於每個可能的類別。】
4.1 測試集預測
添加softmax層實現分類概率預測:
probability_model = tf.keras.Sequential([model, tf.keras.layers.Softmax()])
predictions = probability_model.predict(test_images)
預測:
predictions[0]
輸出:
array([6.1835679e-09, 5.0825996e-11, 1.7550703e-10, 1.2875578e-15,
5.0945664e-11, 1.6961206e-04, 6.4031923e-08, 3.5876669e-03,
1.0340054e-08, 9.9624264e-01], dtype=float32)
預測是10個數字組成的數組,表示模型對圖像對應於10種不同服裝中的每一種的置信度。查看置信度最高的標籤:
np.argmax(predictions[0])
輸出:
9
即模型對測試集中第一張圖片的預測爲標籤 9
,其對應的真實標籤爲 Ankle boot
。
4.2 預測結果可視化
查看真實圖片與預測:
def plot_image(i, predictions_array, true_label, img):
'''
該函數實現打印預測的圖片
'''
predictions_array, true_label, img = predictions_array, true_label[i], img[i]
plt.grid(False)
plt.xticks([]) # 屏蔽座標顯示,下同
plt.yticks([])
plt.imshow(img, cmap=plt.cm.binary)
predicted_label = np.argmax(predictions_array) # 取出預測最大概率的標籤
if predicted_label == true_label:
color = 'blue'
else:
color = 'red'
plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label], # 打印預測標籤
100*np.max(predictions_array), # 打印置信度
class_names[true_label]), # 打印真實標籤
color=color)
def plot_value_array(i, predictions_array, true_label):
'''
該函數實現繪製概率柱狀圖
'''
predictions_array, true_label = predictions_array, true_label[i]
plt.grid(False) # 屏蔽格線顯示
plt.xticks(range(10))
plt.yticks([])
thisplot = plt.bar(range(10), predictions_array, color="#777777") # 繪製柱狀圖
plt.ylim([0, 1]) # 設置y軸刻度顯示範圍
predicted_label = np.argmax(predictions_array) # 取出預測標籤
thisplot[predicted_label].set_color('red') # 設置柱狀圖中預測標籤的顏色
thisplot[true_label].set_color('blue')
def merged_result(i):
'''
該函數實現彙總以上兩個函數輸出結果,提供預測某張照片索引的接口
'''
plt.figure(figsize=(6,3), dpi=150)
plt.subplot(1,2,1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions[i], test_labels)
plt.show()
預測:
i = 12
merged_result(i)
i = 450
merged_result(i)
輸出:
輸出多個預測結果:
def plot_images(num_rows, num_cols):
'''
該函數實現多個預測結果輸出
'''
num_images = num_rows*num_cols # 繪製圖片數量
plt.figure(figsize=(2*2*num_cols, 2*num_rows), dpi=150) # 設置畫布尺寸
for i in range(num_images):
plt.subplot(num_rows, 2*num_cols, 2*i+1) # 第i個子圖的位置
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(num_rows, 2*num_cols, 2*i+2)
plot_value_array(i, predictions[i], test_labels)
plt.tight_layout()
plt.show()
num_rows = 5
num_cols = 3
plot_images(num_rows, num_cols)
輸出:
4.3 單張圖片預測
def predict_image(i):
img = test_images[i] # shape=(28,28)
img = (np.expand_dims(img,0)) # shape=(1,28,28),轉化成列表格式,訓練集和測試集的shape爲(None,28,28)
predictions_single = probability_model.predict(img) # 預測
print('predictions_single:\n', predictions_single)
plot_value_array(1, predictions_single[0], test_labels) # 繪圖
_ = plt.xticks(range(10), class_names, rotation=45) # 設置x軸座標刻度
print('predict label:', np.argmax(predictions_single[0]))
print('true label:', test_labels[i])
predict_image(66)
輸出:
predictions_single:
[[0.19836323 0.01996289 0.1276678 0.28869623 0.05913025 0.044572
0.25288364 0.00403849 0.00407252 0.00061292]]
predict label: 3
true label: 2
參考:
https://www.tensorflow.org/tutorials/keras/classification