Networks/데이터 분석 및 AI

SK networks AI Camp - 인공 신경망

코딩하는 Español되기 2024. 9. 2. 13:00

Tensor Flow

구글에서 만든 딥러닝 프로그램을 쉽게 구현할 수 있도록 기능을 제공하는 라이브러리

모델 구축과 서비스에 필요한 다양한 도구를 제공, 신경망 모델을 빠르게 구성 가능한 Keras를 핵심 API로 채택

 - 가능한 요소 : 필기 숫자 판별, 이미지 인식, 단어 임베딩, 반복 신경망, 시퀀스 투 시퀀스 모델 자연어 처리, 신경망 학습

 - Colab에서 딥러닝 라이브러리를 사용할 때는 그래픽 처리 장치인 GPU를 사용하여 인공 신경망 훈련하기

 - GPU : 벡터와 행렬 연산에 매우 최적화되어 있기에, 덧셈과 곱셈이 많이 수행되는 인공 신경망에 큰 도움이 됨

 

인공 신경망(ANN; Artificial Neural Network)

: 기존 머신러닝 알고리즘으로 다루기 어려웠던 이미지, 음성, 텍스트 분야에서 뛰어난 성능을 발휘하는 딥러닝

 * 딥러닝(DeepLearning) : 인공신경망과 동의어로 사용되는 경우가 多, 혹은 심층 신경망을 딥러닝이라고 함.

 

○ 용어

    ● 출력층(Output layer) : 신경망의 최종 값

    ● 뉴런(Neuron = Unit) : z 값을 계산하는 단위, 뉴런에서 일어나는 일은 선형 계산이 전부

    ● 입력층(Input layer) : 픽셀값 그 자체, 특별한 계산을 하지는 않음

 

○ 인공신경망으로 모델 만들기

    ● 신경망 모델 생성

        * Sequential 클래스의 객체를 만들 때 만든 밀집층의 객체 dense를 전달

        * 아래 코드의 경우 model 객체가 신경망 모델

        * 모델 그림을 그릴 때 입력층과 출력층 사이에 연결선만 나타내고 가중치는 표시하지 않는 경우가 多

        * 절편의 경우 아예 선을 그리지 않는 경우가 많음 하지만 절편이 뉴런마다 더해진다는 사실은 기억할 것

        * 소프트맥스 함수 : 뉴런의 선형 방정식 계산 결과에 적용되는 함수로 활성화 함수(Activation Function)라고 부름

import tensorflow as tf
from tensorflow import keras
from sklearn.model_selection import train_test_split

train_scaled, val_scaled, train_target, val_target = train_test_split(tran_scaled, train_target, test_size = 0.2, random_state = 42)

# (뉴런 개수, 뉴런 출력에 사용할 함수, 입력 크기)
dense = keras.layers.Dense(10, activation="softmax", input_shape=(784, ))
model = keras.Sequential(dense)

 

    ● 케라스 모델을 훈련하기 전 손실 함수 종류 설정 단계(필수)

        - 이진 분류 손실 함수 : loss = 'binary_crossentropy'

        - 이진 분류 활성화 함수 : 시그모이드 함수

        - 다중 분류 손실 함수 : loss = 'categorical_crossentroy'

        - 다중 분류 활성화 함수 : 소프트맥스 함수

        * 아래 코드에서 sparse를 붙인 이유?

         이진 크로스 엔트로피 손실을 위해 아래와 같이 계산했음

           양성일 경우 : - log(a) X 타깃값(=정답)

           음성일 경우 : -log(1-a) X (1 - 타깃값)
         이진 분류에서는 출력층의 뉴런이 하나.

         이 뉴런이 출력하는 확률값 a(시그모이드 함수의 출력값)를 사용해 양성/음성 클래스에 대한 크로스 엔트로피를 계산

model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')

    

    ● 원-핫 인코딩

        - 타깃값을 해당 클래스만 1이고 나머지는 모두 0인 배열로 만드는 방법

        - 변환해 주는 이유

         : 다중 분류에서 크로스 엔트로피 손실 함수를 사용하려면 0, 1, 2와 같이 정수로 된 타깃 값을 변환해야 함

 

    ● 스파스 범주형 교차 엔트로피(sparse_categorical_crossentropy)

      : 텐서플로에서 정수로 된 값을 원-핫 인코딩으로 바꾸지 않고 바로 사용 가능

 

    ● 모델 훈련 및 성능 평가

model.fit(train_scaled, train_target, epochs = 5)
model.evaluate(val_scaled, val_target)

 

○ SGDClassifier와 Keras Sequential 클래스 사용법 비교

    ● 사이킷런 모델

