본문 바로가기
혼공학습단

[혼공머신] 6주차_인공 신경망

by netsgo 2024. 2. 12.
 

혼자 공부하는 머신러닝+딥러닝 - 예스24

- 혼자 해도 충분하다! 1:1 과외하듯 배우는 인공지능 자습서 이 책은 수식과 이론으로 중무장한 머신러닝, 딥러닝 책에 지친 ‘독학하는 입문자’가 ‘꼭 필요한 내용을 제대로’ 학습할 수 있

www.yes24.com

 

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


패션 MNIST

인공 신경망을 이해하기 위해 패션 MNIST 데이터(10종류의 패션 아이템으로 이루어진 데이터)를 이용한다.

from tensorflow import keras

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

 

패션 MNIST 데이터셋은 60,000개의 훈련 데이터와 10,000개의 테스트 데이터로 구성되어 있고 타깃은 0~9까지의 숫자 레이블이다.

 

SGDClassifier를 이용해 모델을 만들어 보자. 먼저 데이터를 255로 나누어 정규화하고 1차원 배열로 만든다.

train_scaled = train_input / 255
train_scaled = train_scaled.reshape(-1, 28 * 28)

 

사이킷런의 SGDClassifier와 corss_validate 함수를 사용해 성능을 확인해 보자.

from sklearn.model_selection import cross_validate
form sklearn.linear_model import SGDClassifier

sc = SGDClassifier(loss = 'log_loss', max_iter = 5, random_state = 42)
scores = cross_validate(sc, train_scaled, train_target, n_jobs = -1)

 

SGDClassifier는 10개의 클래스를 잘 구분할 수 있도록 각 클래스에 대해 모델 파라미터를 찾는 과정이라고 생각할 수 있다.

 

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


대부분의 머신러닝 기법에서와 마찬가지로 전체 데이터를 훈련 데이터와 테스트 데이터로 나누어 실습해 보기로 한다.

from sklearn.model_selection import train_test_split

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

 

먼저 훈련 데이터로 모델을 만들고 그다음 테스트 데이터로 모델을 평가한다. 10개의 패션 아이템을 분류하기 위해 10개의 뉴런으로 구성된 밀집층을 만든다.

dense = keras.layers.Dense(10, activataion = 'softmax', input_shape = (784,))

 

첫 번째 매개변수는 10개의 패션 아이템을 분류하기 위해 뉴런의 개수를 10개로 지정한다. 10개의 뉴런에서 출력되는 값을 확률로 바꾸기 위해 소프트 맥스 함수를 사용한다. 마지막 세 번째 매개변수는 입력값의 크기를 지정한다.

 

이 밀집층을 가진 신경망을 만들기 위해 keras의 Sequential 클래스를 사용한다.

model = keras.Sequential(dense)

 

keras 모델을 훈련하기 전에 손실 함수의 종류를 지정해야 한다. 그다음 훈련 과정에서 계산하고 싶은 측정값을 지정한다.

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

 

keras는 모델이 훈련할 때 기본으로 에포크마다 손실 값을 출력한다. 손실이 줄어드는 것을 보고 훈련이 잘되었다는 것을 알 수 있지만 정홛도를 함께 출력할 수도 있다. 이를 위해 metrics 매개변수에 정확도 지표를 의미하는 'accuracy'를 지정한다.

 

이렇게 학습된 모델은 검증 데이터를 이용해 성능을 평가한다.

model.evaluate(val_scaled, val_target)

 

심층 신경망


인공 신경망 모델에 층을 2개 추가해 보겠다. 앞의 인공 신경망과 다른 점은 입력층과 출력층 사이에 밀집층이 추가되었어으며 이를 은닉층이라고 한다.

 

은닉층에서 선형적인 산술 계산만 수행한다면 수행 역할이 없는 셈이기 때문에 선형 계산을 적당히 비선형적으로 비틀어 주어야 하는데 이 때 많이 활용되는 것이 시그모이드 함수다. 이 함수는 뉴런의 출력 값을 0과 1 사이로 압축한다.

dense1 = keras.layers.Dense(100, activation = 'sigmoid', input_shape = (784,))
dense2 = keras.layers.Dense(10, activation = 'softmax')

 

이제 dense1과 dense2 객체를 Sequential 클래스에 추가하여 심층 신경망을 만든다. 여기서 주의할 점은 출력층을 가장 마지막에 두어야 한다는 점이다.

 

층을 추가하는 다른 방법


Dense 클래스의 객체 dese1, dense2를 만들어 전달할 수도 있지만 Sequential 클래스의 생성자 안에서 바로 Denne 클래스의 객체를 만든다. 이렇게 하면 추가되는 층을 한눈에 쉽게 알아볼 수 있다.

model = keras.Sequential([
keras.layers.Dense(100, activation = 'sigmoid', input_shape = (784,),
name = 'hidden'),
keras.layers.Dense(10, activation = 'softmax', mane = 'output')], name = '패션  MNIST 모델')

 

2개의 Dense 층이 이전과 동일하게 추가도었고 파라미터 개수도 같다. 바뀐 것은 모델 이름과 층 이름이다. 

이 방법이 편리하지만 아주 많은 층을 추가하려면 Sequential 클래스 생성자가 매우 길어진다. Sequential 클래스에서 층을 추가할 때 add() 메서드를 사용한다.

 

