Deep Learning với Tensorflow Module 3 Phần 2: Xây dựng mô hình phân loại trong Neural network - Mô hình nhiều class ( Multi Classification)

MVT
Đang cập nhật

7. Thử nghiệm với tập dữ liệu lớn hơn (Fashiong MNIST)

Trong phần trước, chúng ta đã xây dựng mô hình phân loại 2 class (binary class). Khả năng dự đoán dữ liệu điểm là màu đỏ hoặc màu xanh của mô hình là cực kỳ xuất sắc. Nhưng nếu có nhiều class khác nhau thì liệu khả năng dự đoán của mô hình có tốt như vậy không? Phần này sẽ trả lời cho câu hỏi này.

Giả sử, bạn có một công ty chuyên cung cấp các loại quần áo thời trang đang là xu hướng trên thế giới. Mỗi lần nhập hàng về là một số lượng khổng lồ với nhiều loại hàng và mẫu mã khác nhau trong khi số lượng nhân viên của bạn lại có hạn. Chính vì thế để tối ưu quá trình phân loại hàng hóa, bạn quyết định tìm đến công cụ xử lý quá trình phân loại này. Do đó, bạn đã xây dựng mô hình deep learning neural network để dự đoán liệu món đồ đó là quần, áo, nón, hay giày dép...

Khi phân loại có nhiều hơn 2 đối tượng (class), thì được gọi là phân loại nhiều đối tượng (multiclass classification)

Để xây dựng mô hình phân loại nhiều đối tượng, nó cũng được áp dụng tượng tự như xây dựng mô hình Binary class và có một chút thay đổi trong đó. Bạn có thể tham khảo lai bảng phương pháp sau :

HyperparameterPhân loại 2 class (binary class)Phân loại nhiều class (multi-class)
Input layer shapeCó cùng số biến giải thích (VD : Dự đoán bệnh nhân có bị tim mạch hay không dựa trên 5 đặc tính độ tuổi, giới tính, chiều cao, cân nặng, môi trường sống)Giống với binary class
Hidden layersĐiều chỉnh tùy thuộc vào vấn đề, min=1, max= vô hạnGiống với binary class
Neurons trên hidden layerĐiều chỉnh tùy thuôc vào vấn đề, Thường từ 10 đến 100Giống với binary class
Output layer shape1 ( vì chỉ có 2 class nên chỉ có thể là class này hoặc class kia)Dựa vào số class (VD : 3 cho food, dog, photo)
Hidden activationThường là ReLU (rectified linear unit)Giống với binary class
Output activationSigmoidSoftmax
Loss functionCross entropy (tf.keras.losses.BinaryCrossentropy trong TensorFlow)Cross entropy (tf.keras.losses.CategoricalCrossentropy trong TensorFlow)
OptimizerSGD (stochastic gradient descent), AdamGiống với binary class

Table 1: Typical architecture of a classification network. Source: Adapted from page 295 of Hands-On Machine Learning with Scikit-Learn, Keras & TensorFlow Book by Aurélien Géron

Để bắt đầu, chúng ta sẽ cần load dữ liệu có sẵn Fashion MNIST built-in trong thư viện Tensorflow

# Import các thư viện 
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

Khám phá tập dữ liệu và labels

