8-1에서 배운 내용을 토대로 패션 MNIST 구현해보자.

 

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.reshape(-1, 28, 28, 1) / 255.0
train_scaled, val_scaled, train_target, val_target = train_test_split(
train_scaled, train_target, test_size=0.2, random_state=42 )

reshape.(-1,28,28,1) -> 4차원 배열로 

 

이제 합성곱 신경망을 만들어보자..!

합성곱 신경망 만들기

model = keras.Sequential()
model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu',
			padding = 'same', input_shape=(28,28,1)))

매개변수 설명 : 필터 32개를 사용

커널의 크기는 3x3

활성화 함수 : 렐루 함수

세임패딩 사용 

입력의 차원 28,28,1

 

풀링 층 추가  ( 차원 축소시키는것 )

model.add(keras.MaxPooling2D(2))

최대 풀링 = MaxPooling

평균 풀링 = AveragePooling

 

위 코드에서는 최대값 풀링을 사용했고, (2,2)로  설정했다.   ---> 가로세로가 같으면 숫자 하나만 써도 가능

 

패션 MNIST 이미지 => 28x28크기에 세임 패딩 -> 출력된 특성 맵 또한 28x28 -> 2,2 풀링했으므로 특성맵 크기 절반으로 됨. 특성 맵의 깊이 32가 됨, 따라서 최대 풀링을 통과한 특성 맵의 크기는 (14,14,32)가 될 것.

 

첫 번째 CNN풀링층 다음에 두 번째 CNN 풀링 층을 추가해보자.

model.add(keras.layers.Conv2D(64, kernel_size=3, activation = 'relu',
			padding = 'same'))
model.add(keras.layers.MaxPooling2D(2))

처음과 동일 (필터개수 64개 만 바뀌었다.)

최종 출력되는 특성 맵은 (7,7,64)가 될 것이다.

 

이제 이 특성맵을 일렬로 펼쳐보자.

 

model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(100, activation = 'relu'))
model.add(keras.layers.Dropout(0.4))
model.add(keras.layers.Dense(10, activation = 'softmax'))

은닉층과 출력층 사이에 드롭아웃을 넣었다.

 

이제 summary() 해보자.

Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 28, 28, 32)        320       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 14, 14, 32)       0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 14, 14, 64)        18496     
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 7, 7, 64)         0         
 2D)                                                             
                                                                 
 flatten (Flatten)           (None, 3136)              0         
                                                                 
 dense (Dense)               (None, 100)               313700    
                                                                 
 dropout (Dropout)           (None, 100)               0         
                                                                 
 dense_1 (Dense)             (None, 10)                1010      
                                                                 
=================================================================
Total params: 333,526
Trainable params: 333,526
Non-trainable params: 0
_________________________________________________________________

출력된 것을 보고 각 층을 통과하면서 특성맵의 깊이가 32, 64로 각각 변하는것을 볼 수 있다.

 

모델 컴파일과 훈련

model checkpont 콜백, early stopping 콜백을 같이 사용

model.compile(optimizer = 'adam', loss='sparse_categorical_crossentropy',metrics='accuracy')
checkpoint_cb = keras.callbacks.ModelCheckpoint('best-cnn-model.h5',
                                                save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience = 2,
                                                  restore_best_weights=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience = 2,
                                                  restore_best_weights=True)
history = model.fit(train_scaled, train_target, epochs = 20,
                   validation_data=(val_scaled, val_target),
                   callbacks=[checkpoint_cb, early_stopping_cb])

실행

Epoch 1/20
1500/1500 [==============================] - 27s 17ms/step - loss: 0.5075 - accuracy: 0.8179 - val_loss: 0.3253 - val_accuracy: 0.8817
Epoch 2/20
1500/1500 [==============================] - 25s 17ms/step - loss: 0.3334 - accuracy: 0.8809 - val_loss: 0.2711 - val_accuracy: 0.9009
Epoch 3/20
1500/1500 [==============================] - 24s 16ms/step - loss: 0.2825 - accuracy: 0.8971 - val_loss: 0.2612 - val_accuracy: 0.9053
Epoch 4/20
1500/1500 [==============================] - 23s 16ms/step - loss: 0.2540 - accuracy: 0.9085 - val_loss: 0.2359 - val_accuracy: 0.9146
Epoch 5/20
1500/1500 [==============================] - 25s 17ms/step - loss: 0.2302 - accuracy: 0.9148 - val_loss: 0.2358 - val_accuracy: 0.9121
Epoch 6/20
1500/1500 [==============================] - 26s 18ms/step - loss: 0.2109 - accuracy: 0.9232 - val_loss: 0.2291 - val_accuracy: 0.9134
Epoch 7/20
1500/1500 [==============================] - 30s 20ms/step - loss: 0.1924 - accuracy: 0.9289 - val_loss: 0.2284 - val_accuracy: 0.9178
Epoch 8/20
1500/1500 [==============================] - 26s 17ms/step - loss: 0.1774 - accuracy: 0.9330 - val_loss: 0.2292 - val_accuracy: 0.9195
Epoch 9/20
1500/1500 [==============================] - 28s 19ms/step - loss: 0.1652 - accuracy: 0.9387 - val_loss: 0.2363 - val_accuracy: 0.9193

에포크 절반도 안되어서 조기종료가 되었다.

 

손실그래프를 그려보자.

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

최적 = 7~8번째 에포크 같다

 

EarlyStopping클래스에서 restore_best_weights 매개변수를 True로 지정해서 

현재 model 객체가 최적의 모델 파라미터로 복원되어 있다.

 

즉 ModelCheckpoint 콜백이 저장한 best-cnn-model.h5파일을 다시 읽을 필요 X

 

이제 세트에 대한 성능을  평가해보자.

model.evaluate(val_scaled, val_target)

375/375 [==============================] - 2s 5ms/step - loss: 0.2284 - accuracy: 0.9178
[0.22835350036621094, 0.9178333282470703]

fit()의 결과 중 에포크 7번째 때와 결과값이 같다.!! --> 7번째가 최적의 에포크

 

첫 번째 샘플 이미지를 확인해보자.!!

핸드백인것을 볼 수 있다.

 

predict() 메소드를 이용해서 10개의 클래스에 대한 예측을 해보자.

preds = model.predict(val_scaled[0:1])
print(preds)

1/1 [==============================] - 0s 75ms/step
[[1.8688267e-19 4.8781057e-26 4.4762546e-23 1.3098311e-21 1.7252202e-19
  4.3341434e-19 1.1465990e-20 3.6049053e-22 1.0000000e+00 2.2943934e-20]]

결과를 보면 아홉번 째가 1이고 나머지는 0에 가까운 숫자들뿐이다.

 

시각화를 해보면 더 직관적일 것이다.

이제 리스트로 정리해보자

classes= ['티셔츠','바지','스웨터','드레스','코트','샌달','셔츠','스니커즈','가방','앵클 부츠']
import numpy as np
print(classes[np.argmax(preds)])

가방

합성곱 ( convolution) : 이미지의 유용한 특징,특성을 드러내게 하는 역할을 함.

이미지처리에 좋은 성능을 낼 수 있다!!!

 

앞서 배운 인공신경망의 dense에서는 입력개수마다 가중치가 있다. (모든 입력에 가중치가 존재)

 

뉴런(=출력층)

 

앞에서 MNIST 이미지는 784개의 픽셀을 입력받는 은닉층의 뉴런 개수가 100개 => 출력도 100개였다.

 

합성곱은 위와 다르게 입력데이터 전체에 가중치를 적용X

                일부에만 가중치를 곱한다.

 

일부 = n개의 픽셀에만 !!

합성곱은 한번의 연산으로 끝나지않음!!

그림으로 이해해보자.

 

 

총 8개의 출력 (3,1,0), (1,0,7),,,,,,(2,4,5) 

가중치 w1,w2,w3은 모두 같은 값

 

