09_DL(Deep_Learning)

04_신경망 모델 훈련 도구

chuu_travel 2025. 4. 23. 17:39
728x90
신경망 모델 훈련 도구

 

 

  • 머신러닝 알고리즘은 모델의 구조가 어느정도 고정되어 있음
    • 좋은 성능을 내기 위해서는 매개변수를 조정하고 훈련하는 과정을 반복
  • 딥러닝은 구조를 직접 설계해야함
    • 좋은 성능을 내기 위해 다룰 수 있는 다양한 개념과 도구들을 이해할 필요가 있음

 

손실 곡선

  • 케라스의 fit() 메서드는 History 클래스의 객체를 반환
    • History 객체 : 훈련 과정에서 계산한 지표(손실과 정확도 값)가 저장되어 있음
    • 이 값을 사용하여 그래프를 그려서 훈련 성과를 확인
from tensorflow import keras
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np

 

(x_train, y_train), (x_test, y_test) = keras.datasets.fashion_mnist.load_data()

 

np.unique(x_test)
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,  30,  31,  32,  33,  34,  35,  36,  37,  38,
        39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
        52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,
        65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,
        78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,
        91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103,
       104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
       117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
       130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
       143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
       156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
       169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
       182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
       195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
       208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
       221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
       234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
       247, 248, 249, 250, 251, 252, 253, 254, 255], dtype=uint8)

 

 

# 0부터 255까지의 범위의 데이터를 0~1 사이의 값으로 스케일링 하기 위해 255로 나눔(0~1사이의 값을 잘 학습하는 경향이 있음)
scaled_train = x_train / 255
scaled_train, scaled_val, y_train, y_val = train_test_split(scaled_train, y_train, test_size = 0.2,
                                                            stratify = y_train, random_state = 26)

 

# 모델 정의 함수
def model_fn(a_layer = None):
    model = keras.Sequential()
    model.add(keras.Input(shape = (28, 28)))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(100, activation = "relu"))

    if a_layer:
        model.add(a_layer)

    model.add(keras.layers.Dense(10, activation = "softmax"))

    return model

 

model = model_fn()
model.summary()

 

model.compile(loss = "sparse_categorical_crossentropy", metrics = ["accuracy"])

 

history = model.fit(scaled_train, y_train, epochs = 5, batch_size = 32) # batch_size는 임의값 설정

 

# history 객체 확인
print(history.history)
print(history.history.keys())
# 원래는 loss만 계산되고 metrics = ["accuracy"] 를 추가해줬기 때문에 accuracy도 계산됨
{'accuracy': [0.8141666650772095, 0.8593541383743286, 0.8714583516120911, 0.8800625205039978, 0.8853124976158142], 'loss': [0.5289159417152405, 0.3923627436161041, 0.3565346598625183, 0.3348881006240845, 0.3183060884475708]}
dict_keys(['accuracy', 'loss'])

 

  • 기본적으로 손실을 계산하고 compile에서 accuracy를 추가하여서 loss와 accuracy가 포함됨

 

plt.figure()

plt.plot(history.history["loss"])

plt.xlabel("epoch")
plt.ylabel("loss")
plt.show()

 

 

plt.figure()

plt.plot(history.history["accuracy"])

plt.xlabel("epoch")
plt.ylabel("loss")
plt.show()

 

# epoch를 20으로 늘려서 그래프 확인
model = model_fn()
model.compile(loss = "sparse_categorical_crossentropy", metrics = ["accuracy"])
history = model.fit(scaled_train, y_train, epochs = 20, batch_size = 32)

 

 

plt.figure()

plt.plot(history.history["loss"], label = "loss")
plt.plot(history.history["accuracy"], label = "acc")

plt.xlabel("epoch")
plt.legend()
plt.show()

 

 

검증 손실

  • 에포크에 따른 과대적합과 과소적합을 파악하려면 검증 세트에 대한 점수도 확인해야함
  • 케라스에서는 fit() 메서드에 검증 데이터를 전달할 수 있음