(train_data, train_labels), (test_data,test_labels) = tf.keras.datasets.fashion_mnist.load_data()

Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz 32768/29515 [=================================] - 0s 0us/step 40960/29515 [=========================================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz 26427392/26421880 [==============================] - 0s 0us/step 26435584/26421880 [==============================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz 16384/5148 [===============================================================================================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz 4423680/4422102 [==============================] - 0s 0us/step 4431872/4422102 [==============================] - 0s 0us/step

Kiểm tra thông tin mẫu dữ liệu train đầu tiên:

print(f"Mẫu dữ liệu train: {train_data[0][10:15]}")
print(f"Hình thái mẫu dữ liệu train: {train_data[0].shape}")
print(f"Label của mẫu dữ liệu train: {train_labels[0]}")

Mẫu dữ liệu train: [[ 0 0 0 0 0 0 0 0 0 0 0 0 0 193 228 218 213 198 180 212 210 211 213 223 220 243 202 0] [ 0 0 0 0 0 0 0 0 0 1 3 0 12 219 220 212 218 192 169 227 208 218 224 212 226 197 209 52] [ 0 0 0 0 0 0 0 0 0 0 6 0 99 244 222 220 218 203 198 221 215 213 222 220 245 119 167 56] [ 0 0 0 0 0 0 0 0 0 4 0 0 55 236 228 230 228 240 232 213 218 223 234 217 217 209 92 0] [ 0 0 1 4 6 7 2 0 0 0 0 0 237 226 217 223 222 219 222 221 216 223 229 215 218 255 77 0]] Hình thái mẫu dữ liệu train: (28, 28) Label của mẫu dữ liệu train: 9

Kích thước toàn bộ dữ liệu của train và test :

train_data.shape, train_labels.shape, test_data.shape, test_labels.shape

((60000, 28, 28), (60000,), (10000, 28, 28), (10000,))

Thông tin về labels :

np.unique(train_labels)

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8)

Có 10 classes cho tập dữ liệu này (danh sách từ 0-9, mỗi phần tử đại diện cho 1 class).

LabelDescription
0T-shirt/top
1Trouser
2Pullover
3Dress
4Coat
5Sandal
6Shirt
7Sneaker
8Bag
9Ankle boot
class_names = ["T-shirt/top", "Trouser", "Pullover", "Dress", "Coat", "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot"]
num_classes = len(class_names)
class_names,num_classes

(['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'], 10)

plt.imshow(train_data[0])

<matplotlib.image.AxesImage at 0x7fa2856bb890>

Tạo hàm hiển thị hình ảnh ngẫu nhiên kèm theo label :

import random
import math
def plot_random_images(n_samples=1): 
    random_choices = random.sample(range(len(train_data)), k=n_samples)
    n_cols = 3 
    n_rows = math.ceil(n_samples / n_cols)    
    plt.figure(figsize=(n_rows*6, 12))
    for i, image_index in enumerate(random_choices) :         
        target_image = train_data[image_index] 
        target_class = train_labels[image_index]
        target_class_name = class_names[target_class]
        plt.subplot(n_rows, n_cols, i+1)
        plt.imshow(target_image,cmap=plt.cm.viridis)
        plt.title(target_class_name)
        plt.axis(False)
plot_random_images(10)

Trước khi xây dựng mô hình phân loại, dữ liệu mà chúng ta có được là những dữ liệu hình ảnh. Do đó, việc tìm kiếm các mối liên hệ từ các ma trận tạo thành các hình ảnh sẽ giúp mô hình dự đoán đưa đến kết luận liệu với dữ liệu đó thì hình ảnh được cho sẽ thuộc label nào.

Vì đây là dữ liệu multiclass classification, có một số kiến trúc trong mô hình cần lưu ý : + input shape : với dữ liệu này là 28x28 tensors(tương ứng với chiều cao và chiều rộng của hình) - Với kích thước trên chúng ta sẽ duỗi chúng thành một vector với kích thước 784 + output shape : Giá trị xuất ra của mô hình sẽ là một mảng gồm 10 phần tử tương ứng với 10 class và tổng của chúng sẽ là 1 (mỗi phần tử là khả năng (xác suất) xảy ra một class nào đó, xác suất càng cao thì khả năng xảy ra class đó càng lớn) - Activation : Thay vì dùng sigmoid như mô hình phân loại 2 class, thì với nhiều class sẽ là softmax. + Thay đổi loss function khi compile mô hình từ mô hình 2 class sang nhiều class : - Lưu ý : + Nếu để labels từ 0-9 thì loss function phải là : tf.keras.losses.SparseCategoricalCrossentropy() + Nếu one-hot labels ( tức là tạo cho label thành một mảng gồm 10 phần tử, VD label là 2 thì sẽ có mảng sau : [0,0,1,0,0,0,0,0,0,0]) thì sử dụng : tf.keras.losses.CategoricalCrossentropy(). + Chúng ta sẽ sử dụng validation_data khi gọi hàm fit() cho mô hình. Điều này cho chúng ta theo dõi được quá trình mô hình hoạt động trong cả train và test

Train mô hình chưa được chuẩn hóa (non-normolized)

from tensorflow.keras import Sequential
from tensorflow.keras.layers import Flatten, Dense

Mô hình dưới đây label không được one-hot nên sử dụng tf.keras.losses.SparseCategoricalCrossentropy().Trước khi chạy mô hình, cần chuyển kiểu dữ liệu của label thành float32, nếu không có thể bạn sẽ bị báo lỗi

train_labels = np.float32(train_labels)
test_labels = np.float32(test_labels)

train_labels

array([9., 0., 0., ..., 3., 0., 5.], dtype=float32)

tf.random.set_seed(42)

model_10 = Sequential([
    Flatten(input_shape=(28,28)), 
    Dense(4,activation="relu"),
    Dense(4,activation="relu"),
    Dense(num_classes,activation="softmax")
])

model_10.compile(
    loss=tf.keras.losses.SparseCategoricalCrossentropy(),
    optimizer="adam",
    metrics=["accuracy"]
)

model_10_history = model_10.fit(
    train_data,
    train_labels,
    epochs=10,
    verbose=2,
    validation_data=(test_data, test_labels)
)

Epoch 1/10 1875/1875 - 4s - loss: 2.1772 - accuracy: 0.1593 - val_loss: 1.8122 - val_accuracy: 0.2049 Epoch 2/10 1875/1875 - 4s - loss: 1.7162 - accuracy: 0.2447 - val_loss: 1.6517 - val_accuracy: 0.2895 Epoch 3/10 1875/1875 - 3s - loss: 1.6362 - accuracy: 0.2834 - val_loss: 1.6411 - val_accuracy: 0.3092

...

Epoch 9/10
1875/1875 - 4s - loss: 1.5713 - accuracy: 0.3250 - val_loss: 1.5652 - val_accuracy: 0.3138
Epoch 10/10
1875/1875 - 4s - loss: 1.5689 - accuracy: 0.3201 - val_loss: 1.5680 - val_accuracy: 0.3045
model_10.summary()

Model: "sequential_10" _ Layer (type) Output Shape Param #
================================================================= flatten (Flatten) (None, 784) 0
_ dense_27 (Dense) (None, 4) 3140
_ dense_28 (Dense) (None, 4) 20
_ dense_29 (Dense) (None, 10) 50
================================================================= Total params: 3,210 Trainable params: 3,210 Non-trainable params: 0 _

model_10.evaluate(test_data, test_labels)

313/313 [==============================] - 1s 2ms/step - loss: 1.5680 - accuracy: 0.3045

[1.5680228471755981, 0.304500013589859]

Tạo hàm vẽ biểu đồ mô tả quá trình mô hình train dữ liệu qua từng epochs

def plot_loss_curves(history) : 
    history = history.history
    acc, loss = history["accuracy"], history["loss"]
    val_acc, val_loss = history["val_accuracy"], history["val_loss"]

    plt.figure(figsize=(16,6))
    plt.subplot(121)
    plt.plot(acc, label="train accuracy")
    plt.plot(val_acc, label="val accuracy")
    plt.xlabel("epochs")
    plt.ylabel("percentage")
    plt.legend()

    plt.subplot(122)
    plt.plot(loss, label="train loss")
    plt.plot(val_loss, label="val loss")
    plt.xlabel("epochs")
    plt.ylabel("percentage")
    plt.legend()
plot_loss_curves(model_10_history)

Mô hình có độ chính xác tương đối thấp (~35%) sau 10 epochs khi sử dụng mô hình tương tự với binary class ở phần trước.

Liệu bạn có còn nhớ trước đây chúng ta đã từng nói về chuẩn hóa dữ liệu trong neural network (đưa dữ liệu về 0 - 1)?

model_10 chưa chuẩn hóa các dữ liệu trong ma trận,do đó, các giá trị pixel dao động từ 0 - 255. Trước khi gọi hàm fit, các dữ liệu cần được chuẩn hóa.

Train mô hình với dữ liệu được chuẩn hóa (normolized data)

# Phạm vi các trị train data, test data khi chưa chuẩn hóa
(train_data.min(), train_data.max()), (test_data.min(), test_data.max())

((0, 255), (0, 255))

# Phạm vi các giá trị sau khi chuẩn hóa
train_data_scaled = train_data / 255. #(`255.` chia lấy kiểu float)
test_data_scaled = test_data / 255.

train_data_scaled.min(),train_data_scaled.max()

(0.0, 1.0)

OK, các giá trị đã được đưa về 0-1. Bây giờ ta sẽ chạy lại mô hình trên, chúng ta không cần phải khởi tạo mô hình bằng hàm Sequential, chỉ cần compilefit là được.

model_10.compile(
    loss=tf.keras.losses.SparseCategoricalCrossentropy(),
    optimizer="adam",
    metrics=["accuracy"]
)

model_10_normed_history = model_10.fit(
    train_data_scaled,
    train_labels,
    epochs=10, 
    validation_data=(test_data_scaled,test_labels)
)

Epoch 1/10 1875/1875 [==============================] - 5s 3ms/step - loss: 1.6361 - accuracy: 0.3201 - val_loss: 1.4551 - val_accuracy: 0.3628 Epoch 2/10 1875/1875 [==============================] - 5s 3ms/step - loss: 1.4170 - accuracy: 0.3830 - val_loss: 1.3824 - val_accuracy: 0.4017 Epoch 3/10 1875/1875 [==============================] - 5s 3ms/step - loss: 1.3564 - accuracy: 0.4137 - val_loss: 1.3377 - val_accuracy: 0.4314 ...

Epoch 8/10
1875/1875 [==============================] - 5s 3ms/step - loss: 1.1960 - accuracy: 0.5143 - val_loss: 1.1772 - val_accuracy: 0.5257
Epoch 9/10
1875/1875 [==============================] - 5s 3ms/step - loss: 1.1755 - accuracy: 0.5199 - val_loss: 1.1762 - val_accuracy: 0.5259
Epoch 10/10
1875/1875 [==============================] - 5s 3ms/step - loss: 1.1656 - accuracy: 0.5232 - val_loss: 1.1905 - val_accuracy: 0.5104

Cũng là model_10 trước khi scaled dữ liệu, với 10 epochs, độ chính xác chỉ khoảng ~37%, còn bây giờ nó đã lên đến ~51%. Một sự cải thiện độ chính xác đáng kể khi chỉ cần scaled dữ liệu. Theo dõi quá trình cải thiện của mô hình qua từng epochs:

pd.DataFrame(model_10_normed_history.history)
lossaccuracyval_lossval_accuracy
01.6360520.3201001.4551130.3628
11.4170370.3830331.3824200.4017
21.3563950.4136831.3377290.4314
31.3234880.4340501.3094100.4495
41.3062540.4489671.3094630.4588
51.2896720.4682331.2727320.4840
61.2472990.4937001.2064200.5181
71.1960210.5143001.1772170.5257
81.1754700.5199001.1762300.5259
91.1656210.5232171.1905360.5104
plot_loss_curves(model_10_normed_history)

So sánh giữa mô hình có dữ liệu bình thường và mô hình có dữ liệu được scaled.

plt.figure(figsize=(16,6))
ax1 = plt.subplot(121)
pd.DataFrame(model_10_history.history).plot(ax=ax1)
ax1.set_title("Non normalized data")
ax2 = plt.subplot(122)
pd.DataFrame(model_10_normed_history.history).plot(ax=ax2)
ax2.set_title("normalized data")
plt.suptitle("Compare the performance between non normalized data and normolized data in the same model")

Text(0.5, 0.98, 'Compare the performance between non normalized data and normolized data in the same model')

Từ 2 biểu đồ trên, có thể thấy được mô hình được chuẩn hóa (scaled) dữ liệu cải thiện độ chính xác và giảm độ sai sót đi rất nhiều so với mô hình không được chuẩn hóa.

Vậy còn learning_rate, chúng ta sẽ tìm learning_rate tốt cho mô hình

tf.random.set_seed(42)

# Trước hết chúng ta sẽ copy lại mô hình model_11
model_11 = tf.keras.models.clone_model(model_10)
model_11.summary()

Model: "sequential_10" _ Layer (type) Output Shape Param #
================================================================= flatten (Flatten) (None, 784) 0
_ dense_27 (Dense) (None, 4) 3140
_ dense_28 (Dense) (None, 4) 20
_ dense_29 (Dense) (None, 10) 50
================================================================= Total params: 3,210 Trainable params: 3,210 Non-trainable params: 0 _

trước khi compilefit, ta sẽ tạo một callback function ghi lại learning_rate của mô hình qua từng epoch và sẽ thực hiện train mô hình với 100 epochs

lr_scheduler = tf.keras.callbacks.LearningRateScheduler(lambda epoch : 1e-3 * 10**(epoch / 20),verbose=0)

model_11.compile(
    loss="sparse_categorical_crossentropy",
    optimizer="adam",
    metrics=["accuracy"]
)

model_11_normed_history = model_11.fit(
    train_data_scaled,
    train_labels,
    epochs=100, 
    verbose=0,
    validation_data=(test_data_scaled,test_labels),
    callbacks=[
        lr_scheduler
    ]
)

Biểu diễn đồ thị thể hiện mối liên hệ giữa val_losslearning rate

lrs = 1e-3 * 10**(np.arange(100) / 20)
plt.semilogx(lrs,model_11_normed_history.history["val_loss"])
plt.xlabel("Learning rate")
plt.ylabel("val_loss")
plt.title("Finding the ideal learning rate")

Text(0.5, 1.0, 'Finding the ideal learning rate')

Từ biểu đồ trên, có thể thấy val_loss thấp nhất nằm khi learning rate đạt giá trị $10^{-2}$. Nhưng trước hết, có thể thấy với số epochs 100, mô hình có vẻ như hoạt động không tốt, nên cần rút gọn số epochs lại. Bây giờ, thử fit lại mô hình với leanrning_rate là $10^{-2}$ với epochs là 50

tf.random.set_seed(42)

model_12 = tf.keras.models.clone_model(model_11)

model_12.compile(
    loss="sparse_categorical_crossentropy",
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.01),
    metrics=["accuracy"]
)

model_12_normed_history = model_12.fit(
    train_data_scaled,
    train_labels,
    epochs=50,
    validation_data=(test_data_scaled,test_labels)
)

Epoch 1/50 1875/1875 [==============================] - 5s 3ms/step - loss: 1.0991 - accuracy: 0.5714 - val_loss: 0.9693 - val_accuracy: 0.6478 Epoch 2/50 1875/1875 [==============================] - 5s 3ms/step - loss: 0.8667 - accuracy: 0.6924 - val_loss: 0.8411 - val_accuracy: 0.7120 Epoch 3/50 1875/1875 [==============================] - 5s 3ms/step - loss: 0.8373 - accuracy: 0.7011 - val_loss: 0.8128 - val_accuracy: 0.7123

...

Epoch 48/50
1875/1875 [==============================] - 5s 3ms/step - loss: 0.7480 - accuracy: 0.7327 - val_loss: 0.7731 - val_accuracy: 0.7317
Epoch 49/50
1875/1875 [==============================] - 5s 3ms/step - loss: 0.7464 - accuracy: 0.7333 - val_loss: 0.7966 - val_accuracy: 0.7229
Epoch 50/50
1875/1875 [==============================] - 5s 3ms/step - loss: 0.7407 - accuracy: 0.7356 - val_loss: 0.7792 - val_accuracy: 0.7288

Mô hình đã được train với learning_rate tốt, độ chính xác 70-74%. Với độ chính xác này, mô hình dự báo cũng khá ổn, nhưng mô hình này mới chỉ là mô hình cơ bản, có thể nó sẽ còn tốt hơn rất nhiều nếu nó được cải thiện.

Cải thiện mô hình

Có thể cải thiện mô hình bằng một số cách sau : - Train mô hình lâu hơn ( không khả thi vì mô hình trước 100 epochs tỏ ra rất kém) ❌ - Tăng số lượng dữ liệu train (Dữ liệu không thể thêm trong trường hợp này ) ❌ - Tăng số hidden layers trong mô hình ✅ - Tăng số unit (neurons) trong mỗi layer ✅ - Sử dụng transfer learning (Phần sau sẽ giới thiệu)

Ta sẽ tăng số hidden layers cùng với tăng số lượng unit trong mỗi layer

tf.random.set_seed(42)

model_13 = Sequential([
  Flatten(input_shape=(28,28)),
  Dense(128, activation="relu"),                       
  Dense(128, activation="relu"),
  Dense(128, activation="relu"),
  Dense(num_classes, activation="softmax"),
])

model_13.compile(
    loss="sparse_categorical_crossentropy",
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.01),
    metrics=["accuracy"]
)