앞에서 배운 밀집층의 뉴런은 입력개수만큼 10개의 가중치를 가지고 1개의 출력을 생성.

 

합성곱 신경망은 3개의 가중치를 가지고 8개의 출력을 생성

 

합성곱에서 사용할 가중치의 개수 = 하이퍼파라미터!!!!!!!!!!!!

 

CNN에서는 뉴런 = 필터 이라고 생각하면 된다.

 

뉴런 개수 = 필터

가중치 = 커널 ( 도장) 

 

 

한 개의 필터 : 빨간 박스 = 3x3의 총 9개의특성 x 9개의 가중치 = 1개의 출력

                       + 3번 더 이동하여 총 4개의 출력!!!

 

이를 2차원으로 표현하자면

 

1번 출력 |  2번 출력

--------------------------               이렇게 행렬?처럼 나타낼 수 있다.

3번 출력 |  4번 출력                  

 

위와 같이 (2,2) 크기를 쌓으면 3차원 배열이 된다.

 

만약 필터가 3개가 있다면  (2,2,3)인 3차원 배열로 표현할 수 있다.

 

 

 

구현하기

합성곱 = Conv2D

매개변수  ==> 필터의 개수, 커널사이즈=커널의크기, 활성화 함수, 패딩, 스트라이드

keras.layers.Conv2D(10, kernel_size=(3,3), activation = 'relu', padding='same', strides=1)

 

패딩 / 스트라이드

위의 예시는 (4,4)크기의 입력에 (3,3)크기의 커널(가중치, 도장)을 적용하여 (2,2)크기로 압축? 특성맵을 만들었다.

 

그런데 만약 커널 크기는 (3,3) 그대로하고 출력의 크기를 (4,4)로 만들고 싶다면???

 

--> 입력(4,4)과 동일한 크기의 출력을 만드려면 마치 더 큰 입력에 합성곱하는 척해야 한다.!!

 

예를 들어 , 사실은 4x4이지만 6x6처럼 바꿔준 후 3x3 커널로 합성곱하면 4x4특성 맵으로 만들 수 있다.

그림으로 이해하기

 

이렇게 주위를 가상의 원소로 채우는 것을 패딩이라고한다.

(추우니까 두껍게 입자=패딩)

 

위와 같이 입력/특성을 맞추기 위해 주위를 0으로 패딩하는것 = 세임 패딩

패딩없이 순수한 배열 그대로 합성곱 해서 특성 맵을 만드는것= 밸리드 패딩

keras.layers.Conv2D(10, kernel_size=(3,3), activation = 'relu', padding='same', strides=1)

위 padding => 기본값은 밸리드이다. 세임하고싶은 'same'이라고 써주기!

 

그리고 도장(커널)은 한 칸씩 이동하는데, 만약 두 칸씩 이동한다면 특성 맵( 출력 ) 은 반토막이 날 것이다.

이렇게 n칸씩 지정하는 것을 스트라이드라고 하고, 기본값은 1이다 !!!

 

 

폴링 ( pooling )

풀링: 합성곱 층에서 만든 특성 맵의 가로세로의 크기를 줄이는 역할

 

그림으로 이해

 

위 그림처럼 (2,2,3) 특성맵에 풀링을 적용하게되면 마지막 차원인 개수!만 유지하고 너비,높이가 줄어듦 (1,1,3)

 

위 그림에서는 2,2 ->  1,1로 축소

도장을 찍은 영역에서 가장 큰 값 / 평균 값을 계산하는데, 이를 최대풀링, 평균풀링이라고 한다.

 

특성맵 4개중에서 가장 큰 값 고르고, 그렇게 총 4개를 골랐다면, 그값으로 이루어진 축소 특성맵을 생성!!!!!!!!!!!!!!!!

그림으로 추가 설명

위 그림에서 stride = 2인것 또한 확인할 수 있다.

 

keras.layers.Maxpooling2D(2, strides=2, padding='valid')

괄호안에 숫자는 풀링의 크기를 지정이다!!!

 

총정리

 

RMS Prop    /      ADAM

출처 : https://velog.io/@cha-suyeon/DL-%EC%B5%9C%EC%A0%81%ED%99%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-RMSProp-Adam

 

[DL] 최적화 알고리즘 - RMSProp, Adam

공부 벌레🐛가 되자!어제 최적화 알고리즘을 공부 중이라 하니까 어차피 Adam만 쓰는데, 굳이 다 알아야 하냐 라는 말을 들었습니다. 하지만 최근에 계속 쓰는 Adam까지 결국 왔다는 것...! 그러니

velog.io

 

지수 가중 이동 평균

예시는 런던의 1년 일별 기온입니다.

로 표현이 되었고 그래프에 파란색 점으로 찍혔습니다.

그리고 지수 가중 이동 평균을 구합니다.

식은 오른쪽을 참고하시면 되고

이전 기온의 지수가중이동평균에 0.9를 곱하고, 현재 기온에는 0.1을 곱합니다.

이전 값에 더 많은 가중치(0.9)를 주고, 현재 기온에는 0.1이라는 작은 가중치를 주는 방식입니다.

는 $\frac{1}{1−β} 기간 동안 기온의 평균을 의미합니다.

  • 일 때 10일의 기온 평균
  • 일 때 2일의 기온 평균

그러나 곡선이 올바른 값에서 더 멀어집니다. 그래서 기온이 바뀔 경우 지수가중평균 공식은 더 느리게 적응합니다.

가 0.98이면 이전 값에 많은 가중치를 주고, 현재의 기온에는 작은 가중치를 주게 됩니다.

따라서 기온이 올라가거나 내려가는데 더 느리게 반응하게 됩니다.

만약 값을 0.5로 낮춘다면  식에 따라 2일의 기온을 평균내는 것과 같습니다.

그렇다면 2일의 기온만 평균내서 더 노이즈가 많고 이상치에 민감한 형태가 됩니다. 기온 변화에는 더 빠르게 적응을 하게 됩니다.

이 식은 Exponentially Weighted Averages(지수가중이동평균)이라 부르며,

parameter 혹은 학습 알고리즘의 hyper parameter 값을 바꾸면서 달라지는 효과를 얻게 되고 이를 통해 가장 잘 작동하는 값을 찾게 됩니다.

여기서  값이 hyper parameter 값입니다. 보통 사용하는 값은 0.9 입니다.

이렇게 희한하게 계산되는 지수가중이동평균은 왜 쓰일까요?

지수가중이동평균은 최근 데이터에 더 많은 영향을 받는 데이터들의 평균 흐름을 계산하기 위해 구합니다. 그리고 최근 데이터 지점에 더 높은 가중치를 줍니다.

갑자기 최적화 알고리즘 공부하기 전, 지수가중이동평균을 공부한 이유는 RMSProp에서 쓰이기 때문입니다.

 

RMSProp

RMSProp AdaGrad에서 학습이 안 되는 문제를 해결하기 위해 hyper parameter인 가 추가되었습니다.

변화량이 더 클수록 학습률이 작아져서 조기 종료되는 문제를 해결하기 위해 학습률 크기를 비율로 조정할 수 있도록 제안된 방법입니다.

RMSProp은 최근 경로의 곡면 변화량을 측정하기 위해 지수가중이동평균을 사용합니다.

RMSProp의 장점은 미분값이 큰 곳에서는 업데이트 할 때 큰 값으로 나눠주기 때문에 기존 학습률 보다 작은 값으로 업데이트 됩니다.

따라서 진동을 줄이는데 도움이 됩니다.

반면 미분값이 작은 곳에서는 업데이트시 작은 값으로 나눠주기 때문에 기존 학습률 보다 큰 값으로 업데이트 됩니다.

이는 조기 종료를 막으면서도 더 빠르게 수렴하는 효과를 불러옵니다.

따라서 최근 경로의 gradient는 많이 반영되고 오래된 경로의 gradient는 작게 반영됩니다.

