최신 컨브넷 아키텍처 패턴
from tensorflow import keras
from tensorflow.keras import layers
inputs = keras.Input(shape=(32, 32, 3))
x = layers.Conv2D(32, 3, activation="relu")(inputs)
residual = x
x = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
residual = layers.Conv2D(64, 1)(residual)
x = layers.add([x, residual])
-> shape=(32, 32, 3)는 32x32 크기의 RGB 이미지를 의미합니다. 입력은 keras.Input 함수를 사용하여 정의되며, 모델의 첫 번째 레이어로 사용
-> 3x3 크기의 필터를 사용하여 32개의 출력 채널을 가지는 컨볼루션 레이어를 정의합니다. 입력은 inputs이며, 활성화 함수로 ReLU를 사용 , 특징추출
-> 이는 나중에 스킵 연결(skip connection)을 수행하기 위해 사용
-> residual을 입력으로 받아 1x1 크기의 필터를 사용하여 64개의 출력 채널을 가지는 컨볼루션 레이어를 정의
-> 이전 레이어의 출력이 현재 레이어의 출력에 직접적으로 추가
inputs = keras.Input(shape=(32, 32, 3))
x = layers.Rescaling(1./255)(inputs)
def residual_block(x, filters, pooling=False):
residual = x
x = layers.Conv2D(filters, 3, activation="relu", padding="same")(x)
x = layers.Conv2D(filters, 3, activation="relu", padding="same")(x)
if pooling:
x = layers.MaxPooling2D(2, padding="same")(x)
residual = layers.Conv2D(filters, 1, strides=2)(residual)
elif filters != residual.shape[-1]:
residual = layers.Conv2D(filters, 1)(residual)
x = layers.add([x, residual])
return x
x = residual_block(x, filters=32, pooling=True)
x = residual_block(x, filters=64, pooling=True)
x = residual_block(x, filters=128, pooling=False)
x = layers.GlobalAveragePooling2D()(x)
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
model.summary()
-> pooling=True 인 경우, 2x2 크기의 최대 풀링(Max Pooling)을 적용하고 스킵 연결을 위해 이전 레이어의 출력 residual에 1x1 크기의 필터를 사용하여 다운샘플링을 수행
-> pooling=False 이면서 filters와 residual.shape[-1]이 다른 경우, 1x1 크기의 필터를 사용하여 스킵 연결을 수행
깊이별 분리 합성곱
from google.colab import files
files.upload()
import os, shutil, pathlib
from tensorflow.keras.utils import image_dataset_from_directory
original_dir = pathlib.Path("train")
new_base_dir = pathlib.Path("cats_vs_dogs_small")
def make_subset(subset_name, start_index, end_index):
for category in ("cat", "dog"):
dir = new_base_dir / subset_name / category
os.makedirs(dir)
fnames = [f"{category}.{i}.jpg" for i in range(start_index, end_index)]
for fname in fnames:
shutil.copyfile(src=original_dir / fname,
dst=dir / fname)
make_subset("train", start_index=0, end_index=1000)
make_subset("validation", start_index=1000, end_index=1500)
make_subset("test", start_index=1500, end_index=2500)
train_dataset = image_dataset_from_directory(
new_base_dir / "train",
image_size=(180, 180),
batch_size=32)
validation_dataset = image_dataset_from_directory(
new_base_dir / "validation",
image_size=(180, 180),
batch_size=32)
test_dataset = image_dataset_from_directory(
new_base_dir / "test",
image_size=(180, 180),
batch_size=32)
files 모듈의 upload() 함수를 사용하여 파일을 업로드합니다. 이 함수는 사용자에게 파일 선택 대화 상자를 표시하고, 선택한 파일을 현재 작업 공간으로 업로드합니다.
image_dataset_from_directory 함수를 사용하여 이미지 데이터셋을 생성하는 작업을 수행
-디렉토리에서 이미지를 자동으로 로드하여 데이터셋을 생성합니다.
-이미지 파일의 경로를 사용하여 클래스 레이블을 자동으로 할당합니다.
-이미지를 로드하고 전처리하기 위해 TensorFlow의 데이터셋 파이프라인을 활용합니다.
-데이터셋을 배치로 나누어 모델 학습에 사용할 수 있도록 합니다.
make_subset 함수는 지정된 범위에 해당하는 이미지 파일을 원하는 하위 데이터셋 디렉토리로 복사하는 역할을 합니다. "train", "validation", "test" 세 가지 하위 데이터셋을 생성합니다.
data_augmentation = keras.Sequential(
[
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1),
layers.RandomZoom(0.2),
]
)
layers.RandomFlip("horizontal")
-> 수평 방향으로 랜덤하게 이미지를 뒤집는 레이어입니다. 이를 통해 이미지의 좌우 반전을 수행하여 데이터 다양성을 높일 수 있습니다.
layers.RandomRotation(0.1)
-> 랜덤하게 이미지를 회전시키는 레이어입니다. 이를 통해 이미지를 약간 회전시켜 데이터의 다양성을 추가합니다. 여기서 인자로 주어진 0.1은 회전 각도의 범위를 지정
layers.RandomZoom(0.2)
-> 랜덤하게 이미지를 확대 또는 축소하는 레이어입니다
inputs = keras.Input(shape=(180, 180, 3))
x = data_augmentation(inputs)
x = layers.Rescaling(1./255)(x)
x = layers.Conv2D(filters=32, kernel_size=5, use_bias=False)(x)
for size in [32, 64, 128, 256, 512]:
residual = x
x = layers.BatchNormalization()(x)
x = layers.Activation("relu")(x)
x = layers.SeparableConv2D(size, 3, padding="same", use_bias=False)(x)
x = layers.BatchNormalization()(x)
x = layers.Activation("relu")(x)
x = layers.SeparableConv2D(size, 3, padding="same", use_bias=False)(x)
x = layers.MaxPooling2D(3, strides=2, padding="same")(x)
residual = layers.Conv2D(
size, 1, strides=2, padding="same", use_bias=False)(residual)
x = layers.add([x, residual])
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
x = layers.Rescaling(1./255)(x)
-> 데이터 값을 0-1 범위로 정규화하기 위해 Rescaling 레이어를 사용
x = layers.Conv2D(filters=32, kernel_size=5, use_bias=False)(x)
-> use_bias가 false로 설정되어 있으므로 편향은 사용되지 않는다.
x = layers.BatchNormalization()(x)
-> 배치정규화를 수행한다. 모델의 안정성과 학습 속도를 향상시키는데 도움이 된다.
x = layers.SeparableConv2D(size, 3, padding="same", use_bias=False)(x)
-> 깊이별 분리 컨볼루션을 적용한다.
x = layers.Activation("relu")(x)
-> relu활성화 함수를 적용한다.
x = layers.MaxPooling2D(3, strides=2, padding="same")(x)
-> 최대 풀링 레이어를 추가하여 공간 차원을 줄입니다.
x = layers.GlobalAveragePooling2D()(x)
-> 전역 평균 풀링 레이어를 사용하여 공간 차원을 축소
->이는 입력 이미지의 공간적인 위치에 상관없이 각 채널에 대한 평균값을 계산하여 1차원 벡터로 변환
x = layers.Dropout(0.5)(x)
->드롭아웃 레이어를 사용하여 과적합을 방지하기 위해 일부 뉴런을 무작위로 비활성화합니다. 여기서는 0.5의 드롭아웃 비율을 사용
outputs = layers.Dense(1, activation="sigmoid")(x)
-> 마지막으로, 전결합층(Dense)을 사용하여 이진 분류를 수행합니다. 이진 분류를 위해 출력 뉴런은 1개이며, 시그모이드 활성화 함수를 사용 , dense사용
model.compile(loss="binary_crossentropy",
optimizer="rmsprop",
metrics=["accuracy"])
history = model.fit(
train_dataset,
epochs=100,
validation_data=validation_dataset)
model.compile(loss="binary_crossentropy", optimizer="rmsprop", metrics=["accuracy"])
-> 모델을 컴파일합니다. 이때 손실 함수로 이진 분류를 위한 이진 크로스 엔트로피(binary_crossentropy)를 사용하고, 옵티마이저로 RMSprop을 선택합니다. 평가 지표로는 정확도(accuracy)를 사용
history = model.fit(train_dataset, epochs=100, validation_data=validation_dataset)
-> train_dataset은 학습 데이터셋이며, epochs는 에포크 수를 나타냅니다. validation_data는 검증 데이터셋으로, 학습 중에 모델의 성능을 평가하는 데 사용
여기서 , fit할때 train, label따로 넣어주지 않는 것이 특징