chapter_7_1

딥러닝

인공신경망 1943년 즈음 등장

1차 융성기 ~ 1960 후반

  • 로봇이 인간들과 함께 살 것이다 예언

  • XOR 문제 해결 못함

  • AI 연구 겨울 찾아옴

  • 대안 : 최근접이웃, 결정트리, 서포트벡터머신 등

  • 토론토 대학 AI 연구소 (역전파 알고리즘 개발)

  • CNN 알고리즘 (1980년대 후반)

-2차 융성시기 ~ 1990년대 후반

  • CNN, RNN 알고리즘 등장

  • 연산 속도 문제/정확도 문제

  • 산업계 즉시 활용 어려움

-3차 융성시기 2012 ~ 현재까지

  • GPU 세팅 (그래픽카드)

  • 연산속도문제가 해결

  • 세돌 vs 알파고 바둑 대회(2017년)

  • 정부에서도 본격적으로 투자

  • 교육쪽으로 먼저 투자 시작

  • 대학교육 + 국비교육

  • 데이터과학

2012년

  • CNN 알고리즘 논문 다수 출현

  • 이미지 기본데이터셋

    1. 기존대비 성능이 좋아야 함
    1. 개존대비 연산속도가 좋아야 함

→ 각자 딥러닝 관심 생김

→ 공부하는 패턴 : 최운선순위는 가장 최근 나온 알고리즘

분야가 정말 많음

  • 지도학습 : 분류/수치 예측(회귀)/비지도학습

  • 엑셀데이터(정형데이터)

  • 기초 통계가 중요(리포트 형태가 더 중요)

  • 개발의 상대적 중요성 떨어짐(성과 측면)

딥러닝:비정형데이터

  • 텍스트,음성,이미지,영상

  • 주로 쓰이는 알고리즘 탐색 (최신 알고리즘)

  • 계속 업그레이드 되고 있음

  • 이세돌 vs 알파고 바둑 대회 (2017년)

  • 데이터과학

지도 학습 vs 딥러닝

→ 개발자라면 딥러닝 알고리즘을 가져다가 빠르게 개발하는 기술을 습득.

딥러닝 라이브러리

1
2
import tensorflow
print(tensorflow.__version__)
2.8.0

데이터 불러오기

패션 MNIST

  • 10종류의 패션 아이템으로 구성된 데이터셋
  • 텐서프로를 사용해 이 데이터를 불러온다.
1
2
3
from tensorflow import keras
(train_input, train_target), (test_input, test_target)= keras.datasets.fashion_mnist.load_data()
# load.data()함수는 훈련 데이터와 테스트 데이터를 나누어 반환한다.
  • 데이터 확인

  • 훈련 데이터

    • 60,000개 이미지, 이미지 크기는 28x28
    • 타깃은 60,000개 원소가 있는 1차원 배열
1
print(train_input.shape, train_target.shape)
(60000, 28, 28) (60000,)
  • 테스트 세트
    • 10,000개의 이미지로 이루어짐
1
print(test_input.shape, test_target.shape)
(10000, 28, 28) (10000,)
  • 이미지 시각화
1
2
3
4
5
6
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()

png

  • 타겟 값 리스트
    • 패션 MNIST의 타깃은 0~9까지의 숫자 레이블로 구성된다.
    • 같은 숫자가 나온다면 타깃이 같은 두 샘플은 같은 종류의 옷이다.
1
print([train_target[i] for i in range(10)])
[9, 0, 0, 3, 0, 2, 7, 2, 5, 5]
  • 실제 타겟값의 값을 확인
  • 각 라벨당 6000개의 이미지 존재 60,000개
  • 즉, 각 의류마다 6,000개의 샘플이 들어있다.
1
2
import numpy as np
print(np.unique(train_target, return_counts = True))
(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000]))

로지스틱 회귀로 패션 아이템 분류하기

  • 경사하강법 (기울기)
    • 샘플이 60,000개나 되기에 샘플을 하나씩 꺼내서 모델을 훈련하는 게 더 효율적
    • 해당 상황에 맞는 것이 강사하강법이다.
  • 전제 조건 : 각 컬럼의 데이터셋 동일 (표준화)
  • why 255? 각 픽셀의 값 0~255 사이의 정수값을 가진다.
  • 255로 나누어 0~1 사이의 값으로 정규화 시킴
    • 표준화는 아니지만 양수 값으로 이루어진 이미지를 전처리할 때 사용하는 방벙
1
2
3
4
5
train_scaled = train_input / 255.0

# 경사하강법 사용을 위해 1차원 배열로 만들기
train_scaled = train_scaled.reshape(-1, 28*28)
print(train_scaled.shape)
(60000, 784)

