Keras에서 모델을 정의하는 방법은 크게 두 가지가 있습니다.
둘의 차이를 비교/정리하고, 의문이 생겼던 점들을 정리해둡니다.
([혼자 공부하는 머신러닝+딥러닝] 복습 중에 헷갈려서 따로 찾아본 내용을 정리해둡니다.)
1. Sequential API (순차적 모델)
1) 특징
- Layer들을 순차적으로 쌓는 방식 (=일직선 구조)
- 단순한 모델 (입력 → 은닉층 → 출력) 설계에 적합
2) 예시 코드
- Sequential API를 사용하여 간단한 CNN을 구현하는 코드 예시
from tensorflow import keras
model = keras.Sequential([ # Sequential 객체 선언해두고
keras.layers.Conv2D(32, kernel_size=3, activation='relu', padding='same', input_shape=(28, 28, 1)),
keras.layers.MaxPooling2D(2),
keras.layers.Conv2D(64, kernel_size=3, activation='relu', padding='same'),
keras.layers.MaxPooling2D(2),
keras.layers.Flatten(),
keras.layers.Dense(100, activation='relu'),
keras.layers.Dropout(0.4),
keras.layers.Dense(10, activation='softmax')
]) # 그 안에 순차적으로 쌓으면 됨
model.summary() # 모델 층 구조 확인
3) 장단점
🙂장점
- 코드가 직관적이고 간단함 = 빠르게 기본적인 신경망을 만들기 좋음
- `.add()` 함수를 사용해서 통해 쉽게 layer들을 추가할 수 있음
☹️단점
- 복잡한 구조는 구현할 수 없음
- ex) 입력/출력 여러 개인 경우, Skip Connection, 병렬 네트워크 등
2. Functional API (함수형 모델)
1) 특징
- Layer 객체를 함수처럼 사용하여 입력/출력을 연결하는 방식
- 복잡한 네트워크 (ResNet, U-Net, GAN, 멀티 입력/출력 모델) 설계에 적합함
- 유연성이 높아서 실전에서는 Functional API가 더 자주 쓰임!
2) 예시 코드
- 마찬가지로 간단한 CNN을 구현하는 코드 예시
from tensorflow import keras
from tensorflow.keras import layers
# 입력층 정의
inputs = keras.Input(shape=(28, 28, 1))
# 함수처럼 호출해서 연쇄적으로 layer들을 연결
x = layers.Conv2D(32, kernel_size=3, activation='relu', padding='same')(inputs)
x = layers.MaxPooling2D(2)(x)
x = layers.Conv2D(64, kernel_size=3, activation='relu', padding='same')(x)
x = layers.MaxPooling2D(2)(x)
x = layers.Flatten()(x)
x = layers.Dense(100, activation='relu')(x)
x = layers.Dropout(0.4)(x)
outputs = layers.Dense(10, activation='softmax')(x)
# 최종 모델 정의 (여기엔 입력 & 최종 출력만 넣어주면 됨)
model = keras.Model(inputs=inputs, outputs=outputs)
model.summary() # 모델 구조 확인
3) 장단점
🙂장점
- 멀티 입력/출력을 처리할 수 있음
- Layer를 재사용할 수 있음 (=공유 layer 활용 가능)
- 모델의 입력과 출력을 명확히 정의하여 가독성 높음
☹️단점
- Sequential API보다 코드가 복잡함
- 단순한 모델을 만들 때는 코드가 불필요하게 길어질 수 있음
3. 두 방식의 차이 요약
Sequential API | Functional API | |
구조 | 단순한 레이어 쌓기 (순차적) | 함수로 호출해서 입출력 조정 & 연결 |
사용 용도 | 기본적인 MLP, CNN 등 | ResNet, GAN, 멀티 입력/출력 모델 등 |
멀티 입력/출력 | ❌ 불가능 | ✅ 가능 |
Skip Connection | ❌ 불가능 | ✅ 가능 |
Layer 재사용 | ❌ 불가능 | ✅ 가능 |
코드 가독성 | ✅ 직관적이고 쉬움 | ⚠️ 다소 복잡하지만 유연성 높음 |
4. 멀티 입력/출력 (25.02.04 추가)
Functional API의 가장 큰 장점이 멀티 입출력을 활용해서 유연하게 네트워크를 짤 수 있다는 것 같은데,
그럼 대체 멀티 입출력은 어떤 식으로 하는 걸까?
기본 개념
- 하나의 신경망에서 여러 개의 입력 또는 출력을 동시에 처리하는 모델
- 1) 멀티 입력
- 여러 개의 입력 ⇒ 1개의 출력
- ex1. 고객의 프로필 정보(나이, 성별) & 구매 기록(상품, 가격 등)을 함께 입력하여 구매 확률을 예측
- ex2. 이미지 데이터 & 텍스트(캡션) 데이터를 함께 입력하여 클래스를 분류
- 2) 멀티 출력
- 1개의 입력 ⇒ 여러 개의 출력
- ex1. 의료 분야에서 한 장의 X-ray 이미지로 폐 질환 여부 & 심장 질환 여부를 동시에 예측/분류
- ex2. 얼굴 이미지로 나이 & 성별을 동시에 예측
- 3) 멀티 입력+출력
- 여러 개의 입력 ⇒ 여러 개의 출력 (이것도 가능!)
- ex. 사용자 정보 & 행동 데이터를 입력받아 구매 확률 & 광고 선호도를 동시에 예측
멀티 입력 모델 예시
- `input_1` = 사용자의 나이, 성별 등 (정형 데이터, shape=(10,))
- `input_2` = 최근 쇼핑 행동 (정형 데이터, shape=(5,))
- `output` = 사용자의 상품 구매 확률 (확률값 0~1)
from tensorflow.keras.layers import Input, Dense, Concatenate
from tensorflow.keras.models import Model
# 입력1: 사용자 정보 (나이, 성별 등 10개 특징)
input_1 = keras.Input(shape=(10,), name="user_info")
x1 = layers.Dense(32, activation="relu")(input_1)
# 입력2: 사용자 행동 데이터 (구매 시간 등 5개 특징)
input_2 = keras.Input(shape=(5,), name="user_behavior")
x2 = layers.Dense(16, activation="relu")(input_2)
#⭐두 개의 입력을 결합 (Concatenate)
merged = Concatenate()([x1, x2])
# = layers.concatenate([x1, x2]) 이렇게도 가능하지만 위 코드가 더 유연함
x = layers.Dense(64, activation="relu")(merged) # 결합한 걸 입력으로 사용!
x = layers.Dense(32, activation="relu")(x)
output = layers.Dense(1, activation="sigmoid", name="purchase_prob")(x) # 출력 = 구매 확률 (0~1)
#⭐모델 정의 (입력 2개 → 출력 1개)
model = keras.Model(
inputs=[input_1, input_2],
outputs=output
)
# 모델 사용 예시
model.fit(
[train1, train2], # 두 개의 입력 데이터
target, # 정답 데이터
epochs=10
)
멀티 출력 모델 예시
- `input` = 얼굴 이미지 (28x28 크기, 흑백)
- `output_1` = 나이 예측 (회귀 문제, 한 개의 숫자값 출력)
- `output_2` = 성별 예측 (분류 문제, 0/1 클래스 출력)
from tensorflow.keras.layers import Input, Dense, Conv2D, Flatten
from tensorflow.keras.models import Model
# 입력: 얼굴 이미지 (28x28, 흑백)
input_img = keras.Input(shape=(28, 28, 1), name="face_image")
# 공유되는 CNN 레이어 (=특징 추출)
x = layers.Conv2D(32, kernel_size=3, activation="relu")(input_img)
x = layers.MaxPooling2D(2)(x)
x = layers.Conv2D(64, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(2)(x)
x = layers.Flatten()(x)
# 출력1: 나이 예측
output_age = layers.Dense(1, name="output_age")(x) # 회귀 (나이 예측)
# 출력2: 성별 분류 # 다중분류면 노드 개수를 1이 아닌 클래스 개수만큼 해야함
output_gender = layers.Dense(1, activation="sigmoid", name="output_gender")(x) # 분류 (0=여자, 1=남자)
#⭐모델 정의 (입력 1개 → 출력 2개)
model = keras.Model(
inputs=input_img,
outputs=[output_age, output_gender]
)
#⭐모델 컴파일 - 각 출력에 맞는 loss함수, metric 지정
model.compile(
optimizer="adam",
loss={
"output_age": "mse", # 회귀 = Mean Squared Error
"output_gender": "binary_crossentropy", # 이진 분류 = Binary Crossentropy
},
metrics={
"output_age": "mae", # 회귀 성능 = Mean Absolute Error
"output_gender": "accuracy", # 분류 성능 = Accuracy
}
)
# 모델 학습 예시
model.fit(
image_data, # 입력 데이터
{'category': category_labels, # 첫 번째 출력의 정답
'color': color_labels}, # 두 번째 출력의 정답
epochs=10
)
# 데이터 준비
x_train = ... # 입력 데이터 (이미지 데이터)
y_train = {"output_age": age_labels, "output_gender": gender_labels} # 다중 출력 레이블
# 모델 학습 예시
model.fit(
x_train, # 입력 데이터
y_train, # 출력 데이터 (딕셔너리 형태)
epochs=10, # 학습 횟수
batch_size=32, # 배치 크기
validation_split=0.2 # 검증 데이터 비율
)
멀티 입력+출력 모델 예시
- 위 두 개념을 결합하면, 여러 입력을 받아 여러 결과를 출력하는 모델도 만들 수 있음!
- input : 사용자 프로필 & 사용자 행동 데이터
- output : 구매 확률 & 광고 선호도
# 앞뒤 맥락은 위의 코드들과 동일
model = keras.Model(
inputs=[input_1, input_2],
outputs=[output, gender_output]
)
멀티 입출력 구조의 장점
- 서로 다른 형태의 데이터를 각각 적절한 방식으로 처리할 수 있음
- 한 모델에서 여러 예측/분류 작업을 동시에 수행할 수 있음
- 각 출력에 맞는 손실 함수를 다르게 적용 가능
🤔 추가질문1: 멀티 입력의 상대적인 이점
🤔 정형데이터의 경우, 어차피 concat할 거라면 그냥 처음부터 데이터를 하나로 합쳐서 입력해도 되지 않나? 굳이 멀티 입력을 썼을 때 좋은 점이 있는가?
🤔 다중 입력에서 concatenate를 어떤 식으로 하는지 자세히 알려줘. 두 입력의 샘플 개수가 다르거나 그런 상황도 다 처리할 수 있는 건지 궁금해.
Claude 답변 1
1. 다중 입력을 사용하는 이유
단순히 데이터를 연결하는 것이 아니라 다중 입력을 사용하는 데는 몇 가지 중요한 이유가 있습니다:
A. 각 입력의 특성에 맞는 전처리가 가능합니다:
# 텍스트 입력을 위한 처리
text_input = Input(shape=(100,))
text_features = Dense(64, activation='relu')(text_input)
text_features = Dense(32, activation='relu')(text_features)
# 수치형 데이터를 위한 다른 처리
numeric_input = Input(shape=(20,))
numeric_features = Dense(16, activation='relu')(numeric_input)
# 특성 결합
combined = Concatenate()([text_features, numeric_features])
B. 서로 다른 스케일의 데이터를 각각 적절히 처리할 수 있습니다:
# 이미지 입력 (큰 차원)
image_input = Input(shape=(224, 224, 3))
x1 = Conv2D(32, (3, 3))(image_input)
x1 = Flatten()(x1)
# 메타데이터 입력 (작은 차원)
meta_input = Input(shape=(10,))
x2 = Dense(8)(meta_input)
# 두 특성을 결합하기 전에 각각 적절한 크기로 조정
combined = Concatenate()([x1, x2])
C. 각 입력에 대한 가중치를 다르게 학습할 수 있습니다:
# 주요 특성
main_input = Input(shape=(20,))
main_features = Dense(64, activation='relu')(main_input)
# 보조 특성
aux_input = Input(shape=(10,))
aux_features = Dense(32, activation='relu')(aux_input)
# 가중치가 다르게 학습됨
combined = Concatenate()([main_features, aux_features])
2. Concatenate 작동 방식
Concatenate 연산의 주요 규칙과 제한사항을 설명드리겠습니다:
A. 기본 규칙:
# 배치 크기: 32
# input1: (32, 100)
# input2: (32, 50)
combined = Concatenate()([input1, input2]) # 결과: (32, 150)
B. 주요 제약 사항❗
- 배치 크기(첫 번째 차원)는 반드시 같아야 합니다
- 연결하려는 축(axis)을 제외한 다른 모든 차원이 같아야 합니다
- 사용 예시
# 올바른 사용 예시
input1 = Input(shape=(100,)) # (batch_size, 100)
input2 = Input(shape=(50,)) # (batch_size, 50)
combined = Concatenate(axis=1)([input1, input2]) # (batch_size, 150)
# 잘못된 사용 예시
input1 = Input(shape=(100, 3)) # (batch_size, 100, 3)
input2 = Input(shape=(50, 4)) # (batch_size, 50, 4)
# 에러 발생! 마지막 차원이 다름 (3 != 4)
Claude 답변 2
네, 다중 입력을 사용하는 경우와 단순히 데이터를 연결하는 경우를 비교하여 설명해드리겠습니다.
예를 들어 사용자 프로필(나이, 성별, 지역)과 행동 데이터(클릭 수, 체류시간, 구매내역)를 이용해 상품을 추천하는 모델을 만든다고 가정해보겠습니다.
1. 단순히 데이터를 연결하는 경우:
```python
# 모든 특성을 하나의 입력으로 처리
input_combined = Input(shape=(6,)) # [나이, 성별, 지역, 클릭수, 체류시간, 구매내역]
x = Dense(64, activation='relu')(input_combined)
output = Dense(1, activation='sigmoid')(x)
```
이 접근의 한계:
- 모든 특성이 동일한 방식으로 처리됨
- 각 특성 그룹의 중요도를 제어하기 어려움
- 특성 간의 상호작용을 명시적으로 모델링하기 어려움
2. 다중 입력을 사용하는 경우:
```python
# 사용자 프로필 처리
profile_input = Input(shape=(3,)) # [나이, 성별, 지역]
profile_features = Dense(32, activation='relu', name='profile_dense1')(profile_input)
profile_features = Dense(16, activation='relu', name='profile_dense2')(profile_features)
# 행동 데이터 처리
behavior_input = Input(shape=(3,)) # [클릭수, 체류시간, 구매내역]
behavior_features = Dense(64, activation='relu', name='behavior_dense1')(behavior_input)
behavior_features = Dense(32, activation='relu', name='behavior_dense2')(behavior_features)
# 특성 결합
combined = Concatenate()([profile_features, behavior_features])
output = Dense(1, activation='sigmoid')(combined)
```
다중 입력의 장점:
1. 각 특성 그룹에 맞는 구조 설계 가능
```python
# 범주형 데이터용 임베딩
categorical_input = Input(shape=(1,))
x1 = Embedding(num_categories, 16)(categorical_input)
# 연속형 데이터용 정규화
numerical_input = Input(shape=(1,))
x2 = BatchNormalization()(numerical_input)
```
2. 특성 그룹별 가중치 조절 가능
```python
profile_features = Dense(32, kernel_regularizer=l2(0.01))(profile_input) # 더 강한 규제
behavior_features = Dense(32, kernel_regularizer=l2(0.001))(behavior_input) # 더 약한 규제
```
3. 특성 간 상호작용 명시적 모델링
```python
# 프로필 데이터 처리
profile_branch = Dense(32)(profile_input)
# 행동 데이터 처리
behavior_branch = Dense(32)(behavior_input)
# 상호작용 학습
interaction = Multiply()([profile_branch, behavior_branch])
# 각각의 특성과 상호작용을 모두 결합
combined = Concatenate()([profile_branch, behavior_branch, interaction])
```
4. 선택적 특성 사용 가능
```python
def create_model(use_profile=True, use_behavior=True):
inputs = []
features = []
if use_profile:
profile_input = Input(shape=(3,))
profile_features = Dense(32)(profile_input)
inputs.append(profile_input)
features.append(profile_features)
if use_behavior:
behavior_input = Input(shape=(3,))
behavior_features = Dense(32)(behavior_input)
inputs.append(behavior_input)
features.append(behavior_features)
combined = Concatenate()(features)
output = Dense(1, activation='sigmoid')(combined)
return Model(inputs=inputs, outputs=output)
```
5. 데이터 특성별 사전학습 모델 활용
```python
# 이미지 특성을 위한 사전학습 모델
image_input = Input(shape=(224, 224, 3))
vgg = VGG16(weights='imagenet', include_top=False)
image_features = vgg(image_input)
# 텍스트 특성을 위한 사전학습 모델
text_input = Input(shape=(100,))
bert = BertModel(...)
text_features = bert(text_input)
# 두 특성을 결합
combined = Concatenate()([image_features, text_features])
```
이러한 구조적 이점들은 단순히 데이터를 연결하는 방식으로는 얻을 수 없습니다. 특히 복잡한 문제에서는 각 특성 그룹의 특성을 살리고 적절히 처리하는 것이 모델의 성능 향상에 큰 도움이 됩니다.
또한 모델 해석 관점에서도 다중 입력 구조는 각 특성 그룹이 최종 예측에 어떤 영향을 미치는지 더 명확하게 분석할 수 있게 해줍니다.
🤔 추가질문2: 서로 다른 데이터 타입 처리
아예 다른 데이터 타입끼리도 멀티 입력으로 넣을 수 있나? 예를 들어 이미지와 텍스트를 같이 입력할 경우 어떻게 concatenate가 이루어지는 건가?
1) 이미지와 텍스트는 형태가 달라서 그냥 concatenate 할 수는 없음
- 이미지 → 2D (예: 28x28x3)
- 텍스트 → 1D 벡터 (예: 300차원의 임베딩 벡터)
2) 해결 방법: 두 입력을 모두 같은 차원의 벡터로 변환한 후 합치기
- 이미지 데이터 → CNN을 통해 Flatten()을 사용해 1D 벡터로 변환
- 텍스트 데이터 → 임베딩(Embedding) → LSTM/GRU → Dense Layer로 1D 벡터로 변환
- 두 개의 벡터를 concatenate한 뒤, 최종적으로 하나의 신경망을 거쳐 예측 수행
3) 예시
- 이미지 + 텍스트를 입력받아 감정 분석하는 모델
- 입력 데이터
- input_img: 이미지 (CNN 사용, 입력 크기 = 64x64x3)
- input_text: 텍스트 (LSTM 사용, 입력 길이 = 100 단어)
- 출력 데이터
- 긍정(1) / 부정(0) 감정 예측
- 코드 예시
from tensorflow import keras
from tensorflow.keras import layers
# 1️⃣ 이미지 입력 처리 (CNN)
input_img = keras.Input(shape=(64, 64, 3), name="image_input")
x1 = layers.Conv2D(32, kernel_size=3, activation="relu")(input_img)
x1 = layers.MaxPooling2D(2)(x1)
x1 = layers.Conv2D(64, kernel_size=3, activation="relu")(x1)
x1 = layers.MaxPooling2D(2)(x1)
x1 = layers.Flatten()(x1) # CNN 결과를 1D 벡터로 변환
# 2️⃣ 텍스트 입력 처리 (LSTM)
input_text = keras.Input(shape=(100,), name="text_input") # 100개의 단어 입력
x2 = layers.Embedding(input_dim=5000, output_dim=128)(input_text) # 임베딩 벡터 (128차원)
x2 = layers.LSTM(64)(x2) # LSTM을 통해 시퀀스 데이터를 1D 벡터로 변환
# 3️⃣ 이미지 벡터 + 텍스트 벡터 결합
merged = layers.concatenate([x1, x2]) # 1D 벡터끼리 연결 가능!
# 4️⃣ 최종 예측
x = layers.Dense(64, activation="relu")(merged)
x = layers.Dropout(0.5)(x)
output = layers.Dense(1, activation="sigmoid", name="sentiment_output") # 감정 예측 (0~1)
# 모델 정의
model = keras.Model(
inputs=[input_img, input_text],
outputs=output)
# 모델 학습 예시
model.fit(
[image_data, text_data], ### image_data : (batch_size, 64, 64, 3)
labels, ### text_data: (batch_size, 100)
epochs=10, ### labels: (batch_size, 1)
batch_size=32)
'ML & DL > 딥러닝 기초' 카테고리의 다른 글
[Pytorch+딥러닝] 3-4. 미니 배치와 데이터 로더 (0) | 2025.02.20 |
---|---|
[Pytorch+딥러닝] 3-3. nn.Module과 클래스로 구현하기 (1) | 2025.02.19 |
[Pytorch+딥러닝] 3-2. 다중 선형 회귀 (0) | 2025.02.18 |
[Pytorch+딥러닝] 3-1. 선형 회귀와 자동 미분 (0) | 2025.02.17 |
[Pytorch+딥러닝] 1. Pytorch 기초 (0) | 2025.02.05 |