model_13_normed_history = model_13.fit(
    train_data_scaled,
    train_labels,
    epochs=10, 
    validation_data=(test_data_scaled,test_labels)
)

Epoch 1/10 1875/1875 [==============================] - 6s 3ms/step - loss: 0.5755 - accuracy: 0.7956 - val_loss: 0.5782 - val_accuracy: 0.8067 Epoch 2/10 1875/1875 [==============================] - 5s 3ms/step - loss: 0.4735 - accuracy: 0.8370 - val_loss: 0.5257 - val_accuracy: 0.8145 Epoch 3/10 1875/1875 [==============================] - 5s 3ms/step - loss: 0.4499 - accuracy: 0.8418 - val_loss: 0.4867 - val_accuracy: 0.8331

...

Epoch 8/10
1875/1875 [==============================] - 5s 3ms/step - loss: 0.4071 - accuracy: 0.8583 - val_loss: 0.4340 - val_accuracy: 0.8517
Epoch 9/10
1875/1875 [==============================] - 5s 3ms/step - loss: 0.4099 - accuracy: 0.8576 - val_loss: 0.4954 - val_accuracy: 0.8314
Epoch 10/10
1875/1875 [==============================] - 5s 3ms/step - loss: 0.4021 - accuracy: 0.8602 - val_loss: 0.4390 - val_accuracy: 0.8456