모델 만들기

  • 비정형데이터에 선형모델 또는 비선형모델을 적용시키는 것이 합리적인가?

    • 결론은 아니다!
    • 다른 대안이 있는냐? 인공신경망!
  • 정형데이터에 인공신경망 및 딥러닝 모델을 적용시키는 것이 합리적인가?

    • 결론은 아니다!
  • SGDClassifier 클래스와 cross_validate 함수로 이 데이터에서 교차 검증으로 성능을 확인한다.

1
2
3
4
5
6
7
from sklearn.model_selection import cross_validate
from sklearn.linear_model import SGDClassifier

sc = SGDClassifier(loss='log', max_iter=5, random_state=42) # 반복 횟수를 5번으로 지정

scores = cross_validate(sc, train_scaled, train_target, n_jobs=-1)
print(np.mean(scores['test_score']))
0.8195666666666668
  • 로지스틱 회귀 공식을 그림으로 나타내면 인공신경망의 그림과 같다.
  • 인동신경망을 만들어 패션 아이템 분류 문제의 성능을 높일 수 있는지 지켜보자.

참고

인공신경망 모델 적용

  • 이미지 분류에는 인공 신경망이 적합하다.
1
2
import tensorflow as tf
from tensorflow import keras
  • 텐서플로 = 케라스
  • 케라스 API를 사용해 패션 아이템을 분류하는 가장 간단한 인공 신경망을 만들어 보자.
  • train_test_split()
1
2
3
4
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)
  • test_size=0.2
    • 훈련 세트에서 20%를 검증 세트로 덜어 내었다.
  • 훈련 세트와 검증 세트의 크기를 알아보자.
1
print(train_scaled.shape, train_target.shape)
(48000, 784) (48000,)
1
print(val_scaled.shape, val_target.shape)
(12000, 784) (12000,)
  • 60,000개 중에 12,000개가 검증 세트로 분리되었다.

  • 먼저 훈련 세트로 모델을 만든다. 그 다음 검증 세트로 훈련한 모델을 평가해본다.

  • 이미지 하나에 있는 픽셀은 784개. 뉴런은 10개. 이것을 모두 연결.

  • 완전 연결층 = 밀집층 = 밀집하게 연결되어 있는 것

    • fully connected layer = dense layer
1
print(train_target[:10])
[7 3 5 8 6 9 3 3 9 9]
  • Dense 클래스를 통해 밀집층을 만들어보자
  • 활성화 함수
    • softmax와 같이 뉴런의 선형 방정직 계산 결과에 적용되는 함수.
1
2
# 매개변수의 의미는 차례대로 뉴런개수, 뉴런의 출력에 적용할 함수, 입력의 크기다.
dense = keras.layers.Dense(10, activation = 'softmax', input_shape=(784, ))
  • 방금 만든 밀집층을 가진 신경망 모델을 만들자.
  • Sequential 클래스를 사용한다.
1
model = keras.Sequential(dense)

인공 신경망으로 패션 아이템 분류하기

  • 훈련하기 전의 설정 단계
1
model.compile(loss = 'sparse_categorical_crossentropy', metrics = "accuracy")
  • 모델을 훈련한다.
    • 반복할 에포크 횟수를 epochs 매개변수로 지정
1
model.fit(train_scaled, train_target, epochs = 5)
Epoch 1/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4782 - accuracy: 0.8383
Epoch 2/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4574 - accuracy: 0.8484
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4450 - accuracy: 0.8525
Epoch 4/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.4372 - accuracy: 0.8549
Epoch 5/5
1500/1500 [==============================] - 2s 2ms/step - loss: 0.4318 - accuracy: 0.8575





<keras.callbacks.History at 0x7fb1c0b7f450>
  • 갈수록 정확도가 증가함을 알 수 있다.
  • 검증 세트(val_scaled, val_target)에서 모델의 성능을 확인한다.
1
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 1ms/step - loss: 0.4530 - accuracy: 0.8463





[0.4530307352542877, 0.8463333249092102]
  • Reference : 혼자 공부하는 머신러닝 + 딥러닝

chapter_7_2

심층 신경망

  • 인공신경망에 층을 여러 개 추가하여 패션 MNIST 데이터셋을 분류한다.

  • 동시에 케라스로 심층 신경망을 만들어본다.

  • 368p 그림 참고

  • 케라스로 API를 사용해 패션 MNIST 데이터셋을 불러온다.

1
2
3
from tensorflow import keras

