
๊ธฐ๋ณธ ์์
๊ธฐ์กด: LeNet์ผ๋ก Fashion MNIST ๋ถ๋ฅ ์ค์ต ํ ์์ธก ๊ฒฐ๊ณผ ํ๋ฉด ์บก์ฒ
์ ์ : ๋ชจ๋ธ ์ฑ๋ฅ ํ์ธ ๊ทธ๋ํ ์บก์ณ


โ๏ธ๊ฐ๋ ์ ๋ฆฌ
ํ์ฑํ ํจ์
๊ฐ ๋ด๋ฐ์ ์ถ๋ ฅ๊ฐ์ ๊ฒฐ์ ํ๋ ํจ์
ํ์ฑํ ํจ์(activation function)๋ ๊ฐ ๋ด๋ฐ์ ์ถ๋ ฅ๊ฐ์ ๊ฒฐ์ ํ๋ ํจ์๋ค. ์ ๋ ฅ์ ๋ฐ์ ๋น์ ํ ํจ์์ ํต๊ณผ์์ผ ๋น์ ํ์ ์ธ ์ถ๋ ฅ์ ๋ง๋ ๋ค.
๐๋น์ ํ ํจ์๊ฐ ์๋ค๋ฉด ์ ๊ฒฝ๋ง์ ์ ํ ๋ชจ๋ธ๊ณผ ๋ค๋ฅผ ๋ฐ๊ฐ ์์ผ๋ฉฐ, ์๋ฌด๋ฆฌ ์ธต์ ๋ง์ด ์์๋ ๋ณต์กํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
์ ๋น์ ํ ํจ์์ฌ์ผ ํ ๊น?
์ ํ ํจ์๋ y = ax + b์ ํํ์ด๊ณ , ์ฌ๋ฌ ๊ฐ ์์๋ ๊ฒฐ๊ตญ ์ ํ์ด๋ค.
1์ธต: yโ = aโx + bโ
2์ธต: yโ = aโyโ + bโ = aโ(aโx + bโ) + bโ = (aโaโ)x + (aโbโ + bโ)
์์ฒ๋ผ, ๊ฒฐ๊ตญ y = ax + b์ ๋จ์ํ ์ ํ ๋ณํ์ด ๋์ด๋ฒ๋ฆฐ๋ค.
ํ์ง๋ง ์ค์ ์ธ์์ ๋ฐ์ดํฐ๋ ํจ์ฌ ๋ณต์กํ๋ค. ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฅํ๊ฑฐ๋, ์์ฑ์ ์ธ์ํ๋ ๊ฒ์ ์ ํ ๊ด๊ณ๋ก๋ ๋ถ๊ฐ๋ฅํ๋ค. ๋ฐ๋ผ์ ์ ๊ฒฝ๋ง์ด ์ด๋ฏธ์ง๋ ์์ฑ, ํ
์คํธ์ฒ๋ผ ๋ณต์กํ ํจํด์ ํ์ตํ๋ ค๋ฉด ๋น์ ํ์ฑ์ด ๋ฐ๋์ ํ์ํ๋ค. ๊ทธ๋์ ๊ฐ ์ธต ์ฌ์ด์ ๋น์ ํ ํ์ฑํ ํจ์๋ฅผ ์ฌ์ฉํ๋ค.
โ Sigmoid
f(x) = 1 / (1 + e^(-x))
- ์ถ๋ ฅ ๋ฒ์: (0, 1)
- ํ๋ฅ ์ฒ๋ผ ํด์ ๊ฐ๋ฅ
- S์ ํํ์ ๊ณก์
- ๋ฏธ๋ถ ๊ฐ๋ฅํ ์ฐ์ ํจ์
Sigmoid ํจ์๋ ์ถ๋ ฅ๊ฐ์ด 0๊ณผ 1์ฌ์ด์ ํ๋ฅ ๋ฒ์์ ์์ด ๊ฒฐ๊ณผ๋ฅผ ์ง๊ด์ ์ผ๋ก ํด์ํ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค. ๋ํ ๋ถ๋๋ฌ์ด S์ ๊ณก์ ํํ๋ก ์ฐ์์ ์ด๊ณ ๋ฏธ๋ถ ๊ฐ๋ฅํ์ฌ ์์ ์ ์ธ ํ์ต์ด ๊ฐ๋ฅํ๋ฉฐ, ์ด๊ธฐ ์ ๊ฒฝ๋ง ์ฐ๊ตฌ์ ๋๋ฆฌ ์ฌ์ฉ๋์๋ค.
ํ์ง๋ง ์ ๋ ฅ๊ฐ์ด ๋งค์ฐ ํฌ๊ฑฐ๋ ์์ ๋ ๊ธฐ์ธ๊ธฐ๊ฐ 0์ ๊ฐ๊น์์ง๋ ๊ธฐ์ธ๊ธฐ ์์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ ๊น์ ๋คํธ์ํฌ์์ ํ์ต์ด ์ด๋ ค์์ง๋ค. ๋ํ ๋ชจ๋ ์ถ๋ ฅ์ด ์์๋ผ์ ๊ฐ์ค์น ์ ๋ฐ์ดํธ ๋ฐฉํฅ์ด ์ ํ๋๊ณ , ์ง์ ํจ์ ๊ณ์ฐ์ผ๋ก ์ธํด ์๋์ ์ผ๋ก ๊ณ์ฐ ๋น์ฉ์ด ๋๋ค๋ ๋จ์ ์ด ์๋ค.
โ ReLU(Rectified Linear Unit)
f(x) = max(0, x)
- ์์๋ ๊ทธ๋๋ก, ์์๋ 0์ผ๋ก
- CNN์์ ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๊ธฐ๋ณธ ํ์ฑํ ํจ์
ReLU๋ ๋จ์ํ max ์ฐ์ฐ์ผ๋ก ๊ตฌํ๋์ด ๊ณ์ฐ ์๋๊ฐ ๋งค์ฐ ๋น ๋ฅด๊ณ ํจ์จ์ ์ด๋ค. ์์ ์์ญ์์ ๊ธฐ์ธ๊ธฐ๊ฐ 1๋ก ์ผ์ ํ์ฌ ๊ธฐ์ธ๊ธฐ ์์ค ๋ฌธ์ ๋ฅผ ํฌ๊ฒ ์ํํ์ผ๋ฉฐ, ์์ ๋ด๋ฐ์ ๋นํ์ฑํํ์ฌ ํฌ์ํ ํํ์ ๋ง๋ค์ด ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ์ ๋์ธ๋ค.
ํ์ง๋ง ์์ ์
๋ ฅ์ ๋ํด ๊ธฐ์ธ๊ธฐ๊ฐ 0์ด ๋์ด ๋ด๋ฐ์ด ์
๋ฐ์ดํธ๋์ง ์๋ dead ReLU ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. ์ด๋ฐ ๊ฒฝ์ฐ ํ์ต ๊ณผ์ ์์ ์ผ๋ถ ๋ด๋ฐ์ด ์์ ํ ๋นํ์ฑํ๋์ด ๋คํธ์ํฌ์ ํํ๋ ฅ์ด ๊ฐ์ํ ์ ์์ผ๋ฉฐ, ์ฌ์ ํ ์ถ๋ ฅ์ด 0 ์ค์ฌ์ด ์๋๋ผ๋ ํ๊ณ๊ฐ ์๋ค.
โ softmax
f(xแตข) = e^(xแตข) / Σโฑผ e^(xโฑผ)
- ๋ฒกํฐ์ ๋ชจ๋ ์์๋ฅผ ํ๋ฅ ๋ถํฌ๋ก ๋ณํ
- ๋ชจ๋ ์ถ๋ ฅ๊ฐ์ ํฉ์ด 1
- ๊ฐ ํด๋์ค์ ๋ํ ํ๋ฅ ๋ก ํด์ ๊ฐ๋ฅ
์ฌ์ฉ๋ชฉ์ :
- ์ถ๋ ฅ์ธต์์ ์ฌ์ฉ: ๋ค์ค ํด๋์ค ๋ถ๋ฅ์ ์ต์ข ๋จ๊ณ
- ํ๋ฅ ํด์: ๊ฐ ํด๋์ค๋ณ ์์ธก ํ๋ฅ ์ ๊ณต
- CrossEntropy Loss์ ํจ๊ป ์ฌ์ฉ: ๋ถ๋ฅ ๋ฌธ์ ์ ํ์ค ์กฐํฉ
softmax ํจ์๋ ์ถ๋ ฅ๊ฐ์ ํ๋ฅ ๋ถํฌ๋ก ๋ณํํ์ฌ ๊ฐ ํด๋์ค๋ณ ์์ธก ํ๋ฅ ์ ๋ช ํํ๊ฒ ํด์ํ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค. ๋ชจ๋ ์ถ๋ ฅ๊ฐ์ ํฉ๋ 1์ด ๋์ด ์๋์ ์ค์๋๋ฅผ ์ฝ๊ฒ ํ์ ํ ์ ์์ผ๋ฉฐ, ์ํ์ ์ผ๋ก ์์ ์ ์ด๊ณ ๋ฏธ๋ถ ๊ฐ๋ฅํ์ฌ ์ญ์ ํ ํ์ต์ ์ ํฉํ๋ค.
ํ์ง๋ง ์ง์ ํจ์๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๊ณ์ฐ ๋น์ฉ์ด ์๋์ ์ผ๋ก ๋๊ณ , ์ ๋ ฅ๊ฐ์ด ๊ทน๊ฐ์ ๊ฐ์ง ๋ ๊ธฐ์ธ๊ธฐ๊ฐ ๋งค์ฐ ์์์ ธ ๊ธฐ์ธ๊ธฐ ์์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. ๋ํ ํด๋์ค ์๊ฐ ๋ง์ ๋ ๊ณ์ฐ ๋ณต์ก๋๊ฐ ์ฆ๊ฐํ๋ค๋ ๋จ์ ์ด ์๋ค.
โ ReLU์ ๋ณํ ํจ์
- Leaky ReLU
- ELU(Exponentail Linear Unit)


