ch0nny_log

[빅데이터분석] 딥러닝_10. 3층 신경망 전체 코드 구현 / 언더 피팅과 오버피팅을 방지하는 방법 본문

카테고리 없음

[빅데이터분석] 딥러닝_10. 3층 신경망 전체 코드 구현 / 언더 피팅과 오버피팅을 방지하는 방법

chonny 2024. 10. 15. 09:45

▣ 5. 14 텐써 플로우로 3층 신경망 전체 코드 구현

 

■ 실습1. 아래와 같이  신경망을 구현하시오 !

 입력층 ----> 은닉1층 ---> 은닉2층 ----> 은닉3층 -----> 출력층
 (784개)         (64개)           (32개)           (64개)          (10개)

위의 다른 코드들은 다 똑같고 아래의 모델 구성 층만 다음과 같이 구성합니다.

model = Sequential()
model.add(Flatten( input_shape=(784, ) )  ) # 입력층 0층
model.add(Dense(64, activation='sigmoid') )  # 은닉층(1층)
model.add(Dense(32, activation='sigmoid') )  # 은닉층(2층)
model.add(Dense(64, activation='sigmoid') )  # 은닉층(3층)
model.add(Dense(10, activation='softmax') )  # 출력층(4층)


전체코드:

#1. mnist 필기체 데이터 불러오기 
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 차원 축소하기
x_train = x_train.reshape((x_train.shape[0], 784)).astype('float32') / 255
x_test = x_test.reshape((x_test.shape[0], 784)).astype('float32') / 255

# 레이블을 one-hot encoding으로 변환하기
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

#2. 신경망 구성에 필요한 모듈 불러오기 
import tensorflow as tf
tf.random.set_seed(777)
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.losses import categorical_crossentropy

#3. 모델 구성하기 
model = Sequential()


model = Sequential()
model.add(Flatten( input_shape=(784, ) )  ) # 입력층 0층
model.add(Dense(64, activation='sigmoid') )  # 은닉층(1층)
model.add(Dense(32, activation='sigmoid') )  # 은닉층(2층)
model.add(Dense(64, activation='sigmoid') )  # 은닉층(3층)
model.add(Dense(10, activation='softmax') )  # 출력층(4층)

# 모델 준비하기
model.compile(optimizer=SGD(), 
              loss=categorical_crossentropy,  # 분류를 위한 오차함수
              metrics=['accuracy'])  # 분류 문제에 맞는 평가 지표 사용

# 학습 시키기 
model.fit(x_train, y_train, epochs=200, validation_data=(x_test, y_test))



875/1875 ━━━━━━━━━━━━━━━━━━━━ 1s 705us/step - accuracy: 0.9934 - loss: 0.0317 

수업시간에는 수업을 나가야하니까 200 에폭으로 하시고
실제로 신경망 구현할 때는 넉넉히 500 에폭을 쓰세요 !

 

 

 

 

 

 


■ 실습2.  위의 모델을 평가하는 코드를 따로 실행하시오 ! 

model.evaluate( x_test,  y_test )



[0.10080892592668533, 0.9706000089645386] 
    ↑                             ↑
  오차                         정확도

크게 오버피팅이 발생하지 않았습니다. 

 

 

 

■ 실습3. 위의 훈련과정을 시각화 해서 학습을 계속 해도 진전이 있을지를 확인하시오 !

#위의 코드의 모델 훈련하는 코드를 변경을 합니다. 

history = model.fit( x_train, y_train,  
                        epochs= 100, 
                        batch_size=100,
                        validation_data = (x_test, y_test)  )
                        

# 훈련 중간중간 마다 테스트 데이터에 대한 정확도를 같이 확인해봅니다.
# 그래서 정확도가 높아지면서 오버피팅이 발생하는지 추세를 확인합니다. 