(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
40960/29515 [=========================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
26435584/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
16384/5148 [===============================================================================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step
4431872/4422102 [==============================] - 0s 0us/step
  • 이미지의 픽셀값을 0 ~ 255 범위에서 0 ~ 1로 변환
  • 28x28 크기의 2차원 배열을 784 크기인 1차원 배열로 펼친다.
  • train_test_split() 함수로 훈련 세트와 검증 세트로 나눈다.
1
2
3
4
5
6
7
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)
  • 입력층과 출력층 사이에 밀집층을 만들 예정이다.

  • 은닉층 : 입력층과 출력층 사이에 있는 모든 층

  • 케라스의 Dense 클래스로 다음 내용을 만든다.

    • sigmoid 활성화 함수를 사용한 은닉층
    • softmax 함수를 사용한 출력층
  • 층을 추가하는 방법

    • Dense 클래스의 객체 dense1, 2를 만들어 Sequential 클래스에 전달한다.
1
2
dense1 = keras.layers.Dense(100, activation='sigmoid', input_shape=(784,))
dense2 = keras.layers.Dense(10, activation='softmax')
  • dense1이 은닉층이고 100개의 뉴런을 가진 밀집층이다.
    • 활성화 함수를 ‘sigmoid’로 지정했고 매개변수로 입력의 크기를 (784,)로 지정했다.
  • dense2는 출력층이다.
    • 10개의 클래스를 분류하므로 10개의 뉴런을 두었고 활성화 함수는 softmax로 지정했다.

심층 신경망

  • 컨셉만 이해하라!

  • 직접 신경망 만들 일은 없고 가져다 쓰기만 하면 된다.

  • 앞의 dense1과 dense2 객체를 Sequential 클래스에 추가하여 심층 신경망을 만들 예정이다.

1
2
model = keras.Sequential([dense1, dense2])
model.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
_________________________________________________________________
  • 위와 같이 Sequential 클래스의 객체를 만들 때 여러 개의 층을 추가하려면 층을 리스트로 만들어 전달해야 한다.
  • model.summary()로 층에 대한 정보를 얻을 수 있다.
    • 첫 줄에 모델의 이름이 나온다.
    • 이 모델에 들어 있는 층이 순서대로 나열된다.
      • 이 순서는 맨 처음 추가한 은닉층에서 출력층의 순서로 나열된다.
    • 층마다 층 이름, 클래스, 출력 크기, 모델 파라미터 개수가 출력된다.
    • name 매개변수로 이름을 지정하지 않으면 디폴트인 ‘dense’로 네이밍된다.
    • 출력 크기는 (None,100)인데, 첫 번째 차원은 샘플 개수를 나타낸다.
      • None인 이유는 어떤 배치 크기에도 잘 대응하기 위함이다.
      • 두 번째 차원인 100은 뉴런 개수가 100이며, 따라서 100개의 출력이 나옴을 나타낸다.

층을 추가하는 다른 방법

  • Sequential 클래스의 생성자 안에서 바로 Dense 클래스의 객체를 만든다.
1
2
3
4
5
model = keras.Sequential([
keras.layers.Dense(100, activation='sigmoid', input_shape=(784,), name='hidden'), # 층을 쌓아간다
keras.layers.Dense(10, activation='softmax', name='output') # 층을 쌓아간다
], name='패션 MNIST 모델')
model.summary()
Model: "패션 MNIST 모델"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 hidden (Dense)              (None, 100)               78500     
                                                                 
 output (Dense)              (None, 10)                1010      
                                                                 
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________

층을 추가하는 다른 방법 2

  • Sequential 클래스의 객체를 만들고 이 객체의 add() 메서드를 호출하여 층을 추가한다.
1
2
3
4
5
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: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_2 (Dense)             (None, 100)               78500     
                                                                 
 dense_3 (Dense)             (None, 10)                1010      
                                                                 
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________
  • 이제 모델을 훈련한다.
    • 반복할 에포크 횟수를 epochs 매개변수로 지정
1
2
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
Epoch 1/5
1500/1500 [==============================] - 6s 3ms/step - loss: 0.5628 - accuracy: 0.8069
Epoch 2/5
1500/1500 [==============================] - 4s 3ms/step - loss: 0.4087 - accuracy: 0.8522
Epoch 3/5
1500/1500 [==============================] - 5s 3ms/step - loss: 0.3747 - accuracy: 0.8645
Epoch 4/5
1500/1500 [==============================] - 4s 3ms/step - loss: 0.3506 - accuracy: 0.8737
Epoch 5/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.3344 - accuracy: 0.8784





<keras.callbacks.History at 0x7f5bcb861b50>
  • 렐루 함수

    • 층이 많은 신경망일수록 그 효과가 누적되어 학습이 어려워진다.
    • 이를 개선하기 위한 활성화 함수이다.
    • relu() 함수는 입력이 양수일 그냥 통과시키고, 입력이 음수라면 0으로 만든다.
  • Flatten 클래스

    • 배치 차원을 제외하고 나머지 입력 차원을 모두 일렬로 펼친다.
    • Flatten 클래스를 층처럼 입렬층과 은닉층 사잉에 추가하기 때문에 이를 층이라 부른다.
    • 다음 코드처럼 입력층 바로 뒤에 추가한다.
1
2
3
4
5
6
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28))) # 기존 코드 비교
model.add(keras.layers.Dense(100, activation='relu')) # relu 로 변경
model.add(keras.layers.Dense(10, activation='softmax'))

