Phân loại cơ bản: Dự đoán ảnh quần áo giày dép

Xem trên TensorFlow.org Chạy trên Google Colab Xem mã nguồn trên GitHub Tải notebook

Lưu ý: Cộng đồng TensorFlow tại Việt Nam đã và đang dịch những tài liệu này từ nguyên bản tiếng Anh. Những bản dịch này được hoàn thiện dựa trên sự nỗ lực đóng góp từ cộng đồng lập trình viên sử dụng TensorFlow, và điều này có thể không đảm bảo được tính cập nhật của bản dịch đối với Tài liệu chính thức bằng tiếng Anh này. Nếu bạn có bất kỳ đề xuất nào nhằm cải thiện bản dịch này, vui lòng tạo Pull request đến kho chứa trên GitHub của tensorflow/docs-l10n. Để đăng ký dịch hoặc cải thiện nội dung bản dịch, các bạn hãy liên hệ và đặt vấn đề tại docs-vi@tensorflow.org.

Trong hướng dẫn này, chúng ta sẽ huấn luyện một mô hình mạng neuron để phân loại các hình ảnh về quần áo và giày dép.

Đừng ngại nếu bạn không hiểu hết mọi chi tiết, vì chương trình trong hướng dẫn này là một chương trình TensorFlow hoàn chỉnh, và các chi tiết sẽ dần được giải thích ở những phần sau.

Hướng dẫn này dùng tf.keras, một API cấp cao để xây dựng và huấn luyện các mô hình trong TensorFlow.

# TensorFlow and tf.keras
import tensorflow as tf
from tensorflow import keras

# Helper libraries
import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)
2.3.0

Import tập dữ liệu về quần áo và giày dép từ Fashion MNIST

fashion_mnist = keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = 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
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/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

Tập dữ liệu sau khi được tải sẽ trả về 4 mảng NumPy:

  • 2 mảng train_imagestrain_labelstập huấn luyện. Mô hình sẽ học từ dữ liệu của 2 mảng này.
  • 2 mảng test_images vả test_labelstập kiểm thử. Sau khi mô hình được huấn luyện xong, chúng ta sẽ chạy thử mô hình với dữ liệu đầu vào từ test_images để lấy kết quả, và so sánh kết quả đó với dữ liệu đối ứng từ test_labels để đánh giá chất lượng của mạng neuron.

Mỗi ảnh là một mảng NumPy 2 chiều, 28x28, với mỗi pixel có giá trị từ 0 đến 255. Nhãn là một mảng của các số nguyên từ 0 đến 9, tương ứng với mỗi lớp quần áo giày dép:

Nhãn Lớp
0 Áo thun
1 Quần dài
2 Áo liền quần
3 Đầm
4 Áo khoác
5 Sandal
6 Áo sơ mi
7 Giày
8 Túi xách
9 Ủng

Mỗi ảnh sẽ được gán với một nhãn duy nhất. Vì tên của mỗi lớp không có trong tập dữ liệu, nên chúng ta có thể định nghĩa ở đây để dùng về sau:

class_names = ['Áo thun', 'Quần dài', 'Áo liền quần', 'Đầm', 'Áo khoác',
               'Sandal', 'Áo sơ mi', 'Giày', 'Túi xách', 'Ủng']

Khám phá dữ liệu

Chúng ta có thể khám phá dữ liệu một chút trước khi huấn luyện mô hình. Câu lệnh sau sẽ cho ta thấy có 60.000 ảnh trong tập huấn luyện, với mỗi ảnh được biểu diễn theo dạng 28x28 pixel:

train_images.shape
(60000, 28, 28)

Tương tự, tập huấn luyện cũng có 60.000 nhãn đối ứng:

len(train_labels)
60000

Mỗi nhãn là một số nguyên từ 0 đến 9:

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

Có 10.000 ảnh trong tập kiểm thử, mỗi ảnh cũng được biểu diễn ở dãng 28 x 28 pixel:

