Trang này được dịch bởi Cloud Translation API.
Switch to English

Phân loại hình ảnh

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ tay

Hướng dẫn này chỉ ra cách phân loại hình ảnh của hoa. Nó tạo bộ phân loại hình ảnh bằng mô hình keras.Sequential và tải dữ liệu bằng cách sử dụng preprocessing.image_dataset_from_directory . Bạn sẽ có được kinh nghiệm thực tế với các khái niệm sau:

  • Tải tập dữ liệu ra đĩa một cách hiệu quả.
  • Xác định trang bị quá mức và áp dụng các kỹ thuật để giảm thiểu nó, bao gồm cả việc tăng dữ liệu và Bỏ học.

Hướng dẫn này tuân theo quy trình công việc học máy cơ bản:

  1. Kiểm tra và hiểu dữ liệu
  2. Xây dựng đường dẫn đầu vào
  3. Xây dựng mô hình
  4. Đào tạo mô hình
  5. Kiểm tra mô hình
  6. Cải thiện mô hình và lặp lại quy trình

Nhập TensorFlow và các thư viện khác

import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential

Tải xuống và khám phá tập dữ liệu

Hướng dẫn này sử dụng tập dữ liệu khoảng 3.700 bức ảnh về hoa. Tập dữ liệu chứa 5 thư mục con, mỗi thư mục một lớp:

flower_photo/
  daisy/
  dandelion/
  roses/
  sunflowers/
  tulips/
import pathlib
dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
data_dir = tf.keras.utils.get_file('flower_photos', origin=dataset_url, untar=True)
data_dir = pathlib.Path(data_dir)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 5s 0us/step

Sau khi tải xuống, bây giờ bạn sẽ có sẵn một bản sao của tập dữ liệu. Có tổng cộng 3.670 hình ảnh:

image_count = len(list(data_dir.glob('*/*.jpg')))
print(image_count)
3670

Đây là một số loại hoa hồng:

roses = list(data_dir.glob('roses/*'))
PIL.Image.open(str(roses[0]))

png

PIL.Image.open(str(roses[1]))

png

Và một số hoa tulip:

tulips = list(data_dir.glob('tulips/*'))
PIL.Image.open(str(tulips[0]))

png

PIL.Image.open(str(tulips[1]))

png

Tải bằng keras.preprocessing

Hãy tải những hình ảnh này ra đĩa bằng tiện ích image_dataset_from_directory hữu ích. Điều này sẽ đưa bạn từ một thư mục hình ảnh trên đĩa sangtf.data.Dataset chỉ trong một vài dòng mã. Nếu muốn, bạn cũng có thể viết mã tải dữ liệu của riêng mình từ đầu bằng cách truy cập hướng dẫn tải hình ảnh .

Tạo tập dữ liệu

Xác định một số tham số cho bộ nạp:

batch_size = 32
img_height = 180
img_width = 180

Bạn nên sử dụng phân tách xác thực khi phát triển mô hình của mình. Hãy sử dụng 80% hình ảnh để đào tạo và 20% để xác thực.

train_ds = tf.keras.preprocessing.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="training",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)
Found 3670 files belonging to 5 classes.
Using 2936 files for training.

val_ds = tf.keras.preprocessing.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="validation",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)
Found 3670 files belonging to 5 classes.
Using 734 files for validation.

Bạn có thể tìm tên lớp trong thuộc tính class_names trên các tập dữ liệu này. Chúng tương ứng với tên thư mục theo thứ tự bảng chữ cái.

class_names = train_ds.class_names
print(class_names)
['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']

Trực quan hóa dữ liệu

Dưới đây là 9 hình ảnh đầu tiên từ tập dữ liệu đào tạo.

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(1):
  for i in range(9):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(images[i].numpy().astype("uint8"))
    plt.title(class_names[labels[i]])
    plt.axis("off")

png

