Tensorflow & Keras

์ฐฝ์Šˆยท2025๋…„ 4์›” 11์ผ

Deep Learning

๋ชฉ๋ก ๋ณด๊ธฐ
14/16
post-thumbnail

ํ…์„œํ”Œ๋กœ์šฐ (Tensorflow)

๋”ฅ๋Ÿฌ๋‹ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ์ผ์ข…

  • ๋‚ด๋ถ€์ ์œผ๋กœ C/C++๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ๊ณ  ํŒŒ์ด์ฌ์„ ๋น„๋กฏํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์–ธ์–ด์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ํ…์„œ(Tensor)์™€ ํ”Œ๋กœ์šฐ(Flow)๊ฐ€ ํ•ฉ์ณ์ง„ ์šฉ์–ด๋กœ ํ…์„œ๋Š” ๋‹ค์ฐจ์› ๋ฐฐ์—ด (์Šค์นผ๋ผ, ๋ฒกํ„ฐ, ํ–‰๋ ฌ ํ…์„œ)์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ ํ”Œ๋กœ์šฐ๋Š” Data Flow์˜ ์˜๋ฏธ๋กœ ์œ ๋‹›๋“ค ์‚ฌ์ด๋กœ ํ…์„œ๋“ค์ด ํ˜๋Ÿฌ๋‹ค๋‹Œ๋‹ค๋Š” ๋œป์ด๋‹ค.

์ผ€๋ผ์Šค (Keras)

ํŒŒ์ด์ฌ์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์œผ๋ฉฐ, ๊ณ ์ˆ˜์ค€ ๋”ฅ๋Ÿฌ๋‹ API์ด๋‹ค.

  • ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฑ์—”๋“œ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ฐ€์žฅ ๋งŽ์ด ์„ ํƒ๋˜๋Š” ๋ฐฑ์—”๋“œ๋Š” ํ…์„œํ”Œ๋กœ์šฐ์ด๋‹ค.
  • ์‰ฝ๊ณ  ๋น ๋ฅธ ํ”„๋กœํ† ํƒ€์ดํ•‘์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ํ”ผ๋“œํฌ์›Œ๋“œ ์‹ ๊ฒฝ๋ง, ์ปจ๋ณผ๋ฃจ์…˜ ์‹ ๊ฒฝ๋ง๊ณผ ์ˆœํ™˜ ์‹ ๊ฒฝ๋ง์€ ๋ฌผ๋ก , ์—ฌ๋Ÿฌ ๊ฐ€์ง€์˜ ์กฐํ•ฉ๋„ ์ง€์›ํ•œ๋‹ค.
  • CPU ๋ฐ GPU์—์„œ ์›ํ™œํ•˜๊ฒŒ ์‹คํ–‰๋œ๋‹ค.
  • ์ผ€๋ผ์Šค๋Š” ์—…๊ณ„์™€ ํ•™๊ณ„์—์„œ ํญ๋„“๊ฒŒ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์œผ๋ฉฐ ํŒŒ์ดํ† ์น˜(pytorch)๋Š” ์—ฐ๊ตฌ์ž๋“ค์ด ์„ ํ˜ธํ•œ๋‹ค.

๐Ÿ”— ์ผ€๋ผ์Šค๋กœ ์‹ ๊ฒฝ๋ง ์ž‘์„ฑ

  • ์ผ€๋ผ์Šค์˜ ํ•ต์‹ฌ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ๋ชจ๋ธ(model)์ด๋ฉฐ ๋ ˆ์ด์–ด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ชจ๋ธ ์œ ํ˜•์€ Sequential ์„ ํ˜• ์Šคํƒ ๋ชจ๋ธ์ด๋‹ค. Sequential ๋ชจ๋ธ์€ ๋ ˆ์ด์–ด๋ฅผ ์„ ํ˜•์œผ๋กœ ์Œ“์„ ์ˆ˜ ์žˆ๋Š” ์‹ ๊ฒฝ๋ง ๋ชจ๋ธ์ด๋‹ค.

๐Ÿ“ฆ code: ์ผ€๋ผ์Šค๋กœ ์‹ ๊ฒฝ๋ง ์ž‘์„ฑ

# (1) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํฌํ•จ
import numpy as np
import tensorflow as tf

# (2) ์ž…๋ ฅ๋ฐ์ดํ„ฐ์™€ ์ •๋‹ต ๋ ˆ์ด๋ธ”์„ ์ค€๋น„ (Numpy ๋ฐฐ์—ด์ด๋‚˜ Python listํ˜•์‹์œผ๋กœ ์ค€๋น„)
X = np.array([[0,0], [0,1], [1,0], [1,1]])  # XOR ๋ฌธ์ œ์˜ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ
y = np.array([[0], [1], [1], [0]])  # XOR ๋ฌธ์ œ์˜ ์ •๋‹ต ๋ ˆ์ด๋ธ”

# (3) Sequential ๋ชจ๋ธ์„ ์ƒ์„ฑ
model = tf.keras.models.Sequential()

# (4) Sequential ๋ชจ๋ธ์— add() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ๋ ˆ์ด์–ด๋ฅผ ์ถ”๊ฐ€
model.add(tf.keras.layers.Dense(units=2, input_shape=(2,), activation='sigmoid'))  # ์ฒซ ๋ฒˆ์งธ ์€๋‹‰์ธต, 2๊ฐœ์˜ ์œ ๋‹›
model.add(tf.keras.layers.Dense(units=1, activation='sigmoid'))  # ์ถœ๋ ฅ์ธต, 1๊ฐœ์˜ ์œ ๋‹›