Mô hình đã tăng tỉ lệ chính xác lên rất nhiều sau khi cải thiện mô hình.

Đánh giá mô hình được cải thiện

model_13.evaluate(test_data_scaled, test_labels)

313/313 [==============================] - 1s 2ms/step - loss: 0.4390 - accuracy: 0.8456

[0.43895140290260315, 0.8456000089645386]

Nhìn chung, mô hình có khả năng dự đoán chính xác lên đến 83% trong tổng số mẫu. Vậy độ chính xác cụ thể từng loại class của tập dữ liệu này là bao nhiêu?

  • Đầu tiên sẽ cho mô hình dữ đoán dữ liệu test
  • So sánh kết quả dự đoán với test_label để đánh giá độ chính xac

Để làm được điều này, trước tiên ta sẽ dự đoán mô hình rồi sau đó sẽ tạo hàm thể hiện độ chính xác của ma trận confusion_matrix

y_pred_probs = model_13.predict(test_data_scaled)
y_preds_probs.shape

(1000, 1)

Giá trị dự đoán gồm có 10000 tương ứng với 10000 mẫu từ tập dữ liệu test. Trong mỗi 10000 mẫu đó là một mảng gồm 10 phần tử đại diện cho 10 class thể hiện xác suất lựa chọn cho class đó. Vậy với xác suất nào lớn nhất, ta sẽ lựa chọn vị trí đó đại diện cho label dự đoán đó. VD : [0.1, 0.5,0.05,0.05,0.05,0.05, 0.05,0.05,0.05,0.05] với 0.5 là lớn nhất nên dự đoán class 1 là label cho mẫu này.