Bạn sẽ đào tạo một mô hình bằng cách sử dụng các tập dữ liệu này bằng cách chuyển chúng đến model.fit trong giây lát. Nếu muốn, bạn cũng có thể lặp lại theo cách thủ công qua tập dữ liệu và truy xuất hàng loạt hình ảnh:

for image_batch, labels_batch in train_ds:
  print(image_batch.shape)
  print(labels_batch.shape)
  break
(32, 180, 180, 3)
(32,)

image_batch là một tensor của hình dạng (32, 180, 180, 3) . Đây là một loạt gồm 32 hình ảnh có hình dạng 180x180x3 (kích thước cuối cùng đề cập đến các kênh màu RGB). label_batch là một tenxơ của hình (32,) , đây là những nhãn tương ứng với 32 hình ảnh.

Bạn có thể gọi .numpy() trên image_batch và các tensors labels_batch để chuyển chúng thành numpy.ndarray .

Định cấu hình tập dữ liệu cho hiệu suất

Hãy đảm bảo sử dụng tìm nạp trước có bộ đệm để bạn có thể mang lại dữ liệu từ đĩa mà không bị chặn I / O. Đây là hai phương pháp quan trọng bạn nên sử dụng khi tải dữ liệu.

Dataset.cache() giữ hình ảnh trong bộ nhớ sau khi chúng được tải ra khỏi đĩa trong kỷ nguyên đầu tiên. Điều này sẽ đảm bảo tập dữ liệu không trở thành nút cổ chai trong khi đào tạo mô hình của bạn. Nếu tập dữ liệu của bạn quá lớn để vừa với bộ nhớ, bạn cũng có thể sử dụng phương pháp này để tạo bộ đệm ẩn trên đĩa hiệu quả.

Dataset.prefetch() chồng lên quá trình tiền xử lý dữ liệu và thực thi mô hình trong khi đào tạo.

Bạn đọc quan tâm có thể tìm hiểu thêm về cả hai phương pháp, cũng như cách lưu dữ liệu vào đĩa đệm trong phần hướng dẫn về hiệu suất dữ liệu .

AUTOTUNE = tf.data.experimental.AUTOTUNE

train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

Chuẩn hóa dữ liệu

Các giá trị kênh RGB nằm trong phạm vi [0, 255] . Điều này không lý tưởng cho mạng nơ-ron; nói chung, bạn nên tìm cách giảm giá trị đầu vào của mình. Tại đây, bạn sẽ chuẩn hóa các giá trị trong phạm vi [0, 1] bằng cách sử dụng lớp Thay đổi tỷ lệ.

normalization_layer = layers.experimental.preprocessing.Rescaling(1./255)

Có hai cách để sử dụng lớp này. Bạn có thể áp dụng nó vào tập dữ liệu bằng cách gọi bản đồ:

normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_ds))
first_image = image_batch[0]
# Notice the pixels values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))
0.006427039 0.99052274

Hoặc, bạn có thể bao gồm lớp bên trong định nghĩa mô hình của mình, điều này có thể đơn giản hóa việc triển khai. Hãy sử dụng cách tiếp cận thứ hai ở đây.

Tạo mô hình

Mô hình bao gồm ba khối chập với một lớp nhóm tối đa trong mỗi khối. Có một lớp được kết nối đầy đủ với 128 đơn vị trên cùng được kích hoạt bằng chức năng kích hoạt relu . Mô hình này chưa được điều chỉnh để có độ chính xác cao, mục tiêu của hướng dẫn này là thể hiện một cách tiếp cận tiêu chuẩn.

num_classes = 5

