Sử dụng mạng nơ-ron chuyển đổi (CNN) với hình ảnh phức tạp

1. Trước khi bắt đầu

Trong lớp học lập trình này, bạn sẽ sử dụng lượt chuyển đổi để phân loại hình ảnh về ngựa và con người. Bạn sẽ sử dụng TensorFlow trong phòng thí nghiệm này để tạo một CNN được đào tạo để nhận dạng hình ảnh về ngựa và con người, cũng như phân loại các hình ảnh đó.

Điều kiện tiên quyết

Nếu trước đây bạn chưa từng xây dựng lượt chuyển đổi bằng TensorFlow, thì bạn có thể cần hoàn thành quá trình tạo lượt chuyển đổi và thực hiện phương pháp nhóm, trong đó chúng tôi giới thiệu lượt chuyển đổi và nhóm, đồng thời Xây dựng mạng nơ-ron cách mạng (CNN) để tăng cường thị lực máy tính. Chúng tôi sẽ thảo luận cách giúp máy tính nhận dạng hình ảnh hiệu quả hơn.

Kiến thức bạn sẽ học được

  • Cách đào tạo máy tính để nhận dạng các tính năng trong hình ảnh mà trong đó đối tượng không rõ ràng

Sản phẩm bạn sẽ tạo ra

  • Mạng nơron cách mạng có thể phân biệt giữa hình ảnh ngựa và hình của con người

Bạn cần có

Bạn có thể tìm thấy mã cho phần còn lại của lớp học lập trình chạy trong Colab.

Bạn cũng cần cài đặt TensorFlow và các thư viện bạn đã cài đặt trong lớp học lập trình trước đó.

2. Bắt đầu: Lấy dữ liệu

Bạn sẽ làm điều này bằng cách tạo một mã phân loại ngựa hoặc người. Mã này sẽ cho bạn biết liệu một hình ảnh cụ thể có chứa ngựa hay con người hay không, nơi mạng được đào tạo để nhận ra các tính năng xác định đó là con ngựa nào. Bạn sẽ phải xử lý dữ liệu một chút trước khi có thể huấn luyện.

Trước tiên, hãy tải dữ liệu xuống:

!wget --no-check-certificate https://storage.googleapis.com/laurencemoroney-blog.appspot.com/horse-or-human.zip  -O /tmp/horse-or-human.zip

Mã Python sau sẽ sử dụng thư viện hệ điều hành để sử dụng thư viện hệ điều hành, cho phép bạn truy cập vào hệ thống tệp và thư viện tệp zip, từ đó cho phép bạn giải nén dữ liệu.

import os
import zipfile
 
local_zip = '/tmp/horse-or-human.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('/tmp/horse-or-human')
zip_ref.close()

Nội dung của tệp zip được trích xuất vào thư mục cơ sở /tmp/horse-or-human, chứa các thư mục con và thư mục con.

Nói một cách ngắn gọn, bộ đào tạo là dữ liệu dùng để thông báo cho mô hình mạng nơ-ron, đó là giao diện của một con ngựa;

3. Sử dụng ImageGenerator để gắn nhãn và chuẩn bị dữ liệu

Bạn không được gắn nhãn rõ ràng hình ảnh là ngựa hoặc người.

Sau đó, bạn sẽ thấy một tên gọi là ImageDataGenerator đang được sử dụng. Googlebot đọc hình ảnh từ thư mục con và tự động gắn nhãn hình ảnh từ tên của thư mục con đó. Ví dụ: bạn có một thư mục huấn luyện chứa thư mục ngựa và thư mục do con người thực hiện. ImageDataGenerator sẽ gắn nhãn các hình ảnh một cách phù hợp cho bạn, giảm một bước lập trình.

Hãy xác định từng thư mục trong số đó.

# Directory with our training horse pictures
train_horse_dir = os.path.join('/tmp/horse-or-human/horses')
 
# Directory with our training human pictures
train_human_dir = os.path.join('/tmp/horse-or-human/humans')

Bây giờ, hãy xem tên tệp trông như thế nào trong thư mục huấn luyện ngựa và con người:

train_horse_names = os.listdir(train_horse_dir)
print(train_horse_names[:10])
train_human_names = os.listdir(train_human_dir)
print(train_human_names[:10])

Tìm tổng số hình ảnh ngựa và người trong các thư mục:

print('total training horse images:', len(os.listdir(train_horse_dir)))
print('total training human images:', len(os.listdir(train_human_dir)))

4. Khám phá dữ liệu

Hãy xem một vài bức ảnh để có thể hiểu rõ hơn về hình ảnh của bức ảnh.

Trước tiên, hãy định cấu hình các thông số matplot:

%matplotlib inline
 
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
 
# Parameters for our graph; we'll output images in a 4x4 configuration
nrows = 4
ncols = 4
 
# Index for iterating over images
pic_index = 0

Bây giờ, hãy hiển thị một lô hình ảnh 8 con ngựa và 8 hình ảnh con người. Bạn có thể chạy lại ô để xem lô mới mỗi lần.

# Set up matplotlib fig, and size it to fit 4x4 pics
fig = plt.gcf()
fig.set_size_inches(ncols * 4, nrows * 4)
 
pic_index += 8
next_horse_pix = [os.path.join(train_horse_dir, fname) 
                for fname in train_horse_names[pic_index-8:pic_index]]
next_human_pix = [os.path.join(train_human_dir, fname) 
                for fname in train_human_names[pic_index-8:pic_index]]
 
for i, img_path in enumerate(next_horse_pix+next_human_pix):
  # Set up subplot; subplot indices start at 1
  sp = plt.subplot(nrows, ncols, i + 1)
  sp.axis('Off') # Don't show axes (or gridlines)
 
  img = mpimg.imread(img_path)
  plt.imshow(img)
 
plt.show()
 

Dưới đây là một số hình ảnh mẫu cho thấy ngựa và người ở các tư thế và hướng khác nhau:

6b6ebbc6e694ccd2.png

5. Xác định mô hình

Bắt đầu xác định mô hình.

Bắt đầu bằng cách nhập TensorFlow:

import tensorflow as tf

Sau đó, thêm các lớp đối lưu và làm phẳng kết quả cuối cùng để đưa vào các lớp kết nối đông đúc. Cuối cùng, hãy thêm các lớp được kết nối dày đặc.

Lưu ý rằng vì bạn đang gặp phải vấn đề về phân loại hai lớp (vấn đề phân loại nhị phân) nên bạn sẽ kết thúc mạng của mình bằng kích hoạt sigmoid để đầu ra của mạng của bạn sẽ là một vô hướng duy nhất giữa 0 và 1, mã hóa xác suất rằng hình ảnh hiện tại là lớp 1 (trái ngược với lớp 0).

model = tf.keras.models.Sequential([
    # Note the input shape is the desired size of the image 300x300 with 3 bytes color
    # This is the first convolution
    tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(300, 300, 3)),
    tf.keras.layers.MaxPooling2D(2, 2),
    # The second convolution
    tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # The third convolution
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # The fourth convolution
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # The fifth convolution
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # Flatten the results to feed into a DNN
    tf.keras.layers.Flatten(),
    # 512 neuron hidden layer
    tf.keras.layers.Dense(512, activation='relu'),
    # Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class ('horses') and 1 for the other ('humans')
    tf.keras.layers.Dense(1, activation='sigmoid')
])

Lệnh gọi phương thức model.summary() sẽ in bản tóm tắt của mạng.

model.summary()

Bạn có thể xem kết quả tại đây:

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 298, 298, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 149, 149, 16)      0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 147, 147, 32)      4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 73, 73, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 71, 71, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 35, 35, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 33, 33, 64)        36928     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 16, 16, 64)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 14, 14, 64)        36928     
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 7, 7, 64)          0         
_________________________________________________________________
flatten (Flatten)            (None, 3136)              0         
_________________________________________________________________
dense (Dense)                (None, 512)               1606144   
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 513       
=================================================================
Total params: 1,704,097
Trainable params: 1,704,097
Non-trainable params: 0