gradient 제곱에 곱해지는 가중치가 지수승으로 변화하기 때문에 지수가중평균이라 부릅니다.

매번 새로운 gradient 제곱의 비율을 반영하여 평균을 업데이트 하는 방식입니다.

 

Adam

드디어 마지막 Adam입니다!

Adam은 Momentum과 RMSProp이 합쳐진 형태입니다.

진행하던 속도에 관성도 주고, 최근 경로의 곡면의 변화량에 따른 적응적 학습률을 갖는 알고리즘입니다.

'관성' + '적응적 학습률' = Adam

Momentum처럼 진행하던 속도에 관성을 주고, RMSProp과 같이 학습률을 적응적으로 조정하는 알고리즘

이 방법은 모멘텀이 있는 경사 하강법의 효과와 RMSProp이 있는 경사 하강법의 효과를 합친 결과가 나옵니다.

이것은 매우 넓은 범위의 아키텍처를 가진 서로 다른 신경망에서 잘 작동한다는 것이 증명되었습니다.

그래서 일반적 알고리즘에서 현재 가장 많이 사용되고 있습니다.

이 알고리즘은 출발 지점에서 멀리 떨어진 곳으로 이동하는 초기 경로의 편향 문제가 있는 RMSProp의 단점을 제거하였습니다.

천천히 살펴보겠습니다.

 

개념

  • 1차 관성으로 속도를 계산합니다. 속도에 마찰 계수  대신에 가중치 을 곱해서 gradient의 지수가중이동평균을 구하는 형태로 수정되었습니다.
  • 2차 관성으로 의 지수가중이동평균을 구합니다.
  • 마지막은 parameter 업데이트 식으로 1, 2차 관성을 사용합니다.

 

초기 경로에서 편향이 발생하는 이유

훈련을 시작할 때 1차 관성 , 2차 관성  모두 0으로 초기화합니다.

이 상태에서 첫 번째 단계를 시행하면

각각 0.1, 0.01이 곱해져서 값이 작아집니다.

이 작아지면 적응적 학습률이 커져 출발 지점에서 멀리 떨어진 곳으로 이동하게 되고,

최적해에 도달하지 못할 수도 있습니다.

그래서 도입한 방법이 해당 방법입니다. 이렇게 알고리즘을 개선했을 때,

식이 상쇄되기 때문에  이 되어 아주 작아질 일이 없습니다.

이 방법으로 학습 초반에 학습률이 급격히 커지는 편향이 제거되고, 훈련이 진행될 수록 와 $(1-\beta^t_2)는 1에 수렴하므로 원래 알고리즘 형태로 돌아옵니다.

수식에 대한 이해가 어렵다면 그냥 넘어가도 될듯 하옵니다.

 

hyperparameter

이때까지 본 optimizer와 비교해서 Adam이 가장 특별한 점은 이 hyper parameter 값이 많다는 점인데요.

하나씩 살펴보겠습니다.

  • :학습률. 매우 중요하고 보정될 필요가 있으므로 다양한 값을 시도해서 잘 맞는 값을 찾아야 합니다.
  • : 기본적으로 0.9를 사용. 모멘텀에 관한 항
  • : 기본적으로 0.99나 0.999를 이용(논문에서는 0.999를 추천)
  • : Adam 논문에서는 을 추천. 분모가 0이 되는 것을 막기 위해 더해주는 상수. 하지만 이 값을 설정하지 않아도 전체 성능에는 영향이 없습니다.

보통 , , 의 세 가지 값은 고정 시켜두고, 을 여러 값을 시도해가면서 가장 잘 작동되는 최적의 값을 찾습니다.

Adam은 Adaptive moment estimation에서 온 용어입니다.

이 도함수의 평균을 계산하므로 첫 번째 momentum이고,

는 지수가중평균의 제곱을 계산하므로 두 번째 momentum입니다.

이 모두를 Adma optimizer algorithm라 부릅니다.

'부록' 카테고리의 다른 글

Resnet 논문 리뷰  (0) 2024.04.22
Style GAN & Style GAN2  (1) 2024.04.07
LSTM  (0) 2023.08.02
[Modeling] XGB Classifier + 그리드서치 / 베이지안 옵티마이저  (0) 2023.03.18
Bayesian Optimization  (2) 2023.02.21

인공 신경망 모델을 훈련하는 모범 사례 필요한 도구들을 살펴보자.

이번 절에서는 케라스 API를 사용해서 모델을 훈련하는데 필요한 다양한 도구들을 살펴보자.

손실곡선

앞에서 fit()을 출력하면 마지막 출력으로

<keras.callbacks.History at 0x264150f7040>

가 출력된다.

이 코드는 훈련에서 사용한 지표, 즉 손실과 정확도 값이 저장되어 있는데, 이를 그래프로 그려보자.

 

처음부터 코드 다시짜보자.!

from tensorflow impor 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, trian_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, activation = 'softmax'))
    return model
model = model_fn()
model.summary()

Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense (Dense)               (None, 100)               78500     
                                                                 
 dense_1 (Dense)             (None, 10)                1010      
                                                                 
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________

이제 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'])

손실과 정확도가 있는것을 확인할 수 있다.

 

이제 그림을 그려보자!!

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

 

5개의 에포크가 0~4사이에 포함되어 있다.

y축은 계산된 손실

 

이번에는 정확도를 출력 해보자.

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

에포크마다 손실이 감소하고 정확도가 올라감을 확인할 수 있다.

어?! 그러면 에포크를 ㅈㄴ 늘리면 좋은거 아닐까??

20으로 해보자.

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

에포크를 늘리니까 손실이 계속 감소하는것을 볼 수 있다!!

하지만 이게 무조건 좋은것일까????

 

검증 손실

앞서 머신러닝에서, 과대/과소 적합과 에포크 횟수와의 관계를 배웠었다.

인공 신경망은 모두 경사 하강법을 사용하기 때문에 동일한 개념이 여기서도 적용된다.!

 

에포크 횟수에 따른 과대/과소 적합을 파악하려면 훈련세트에 대한 점수뿐만 아니라 검증 세트에 대한

점수도 필요하다. 따라서 앞에서처럼 훈련 세트의 손실만 그려서는 안된다.

 

에포크를 과도하게 늘리면 과대적합이 발생하고, 그렇다고 너무 적게 설정하면 과소적합이 발생한다.

 

적절한 에포크횟수를 설정하기 위해서는 크마다 검증 손실을 계산하고, 이를 확인해가며 설정하는것이 중요하다.

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

history 딕셔너리에 어떤 키가 들어 있는지 보자.

print(history.history.keys())

dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])

그림을 그려보자 !

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epochs')
plt.ylabel('loss')
plt.legend(['train'])

주황 = 검증 세트

파랑 = 훈련 세트

에포크가 증가할 수록 훈련세트의 손실은 꾸준히 감소하지만, 검증세트의 손실은 5를 기준을 증가한다!!!!

에포크가 증가할수록 과대적합이 일어나는 것을 위 그래프를 통해 확인할 수 있다.

 

 

이제 Adam 옵티마이를 적용해보고 훈련손실과 검증손실을 다시 그려보자.

model= model_fn()
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('epochs')
plt.ylabel('loss')
plt.legend(['train','val'])
plt.show()

검증세트의 loss가 위의 SGD? 를 썼을때보다 확실히 낮은것을 볼 수 있다.

이는 Adam 옵티마이저가 이 데이터셋에 더 유용하다는것을 보여준다!!

 

규제방식에 대해서 알아보자!!

 

드롭아웃

드롭아웃은 아래 그림처럼 훈련과정에서 층에 있는 일부 뉴런을 랜덤하게 꺼서( 뉴런의 출력을 0으로 만들어)

과대 적합을 막는다.

 

말그대로 층에 있는 뉴런 한명 드롭아웃 시키는것!! (랜덤)