model.summary()
Model: "sequential_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense_4 (Dense)             (None, 100)               78500     
                                                                 
 dense_5 (Dense)             (None, 10)                1010      
                                                                 
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________
  • 훈련 데이터를 다시 준비해서 모델을 훈련한다.
1
2
3
4
5
6
(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)
  • 모델을 컴파일하고 훈련한다.
1
2
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
Epoch 1/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.5283 - accuracy: 0.8151
Epoch 2/5
1500/1500 [==============================] - 4s 3ms/step - loss: 0.3926 - accuracy: 0.8602
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3562 - accuracy: 0.8713
Epoch 4/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.3336 - accuracy: 0.8809
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3203 - accuracy: 0.8853





<keras.callbacks.History at 0x7f5bcb762a10>
  • 시그모이드 함수를 사용했을 때와 비교하면 성능이 조금 향상되었다.
  • 검증 세트에서의 성능도 확인하자.
1
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 3ms/step - loss: 0.3713 - accuracy: 0.8717





[0.3712655007839203, 0.871749997138977]
  • 검증 성능도 향상되었다.
1
2
3
4
5
6
(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)
1
2
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
Epoch 1/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.3094 - accuracy: 0.8890
Epoch 2/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.2989 - accuracy: 0.8951
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.2902 - accuracy: 0.8974
Epoch 4/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.2825 - accuracy: 0.9018
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.2781 - accuracy: 0.9024





<keras.callbacks.History at 0x7f5bcb835d10>
1
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 1ms/step - loss: 0.4110 - accuracy: 0.8792





[0.41104814410209656, 0.8791666626930237]

옵티마이저의 개념

–> Adam 사용하라
–> why Adam? 최고점을 찾기 위해서

  • 스텝방향 & 스템사이즈를 모두 고려한 옵티마이저
  • 스텝방향 : GD, SGD, Momentum, NAG
  • 스텝사이즈 : GD, SGD, Adagrad, RMSProp
  • 하이퍼 파라미터는 사람이 지정해야 하는 파라미터
  • 신경망에는 특히 하이퍼 파라미터가 많다.
  • 은닉층의 뉴런 개수도 하이퍼 파라미터이다.
  • compile() 에서는 케라스의 기본 하강법 알고리즘인 RMSprop을 사용했다.
    • 케라스는 다양한 종류의 경사 하강법 알고리즘을 제공한다.
    • 이들을 ‘옵티마이저’라고 부른다.

옵티마이저

  • 381p
  • SGD 옵티마이저를 사용하려면 compile() 메서드의 optimizer 매개변수를 ‘sgd’로 지정
1
model.compile(optimizer='sgd', loss='sparse_categorical_crossentropy', metrics='accuracy')
  • ‘sgd’ 문자열은 이 클래스의 기본 설정 매개변수로 생성한 객체와 동일하다.
  • 다음 코드는 위의 코드와 정확히 동일하다.
1
2
sgd = keras.optimizers.SGD()
model.compile(optimizer=sgd, loss='sparse_categorical_crossentropy', metrics='accuracy')
  • 382p
  • learning_rate = 0.1
    • 만약 SGD 클래스의 학습률 기본값이 0.01일 때 이를 바꾸고 싶다면 다음와 같이 지정한다.
  • 랜덤서치, 그리드서치
  • 딥러닝에서도 하이퍼파라미터 튜닝
1
sgd = keras.optimizers.SGD(learning_rate = 0.1)
  • 기본 경사 하강법 옵티마이저는 모두 SGD 클래스에서 제공한다.
  • SGD 클래서의 momentum 매개변수의 기본값은 0이다. 보통 0.9이상을 지정한다.
  • 다음처럼 SGD 클래스의 nesterov 매개변수를 기본값 False 에서 True로 바꾸면 네스테로프 모멘텀 최적화를 사용한다.
    • 테스테로프 모멘텀은 모멘텀 최적화를 2번 반복하여 구현한다.
    • 대부분의 경우 네스테로프 모멘텀 최적화가 기본 확률적 경사 하강법보다 더 나은 성능을 제공한다.