One-Hot Encoding
๋ฒ์ฃผํ ๋ฐ์ดํฐ(ํด๋์ค ๋ ์ด๋ธ)๋ฅผ ์ ๊ฒฝ๋ง์ด ์ฒ๋ฆฌํ ์ ์๋ ๋ฐฉ์์ผ๋ก ๋ฐ๊พธ๋ ์ธ์ฝ๋ฉ ๋ฐฉ์
์-ํซ ์ธ์ฝ๋ฉ์ ์ ์ํ ํด๋์ค ๋ ์ด๋ธ์ ๋ฒกํฐ ํํ๋ก ๋ณํํ๋ ๋ฐฉ์์ด๋ค. softmax ์ถ๋ ฅ๊ณผ ์ง์ ๋น๊ตํ๊ธฐ ์ํด ๋ ์ด๋ธ๋ ๊ฐ์ ํํ๋ก ๋ณํํด์ฃผ์ด์ผ ํ๋ค.
MNIST ์๊ธ์จ ์ซ์ ๋ถ๋ฅ์์ ์ซ์ 3์ ์-ํซ ์ธ์ฝ๋ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
# ํด๋์ค 3
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
- ์ด 10๊ฐ ํด๋์ค(0-9)
- ํด๋น ์ธ๋ฑ์ค๋ง 1์ด๊ณ , ๋๋จธ์ง๋ ๋ชจ๋ 0
- ์ด ๋ฒกํฐ๋ softmax ์ถ๋ ฅ๊ณผ ์ง์ ๋น๊ตํ ์ ์๋ ๋ ์ด๋ธ ๋ฒกํฐ์ด๋ค.
์ ํ์ํ ๊น?
์ ๊ฒฝ๋ง์ ์ถ๋ ฅ์ธต์์ ๊ฐ ํด๋์ค์ ๋ํ ํ๋ฅ (softmax ์ถ๋ ฅ)์ ๊ณ์ฐํ๋ค.
[0.02, 0.01, 0.04, 0.91, 0.01, 0.003, 0.005, 0.002, 0.007, 0.001]
์ด ๊ฒฐ๊ณผ๋ฅผ ๋น๊ตํ๊ธฐ ์ํด, ๋ ์ด๋ธ๋ ๋์ผํ ํ์์ ๋ฒกํฐ๋ก ์ค๋นํด์ผ ํ๋ฉฐ, ๊ทธ ๋ฒกํฐ๊ฐ ์-ํซ ์ธ์ฝ๋ฉ๋ ํํ์ด๋ค.
from tensorflow.keras.utils import to_categorical
labels = [3, 0, 4] # ํด๋์ค ๋ ์ด๋ธ
one_hot = to_categorical(labels, num_classes=10)
print(one_hot)
# [[0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
# [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]]
๐ categorical_crossentropy๋ฅผ ์ฐ๋ ค๋ฉด ๋ ์ด๋ธ์ด one-hot์ด์ด์ผ ํ๋ค. ์ ์ ์ธ๋ฑ์ค ๊ทธ๋๋ก ์ธ ๋sparse_categorical_crossentropy๋ฅผ ์ด๋ค.
์ฝ๋ฐฑ
ํ๋ จ ์ค๊ฐ์ค๊ฐ ํน์ ์กฐ๊ฑด์ด ๋ง์กฑ๋๋ฉด ์๋์ผ๋ก ๋์ํ๋ ๊ธฐ๋ฅ
| EarlyStopping | ๊ฒ์ฆ ์์ค์ด ๋ ์ด์ ๊ฐ์ ๋์ง ์์ผ๋ฉด ์กฐ๊ธฐ ์ข ๋ฃ |
| ModelCheckpoint | ์ฑ๋ฅ์ด ๊ฐ์ฅ ์ข์๋ ๋ชจ๋ธ ๊ฐ์ค์น๋ฅผ ์ ์ฅ |
| ReduceLROnPlateau | ์ฑ๋ฅ์ด ์ ์ฒด๋๋ฉด ํ์ต๋ฅ ์ ์๋์ผ๋ก ์ค์ |
| TensorBoard | ์๊ฐํ ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํด์ค |
from tensorflow.keras.callbacks import EarlyStopping
early_stop = EarlyStopping(monitor='val_loss', patience=3)
model.fit(x_train, y_train,
validation_split=0.2,
epochs=50,
callbacks=[early_stop])
์์ค ํจ์
์์ค ํจ์๋ ๋ชจ๋ธ์ ์์ธก๊ฐ๊ณผ ์ค์ ์ ๋ต ์ฌ์ด์ ์ฐจ์ด๋ฅผ ์์น๋ก ํํํ ๊ฒ์ด๋ค. ๋ชจ๋ธ์ด "์ผ๋ง๋ ํ๋ ธ๋์ง"๋ฅผ ์ธก์ ํ์ฌ ํ์ต ๊ณผ์ ์์ ๊ฐ์ค์น๋ฅผ ์ ๋ฐ์ดํธํ๋ ๋ฐฉํฅ์ ๊ฒฐ์ ํ๋ค. CNN์์ ๋ถ๋ฅ ๋ฌธ์ ๋ฅผ ๋ค๋ฃฐ ๋๋ ์ฃผ๋ก Cross Entropy ํจ์๋ฅผ ์ฌ์ฉํ๋ค.
โ Categorical Crossentropy
- softmax ์ถ๋ ฅ๊ณผ one-hot ์ธ์ฝ๋ฉ ์ ๋ต์ ๋น๊ตํ์ฌ ์์ค์ ๊ณ์ฐ
- ํ๋ฅ ๋ถํฌ ๊ฐ์ ์ฐจ์ด๋ฅผ ์ธก์
- ๊ฐ์ด 0์ ๊ฐ๊น์ธ์๋ก ์ข์ ์์ธก์ ์๋ฏธ
Loss = -Σ(y_true * log(y_pred))
์ฌ์ฉ ์กฐ๊ฑด:
- ์ ๋ต์ด one-hot ์ธ์ฝ๋ฉ ํํ์ฌ์ผ ํ๋ค
- ์ถ๋ ฅ์ธต์ softmax ํ์ฑํ ํจ์๋ฅผ ์ฌ์ฉ
- ์ํธ ๋ฐฐํ์ ์ธ ํด๋์ค(ํ ๋ฒ์ ํ๋์ ํด๋์ค๋ง ์ ํ)
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
๐ช์ค์ต
import keras
from keras import layers
lenet5 = keras.Sequential()
lenet5.add(layers.Input(shape=(28, 28, 1)))
lenet5.add(layers.Conv2D(filters=6, kernel_size=5, activation='sigmoid', padding='same'))
lenet5.add(layers.AveragePooling2D(pool_size=2))
lenet5.add(layers.Conv2D(filters=16, kernel_size=5, activation='sigmoid'))
lenet5.add(layers.AveragePooling2D(pool_size=2))
lenet5.add(layers.Flatten())
lenet5.add(layers.Dense(120, activation='sigmoid'))
lenet5.add(layers.Dense(84, activation='sigmoid'))
lenet5.add(layers.Dense(10, activation ='softmax'))
- LeNet-5 ๊ตฌ์กฐ๋ฅผ Sequential API๋ก ๊ตฌํํ ์ ์ฒด ๋ชจ๋ธ ์ ์ ๋ธ๋ก
- ์ ๋ ฅ ์ด๋ฏธ์ง: 28x28 ํฝ์ , ์ฑ๋ ์ 1
- ์ด 9๊ฐ์ ๋ ์ด์ด๋ก ๊ตฌ์ฑ
[ํ๋ฆ]
์ ๋ ฅ์ธต
- 28x28 ํฌ๊ธฐ์ ํ๋ฐฑ ์ด๋ฏธ์ง ์ ๋ ฅ
1์ฐจ ํฉ์ฑ๊ณฑ+ํ๋ง
- conv2D: 6๊ฐ์ 5x5 ํํฐ ์ ์ฉ, padding='same', ํ์ฑํ ํจ์ sigmoid
- AveragePooling2D: 2x2 ํํฐ๋ก ๋ค์ด ์ํ๋ง
2์ฐจ ํฉ์ฑ๊ณฑ+ํ๋ง
- Conv2D: 16๊ฐ์ 5x5 ํํฐ ์ ์ฉ, ํ์ฑํ ํจ์ sigmoid
- AveragePooling2D: 2x2 ํํฐ๋ก ๋ค์ ๋ค์ด ์ํ๋ง
์์ ์ฐ๊ฒฐ์ธต Fully Connected Layers
- Flatten: 2D feature map์ 1D ๋ฒกํฐ๋ก ๋ณํ
- Dense(120): 120๊ฐ์ ๋ด๋ฐ, sigmoid ํ์ฑํ
- Dense(84): 84๊ฐ์ ๋ด๋ฐ, sigmoid ํ์ฑํ
- Dense(10): 10๊ฐ์ ๋ด๋ฐ, softmax ์ถ๋ ฅ => 10๊ฐ์ ํด๋์ค ํ๋ฅ ๋ก ๋ณํ
lenet5.summary()