하이퍼파라미터 : 몇개의 뉴런을 드롭아웃 시킬건지..

 

드롭아웃이 어떻게 과적합을 막지???

----------------->

1. 이전 층의 일부 뉴런이 랜덤하게 꺼지면 (0이 되면) 특정 뉴런에 과대하게 의존하는것을 줄이고 모든 입력에 주의를 기울이는쪽으로 변함.

2. 일종의 앙상블 기법임. 랜덤하게 드롭아웃된 신경들끼리 훈련하기 때문에 다른 알고리즘끼리 학습되는 느낌.

 

Dropout을 적용해보자.

model = model_fn(keras.layers.Dropout(0.3))   # 30%를 버리기로 했다!!!
model.summary()

Model: "sequential_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten_2 (Flatten)         (None, 784)               0         
                                                                 
 dense_4 (Dense)             (None, 100)               78500     
                                                                 
 dropout (Dropout)           (None, 100)               0         
                                                                 
 dense_5 (Dense)             (None, 10)                1010      
                                                                 
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________

 생각해보면

 Dropout() 메소드는 훈련할 때만 사용해야하고, 훈련이 끝난 후 평가, 예측을 할 때는 사용하지 말아야한다.

그러려면 모델을 훈련할때 Dropout()을 사용했다면 끝난후 층을 다시 빼야 한다.

하지만 Keras(tensorflow)는 자동으로 dropout을 적용하지 않는다. (평가/예측 시) 따라서 따로 빼줄 필요가 없다.

 

어쨌든 dropout해서 훈련한 결과를 그래프로 그려보자 !!

 

주황색 (검증세트)의 loss가 더 줄어든 모습이다.

과대적이 확실히 줄어든 모습이다.

 

모델 저장과 복원

에포크 횟수를 20에서 10으로 바꾸고 다시 훈련해보자.

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

케라스 모델은 훈련된 모델의 파라미터를 저장하는 간편한 메소드를 제공한다.

save_weights()을통해 저장하도록 해보자.

model.save_weights('model-weights.h5')
model.save('model-whole.h5')
!ls -al *.h5

 

모델 predict() 메소드에서 가장 큰 값을 고르기 위해 argmax() 함수를 사용하자.!

이 함수는 배열에서 가장 큰 값의 인덱스를 반환한다.

예를 들어 배열의 첫번째 원소가 가 큰 값일 경 0을 반환한다.

 

다행히 우리가 준비한 타겟값도 0부터 시작하기 때문에 비교하기 쉽다.

import numpy as np
val_labels = np.argmax(model.predict(val_scaled),axis=-1)
print(np.mean(val_labels == val_target))

argmax()함수의 axis = -1은 배열의 마지막 차원을 따라 최대값을 고릅니다.

검증 세트는 2차원 배열이기 때문에 마지막 차원은 1이 된다.

아래 사진을 통해 이해하자.

axis=1이면 열을 따라 각 행의 최대값의 인덱스를 선택하고, axis=0 이면 행을 따라 각 열의 최대값의 인덱스를 선택합니다.

 

그 다음 라인은 argmax()로 고른 index(val_labels)와 타깃(val_target)을 비교한다.

두 배열에서 각 위치의 값이 같으면 1이되고 다르면 0이 됩니다. 이를 평균하면 정확도 된다.

 

이번에는 모델 전체를 파일에서 읽은 다음 검증 세트의 정확도를 출력해보자.

 

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


375/375 [==============================] - 1s 3ms/step - loss: 0.3393 - accuracy: 0.8792
[0.33925992250442505, 0.8791666626930237]

 

콜 백

 콜백 = 훈련 과정 중에 어떤 작업을 수행할 수 있게 해주는 객체로 keras.callbacks로 사용가능

여기서는 keras.callbacks.ModelCheckpoint을 사용. 역할 = 에포크마다 모델을 저장함!!

best_only = True : 가장 낮은 검증 점수를 만드ㅊ 모델 저장 가능

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

왜 가장 낮은 검증점수를 저장????

 검증점수가 상승하기 시작하면 과적합이 발생해서 훈련 의미? 필요가없다.

이 때 중지한다면 상당한 시간 (자원)을 아낄 수 있다.

 

이를 조기 종료(early stopping)이라고 하는데, 딥러닝분야에서 아주 많이 쓰인다고 한다.

규제 방법중에 하나라고 생각하면 편할듯.

 

코드 = EarlyStopping 콜백 클래스

 

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,
												  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])

훈련을 마치고나서 몇 번째 에포크에서 훈련이 중지되었는지 확인가능!!!(아래 코드)

early_stopping_cb.stopped_epoch

 

이어서 훈련 손실과 검증 손실을 출력해서 확인해보자.

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

 

 

배울 것 : 심층 신경망, 렐루 함수, 옵티마이저 

 

먼저 케라스에서 패션 MNIST 데이터셋을 불러오자,

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

이미지 픽셀값 0~255을 1로 변환하고, 픽셀크기 28*28 2차원 배열을 1차원으로 펼쳐보자.

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

이제 인공 신경망 모델을 두개 추가해보자!!

은닉층 + 출력층

은닉층의 역할

2a + b = 4

b + 2c = 2   ------> 2a-2c=2   -> a - c = 1

b는 사라지고 a와 c에 대한 식만 남음!!  ( b가 은닉층이라고 보면 된다. )

 

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

 

은닉층의 뉴런 수 : 100개로 설정했다.

유의사항 : 무조건 출력층의 뉴런 수 보다는 크게 설정해야함.

 

 

심층 신경망 만들기

model = keras.Sequential([dense1,dense2])

summary를 통해 요약정보 출력하기

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
_________________________________________________________________

dense 2개 모두 출력층의 shape을 보면 첫 번째가 None이 나온다.

첫 번째 차원은 샘플의 개수인데, 개수가 설정되어 있지 않기 때문에 None이 출력되는것이다.

Why?

 

케라스의 fit()은 기본적으로 데이터를 한 번에 다 사용하지 않고 여러번 나누어서 사용한다.

( 케라스의 fit은 미니배치 경사하강법을 사용하기 때문  + default 배치 사이즈 = 32)

때문에  샘플 개수를 고정하지 않고 어떤 배치 사이즈에서도 유연하게 대응할 수 있도록 None으로 설정하는것이다.

 

shape의 두번째는 각각 100,10 인데 이것은 예상하다시피 뉴런 개수이다.

마지막으로 Param에는 모델 파라미터 개수가 출력된다.

785 x 100 (픽셀 개수 x 100개의 가중치  + 100개 마다 하나의 절편)

101x10     (100개의 은닉층 x 10개의 출력층 + 10개마다 하나의 절편)

 

모델에 층을 추가하는 법: 

아래 코드는 안좋은 코드

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

출력해보자,

model.summary()

Model: " 패션 MNIST 모델"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 hidden_layer (Dense)        (None, 100)               78500     
                                                                 
 output (Dense)              (None, 10)                1010      
                                                                 
=================================================================
Total params: 79,510
Trainable params: 79,510
Non-trainable params: 0
_________________________________________________________________

.add() 메소드로 편하게 추가!!!

 

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

이제 compile하고 훈련시켜보자!

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


Epoch 1/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.4131 - accuracy: 0.8516
Epoch 2/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3781 - accuracy: 0.8635
Epoch 3/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3546 - accuracy: 0.8711
Epoch 4/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3385 - accuracy: 0.8775
Epoch 5/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3235 - accuracy: 0.8811

렐루 함수

시그모이드함수와 차이:

시그모이드 함수는 출력 값을 0~1으로 만들어주는 함수이다.

하지만 시그모이드는 오른쪽, 왼쪽 끝을 보면 누워있기 때문에, 출력을 만드는데 시간이 오래걸리는 단점이 있다.

 

특히 층이 많은 신경망일수록 단점이 극명해지기 되는데, 이를 보완하기 위해 등장한것이 렐루함수이다.

 