y_pred_labels = np.argmax(y_pred_probs,axis=1)
y_pred_labels

array([9, 2, 1, ..., 8, 1, 5])

from sklearn.metrics import confusion_matrix
import itertools

def plot_confusion_matrix(y_true, y_preds, class_names=None) : 
  cm = confusion_matrix(y_true, y_preds) 
  cm_normed = cm / np.sum(cm,axis=1).astype(np.float32)

  if class_names and cm.shape[0] != len(class_names) : 
    print("the length of class_names is not valid")

  if class_names : 
    labels = class_names 
  else : 
    labels = range(cm.shape[0])

  fig, ax = plt.subplots(figsize=(3*cm.shape[0],3*cm.shape[1]))
  cax = ax.matshow(cm, cmap=plt.cm.Blues)
  fig.colorbar(cax)
  print(labels)
  ax.set(
      title="Confusion matrix",
      xlabel="Predict Label",
      ylabel="True label",
      xticks=range(cm.shape[0]),
      yticks=range(cm.shape[1]),
      xticklabels=labels,
      yticklabels=labels,
  )
  ax.xaxis.set_ticks_position("bottom")
  ax.title.set_size(24)
  ax.set_xticklabels(labels=labels, rotation=90)
  ax.xaxis.label.set_size(20)
  ax.grid(False)

  threshold = (np.max(cm) + np.min(cm)) / 2

  for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])) : 
    plt.text(j,
             i,
             f"{cm[i,j]} ({cm_normed[i,j]*100:.2f})%", 
             fontsize=15, 
             horizontalalignment="center",
             color="black" if threshold > cm[i,j] else "white") 