model = Sequential([
  layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(32, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(64, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Flatten(),
  layers.Dense(128, activation='relu'),
  layers.Dense(num_classes)
])

Biên dịch mô hình

Đối với hướng dẫn này, hãy chọn hàm optimizers.Adam ưu hóa Optimizer.Adam và losses.SparseCategoricalCrossentropy mất mát losses.SparseCategoricalCrossentropy . Để xem độ chính xác của quá trình đào tạo và xác thực cho từng kỷ nguyên đào tạo, hãy chuyển đối metrics số.

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

Tóm tắt mô hình

Xem tất cả các lớp của mạng bằng phương pháp summary của mô hình:

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
rescaling_1 (Rescaling)      (None, 180, 180, 3)       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 180, 180, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 90, 90, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 90, 90, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 45, 45, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 45, 45, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 22, 22, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 30976)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               3965056   
_________________________________________________________________
dense_1 (Dense)              (None, 5)                 645       
=================================================================
Total params: 3,989,285
Trainable params: 3,989,285
Non-trainable params: 0
_________________________________________________________________

Đào tạo mô hình

epochs=10
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
Epoch 1/10
92/92 [==============================] - 3s 27ms/step - loss: 1.3816 - accuracy: 0.4077 - val_loss: 1.0884 - val_accuracy: 0.5518
Epoch 2/10
92/92 [==============================] - 1s 10ms/step - loss: 1.0222 - accuracy: 0.6039 - val_loss: 0.9661 - val_accuracy: 0.5872
Epoch 3/10
92/92 [==============================] - 1s 10ms/step - loss: 0.8417 - accuracy: 0.6778 - val_loss: 0.8763 - val_accuracy: 0.6417
Epoch 4/10
92/92 [==============================] - 1s 10ms/step - loss: 0.6234 - accuracy: 0.7691 - val_loss: 0.8961 - val_accuracy: 0.6444
Epoch 5/10
92/92 [==============================] - 1s 10ms/step - loss: 0.4066 - accuracy: 0.8580 - val_loss: 0.9164 - val_accuracy: 0.6717
Epoch 6/10
92/92 [==============================] - 1s 10ms/step - loss: 0.2379 - accuracy: 0.9234 - val_loss: 1.1665 - val_accuracy: 0.6417
Epoch 7/10
92/92 [==============================] - 1s 10ms/step - loss: 0.1372 - accuracy: 0.9571 - val_loss: 1.3581 - val_accuracy: 0.6621
Epoch 8/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0802 - accuracy: 0.9789 - val_loss: 1.5392 - val_accuracy: 0.6526
Epoch 9/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0405 - accuracy: 0.9918 - val_loss: 1.7072 - val_accuracy: 0.6730
Epoch 10/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0311 - accuracy: 0.9925 - val_loss: 1.7984 - val_accuracy: 0.6458

Hình dung kết quả đào tạo

Tạo các biểu đồ tổn thất và độ chính xác trên các tập huấn luyện và xác nhận.

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

png

Như bạn có thể thấy từ các biểu đồ, độ chính xác của quá trình huấn luyện và độ chính xác của việc xác thực bị giảm do biên độ lớn và mô hình chỉ đạt được độ chính xác khoảng 60% trên bộ xác thực.

Hãy xem điều gì đã xảy ra và cố gắng tăng hiệu suất tổng thể của mô hình.

Overfitting

Trong các biểu đồ ở trên, độ chính xác của quá trình huấn luyện đang tăng tuyến tính theo thời gian, trong khi độ chính xác của quá trình xác nhận sẽ giảm khoảng 60% trong quá trình luyện. Ngoài ra, sự khác biệt về độ chính xác giữa đào tạo và độ chính xác xác thực là đáng chú ý — một dấu hiệu của việc trang bị quá nhiều .

Khi có một số lượng nhỏ các ví dụ đào tạo, mô hình đôi khi học hỏi từ các tiếng ồn hoặc các chi tiết không mong muốn từ các ví dụ đào tạo — đến mức nó tác động tiêu cực đến hiệu suất của mô hình đối với các ví dụ mới. Hiện tượng này được gọi là overfitting. Có nghĩa là mô hình sẽ gặp khó khăn khi tổng quát hóa trên một tập dữ liệu mới.

Có nhiều cách để chống lại tình trạng ăn quá nhiều trong quá trình luyện tập. Trong hướng dẫn này, bạn sẽ sử dụng phương pháp tăng dữ liệu và thêm Dropout vào mô hình của mình.

Tăng dữ liệu

Overfitting thường xảy ra khi có một số lượng nhỏ các ví dụ đào tạo. Tăng cường dữ liệu sử dụng phương pháp tạo dữ liệu đào tạo bổ sung từ các ví dụ hiện có của bạn bằng cách tăng cường chúng bằng cách sử dụng các phép biến đổi ngẫu nhiên mang lại hình ảnh trông đáng tin cậy. Điều này giúp mô hình hiển thị nhiều khía cạnh của dữ liệu hơn và khái quát hóa tốt hơn.

Bạn sẽ triển khai tăng dữ liệu bằng cách sử dụng Lớp tiền xử lý Keras thử nghiệm. Chúng có thể được đưa vào bên trong mô hình của bạn giống như các lớp khác và chạy trên GPU.

data_augmentation = keras.Sequential(
  [
    layers.experimental.preprocessing.RandomFlip("horizontal", 
                                                 input_shape=(img_height, 
                                                              img_width,
                                                              3)),
    layers.experimental.preprocessing.RandomRotation(0.1),
    layers.experimental.preprocessing.RandomZoom(0.1),
  ]
)

Hãy hình dung một vài ví dụ tăng cường trông như thế nào bằng cách áp dụng tăng dữ liệu nhiều lần cho cùng một hình ảnh:

plt.figure(figsize=(10, 10))
for images, _ in train_ds.take(1):
  for i in range(9):
    augmented_images = data_augmentation(images)
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(augmented_images[0].numpy().astype("uint8"))
    plt.axis("off")

png

Bạn sẽ sử dụng tăng dữ liệu để đào tạo một mô hình trong giây lát.

Rơi ra ngoài

Một kỹ thuật khác để giảm tình trạng quá tải là giới thiệu Dropout vào mạng lưới, một hình thức chính quy hóa .

Khi bạn áp dụng Dropout cho một lớp, nó ngẫu nhiên rơi ra (bằng cách đặt kích hoạt thành 0) một số đơn vị đầu ra từ lớp trong quá trình đào tạo. Bỏ học lấy một số phân số làm giá trị đầu vào của nó, ở dạng chẳng hạn như 0,1, 0,2, 0,4, v.v. Điều này có nghĩa là bỏ 10%, 20% hoặc 40% đơn vị đầu ra ngẫu nhiên khỏi lớp được áp dụng.

Hãy tạo một mạng nơ-ron mới bằng cách sử dụng layers.Dropout , sau đó đào tạo nó bằng cách sử dụng hình ảnh tăng cường.

model = Sequential([
  data_augmentation,
  layers.experimental.preprocessing.Rescaling(1./255),
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(32, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(64, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Dropout(0.2),
  layers.Flatten(),
  layers.Dense(128, activation='relu'),
  layers.Dense(num_classes)
])

Biên dịch và đào tạo mô hình

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])
model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
sequential_1 (Sequential)    (None, 180, 180, 3)       0         
_________________________________________________________________
rescaling_2 (Rescaling)      (None, 180, 180, 3)       0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 180, 180, 16)      448       
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 90, 90, 16)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 90, 90, 32)        4640      
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 45, 45, 32)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 45, 45, 64)        18496     
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 22, 22, 64)        0         
_________________________________________________________________
dropout (Dropout)            (None, 22, 22, 64)        0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 30976)             0         
_________________________________________________________________
dense_2 (Dense)              (None, 128)               3965056   
_________________________________________________________________
dense_3 (Dense)              (None, 5)                 645       
=================================================================
Total params: 3,989,285
Trainable params: 3,989,285
Non-trainable params: 0
_________________________________________________________________