test_images.shape
(10000, 28, 28)

Và tập kiểm thử cũng chứa 10,000 nhãn:

len(test_labels)
10000

Tiền xử lý dữ liệu

Dữ liệu cần được tiền xử lý trước khi được dùng để huấn luyện mạng neuron. Phân tích ảnh đầu tiên trong tập dữ liệu, chúng ta sẽ thấy các pixel có giá trị từ 0 đến 255:

plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.grid(False)
plt.show()

png

Chúng ta cần tiền xử lý để mỗi một điểm ảnh có giá trị từ 0 đến 1 (có thể hiểu là 0% đến 100%). Để làm điều này, chúng ta chỉ cần lấy giá trị của pixel chia cho 255. Cần lưu ý rằng việc tiền xử lý này phải được áp dụng đồng thời cho cả tập huấn luyệntập kiểm thử:

train_images = train_images / 255.0

test_images = test_images / 255.0

Để chắc chắn việc tiền xử lý diễn ra chính xác, chúng ta có thể in ra 25 ảnh đầu trong tập huấn luyện cùng với tên lớp dưới mỗi ảnh.

plt.figure(figsize=(10,10))
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()

png

Xây dựng mô hình

Để xây dựng mạng neuron, chúng tay cần cấu hình các layer của mô hình, và sau đó biên dịch mô hình.

Thiết lập các layers

Thành phần cơ bản của một mạng neuron là các layer. Các layer trích xuất các điểm đặc biệt từ dữ liệu mà chúng đón nhận. Khi thực hiện tốt, những điểm đặc biệt này mang nhiều ý nghĩa và phục vụ cho toán của chúng ta.

Đa số các mô hình deep learning đều chứa các layer đơn gian được xâu chuỗi lại với nhau. Đa số các layer, ví dụ tf.keras.layers.Dense, đều có các trọng số sẽ được học trong quá trình huấn luyện.

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

Trong mạng neuron trên, lớp đầu tiên, tf.keras.layers.Flatten, chuyển đổi định dạng của hình ảnh từ mảng hai chiều (28x28) thành mảng một chiều (28x28 = 784). Tưởng tương công việc của layer này là cắt từng dòng của anh, và ghép nối lại thành một dòng duy nhất nhưng dài gấp 28 lần. Lớp này không có trọng số để học, nó chỉ định dạng lại dữ liệu.

Sau layer làm phẳng ảnh (từ 2 chiều thành 1 chiều), phần mạng neuron còn lại gồm một chuỗi hai layer tf.keras.layers.Dense. Đây là các layer neuron được kết nối hoàn toàn (mỗi một neuron của layer này kết nối đến tất cả các neuron của lớp trước và sau nó). Layer Dense đầu tiên có 128 nút (hoặc neuron). Layer thứ hai (và cuối cùng) là lớp softmax có 10 nút, với mỗi nút tương đương với điểm xác suất, và tổng các giá trị của 10 nút này là 1 (tương đương 100%). Mỗi nút chứa một giá trị cho biết xác suất hình ảnh hiện tại thuộc về một trong 10 lớp.

Biên dịch mô hình

Trước khi mô hình có thể được huấn luyện, chúng ta cần thêm vài chỉnh sửa. Các chỉnh sửa này được thêm vào trong bước biên dịch của mô hình:

  • Hàm thiệt hại — dùng để đo lường mức độ chính xác của mô hình trong quá trình huấn luyện. Chúng ta cần giảm thiểu giá trị của hạm này "điều khiển" mô hình đi đúng hướng (thiệt hại càng ít, chính xác càng cao).
  • Trình tối ưu hoá — Đây là cách mô hình được cập nhật dựa trên dữ liệu huấn luyện được cung cấp và hàm thiệt hại.
  • Số liệu — dùng để theo dõi các bước huấn luyện và kiểm thử. Ví dụ sau dùng accuracy, tỉ lệ ảnh được phân loại chính xác.
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

