import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
import os
import PIL
import pathlib
data_dir = tf.keras.utils.get_file( 'flower_photos' , origin=dataset_url, untar= True ) #origin=dataset_url: 상용할 url, untar=True:압축을 풀어라
data_dir = pathlib.Path(data_dir)
roses = list (data_dir.glob( 'roses/*' ))
PIL.Image. open ( str (roses[ 2 ]))
tulips = list (data_dir.glob( 'tulips/*' ))
PIL.Image. open ( str (tulips[ 2 ]))
batch_size = 32
img_height = 180
img_width = 180
num_classes = 5
epochs = 15
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
)
이 코드는 학습 데이터셋(training dataset)을 만드는 코드입니다.
tf.keras.preprocessing.image_dataset_from_directory() 함수는 이미지 데이터 디렉토리에서 이미지 데이터를 불러와 텐서플로우의 이미지 데이터셋 객체로 만듭니다.
data_dir 매개변수는 이미지 데이터 디렉토리의 경로입니다.
validation_split 매개변수는 검증 데이터셋의 비율을 의미합니다. 예를 들어, validation_split이 0.2일 경우, 전체 이미지 데이터셋 중 20%의 데이터가 검증 데이터셋으로 사용됩니다.
subset 매개변수는 생성할 데이터셋의 종류를 의미합니다. 예를 들어, subset이 'training'일 경우, 학습 데이터셋만 생성됩니다.
seed 매개변수는 랜덤한 순서를 정하는 난수 초기값입니다.
image_size 매개변수는 이미지의 높이와 너비를 의미합니다. 예를 들어, image_size가 (180, 180)일 경우, 이미지의 높이와 너비가 모두 180픽셀로 만듭니다.
batch_size는 위에서 설정해둔 batch_size로 설정합니다.
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
)
이 코드는 검증 데이터셋을 만드는 코드입니다.
위의 학습 데이터 셋 만드는 과정과 동일합니다.
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' )
이 코드는 훈련 데이터 세트에서 9개의 이미지를 추출하여 시각화하는 코드입니다.
plt.figure 명령은 그래프 크기를 10x10으로 설정합니다.
반복문 for images, labels in train_ds.take(1)은 훈련 데이터 세트에서 한 번에 1 배치 (32개)의 이미지와 레이블을 추출합니다.
내부 반복문 for i in range(9)는 9개의 이미지를 순회합니다.
plt.subplot 명령은 3x3 격자 구조에서 i+1번째 위치에 그래프를 그리기 위해 사용됩니다.
plt.imshow 명령은 이미지를 시각화합니다. 이미지의 타입을 'uint8'으로 변환하여 표시합니다.
plt.title 명령은 각 이미지에 대한 클래스 이름을 타이틀로 설정합니다.
plt.axis('off') 명령은 그래프의 축을 숨깁니다.
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)
이 코드는 TensorFlow의 tf.data API를 사용하여 데이터셋(train_ds와 val_ds)을 전처리하는 과정을 나타냅니다.
train_ds = train_ds.cache(): 이 명령은 훈련 데이터셋을 캐시하는 것을 나타냅니다. 캐시는 한 번 로드된 데이터를 메모리에 저장하여 다음에 필요할 때마다 빠르게 접근할 수 있도록 합니다.
train_ds = train_ds.shuffle(1000): 이 명령은 훈련 데이터셋을 1000개씩 섞는 것을 나타냅니다. 이는 각 epoch의 훈련 과정에서 다른 데이터를 보여주기 위해 사용됩니다.
train_ds = train_ds.prefetch(buffer_size=AUTOTUNE): 이 명령은 훈련 데이터셋의 미리 적재를 위해 TensorFlow가 사용할 수 있는 버퍼 크기를 지정하는 것을 나타냅니다.
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE): 이 명령은 검증 데이터셋을 캐시하고 미리 적재를 위해 TensorFlow가 사용할 수 있는 버퍼 크기를 지정하는 것을 나타냅니다.
normalization_layer = keras.layers.experimental.preprocessing.Rescaling( 1 ./ 255 )
이 코드는 TensorFlow의 Keras API를 사용하여 이미지 정규화 층을 생성하는 것입니다. Rescaling 레이어는 입력 이미지의 각 픽셀 값을 255로 나누어 정규화합니다. 이렇게 정규화된 이미지는 모델에서 더 쉽게 학습할 수 있도록 만듭니다.
normalized_ds = train_ds. map ( lambda x, y: (normalization_layer(x), y))
이 코드는 train_ds 데이터셋에서 각 이미지와 레이블을 가져와서, normalization_layer을 통해 정규화 적용한 후, 정규화된 이미지와 레이블을 쌍으로 가지는 새로운 데이터셋인 normalized_ds를 생성하는 것입니다. map 메소드는 train_ds 데이터셋의 각 원소에 대해 제공된 함수(여기서는 lambda 함수)를 적용하여 새로운 데이터셋을 생성합니다.
image_batch, labels_batch = next ( iter (normalized_ds))
이 코드는 normalized_ds에서 하나의 배치(batch)의 이미지와 레이블(labels)를 가져오는 코드입니다.
next(iter(normalized_ds))는 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))
model = keras.Sequential([
keras.layers.experimental.preprocessing.Rescaling( 1 ./ 255 , input_shape=(img_height, img_width, 3 )),
keras.layers.Conv2D( 16 , 3 , padding= 'same' , activation= 'relu' ),
keras.layers.MaxPooling2D(),
keras.layers.Conv2D( 32 , 3 , padding= 'same' , activation= 'relu' ),
keras.layers.MaxPooling2D(),
keras.layers.Conv2D( 64 , 3 , padding= 'same' , activation= 'relu' ),
keras.layers.MaxPooling2D(),
keras.layers.Flatten(),
keras.layers.Dense( 128 , activation= 'relu' ),
keras.layers.Dense(num_classes)
])
이 코드는 Keras의 Sequential 모델을 생성하는 코드입니다. Sequential 모델은 여러 개의 층(layer)을 순차적으로 쌓아서 구성하는 모델입니다.
첫 번째 층으로는 Rescaling layer가 사용되어 있으며, 이미지의 각 픽셀 값을 255로 나누어 0과 1 사이의 값으로 변환합니다. 그 다음, 3x3 크기의 컨볼루션 층(Conv2D)이 여러 개 쌓여있으며, 이미지의 특징을 추출합니다. 컨볼루션 층 뒤에는 Max pooling 층이 여러 개 쌓여있어 이미지의 차원을 줄여줍니다.
그 다음에 Flatten 층으로 이미지를 1차원 텐서로 변환한 뒤, 완전 연결 층(Dense)이 2개 쌓여있습니다. 최종적으로는 num_classes 개의 노드를 가진 Dense 층이 있어 분류할 클래스 개수만큼의 출력을 생성합니다.
Conv2D는 2차원 합성곱(convolution) 층입니다. 이 층은 이미지에서 여러 개의 필터를 적용하여 공간적 특징을 추출하는데 사용됩니다. 합성곱 연산을 수행하는 동안, 각 필터는 입력 이미지의 작은 영역에서 슬라이딩(sliding)하여 특징을 검출합니다. 검출된 특징은 각 필터의 출력 값으로 변환되어 매핑됩니다.
이 코드에서 필터/커널의 수를 증가시키는 이유는 이미지의 특징을 더욱 잘 감지할 수 있도록 하기 위해서입니다.
각 Conv2D 층에서 증가하는 필터/커널의 수는 고차원의 특징을 감지할 수 있는 능력을 증가시킵니다. 첫 번째 Conv2D 층에서는 16개의 필터/커널을 가지고 있으며, 두 번째 Conv2D 층에서는 32개의 필터/커널을 가지고 있습니다. 이 특징은 신경망의 깊이가 깊어질수록 더욱 복잡하고 정교한 특징을 감지할 수 있도록 합니다.
MaxPooling2D 층은 이미지의 크기를 줄이면서 고차원의 특징을 유지하는데 도움을 줍니다. 이 층을 통과하면서 이미지의 크기가 절반으로 줄어들지만 고차원의 특징은 유지되므로 신경망의 성능을 개선할 수 있습니다.
model. compile (optimizer= 'adam' ,
loss=keras.losses.SparseCategoricalCrossentropy(from_logits= True ),
metrics=[ 'accuracy' ])
history = model.fit(train_ds,
validation_data=val_ds,
epochs=epochs)
def plot_graphs ( history , metric ):
plt.plot(history.history[metric])
plt.plot(history.history[ 'val_' +metric], '' )
plt.xlabel( "Epochs" )
plt.ylabel(metric)
plt.legend([metric, 'val_' +metric])
plt.show()
plot_graphs(history, 'accuracy' )
plot_graphs(history, 'loss' )
정확도와 손실률을 시각화 해 본 결과 오버피팅이 일어났습니다.
이 오버피팅 현상을 가능한 줄여보도록 하겠습니다.
data_augmentation = keras.Sequential(
[
keras.layers.experimental.preprocessing.RandomFlip( "horizontal" ,
input_shape=(img_height,
img_width,
3 )),
keras.layers.experimental.preprocessing.RandomRotation( 0.1 ),
keras.layers.experimental.preprocessing.RandomZoom( 0.1 ),
]
)
이 코드는 Keras 라이브러리를 사용하여 데이터 증강(data augmentation) 신경망을 생성하는 코드입니다.
"Sequential" 객체를 생성하여 순차적으로 적용할 레이어를 나열합니다.
첫 번째 레이어는 "RandomFlip"이며, 입력 이미지를 수평으로 랜덤하게 뒤집는 기능을 가집니다. "input_shape" 파라미터는 이미지의 높이와 너비, 채널 수(3: RGB)를 지정합니다.
두 번째 레이어는 "RandomRotation"으로, 이미지를 랜덤하게 회전시킵니다. "0.1" 파라미터는 회전 각도의 범위를 지정합니다.
세 번째 레이어는 "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" )
이렇게 같은 사진을 뒤집고 확대함으로서 학습 데이터의 증가를 시켰습니다.
model = keras.Sequential([
data_augmentation,
keras.layers.experimental.preprocessing.Rescaling( 1 ./ 255 , input_shape=(img_height, img_width, 3 )),
keras.layers.Conv2D( 16 , 3 , padding= 'same' , activation= 'relu' ),
keras.layers.MaxPooling2D(),
keras.layers.Conv2D( 32 , 3 , padding= 'same' , activation= 'relu' ),
keras.layers.MaxPooling2D(),
keras.layers.Conv2D( 64 , 3 , padding= 'same' , activation= 'relu' ),
keras.layers.MaxPooling2D(),
#새로 추가된 정규화 방법인 드롭아웃 추가
keras.layers.Dropout( 0.2 ), #20퍼센트의 신경망을 제거
keras.layers.Flatten(),
keras.layers.Dense( 128 , activation= 'relu' ),
keras.layers.Dense(num_classes)
])
이제 위의 신경망을 다시 만들어보겠습니다.
또한 드롭아웃 기법을 사용하여 오버피팅을 줄여보도록 하겠습니다.
model. compile (optimizer= 'adam' ,
loss=keras.losses.SparseCategoricalCrossentropy(from_logits= True ),
metrics=[ 'accuracy' ])
history = model.fit(train_ds,
validation_data=val_ds,
epochs=epochs)
plot_graphs(history, 'accuracy' )
plot_graphs(history, 'loss' )
드롭아웃 기법과 학습 데이터의 량을 증가시킨 결과 처음의 결과보다 오버피팅 현상이 현격히 줄어드는 것을 확인했습니다.