plot_confusion_matrix(test_labels, y_pred_labels, class_names=class_names)

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

Dự đoán label

Sau khi đo lường độ chính xác cụ thể trên từng class, chúng ta sẽ tiến hành thử nghiệm với mô hình dự đoán và so sánh với giá trị thực xem kết quả sẽ như thế nào.

Để thử nghiệm, ta sẽ tạo hàm dự đoán hình ảnh ngẫu nhiên trên tập dữ liệu test. Hàm này nhận 3 tham số gồm : + Mô hình dự đoán + Tập dữ liệu test (ma trận hình ảnh được được chuẩn hóa) + Labels của tập dữ liệu test (để so sánh với giá trị dự đoán)

import random 

def make_predict_and_plot_image(model, image_data,labels) : 
  random_image = random.choice(range(len(image_data)))
  image = image_data[random_image]
  target_class = labels[random_image]
  target_class_name = class_names[int(target_class)]

  image_pred_probs = model.predict(tf.expand_dims(image,axis=0))
  image_pred_class= tf.argmax(image_pred_probs,axis=1)
  image_pred_label = class_names[int(image_pred_class)]

  color = "red"
  if target_class_name == image_pred_label : 
    color = "green"

  plt.figure(figsize=(8,4))
  plt.imshow(image)
  plt.title(f"Actual : {target_class_name}\n Predict: {image_pred_label} with {tf.reduce_max(image_pred_probs[0])*100:.2f}%", color=color)
