Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Keras 함수형 API를 활용한 파이썬 잔차 연결(Residual Connection) 구현 방법

Keras는 TensorFlow 패키지에 포함되어 있으며, 아래 코드 한 줄로 간단히 불러와 사용할 수 있습니다.

import tensorflow
from tensorflow import keras

Keras의 함수형 API(Functional API)는 순차형(Sequential) API로 만든 모델보다 훨씬 유연한 모델을 구축할 수 있게 해줍니다. 함수형 API는 비선형 위상(non-linear topology)을 가진 모델을 다룰 수 있고, 레이어 공유, 그리고 여러 개의 입력과 출력을 처리하는 것도 가능합니다. 일반적으로 딥러닝 모델은 여러 레이어로 구성된 방향성 비순환 그래프(DAG, Directed Acyclic Graph) 형태이며, 함수형 API를 사용하면 이러한 레이어 그래프를 손쉽게 만들 수 있습니다.

아래 코드는 Google Colaboratory 환경에서 실행했습니다. Google Colab은 브라우저에서 별도 설정 없이 파이썬 코드를 실행할 수 있게 해주며, GPU도 무료로 사용할 수 있습니다. Colaboratory는 Jupyter Notebook을 기반으로 만들어졌습니다.

예제 코드

다음은 CIFAR10 데이터셋용 장난감(toy) ResNet 모델을 함수형 API로 구현한 예제입니다. 핵심은 layers.add()를 활용해 이전 블록의 출력을 현재 블록의 출력에 더하는 잔차 연결(residual connection) 부분입니다.

print("CIFAR10용 Toy ResNet 모델")
print("모델에 사용될 레이어 생성")
inputs = keras.Input(shape=(32, 32, 3), name="img")
x = layers.Conv2D(32, 3, activation="relu")(inputs)
x = layers.Conv2D(64, 3, activation="relu")(x)
block_1_output = layers.MaxPooling2D(3)(x)

x = layers.Conv2D(64, 3, activation="relu", padding="same")(block_1_output)
x = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
block_2_output = layers.add([x, block_1_output])

x = layers.Conv2D(64, 3, activation="relu", padding="same")(block_2_output)
x = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
block_3_output = layers.add([x, block_2_output])

x = layers.Conv2D(64, 3, activation="relu")(block_3_output)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(256, activation="relu")(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(10)(x)

model = keras.Model(inputs, outputs, name="toy_resnet")
print("모델 상세 정보")
model.summary()

코드 출처 − https://www.tensorflow.org/guide/keras/functional

실행 결과

Model: "toy_resnet"
________________________________________________________________________________
Layer (type)                Output Shape              Param #     Connected to
================================================================================
img (InputLayer)            [(None, 32, 32, 3)]       0
________________________________________________________________________________
conv2d_32 (Conv2D)          (None, 30, 30, 32)        896         img[0][0]
________________________________________________________________________________
conv2d_33 (Conv2D)          (None, 28, 28, 64)        18496       conv2d_32[0][0]
________________________________________________________________________________
max_pooling2d_8             (None, 9, 9, 64)          0           conv2d_33[0][0]
________________________________________________________________________________
conv2d_34 (Conv2D)          (None, 9, 9, 64)          36928       max_pooling2d_8[0][0]
________________________________________________________________________________
conv2d_35 (Conv2D)          (None, 9, 9, 64)          36928       conv2d_34[0][0]
________________________________________________________________________________
add_12 (Add)                (None, 9, 9, 64)          0           conv2d_35[0][0], max_pooling2d_8[0][0]
________________________________________________________________________________
conv2d_36 (Conv2D)          (None, 9, 9, 64)          36928       add_12[0][0]
________________________________________________________________________________
conv2d_37 (Conv2D)          (None, 9, 9, 64)          36928       conv2d_36[0][0]
________________________________________________________________________________
add_13 (Add)                (None, 9, 9, 64)          0           conv2d_37[0][0], add_12[0][0]
________________________________________________________________________________
conv2d_38 (Conv2D)          (None, 7, 7, 64)          36928       add_13[0][0]
________________________________________________________________________________
global_average_pooling2d    (None, 64)                0           conv2d_38[0][0]
________________________________________________________________________________
dense_40 (Dense)            (None, 256)               16640       global_average_pooling2d[0][0]
________________________________________________________________________________
dropout_2 (Dropout)         (None, 256)               0           dense_40[0][0]
________________________________________________________________________________
dense_41 (Dense)            (None, 10)                2570        dropout_2[0][0]
================================================================================
Total params: 223,242
Trainable params: 223,242
Non-trainable params: 0

코드 설명

  • 이 모델은 하나의 입력과 하나의 출력을 가지지만, 내부 구조가 단순히 순차적으로 연결되지 않습니다.

  • 함수형 API는 이처럼 비선형 연결 위상을 가진 모델을 쉽게 다룰 수 있게 해줍니다.

  • 레이어들이 순차적으로만 연결된 것이 아니기 때문에 순차형(Sequential) API로는 이 모델을 구현할 수 없습니다.

  • 바로 이 지점에서 잔차 연결(residual connection)이 중요한 역할을 합니다. layers.add()를 통해 이전 블록의 출력을 현재 블록의 출력에 더함으로써, 깊은 네트워크에서도 기울기 소실 문제를 완화하고 학습을 안정화할 수 있습니다.

  • 위 예제에서는 CIFAR10 데이터셋을 대상으로 하는 샘플 ResNet 모델을 만들어 이를 시연했습니다.