Huấn luyện mô hình

Huấn luyện mô hình mạng neuron cần các bước sau:

  1. Cung cấp dữ liệu huấn luyện cho mô hình. Trong ví dụ này, dữ liệu huấn luyện năm trong 2 mảng train_imagestrain_labels
  2. Mô hình sẽ học cách liên kết ảnh với nhãn.
  3. Chúng ta sẽ yêu cầu mô hình đưa ra dự đoán từ dữ liệu của tập kiểm thử, trong ví dụ này là mảng test_images, sau đó lấy kết quả dự đoán đối chiếu với nhãn trong mảng test_labels.

Để bắt đầu huấn luyện, gọi hàm model.fit. Hàm này được đặt tên fit vì nó sẽ "fit" ("khớp") mô hình với dữ liệu huấn luyện:

model.fit(train_images, train_labels, epochs=10)
Epoch 1/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.4923 - accuracy: 0.8269
Epoch 2/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3714 - accuracy: 0.8662
Epoch 3/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3361 - accuracy: 0.8756
Epoch 4/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.3134 - accuracy: 0.8861
Epoch 5/10
1875/1875 [==============================] - 3s 1ms/step - loss: 0.2942 - accuracy: 0.8916
Epoch 6/10
1875/1875 [==============================] - 3s 1ms/step - loss: 0.2798 - accuracy: 0.8965
Epoch 7/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2661 - accuracy: 0.9014
Epoch 8/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2559 - accuracy: 0.9049
Epoch 9/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2451 - accuracy: 0.9083
Epoch 10/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2360 - accuracy: 0.9118

<tensorflow.python.keras.callbacks.History at 0x7f6f9a5bd6a0>

Trong quá trình huấn luyện, các số liệu như thiệt hại và hay độ chính xác được hiển thị. Với dữ liệu huấn luyện này, mô hình đạt đến độ accuracy vào khoảng 0.88 (88%).

Đánh giá mô hình

Tiếp theo, chúng đánh giá các chất lượng của mô hình bằng tập kiểm thử:

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)

print('\nTest accuracy:', test_acc)
313/313 - 0s - loss: 0.3503 - accuracy: 0.8807

Test accuracy: 0.8806999921798706

Đến thời điểm này, chúng ta thấy rằng độ accuracy của mô hình, khi đánh giá bằng tập kiểm thử, hơi thấp hơn so với số liệu trong quá trình huấn luyện. Khoảng cách giữa hai độ accuracy khi huấn luyện và khi kiểm thử thể hiện sự overfitting. Overfitting xảy ra khi một mô hình ML hoạt động kém hơn khi được cung cấp các đầu vào mới, mà mô hình chưa từng thấy trước đây trong quá trình đào tạo.

Đưa ra dự đoán

Với một mô hình đã được đào tạo, chúng ta có thể dùng nó để đưa ra dự đoán với một số ảnh.

predictions = model.predict(test_images)

Ở đây, mô hình sẽ dự đoán nhãn cho từng hình ảnh trong bộ thử nghiệm. Hãy xem dự đoán đầu tiên:

predictions[0]
array([2.5860672e-07, 6.6167111e-12, 1.0659722e-10, 5.2422120e-12,
       8.9344221e-10, 7.8752491e-04, 1.0192995e-07, 1.4829760e-03,
       9.1724054e-08, 9.9772900e-01], dtype=float32)

Trong ví dụ này, dự đoán là một mảng 10 số thực, mỗi số tương ứng với "độ tự tin" của mô hình rằng ảnh đó thuộc về nhãn đó. Chúng ta có thể thấy nhãn nào có độ tư tin cao nhất:

np.argmax(predictions[0])
9

Vậy là mô hình tự tin nhất rằng ảnh này là một loại ủng, hoặc class_names[9]. Đối chiếu với nhãn trong tập kiểm thử, ta thấy dự đoán này là đúng:

test_labels[0]
9