# (5) compile() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Sequential ๋ชจ๋ธ์„ ์ปดํŒŒ์ผ
model.compile(loss='mean_squared_error', optimizer=tf.keras.optimizers.SGD(learning_rate=0.3))
# loss function์€ MSE, learning rate=0.3

# (6) fit()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ์„œ ํ•™์Šต์„ ์ˆ˜ํ–‰
model.fit(X, y, batch_size=1, epochs=1000)  # ๋ฐฐ์น˜ ํฌ๊ธฐ 1, ์—ํฌํฌ 1000๋ฒˆ

# (7) predict()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ชจ๋ธ์„ ํ…Œ์ŠคํŠธ
print(model.predict(X))  # ์˜ˆ์ธก๋œ ์ถœ๋ ฅ ๊ฐ’

# (8) summary()ํ•จ์ˆ˜๋กœ ๋ชจ๋ธ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆ˜ ์ ๊ฒ€
print(model.summary())  # ๋ชจ๋ธ ์š”์•ฝ ์ •๋ณด ์ถœ๋ ฅ
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 2) 6
_________________________________________________________________
dense_1 (Dense) (None, 1) 3
=================================================================
Total params: 9
Trainable params: 9
Non-trainable params: 0
_________________________________________________________________

๐Ÿ” ์ผ€๋ผ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” 3๊ฐ€์ง€ ๋ฐฉ๋ฒ•

โœ… Sequential ๋ชจ๋ธ์— ํ•„์š”ํ•œ ๋ ˆ์ด์–ด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•

model = Sequential()

model.add(Dense(units=2, input_shape=(2,), activation='sigmoid'))
model.add(Dense(units=1, activation='sigmoid'))

โœ… ํ•จ์ˆ˜ํ˜• API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

inputs = Input(shape=(2,)) # ์ž…๋ ฅ์ธต
x = Dense(2, activation="sigmoid")(inputs) # ์€๋‹‰์ธต
prediction = Dense(1, activation="sigmoid")(x) # ์ถœ๋ ฅ์ธต

model = Model(inputs=inputs, outputs=prediction)
  • ํ•จ์ˆ˜ํ˜• API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ฐ์ฒด๋“ค์„ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋ ˆ์ด์–ด๊ฐ€ ๋‹ค์ค‘ ์ž…๋ ฅ์ด๋‚˜ ๋‹ค์ค‘ ์ถœ๋ ฅ์„ ๊ฐ€์ง€๋„๋ก ์—ฐ๊ฒฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

โœ… Model ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„์„œ ๊ฐ์ž์˜ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•

class SimpleMLP(Model):
    def __init__(self, num_classes):  # ์ƒ์„ฑ์ž ์ž‘์„ฑ
        super(SimpleMLP, self).__init__(name='mlp')
        self.num_classes = num_classes
        self.dense1 = Dense(32, activation='sigmoid')
        self.dense2 = Dense(num_classes, activation='sigmoid')

    def call(self, inputs):  # ์ˆœ๋ฐฉํ–ฅ ํ˜ธ์ถœ์„ ๊ตฌํ˜„ํ•œ๋‹ค
        x = self.dense1(inputs)
        return self.dense2(x)

model = SimpleMLP()
model.compile(...)  # ์†์‹คํ•จ์ˆ˜, ์ตœ์ ํ™”๊ธฐ, ํ‰๊ฐ€์ง€ํ‘œ ๋“ฑ ์„ค์ •
model.fit(...)      # ํ•™์Šต ์ˆ˜ํ–‰


๐Ÿ“ฆ ์ผ€๋ผ์Šค(Keras)๋ฅผ ์ด์šฉํ•œ MNIST ์ˆซ์ž ์ธ์‹

1980๋…„๋Œ€์— ๋ฏธ๊ตญ์˜ ๊ตญ๋ฆฝํ‘œ์ค€ ์—ฐ๊ตฌ์†Œ(NIST)์—์„œ ์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ ์„ธํŠธ๋กœ 6๋งŒ๊ฐœ์˜ ํ›ˆ๋ จ ์ด๋ฏธ์ง€์™€ 1๋งŒ๊ฐœ์˜ ํ…Œ์ŠคํŠธ ์ด๋ฏธ์ง€๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค.

MNIST ํ•„๊ธฐ์ฒด ์ˆซ์ž ๋ฐ์ดํ„ฐ

# ์ˆซ์ž ๋ฐ์ดํ„ฐ ๊ฐ€์ €์˜ค๊ธฐ
import matplotlib.pyplot as plt
import tensorflow as tf