렐루함수

렐루함수는 음수는 무조건 0으로, 0보다 큰 값은 값 그대로를 출력하는 함수이다. 

시간적으로 절약되고, 늘어지는 모양이 없기 때문에 시그모이드보다 시간적으로 효율적이다.

 

한편, 앞서 2차원 배열 (픽셀 크기)을 1차원으로 바꿀때 reshape()을 사용했지만,

앞으로는 flatten을 사용하도록 하자.

Flatten() 메소드는 배치 차원을 제외하고 나머지 모든 차원을 모두 일렬(일차원)로 펼치는 역할을 하므로 매우 유용하다.

 

예시코드:)

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

summary

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
_________________________________________________________________

Flatten() 메소드를 사용하면 위에 보다시피 입력값의 차원을 짐작할 수 있다.  (shape = 784개)

 

 

요약 --> 최종 코드 : 

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

Epoch 1/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.5312 - accuracy: 0.8117
Epoch 2/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3926 - accuracy: 0.8591
Epoch 3/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3544 - accuracy: 0.8710
Epoch 4/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3306 - accuracy: 0.8810
Epoch 5/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3158 - accuracy: 0.8880
<keras.callbacks.History at 0x2641e061b80>

model.evaluate()을 이용해서 모델을 평가해볼 수 있다.

model.evaluate(val_scaled, val_target)

375/375 [==============================] - 0s 906us/step - loss: 0.3603 - accuracy: 0.8738
[0.36031365394592285, 0.8738333582878113]

 

 

옵티마이저

옵티마이저 하이퍼 파라미터 튜닝을 쉽게 해주는 tool

compile() 메소드는 케라스의 기본 경사 하강법인 RMSprop을 사용한다.

 

하지만 RMSprop 말고도 다양한 종류의 경사하강법들이 존재하는데, 이들을 옵티마이저라고 부른다.

(하이퍼 파라미터 튜닝)

 

여기서 가장 기본적인 SGD 옵티마이저를 이용해서 하이퍼파라미터 튜닝을 해보겠다.

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

learning_rate (학습률)또한 하이퍼파라미터인데, 기본값은 0.01이다.

설정하려면 다음과 같은 코드를 실행

sgd=keras.optimizers.SGD(learning_rate=0.1)

기본 / 적응적 학습률 옵티마이저

 

매개변수들 : learning_rate , momentum  ( 기본값은 각각 0.01 , 0 )

 

momentum을 0보다 크게 설정한다면 --> 이전의 그레디언트를 가속도처럼 사용하는 모멘텀 최적화를 사용한다.일반적으로 momentum은 0.9이상을 지정

 

또한 SGD 클래스의 nesterov 매개변를 기본값 False에서 True로 바꾸게 되면 네스테로프 모멘텀 최적화를 사용한다.

sgd=keras.optimizers.SGD(momentum=0.9, nesterov=True)

네스테로프 모멘텀 최적화는 일반적으로 기본적인 경사하강보다 더 나은 성능을 보여준다!!!!

 

모델이 점점 최적점에 가까이 갈수록 학습률을 낮출 수 있다.

이렇게 하면 안정적으로 최적점에 수렴할 가능성이 높다!

이런 학습률 적응적 학습률이라고 한다.

위의 이러한 방식들 학습률을 튜닝하는 수고를 덜어줄 수 있다고한다.

 

 

적응적 학습률을 사용하는 대표적인 옵티마이저는 Adagrad와 RMSprop이다.

 

Adam은 모멘텀최적화와 RMSprop의 장점을 접목시킨 옵티마이저다.

(일반적으로 Adam을 사용)

Adam optimizer를 사용해서 패션MNIST 모델을 훈련시켜보자.

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

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

Epoch 1/5
1500/1500 [==============================] - 3s 2ms/step - loss: 0.5287 - accuracy: 0.8161
Epoch 2/5
1500/1500 [==============================] - 2s 2ms/step - loss: 0.3907 - accuracy: 0.8596
Epoch 3/5
1500/1500 [==============================] - 2s 2ms/step - loss: 0.3501 - accuracy: 0.8727
Epoch 4/5
1500/1500 [==============================] - 2s 2ms/step - loss: 0.3253 - accuracy: 0.8815
Epoch 5/5
1500/1500 [==============================] - 2s 2ms/step - loss: 0.3070 - accuracy: 0.8863
<keras.callbacks.History at 0x264150f7040>

모델을 평가해보자,!!!!

model.evaluate(val_scaled, val_target)

375/375 [==============================] - 0s 1ms/step - loss: 0.3396 - accuracy: 0.8786
[0.33957886695861816, 0.8785833120346069]

RMSprop과 비슷한 성능을 보여준다..!

 

패션 MNIST 데이터셋을 통해서 머신러닝 비지도학습 (군집알고리즘)처럼 데이터를 살펴보자!!

 

딥러닝 대표 라이브러리에는 텐서플로, 케라스가 있다.

케라스 임포트해서 mnist 데이터를 다운받아 보자.

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
29515/29515 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26421880/26421880 [==============================] - 1s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
5148/5148 [==============================] - 0s 0s/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4422102/4422102 [==============================] - 0s 0us/step

출력해보자

print(train_input.shape, train_traget.shape)

(60000, 28, 28) (60000,)

훈련 데이터는 60,000의 이미지로 이루어져 있고,

각 이미지는 28x28픽셀임을 알 수 있다.!!

 

타겟 데이터 또한 60,000장이다.!

 

print(test_input.shape, test_target.shape)

(10000, 28, 28) (10000,)

테스트 데이터또한 훈련 데이터와 개수,사이즈가 같다.!

 

그림으로 샘플이 무엇인지 살펴봅시다람쥐.

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

 

리스트함축구문을 사용해서 처음 10 샘플의 타겟값을 리스트 만든 후 출력해보자.

[train_target[i] for i in range(10)]

[9, 0, 0, 3, 0, 2, 7, 2, 5, 5]

이제 unique()함수를 통해 레이블당 샘플 개수를 확인해보자.!

import numpy as np
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],
       dtype=int64))

6000개씩 10종류가 있음을 확인할 수 있다.

 

 

로지스틱 회귀로 분류하기

앞서 배운 SGDClassifier (확률적)경사하강법을 사용할 때 처럼 표준화 전처리를 해야함.

흑백 이미지의 경우 0~255의 값을 가지므로, 255으로 나누면 0~1로 표현가능하다.

 

우선 reshape()을 통해 3차원->2차원으로 배열을 바꿔보자,

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

잘 됐는지 확인해보자.

print(train_scaled.shape)

(60000, 784)

 

교차 검증을 통해서 성능을 확인해보자!!!

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.8192833333333333

높지가 않다!!!

 

 

로지스틱 회귀 공식 떠올려보자,,,!

픽셀 784개에 대해서 각각 가중치를 다르게 매겨서 소프트맥수 함수 통과~ 출력 :0~1사이!!

 

뉴런 = z값 ( 출력층의 값) == 유닛

입력층 = 픽셀값 그잡채

 

 

텐서플로와 케라스는 원래 별도의 라이브러리와 같은 개념이였는데,

텐서플로는 구글이 개발, 오픈소스로 공개한 라이브러리 이고, 케라스는 프랑소와라는 사람이 만들었다.

프랑소와가 구글에 합류하고, 케라스 api를 텐서플로에 넣은 후, 텐서플로는 케라스api만 사용하게 되었고,

이후 사실상 tensorflow=keras 같은 개념이라고 볼 수 있을것 같다.

 

 

1. 인공 신경망 만들기

from sklearn.model_selectoin 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)

인공 신경망 (딥러닝) 에서는 머신러닝 했을때와 다르게 교차 검증 하지 않는다!!!!!!!!!!!!!!

1. 데이터셋 충분히 크기 때문에 교차검증 효과적 X

2. 시간이 너~무 오래걸림