make_predict_and_plot_image(model_13, test_data_scaled,test_labels)

Mô hình đã học được những đặc tính nào?

Ở những bài viết trước đã có nhiều lần đề cập đến việc tìm các đặc tính (các điểm chung hoặc đặc trưng của các hình ảnh để liên kết giữa chúng với nhau) tiêu biểu trong neural network bằng các con số, nhưng với mô hình này đặc tính trong nó là gì ?

model_13.layers

[<keras.layers.core.Flatten at 0x7fa2870a3350>, <keras.layers.core.Dense at 0x7fa2870a3490>, <keras.layers.core.Dense at 0x7fa2870a3850>, <keras.layers.core.Dense at 0x7fa2870a3c10>, <keras.layers.core.Dense at 0x7fa287096050>]

model_13.layers[1]

<keras.layers.core.Dense at 0x7fa2870a3490>

Và chúng ta có thể tìm thấy các đặc tính được học bởi các layer cụ thể bằng phương thức get_weights()

weights, bias = model_13.layers[1].get_weights()

weights, bias[:5], weights.shape, bias.shape

(array([[ 2.66954973e-02, -9.56996530e-03, 7.47938275e-01, ..., -4.07474041e-02, 1.48007795e-02, -3.90902162e-04], [ 6.45309016e-02, -6.13619164e-02, 1.09231544e+00, ..., 4.91267771e-01, 5.40623888e-02, 4.54814956e-02], [ 4.06332910e-02, 4.49500158e-02, 2.56287861e+00, ..., 4.35182095e-01, -4.74400409e-02, -5.38105518e-03], ..., [ 7.16553256e-02, -3.15123200e-02, 1.48855698e+00, ..., 1.52392995e+00, 6.87063709e-02, 3.35246995e-02], [ 2.92116031e-02, -6.55823946e-03, 9.96252239e-01, ..., 1.63837636e+00, 4.45979163e-02, 6.81063607e-02], [ 5.24425432e-02, -7.66266957e-02, 7.30403543e-01, ..., 1.63493883e-02, 4.40619066e-02, -4.64956462e-03]], dtype=float32), array([-0.0600384 , -0.06001915, 1.6928424 , -0.09111124, -0.06004031], dtype=float32), (784, 128), (128,))