Cột hình dạng đầu ra cho biết cách kích thước của bản đồ tính năng phát triển trong mỗi lớp kế tiếp. Các lớp chuyển đổi làm giảm kích thước của bản đồ đối tượng xuống một chút do khoảng đệm và mỗi lớp gộp một nửa kích thước.

6. Biên dịch mô hình

Tiếp theo, hãy định cấu hình các thông số kỹ thuật cho việc đào tạo mô hình. Đào tạo mô hình của bạn với tổn thất binary_crossentropy là vì vấn đề phân loại nhị phân và kích hoạt cuối cùng của bạn là một khối nhỏ. (Để xem lại thông tin về chỉ số thua cuộc, hãy xem bài viết Phân tích theo công nghệ máy học.) Sử dụng trình tối ưu hóa rmsprop với tỷ lệ học là 0,001. Trong quá trình đào tạo, hãy theo dõi độ chính xác của việc phân loại.

from tensorflow.keras.optimizers import RMSprop
 
model.compile(loss='binary_crossentropy',
              optimizer=RMSprop(lr=0.001),
              metrics=['acc'])

7. Đào tạo mô hình từ máy phát điện

Thiết lập trình tạo dữ liệu đọc ảnh trong thư mục nguồn của bạn, chuyển đổi chúng thành các mã nổi 3, và cấp chúng (có nhãn) vào mạng của bạn.

Bạn sẽ có một trình tạo cho hình ảnh đào tạo và một trình tạo cho hình ảnh xác thực. Trình tạo của bạn sẽ tạo ra các lô hình ảnh có kích thước 300x300 và các nhãn của chúng (tệp nhị phân).

Như bạn đã biết, dữ liệu chuyển vào mạng nơ-ron thường được chuẩn hóa theo cách nào đó để mạng có thể xử lý tốt hơn. (Thông thường, việc cung cấp pixel thô cho một CNN.) Trong trường hợp của bạn, bạn sẽ xử lý trước hình ảnh bằng cách chuẩn hóa các giá trị pixel nằm trong phạm vi [0, 1] (ban đầu tất cả các giá trị nằm trong phạm vi [0, 255]).

Trong Keras, bạn có thể làm việc này thông qua lớp keras.preprocessing.image.ImageDataGenerator bằng cách dùng thông số thang điểm lại. Lớp ImageDataGenerator đó cho phép bạn tạo bản sao của các lô hình ảnh tăng cường (và nhãn của các gói đó) thông qua .flow(data, labels) hoặc .flow_from_directory(directory). Sau đó, bạn có thể dùng các trình tạo đó với các phương thức mô hình Keras chấp nhận trình tạo dữ liệu làm dữ liệu đầu vào: fit_generator, evaluate_generatorpredict_generator.

from tensorflow.keras.preprocessing.image import ImageDataGenerator
 
# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
 
# Flow training images in batches of 128 using train_datagen generator
train_generator = train_datagen.flow_from_directory(
        '/tmp/horse-or-human/',  # This is the source directory for training images
        target_size=(300, 300),  # All images will be resized to 150x150
        batch_size=128,
        # Since we use binary_crossentropy loss, we need binary labels
        class_mode='binary')

8. Tham gia tập huấn

Huấn luyện trong 15 kỷ nguyên. (Có thể mất vài phút để chạy.)

history = model.fit(
      train_generator,
      steps_per_epoch=8,  
      epochs=15,
      verbose=1)

Lưu ý giá trị trên mỗi epoch.

Sự mất mátđộ chính xác là một chỉ báo tuyệt vời về tiến độ đào tạo. Họ dự đoán và phân loại dữ liệu đào tạo, sau đó đo lường dựa trên nhãn đã biết, tính toán kết quả. Độ chính xác là phần dự đoán chính xác.

