이 페이지는 Cloud Translation API를 통해 번역되었습니다.
Switch to English

이미지 분류

TensorFlow.org에서보기 Google Colab에서 실행 GitHub에서 소스보기 노트북 다운로드

이 학습서는 꽃의 이미지를 분류하는 방법을 보여줍니다. keras.Sequential 모델을 사용하여 이미지 분류기를 만들고 preprocessing.image_dataset_from_directory 사용하여 데이터를로드합니다. 다음과 같은 개념을 통해 실질적인 경험을 얻을 수 있습니다.

  • 디스크에서 데이터 세트를 효율적으로로드합니다.
  • 데이터 기능 보강 및 드롭 아웃을 포함하여 과적 합을 식별하고이를 완화시키는 기술을 적용합니다.

이 학습서는 기본 기계 학습 워크 플로우를 따릅니다.

  1. 데이터 검사 및 이해
  2. 입력 파이프 라인 구축
  3. 모델 구축
  4. 모델 훈련
  5. 모델 테스트
  6. 모델 개선 및 프로세스 반복
pip install -q tf-nightly

TensorFlow 및 기타 라이브러리 가져 오기

 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
 

데이터 세트 다운로드 및 탐색

이 튜토리얼에서는 약 3,700 장의 꽃 사진 데이터 세트를 사용합니다. 데이터 세트에는 클래스 당 하나씩 5 개의 하위 디렉토리가 있습니다.

 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 [==============================] - 4s 0us/step

다운로드 한 후에는 데이터 세트 사본을 사용할 수 있습니다. 총 3,670 개의 이미지가 있습니다 :

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

다음은 장미입니다.

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

png

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

png

그리고 일부 튤립 :

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

png

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

png

keras.preprocessing을 사용하여로드

유용한 image_dataset_from_directory 유틸리티를 사용하여 이러한 이미지를 디스크에로드 해 봅시다. 디스크의 이미지 디렉토리에서 몇 줄의 코드로 tf.data.Dataset 으로 이동합니다. 원하는 경우 이미지로드 자습서를 방문하여 데이터로드 코드를 처음부터 새로 작성할 수도 있습니다.

데이터 세트 만들기

로더에 대한 일부 매개 변수를 정의하십시오.

 batch_size = 32
img_height = 180
img_width = 180
 

모델을 개발할 때 유효성 검사 분할을 사용하는 것이 좋습니다. 교육에 이미지의 80 %를 사용하고 유효성 검사에 20 %를 사용합니다.

 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.

이러한 데이터 세트의 class_names 속성에서 클래스 이름을 찾을 수 있습니다. 이들은 알파벳 순서로 디렉토리 이름에 해당합니다.

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

데이터 시각화

교육 데이터 세트의 처음 9 개 이미지는 다음과 같습니다.

 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

이 데이터 세트를 사용하여 모델을 model.fit 에 전달하여 모델을 model.fit . 원하는 경우 데이터 세트를 수동으로 반복하고 이미지 배치를 검색 할 수도 있습니다.

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

image_batch(32, 180, 180, 3) 모양의 텐서입니다. 이것은 180x180x3 모양의 32 개 이미지 묶음입니다 (마지막 치수는 컬러 채널 RGB를 나타냄). label_batch 는 모양 (32,) 의 텐서이며 32 이미지에 해당하는 레이블입니다.

당신은 호출 할 수 있습니다 .numpy()image_batchlabels_batch A를 변환하는 텐서 numpy.ndarray .

성능을위한 데이터 세트 구성

버퍼링 된 프리 페치를 사용하여 I / O가 차단되지 않고 디스크에서 데이터를 생성 할 수 있도록하겠습니다. 데이터를로드 할 때 사용해야하는 두 가지 중요한 방법입니다.

Dataset.cache() 는 첫 번째 에포크 (epoch) 동안 디스크에서 이미지를로드 한 후에 이미지를 메모리에 유지합니다. 이렇게하면 모델을 훈련하는 동안 데이터 세트가 병목 현상이되지 않습니다. 데이터 세트가 너무 커서 메모리에 맞지 않는 경우이 방법을 사용하여 수행 가능한 온 디스크 캐시를 작성할 수도 있습니다.

Dataset.prefetch() 훈련 중에 데이터 전처리 및 모델 실행과 겹칩니다.

관심있는 독자는 두 가지 방법과 데이터 성능 안내서데이터 를 디스크에 캐시하는 방법에 대해 자세히 알아볼 수 있습니다.

 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)
 

데이터 표준화