# 예시로 accuracy 리스트 값을 사용
train_acc_list = history.history['accuracy']  # 훈련 데이터의 정확도 리스트
test_acc_list = history.history['val_accuracy']  # 테스트 데이터의 정확도 리스트

# 에포크 수에 따른 x축 데이터 생성
x = np.arange(len(train_acc_list))

# 시각화
plt.figure(figsize=(8, 6))  # 그래프 크기 설정
plt.plot(x, train_acc_list, label='Train Accuracy')  # 훈련 데이터 정확도 라인
plt.plot(x, test_acc_list, label='Test Accuracy', linestyle='--')  # 테스트 데이터 정확도 라인

# 그래프의 축, 범위 및 레이블 설정
plt.ylim(0.8, 1.0)  # y축 범위를 0.8에서 1.0까지로 설정
plt.xlabel('Epochs', fontsize=12)  # x축 레이블
plt.ylabel('Accuracy', fontsize=12)  # y축 레이블

# 범례 추가 및 위치 설정
plt.legend(loc='lower right', fontsize=12)

# 타이틀 추가 (선택 사항)
plt.title('Train vs Test Accuracy', fontsize=14)

# 그래프 출력
plt.show()

 

                     
전체코드:

https://cafe.daum.net/oracleoracle/SpOP/213


▣ 6장. 언더 피팅과 오버피팅을 방지하는 방법들 소개

 

 " 학습이 더 잘되도록 하는 방법들 소개하는 챕터"

1. 언더피팅을 방지하는 방법들
          - 가중치 초기값 설정의 중요성
          - 경사하강법의 종류
          - 배치정규화

2. 오버피팅을 방지하는 방법들 
         - 드롭아웃 (신경만 뉴런삭제)
         - L2 정규화(가중치 감소)

 

 

 

■ 가중치 초기값 설정의 중요성

 


 신경망이 제일 처음 만들어질 때 가중치의 숫자값이 랜덤으로 생성이 됩니다.

  입력층 -----------------> 은닉층 -----------------> 출력층
   768                          100                            10

 (100, 768)  ◎  ( 768, 100 ) =  (100, 100 ) ◎ (100, 10 ) = (100,10)
                         ↑                                 ↑
                     은닉층의 가중치 행렬(W1)    출력층쪽의 가중치(W2)

 가중치 행렬의 숫자가 W1 만해도 76800개가 만들어지고 W2 는 1000개의 숫자가 처음에 랜덤으로 생성이 됩니다.
 학습이 잘되게 하려면 이 숫자들이 정규분포 형태를 보여야합니다. 

 독립변수들의 데이터가 정규분포 형태이고 종속변수도 정규분포 형태면 학습이 아주 잘됩니다.  그런데 정규분포 형태가 아니면 학습이 원만하지 않을거다라고 예상을 하고 있어야합니다. 

 그런데 가중치도 마찬가지고 처음부터 정규분포 형태로 구성되어야 학습이 잘됩니다.
 학습 시킬 데이터는 정규분포가 아니어도 어쩔수 없지만 가중치는 정규분포로 구성할 수 있습니다. 

 


■ 이론설명1.  생성되는 가중치 숫자들의 표준편차가 너무 큰 경우

그림 6-10 


학습이 잘 안되는 가중치 입니다.  랜덤으로 생성된 가중치 숫자들이 앞과 뒤에 
다 몰려있습니다. 이런 현상을 다른 예로 들면 어느반 학생들의 시험점수가 있는데
아주 잘하는 학생들과 아주 못하는 학생들로 구성된 경우입니다. 
시험문제가 너무 어려우면 이런 현상이 발생합니다. 
 
예:  W1 = 100 * np.random.randn( 784 , 100 ) 

■ 이론설명2. 생성되는 가중치 숫자들의 표준편차가 너무 작은 경우 

그림 6-11

가중치의 숫자들이 전부 평균값에 몰려 있습니다. 이렇게 되면 학습이 잘 안됩니다.
시험문제가 너무 쉬우면 학생들의 점수가 전부 평균에 가까워집니다. 