Epoch 1/15
9/9 [==============================] - 9s 1s/step - loss: 0.8662 - acc: 0.5151
Epoch 2/15
9/9 [==============================] - 8s 927ms/step - loss: 0.7212 - acc: 0.5969
Epoch 3/15
9/9 [==============================] - 8s 921ms/step - loss: 0.6612 - acc: 0.6592
Epoch 4/15
9/9 [==============================] - 8s 925ms/step - loss: 0.3135 - acc: 0.8481
Epoch 5/15
9/9 [==============================] - 8s 919ms/step - loss: 0.4640 - acc: 0.8530
Epoch 6/15
9/9 [==============================] - 8s 896ms/step - loss: 0.2306 - acc: 0.9231
Epoch 7/15
9/9 [==============================] - 8s 915ms/step - loss: 0.1464 - acc: 0.9396
Epoch 8/15
9/9 [==============================] - 8s 935ms/step - loss: 0.2663 - acc: 0.8919
Epoch 9/15
9/9 [==============================] - 8s 883ms/step - loss: 0.0772 - acc: 0.9698
Epoch 10/15
9/9 [==============================] - 9s 951ms/step - loss: 0.0403 - acc: 0.9805
Epoch 11/15
9/9 [==============================] - 8s 891ms/step - loss: 0.2618 - acc: 0.9075
Epoch 12/15
9/9 [==============================] - 8s 902ms/step - loss: 0.0434 - acc: 0.9873
Epoch 13/15
9/9 [==============================] - 8s 904ms/step - loss: 0.0187 - acc: 0.9932
Epoch 14/15
9/9 [==============================] - 9s 951ms/step - loss: 0.0974 - acc: 0.9649
Epoch 15/15
9/9 [==============================] - 8s 877ms/step - loss: 0.2859 - acc: 0.9338

9. Thử nghiệm mô hình

Giờ đây, bạn có thể chạy dự đoán bằng mô hình này. Mã sẽ cho phép bạn chọn một hoặc nhiều tệp từ hệ thống tệp của mình. Sau đó, mô hình này sẽ tải lên và chạy qua mô hình, cho biết vật thể là con ngựa hay con người.

Bạn có thể tải hình ảnh từ Internet xuống hệ thống tệp để dùng thử! Lưu ý rằng bạn có thể thấy rằng mạng có nhiều sai sót mặc dù độ chính xác của việc đào tạo trên 99%.

Vấn đề này được gọi là phân phối quá mức, tức là mạng nơ-ron được đào tạo với dữ liệu rất hạn chế (chỉ có khoảng 500 hình ảnh cho mỗi lớp). Vì vậy, rất tốt trong việc nhận dạng các hình ảnh trông giống như trong tập huấn luyện, nhưng có thể thất bại rất nhiều đối với những hình ảnh không có trong tập huấn.

Đó là điểm dữ liệu chứng minh rằng bạn càng huấn luyện nhiều dữ liệu thì mạng cuối cùng của bạn sẽ càng tốt hơn!

Có nhiều kỹ thuật có thể được dùng để cải thiện quá trình đào tạo của bạn, mặc dù dữ liệu có giới hạn, bao gồm cả phương pháp cải thiện hình ảnh, nhưng cách đó không nằm trong phạm vi của lớp học lập trình này.

import numpy as np
from google.colab import files
from keras.preprocessing import image
 
uploaded = files.upload()
 
for fn in uploaded.keys():
 
  # predicting images
  path = '/content/' + fn
  img = image.load_img(path, target_size=(300, 300))
  x = image.img_to_array(img)
  x = np.expand_dims(x, axis=0)
 
  images = np.vstack([x])
  classes = model.predict(images, batch_size=10)
  print(classes[0])
  if classes[0]>0.5:
    print(fn + " is a human")
  else:
    print(fn + " is a horse")

Ví dụ: giả sử bạn muốn thử nghiệm với hình ảnh này:

9e07a57ff3be7a82.jpeg

Sau đây là những nội dung mà lớp học này tạo ra:

77b678e70b00862a.png

Mặc dù là đồ họa hoạt hình nhưng nó vẫn phân loại chính xác.

Hình ảnh sau đây cũng phân loại chính xác:

c9213173d9f3d83c.jpeg

f2844da737a1a2f2.png

Hãy thử một vài hình ảnh của riêng bạn và khám phá!

10. Hình ảnh hóa đại diện trung gian