Có thể thấy ma trận weights có kích thước tương tự với dữ liệu được duỗi sau khi nhập vào 784(28 x 28) và có sự sao chép của các ma trận weights với mỗi neuons trong một layer đươc chỉ định (layer được chỉ định có 128 neurons)

Mỗi giá trị trong ma trận weights phản ánh cách mà các giá trị cụ thể trong dữ liệu đưa vào ảnh hưởng đến việc quyết định của mô hình

Kết luận

Chúng ta đã xây dựng một chuỗi mô hình, nhưng chưa từng đề cập đến những gì xảy ra bên trong quá trình train của mô hình đó là gì. Vậy chính xác mô hình đã học những gì ?

Mô hình đã học bằng việc cập nhật và cải thiện các đặc tính (các giá trị ma trận weightbias) qua mỗi epochs. Nó thực hiện việc cải thiện bằng cách so sánh các điểm đặc trưng mà nó đã được học từ dữ liệu được đưa vào để nó dự đoán label với label thực.

Nếu các đặc tính (weightbias) không đưa đến kết quả như mong muốn như làm giảm loss function (loss càng cao đồng nghĩa dự đoán càng tệ), thì khi đó optimizer sẽ cố gắng đưa mô hình cập nhật trở lại các đặc tính theo cách phù hợp ( sử dụng label thực để tham chiếu).

Quá trình sử dụng label thực như một phép tham chiếu để cải thiện mô hình dự đoán được gọi là backpropagation.

Có thể hiểu cách khác, dữ liệu và labels được truyền qua mô hình (truyền tới) và mô hình cố gắng tìm ra mối liên hệ giữa dữ liệu với label đó.

Và nếu như mô hình học về mối liên hệ không gần với mối liên hệ thực tế hoặc nó có thể cải thiện thì mô hình đó sẽ thực hiện bằng cách truyền ngược lại và điều chỉnh ma trận weightbias để nó thể hiện dữ liệu tốt hơn.


Bài viết có liên quan