1
sgd = keras.optimizers.SGD(momentum = 0.9, nesterov = True)
  • 적응적 학습률

    • 모델이 최적점에 가까이 갈수록 학습률을 낮출 수 있다.
    • 이렇게 하면 안정적으로 최적점에 수렴할 가능성이 높다.
    • 이런 학습률을 적응적 학습률이라고 한다.
  • Adagrad() 클래스

    • 적응적 학습률을 사용하는 대표적인 옵티마이저이다.
    • optimizer 매개변수에서 지정할 수 있다.
    • optimizer 매개변수의 기본값이 바로 rmsprop이다.
1
2
adagrad = keras.optimizers.Adagrad()
model.compile(optimizer=adagrad, loss='sparse_categorical_crossentropy', metrics='accuracy')
  • RMSprop() 클래스
    • 적응적 학습률을 사용하는 대표적인 옵티마이저이다.
    • optimizer 매개변수에서 지정할 수 있다.
    • optimizer 매개변수의 기본값이 바로 rmsprop이다.
1
2
rmsprop = keras.optimizers.RMSprop()
model.compile(optimizer=rmsprop, loss='sparse_categorical_crossentropy', metrics='accuracy')
  • 다만, Adam을 사용하는 것이 더 좋다.

  • Adam

    • 모멘텀 최적화와 RMSprop의 장점을 접목한 것이 Adam이다.
    • 적응적 학습률을 사용하는 이 3개의 클래스는 learning_rate 매개변수의 기본값을 0.001로 두고 사용한다.
  • Adam 클래스의 매개변수 기본값을 사용해 패션 MNIST 모델을 훈련해본다.

  • 일단 모델을 다시 생성한다.

1
2
3
4
model = keras.Sequential()
model.add(keras.layers.Flatten(input_shape=(28,28))) # 기존 코드 비교
model.add(keras.layers.Dense(100, activation='relu')) # relu 로 변경
model.add(keras.layers.Dense(10, activation='softmax'))
  • compile() 메서드의 optimizer를 ‘adam’으로 설정하고 5번의 에포크 동안 훈련한다.
1
2
3
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')
model.fit(train_scaled, train_target, epochs=5)
model.evaluate(val_scaled, val_target)
Epoch 1/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.5293 - accuracy: 0.8155
Epoch 2/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3980 - accuracy: 0.8571
Epoch 3/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3542 - accuracy: 0.8713
Epoch 4/5
1500/1500 [==============================] - 4s 3ms/step - loss: 0.3287 - accuracy: 0.8798
Epoch 5/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.3081 - accuracy: 0.8867
375/375 [==============================] - 1s 2ms/step - loss: 0.3296 - accuracy: 0.8806





[0.32961416244506836, 0.8805833458900452]
  • 결과를 보면 기본 RMSprop을 사용했을 때와 거의 같은 성능을 보인다.
  • 검증 세트에서의 성능도 확인한다.
1
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 3ms/step - loss: 0.3296 - accuracy: 0.8806





[0.32961416244506836, 0.8805833458900452]
  • 환경마다 차이가 있을 수 있지만 여기서는 기본 RMSprop보다 조금 더 나은 성능을 보인다.

  • Reference : 혼자 공부하는 머신러닝 + 딥러닝

chapter_7_3

7-3. 신경망 모델 훈련

  • 케라스 API를 사용해 모델을 훈련하는데 필요한 다양한 도구들을 알아본다.

손실곡선

  • 패션 MNIST 데이터셋을 적재하고 훈련 세트와 검증 세트로 나눈다.