(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
# numpy ๋ฐฐ์—ด ํ˜•ํƒœ ์ถœ๋ ฅ
train_images.shape
#(60000, 28, 28)

train_labels
#array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)

test_images.shape
#(10000, 28, 28)

plt.imshow(train_images[0], cmap="Greys")

์‹ ๊ฒฝ๋ง ๋ชจ๋ธ ๊ตฌ์ถ•ํ•˜๊ธฐ

# ์‹ ๊ฒฝ๋ง ๋ชจ๋ธ ๊ตฌ์ถ•
model = tf.keras.models.Sequential()

model.add(tf.keras.layers.Dense(512, activation='relu', input_shape=(784,))) # 512 units, input shape 28*28=784
model.add(tf.keras.layers.Dense(10, activation='sigmoid')) # 10 units, sigmoid activation function
model.compile(optimizer='rmsprop',loss='mse', metrics=['accuracy'])
  • ์†์‹คํ•จ์ˆ˜(loss function): ์‹ ๊ฒฝ๋ง์˜ ์ถœ๋ ฅ๊ณผ ์ •๋‹ต ๊ฐ„์˜ ์˜ค์ฐจ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜ [mse]
  • ์˜ตํ‹ฐ๋งˆ์ด์ €(optimizer): ์†์‹ค ํ•จ์ˆ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹ ๊ฒฝ๋ง์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ตœ์ ํ™”ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜ [rmsprop]
  • ์ง€ํ‘œ(metric): ํ›ˆ๋ จ๊ณผ ํ…Œ์ŠคํŠธ ๊ณผ์ •์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ฒ™๋„ [accuracy]

๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ

# ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ
train_images = train_images.reshape((60000, 784))
train_images = train_images.astype('float32') / 255.0

test_images = test_images.reshape((10000, 784))
test_images = test_images.astype('float32') / 255.0

# ์ •๋‹ต ๋ ˆ์ด๋ธ” ํ˜•ํƒœ ๋ณ€๊ฒฝ(์›ํ•ซ ์ธ์ฝ”๋”ฉ)
train_labels = tf.keras.utils.to_categorical(train_labels)
test_labels = tf.keras.utils.to_categorical(test_labels)

# train_labels[0]
# array([0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], dtype=float32)

ํ•™์Šต ๋ฐ ํ…Œ์ŠคํŠธ

# ํ•™์Šต
history = model.fit(train_images, train_labels, epochs=5, batch_size=128)
Epoch 1/5
469/469 [==============================] - 2s 3ms/step - loss: 0.0158 - accuracy: 0.9168
...
Epoch 5/5
469/469 [==============================] - 2s 3ms/step - loss: 0.0027 - accuracy: 0.9867
# ํ…Œ์ŠคํŠธ
test_loss, test_acc = model.evaluate(test_images, test_labels)
print('ํ…Œ์ŠคํŠธ ์ •ํ™•๋„:', test_acc)
313/313 [==============================] - 0s 892us/step - loss: 0.0039 - accuracy: 0.9788
ํ…Œ์ŠคํŠธ ์ •ํ™•๋„: 0.9787999987602234

๊ทธ๋ž˜ํ”„ ๊ทธ๋ฆฌ๊ธฐ ๋ฐ ์‹ค์ œ ์ด๋ฏธ์ง€๋กœ ํ…Œ์ŠคํŠธ (Google drive)

# ๊ทธ๋ž˜ํ”„ ๊ทธ๋ฆฌ๊ธฐ
loss = history.history['loss']
acc = history.history['accuracy']
epochs = range(1, len(loss)+1)
plt.plot(epochs, loss, 'b', label='Training Loss')
plt.plot(epochs, acc, 'r', label='Accuracy')
plt.xlabel('epochsโ€™)
plt.ylabel('loss/accโ€™)
plt.show()

# ์‹ค์ œ ์ด๋ฏธ์ง€๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ
from google.colab import drive
drive.mount('/content/drive')
# Mounted at /content/drive
import os
print(os.listdir('/content/drive/MyDriveโ€™))
# ['Colab Notebooks', 'Google Formโ€™,โ€ฆ]
# ์‹ค์ œ ์ด๋ฏธ์ง€๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ
import cv2 as cv
image = cv.imread('test.png', cv.IMREAD_GRAYSCALE)
image = cv.resize(image, (28, 28))
image = image.astype('float32')
image = image.reshape(1, 784)
image = 255-image
image /= 255.0
plt.imshow(image.reshape(28, 28),cmap='Greys')
plt.show()
pred = model.predict(image.reshape(1, 784), batch_size=1)
print("์ถ”์ •๋œ ์ˆซ์ž=", pred.argmax())

์ถ”์ •๋œ ์ˆซ์ž= 2

์ผ€๋ผ์Šค(Keras)์˜ ๋ฐ์ดํ„ฐ, ํด๋ž˜์Šค, ๋งค๊ฐœ๋ณ€์ˆ˜

์ผ€๋ผ์Šค์˜ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ

  • ๋„˜ํŒŒ์ด ๋ฐฐ์—ด: ๋ฉ”๋ชจ๋ฆฌ์— ์ ์ œ ๊ฐ€๋Šฅํ•˜๋ฉด ์ข‹์€ ์„ ํƒ์ด๋‹ค.
  • TensorFlow Dataset ๊ฐ์ฒด: ํฌ๊ธฐ๊ฐ€ ์ปค์„œ, ๋ฉ”๋ชจ๋ฆฌ์— ํ•œ ๋ฒˆ์— ์ ์žฌ๋  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ์— ๋””์Šคํฌ ๋˜๋Š” ๋ถ„์‚ฐ ํŒŒ์ผ ์‹œ์Šคํ…œ์—์„œ ์ŠคํŠธ๋ฆฌ๋ฐ๋  ์ˆ˜ ์žˆ๋‹ค.
  • ํŒŒ์ด์ฌ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ: ์˜ˆ๋ฅผ ๋“ค์–ด์„œ keras.utils.Sequence ํด๋ž˜์Šค๋Š” ํ•˜๋“œ ๋””์Šคํฌ์— ์œ„์น˜ํ•œ ํŒŒ์ผ์„ ์ฝ์–ด์„œ ์ˆœ์ฐจ์ ์œผ๋กœ ์ผ€๋ผ์Šค ๋ชจ๋ธ๋กœ ๊ณต๊ธ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ก ํ…์„œ(Tensor)

ํ…์„œ(tensor)๋Š” ๋‹ค์ฐจ์› ๋„˜ํŒŒ์ด ๋ฐฐ์—ด์ด๋‹ค.

  • 2์ฐจ์›์€ ํ–‰๋ ฌ(matrix), 3์ฐจ์› ์ด์ƒ์„ ์ „ํ†ต์ ์œผ๋กœ ํ…์„œ(tensor)๋ผ๊ณ  ๋ถˆ๋ ค์™”๋‹ค.
  • ํ…์„œ๋Š” ๋ฐ์ดํ„ฐ(์‹ค์ˆ˜)๋ฅผ ์ €์žฅํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
  • ํ…์„œ์—์„œ๋Š” ๋ฐฐ์—ด์˜ ์ฐจ์›์„ ์ถ•(axis)์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด์„œ 3์ฐจ์› ํ…์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ…์„œ(tensor)์˜ ์†์„ฑ

  • ํ…์„œ์˜ ์ฐจ์›(์ถ•์˜ ๊ฐœ์ˆ˜): ํ…์„œ์— ์กด์žฌํ•˜๋Š” ์ถ•์˜ ๊ฐœ์ˆ˜๋กœ 3์ฐจ์› ํ…์„œ์—๋Š” 3๊ฐœ์˜ ์ถ•์ด ์žˆ๋‹ค. [x.ndim]
  • ํ˜•์ƒ(shape): ํ…์„œ์˜ ๊ฐ ์ถ•์œผ๋กœ ์–ผ๋งˆ๋‚˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€๋ฅผ ํŒŒ์ด์ฌ์˜ ํŠœํ”Œ๋กœ ๋‚˜ํƒ€๋‚ธ ๊ฒƒ์ด๋‹ค. [x.shape]
  • ๋ฐ์ดํ„ฐ ํƒ€์ž…(data type): ํ…์„œ ์š”์†Œ์˜ ์ž๋ฃŒํ˜• [x.dtype]

import numpy as np

x = np.array(
    [[[ 0,  1,  2,  3,  4],
      [ 5,  6,  7,  8,  9]],
     
     [[10, 11, 12, 13, 14],
      [15, 16, 17, 18, 19]],
     
     [[20, 21, 22, 23, 24],
      [25, 26, 27, 28, 29]]]
)

x.ndim
# 3

x.shape
# (3, 2, 5)

x.dtype
# dtype('int64')

๐Ÿ’ก ํ›ˆ๋ จ ๋ฐ์ดํ„ฐ์˜ ํ˜•์ƒ

  • ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ: (๋ฐฐ์น˜ ํฌ๊ธฐ, ํŠน์ง•์ˆ˜) ํ˜•์ƒ์˜ 2์ฐจ์› ๋„˜ํŒŒ์ด ํ…์„œ์— ์ €์žฅ๋œ๋‹ค.
(batch_size, features)=(365,3)

  • ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ: (๋ฐฐ์น˜ ํฌ๊ธฐ, ์ด๋ฏธ์ง€ ๋†’์ด, ์ด๋ฏธ์ง€ ๋„ˆ๋น„, ์ฑ„๋„์ˆ˜) ํ˜•์ƒ์˜ 4์ฐจ์› ๋„˜ํŒŒ์ด ํ…์„œ์— ์ €์žฅ๋œ๋‹ค
(batch_size, height, width, channels)=c(365,28,28,3)

  • ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ: (๋ฐฐ์น˜ ํฌ๊ธฐ, ํƒ€์ž… ์Šคํ…, ํŠน์ง•์ˆ˜) ํ˜•์ƒ์˜ 3์ฐจ์› ๋„˜ํŒŒ์ด ํ…์„œ์— ์ €์žฅ๋œ๋‹ค.
(batch_size, timesteps, features)=(365,24,2)

  • ๋™์˜์ƒ ๋ฐ์ดํ„ฐ: (๋ฐฐ์น˜ ํฌ๊ธฐ, ํ”„๋ ˆ์ž„์ˆ˜, ์ด๋ฏธ์ง€ ๋†’์ด, ์ด๋ฏธ์ง€ ๋„ˆ๋น„, ์ฑ„๋„์ˆ˜) ํ˜•์ƒ์˜ 5์ฐจ์› ๋„˜ํŒŒ์ด ํ…์„œ์— ์ €์žฅ๋œ๋‹ค.
(batch_size, frame, height, width, channels)=c(365,100,28,28,3)


๐Ÿ’ก ์ผ€๋ผ์Šค์˜ ํด๋ž˜์Šค

์ผ€๋ผ์Šค๋กœ ์‹ ๊ฒฝ๋ง์„ ๊ตฌ์ถ•ํ•  ๋•Œ ํ•„์š”ํ•œ ์š”์†Œ๋“ค:

  • ๋ชจ๋ธ: ํ•˜๋‚˜์˜ ์‹ ๊ฒฝ๋ง์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • ๋ ˆ์ด์–ด: ์‹ ๊ฒฝ๋ง์—์„œ ํ•˜๋‚˜์˜ ์ธต์ด๋‹ค.
  • ์ž…๋ ฅ ๋ฐ์ดํ„ฐ: ํ…์„œํ”Œ๋กœ์šฐ ํ…์„œ ํ˜•์‹์ด๋‹ค.
  • ์†์‹ค ํ•จ์ˆ˜: ์‹ ๊ฒฝ๋ง์˜ ์ถœ๋ ฅ๊ณผ ์ •๋‹ต ๋ ˆ์ด๋ธ” ๊ฐ„์˜ ์ฐจ์ด๋ฅผ ์ธก์ •ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.
  • ์˜ตํ‹ฐ๋งˆ์ด์ €: ํ•™์Šต์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ตœ์ ํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค. ํ•™์Šต๋ฅ ๊ณผ ๋ชจ๋ฉ˜ํ…€์„ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค

์ผ€๋ผ์Šค์—๋Š” ์ด๋Ÿฐ ์š”์†Œ๋“ค์„ ๊ตฌํ˜„ํ•œ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค ์กด์žฌํ•œ๋‹ค.


Sequential ๋ชจ๋ธ

์ž…๋ ฅ์‹ ํ˜ธ๊ฐ€ ํ•œ ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ์ „๋‹ฌ๋˜๋Š” ํ”ผ๋“œ ํฌ์›Œ๋“œ ์‹ ๊ฒฝ๋ง์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฐ€์žฅ ๊ธฐ์ดˆ์ ์ธ ์‹ ๊ฒฝ๋ง
โœ… compile(optimizer, loss=None, metrics=None): ํ›ˆ๋ จ์„ ์œ„ํ•ด์„œ ๋ชจ๋ธ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฉ”์†Œ๋“œ

  • optimizer: ์˜ตํ‹ฐ๋งˆ์ด์ € ์ด๋ฆ„
  • loss: ์†์‹คํ•จ์ˆ˜ ์ด๋ฆ„
  • metrics: ์„ฑ๋Šฅ ์ธก์ •ํ•ญ๋ชฉ (ex. metrics=[โ€˜accuracyโ€™])

โœ… fit(x=None, y=None, batch_size=None, epochs=1, verbose=1): ํ›ˆ๋ จ ๋ฉ”์†Œ๋“œ

  • batch_size: ๊ฐ€์ค‘์น˜ ์—…๋ฐ์ดํŠธ์‹œ ์ฒ˜๋ฆฌํ•˜๋Š” ์ƒ˜ํ”Œ ์ˆ˜
  • epochs: ๋ฐ์ดํ„ฐ ์„ธํŠธ ๋ช‡ ๋ฒˆ ๋ฐ˜๋ณตํ•˜๋Š”์ง€

โœ… evaluate(x=None, y=None): ํ…Œ์ŠคํŠธ ๋ชจ๋“œ์—์„œ ๋ชจ๋ธ์˜ ์†์‹ค ํ•จ์ˆ˜ ๊ฐ’๊ณผ ์ธก์ • ํ•ญ๋ชฉ ๊ฐ’์„ ๋ฐ˜ํ™˜

โœ… predict(x, batch_size=None): ์ž…๋ ฅ ์ƒ˜ํ”Œ์— ๋Œ€ํ•œ ์˜ˆ์ธก๊ฐ’์„ ์ƒ์„ฑ

โœ… add(layer): ๋ ˆ์ด์–ด๋ฅผ ๋ชจ๋ธ์— ์ถ”๊ฐ€


layer ํด๋ž˜์Šค

โœ… Input(shape, batch_size, name): ์ž…๋ ฅ์„ ๋ฐ›์•„์„œ ์ผ€๋ผ์Šค ํ…์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฐ์ฒด

โœ… Dense(units, activation=None, use_bias=True, input_shape): ์œ ๋‹›๋“ค์ด ์ „๋ถ€ ์—ฐ๊ฒฐ๋œ ๋ ˆ์ด์–ด

  • use_bias: ๋ฐ”์ด์–ด์Šค ์‚ฌ์šฉ ์œ ๋ฌด

โœ… Embedding(input_dim, output_dim): ์ž์—ฐ์–ด ์ฒ˜๋ฆฌ์˜ ์ฒซ๋‹จ๊ณ„์— ์‚ฌ์šฉ๋˜๋Š” ์ž„๋ฒ ๋”ฉ ๋ ˆ์ด์–ด


์†์‹ค ํ•จ์ˆ˜(loss function)

โœ… MeanSquaredError: ์ •๋‹ต ๋ ˆ์ด๋ธ”๊ณผ ์˜ˆ์ธก๊ฐ’ ์‚ฌ์ด์˜ ํ‰๊ท  ์ œ๊ณฑ ์˜ค์ฐจ๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค. [โ€˜mseโ€™]

MSE=1nโˆ‘i=1n(yiโˆ’y^i)2\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2

MeanSquaredError
(1st sample) 1 + 0 = 1
(2nd sample) 1 + 0 = 1
์ „์ฒด ํ•ฉ = 1 + 1 = 2
์ „์ฒด ์š”์†Œ ์ˆ˜ = 4
MSE = 2 / 4 = 0.5

โœ… BinaryCrossentropy: ์ •๋‹ต ๋ ˆ์ด๋ธ”๊ณผ ์˜ˆ์ธก ๋ ˆ์ด๋ธ” ๊ฐ„์˜ ๊ต์ฐจ ์—”ํŠธ๋กœํ”ผ ์†์‹ค์„ ๊ณ„์‚ฐํ•œ๋‹ค(์˜ˆ๋ฅผ ๋“ค์–ด์„œ ๊ฐ•์•„์ง€, ๊ฐ•์•„์ง€ ์•„๋‹˜). [โ€˜binary_crossentropyโ€™]

BCE=โˆ’1nโˆ‘[ylogโก(y^)+(1โˆ’y)logโก(1โˆ’y^)]\text{BCE} = -\frac{1}{n} \sum \left[ y \log(\hat{y}) + (1 - y) \log(1 - \hat{y}) \right]

BinaryCrossentropy
(1) y=0, p=0.6 -> -log(0.4) โ‰ˆ 0.916
(2) y=1, p=0.4 -> -log(0.4) โ‰ˆ 0.916
(3) y=0, p=0.4 -> -log(0.6) โ‰ˆ 0.511
(4) y=0, p=0.6 -> -log(0.4) โ‰ˆ 0.916
์ดํ•ฉ = 3.259 / 4 = 0.8149

import matplotlib.pyplot as plt
import tensorflow as tf; import numpy as np

y_true = [[0., 1.], [0., 0.]]
y_pred = [[1., 1.], [1., 0.]]
mse = tf.keras.losses.MeanSquaredError()
print(mse(y_true, y_pred).numpy()) # 0.5

y_true = [[0., 1.], [0., 0.]]
y_pred = [[0.6, 0.4], [0.4, 0.6]]
bce = tf.keras.losses.BinaryCrossentropy()
print(bce(y_true, y_pred).numpy()) # 0.8149245

โœ… CategoricalCrossentropy: ์ •๋‹ต ๋ ˆ์ด๋ธ”๊ณผ ์˜ˆ์ธก ๋ ˆ์ด๋ธ” ๊ฐ„์˜ ๊ต์ฐจ ์—”ํŠธ๋กœํ”ผ ์†์‹ค์„ ๊ณ„์‚ฐํ•œ๋‹ค(์˜ˆ๋ฅผ ๋“ค์–ด์„œ ๊ฐ•์•„์ง€, ๊ณ ์–‘์ด, ํ˜ธ๋ž‘์ด). ์ •๋‹ต ๋ ˆ์ด๋ธ”์€ ์›ํ•ซ ์ธ์ฝ”๋”ฉ์œผ๋กœ ์ œ๊ณต [โ€˜categorical_crossentropyโ€™]

CCE=โˆ’โˆ‘i=1nyilogโก(y^i)\text{CCE} = -\sum_{i=1}^{n} y_i \log(\hat{y}_i)

CategoricalCrossentropy
(1st sample) y = [0,1,0], p = [0.05, 0.95, 0]
--> -log(0.95) โ‰ˆ 0.051
(2nd sample) y = [0,0,1], p = [0.1, 0.8, 0.1]
--> -log(0.1) โ‰ˆ 2.302
์ดํ•ฉ = 0.051 + 2.302 = 2.353 / 2 = 1.1769

โœ… SparseCategoricalCrossentropy: ์ •๋‹ต ๋ ˆ์ด๋ธ”๊ณผ ์˜ˆ์ธก ๋ ˆ์ด๋ธ” ๊ฐ„์˜ ๊ต์ฐจ ์—”ํŠธ๋กœํ”ผ ์†์‹ค์„ ๊ณ„์‚ฐํ•œ๋‹ค. (์˜ˆ๋ฅผ ๋“ค์–ด์„œ ๊ฐ•์•„์ง€, ๊ณ ์–‘์ด, ํ˜ธ๋ž‘์ด)
์ •๋‹ต ๋ ˆ์ด๋ธ”์€ ์ •์ˆ˜๋กœ ์ œ๊ณต [โ€˜sparse_categorical_crossentropyโ€™]

SparseCategoricalCrossentropy
(1st sample) y=1 โ†’ p=0.95 โ†’ -log(0.95) โ‰ˆ 0.051
(2nd sample) y=2 โ†’ p=0.1 โ†’ -log(0.1) โ‰ˆ 2.302
ํ‰๊ท  = (0.051 + 2.302) / 2 = 1.1769

y_true = [[0, 1, 0], [0, 0, 1]] # ์›-ํ•ซ ํ‘œํ˜„์œผ๋กœ ๋ถ€๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
y_pred = [[0.05, 0.95, 0], [0.1, 0.8, 0.1]]
cce = tf.keras.losses.CategoricalCrossentropy()
print(cce(y_true, y_pred).numpy()) # 1.1769392
y_true = np.array([1, 2]) # ์ •์ˆ˜๋กœ ๋ถ€๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
y_pred = np.array([[0.05, 0.95, 0], [0.1, 0.8, 0.1]])
scce = tf.keras.losses.SparseCategoricalCrossentropy()
print(scce(y_true, y_pred).numpy()) # 1.1769392

์ธก์ • ํ•ญ๋ชฉ(metrics)

  • Accuracy: ์ •ํ™•๋„์ด๋‹ค. ์˜ˆ์ธก๊ฐ’์ด ์ •๋‹ต ๋ ˆ์ด๋ธ”๊ณผ ๊ฐ™์€ ํšŸ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค.
  • categorical_accuracy: ๋ฒ”์ฃผํ˜• ์ •ํ™•๋„์ด๋‹ค. ์‹ ๊ฒฝ๋ง์˜ ์˜ˆ์ธก๊ฐ’์ด ์›-ํ•ซ ๋ ˆ์ด๋ธ”๊ณผ ์ผ์น˜ํ•˜๋Š” ๋นˆ๋„๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค.
m = tf.keras.metrics.Accuracy()
m.update_state( [[1], [2], [3], [4]], [[0], [2], [3], [4]])
print(m.result().numpy()) # 0.75

m = tf.keras.metrics.CategoricalAccuracy()
m.update_state([[0, 0, 1], [0, 1, 0]], [[0.1, 0.9, 0.8], [0.05, 0.95, 0]])
print(m.result().numpy()) # 0.5

์˜ตํ‹ฐ๋งˆ์ด์ €(optimizer)

์†์‹ค ํ•จ์ˆ˜๋ฅผ ๋ฏธ๋ถ„ํ•˜์—ฌ์„œ ๊ฐ€์ค‘์น˜๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ณ€๊ฒฝํ•˜๋Š” ๊ฐ์ฒด

  • SGD: ํ™•๋ฅ ์  ๊ฒฝ์‚ฌ ํ•˜๊ฐ•๋ฒ•(Stochastic Gradient Descent, SGD), Nesterov ๋ชจ๋ฉ˜ํ…€์„ ์ง€์›, ์–•์€ ์‹ ๊ฒฝ๋ง ์ ํ•ฉ
opt = tf.keras.optimizers.SGD(learning_rate=0.1, momentum=0.9)
  • Adagrad: ๊ฐ€๋ณ€ ํ•™์Šต๋ฅ ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•, SGD๋ฅผ ์ง„๋ณด์‹œํ‚จ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•
  • Adadelta: ๋ชจ๋ฉ˜ํ…€์„ ์ด์šฉํ•˜์—ฌ ๊ฐ์†Œํ•˜๋Š” ํ•™์Šต๋ฅ  ๋ฌธ์ œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” Adagrad์˜ ๋ณ€ํ˜•
  • RMSprop: Adagrad์— ๋Œ€ํ•œ ์ˆ˜์ •ํŒ
  • Adam: ๊ธฐ๋ณธ์ ์œผ๋กœ (RMSprop + ๋ชจ๋ฉ˜ํ…€), ์ธ๊ธฐ์žˆ์Œ

ํ™œ์„ฑํ™” ํ•จ์ˆ˜(activation function)

  • sigmoid
  • relu(Rectified Linear Unit)
  • softmax
  • tanh
  • selu(Scaled Exponential Linear Unit)
  • softplus


๐Ÿ’ก ํ•˜์ดํผ ๋งค๊ฐœ๋ณ€์ˆ˜

์‹ ๊ฒฝ๋ง์—์„œ ํ•™์Šต ์ „์— ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์„ค์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฐ’๋“ค์„ ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ๋ผ๊ณ  ํ•œ๋‹ค.
์ด ๊ฐ’๋“ค์€ ๋ชจ๋ธ์˜ ์„ฑ๋Šฅ๊ณผ ํ•™์Šต ํšจ์œจ์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค.

๋Œ€ํ‘œ์ ์ธ ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ

ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ์„ค๋ช…
์€๋‹‰์ธต์˜ ๊ฐœ์ˆ˜์˜ค์ฐจ๊ฐ€ ๋” ์ด์ƒ ๊ฐœ์„ ๋˜์ง€ ์•Š์„ ๋•Œ๊นŒ์ง€ ๋ ˆ์ด์–ด๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
์œ ๋‹›์˜ ๊ฐœ์ˆ˜์œ ๋‹› ์ˆ˜๊ฐ€ ๋งŽ์„์ˆ˜๋ก ํ‘œํ˜„๋ ฅ์ด ์ข‹์•„์ง€์ง€๋งŒ ๊ณผ์ ํ•ฉ์— ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.
ํ•™์Šต๋ฅ  (learning rate)๋„ˆ๋ฌด ํฌ๋ฉด ๋ฐœ์‚ฐํ•˜๊ณ , ๋„ˆ๋ฌด ์ž‘์œผ๋ฉด ํ•™์Šต์ด ๋А๋ฆฌ๋‹ค. ์ ์‘์  ํ•™์Šต๋ฅ ์ด ํšจ๊ณผ์ ์ด๋‹ค.
๋ชจ๋ฉ˜ํ…€ (momentum)์ง„๋™์„ ๋ฐฉ์ง€ํ•˜์—ฌ ์•ˆ์ •์ ์ธ ํ•™์Šต์„ ๋„์™€์ค€๋‹ค. ๋ณดํ†ต 0.5~0.9 ์‚ฌ์ด์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•œ๋‹ค.
๋ฏธ๋‹ˆ ๋ฐฐ์น˜ ํฌ๊ธฐ์ผ๋ฐ˜์ ์œผ๋กœ 32๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ, 64, 128, 256 ๋“ฑ๋„ ์‚ฌ์šฉ๋œ๋‹ค.
์—ํฌํฌ ์ˆ˜ (epochs)๊ฒ€์ฆ ์ •ํ™•๋„๊ฐ€ ๊ฐ์†Œํ•˜๊ธฐ ์‹œ์ž‘ํ•  ๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณตํ•œ๋‹ค.

๐Ÿ” ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ ํƒ์ƒ‰ ๋ฐฉ๋ฒ•

  • ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ
    ํ”„๋ ˆ์ž„์›Œํฌ(์˜ˆ: Keras, sklearn)์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

  • ์ˆ˜๋™ ๊ฒ€์ƒ‰
    ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๊ฐ’์„ ์ง€์ •ํ•˜๋ฉด์„œ ๊ฒฝํ—˜์ ์œผ๋กœ ์กฐ์ •ํ•œ๋‹ค.

  • ๊ทธ๋ฆฌ๋“œ ๊ฒ€์ƒ‰ (Grid Search)
    ๊ฐ ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ›„๋ณด ๊ฐ’์„ ์ •ํ•˜๊ณ , ๋ชจ๋“  ์กฐํ•ฉ์„ ์‹คํ—˜ํ•˜์—ฌ ๊ฐ€์žฅ ์„ฑ๋Šฅ์ด ์ข‹์€ ์กฐํ•ฉ์„ ์ฐพ๋Š”๋‹ค.
    sklearn์˜ GridSearchCV๋ฅผ ํ†ตํ•ด ์†์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋žœ๋ค ๊ฒ€์ƒ‰ (Random Search)
    ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๋ฌด์ž‘์œ„๋กœ ์„ ํƒํ•˜์—ฌ ์—ฌ๋Ÿฌ ์กฐํ•ฉ์„ ์‹คํ—˜ํ•œ๋‹ค.
    ๊ทธ๋ฆฌ๋“œ ๊ฒ€์ƒ‰๋ณด๋‹ค ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“ฆ ๊ทธ๋ฆฌ๋“œ ๊ฒ€์ƒ‰ ์˜ˆ์ œ

# ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋กœ๋“œ
# !pip install scikeras
# !pip install scikit-learn==1.3.2
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from sklearn.model_selection import GridSearchCV
from scikeras.wrappers import KerasClassifier

# ๋ฐ์ดํ„ฐ ์„ธํŠธ ์ค€๋น„
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()

train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255

train_labels = tf.keras.utils.to_categorical(train_labels)
test_labels = tf.keras.utils.to_categorical(test_labels)
# ์‹ ๊ฒฝ๋ง ๋ชจ๋ธ ๊ตฌ์ถ•
def build_model():
    network = tf.keras.models.Sequential()
    network.add(tf.keras.layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
    network.add(tf.keras.layers.Dense(10, activation='sigmoid'))
    network.compile(
        optimizer='rmsprop',
        loss='categorical_crossentropy',
        metrics=['accuracy']
    )
    return network

# ํ•˜์ดํผ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋”•์…”๋„ˆ๋ฆฌ
param_grid = {
    'epochs': [1, 2, 3],        # ์—ํฌํฌ ์ˆ˜: 1, 2, 3
    'batch_size': [32, 64]      # ๋ฐฐ์น˜ ํฌ๊ธฐ: 32, 64
}
# ์ผ€๋ผ์Šค ๋ชจ๋ธ์„ sklearn์—์„œ ์‚ฌ์šฉํ•˜๋„๋ก ํฌ์žฅํ•œ๋‹ค.
model = KerasClassifier(build_fn=build_model, verbose=1)

# ๊ทธ๋ฆฌ๋“œ ๊ฒ€์ƒ‰
gs = GridSearchCV(
    estimator=model,
    param_grid=param_grid,
    cv=3,
    n_jobs=-1
)

# ๊ทธ๋ฆฌ๋“œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
grid_result = gs.fit(train_images, train_labels)
print(grid_result.best_score_)
print(grid_result.best_params_)
...
Epoch 3/3
938/938 [==============================] - 3s 4ms/step - loss: 0.0664 - accuracy: 0.9799
157/157 [==============================] - 0s 939us/step
0.968666672706604
{'batch_size': 64, 'epochs': 3}

๐Ÿท๏ธ Summary

์ผ€๋ผ์Šค๋ฅผ ํ™œ์šฉํ•œ ์‹ ๊ฒฝ๋ง ๋ชจ๋ธ ๊ตฌ์„ฑ๊ณผ ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ
Sequential ๋ชจ๋ธ์€ ์ผ€๋ผ์Šค์—์„œ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ์‹ ๊ฒฝ๋ง ๊ตฌ์„ฑ ๋ฐฉ์‹์ด๋ฉฐ, Dense ๋ ˆ์ด์–ด๋ฅผ ํ†ตํ•ด ์™„์ „ ์—ฐ๊ฒฐ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ ๋‹ค.

ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์€๋‹‰์ธต ์ˆ˜, ์œ ๋‹› ์ˆ˜, ํ•™์Šต๋ฅ  ๋“ฑ ๋ชจ๋ธ ์™ธ๋ถ€์—์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์„ค์ •ํ•˜๋Š” ๊ฐ’์ด๋ฉฐ, ๊ทธ๋ฆฌ๋“œ ๊ฒ€์ƒ‰ ๋“ฑ์„ ํ†ตํ•ด ์ตœ์ ๊ฐ’์„ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€