model = model_fn()
model.compile(loss = "sparse_categorical_crossentropy", metrics = ["accuracy"])
history = model.fit(scaled_train, y_train, epochs = 20, validation_data = (scaled_val, y_val), batch_size = 32)

 

 

print(history.history.keys())
dict_keys(['accuracy', 'loss', 'val_accuracy', 'val_loss'])

 

plt.figure()

plt.plot(history.history["loss"], label = "train_loss")
plt.plot(history.history["val_loss"], label = "val_loss")
plt.plot(history.history["accuracy"], label = "train_acc")
plt.plot(history.history["val_accuracy"], label = "val_acc")

plt.xlabel("epoch")
plt.legend()
plt.show()

  • 훈련 손실은 계속 감소하는데 검증 손실은 감소하다가 상승하여 과대적합 모델이 만들어짐
    • 검증 손실이 상승하는 시점을 가능한 한 뒤로 늦출 필요가 있음

 

# Adam 옵티마이저 선택
model = model_fn()
model.compile(loss = "sparse_categorical_crossentropy", metrics = ["accuracy"], optimizer = "adam")
history = model.fit(scaled_train, y_train, epochs = 20, validation_data = (scaled_val, y_val), 
                    batch_size = 32)

 

plt.figure()

plt.plot(history.history["loss"], label = "train_loss")
plt.plot(history.history["val_loss"], label = "val_loss")
plt.plot(history.history["accuracy"], label = "train_acc")
plt.plot(history.history["val_accuracy"], label = "val_acc")

plt.xlabel("epoch")
plt.legend()
plt.show()

  • RMSprop을 사용할 때보다 검증 손실 그래프의 감소 추세가 길게 이어지고 있어 Adam 옵티마이저가 이 데이터셋에 잘 맞는 것으로 보임

 

드롭아웃(dropout)

 

  • 훈련 과정에서 층의 일부 유닛을 랜덤하게 끄는 것으로(출력을 0으로 만드는 것으로) 과대적합을 방지
    • 특정 유닛에 과대하게 의존하는 것을 줄일 수 있음
    • 일부 유닛의 출력이 없을 수 있다는 것을 감안하여 학습하게 됨
      • 모든 입력값에 대해 충분한 주의를 기울이는 방향으로 학습
  • 드롭아웃은 케라스에서 Dropout클래스로 제공
  • 특정 층 뒤에 드롭아웃을 두어 해당 층의 출력을 랜덤하게 0으로 만듦
  • Flatten과 마찬가지로 층처럼 사용되지만 훈련되는 모델 파라미터가 없음

 

model = model_fn(keras.layers.Dropout(0.3)) # 30% 드롭아웃
model.summary()

 

  • 드롭아웃 층은 입력과 출력의 크기가 같음
    • 일부 유닛의 출력을 0으로 만들지만 전체 출력 배열의 크기를 바꾸지 않음
  • 모델 훈련이 끝난 후에 평가나 예측을 수행할 때에는 드롭아웃을 적용하지 않아야 함
    • 훈련된 모든 유닛을 사용해야 올바른 예측을 수행할 수 있기 때문에
    • 케라스에서는 모델을 평가와 예측에 사용할 때에는 자동으로 드롭아웃을 적용하지 않음

 

model.compile(optimizer = "adam", loss = "sparse_categorical_crossentropy", metrics = ["accuracy"])
history = model.fit(scaled_train, y_train, epochs = 20, validation_data = (scaled_val, y_val),
                    batch_size = 32)

 

plt.figure()

plt.plot(history.history["loss"], label = "train_loss")
plt.plot(history.history["val_loss"], label = "val_loss")
plt.plot(history.history["accuracy"], label = "train_acc")
plt.plot(history.history["val_accuracy"], label = "val_acc")

plt.xlabel("epoch")
plt.legend()
plt.show()

  • 앞선 방법들에 비해 loss와 acc가 비교적 일정하게 유지됨

 

 