1
2
3
4
5
6
7
8
9
10
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)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
40960/29515 [=========================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 1s 0us/step
26435584/26421880 [==============================] - 1s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
16384/5148 [===============================================================================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step
4431872/4422102 [==============================] - 0s 0us/step
  • 모델을 만든다.
    • 사용자 정의함수를 작성함
    • if 구문을 제외하면 7-2의 코드와 동일하다.
    • if 구문의 역할은 model_fn() 함수에 케라스 층을 추가하면 은닉층 뒤어 또 하나의 층을 추가하는 것이다.
  • 모델 구조를 출력해본다.
1
2
3
4
5
6
7
8
9
10
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(100, activation='softmax'))
return model
model = model_fn()
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                /images/chapter_7_3/output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense (Dense)               (None, 100)               78500     
                                                                 
 dense_1 (Dense)             (None, 100)               10100     
                                                                 
=================================================================
Total params: 88,600
Trainable params: 88,600
Non-trainable params: 0
_________________________________________________________________
  • 모델 정의 후, 학습
  • fit() 메서드의 결과를 history 변수에 담아본다.
1
2
model.compile(loss='sparse_categorical_crossentropy', metrics = 'accuracy')
history = model.fit(train_scaled, train_target, epochs = 5, verbose = 1)
Epoch 1/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.5574 - accuracy: 0.8081
Epoch 2/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.3972 - accuracy: 0.8574
Epoch 3/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.3572 - accuracy: 0.8710
Epoch 4/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.3353 - accuracy: 0.8805
Epoch 5/5
1500/1500 [==============================] - 4s 2ms/step - loss: 0.3187 - accuracy: 0.8855
  • history 객체 값은 무슨 값이 있냐?
    • history 객체에는 훈련 측정값이 담겨 있는 history 딕셔너리가 들어 있다.
    • dictionary 값으로 출력되기 때문에 다음과 같이 작성
1
print(history.history.keys())
dict_keys(['loss', 'accuracy'])
  • 결과 : 손실과 정확도가 포함되어 있다.

  • 손실 곡선

    • history 속성에 포함된 손실과 정확도는 에포크마다 계산한 값이 순서대로 나열된 단순한 리스트이다.
    • 멧플롯립으로 간단히 그릴 수 있다.
1
2
3
4
5
6
import matplotlib.pyplot as plt

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

png

  • 정확도 출력
    • 이번에는 정확도를 출력해본다.
1
2
3
4
plt.plot(history.history['accuracy'])
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()

png

  • 확실히 에포크마다 손실이 감소하고 정확도가 향상됨을 알 수 있다.
  • 계속 손실이 감소하는지 확인해보자.
    • 에포크를 20으로 늘려서 모델을 훈련하고 손실을 그려본다.
1
2
3
4
5
6
7
8
model = model_fn()
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')

history = model.fit(train_scaled, train_target, epochs=20, verbose=0) # 수치 조정
plt.plot(history.history['loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

png

  • 예상대로 손실이 잘 감소한다.

  • 검증손실

    • 다음과 같이 loss, accuracy, val_loss, val_accuracy 가 출력되도록 하는 것이 정석이다.
    • 에포크마다 검증 손실을 계산하기 위해 케라스 모델의 fit()메서드에 검증 데이터를 전달할 수 있다.
1
2
3
4
5
model = model_fn()
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')

history = model.fit(train_scaled, train_target, epochs=10, verbose=1,
validation_data=(val_scaled, val_target))
Epoch 1/10
1500/1500 [==============================] - 9s 6ms/step - loss: 0.5619 - accuracy: 0.8060 - val_loss: 0.4507 - val_accuracy: 0.8375
Epoch 2/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.3984 - accuracy: 0.8571 - val_loss: 0.3923 - val_accuracy: 0.8600
Epoch 3/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.3603 - accuracy: 0.8704 - val_loss: 0.3582 - val_accuracy: 0.8761
Epoch 4/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.3351 - accuracy: 0.8792 - val_loss: 0.3619 - val_accuracy: 0.8770
Epoch 5/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.3207 - accuracy: 0.8860 - val_loss: 0.3707 - val_accuracy: 0.8754
Epoch 6/10
1500/1500 [==============================] - 4s 3ms/step - loss: 0.3084 - accuracy: 0.8907 - val_loss: 0.3775 - val_accuracy: 0.8703
Epoch 7/10
1500/1500 [==============================] - 4s 3ms/step - loss: 0.2998 - accuracy: 0.8948 - val_loss: 0.3707 - val_accuracy: 0.8787
Epoch 8/10
1500/1500 [==============================] - 4s 3ms/step - loss: 0.2901 - accuracy: 0.8981 - val_loss: 0.3494 - val_accuracy: 0.8805
Epoch 9/10
1500/1500 [==============================] - 4s 3ms/step - loss: 0.2815 - accuracy: 0.9015 - val_loss: 0.3691 - val_accuracy: 0.8823
Epoch 10/10
1500/1500 [==============================] - 4s 3ms/step - loss: 0.2756 - accuracy: 0.9034 - val_loss: 0.4148 - val_accuracy: 0.8700
  • 과대 / 과소적합 문제를 조사하기 위해 훈련 손실과 검증 손실을 한 그래프에 그려서 비교해본다.
1
2
3
4
5
6
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

png

  • 검증 데이터 val이 갈수록 손실이 증가한다.
  • 더 나은 그래프를 위해 조정해본다.
  • 위 내용에서 optimizer = adam을 추가
1
2
3
4
5
6
7
8
9
10
11
12
model = model_fn()
model.compile(optimizer = 'adam', loss='sparse_categorical_crossentropy', metrics='accuracy') # adam 추가

history = model.fit(train_scaled, train_target, epochs=10, verbose=1,
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()
Epoch 1/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.5635 - accuracy: 0.8080 - val_loss: 0.5627 - val_accuracy: 0.7847
Epoch 2/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.4053 - accuracy: 0.8535 - val_loss: 0.3899 - val_accuracy: 0.8593
Epoch 3/10
1500/1500 [==============================] - 5s 3ms/step - loss: 0.3595 - accuracy: 0.8705 - val_loss: 0.3780 - val_accuracy: 0.8627
Epoch 4/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.3311 - accuracy: 0.8785 - val_loss: 0.3409 - val_accuracy: 0.8767
Epoch 5/10
1500/1500 [==============================] - 5s 3ms/step - loss: 0.3130 - accuracy: 0.8855 - val_loss: 0.3361 - val_accuracy: 0.8784
Epoch 6/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.2950 - accuracy: 0.8899 - val_loss: 0.3473 - val_accuracy: 0.8775
Epoch 7/10
1500/1500 [==============================] - 5s 4ms/step - loss: 0.2818 - accuracy: 0.8961 - val_loss: 0.3380 - val_accuracy: 0.8781
Epoch 8/10
1500/1500 [==============================] - 5s 3ms/step - loss: 0.2707 - accuracy: 0.9003 - val_loss: 0.3430 - val_accuracy: 0.8823
Epoch 9/10
1500/1500 [==============================] - 6s 4ms/step - loss: 0.2623 - accuracy: 0.9024 - val_loss: 0.3381 - val_accuracy: 0.8830
Epoch 10/10
1500/1500 [==============================] - 5s 3ms/step - loss: 0.2520 - accuracy: 0.9064 - val_loss: 0.3427 - val_accuracy: 0.8813

png

  • val의 손실이 성공적으로 줄어들었다.
  • 구글링 : image classification django -> 개발자라면 공부해봐라
  • 구글링 : image classification tensorflow -> 이것도

드롭아웃

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

  • 기본적으로는 모든 파라미터를 연산하는 것이 원칙

    • 그런데, 일부 뉴런에서 출력이 없는 뉴런 발생
    • 기존 일부 뉴런은 계산에서 제외 시킴
  • 인공신경망(뇌과학)

    • 값이 쏠림 현상 = 뇌에 피가 고인 현상
      = 뇌출혈
  • 앞서 정의한 model_fn() 함수에 드롭아웃 객체를 전달하여 층을 추가해본다.

  • 여기에서 30% 정도를 드롭아웃한다.

1
2
model = model_fn(keras.layers.Dropout(0.3)) # 30% 드롭아웃
model.summary()
Model: "sequential_4"
_________________________________________________________________
 Layer (type)                /images/chapter_7_3/output Shape              Param #   
=================================================================
 flatten_4 (Flatten)         (None, 784)               0         
                                                                 
 dense_8 (Dense)             (None, 100)               78500     
                                                                 
 dropout (Dropout)           (None, 100)               0         
                                                                 
 dense_9 (Dense)             (None, 100)               10100     
                                                                 
=================================================================
Total params: 88,600
Trainable params: 88,600
Non-trainable params: 0
_________________________________________________________________
  • 결과. 은닉층 뒤에 추가된 드롭아웃 층(Dropout)은 훈련되는 모델 파라미터가 없다.
  • 일부 뉴런의 출력을 0으로 만들지만 전체 출력 배열의 크기를 바꾸지는 않는다.
  • 그래서 마음 편하게 검증 점수를 계산할 수 있다.
  • 드롭아웃한 상태에서 이전과 마찬가지로 훈련 손실과 검증 손실의 그래프를 그려 비교해본다.
1
2
3
4
5
6
7
8
9
10
11
model.compile(optimizer = 'adam', loss='sparse_categorical_crossentropy', metrics='accuracy') # adam 추가

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()

png

  • 과대적합이 확실히 줄었다.
  • 다만, 20번의 에포크 동안 훈련했기에 결국 다소 과대적합이 되었다.
  • 여기서 더 과대적합 하지 않게 하려면 에포크 횟수를 10으로 하고 다시 훈련하면 된다.

모델 저장과 복원

  • 개발자 : 정확도는 중요하지 않음

    • 딥러닝 모델 활용해서 웹앱을 개발
  • 분석가 & 머신러닝 엔지니어 : 캐글대회(정확도 검증 필수)

  • 에포크 횟수를 10으로 하고 다시 훈련한다.

  • 그리고 나중에 사용하려면 이 모델을 저장해야 한다.

1
2
3
4
5
model = model_fn(keras.layers.Dropout(0.3))                                                    # 30% 드롭아웃
model.compile(optimizer = 'adam', loss='sparse_categorical_crossentropy', metrics='accuracy') # adam 추가

history = model.fit(train_scaled, train_target, epochs=20, verbose=0, # 수치 조정
validation_data=(val_scaled, val_target))
  • save_weights()
    • 훈련된 모델의 파라미터를 저장한다.
  • save()
    • 모델 구조와 모델 파라미터를 함께 저장한다.
1
2
model.save_weights('model-weights.h5')
model.save('model-whole.h5')
  • 두 가지 실험을 해본다.

    • 첫 번째는 훈련을 하지 않은 새로운 모델을 만들고 model-weights.h5 파일에서 훈련된 모델 파라미터를 읽어서 사용한다.
    • 두 번째는 아예 model-whole.h5 파일에서 새로운 모델을 만들어 바로 사용한다.
  • 첫 번째 실험

    • 모델 불러오기
1
2
model = model_fn(keras.layers.Dropout(0.3))
model.load_weights('model-weights.h5')
  • 406p
  • 10개 확률 중에 가장 큰 값의 인덱스를 골라 타깃 레이블과 비교하여 정확도를 계산해 본다.
1
2
3
4
import numpy as np

val_labels = np.argmax(model.predict(val_scaled), axis=-1)
print(np.mean(val_labels == val_target))
0.8840833333333333
  • 모델 전체를 파일에서 읽은 다음 검증 세트의 정확도를 출력해 본다.
  • load_model()을 이용하여 파일을 읽으면 된다.
1
2
model = keras.models.load_model('model-whole.h5')
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 3ms/step - loss: 0.3263 - accuracy: 0.8841





[0.326292484998703, 0.8840833306312561]
  • 같은 모델을 저장하고 다시 불렀기에 이전 코드와 동일한 정확도를 얻었다.

콜백

  • 408p
  • 지금까지 20번의 에포크 동안 모델을 훈련하여 검증 점수가 상승하는 지점을 확인했다.
  • 이전처럼 모델을 두 번씩 훈련하지 않고 한 번에 끝내기 위해 콜백을 사용할 수 있다.
  • 콜백 = 훈련 과정 중간에 어떤 작업을 수행할 수 있게 하는 객체이다.
1
2
3
4
5
6
7
8
9
10
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='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])
<keras.callbacks.History at 0x7f3da939e310>
  • model_fn()함수로 모델을 만들고 compile()을 호출한다.
  • 모델이 훈련한 후에 best-model.h5에 최상의 검증 점수를 낸 모델이 저장된다.
  • 이 모델을 load_model()함수로 다시 읽어서 예측을 수행한다.
1
2
model = keras.models.load_model('best-model.h5')
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 1s 2ms/step - loss: 0.3197 - accuracy: 0.8858





[0.31966158747673035, 0.8858333230018616]
  • EarlyStopping

    • 조기 종료
    • 에포크를 많이 주면 줄수록 성능(가중치 업데이트 / 기울기가 계속 미분)이 좋아야 하는 것이 원리
    • 에포크 100 / 50 에포크 시점과 90 에포크 시점 성능 차이 없음
    • 즉, 계속 진행해도 좋아질지 안 좋아질지 모르기에 조기 종료하는 것.
  • EarlyStopping 콜백을 ModelCheckpoint 콜백과 함께 사용하면 가장 낮은 검증 손실의 모델을 파일에 저장한다.

  • 그리고 검증 손실이 다시 상승할 때 훈련을 중지할 수 있다.

  • 훈련을 중지한 다음 현재 모델의 파라미터를 최상의 파라미터로 되돌린다.

  • 두 콜백을 함께 사용해보자.

1
2
3
4
5
6
7
8
9
10
11
12
model = model_fn(keras.layers.Dropout(0.3))
model.compile(optimizer='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, # patience는 몇 개의 콜백을 리스트로 전달할지 결정한다.
restore_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])
  • 몇 번째 훈련에서 중지되는지 다음 코드로 확인할 수 있다.
1
print(early_stopping_cb.stopped_epoch)
10
  • epoch 값이 10에 다다랐을 때, ‘조기종료’한다.
1
2
3
4
5
6
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

png

  • 이런 식으로 조기 종료 기법을 사용하면 안심하고 에포크 횟수를 크게 지정해도 괜찮다.

  • Reference : 혼자 공부하는 머신러닝 + 딥러닝