epochs = 15
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
Epoch 1/15
92/92 [==============================] - 1s 13ms/step - loss: 1.4326 - accuracy: 0.3760 - val_loss: 1.1774 - val_accuracy: 0.5123
Epoch 2/15
92/92 [==============================] - 1s 12ms/step - loss: 1.1058 - accuracy: 0.5525 - val_loss: 0.9981 - val_accuracy: 0.5967
Epoch 3/15
92/92 [==============================] - 1s 12ms/step - loss: 1.0014 - accuracy: 0.5937 - val_loss: 0.9525 - val_accuracy: 0.6185
Epoch 4/15
92/92 [==============================] - 1s 12ms/step - loss: 0.9205 - accuracy: 0.6383 - val_loss: 0.9474 - val_accuracy: 0.6376
Epoch 5/15
92/92 [==============================] - 1s 12ms/step - loss: 0.8813 - accuracy: 0.6594 - val_loss: 0.9383 - val_accuracy: 0.6417
Epoch 6/15
92/92 [==============================] - 1s 12ms/step - loss: 0.8366 - accuracy: 0.6734 - val_loss: 0.8468 - val_accuracy: 0.6512
Epoch 7/15
92/92 [==============================] - 1s 12ms/step - loss: 0.7955 - accuracy: 0.6979 - val_loss: 0.8837 - val_accuracy: 0.6717
Epoch 8/15
92/92 [==============================] - 1s 12ms/step - loss: 0.7485 - accuracy: 0.7163 - val_loss: 0.8417 - val_accuracy: 0.6730
Epoch 9/15
92/92 [==============================] - 1s 12ms/step - loss: 0.7276 - accuracy: 0.7282 - val_loss: 0.8505 - val_accuracy: 0.6826
Epoch 10/15
92/92 [==============================] - 1s 12ms/step - loss: 0.6981 - accuracy: 0.7374 - val_loss: 0.7679 - val_accuracy: 0.6948
Epoch 11/15
92/92 [==============================] - 1s 12ms/step - loss: 0.6755 - accuracy: 0.7446 - val_loss: 0.7863 - val_accuracy: 0.6948
Epoch 12/15
92/92 [==============================] - 1s 12ms/step - loss: 0.6375 - accuracy: 0.7585 - val_loss: 0.7911 - val_accuracy: 0.7044
Epoch 13/15
92/92 [==============================] - 1s 12ms/step - loss: 0.6095 - accuracy: 0.7790 - val_loss: 0.7403 - val_accuracy: 0.7139
Epoch 14/15
92/92 [==============================] - 1s 12ms/step - loss: 0.6116 - accuracy: 0.7681 - val_loss: 0.7794 - val_accuracy: 0.7153
Epoch 15/15
92/92 [==============================] - 1s 12ms/step - loss: 0.5818 - accuracy: 0.7762 - val_loss: 0.7729 - val_accuracy: 0.7044

Hình dung kết quả đào tạo

Sau khi áp dụng tăng dữ liệu và Bỏ qua, ít trang bị quá mức hơn trước và độ chính xác của quá trình đào tạo và xác thực được liên kết chặt chẽ hơn.

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

png

Dự đoán về dữ liệu mới

Cuối cùng, hãy sử dụng mô hình của chúng tôi để phân loại một hình ảnh không được bao gồm trong bộ đào tạo hoặc xác nhận.

sunflower_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/592px-Red_sunflower.jpg"
sunflower_path = tf.keras.utils.get_file('Red_sunflower', origin=sunflower_url)

img = keras.preprocessing.image.load_img(
    sunflower_path, target_size=(img_height, img_width)
)
img_array = keras.preprocessing.image.img_to_array(img)
img_array = tf.expand_dims(img_array, 0) # Create a batch

predictions = model.predict(img_array)
score = tf.nn.softmax(predictions[0])

print(
    "This image most likely belongs to {} with a {:.2f} percent confidence."
    .format(class_names[np.argmax(score)], 100 * np.max(score))
)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/592px-Red_sunflower.jpg
122880/117948 [===============================] - 0s 0us/step
This image most likely belongs to sunflowers with a 99.45 percent confidence.