모델 저장과 복원

  • 케라스 모델은 모델의 파라미터를 저장하는 save_weights() 메서드를 제공
  • 모델 구조와 모델 파라미터를 제공하는 save() 메서드도 제공

 

model.save_weights("./model/model.weights.h5") # .weights.h5 : 확장자
model.save("./model/model-whole.keras") # .keras : 전체 모델을 저장할 때의 확장자

 

 

모델 파라미터만 읽어오기

model_weight = model_fn(keras.layers.Dropout(0.3))
model_weight.load_weights("./model/model.weights.h5")

 

  • load_weights() 메서드를 사용하면 save_weights() 로 저장했던 모델과 정확히 같은 구조를 가져야함
  • 텐서플로 버전에 따라 load_weights() 의 버그로 인해 evaluate()를 사용하기 전에 compile()을 반드시 실행해야 하는 경우가 있음

 

scaled_val.shape

 

model_weight.predict(scaled_val).shape

(12000, 10)

 

 

# 첫 번째 검증 데이터에 대한 예측값
model_weight.predict(scaled_val)[0]

 

array([5.6639659e-01, 3.8576734e-05, 1.3055797e-02, 2.5340202e-01,
       3.0511387e-03, 6.0071110e-05, 1.6313180e-01, 2.1901755e-10,
       8.6395926e-04, 1.9496866e-08], dtype=float32)

 

y_val[0]
0

 

 

val_labels = np.argmax(model_weight.predict(scaled_val), axis = -1)

 

 

val_labels[0]
0

 

y_val[0]
0

 

 

val_labels == y_val
array([ True,  True,  True, ...,  True,  True,  True])

 

 

np.mean(val_labels == y_val)
0.8881666666666667

 

 

  • 케라스의 predict() 메서드는 모델의 예측을 수행
    • 종속변수가 10개의 클래스로 이루어져있기 때문에 각 클래스에 대한 확률을 반환
  • predict() 결과에서 가장 큰 값을 고르기 위해 argmax() 함수를 사용
    • 배열에서 가장 큰 값의 인덱스를 반환
    • 몇 번째 클래스에 대한 확률값이 가장 큰지 반환
      • 몇 번째 클래스로 예측했는지 알 수 있음
  • argmax() 함수의 axis = -1 은 배열의 마지막 차원을 따라서 최댓값을 고름
    • axis = 1 이면 열을 따라 각 행의 최댓값의 인덱스를 선택
    • axis = 0 이면 행을 따라 각 열의 최댓값의 인덱스를 선택
  • argmax()로 고른 인덱스와 타깃 인덱스를 비교하여 같으면 1, 다르면 0
    • 위 값의 평균을 구하면 정확도가 됨

 

 

모델 전체 읽어오기

model_whole = keras.models.load_model("./model/model-whole.keras")

 

model_whole.evaluate(scaled_val, y_val)

[0.3179406225681305, 0.8881666660308838]

 

 

콜백(callback)

  • 훈련 과정 중간에 어떤 작업을 수행할 수 있게 하는 객체
  • fit() 메서드의 callbacks 매개변수에 리스트로 전달하여 사용

 

ModelCheckpoint 콜백

  • 최상의 검증 점수를 만드는 모델을 저장

 

checkpoint_cb = keras.callbacks.ModelCheckpoint("./model/best-model.keras", save_best_only = True) 
# save_best_only = True : 최고의 모델(최저epoch)일때만 저장

 

model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer = "adam", loss = "sparse_categorical_crossentropy", metrics = ["accuracy"])
history = model.fit(scaled_train, y_train, epochs = 20, validation_data = (scaled_val, y_val),
                    callbacks = [checkpoint_cb], batch_size = 32)

  • 모델이 훈련한 후에 best-model.keras에 최상의 val_loss를 낸 모델이 저장됨
model.evaluate(scaled_val, y_val)
# 20에포크가 끝난 상태가 저장되어있음

[0.32728010416030884, 0.8872500061988831]

 

 