(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
print(train_target)
print(train_input.shape, train_target.shape)
- Keras์์ ์ ๊ณตํ๋ Fashion MNIST ๋ฐ์ดํฐ์ ํธ์ถ

import matplotlib.pyplot as plt
fig, axs = plt.subplots(1, 10, figsize=(10, 10))
for i in range(10):
axs[i].imshow(train_input[i], cmap='gray_r')
axs[i].axis('off')
plt.show()
- Fashion MNIST ํ๋ จ ์ด๋ฏธ์ง 10์ฅ ์๊ฐํ

train_input = train_input.reshape(-1, 28, 28, 1)/255.0
- ํฝ์ ์ ๊ทํ + ์ฐจ์ ๋ณ๊ฒฝ
- reshape(): CNN ์ ๋ ฅํ์์ผ๋ก ๋ณํ. - ์ ์ํ ๊ฐ์ ์๋ ๊ณ์ฐ
- /255.0: ํฝ์ ๊ฐ์ 0~1 ์ฌ์ด ์ค์๋ก ์ ๊ทํํ์ฌ ํ์ต ์์ ์ฑ ํฅ์
from sklearn.model_selection import train_test_split
train_scaled, val_scaled, train_target, val_target = train_test_split(
train_input, train_target, test_size=0.2, random_state=42)
- train, test ์ธํธ ๋ถ๋ฆฌ
- ์ ์ฒด ํ๋ จ ๋ฐ์ดํฐ ์ค 80%๋ train_scaled, 20%๋ val_scaled
- ๊ณผ์ ํฉ ์ฌ๋ถ ํ๋จ์ ์ํด validation ๋ฐ์ดํฐ ๋ณ๋๋ก ํ๋ณด
- random_state=42: ํญ์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ ๋ถํ
checkpoint_cb = keras.callbacks.ModelCheckpoint('lenet5-model.keras', save_best_only=True)
- ๋ชจ๋ธ ์ฒดํฌํฌ์ธํธ ์ ์ฅ ์ฝ๋ฐฑ
- ๊ฒ์ฆ ์ฑ๋ฅ์ด ๊ฐ์ฅ ์ข์๋ ์์ ์ ๊ฐ์ค์น๋ง ์ ์ฅ
- ๋์ค์ ๋ชจ๋ธ์ ๋ถ๋ฌ์ ์ฑ๋ฅ ์ฌํ ๊ฐ๋ฅ
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2, restore_best_weights=True)
- ์กฐ๊ธฐ ์ข ๋ฃ ์ฝ๋ฐฑ
- patience=2: ๊ฒ์ฆ ์ฑ๋ฅ์ด 2epoch ์ด์ ํ์ฑ๋์ง ์์ผ๋ฉด ํ์ต ์ค๋จ
- restore_best_weight=True: ์ฑ๋ฅ์ด ๊ฐ์ฅ ์ข์๋ ์ง์ ์ ๊ฐ์ค์น๋ก ๋ณต์
lenet5.compile(loss='sparse_categorical_crossentropy', metrics=['accuracy'])
- ๋ชจ๋ธ ์ปดํ์ผ
- ๋ผ๋ฒจ์ด ์ ์ํ์ด๋ฏ๋ก, sparse_categorical_crossentropy ํจ์ ์ฌ์ฉ
- metrics=['accuracy']: ์ ํ๋๋ฅผ ๋ชจ๋ํฐ๋ง ์งํ๋ก ์ฌ์ฉ
hist = lenet5.fit(
train_scaled, train_target,
epochs=20,
validation_data=(val_scaled, val_target),
callbacks=[checkpoint_cb, early_stopping_cb])
- ๋ชจ๋ธ ํ๋ จ ์ํ

epochs=range(1, len(hist.history['loss'])+1)
fig, axs = plt.subplots(1, 2, figsize=(12, 4))
axs[0].plot(epochs, hist.history['loss'], label='Training Loss')
axs[0].plot(epochs, hist.history['val_loss'], label='Validation Loss')
axs[0].set_xticks(epochs)
axs[0].set_xlabel('Epochs')
axs[0].set_ylabel('Loss')
axs[1].plot(epochs, hist.history['accuracy'])
axs[1].plot(epochs, hist.history['val_accuracy'])
axs[1].set_xticks(epochs)
axs[1].set_xlabel('Epochs')
axs[1].set_ylabel('Accuracy')
plt.show()