RGB 채널 값은 [0, 255] 범위에 있습니다. 신경망에는 이상적이지 않습니다. 일반적으로 입력 값을 작게 만들어야합니다. 여기서는 Rescaling 레이어를 사용하여 [0, 1] 에있는 값을 표준화합니다.

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

이 레이어를 사용하는 방법에는 두 가지가 있습니다. map을 호출하여 데이터 세트에 적용 할 수 있습니다.

 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.0 1.0

또는 모델 정의 내에 계층을 포함시켜 배포를 단순화 할 수 있습니다. 우리는 여기서 두 번째 접근법을 사용할 것입니다.

모델 만들기

이 모델은 각각 최대 풀 레이어가있는 3 개의 회선 블록으로 구성됩니다. relu 활성화 기능에 의해 활성화되는 128 개의 장치가 완전히 연결된 레이어가 있습니다. 이 모델은 높은 정확도로 조정되지 않았습니다.이 학습서의 목표는 표준 접근 방식을 보여주는 것입니다.

 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)
])
 

모델 컴파일

이 자습서에서는 optimizers.Adam 마이저를 선택하십시오 losses.SparseCategoricalCrossentropy optimizers.Adam 마이저 및 손실 losses.SparseCategoricalCrossentropy loss 함수. 각 교육 시대에 대한 교육 및 유효성 검사 정확도를 보려면 metrics 인수를 전달하십시오.

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

모델 요약

모델의 summary 방법을 사용하여 네트워크의 모든 계층을 summary .

 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
_________________________________________________________________

모델 훈련

 epochs=10
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
 
Epoch 1/10
92/92 [==============================] - 4s 47ms/step - loss: 1.9328 - accuracy: 0.2896 - val_loss: 1.1022 - val_accuracy: 0.5245
Epoch 2/10
92/92 [==============================] - 1s 10ms/step - loss: 1.0441 - accuracy: 0.5761 - val_loss: 1.0057 - val_accuracy: 0.5913
Epoch 3/10
92/92 [==============================] - 1s 10ms/step - loss: 0.8640 - accuracy: 0.6763 - val_loss: 0.8951 - val_accuracy: 0.6499
Epoch 4/10
92/92 [==============================] - 1s 10ms/step - loss: 0.7106 - accuracy: 0.7472 - val_loss: 0.8992 - val_accuracy: 0.6621
Epoch 5/10
92/92 [==============================] - 1s 10ms/step - loss: 0.4817 - accuracy: 0.8285 - val_loss: 0.8997 - val_accuracy: 0.6662
Epoch 6/10
92/92 [==============================] - 1s 10ms/step - loss: 0.3131 - accuracy: 0.8903 - val_loss: 1.0014 - val_accuracy: 0.6567
Epoch 7/10
92/92 [==============================] - 1s 10ms/step - loss: 0.1782 - accuracy: 0.9436 - val_loss: 1.2140 - val_accuracy: 0.6431
Epoch 8/10
92/92 [==============================] - 1s 10ms/step - loss: 0.1024 - accuracy: 0.9703 - val_loss: 1.5144 - val_accuracy: 0.6240
Epoch 9/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0736 - accuracy: 0.9815 - val_loss: 1.7651 - val_accuracy: 0.5926
Epoch 10/10
92/92 [==============================] - 1s 10ms/step - loss: 0.0761 - accuracy: 0.9775 - val_loss: 2.0429 - val_accuracy: 0.5967

훈련 결과 시각화

교육 및 검증 세트에서 손실 및 정확도 도표를 작성하십시오.

 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

그림에서 알 수 있듯이 훈련 정확도와 유효성 검사 정확도는 큰 차이로 사라지고 모델은 유효성 검사 세트에서 약 60 % 정확도를 달성했습니다.

무엇이 잘못되었는지 살펴보고 모델의 전반적인 성능을 향상 시키십시오.

과적 합

위의 그림에서 훈련 정확도는 시간이 지남에 따라 선형 적으로 증가하는 반면 검증 정확도는 훈련 과정에서 약 60 % 정도 멈 춥니 다. 또한, 교육 및 검증의 정확성과 정밀도의 차이의 눈에 띄는-표시이다 overfitting .

적은 수의 학습 예제가있는 경우 모델이 새 예제에서 모델의 성능에 부정적인 영향을주는 정도로 학습 예제의 노이즈 또는 원치 않는 세부 사항을 통해 학습합니다. 이 현상을 과적 합이라고합니다. 이는 모델이 새로운 데이터 세트를 일반화하는 데 어려움을 겪을 것임을 의미합니다.

훈련 과정에서 과적 합과 싸울 수있는 여러 가지 방법이 있습니다. 이 학습서에서는 데이터 기능 보강을 사용하고 모델에 Dropout 을 추가합니다.