Để nắm bắt loại tính năng mà CNN đã học được, bạn nên làm những việc thú vị bằng cách trực quan hóa cách đầu vào được biến đổi khi đi qua CNN.

Chọn một hình ảnh ngẫu nhiên từ bộ đào tạo, sau đó tạo một hình trong đó mỗi hàng là đầu ra của một lớp và mỗi hình ảnh trong hàng là một bộ lọc cụ thể trong bản đồ tính năng đầu ra đó. Chạy lại ô đó để tạo biểu diễn trung gian cho nhiều hình ảnh đào tạo.

import numpy as np
import random
from tensorflow.keras.preprocessing.image import img_to_array, load_img
 
# Let's define a new Model that will take an image as input, and will output
# intermediate representations for all layers in the previous model after
# the first.
successive_outputs = [layer.output for layer in model.layers[1:]]
#visualization_model = Model(img_input, successive_outputs)
visualization_model = tf.keras.models.Model(inputs = model.input, outputs = successive_outputs)
# Let's prepare a random input image from the training set.
horse_img_files = [os.path.join(train_horse_dir, f) for f in train_horse_names]
human_img_files = [os.path.join(train_human_dir, f) for f in train_human_names]
img_path = random.choice(horse_img_files + human_img_files)
 
img = load_img(img_path, target_size=(300, 300))  # this is a PIL image
x = img_to_array(img)  # Numpy array with shape (150, 150, 3)
x = x.reshape((1,) + x.shape)  # Numpy array with shape (1, 150, 150, 3)
 
# Rescale by 1/255
x /= 255
 
# Let's run our image through our network, thus obtaining all
# intermediate representations for this image.
successive_feature_maps = visualization_model.predict(x)
 
# These are the names of the layers, so can have them as part of our plot
layer_names = [layer.name for layer in model.layers]
 
# Now let's display our representations
for layer_name, feature_map in zip(layer_names, successive_feature_maps):
  if len(feature_map.shape) == 4:
    # Just do this for the conv / maxpool layers, not the fully-connected layers
    n_features = feature_map.shape[-1]  # number of features in feature map
    # The feature map has shape (1, size, size, n_features)
    size = feature_map.shape[1]
    # We will tile our images in this matrix
    display_grid = np.zeros((size, size * n_features))
    for i in range(n_features):
      # Postprocess the feature to make it visually palatable
      x = feature_map[0, :, :, i]
      x -= x.mean()
      if x.std()>0:
        x /= x.std()
      x *= 64
      x += 128
      x = np.clip(x, 0, 255).astype('uint8')
      # We'll tile each filter into this big horizontal grid
      display_grid[:, i * size : (i + 1) * size] = x
    # Display the grid
    scale = 20. / n_features
    plt.figure(figsize=(scale * n_features, scale))
    plt.title(layer_name)
    plt.grid(False)
    plt.imshow(display_grid, aspect='auto', cmap='viridis')

Sau đây là ví dụ về kết quả:

e078d1bc9662c93f.png

Như bạn có thể thấy, bạn chuyển từ pixel thô của hình ảnh sang hình ảnh ngày càng trừu tượng và nhỏ gọn. Phần mô tả ở phần dưới bắt đầu làm nổi bật những nội dung mà mạng chú ý và thể hiện ít và ít tính năng hơn đang được "activate." Hầu hết đều được đặt thành 0. Điều đó được gọi là sự phân biệt nhau. Sự rộng rãi đại diện là một tính năng chính của công nghệ học sâu.

Những hình ảnh này ngày càng có ít thông tin về pixel gốc của hình ảnh, nhưng ngày càng tinh chỉnh thông tin về lớp của hình ảnh. Bạn có thể coi CNN (hoặc một mạng sâu nói chung) như một đường liên kết chưng cất thông tin.

11. Xin chúc mừng

Bạn đã tìm hiểu cách sử dụng CNN để cải thiện hình ảnh phức tạp. Để tìm hiểu cách cải thiện hơn nữa các mô hình thị giác máy tính của bạn, hãy chuyển sang Sử dụng mạng nơ-ron chuyển đổi (CNN) với tập dữ liệu lớn để tránh hiển thị quá mức.