훈려세트에서 20%를 검증세트로 따로 떼어냈다 .  ( test_size= 0.2 )

print(train_scaled.shape, train_target.shape)
(48000, 784) (48000,)

60,000 - ( 60,000 x 0.2 ) = 48,000 정상적으로 나눠졌다.

 

print(val_scaeld.shape, var_target.shape)

(12000, 784) (12000,)

 60,000 x 0.2 = 12,000 정상적으로 나눠짐 !

 

 

이제 인공 신경망을 만들어보자.

dense=밀집한 ==> 입력층의 수많은 784개의 픽셀등을 뜻한다고 생각하면 편하다.

케라스의 Dense클래스를 이용해서 밀집층을 만들자. 매개변수는 뉴런수,함수,입력크기이다.

 

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

activation = 출력층에 적용할 함수 (이진분류이면 시그모이드, 다중 분류 소프트맥스)

input_shape = 입력의 크기

 

model = keras.Sequential(dense)

이제 신경망을 만들었다.

MNIST 데이터셋을 분류해보자.

 

훈련하기전 마지막 설정 단계 : model.compile()

손실함수

이진분류 : loss = 'binary_crossentropy'

다중분류 : loss  = 'categoricail_crossentropy'

 

sparse_binary / categoricail_crossentropy => 원-핫 인코딩 안해도 될때 sparse 사용!

 

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

 

metrics에 대해 알아보자.

케라스는 모델이 훈련할 때 기본으로 에포크마다 손실값을 출력한다.

손실이 줄어드는 것을 보고 훈련이 잘 되는것을 볼 수 있지만 정확도까지 출력해주면 더 좋을것이다.

이를 위해 accuracy를 지정!

 

이제 다 끝났으니 훈련시켜보자.

model.fit(train_scaled, train_target, epochs = 20)

Epoch 1/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3956 - accuracy: 0.8672
Epoch 2/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3956 - accuracy: 0.8666
Epoch 3/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3945 - accuracy: 0.8680
Epoch 4/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3942 - accuracy: 0.8679
Epoch 5/5
1500/1500 [==============================] - 2s 1ms/step - loss: 0.3929 - accuracy: 0.8688

 

이제 검증세트 모델의 성능을 확인해보자 !!

model.evaluate(val_scaled, val_target)

375/375 [==============================] - 0s 882us/step - loss: 0.4426 - accuracy: 0.8518

검증세트 모델도 훈련데이터세트와 비슷한 accuracy를 나타냄을 볼 수 있다.

 

베이지안 최적화 

- 용도 : 하이퍼파라미터 최적해 찾기 ( 엑셀 해찾기 기능)

베이지안 최적화를 보기 전, 파라미터와 하이퍼 파라미터를 다시 생각해보자.

 

 

파라미터는 데이터를 통해 구해지며, 모델 내부적으로 결정되는 값이다.

예를 들면 , 한 클래스에 속해 있는 학생들의 키에 대한 정규분포를 그린다고 해보자. 정규분포를 그리면 평균(μ)과 표준편차(σ) 값이 구해지는데, 여기서 평균과 표준편차를 파라미터(parameter)라고 한다.

 

하이퍼 파라미터는 모델링할 때 사용자가 직접 세팅해주는 값을 뜻한다.

하이퍼 파라미터는 정해진 최적의 값이 없다. 휴리스틱한 방법이나 경험 법칙(rules of thumb)에 의해 결정하는 경우가 많다.

변수(입력 데이터)가 5개 있다고 가정했을 때, 변수1의 값을 설정해놓고, 변수 2, 3,4...이렇게 찾는것이 아니다.

변수 1의 최적값은 변수 2의 값이 바뀌면 또 바뀐다. 따라서 변수들을 동시에 바꿔가며 최적의 값을 찾는 것이다.

 

이렇게 최적값을 찾는것은 매우 복잡하고 시간이 오래 걸리는 작업이므로 해결하기 위한 방법들이 있다.

 

1. 그리드 서치

그리드 서치는 하이퍼 파라미터의 최적값을 교차 검증을 통해 찾아준다. (그리드 서치를 사용하면 cross_validate를 따로 호출 할 필요가 없다.)

그리드 서치에 대한 자세한 설명은 참조 : https://woghkszhf.tistory.com/10

 

5 - 2 교차 검증과 그리드 서치

여태까지 배웠던 최근접 이웃, 로지스틱 회귀, SGD, 트리 등 다양한 머신러닝 모델들이 있었지만, 모든 모델들은 결국 실전에 투입시키기 위해 사용한다. 따라서 성능을 높이기 위해 반복적으로

woghkszhf.tistory.com

  • 즉, 모든 parameter의 경우의 수에 대해 cross-validation 결과가 가장 좋은 parameter를 고르는 방법이다.
  • 전체 탐색 대상 구간을 어떻게 설정할지, 간격은 어떻게 설정할지 등을 결정하는 데 있어 여전히 사람의 손이 필요하나
  • 앞선 Manual Search와 비교하면 좀 더 균등하고 전역적인 탐색이 가능하다는 장점이 있다.
  • 하지만 탐색하고자하는 hyperparameter의 개수를 한 번에 여러 종류로 가져갈수록, 전체 탐색 시간이 기하급수적으로 증가한다는 단점이 있다.

2. 랜덤 서치

  • Random Search는 Grid Search와 큰 맥락은 유사하나
  • 탐색 대상 구간 내의 후보 hyperparameter 값들을 랜덤 샘플링을 통해 선정한다는 점이 다르다.
  • Random Search는 Grid Search에 비해 불필요한 반복 수행 횟수를 대폭 줄이면서, 동시에 정해진 간격(grid) 사이에 위치한 값들에 대해서도 확률적으로 탐색이 가능하므로, 최적 값을 더 빨리 찾을 수 있는 것으로 알려져 있다.
  • 즉, 랜덤서치는 모든 grid를 전부 찾는 대신, 랜덤하게 일부의 파라미터 들만 관측한 후, 그 중에서 가장 좋은 파라미터를 고른다.
  • 그리드 서치는 중요한/ 안중요한 파라미터들을 동일하게 관측해야하기 때문에, 정작 중요한 파라미터를 다양하게 시도해볼 수 있는 기회가 적지만
  • 랜덤서치는 grid로 제한되지 않기 때문에 확률적으로 중요 변수들을 더 살펴볼 수 있는 기회를 받게 됩니다.
  • 그럼에도 불구하고, 랜덤 서치에서도 '여전히 약간의 불필요한 탐색을 반복하는 것 같다’는 느낌을 지우기 어려우실 것이라고 생각합니다.
  • 왜냐하면 그리드 서치와 랜덤 서치 모두, 바로 다음 번 시도할 후보 hyperparameter 값을 선정하는 과정에서, 이전까지의 조사 과정에서 얻어진 하이퍼파라미터 값들의 성능 결과에 대한 ‘사전 지식’이 전혀 반영되어 있지 않기 때문입니다.
  • 매 회 새로운 하이퍼 파라미터 값에 대한 조사를 수행할 시 ‘사전 지식’을 충분히 반영하면서, 동시에 전체적인 탐색 과정을 체계적으로 수행할 수 있는 방법론으로, 베이지안 옵티마이저를 들 수 있습니다.