best_model = keras.models.load_model("./model/best-model.keras")
best_model.evaluate(scaled_val, y_val)
# 최상의 검증상태가 저장됨

[0.31871214509010315, 0.887416660785675]
  • ModelCheckpoint 콜백이 가장 낮은 검증 손실값의 모델을 자동으로 저장해주어 편하지만 여전히 20번의 훈련을 해야해서 불편함이 있음

 

EarlyStopping 콜백

  • 과대적합이 시작되기 전에(검증점수가 상승할 때)훈련을 미리 중지하는 것을 조기 종료(early stopping)라고 부름
  • 훈련 에포크 횟수를 제한하는 역할이지만 모델이 과적합되는 것을 막아 주기 때문에 규제 방법 중 하나로 여겨지기도 함
early_stopping_cb = keras.callbacks.EarlyStopping(patience = 2, restore_best_weights = True)

 

  • patience
    • 검증 점수가 향상되지 않더라도 지켜볼 에포크 횟수
    • patience 가 2라면 2번 연속 검증 점수가 향상되지 않으면 훈련 중지
  • restore_best_weights
    • 가장 낮은 검증 손실을 낸 모델의 상태로 되돌림

 

model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer = "adam", loss = "sparse_categorical_crossentropy", metrics = ["accuracy"])
history = model.fit(scaled_train, y_train, epochs = 20, validation_data = (scaled_val, y_val),
                    callbacks = [checkpoint_cb, early_stopping_cb], batch_size = 32)

 

# 몇 번째 에포크에서 중지되었는지 확인
print(early_stopping_cb.stopped_epoch)
4

 

  • 5번째 에포크까지 훈련한 후 중지됨
    • patience가 2이기 때문에 3번째 에포크 때의 모델이 최상의 모델임

 

model.evaluate(scaled_val, y_val)

[0.36178240180015564, 0.871999979019165]

 

 

plt.figure()

plt.plot(history.history["loss"], label = "train_loss")
plt.plot(history.history["val_loss"], label = "val_loss")
plt.plot(history.history["accuracy"], label = "train_acc")
plt.plot(history.history["val_accuracy"], label = "val_acc")

plt.xlabel("epoch")
plt.legend()

plt.xticks(ticks = range(20))

plt.show()

 

 

validation_split

  • 훈련 데이터를 자체적으로 훈련/검증 셋으로 나누어서 훈련/검증을 수행
(x_train, y_train), (x_test, y_test) = keras.datasets.fashion_mnist.load_data()
scaled_train = x_train / 255
scaled_test = x_test / 255

 

cp_cb = keras.callbacks.ModelCheckpoint("./model/best-model.keras", save_best_only = True)
es_cb = keras.callbacks.EarlyStopping(patience = 2, restore_best_weights = True)

 

model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer = "adam", loss = "sparse_categorical_crossentropy", metrics = ["accuracy"])
history = model.fit(scaled_train, y_train, epochs = 20, validation_split = 0.2, batch_size = 32,
                    callbacks = [cp_cb, es_cb])

# validation_split = 0.2 따로 20%는 테스트데이터로 빼놓음

 

 

plt.figure()

plt.plot(history.history["loss"], label = "train_loss")
plt.plot(history.history["val_loss"], label = "val_loss")
plt.plot(history.history["accuracy"], label = "train_acc")
plt.plot(history.history["val_accuracy"], label = "val_acc")

plt.xlabel("epoch")
plt.legend()

plt.xticks(ticks = range(20))

plt.show()

 

model.evaluate(scaled_test, y_test)

[0.3501654863357544, 0.8744999766349792]
728x90

'09_DL(Deep_Learning)' 카테고리의 다른 글

06_초음파 광물 예측  (0) 2025.04.24
05_다중분류(Iris)  (0) 2025.04.23
월별 사인(sin) 코사인(cos) 시각화  (0) 2025.04.21
03_다층 인공신경망  (0) 2025.04.18
02_단층 인공신경망  (1) 2025.04.18