예:  W1 = 0.00001 * np.random.randn( 784, 100 )

■ 이론설명3. 신경망 학습이 잘되는 가중치 구성의 경우 

그림6-13


가중치와 위와 같이 정규분포 형태를 보여야 신경망 학습이 잘됩니다. 
학습 시작하기전에 처음부터 위와 같이 만들어줘야합니다. 

예: W1 = 0.01 * np.random.randn(784, 100)

 


※  텐써 플로우에서는 가중치 초기값이 어떻게 구성될까요 ?

  기본값이 이미 정규 분포로 잘 구성되어서 크게 신경안써도 되지만 더 정교하게
  초기값을 구성하고 싶다면 다음의 옵션을 쓰면 됩니다. 
 

 

 


1. he 초기화 :  relu 함수와 궁합이 잘 맞습니다. 

예: model.add( Dense(64, activation='relu', kernel_initializer='he_normal'))

2. Glorotuniform 초기화

예: model.add( Dense(64, activation='relu', kernel_initializer='glorot_uniform'))

3. RadomNormal 초기화 :  sigmoid 와 궁합이 잘 맞습니다. 

예: model.add( Dense(64, activation='relu', kernel_initializer=initializers.RandomNormal(mean=0., stddev=0.05))

4. zeros 초기화

예: model.add( Dense(64, activation='relu', kernel_initializer='zeros'))

문제1. relu 함수와 짝꿍인 he 초기화 가중치 구성으로 앞에서 만들었던 필기체 신경망
        을 300 에폭으로 돌리고 식사하러 가세요 ~

예: model.add( Dense(64, activation='relu', kernel_initializer='he_normal'))


https://cafe.daum.net/oracleoracle/SpOP/215

200 에폭이 넘어가면서 부터 훈련 데이터의 정확도가 100% 를 보이고 있습니다. 

 

 

 


■  배치 정규화   p210


https://cafe.daum.net/oracleoracle/SpOP/219 <-- 그림 참고

 가중치 초기값을 적당한 값으로 설정하면 정규 분포 형태를 보이는 값으로 
 처음에는 설정이 되는데 학습을 하다 보면 신경망이 깊어지다 보니까 
 각 층의 활성화된 값(입력값x가중치) 이 정규 분포형태를 읽어버리는 현상이
 발생합니다.   1박 2일 게임처럼 신경망 층 뒤로 갈수록 엉뚱한 해석을 하게 되는
현상이 발생합니다. 이를 해결하는 방법이 "배치 정규화" 입니다. 

 

 

 

 


■ 실습1.  앞에서 만든 필기체 분류 신경망 코드에 배치 정규화 코드를 추가하시오 !

https://cafe.daum.net/oracleoracle/SpOP/216

# 은닉층(1층): 가중치 초기화 + 배치 정규화 + 활성화 함수
model.add(Dense(64, kernel_initializer='he_normal'))
model.add(BatchNormalization())  # 배치 정규화
model.add(Dense(64, activation='relu'))

# 은닉층(2층): 가중치 초기화 + 배치 정규화 + 활성화 함수
model.add(Dense(32, kernel_initializer='he_normal'))
model.add(BatchNormalization())  # 배치 정규화
model.add(Dense(32, activation='relu'))

# 은닉층(3층): 가중치 초기화 + 배치 정규화 + 활성화 함수
model.add(Dense(64, kernel_initializer='he_normal'))
model.add(BatchNormalization())  # 배치 정규화
model.add(Dense(64, activation='relu'))



600/600 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step - accuracy: 1.0000 - loss: 0.0012 - val_accuracy: 0.9665 - val_loss: 0.1759
313/313 ━━━━━━━━━━━━━━━━━━━━ 0s 633us/step - accuracy: 0.9618 - loss: 0.2032

배치 정규화를 안썼을때와 비교했을 때는 200 에폭이 되어야 훈련 데이터의 정확도가
100% 였는데 배치 정규화를 쓰니까 100 에폭도 안되어서 훈련 데이터의 정확도가
100% 를 찍고 있습니다.

 

 


■  경사 하강법을 변경해서 정확도 올리기 

 

입력 데이터에 맞는 경사하강법이 있습니다.   경사하강법을 잘 이해하고 입력 데이터에
맞게 경사하강법을 변경해보겠습니다. 

https://cafe.daum.net/oracleoracle/SpOP/220


경사하강법의 종류:


https://cafe.daum.net/oracleoracle/SpOP/221


1. SGD : 확률적 경사하강법으로 배치단위로 복원 추출한 데이터를 학습해서 경사하강
            하는 가장 일반적인 경사하강법입니다.

  수식 :  가중치 = 가중치 - 러닝 레이트 * 기울기 
                                    (학습률 )

텐써 플로우 구현예제: 

# 모델 준비하기
model.compile(optimizer=SGD(), 
                    loss=categorical_crossentropy,  # 분류를 위한 오차함수
                    metrics=['accuracy'])  # 분류 문제에 맞는 평가 지표 사용

2. Adam  :  관성을 이용해서 local minimum 을 빠져나가게 하는 모멘텀의 장점과 
               학습률을 자동 조절되게 해서 경사하강하는 Adagrade 의 장점을 결합한
               경사하강법 ( 학습률이 처음에는 크다가 목적지에 다다르면 작아집니다.)

 모멤텀의 장점 + 아다그레이드의 장점을 둘 다 살린 경사하강법
   ↓
 관성을 이용해서 로컬 미니멈을 빠져나갑니다.

# 모델 준비하기
model.compile(optimizer= Adam(), 
                    loss=categorical_crossentropy,  # 분류를 위한 오차함수
                    metrics=['accuracy'])  # 분류 문제에 맞는 평가 지표 사용


https://cafe.daum.net/oracleoracle/SpOP/224


모멘텀의 수식 :  가속도를 이용해서 로컬미니멈에서 빠져나오게 설계된 경사하강법

     속도 = 마찰계수*속도 - 러닝레이트*기울기 
     가중치 = 가중치 + 속도 

아다그레이드 수식 : 러닝레이트가 자동 조절됨

 h = h + 기울기 ◎ 기울기                            1
 가중치 = 가중치 - 러닝레이트 * 기울기 *-----------
                                                                루트 h

3. RMSprop : Adagrade 의 장점을 더 좋게 만든
                    경사하강법.

발걸음이 자동 조절되는데 목표지점에 도달 할때 이전에 내려오던 그 걸음걸이를 
살펴서 발걸음을 조절합니다. 

 

 

Daum 카페

 

cafe.daum.net

 


면접질문:  Adam 경사하강법에 대해서 편하게 아는데로 애기해주실 수 있나요?

답 :  관성을 이용한 경사하강법인 모멘텀의 장점과 러닝 레이트를 자동 조절하는 아다그레이드의 장점을 살린 경사하강법입니다. 

 

 

언더피팅을 방지하는 3가지 방법 총정리?

 1. 가중치 초기값 설정을 정규분포가 되게 설정
 2. 배치 정규화
 3. 경사하강법


■ 드롭아웃(drop out)

 

 

 

오버피팅을 억제하기 위해서 뉴런을 임의로 삭제 하면서 학습시키는 방법

 

속담:  사공이 많으면 배가 산으로 간다는 속담을 연상하면 됩니다. 
결정장애가 일어난 신경망의 뉴런을 랜덤으로 삭제해서   훈련 데이터의 정확도가 테스트 데이터의 정확도를 비슷하게 맞춰주는 작업을   드롭아웃이라고 합니다.  

주의사항 :  뉴런을 너무 많이 삭제하게 되면 오히려 언더피팅이 발생하게 됩니다. 

 

 

 


■ 실습1.  드롭아웃이 추가된 필기체 분류 신경망을 만드시오 !

model = Sequential()            
model.add(Flatten(input_shape=(784,  )  )  ) # 입력층(0층)
model.add(BatchNormalization() )
model.add(Dropout(0.2) )  # 뉴런중에 20% 를 무작위로 잘라버리겠다.

 

600/600 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - acc: 0.9924 - loss: 0.0227 - val_acc: 0.9780 - val_loss: 0.0925
313/313 ━━━━━━━━━━━━━━━━━━━━ 0s 996us/step - acc: 0.9758 - loss: 0.1002

훈련 데이터의 정확도: 0.99 이고 테스트 데이터의 정확도가 0.97 로 오버피팅이 발생하고 있습니다. 

문제1. 위의 코드에 다음층에 dropout 을 더 추가하면 오버피팅이 방지되는지 확인하시오 !
# 1. 필요한 패키지 가져오는 코드

import tensorflow as tf   # 텐써 플로우 2.0
from tensorflow.keras.datasets.mnist import load_data  # 텐써플로우에 내장되어있는 mnist 데이터를 가져온다.
from tensorflow.keras.models import Sequential  # 모델을 구성하기 위한 모듈
from tensorflow.keras.layers import Dense, Flatten, BatchNormalization, Dropout  # 완전 연결계층을 구성하기 위한 모듈
from tensorflow.keras.utils import to_categorical # one encoding 하는 모듈
import numpy as  np

tf.random.set_seed(777)

(x_train, y_train), (x_test, y_test) = load_data(path='mnist.npz')  # mnist 데이터 로드
   
# 2. 정규화 진행  
x_train = (x_train.reshape((60000, 28 * 28))) / 255
x_test = (x_test.reshape((10000, 28 * 28))) / 255

# 3. 정답 데이터를 준비한다.
# 하나의 숫자를 one hot encoding 한다. (예:  4 ---> 0 0 0 0 1 0 0 0 0 0 )
y_train = to_categorical(y_train)  # 훈련 데이터의 라벨(정답)을 원핫 인코딩
y_test = to_categorical(y_test)    # 테스트 데이터의 라벨(정답)을 원핫 인코딩


# 4. 모델을 구성합니다. 3층 신경망으로 구성
# (100, 784) ◎ ( 784, 100) = (100, 100) ◎ (100, 50 ) = (100, 50) ◎ (50, 10 ) = (100,10)
model = Sequential()            
model.add(Flatten(input_shape=(784,  )  )  ) # 입력층(0층)
model.add(BatchNormalization() )
model.add(Dropout(0.2) )

model.add(Dense( 100, activation='sigmoid' ))  # 은닉층(1층)
model.add(BatchNormalization() )
model.add(Dropout(0.2) )

model.add(Dense( 50, activation ='sigmoid') )  # 은닉층 (2층)
model.add(Dense( 10, activation='softmax') )  # 출력층 (3층)


# 5. 모델을 설정합니다. ( 경사하강법, 오차함수를 정의해줍니다. )
model.compile(optimizer='RMSprop',
                           loss = 'categorical_crossentropy',
                           metrics=['acc'])  # 학습과정에서 정확도를 보려고

#6. 모델을 훈련시킵니다.

history = model.fit(x_train, y_train,
                    epochs = 30,  # 30에폭
                    batch_size = 100,
                    validation_data = (x_test, y_test) )

# 7.모델을 평가합니다. (오차, 정확도가 출력됩니다.)

model.eval‎uate(x_test, y_test)

# 위의 코드들 밑에 바로 구현

train_acc_list = history.history['acc']
test_acc_list = history.history['val_acc']

print( train_acc_list )
print( test_acc_list )

# 시각화 까지 하세요 ~
import numpy as  np
import  matplotlib.pyplot  as  plt

x = np.arange( len(train_acc_list) )
plt.plot( x, train_acc_list, label='train acc')
plt.plot( x, test_acc_list, label='test acc',  linestyle='--')
plt.legend(loc='lower right')
plt.xlabel('epochs')
plt.ylabel('accuracy')
plt.show()​

 

 


▣ 6.5     얼리 스탑 기능 ( early  stopping 기능 )

 

뜻  ?  학습을 일찍 끝내겠다.  더 이상 개선의 여지가 보이지 않는다면 일찍 끝내겠다.

 

설명:  14 에폭에서 한번 만났고 21에폭에서 또 만났는데 가급적이면 정확도가 더 높은 두번째 21에폭에서 학습을 멈추게 하는게 바람직합니다. 

 

 


■ 실습1. 얼리 스탑 기능을 추가한 신경망 전체코드를 수행하시오 !

# MNIST 데이터 불러오기 및 전처리
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import EarlyStopping

(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 차원 축소 및 정규화
x_train = x_train.reshape((x_train.shape[0], 784)).astype('float32') / 255
x_test = x_test.reshape((x_test.shape[0], 784)).astype('float32') / 255

# 레이블을 one-hot encoding으로 변환
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

# 신경망 구성에 필요한 모듈 불러오기
import tensorflow as tf
tf.random.set_seed(777)
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, BatchNormalization
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.losses import categorical_crossentropy

# 모델 구성하기
model = Sequential()

model.add(Flatten(input_shape=(784,)))  # 입력층

# 은닉층 1층
model.add(Dense(64, kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Dense(64, activation='relu'))

# 은닉층 2층
model.add(Dense(32, kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Dense(32, activation='relu'))

# 은닉층 3층
model.add(Dense(64, kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Dense(64, activation='relu'))

# 출력층
model.add(Dense(10, activation='softmax'))

# 모델 컴파일
model.compile(optimizer=SGD(), 
              loss=categorical_crossentropy,  
              metrics=['accuracy'])  

# 얼리 스탑 기능 추가
early_stopping = EarlyStopping(monitor='val_loss',  
                               patience=10,         
                               restore_best_weights=True)

# 모델 훈련
history = model.fit(x_train, y_train,  
                    epochs=100, 
                    batch_size=100,
                    validation_data=(x_test, y_test),
                    callbacks=[early_stopping])

# 모델 평가
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"\nTest Loss: {test_loss:.4f}, Test Accuracy: {test_acc:.4f}")

# 학습 결과 시각화
import matplotlib.pyplot as plt

# 훈련 및 테스트 정확도 리스트
train_acc_list = history.history['accuracy']
test_acc_list = history.history['val_accuracy']

# 에포크 수에 따른 x축 데이터 생성
x = np.arange(len(train_acc_list))

# 시각화
plt.figure(figsize=(8, 6))
plt.plot(x, train_acc_list, label='Train Accuracy')
plt.plot(x, test_acc_list, label='Test Accuracy', linestyle='--')

# 그래프의 축, 범위 및 레이블 설정
plt.ylim(0.8, 1.0)
plt.xlabel('Epochs', fontsize=12)
plt.ylabel('Accuracy', fontsize=12)

# 범례 추가 및 위치 설정
plt.legend(loc='lower right', fontsize=12)

# 타이틀 추가
plt.title('Train vs Test Accuracy', fontsize=14)

# 그래프 출력
plt.show()

 

EarlyStopping 콜백에서 monitor='val_loss'로 설정했기 때문에 **검증 데이터(validation data)**에서의 손실(val_loss)을 모니터링합니다. 즉, 학습 중에 검증 데이터의 손실 값이 더 이상 감소하지 않으면, 그것을 "성능이 개선되지 않는 것"으로 간주합니다.

  • patience=10은 검증 손실 값이 10번 연속으로 개선되지 않으면 학습을 중단한다는 의미입니다. 10번의 에포크 동안 검증 손실이 줄어들지 않으면 그때 학습을 멈추게 됩니다.
  • restore_best_weights=True로 설정했기 때문에, 학습이 중단되었을 때 가장 성능이 좋았던(손실이 가장 낮았던) 시점의 가중치를 복원하게 됩니다. 이를 통해, 학습이 지나치게 진행되어 성능이 나빠지는 것을 방지할 수 있습니다.

따라서, 모델이 10번 연속으로 개선되지 않는다면 그 즉시 학습을 중단하고 최적의 모델 가중치로 복원됩니다.

 

 

 


■   가중치 감소

 "학습하는 과정에서 큰 가중치에 대해서는 그에 상응하는 큰 패널티를 부여하여 오버피팅을 억제하는 방법 "


고양이 사진     입력층 ---------------> 은닉층 ----------------------->
                          □  ------- ---> 뾰족한 귀가 있어요  ----> 아주 큰 가중치로 갱신
                          □  -------------> 꼬리가 있어요        ----> 가중치
                          □  ------------->  집계발을 가지고 있어요 --> 가중치
                          □  ------------->  수염이 있어요          ---> 가중치
                           :
                           :
                          □ -------------->  장난스럽게 보여요  ----> 가중치

 

 

설명: 고양이 귀가 뾰족하다는것을 신경망이 알게되어서 고양이 귀에 대해서는 아주 큰 가중치를 부여하겠금 학습이 되었는데 그러다 보니 뾰족하지 않은 귀를 가진 고양이 사진이 입력되면 고양이가 아니라고 잘못 판단하는 상태가 된 신경망입니다.

이럴때는 '가중치 감소' 를 사용하여 아주 큰 가중치에 대해서는 더 큰 패널티를 부여합니다. 이걸 'L2 정규화' 라고 합니다. 
텐써 플로우로는 다음과 같이 하면 됩니다.

 

예제:  model.add( Dense( 50, kernel_regular='l2', activation='relu') ) 


■ 실습1.  '가중치 감소' 를 구현한 필기체 분류 신경망을 만드시오 !

from tensorflow.keras.regularizers import l2

model.add(Dense(64, kernel_initializer='he_normal', kernel_regularizer=l2(0.01)))

https://cafe.daum.net/oracleoracle/SpOP/232 <-- 전체 코드

from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import EarlyStopping, Callback
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, BatchNormalization, Dropout
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.losses import categorical_crossentropy
from tensorflow.keras.regularizers import l2

# 1. MNIST 필기체 데이터 불러오기
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 차원 축소하기
x_train = x_train.reshape((x_train.shape[0], 784)).astype('float32') / 255
x_test = x_test.reshape((x_test.shape[0], 784)).astype('float32') / 255

# 레이블을 one-hot encoding으로 변환하기
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

# 2. 커스텀 콜백 정의 (훈련 데이터와 테스트 데이터 정확도 동일할 때 멈춤)
class CustomStopCallback(Callback):
    def on_epoch_end(self, epoch, logs=None):
        train_accuracy = logs.get('accuracy')  # 훈련 데이터 정확도
        val_accuracy = logs.get('val_accuracy')  # 테스트 데이터 정확도

        # 훈련과 테스트 정확도가 같아지면 멈춤
        if train_accuracy == val_accuracy:
            print(f"\nEpoch {epoch + 1}: Train accuracy and validation accuracy are the same. Stopping training.")
            self.model.stop_training = True

# 3. 모델 구성하기
model = Sequential()

model.add(Flatten(input_shape=(784,)))  # 입력층

# 은닉층(1층): 가중치 초기화 + 배치 정규화 + 활성화 함수
model.add(Dense(64, kernel_initializer='he_normal', kernel_regularizer=l2(0.01)))
model.add(BatchNormalization())  # 배치 정규화
model.add(Dropout(0.2)) 
model.add(Dense(64, activation='relu'))

# 은닉층(2층): 가중치 초기화 + 배치 정규화 + 활성화 함수
model.add(Dense(32, kernel_initializer='he_normal',kernel_regularizer=l2(0.01)))
model.add(BatchNormalization())  # 배치 정규화
#model.add(Dropout(0.1)) 
model.add(Dense(32, activation='relu'))

# 은닉층(3층): 가중치 초기화 + 배치 정규화 + 활성화 함수
model.add(Dense(64, kernel_initializer='he_normal',kernel_regularizer=l2(0.01)))
model.add(BatchNormalization())  # 배치 정규화
model.add(Dense(64, activation='relu'))

# 출력층(4층)
model.add(Dense(10, activation='softmax'))

# 모델 준비하기
model.compile(optimizer=SGD(), 
              loss=categorical_crossentropy,  # 분류를 위한 오차함수
              metrics=['accuracy'])  # 분류 문제에 맞는 평가 지표 사용

# 4. 얼리 스탑 기능 추가 (가장 높은 검증 정확도를 기준으로)
early_stopping = EarlyStopping(monitor='val_accuracy',  # 검증 데이터의 정확도를 기준으로 함
                               patience=10,  # 성능 개선이 없을 때 20 에포크를 더 기다림
                               restore_best_weights=True)  # 가장 성능이 좋았던 가중치 복원

# 5. 학습 시키기 (100 에포크로 설정하고 얼리 스탑 및 커스텀 콜백 추가)
history = model.fit(x_train, y_train,  
                    epochs=200, 
                    batch_size=100,
                    validation_data=(x_test, y_test),
                    callbacks=[early_stopping, CustomStopCallback()])  # 얼리 스탑 및 커스텀 콜백 추가

# 6. 모델 평가
# model.eval‎uate(x_test, y_test)

# 학습 결과 시각화
import matplotlib.pyplot as plt

# 예시로 accuracy 리스트 값을 사용
train_acc_list = history.history['accuracy']  # 훈련 데이터의 정확도 리스트
test_acc_list = history.history['val_accuracy']  # 테스트 데이터의 정확도 리스트

# 에포크 수에 따른 x축 데이터 생성
x = np.arange(len(train_acc_list))

# 시각화
plt.figure(figsize=(8, 6))  # 그래프 크기 설정
plt.plot(x, train_acc_list, label='Train Accuracy')  # 훈련 데이터 정확도 라인
plt.plot(x, test_acc_list, label='Test Accuracy', linestyle='--')  # 테스트 데이터 정확도 라인

# 그래프의 축, 범위 및 레이블 설정
plt.ylim(0.8, 1.0)  # y축 범위를 0.8에서 1.0까지로 설정
plt.xlabel('Epochs', fontsize=12)  # x축 레이블
plt.ylabel('Accuracy', fontsize=12)  # y축 레이블

# 범례 추가 및 위치 설정
plt.legend(loc='lower right', fontsize=12)

# 타이틀 추가 (선택 사항)
plt.title('Train vs Test Accuracy', fontsize=14)

# 그래프 출력
plt.show()


테스트 데이터의 정확도가 훈련 데이터의 정확도 보다 처음부터 높게 나오고 있어서 오버피팅이 전혀 발생하고 있지 않습니다. 

 

문제1. 위의 모델을 save 시키시오 !

별도의 셀을 열고 model.save("c:\\data\\model7.h5") 


문제2. 위의 모델을 불러와서 테스트 데이터를 평가하고 정확도를 보시오 !
from tensorflow.keras.models import  load_model

new_model = load_model("c:\\data\\model7.h5")

new_model.evaluate( x_test, y_test )  # [0.1266438066959381, 0.9764999747276306]​


훈련 중에 테스트 데이터의 정확도는 0.9702 였는데  저장한 모델을 불러와서 확인한 테스트 데이터의 정확도는 0.7964 입니다. 더 정확도가 좋게 나왔습니다.  
이유는 restore_best_weights=True 때문입니다. 학습중에 가장 좋았던 가중치로 모델을 save 시켰기 때문입니다.