3. 베이지안 옵티마이저

  • Bayesian Optimization 은 어느 입력값(x)를 받는 미지의 목적 함수(f(x))를 상정하여,
  • 해당 함숫값(f(x))을 최대로 만드는 최적해를 찾는 것을 목적으로 합니다.
    • 즉, 목적 함수(탐색대상함수)와 하이퍼파라미터 쌍(pair)을 대상으로 Surrogate Model(대체 모델) 을 만들고,
    • 순차적으로 하이퍼 파라미터를 업데이트해 가면서 평가를 통해 최적의 하이퍼파라미터 조합을 탐색합니다.
    • 이 때의 목점 함수 black-box function 이라고 합니다.
  • Bayesian Optimization 에는 두 가지 필수 요소가 존재합니다.
    • 먼저 Surrogate Model 은, 현재까지 조사된 입력값-함숫결과값 점들 (x1, f(x1)),...,(xt, f(xt)) 을 바탕으로, 미지의 목적 함수의 형태에 대한 확률적인 추정을 수행하는 모델을 지칭합니다.
    • 그리고 Acquisition Function 은, 목적 함수에 대한 현재까지의 확률적 추정 결과를 바탕으로, ‘최적 입력값을 찾는 데 있어 가장 유용할 만한’ 다음 입력값 후보를 추천해 주는 함수를 지칭합니다.

Bayesian Optimization 수행 과정

  • 자세한 수행 과정
    1. 입력값, 목적 함수 및 그 외 설정값들을 정의한다.
      • 입력값 x : 여러가지 hyperparameter
      • 목적 함수 f(x) : 설정한 입력값을 적용해 학습한, 딥러닝 모델의 성능 결과 수치(e.g. 정확도)
      • 입력값 x 의 탐색 대상 구간 : (a,b)
      • 입력값-함숫결과값 점들의 갯수 : n
      • 조사할 입력값-함숫결과값 점들의 갯수 : N
    2. 설정한 탐색 대상 구간 (a,b) 내에서 처음 n 개의 입력값들을 랜덤하게 샘플링하여 선택한다.
    3. 선택한 n 개의 입력값 x1, x2, ..., xn 을 각각 모델의 하이퍼 파라미터로 설정하여 딥러닝 모델을 학습한 뒤, 학습이 완료된 모델의 성능 결과 수치를 계산한다.
      • 이들을 각각 함숫결과값 f(x1), f(x2), ..., f(xn) 으로 간주한다.
    4. 입력값-함숫결과값 점들의 모음 (x1, f(x1)), (x2, f(x2)), ..., (xn, f(xn)) 에 대하여 Surrogate Model(대체 모델) 로 확률적 추정을 수행합니다.
    5. 조사된 입력값-함숫결과값 점들이 총 N 개에 도달할 때까지, 아래의 과정을 반복적으로 수행한다.
      • 기존 입력값-함숫결과값 점들의 모음 (x1, f(x1)),(x2, f(x2)), ..., (xt, f(xt)) 에 대한 Surrogate Model (대체 모델)의 확률적 추정 결과를 바탕으로, 입력값 구간 (a,b) 내에서의 EI 의 값을 계산하고, 그 값이 가장 큰 점을 다음 입력값 후보 x1 로 선정한다.
      • 다음 입력값 후보 x1 를 hyperparameter 로 설정하여 딥러닝 모델을 학습한 뒤, 학습이 완료된 모델의 성능 결과 수치를 계산하고, 이를 f(x1) 값으로 간주한다.
      • 새로운 점 (x2, f(x2)) 을 기존 입력값-함숫결과값 점들의 모음에 추가하고, 갱신된 점들의 모음에 대하여 Surrogate Model 로 확률적 추정을 다시 수행한다.
    6. 총 N 개의 입력값-함숫결과값 점들에 대하여 확률적으로 추정된 목적 함수 결과물을 바탕으로, 평균 함수 μ(x) 을 최대로 만드는 최적해를 최종 선택합니다. 추후 해당값을 하이퍼파라미터로 사용하여 딥러닝 모델을 학습하면, 일반화 성능이 극대화된 모델을 얻을 수 있다.

데이콘 FIFA 선수 이적료 예측 모델 만드는 대회에서

XGBoost와 베이지안 옵티마이저를 사용했는데, 그 당시에는 베이지안 옵티마이저에 대해 몰랐었지만, 공부하며 알아보니 신기하고 재밌었다. 오늘 일기끝

'부록' 카테고리의 다른 글

Resnet 논문 리뷰  (0) 2024.04.22
Style GAN & Style GAN2  (1) 2024.04.07
LSTM  (0) 2023.08.02
[Modeling] XGB Classifier + 그리드서치 / 베이지안 옵티마이저  (0) 2023.03.18
[딥러닝] 옵티마이저 [퍼온 글]  (0) 2023.03.06

지도학습 - 정답 (타겟)이 있을때 머신러닝

비지도학습 - 정답(타겟) 이 없을때 머신러닝을 하는것.

 

비지도학습의 예시 - 과일 사진 데이터를 이용해서

각 사진들의 특성을 뽑아서 학습, 분류해서 우리가 나중에 확인해보자.

 

캐글의 데이터셋에서 과일 사진 불러오기

print(fruits.shape)

(300, 100, 100)

과일 사진 데이터셋의 shape 확인해보니 3차원 배열임을 확인할 수 있다.

첫 번째 300은 샘플의 개수 (300개의 사진)

두 번째 100은 이미지 세로(100X100) 

세 번째 100은 이미지 가로(100X100) 이미지 크기를 나타낸다.

<<100x100의 이미지가 300장 있다는 소리>>

 

첫 번째 이미지의 첫 번째 행을 출력해보자.

300개의 사진 모두

0~255의 값으로 이루어진 100x100 행렬이다.

 

 

shape ( 300, 100, 100)

첫번째 사진 : 0

첫 번째 행 : 100개

 [ 맨 위 100개]

print(fruits[0,0,:])

[  1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   2   1
   2   2   2   2   2   2   1   1   1   1   1   1   1   1   2   3   2   1
   2   1   1   1   1   2   1   3   2   1   3   1   4   1   2   5   5   5
  19 148 192 117  28   1   1   2   1   4   1   1   3   1   1   1   1   1
   2   2   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1
   1   1   1   1   1   1   1   1   1   1]

0: 검정

255: 흰색

 

그림으로 직관적으로 이해해보자.

imshow() 를 통해

그림 중에서 맨~~위에 y=0의 x들이 위의 숫자로 이루어진 코드이다. 

 

과적합? 될 수 있으므로 흑0->255 , 흰 255->0 으로 역전시켜서 과적합을 방지하자.

흰 바탕 -> 검은 바탕만들기

 

 c_map = 'gray_r'  ==>      _r 을 추가하여 색상 반전

plt.imshow(fruits[0],cmap='gray_r')

 

총 300개의 사진중

0~100    : 사과

100~200:파인애플

200~300:바나나

각각 100개씩 있으므로 파인애플과 바나나도 출력해보자.

fig,axs = plt.subplots(1,2)

axs[0].imshow(fruits[100],cmap='gray_r')
axs[1].imshow(fruits[200],cmap='gray_r')
plt.show()

 

픽셀값 분석하기

apple = fruits[0:100].reshape(-1, 100*100)
pineapple = fruits[100:200].reshape(-1, 100*100)
banana = fruits[200:300].reshape(-1, 100*100)

위 코드를 통해 3차원 배열을 2차원 배열로 합쳤다. (100*100을 10,000으로!!! 사이즈 픽스)

-1을 통해 앞의 300?은 뒤 100*100으로 reshape한 후 나머지 배열에 맞게 자동으로 맞춰지게 한다.

 

이제 사과,바나나, 파인애플의 배열의 크기는 (100,10000)이다. 2차원배열로!

 

print(apple.mean(axis=1))

[ 88.3346  97.9249  87.3709  98.3703  92.8705  82.6439  94.4244  95.5999
  90.681   81.6226  87.0578  95.0745  93.8416  87.017   97.5078  87.2019
  88.9827 100.9158  92.7823 100.9184 104.9854  88.674   99.5643  97.2495
  94.1179  92.1935  95.1671  93.3322 102.8967  94.6695  90.5285  89.0744
  97.7641  97.2938 100.7564  90.5236 100.2542  85.8452  96.4615  97.1492
  90.711  102.3193  87.1629  89.8751  86.7327  86.3991  95.2865  89.1709
  96.8163  91.6604  96.1065  99.6829  94.9718  87.4812  89.2596  89.5268
  93.799   97.3983  87.151   97.825  103.22    94.4239  83.6657  83.5159
 102.8453  87.0379  91.2742 100.4848  93.8388  90.8568  97.4616  97.5022
  82.446   87.1789  96.9206  90.3135  90.565   97.6538  98.0919  93.6252
  87.3867  84.7073  89.1135  86.7646  88.7301  86.643   96.7323  97.2604
  81.9424  87.1687  97.2066  83.4712  95.9781  91.8096  98.4086 100.7823
 101.556  100.7027  91.6098  88.8976]