# 모델
sc = SGDBlassifier(loss='log_loss', max_iter = 5) # loss= 손실함수 , max_iter = 반복 횟수

# 훈련
sc.fit(train_scaled, train_target)

# 평가
sc.score(val_scaled, val_target)

 

    ● 케라스 모델

dense = keras.layers.Dense(10, activation='softmax', input_shape=(784,)) # Dense = 층생성

# 모델
model = keras.Ssequential(dense)
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')

# 훈련
model.fit(train_scaled, train_target, epochs=5)

# 평가
model.evaluate(val_scaled, val_target)

 

○ 심층 신경망(Deep neural network; DNN)

  : 2개 이상의 층을 포함한 신경망(종종 다층 인공 신경망, 심층 신경망, 딥러닝을 같은 의미로 사용)

    ● 데이터 불러오기(패션 MNIST 데이터 셋 사용)

from tensorflow import keras
(train_input, train_target), (test_input, test_target) =\ keras.datasets.fashion_mnist.load_data()

from sklearn.model_selection import train_test_split

train_scaled = train_input / 255.0
train_scaled = train_scaled.reshape(-1, 28*28)
train_scaled, val_scaled, tarin_target, val_target = train_test_split(train_scaled, train_target, test_size=0.2, random_state=42)

 

    ● 층 추가 방법 1 : 출력층을 케라스의 Dense 클래스로 생성하기

       * 시그모이드 활성화 함수를 사용

       * 시그모이드 함수 (활성화 함수) : 뉴런의 출력 z 값을 0 ~ 1 사이로 압축

# 은닉층 : 100개의 뉴런을 가진 밀집층
dense1 = keras.layers.Dense(100, activation='sigmoid', input_shape=(784,))

# 출력층 : 10개의 클래스를 분류하므로 10개의 뉴런을 설정
dense2 = keras.layers.Dense(10, activation='softmax')

 

    ● 층 추가 방법 2 : Sequential 클래스의 생성자 안에서 바로 Dense 클래스의 객체를 만들기

model = keras.sequential([
	keras.layers.Dense(100, activation='sigmoid', input_shape(784,)), name='hidden'),
    keras.layers.Dense(10, activation='softmax', name='output')
    ], name='fashion MNIST model')

 

    ● 층 추가 방법 3 : add() 메서드 사용

model = keras.Sequential()
model.add(keras.layers.Dense(100, activation='sigmoid', input_shape=(784,))
model.add(keras.layers.Dense(10, activation='softmax'))

model.summary()

 

    ● 모델 생성 및 훈련

# 생성
model = kears.Sequential([dense1, dense2])
model.summary()

# 훈련
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)

 

○ 렐루 함수

  : 입력이 양수일 경우 활성화 함수가 없는 것처럼 입력을 pass 하고 음수일 경우 0으로 만듦

    ● 시그모이드 함수의 단점 : 오른쪽과 왼쪽 끝으로 갈수록 그래프가 누워있음

                                                So, 올바른 출력을 만드는 데 신속한 대응 불가

                                                특히 층이 많은 심층 신경망일수록 누적되어 학습을 어렵게 만듦

model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28)))
model.add(keras.layers.Dense(100, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))

(train_input, train_target), (test_input, test_target) =\
	keras.datasets.fashion_mnist.load_data()

train_scaled = tarin_input / 255.0
train_scaled, val_scaled, train_taraget, val_target = train_test_split(
	train_scaled, train_target, test_size = 0.2, random_state = 42)
    
    
model.compile(loss = 'sparse_categorical_crossentropy', metrics = 'accuracy')
model.fit(train_scaled, train_target, epochs = 5)

 

○ 옵티마이저(optimizer)

: 신경망의 가중치와 절편을 학습하기 위한 알고리즘 또는 방법

  케라스의 경우 경사 하강법 알고리즘이 구현되어 있으며, 대표적으로 SGD, 네스테로프 모멘텀, RMSprop, Adam 등

  * SGD : 기본 경사 하강법 옵티마이저 클래스

     - 매개변수

       1. learning_rate : 학습률 지정(default : 0.01)

       2. momentum : 매개변수에 0 이상 값 지정하면 모멘텀 최적화 수행

       3. nesterov : True로 설정하면 네스테로프 모멘텀 최적화 수행

  * Adagrad

     - 매개변수

       1. learning_rate : defaut = 0.001

       2. Adagrad : 그레디언트 제곱을 누적해 학습률을 나눔

  * RMSprop 

     - 매개변수

       1. learning_rate : default = 0.001

       2. Adagrad처럼 그레디언트 제곱으로 학습률을 나누지만 최근의 그레디언트를 사용하기 위해 지수 감소를 사용

  * Adam

     - 매개변수

       1. learning_rate : default = 0.001

       2. beta_1 : 모멘텀 최적화의 그레디언트 지수 감소 평균 조절(default : 0.9)

       3. beta_2 : RMSprop의 그레디언트 제곱 지수 감소 평균 조절(default : 0.999)

 