데이터 확대

과적 합은 일반적으로 적은 수의 훈련 예제가있을 때 발생합니다. 데이터 확대 는 기존 예제에서 추가 훈련 데이터를 생성 한 다음 무작위 변환을 사용하여 믿을 수있는 이미지를 생성하는 접근 방식을 취합니다. 이는 모델을 데이터의 더 많은 측면에 노출시키고 일반화하는 데 도움이됩니다.

우리는 실험적인 Keras 전처리 레이어를 사용하여 데이터 확대를 구현할 것입니다. 이들은 다른 레이어처럼 모델 내부에 포함되어 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),
  ]
)
 

동일한 이미지에 여러 번 데이터 보강을 적용하여 기능 보강 된 몇 가지 예가 어떤 모양인지 시각화 해 보겠습니다.

 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

데이터 보강을 사용하여 모델을 곧 학습 할 것입니다.

탈락

과적 합을 줄이는 또 다른 기술은 정규화의 한 형태 인 네트워크에 Dropout 을 도입하는 것입니다.

드롭 아웃을 레이어에 적용하면 훈련 과정에서 레이어에서 여러 출력 단위가 임의로 활성화됩니다 (활성화를 0으로 설정). 드롭 아웃은 0.1, 0.2, 0.4 등의 형태로 입력 값으로 소수를 취합니다. 이는 적용된 레이어에서 출력 단위의 10 %, 20 % 또는 40 %를 임의로 제거하는 것을 의미합니다.

layers.Dropout 사용하여 새로운 신경망을 layers.Dropout 다음 증강 이미지를 사용하여 훈련시켜 봅시다.

 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)
])
 

모델 컴파일 및 훈련

 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 [==============================] - 2s 20ms/step - loss: 1.4842 - accuracy: 0.3279 - val_loss: 1.0863 - val_accuracy: 0.5640
Epoch 2/15
92/92 [==============================] - 1s 12ms/step - loss: 1.1215 - accuracy: 0.5284 - val_loss: 1.0374 - val_accuracy: 0.6022
Epoch 3/15
92/92 [==============================] - 1s 12ms/step - loss: 0.9680 - accuracy: 0.6117 - val_loss: 0.9200 - val_accuracy: 0.6485
Epoch 4/15
92/92 [==============================] - 1s 12ms/step - loss: 0.8538 - accuracy: 0.6753 - val_loss: 0.9206 - val_accuracy: 0.6417
Epoch 5/15
92/92 [==============================] - 1s 12ms/step - loss: 0.7744 - accuracy: 0.6977 - val_loss: 0.8169 - val_accuracy: 0.6839
Epoch 6/15
92/92 [==============================] - 1s 13ms/step - loss: 0.7758 - accuracy: 0.7093 - val_loss: 0.7743 - val_accuracy: 0.6880
Epoch 7/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6805 - accuracy: 0.7481 - val_loss: 0.8598 - val_accuracy: 0.6540
Epoch 8/15
92/92 [==============================] - 1s 13ms/step - loss: 0.7132 - accuracy: 0.7278 - val_loss: 0.7177 - val_accuracy: 0.7207
Epoch 9/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6634 - accuracy: 0.7580 - val_loss: 0.7152 - val_accuracy: 0.7166
Epoch 10/15
92/92 [==============================] - 1s 13ms/step - loss: 0.6562 - accuracy: 0.7538 - val_loss: 0.7251 - val_accuracy: 0.7248
Epoch 11/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5798 - accuracy: 0.7840 - val_loss: 0.7016 - val_accuracy: 0.7357
Epoch 12/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5635 - accuracy: 0.7913 - val_loss: 0.7755 - val_accuracy: 0.7248
Epoch 13/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5361 - accuracy: 0.7982 - val_loss: 0.7575 - val_accuracy: 0.7153
Epoch 14/15
92/92 [==============================] - 1s 13ms/step - loss: 0.5420 - accuracy: 0.8022 - val_loss: 0.7375 - val_accuracy: 0.7302
Epoch 15/15
92/92 [==============================] - 1s 12ms/step - loss: 0.5132 - accuracy: 0.8120 - val_loss: 0.7561 - val_accuracy: 0.7289

훈련 결과 시각화

데이터 기능 보강 및 드롭 아웃을 적용한 후 이전보다 과적 합이 적으며 교육 및 유효성 검사 정확도가 밀접하게 정렬됩니다.

 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

새로운 데이터 예측

마지막으로, 모델을 사용하여 교육 또는 검증 세트에 포함되지 않은 이미지를 분류 해 봅시다.

 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 89.39 percent confidence.