이제 모델을 5회 훈련하면 다음과 같다.

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

 

인공 신경망에 몇 개의 층을 추가하더라도 compile() 메서드와 fit() 메서드의 사용법은 동일하다.

 

렐루 함수


인공 신경망의 은닉층에 많이 사용되는 함수는 렐루 함수이다. 렐루 함수는 입력이 양수인 경우 마치 활성화 함수가 없는 것처럼 입력을 그대로 통과시키고 입력이 음수일 경우에는 0으로 만든다.

 

렐루 함수를 적용하기 전에 Flatten 층을 살펴 보자. Flatten 클래스는 인공 신경망의 성능에 기여하는 바는 없지만 입력층과 은닉층 사이에 추가하기 때문에 층이라고 부른다. Flatten 층은 입력층 바로 뒤에 추가한다. reshape() 메서드를 적용하지 않은 코드는 다음과 같다.

(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 = 2.0, random_state = 42)

model.compile(loss = 'sparse_categorical_crosssentropy', metrics = 'accuracy')
model.fit(train_scaled, train_target, epochs = 5)

 

옵티마이저(Optimizer)


keras의 compile() 메서드는 경사 하강법 알고리즘인 RMSprop을 사용한다. keras는 다양한 종류의 경사 하강법 알고리즘을 제공하는데 이들을 옵티마이저라고 한다. 

기본 경사 하강법 옵티마이저에는 SGD, 모멘텀, 네스테로프 모멘텀 등이 있고, 적응적 학습률 옵티마이저에는 RMSprop, Adam, Adagrad 등이 있다.

 

SGD 클래스의 momentum 매개변수의 기본값은 0이다. 이를 0보다 큰 값으로 지정하면 마치 이전의 그레이디언트를 가속도처럼 사용하는 모멘텀 최적화를 사용한다. SGD 클래스의 nesterov 매개변수를 True로 사용하면 네스테로프 모멘텀 최적화를 사용한다.

모델이 최적점에 가까이 갈수록 학습률을 낮출 수 있는데 이런 학습률을 적응적 학습률이라고 한다. 적응적 학습률을 사용하는 대표적인 옵티마이저는 Adagrad와 RMSprop이다. 모멘텀 최적화와 RMSprop의 장점을 접목한 것이 Adam이다.

 

신경망 모델 훈련


손실 곡선

keras의 fit() 메서드는 history 클래스를 반환한다. history 객체에는 훈련 과정에서 계산한 지표가 저장되어 있다. 이 값을 사용하면 그래프를 그릴 수 있다.

history 객체를 확인해 보면 다음과 같은 딕셔너리가 들어 있다.

print(history.history.keys())

# output
# dict_keys(['loss', 'accuracy'])

 

손실과 정확도는 다음과 같이 그래프로 그릴 수 있다.

import matplotlib.pyplot as plt
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')

plt.plot(history.history['accuracy'])
plt.xlabel('epoch']
plt.ylabel('accuracy')

plt.show()

 

검증 손실

에포크마다 검증 손실을 계산하기 위해 keras 모델의 fit() 메서드에 검증 데이터를 전달할 수 있다. validation_data 매개변수에 검증에 사용할 입력과 타깃값을 튜플로 만들어 전달한다.

model = model_fn()
model.compili(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()

 

 

드롭 아웃

드롭 아웃은 훈련 과정에서 일부 뉴런을 랜덤하게 꺼서 과대 적합을 막는 것이다. 드롭 아웃을 사용하면 마치 여러 개의 신경망을 앙상블 학습하는 것으로 생각할 수 있기 때문에 과대 적합을 막는 좋은 기법으로 생각하기에 타당하다.

 

콜백

콜백은 훈련 과정 중간에 어떤 작업을 수행할 수 있게 하는 객체로 keras.callbacks 패키지 아래에 있는 클래스들이다. fit() 메서드의 callbacks 매개변수에 리스트로 전달하여 사용한다.

모델의 훈련이 진행되는 동안 검증 점수가 상승하기 시작하면 이후에는 과대 적합이 더 커지기 때문에 훈련을 계속할 필요가 없다. 이렇게 과대 적합이 시작되기 직전에 훈련을 멈추는 것을 조기 종료(early stopping)라고 한다. keras에는 조기 종료를 위한 EarlyStopping 콜백을 제공한다. EarlyStopping 콜백을 ModelCheckpoint 콜백과 함께 사용하면 가장 낮은 검증 손실의 모델을 파일에 저장하고 검증 손실이 다시 상승할 때 훈련을 중지할 수 있다. 또한 훈련을 중지한 다음 현재 모델의 파라미터를 최상의 파라미터로 되돌린다.

 

이 두 콜백을 함께 사용하면 다음과 같다.

model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer = 'adam', loss = 'sparse_categorical_crossentropy', metrics = 'accuracy')
checkpoint_cb = keras.callbacks.ModeCheckpoint('best-model.h5', save_best_only = True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience = 2, restore_best_weights = True)
history = model.fit(train_scaled, train, target, epochs = 20, verboss = 0,
validation_data = (val_scaled, val_target), callbacks = [checkpoint_cb, early_stopping_cb])