신경망 모델 훈련

○ 손실 곡선

    ● 모델 생성(훈련 + 검증 세트로 나누고 모델 생성)

from tensorflow import keras
from sklearn.model_selection import train_test_split


# 훈련, 검증세트 나누기
(train_input, train_target), (test_input, test_target) =\
	keras.datasets.fashion_mnist.load_data()

train_scaled = train_input / 255.0
train_scaled, val_scaled, train_target, val_target = train_test_split(
	train_scaled, train_target, test_size=0.2, random_state=42)
    
    
# 모델 생성
def model_fn(a_layer = None):
	model = keras.Sequential()
    model.add(keras.layers.Flatten(input_shape=(28, 28)))
    model.add(keras.layers.Dense(100, activation='relu'))
    if a_layer:
    	model.add(a_layer)
    model.add(keras.layers.Dense(10, adctivation('softmax'))
    return model

 

    ● 그래프로 표현

        * fit() 메서드 결과에는 손실과 정확도가 들어있음

          (현재 history 객체에 훈련 측정값이 담긴 history 딕셔너리가 들어있음)

model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')

# history 변수에 fit() 결과 담기
history = model.fit(train_scaled, train_target, epochs=5, verbose=0)
# 출력
# print(history.history.keys())


# 맷플로리를 통한 시각화

import matplotlib.pyplot as plt

# 손실 출력
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

# 정확도 출력
plt.plot(history.history['accuracy'])
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()

 

○ 검증손실

  : 인공 신경망은 모두 일종의 경사 하강법을 사용하기 때문에 에포크 사이의 관계와 과대/과소 적합이 동일하게 적용됨

    따라서 에포크에 따른 과대/과소 적합을 파악하려면 훈련 세트 점수와 검증 세트에 대한 점수가 필요 

    * 인공 신경망 모델이 최적화하는 대상은 정확도가 아니라 손실 함수

       So, 모델 훈련이 잘 되었는지 판단하려면 손실 함숫값을 확인하는 것이 더 좋음

 

    ● 에포크마다 검증 손실을 계산하기

model = model_fn()
model.compile(loss='sparse_categorical_crossentropy', metrics = 'accuracy')

# fit() 메서드에 검증 데이터를 전달하여 에포크마다 검증 손실을 계산
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
	validation_data =(val_scaled, val_target)
    

# 검증 세트에 대한 손실 = 'val_loss', 정확도 = 'val_accuracy'
# 과대/과소 적합 문제 조사를 위한 훈련/검증 손실 그래프 그리기
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

 

    ● 옵티마이저 하이퍼파라미터 조정을 통해 과대적합 완화

       - Adam 옵티마이저 사용

        : Adam의 경우 적응적 학습률을 적용 So, 에포크가 진행되면서 학습률의 크기 조정 가능

          * 적응적 학습률(adaptive learning rate)

            : 모델이 최적점에 가까이 갈수록 안정적으로 수렴하도록 학습률을 낮추도록 조정하는 방법

              학습률 매개변수를 튜닝하는 수고를 덜 수 있는 장점

model = model_fn()
model.compile(optimize = 'adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
	validation_data=(val_scaled, val_target))

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

 

○ 드롭아웃(dropout)

: 훈련 과정에서 층에 있는 일부 뉴런을 랜덤 하게 꺼서(뉴런 출력을 0으로 만들어서) 과대적합 방지

왼쪽은 두 번째 뉴런이 드롭아웃되어 h2 출력이 없고, 오른쪽은 첫 번째 뉴런이 드롭아웃되어 h1 출력이 없음

드롭아웃이 과대적합을 왜 막을까?

   - 이전 층의 일부 뉴런이 랜덤 하게 꺼지면 특정 뉴런에 과대 의존을 줄이고 모든 입력에 대해 주의를 기울여야 함

     일부 뉴런의 출력이 없을 수 있다는 점을 감안하면 이 신경망은 더 안정적인 예측을 만들 수 있음

   - 드롭아웃을 적용해 훈련하는 것은 마치 2개의 앙상블 하는 것처럼 생각할 수 있음(앙상블 = 과대적합을 막아줌)

      * 앙상블 = 더 좋은 예측 결과를 만들기 위해 여러 개의 모델을 훈련하는 머신러닝 알고리즘

   

    ● 케라스로 드롭아웃 객체를 전달하여 층을 추가

      * 훈련이 끝난 뒤에 평가 or 예측을 수행할 때는 드롭아웃 적용하면 안 됨!

      * 케라스(텐서플로)는 모델을 평가와 예측을 사용할 때는 자동으로 드롭아웃을 적용하지 않음

# 30% 정도를 드롭아웃
model = model_fn(keras.layers.Dropout(0.3))
# 출력 결과를 보면 은닉층 뒤에 추가된 드롭아웃 층은 훈련되는 모델 파라미터가 없음
model.summary()

model.compile(optimize = 'adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
	validation_data=(val_scaled, val_target))

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

 

모델 저장과 복원

○ save_weights() 메서드 (in keras)

  : 텐서플로의 체크포인트 포맷으로 저장하지만 파일 확장자가 '. h5'일 경우 HDF5 포맷으로 저장

 

○ save() 메서도

  : 모델 구조와 모델 파라미터를 함께 저장. 기본적으로 SavedModel 포맷으로 하지만 '. h5'일 경우 HDF5 포맷으로 저장

model.compile(optimize = 'adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0,
	validation_data=(val_scaled, val_target))

model.save_weights('model-weights.h5')
model.save('model_whole.h5')

# 확인
!ls -al *.h5

 

○ 실습

 

1. 새로운 모델을 만들고 저장된 파일(model_whole.h5)에서 훈련된 모델 파라미터 읽어 사용하기

 

    ● load_weights() 메서드를 사용하려면 저장한 모델과 정확히 똑같은 구조를 가져야 함

       So, model_fn() 함수를 사용하여 동일한 모델을 사용(모듈화의 중요성)

 

    ● evaluate() 메서드

      : 모델 파라미터를 읽은 후 정확도를 출력 가능하지만 손실을 계산하기 위해 complie() 메서드를 먼저 실행해야 함

         load_model() 함수를 실행한 후에는 모델 파라미터, 구조, 옵티마이저 상태까지 복원하여 사용 가능

model = model_fn(keras.layers.Dropout(0.3))
model.load_weights('model-weights.h5')

# 정확도 계산
import numpy as np

# predict() 메서드 결과에서 가장 큰 값을 고르기 위해 넘파이의 argmax() 함수 사용
# argmax() : 배열에서 가장 큰 값의 인덱스를 반환
val_labels = np.argmax(model.predict(val_scaled), axis=1)
print(np.mean(val_labels == val_target))

# 정확도 출력
model = keras.models.load_model('model-whole.h5')
model.evaluate(val_scaled, val_target)

 

2. 콜백(callback) : model_whole.h5 파일에서 새로운 모델을 만들어 바로 사용하기

  : 훈련 과정 중간에 어떤 작업을 수행할 수 있는 객체

    keras.callbacks 패키지 아래에 있는 클래스

    fit() 메서드의 callbacks 매개변수에 리스트로 전달하여 사용

   * 사용하는 ModelCheckpoint 콜백은 기본적으로 에포크마다 모델을 저장

      So, save_best_only = True 매개변수 지정으로 가장 낮은 검증 손실을 만드는 모델을 저장 가능

model = model_fn(keras.layers.Dropout(0.3))

model.compile(optimize = 'adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-model.h5', save_best_only = True)
model.fit(train_scaled, train_target, epochs=20, verbose=0,
	validation_data=(val_scaled, val_target),
    callbacks=[checkpoint_cb])
    
# 모델 예측 수행

model = keras.models.load_model('best-model.h5')
model.evaluate(val_scaled, val_target)

 

○ 조기 종료 : 과대적합이 커지기 전에 훈련을 미리 중지하는 것

 

    ● EarlyStopping 콜백

        - patience 매개변수 : 검증 점수가 향상되지 않더라도 참을 에포크 횟수로 지정

          e.g. patience=2 → 2번 연속 검증 점수가 향사오디지 않으면 훈련 중지

        - restore_best_weights = True : 가장 낮은 검증 손실을 낸 모델 파라미터로 돌림

model = model_fn(keras.layers.Dropout(0.3))

model.compile(optimize = 'adam', loss='sparse_categorical_crossentropy', metrics='accuracy')

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-model.h5', save_best_only = True)

early_stopping_cb = keras.callbacks.EarlyStopping(patience=2, restotr_bbest_weights=True)

model.fit(train_scaled, train_target, epochs=20, verbose=0,
	validation_data=(val_scaled, val_target),
    callbacks=[checkpoint_cb, early_stopping_cb])
    
# 훈련/검증 손실 출력
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
ptl.show()

# 모델 성능 확인
model.evaluate(val_scaled, val_target)