TensorFlow2 學習——MLP圖像分類
導包
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
原始數據
- 加載數據集
# 衣物類圖片數據集 fashion_mnist # 訓練集All,測試集 (X_train_all, y_train_all),(X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data() # 手寫數字數據集mnist # tf.keras.datasets.mnist.load_data()
- 查看數據集
# 查看數據維度 print(X_train_all.shape) # 查看標籤種類 print(set(y_train_all))
數據作圖
- 單個圖片數據分析
# 其中一張圖片的數據 print(X_train_all[0])
- 此處會打印一個28*28的矩陣
- 矩陣內的值表示0-255的灰度值(即代表圖片的一個像素)
- 展示單張圖片
def show_single_image(img_arr): plt.imshow(img_arr, cmap="binary") plt.show() show_single_image(X_train_all[1])
- 展示多張圖片
def show_images(n_rows, n_cols, x_data, y_data, class_names): assert len(x_data) == len(y_data) assert n_rows * n_cols < len(x_data) plt.figure(figsize=(n_cols * 1.5, n_rows * 1.5)) for row in range(n_rows): for col in range(n_cols): index = row * n_cols + col plt.subplot(n_rows, n_cols, index + 1) plt.imshow(x_data[index], cmap="binary", interpolation="nearest") plt.axis("off") plt.title(class_names[y_data[index]]) plt.show() class_names = ["T-shirt", "Trouser", "Pullover", "Dress", "Coat", "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot"] show_images(3, 5, X_train_all, y_train_all, class_names)
數據劃分與標準化
- 訓練集、驗證集拆分
# X_train, X_valid = X_train_all[:50000], X_train_all[50000:] # y_train, y_valid = y_train_all[:50000], y_train_all[50000:] X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size=0.25) print("train: ", X_train.shape, y_train.shape) print("valid: ", X_valid.shape, y_valid.shape) print(" test: ", X_test.shape, y_test.shape)
- 數據標準化
scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train.reshape(-1, 28 * 28)).reshape(-1, 28, 28) X_valid_scaled = scaler.transform(X_valid.reshape(-1, 28 * 28)).reshape(-1, 28, 28) X_test_scaled = scaler.transform(X_test.reshape(-1, 28 * 28)).reshape(-1, 28, 28) print(X_train_scaled.max(), X_train_scaled.min())
構建模型並訓練
-
搭建神經網絡層
# relu: y = max(0, x) # softmax: 用於解決多分類問題,將向量變成概率分佈 # x = [x1, x2, x3] # sum = e^x1 + e^x2 + e^x3 # y = [e^x1/sum, e^x2/sum, e^x3/sum] # 方法1 # model = tf.keras.models.Sequential() # model.add(tf.keras.layers.Flatten(input_shape=[28, 28])) # model.add(tf.keras.layers.Dense(300, activation="relu")) # model.add(tf.keras.layers.Dense(100, activation="relu")) # model.add(tf.keras.layers.Dense(10, activation="softmax")) # 方法2 model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(input_shape=[28, 28]), tf.keras.layers.Dense(200, activation="relu"), tf.keras.layers.Dense(150, activation="relu"), # tf.keras.layers.Dropout(0.5), # 添加Dropout層,可以抑制過擬合。0.5表示丟棄50%單元 tf.keras.layers.Dense(100, activation="relu"), tf.keras.layers.Dense(10, activation="softmax") ]) # 方法3 函數式 # input = tf.keras.Input(shape=(28, 28)) # x = tf.keras.layers.Flatten()(input) # x = tf.keras.layers.Dense(200, activation="relu")(x) # x = tf.keras.layers.Dense(150, activation="relu")(x) # x = tf.keras.layers.Dense(100, activation="relu")(x) # output = tf.keras.layers.Dense(10, activation="softmax")(x) # model = tf.keras.Model(inputs=input, outputs=output) # 方法4 定義一個類,繼承tf.keras.Model # class My_Model(tf.keras.Model): # def __init__(self): # def call(self, inputs):
- Flatten用於降維,例如此處將28*28的矩陣打散爲784個特徵的一維向量
- Dense是全連接層,接收上一層的所有參數
- 第一個參數,指的是本層有多少個神經元節點
- 第二個參數,activation是激活函數,用於處理接收到的數據,包括relu、sigmoid、softmax等
- 最後一個Dense,節點數被設置爲10,因爲本數據標籤只有10類,因此最後只有10種輸出
-
模型編譯
model.compile(loss = "sparse_categorical_crossentropy", optimizer = "adam", metrics = ["accuracy"])
- loss 代表損失函數的計算方式
- 例如還可以寫成mse、categorical_crossentropy等
- 此處因爲是多分類問題,所以可以採用sparse_categorical_crossentropy或categorical_crossentropy
- 如要使用categorical_crossentropy,需要將標籤數據轉換爲onehot編碼形式,代碼如下
y_train_onehot = tf.keras.utils.to_categorical(y_train) y_valid_onehot = tf.keras.utils.to_categorical(y_valid) y_test_onehot = tf.keras.utils.to_categorical(y_test)
- optimizer 代表優化的方式
- 例如sgd、rmsprop、adam等
- 需要調節其內部參數,可以寫成
tf.keras.optimizers.Adam(lr=0.001)
- metrics 代表了模型評判標準,會隨着訓練時打印
- loss 代表損失函數的計算方式
-
模型總覽
model.summary()
Model: "sequential_13" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= flatten_13 (Flatten) (None, 784) 0 _________________________________________________________________ dense_46 (Dense) (None, 200) 157000 _________________________________________________________________ dense_47 (Dense) (None, 150) 30150 _________________________________________________________________ dense_48 (Dense) (None, 100) 15100 _________________________________________________________________ dense_49 (Dense) (None, 10) 1010 ================================================================= Total params: 203,260 Trainable params: 203,260 Non-trainable params: 0
- Layer列 - 是我們構建的網絡層
- Output Shape列 - 是當前網絡層的信息
- 第一個參數None是樣本數,因爲構建模型時並不知道等會兒需要訓練多少數據,因此是None
- 第二個參數是該層神經元節點的個數
- Param # - 是該網絡層待訓練參數的個數
- 計算公式:上一層節點個數 * 本層節點個數 + 本層節點個數 = 本層參數個數
- 例如 784 * 200 + 200 = 157000,200 * 150 + 150 = 30150
- 其實就是說,本層每個節點針對上一層的所有節點分別都有一個權重w,最後本層每個節點還有一個偏置值b
-
開始訓練模型
# X_train_scaled 即訓練集特徵 # y_train 即訓練集標籤 # epochs 是訓練的批次數 # validation_data 用於指定驗證集 history = model.fit(X_train_scaled, y_train, epochs=10, validation_data=(X_valid_scaled, y_valid))
- 訓練過程較慢,需等待
- 其中 loss: 0.2208 - accuracy: 0.9171 代表了訓練集的損失值、準確的
- 其中 val_loss: 0.3333 - val_accuracy: 0.8916 代表了驗證集的損失值、準確的
模型評估與預測
- 查看模型訓練歷史記錄
pd.DataFrame(history.history)
- 利用歷史記錄作圖
pd.DataFrame(history.history).plot(figsize=(8, 5)) plt.grid(True) #plt.gca().set_ylim(0, 1) plt.show()
- 使用測試集進行模型評估
model.evaluate(X_test_scaled, y_test)
- 輸出結果示例
[0.35619084103703497, 0.8747]
- 第一個是損失值,第二個是準確率
- 輸出結果示例
- 利用模型進行預測
- 使用predict進行預測
# 使用predict進行預測 result = model.predict(X_test_scaled[0:1]) print(result) # 10個類別,每個類別都有一個概率 print(result.sum()) # 所有類別概率和爲1 print(result.max(), np.argmax(result)) # 概率最大的就是預測的類別
[[4.1775929e-09 4.8089838e-10 9.2707603e-10 7.3885815e-08 5.2114243e-11 2.4399082e-03 1.8424946e-09 9.9424636e-03 2.9137237e-12 9.8761755e-01]] 1.0 0.98761755 9
- 使用predict_classes直接得到預測的類別
# 使用predict_classes直接得到預測的類別 print(model.predict_classes(X_test_scaled)[:30]) print(y_test[:30])
[9 2 1 1 6 1 4 6 5 7 4 5 8 3 4 1 2 2 8 0 2 5 7 5 1 2 6 0 9 6] [9 2 1 1 6 1 4 6 5 7 4 5 7 3 4 1 2 4 8 0 2 5 7 9 1 4 6 0 9 3]
- 使用predict進行預測
其他:回調Callback的使用
- Callback
- Tensorboard - 生成Tensorboard記錄,方便查看
- EarlyStopping - 用於提前停止訓練。本次loss與上次loss之差小於min_delta達到patience次,那就停止訓練
- ModelChackpoint - 保存模型,save_best_only保存最佳的
- 代碼示例
import os # windows下寫.\callbacks,linux下寫./callbacks,或者都寫callbacks(不指定前面的斜槓/反斜槓) logdir = ".\callbacks" if not os.path.exists(logdir): os.mkdir(logdir) ouput_model_file = os.path.join(logdir, "fashion_mnist_model.h5") callbacks = [ tf.keras.callbacks.TensorBoard(logdir), tf.keras.callbacks.ModelCheckpoint(ouput_model_file, save_best_only=True), tf.keras.callbacks.EarlyStopping(min_delta=1e-3, patience=5) ] history = model.fit(X_train_scaled, y_train, epochs=20, validation_data=(X_valid_scaled, y_valid), callbacks = callbacks)
- 查看Tensorboard的命令
tensorboard --logdir callbacks的目錄