[DL] 혼공머닝 6편 딥러닝 기초 케라스(keras), TensorFlow
카테고리: DL
태그: Deep Learning, keras, TensorFlow
💡 ‘혼자 공부하는 머신러닝 딥러닝(박해선 저)’ 책을 읽고 공부한 내용을 요약한 페이지입니다.
책에 나오는 코드를 그대로 쓰지 않고, 코드와 파라미터를 변형하고 조정해가며 다른 결과를 출력하며 공부했습니다.
07 딥러닝을 시작합니다
07-1 인공 신경망
인공 신경망을 설명하기 앞서 패션MNIST 데이터셋을 불러와보자. 이 데이터는 텐서플로(TensorFlow)를 사용해 불러올 수 있다.
from tensorflow import keras
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
print(train_input.shape, train_target.shape)
#(60000, 28, 28) (60000,)
#데이터는 28*28크기의 60,000개 이미지로 구성되어있다. 타깃도 60,000개의 원소가 있는 1차원 배열이다.
#10개의 샘플을 그림으로 출력해보자
import matplotlib.pyplot as plt
fig, axs = plt.subplots(1,10,figsize=(10,10))
for i in range(10):
axs[i].imshow(train_input[i], cmap='gray_r')
axs[i].axis('off')
plt.show()
이 훈련 샘플은 60,000개나 되기 때문에 전체 데이터를 한꺼번에 쓰는 것보다 하나씩 꺼내서 훈련하는 것이 효율적이다. 이럴 때 유용한 것이 확률적 경사 하강법이다. SGDClassifier
를 사용할 땐 데이터를 전처리 해줘야 했다. 패션MNIST 데이터는 각 픽셀이 0~255의 정숫값을 가지므로 이를 255로 나누어 0~1 사이의 값으로 정규화하고 1차원 배열로 펼쳐야 한다.
train_scaled = train_input/255.0
train_scaled = train_scaled.reshape(-1, 28*28)
import numpy as np
#교차검증으로 성능 확인
from sklearn.model_selection import cross_validate
from sklearn.linear_model import SGDClassifier
sc = SGDClassifier(loss='log', max_iter=5, random_state=42)
scores = cross_validate(sc, train_scaled, train_target, n_jobs=-1)
print(np.mean(scores['test_score'])) #0.81956
검증 점수가 썩 만족스럽진 않다.
로지스틱 회귀 모델과 비슷한 원리이면서 동시에 더 질높은 기능을 제공하는 것이 바로 인공 신경망(artificial neural network) 이다.
로지스틱 회귀에서는 교차 검증을 사용해 모델을 평가했지만, 인공 신경망에서는 교차 검증을 사용하지 않고 검증 세트를 별도로 덜어내어 사용한다. 이렇게 하는 이유는 시간을 덜기 위함과 딥러닝 분야의 데이터셋은 충분히 크기에 검증 점수가 안정적이기 때문이다.
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(train_scaled, train_target, test_size=0.2, random_state=42)
케라스의 Dense
클래스를 사용하여 밀집층(dense layer) 을 만들 수 있다.
dense = keras.layers.Dense(10, activation='softmax', input_shape=(784,))
#첫번째 매개변수에는 뉴런 개수를 지정한다.
#activation매개변수는 뉴런에서 출력되는 값을 확률로 바꾸기 위한 함수를 입력한다.
#2진분류라면 sigmoid를 쓰고 다중분류라면 softmax를 쓴다.
model=keras.Sequential(dense)
#케라스에서 훈련하기 전 설정단계
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
#loss매개변수에 손실함수의 종류를 입력.
#metrics매개변수에 accuracy를 입력하여 정확도 지표를 같이 출력
이진분류에선 손실함수를 binary_crossentropy
를 사용하고, 다중분류에선 손’categorial_crossentropy
를 사용한다. 이 때 정수로 된 타깃값을 원-핫 인코딩으로 바꾸지 않고 바로 사용하는 것이 sparse_categorial_crossentropy
이다.
model.fit(train_scaled, train_target, epochs=5)
#결과값
Epoch 1/5
1500/1500 [==============================] - 3s 1ms/step - loss: 0.6051 - accuracy: 0.7949
Epoch 2/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.4789 - accuracy: 0.8395
Epoch 3/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.4555 - accuracy: 0.8465
Epoch 4/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.4460 - accuracy: 0.8521
Epoch 5/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.4377 - accuracy: 0.8549
<keras.callbacks.History at 0x7f2d68606590>
#훈련세트의 정확도는 0.8549
model.evaluate(val_scaled, val_target)
#결과값
375/375 [==============================] - 1s 1ms/step - loss: 0.4554 - accuracy: 0.8469
[0.45541203022003174, 0.846916675567627]
#검증세트의 정확도는 0.8359
07-1 핵심 키워드
- 인공 신경망: 기존 머신러닝 알고리즘으로 다루기 어려웠던 이미지, 음성, 텍스트 분야에서 뛰어난 성능을 발휘한다. 종종 딥러닝으로 불린다.
- 텐서플로: 구글이 만든 딥러닝 라이브러리
- 밀집층: 가장 간단한 인공 신경망의 층. 뉴런들이 모두 연결되어 있기에 완전 연결 층이라고도 불린다.
- 원-핫 인코딩: 정숫값을 배열에서 해당 정수 위치의 원소만 1이고 나머지는 0으로 모두 변환하는 것.
sparse_categorical_entropy
손실함수는 이런 변환을 수행할 필요가 없다.
07-1 핵심 패키지와 함수
- TensorFlow
Dense
: 신경망에서 가장 기본 층인 밀집층을 만드는 클래스activation
매개변수에 사용할 활성화 함수를 지정. 다중분류는 **softmax**
함수 지정
Sequential
: 신경망 모델을 만드는 클래스compile()
: 모델 객체를 만든 후 훈련하기 전에 사용할 손실함수와 측정 지표를 지정하는 메서드loss
매개변수에 손실 함수를 지정. 이진분류면binary_crossentropy
, 다중분류면categorical_crossentropy
, 클래스 레이블이 정수일 경우sparse_categorical_crossentropy
를 지정metrics
매개변수에 훈련 과정에서 측정하고픈 지표 지정
07-2 심층 신경망
이번엔 신경망에 층을 추가해보자.
rom 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, train_target, val_target = train_test_split(train_scaled, train_target, test_size=0.2, random_state=42)
앞선 절에서 만든 것과의 차이는 입력층과 출력층 사이에 은닉층(hidden layer) 을 추가한 것이다. 은닉층에도 활성화함수를 적용하는데, 대표적으로 시그모이드 함수나 렐루(ReLU)
함수를 사용한다.
#은닉층. sigmoid함수를 써서 z값을 0과 1사이로 압축
#은닉층의 뉴런 개수는 임의로 설정하는데, 출력층의 뉴런보단 많아야 한다.
dense1 = keras.layers.Dense(100, activation='sigmoid', input_shape=(784,))
#출력층
dense2 = keras.layers.Dense(10, activation='softmax')
model = keras.Sequential([dense1, dense2])
model.summary()
#summary값
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 100) 78500
dense_1 (Dense) (None, 10) 1010
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
summary()
메서드를 출력하면 위처럼 유용한 정보를 얻을 수 있다. 맨 첫 줄에 모델의 이름이 나오고, 그 다음에 이 모델의 층이 순서로 나열된다. 층마다 층 이름, 클래스, 출력 크기, 모델 파라미터 개수가 출력된다. 이 모델의 경우 픽셀784개가 입력층의 뉴런 수고, 이것이 은닉층 100개 뉴런과 곱해지므로 78,400개의 파라미터가 있다. 거기에 뉴런마다 1개의 절편이 있으니 총 78,500개의 파라미터가 있다. 그리고선 두번째 층에서 100개의 은닉층 뉴런과 10개의 출력층 뉴런이 연결되니 1,000+10(절편), 총 1,010개의 파라미터가 있다. 이들을 합하면 summary에 나온 79,510 값이 도출된다.
층을 추가하는 다른 방법도 있다. 바로 다음처럼 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='패션 MNIST 모델')
하지만 위 방법은 층을 여러개 추가하면 Sequential 생성자가 너무 길어진다. 가장 널리 사용되는 방법은 add()
메서드이다. 이 방법은 한눈에 추가되는 층을 볼 수 있고 프로그램 실행 시 동적으로 층을 선택하여 추가할 수 있다.
model = keras.Sequential()
model.add(keras.layers.Dense(100, activation='sigmoid', input_shape=(784,)))
model.add(keras.layers.Dense(10, activation='softmax'))
시그모이드 함수는 양끝에서 그래프가 누워있기에 올바른 출력을 만드는데 신속하게 대응하지 못한다는 단점이 있다. 이를 개선하기 위해 등장한 활성화함수가 렐루(ReLU) 함수이다.
렐루 함수는 max(0,z)와 같이 쓸 수 있다. 양수일 경우엔 활성화 함수가 없는 양 값을 그냥 통과시키고 음수인 경우엔 0을 출력한다. 렐루 함수는 특히 이미지 처리에서 좋은 성능을 낸다.
model= keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28)))
#Flatten클래스는 입력차원을 일렬로 펼치는 역할
model.add(keras.layers.Dense(100,activation='relu')) #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 = 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)
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
#시그모이드 함수를 사용했을 때보다 성능이 조금 향상
07-2 핵심 키워드
- 심층 신경망: 2개 이상의 층을 포함한 신경망
- 렐루 함수: 이미지 분류 모델의 은닉층에 많이 사용하는 활성화함수
- 옵티마이저: 신경망의 가중치와 절편을 학습하기 위한 알고리즘. 케라스에는 다양한 경사 하강법 알고리즘이 옵티마이저로 구현되어 있다.
07-2 핵심 패키지와 함수
- TensorFlow
add()
: 케라스 모델에 층을 추가하는 메서드summary()
: 케라스 모델의 정보를 출력하는 메서드SGD
: 기본 경사 하강법 옵티마이저 클래스Adagrad
: Adagrad 옵티마이저 클래스RMSprop
: RMSprop 옵티마이저 클래스
07-3 신경망 모델 훈련
History
객체에는 훈련 과정에서 계산한 지표, 즉 손실과 정확도 값이 저장되어 있다. 이 값을 사용하면 그래프를 그릴 수 있다.
#데이터 준비
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: #층을 추가하면 은닉층 뒤에 또 하나의 층을 추가하는 if문
model.add(a_layer)
model.add(keras.layers.Dense(10, activation='softmax'))
return model
#fit()메서드의 결과를 history변수에 담기
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=5, verbose=0)
print(history.history.keys())
#결과값
dict_keys(['loss', 'accuracy'])
history 객체에는 손실과 정확도를 포함한 딕셔너리가 있다. 이는 각 에포크마다 계산한 값이 순서대로 나열된 단순한 리스트다. 맷플롯립을 사용해 쉽게 그래프로 그릴 수 있을 것이다.
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('loss')
plt.show()
에포크에 따른 과대/과소적합을 잘 파악하려면 훈련세트의 손실만 그려서는 안 되고 검증 세트의 손실 역시도 그려야 한다. 이를 위해선 fit()
메서드에 validation_data
매개변수를 입력해주면 된다.
model = model_fn()
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=20, verbose=0, validation_data=(val_scaled, val_target))
print(history.history.keys())
#결과값
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
#검증세트에 대한 손실과 정확도도 저장된 것을 볼 수 있다.
plt.plot(history.history['val_loss'])
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['val', 'train'])
plt.show()
노란색이 훈련세트의 손실, 파란색이 검증세트의 손실이다
초기에 검증 손실이 감소하다가 금방 다시 상승하기 시작한다. 검증손실이 다시 상승하는 시점을 가능한 뒤로 늦추면 훨씬 성능이 좋아질 것이다.
드롭아웃(dropout) 은 훈련 과정에서 층에 있는 일부 뉴런을 랜덤하게 꺼서 출력을 0으로 만들고, 과대적합을 막는 방법이다. 케라스에서는 드롭아웃을 keras.layers
패키지 아래 Dropout
클래스로 제공한다.
#앞서 정의한 model_fn함수에 드롭아웃 층을 넣어보자
model = model_fn(keras.layers.Dropout(0.3))
model.summary()
Model: "sequential_7"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_5 (Flatten) (None, 784) 0
dense_14 (Dense) (None, 100) 78500
dropout (Dropout) (None, 100) 0
dense_15 (Dense) (None, 10) 1010
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________
은닉층 뒤에 추가된 Dropout층을 보면 훈련되는 모델 파라미터가 없다. 일부 뉴런의 출력을 0으로 만들지만 전체 출력 배열의 크기를 바꾸지는 않는다. 당연히 훈련 종료 후 평가나 예측을 수행할 때는 드롭아웃이 적용되면 안 된다. 똑똑하게도 Dropout클래스는 평가/예측 시에 자동으로 드롭아웃을 적용하지 않는다. 이전과 마찬가지로 훈련손실과 검증손실 그래프를 그려보자
model.compile(optimizer='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()
과대적합이 확실히 줄어든 것을 볼 수 있다.
이제 이 모델의 훈련된 파라미터를 저장해보자.
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
history = model.fit(train_scaled, train_target, epochs=10, verbose=0, validation_data=(val_scaled, val_target))
model.save_weights('model-weights.h5') #훈련 파라미터를 저장하는 메서드
model.save('model-whole.h5') #모델 구조와 모델 파라미터를 함께 저장하는 메서드
콜백(callback) 은 훈련 과정 중간에 어떤 작업을 수행할 수 있게 하는 객체로 keras.callbacks
패키지 아래에 있는 클래스들이다. fit()
메서드의 callbacks
매개변수에 리스트로 전달하여 사용한다.
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='aparse_categorical_crossentropy', metrics='accuracy')
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-model.h5', save_best_only=True)
#save_best_only매개변수를 True로 설정하여 가장 낮은 검증 점수를 만드는 모델을 저장한다.
history.model.fit(train_scaled, train_target, epochs=20, verbose=0, validation_data=(val_scaled, val_target), callbacks=[checkpoint_cb, early_stopping_cb])
#checkpoint_cb를 만든 후 callbacks매개변수에 리스트로 감싸서 전달해준다.
#모델이 훈련한 후 best-model.h5에 최상의 검증 점수를 낸 모델이 저장된다.
더 나아가 과대적합이 시작되기 전에 미리 훈련을 중지하도록 할 수 있다. 이를 조기 종료(early stopping) 라고 부른다. 케라스에서는 조기 종료를 위한 EarlyStopping
콜백을 제공한다.
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='adam', loss='aparse_categorical_crossentropy', metrics='accuracy')
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-model.h5', save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=2, restore_best_weights=True)
#patience를 2로 지정하면 2번 연속 검증 점수 향상이 없을 시 훈련 중지
#retore_best_weights를 True로 지정하면 가장 낮은 검증 손실을 낸 모델 파라미터로 되돌림
history.model.fit(train_scaled, train_target, epochs=20, verbose=0, validation_data=(val_scaled, val_target), callbacks=[checkpoint_cb, early_stopping_cb])
#몇 에포크만에 훈련을 중지했는지
print(early_stopping_cb.stopped_epoch)
07-3 핵심 키워드
- 드롭아웃: 은닉층에 있는 뉴런의 출력을 랜덤하게 꺼서 과대적합을 막는 기법. 드롭아웃은 훈련 중에 적용되며 평가나 예측에서는 사용되지 않음.
- 콜백: 케라스 모델을 훈련하는 도중 어떤 작업을 수행할 수 있도록 도와주는 도구
- 조기 종료: 검증점수가 더 이상 감소하지 않고 상승하여 과대적합이 일어나면 훈련을 중지하는 기법
07-3 핵심 패키지와 함수
- TensorFlow
Dropout
- 첫번째 매개변수로 드롭아웃 할 비율을 지정
save_weights()
: 모든 층의 가중치와 절편을 파일에 저장load_weights()
: 모든 층의 가중치와 절편을 파일에 읽음save()
: 모델 구조와 모든 가중치와 절편을 파일에 저장ModelCheckpoint
: 케라스 모델과 가중치를 일정 간격으로 저장- 첫번째 매개변수에 저장할 파일 지정
monitor
매개변수에 모니터링할 지표 지정save_best_only
매개변수를 True로 지정하면 가장 낮은 점수를 만드는 모델을 저장
EarlyStopping
: 관심 지표가 더 이상 향상하지 않으면 훈련 중지patience
매개변수에 모델이 지속가능한 최대 에포크 횟수 지정
- Numpy
argmax
: 배열에서 축을 따라 최댓값의 인덱스를 반환