그래프로 그려보자.(히스트)

plt.hist(np.mean(apple,axis=1), alpha=0.8)
plt.hist(np.mean(pineapple,axis=1), alpha=0.8)
plt.hist(np.mean(banana,axis=1), alpha=0.8)

사진(픽셀 평균값) 을 보니까 

바나나의 픽셀 평균값은 40아래에 많이 집중되어있다. (초록)

사과와 파인애플은 위 그래프만 보고 구분하기 힘들다.

 

더 좋은 방법은 없을까?

샘플의 평균값 말고 

그래프 종류

1 : 선 그래프 (line plot)

시계열 데이터와 같이 연속적인 값의 변화와 패턴을 파악하는데 적합하다.

 

그래프 꾸미기 옵션

'o' =                  : 선 아닌 점그래프로 표현

marker = 'o'      : 마커 모양 ('o' , '+', '*' 등등)

markerfacecolor='green' : 마커 배경색

markersize=10 : 마커 크기

color='olive'      : 선의 색

linewidth = 2    : 선의 두께

label=               : 라벨 지정

 

그래프 여러개 그리기

fig = plt.figure( figsize= ( x, y))

ax1 = fig.add_subplot(2,2,1)

ax2 = fig.add_subplot(2,2,2)

ax3 = fig.add_subplot(2,2,3)

ax4 = fig.add_subplot(2,2,4)

 

sns.stripplot(~, ~, data=df, ax1)

 

2 : 면적 그래프

선 그래프와 같지만  그래프 아래 색이 입혀진다.

색의 투명도 alpha= num 으로 투명도 설정 가능

stacked = True / False 로 누적할지 여부 설정 가능 (default= True)

 

 

 

3 : 막대 그래프

막대 그래프 : 각 변수 사이 값의 크기 차이를 설명하는데 적합하다.

kind=barh로 하면 수직 / 수평 역전가능 ! 

 

4 : 히스토그램

변수 하나인 단변수 데이터의 빈도수를 그래프로 표현한다.

x축을 같은 크기의 여러 구간으로 나누고 각 구간에 속하는 데이터 값의 개수를 y축에 표시함.!

 

5: 산점도

서로 다른 두 변수 사이의 관계를 나타낸다.

mpg 데이터로 산점도 그래프를 그려보자.

 

 

# cylinders 개수의 상대적 비율을 계산하여 시리즈 생성
cylinders_size = df.cylinders/df.cylinders.max() * 300

# 3개의 변수로 산점도 그리기
df.plot(kind='scatter',x = 'weight', y = 'mpg', marker='+', figsize=(10,5),
       cmap = 'viridis', c=cylinders_size, s=50, alpha=0.3)     # 실린더 사이즈 값
plt.title('Scatter Plot : mpg-weight-cylinders')
plt.show()
plt.savefig('C:\\Users\\woghk\\OneDrive\\바탕 화면\\스터디\\판다스 자료실\\part3\/저장파일.png')
plt.savefig('C:\\Users\\woghk\\OneDrive\\바탕 화면\\스터디\\판다스 자료실\\part3\/저장파일(투명).png', transparent=True)
plt.show()

 

 

cmap =  컬러 설정 ( 종류가 엄청 많지만 cmap = 'viridis' 만 쓰자.)

 

5: 파이 차트

원을 파이처럼 나눠서 표현.

비율을 한눈에 볼 수 있다는 장점

 

 

6: 박스 플롯

범주형 데이터의 분포를 파악하는데 적합하다

 

# 박스 플롯

df = pd.read_csv("C:\\Users\\woghk\\OneDrive\\바탕 화면\\스터디\\판다스 자료실\\part3\/auto-mpg.csv",header=None)
print(df.head())

#열 이름 지정
df.columns = ['mpg','cylinders','displacement','horsepower','weight',
             'acceleration','model year','origin','name']
from matplotlib import font_manager, rc
font_path = "C:\\Users\\woghk\\Downloads\\nanum-all\\나눔 글꼴\\나눔고딕\\NanumFontSetup_TTF_GOTHIC\/malgun.ttf"
font_name = font_manager.FontProperties(fname=font_path).get_name()
rc('font', family = font_name)

plt.style.use('seaborn-poster')
plt.rcParams['axes.unicode_minus']=False

fig = plt.figure(figsize=(15,5))
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)

ax1.boxplot(x=[df[df['origin']==1]['mpg'],  # 위에 origin = 1 미국, 2: 영국, 3: 일본    
              df[df['origin']==2]['mpg'],   # 미국[연비]....
              df[df['origin']==3]['mpg']],
            labels = ['USA','EU','JAPAN'])

ax2.boxplot(x=[df[df['origin']==1]['mpg'],
              df[df['origin']==2]['mpg'],
              df[df['origin']==3]['mpg']],
              labels = ['USA','EU','JAPAN'],
              vert=False)
ax1.set_title('제조국가별 연비 분포(수직 박스 플롯)')
ax2.set_title('제조국가별 연비 분포(수평 박스 플롯)')
plt.show()

# 박스플롯에서 평균을 볼 수 없다. 주황색은 중간값
# 동그라미는 이상값??

주황색 중간값

맨 위 맨 아래 : 최소, 최대값 

박스의 끝 : 1분위, 3분위 값

 

 

 

 

고급 그래프 ( seaborn 활용 )

seaborn = matlpot의 기능 스타일 확장 버전이다.

 

회귀선이 있는 산점도

regplot() 서로 다른 2개의 연속 변수 사이의 산점도를 그리고 선형회귀분석에 의한 회귀선을 함께 나타낸다.

fit_reg = False 옵션을 설정하면 회귀선을 안 보이게 할 수 있다.

 

## regplot() 함수 = 서로 다른 2개의 연속 변수 사이의 산점도를 그리고 선형회귀 분석에 의한 회귀선을 나타냄.
# fig_reg = False 하면 회귀선 안보이게 할 수 있다.
import matplotlib.pyplot as plt
import seaborn as sns
# 스타일 테마 ( darkgrid, whitegrid, dark, white, ticks)
sns.set_style('darkgrid')
# 그래프 객체 생성
fig = plt.figure(figsize=(15,5))
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)
# 그래프 = 선형회귀선 표시   (fit_reg = True)
sns.regplot(x='age',
            y='fare',
            data = titanic,
            ax= ax1)
# 그래프 = 선형회귀선 표시 X (fit_reg = False)
sns.regplot(x = 'age',
            y = 'fare',
            data = titanic,
            ax = ax2,
            fit_reg = False)
plt.show()

 

히트맵

히트맵으로 상관관계를 표현하기도 좋음!

table = titanic.pivot_table(index=['sex'], columns = ['class'], aggfunc = 'size')

#히트맵 그리기
sns.heatmap(table,
            annot = True, fmt = 'd',
            cmap = 'YlGnBu',
            linewidth = .5,
            cbar = False)
plt.show()

 

 

 

범주형 데이터의 산점도

stripplot() 과 swarmplot()이용

 

 

기존에 범주형 데이터면 stripplot을 애용했지만

분산까지 보여주는 swarmplot()을 쓰도록 하자!!!

 

 

조인트 그래프

jointplot() => 산점도를 기본으로 표시하고, 히스토그램을 동시에 보여줌

+ Recent posts