Ta thử vẽ biểu đồ để xem các dự đoán trên cả 10 lớp của mô hình.

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])
  predicted_label = np.argmax(predictions_array)

  thisplot[predicted_label].set_color('red')
  thisplot[true_label].set_color('blue')

Chúng ta có thể nhìn vào ảnh 0th, các dự đoán, và mảng dự đoán. Nhãn dự đoán đúng màu xanh và nhãn sai màu đỏ. Con số là số phần trăm của các nhãn được dự đoán.

i = 0
plt.figure(figsize=(6,3))
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()

png

i = 12
plt.figure(figsize=(6,3))
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()

png

Thử vẽ biểu đồ với vài ảnh và dự đoán đi kèm. Chú ý thấy rằng mô hình đôi khi dự đoán sai dù điểm tự tin rất cao.

# Plot the first X test images, their predicted labels, and the true labels.
# Color correct predictions in blue and incorrect predictions in red.
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  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()

png

Cuối cùng, dùng mô hình để đưa ra dự đoán về một ảnh duy nhất.

# Grab an image from the test dataset.
img = test_images[1]

print(img.shape)
(28, 28)

Các mô hình tf.keras được tối ưu hóa để đưa ra dự đoán về một hoặc bộ sưu tập các ví dụ cùng một lúc. Theo đó, mặc dù bạn đang sử dụng một ảnh duy nhất, bạn cần thêm nó vào list:

# Add the image to a batch where it's the only member.
img = (np.expand_dims(img,0))

print(img.shape)
(1, 28, 28)

Dự đoán nhãn cho ảnh này:

predictions_single = model.predict(img)

print(predictions_single)
[[1.9486699e-06 1.4760009e-13 9.9917406e-01 1.0503250e-09 8.2414605e-05
  2.6404438e-11 7.4154255e-04 3.7659091e-17 9.4212324e-14 9.7595890e-13]]

plot_value_array(1, predictions_single[0], test_labels)
_ = plt.xticks(range(10), class_names, rotation=45)

png

model.predict trả về một list của lists — mỗi list cho mỗi ảnh trong lô dữ liệu. Lấy dự đoán cho hình ảnh trong lô:

np.argmax(predictions_single[0])
2

Mô hình dự đoán ảnh này có nhãn là đúng như mong muốn.


#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

Chúng ta sẽ dùng tập dữ liệu về quần áo và giày dép từ Fashion MNIST, chứa khoảng 70,000 ảnh đen trắng phân thành 10 loại. Mỗi một ảnh là một loại quần áo hoặc giày dép với độ phân giải thấp (28 by 28 pixel), như hình minh hoạ bên dưới:

Fashion MNIST sprite
Figure 1. Fashion-MNIST samples (by Zalando, MIT License).
 

Fashion MNIST là tập dữ liệu được dùng để thay thế cho tập dữ liệu MNIST kinh điển thường dùng cho các chương trình "Hello, World" của machine learning trong lĩnh vực thị giác máy tính. Tập dữ liệu kinh điển vừa đề cập gồm ảnh của các con số (ví dụ 0, 1, 2) được viết tay. Các ảnh này có cùng định dạng tệp và độ phân giải với các ảnh về quần áo và giầy dép chúng ta sắp dùng.

Hướng dẫn này sử dụng tập dữ liệu Fashion MNIST, vì đây là một bài toán tương đối phức tạp hơn so với bài toán nhận diện chữ số viết tay. Cả 2 tập dữ liệu (Fashion MNIST và MNIST kinh điển) đều tương đối nhỏ và thường dùng để đảm bảo một giải thuật chạy đúng, phù hợp cho việc kiểm thử và debug.

Với tập dữ liệu này, 60.000 ảnh sẽ được dùng để huấn luyện và 10.000 ảnh sẽ đường dùng để đánh giá khả năng phân loại nhận diện ảnh của mạng neuron. Chúng ta có dùng tập dữ liệu Fashion MNIST trực tiếp từ TensorFlow như sau: