ㅅㅇ

딥러닝 _02_첫번째 딥러닝- MLP 구현 본문

AI_STUDY/딥러닝

딥러닝 _02_첫번째 딥러닝- MLP 구현

SO__OS 2022. 7. 12. 19:44

플레이데이터 빅데이터캠프 공부 내용 _ 7/11

딥러닝 _02_첫번째 딥러닝- MLP 구현

MLP(Multi Layer Perceptron) - 가장 기본적인 딥러닝모델의 구조

  - dense layer 로만 이뤄진 모델 구조

1.  Keras 개발 Process

1. 입력 텐서(X)와 출력 텐서(y)로 이뤄진 훈련 데이터를 정의

 

2. 입력과 출력을 연결하는 Layer(층)으로 이뤄진 네트워크(모델) 구조를 을 정의

                                                                             (딥러닝은 머신러닝과 달리 모델 구조는 우리가 설정해야 한다.) 


    - Sequential 방식: 순서대로 쌓아올린 네트워크로 이뤄진 모델을 생성하는 방식
    - Functional API 방식: 다양한 구조의 네트워크로 이뤄진 모델을 생성하는 방식
    - Subclass 방식: 네트워크를 정의하는 클래스를 구현.

 

3. 모델 Compile(컴파일) - 모델을 학습할 수 있는 상태로 만들어준다.
    - 모델이 Train(학습) 할때

     사용할 손실함수(Loss Function), 최적화기법(Optimizer), 학습과정을 모니터링할 평가지표(Metrics)를 설정

 

 - Compile : 실행할 수 있는 상태 (학습할 수 있는 상태) 로 만들어 주는 것. (기계어로 바꾸는 의미 아님.)

 

4. Training(학습/훈련)
    - Train dataset을 이용해 모델을 Train 시킨다.
   

 

2. 딥러닝 대표 예제 데이터 셋 - MNIST 이미지 분류

 

  • MNIST(Modified National Institute of Standards and Technology) database
  • 흑백 손글씨 숫자 0-9까지 10개의 범주로 구분해놓은 데이터셋
  • 하나의 이미지는 28 * 28 pixel 의 크기
  • 6만개의 Train 이미지와 1만개의 Test 이미지로 구성됨.

# 이 데이터 셋으로 딥러닝 분석을 할 것이다.

 

 

3. Keras 개발 예제 - MNIST 이미지 분류

3.1 데이터 로드 및 확인

 

(1) import

- tensorflow 별칭 : tf

import numpy as np # 넘파이
 
import tensorflow as tf # 텐서플로우 - 별칭 : tf
from tensorflow import keras # keras

 

(2) MNIST dataset Loading

 

-  데이터를  (train 이미지 X - 라벨 y), (test 이미지 X - 라벨 y) 로  데이터 나눠서 들고 오기.

(train_image, train_label), (test_image, test_label) = keras.datasets.mnist.load_data()

 

 # import  로 해도 됨. 뭘로 하든 상관없음.

from tensorflow.keras.mnist import load.data

 

(3) 데이터셋 type 확인 & shape 확인

 

- type : numpy 배열

type(train_image), type(train_label)
(numpy.ndarray, numpy.ndarray)
 
 

- 각 input data의 shape  == > 중요. 데이터 로드 후 항상 확인해야하는 부분.

 

(데이터개수, 특성의 형태)
(데이터개수, height, width)  - 데이터의 갯수, 데이터(이미지) 하나의 행, 열.   (색에 대한 없음 - > 흑백)

 

(60000, 28, 28)   :  28x28 (28행, 28열) 사이즈의 흑백(grayscale) 이미지    60000 장

 

print(train_image.shape, test_image.shape)
(60000, 28, 28) (10000, 28, 28)
 
 

- 각 label data의 shape

 

print(train_label.shape, test_label.shape)
(60000,) (10000,)

 

(4) Input 데이터(이미지) 확인 - 시각화( plt.imshow() )

 

- 0 ~ 19 index 의 데이터 확인  (20장의 이미지)

- 배경 : black (0), 숫자글씨 : white (255)

 

import matplotlib.pyplot as plt
plt.figure(figsize=(15, 5))

# 20장의 이미지와 label을 확인
N = 20
for i in range(N):
    plt.subplot(2, int(N/2), i+1) # 두 줄로 보기
    
    plt.imshow(train_image[i], cmap='gray') # train_image의 i번째 이미지를 출력 - grayscale의 경우 cmap 을 gray로 지정.
    
    plt.title(str(train_label[i]), fontsize=20) # subplot(axes)의 제목을 label로 설정.

#    plt.axis('off') # 축을 그리지 않는다.
    
plt.tight_layout()
plt.show()
 

 

(5) lnput 데이터 확인 

train_image[0] # 0번째 데이터

 

 

3.2 네트워크 (모델) 구현 및 구조 확인

 

(1) 네트워크 (모델) 구현

 

- Network : 전체 모델 구조 를 뜻한다.

 

- Sequential(순차) 방식으로 모델 생성 : model = keras.Sequential()

- 모델에 layer들을 추가 : model.add(레이어 객체)


- layer : 모델이 추론하는 각각의 단계를 의미
    => Input(학습/추론 할 데이터를 넣어주는 단계), Hidde(추론과정을 담당), Output(추론결과를 출력) Layer 로 구성된다.

 

   1 Input Layer 추가

 

   2 Hidden Layer 들을 추가

       -  art  : 모델 네트워크 만드는 것 (내 데이터에 맞게 모델 구조를 만들어 성능에 영향을 주는 작업) 
                            - 정해진 규칙이 있는게 아니라 내가 만드는 작업

 

   3 Output Layer 추가
       - 마지막에 추가된 layer는 output layer 이다.

 

- input layer : 입력받아,

    => HIDDEN 단계 - layer 단계에서 해야할 일을 하고 출력 내보내고, 다음 layer 입력으로 들어가 일하고 ...

    = >마지막 최종 출력 : 출력 내보냄.

       - 이전 layer 의 출력이 현재 일할 layer의 입력이 되는 것이다.

 

# Sequential(순차) 방식으로 모델 생성 
model = keras.Sequential()

# 1. Input Layer 추가
model.add(keras.layers.InputLayer((28,28))) 

# 2. Hidden Layer 들을 추가
model.add(keras.layers.Flatten())  

model.add(keras.layers.Dense(256, activation='relu')) # Fully Connected Layer  # unit 256 (LR 256- 추론하는애가 256개)
model.add(keras.layers.Dense(128, activation='relu'))

# 3. Output Layer 추가
model.add(keras.layers.Dense(10, activation='softmax'))

 

(2) 모델의 구조를 확인 (Text로 리턴)

 

- Output Shape : 해당 layer 처리 후 결과 output 의 shape 를 말해줌. 
   - flatten 의 out 으로 1차원 배열 (None, 784) 이 나와 dense 의 입력으로 들어간다.

   - 데이터의 갯수는 모르기에 none 이다.

 

- Param : 학습할 대상 갯수 (업데이트 해야할 파라미터 개수. 구조그림에서 선의 갯수 )

 

model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense (Dense)               (None, 256)               200960    
                                                                 
 dense_1 (Dense)             (None, 128)               32896     
                                                                 
 dense_2 (Dense)             (None, 10)                1290      
                                                                 
=================================================================
Total params: 235,146
Trainable params: 235,146
Non-trainable params: 0
_________________________________________________________________

 

 

(3) 모델의 구조를 확인 (graphviz 이용해 시각화)

 

# pip install pydot pydotplus graphviz 설치 필요.

from tensorflow.keras.utils import plot_model
plot_model(model, to_file='model_shapes.png', show_shapes=True)

 

 

3.3 컴파일 단계 (학습에 필요한 기능을 추가)

 

- 정의된 네트워크 모델에 학습을 위한 추가 설정을 되어야 컴파일(학습이 가능한 상태)이 가능하다.


    - Optimizer (필수) : 최적화 알고리즘
    - 손실함수 (필수) 
    - 평가지표

 

model.compile(optimizer='adam', # 최적화 함수 : Adam 알고리즘 
              loss='categorical_crossentropy', # cross entropy
              metrics=['accuracy']) # 추가 평가지표 - 정확도

 

3.4 데이터 준비 - 전처리

 

- X (Input Data Image) : float32 로 변환하고  0 ~ 1 사이의 값으로 정규화 
    - 0 ~ 255 = > 0 ~ 1 = > 기존값/255 

                                          # minmaxscale 할 필요 없음. ( 데이터 모두 max 값 255 로 동일하기에)


- y (Output Data) 0 ~ 9 : one hot encoding 처리

        - Label이 다중분류(Multi class classification)일 경우 One Hot Encoding 한다.


    - Keras의 onehot encoding 처리 함수
        - tensorflow.keras.utils.to_categorical()
        


- 일반적인 8 bit 정수표현  : signed int 최상위비트가 부호 비트. -128 ~ 127
- 색농도 0 ~ 255표현 : 음수필요 없음. unsigned int

print(np.min(train_image), np.max(train_image), train_image.dtype, sep=', ')
0, 255, uint8

 

(1) input image(X)를 정규화. 

  - 이미지를 0 ~ 1 로 정규화 하고 타입을 float32로 변환
      
     = > X_train 이 가지는 제일 큰 값(max)인 255로 나누면  0 ~ 1 로 정규화.

 

X_train = train_image.astype(np.float32) # 타입 변환: unit8 -> float32
X_train /= 255.0 # X_train= X_train/255.0 

X_test = test_image.astype(np.float32)
X_test /= 255.0

print(np.min(X_train), np.max(X_test), X_train.dtype, sep=', ')
0.0, 1.0, float32
 

(2) label (y) 를 one hot encoding

모델이 각 클래스 별 확률이 출력되도록 정의 되었기 때문에 

  학습시(최적화시) 오차를 구하기 위해서는 정답이 label 이 아니라 확률이 되도록 y 를 one hot encoding 
- 모든 머신러닝, 딥너링에서 제공한다. 
    - keras.utils.to_categorical()
 
y_train = keras.utils.to_categorical(train_label)
y_test = keras.utils.to_categorical(test_label)

print(train_label.shape, test_label.shape) # 변환 전
print(y_train.shape, y_test.shape) # 변환 후
(60000,) (10000,)
(60000, 10) (10000, 10)

 

y_train[:5]
array([[0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]], dtype=float32)

 

3.5 학습 (fit) 및 History 확인

 

- model.fit()
    - 모델 학습 메소드. 
    - 학습과정의 Log를 History 객체로 반환한다.


- History : train 시 에폭별 평가지표값들을 모아서 제공.   

 

 

(1) 학습 (fit)

 

epochs : 전체 train dataset 을 몇 번 반복해서 학습시킬지 정의

                (데이터를 여러번 봐야할 필요가 있다. 몇 백 몇 천 줄 수 도 있음. )

                      == > 1 epoch (단위개념) : 한 번 학습

               

batch_size  : Train dataset 을 학습시키는 단위로 한번에 지정한 개수를 정의

                      100 개의 데이터 씩 끊어서 학습시킨다.

                        == > batch-size를 한번 학습한 것을 1 step(단위) 

 

validation_split :  train/validation 분리 - val data set 의 비율을 정의

 

 

- train set 의 30%(0.3)를 validation set으로 사용 

- 문제집(train set) 에 문제가 42000 개

   - epochs = 10  :  42000 개 문제를 10 번 풀어라.   
   - batch_size = 100  :  42000 개의 문제를 100 문제를 단위로 나눠 풀고 정답확인해라.  = > 420 step 

                                     (100 문제 풀고 정답확인하고 다음 100문제 풀고 정답확인하고 ... 백개의 단위로 문제를 푼다.)

 

hist = model.fit(X_train, y_train, # input: X_train output : y_train
                  epochs=10,  # 전체 train dataset 을 몇 번 반복해서 학습시킬지 정의. 
                  batch_size=100, # Train dataset 을 학습시키는 단위로 한번에 지정한 개수(100)의 데이터 씩 끊어서 학습
                  validation_split=0.3 # train/validation 분리 - train set 의 30%(0.3)를 validation set으로 사용 
                 )
Epoch 1/10
420/420 [==============================] - 4s 8ms/step - loss: 0.3048 - accuracy: 0.9122 - val_loss: 0.1568 - val_accuracy: 0.9543
Epoch 2/10
420/420 [==============================] - 4s 9ms/step - loss: 0.1157 - accuracy: 0.9655 - val_loss: 0.1154 - val_accuracy: 0.9662
Epoch 3/10
420/420 [==============================] - 3s 7ms/step - loss: 0.0732 - accuracy: 0.9778 - val_loss: 0.1116 - val_accuracy: 0.9663
Epoch 4/10
420/420 [==============================] - 3s 8ms/step - loss: 0.0496 - accuracy: 0.9847 - val_loss: 0.1051 - val_accuracy: 0.9689
Epoch 5/10
420/420 [==============================] - 3s 8ms/step - loss: 0.0348 - accuracy: 0.9894 - val_loss: 0.1031 - val_accuracy: 0.9706
Epoch 6/10
420/420 [==============================] - 3s 8ms/step - loss: 0.0265 - accuracy: 0.9918 - val_loss: 0.1053 - val_accuracy: 0.9718
Epoch 7/10
420/420 [==============================] - 4s 9ms/step - loss: 0.0220 - accuracy: 0.9930 - val_loss: 0.1330 - val_accuracy: 0.9660
Epoch 8/10
420/420 [==============================] - 4s 10ms/step - loss: 0.0176 - accuracy: 0.9945 - val_loss: 0.1086 - val_accuracy: 0.9727
Epoch 9/10
420/420 [==============================] - 3s 8ms/step - loss: 0.0148 - accuracy: 0.9954 - val_loss: 0.1116 - val_accuracy: 0.9726
Epoch 10/10
420/420 [==============================] - 3s 8ms/step - loss: 0.0134 - accuracy: 0.9957 - val_loss: 0.1036 - val_accuracy: 0.9756

 

- train 70 % 가지고 학습하고 train 과 val set 으로 검증을 한다. 

    = > train set 데이터 42000 개가 있다.

- banch_size = 100 

    = > 42000 개 데이터를 100 씩 => 420 step 의 과정을 거친다. (100개씩 420 step 한 게 1 epoch)

    = > epochs = 10 => 이걸 10번 

     

 

[출력 설명]


Epoch 1/10  # 현재 학습중인 epoch/ 전체 epoch수

420/420 [==============================] - 4s 8ms/step - loss: 0.3048 - accuracy: 0.9122 - val_loss: 0.1568 - val_accuracy: 0.9543


# 현재 학습중인 step/전체 step 수          # 1epoch 걸린시간:4s 1step 학습에 걸린시간: 8ms  

# - train 오차 loss - accuracy - validation 오차(loss) - validation 정확도

(원래 기본 loss 나오고 아까 추가 평가지표 정확도 추가해서)

- epoch 별 성능을 보면, train 은 여러번 학습을 할수록 좋아진다.

                                       test은 좋아지다가 안 좋아짐 = > 모델이 복잡해짐. overfitting

 

 

(2) history 확인 및 시각화

 

위와 같이 모델이 학습을 끝내면

epoch 별 train 오차loss - accuracy - validation 오차(loss) - validation 정확도 를 반환하는데,

epoch 별 history를 시각화하여 보기 위해 이를 hist 변수에 담아준다.

 

hist.history - {key 성능이름 : epoch 별 성능변화(loss, accuracy)} dict 형식으로 반환

 

 

print(type(hist))
print(type(hist.history))
hist.history
<class 'keras.callbacks.History'>
<class 'dict'>
{'loss': [0.300797700881958,
  0.11193343251943588,
  0.07239670306444168,
  0.048918649554252625,
  0.0363864041864872,
  0.02820790559053421,
  0.021542903035879135,
  0.015045295469462872,
  0.011798908933997154,
  0.017987487837672234],
 'accuracy': [0.9140238165855408,
  0.9668095111846924,
  0.9782381057739258,
  0.9855476021766663,
  0.9883809685707092,
  0.9912857413291931,
  0.9931666851043701,
  0.9955475926399231,
  0.9963333606719971,
  0.9936904907226562],
 'val_loss': [0.14970698952674866,
  0.1195853129029274,
  0.11769795417785645,
  0.09895452111959457,
  0.10371357202529907,
  0.09293486177921295,
  0.1014929711818695,
  0.11051476746797562,
  0.11349636316299438,
  0.10624457895755768],
 'val_accuracy': [0.9559444189071655,
  0.9640555381774902,
  0.9626111388206482,
  0.9716110825538635,
  0.9708333611488342,
  0.9731666445732117,
  0.9737777709960938,
  0.9726666808128357,
  0.9733333587646484,
  0.9735000133514404]}

 

- graph

 

    - loss 오차 - 작을수록 좋은 것. 
    - accuracy 정확도 - 클수록 좋은 것.

         = >  3 epoch 5 epoch 에서 성능 좋음.

import matplotlib.pyplot as plt
plt.figure(figsize=(7,10))

plt.subplot(2,1,1)
plt.plot(range(1,11), hist.history['loss'], label='Train Loss')
plt.plot(range(1,11), hist.history['val_loss'], label="Validation Loss")
plt.title("Loss per Epoch", fontsize=20)
plt.legend()

plt.subplot(2,1,2)
plt.plot(range(1,11), hist.history['accuracy'], label='Train Accuracy')
plt.plot(range(1,11), hist.history['val_accuracy'], label='Validation Accuracy')
plt.title("Accuracy per Epoch", fontsize=20)

plt.legend()
plt.tight_layout()
plt.show()

 

 

3.6 데이터 셋 평가 = > 최종평가

 

model.evaluate(X,y)

 

test_loss, test_acc = model.evaluate(X_test, y_test)
313/313 [==============================] - 2s 5ms/step - loss: 0.0840 - accuracy: 0.9801

 

print(test_loss, test_acc)
0.0839601531624794 0.9800999760627747
 
 
 

3.7 새로운 데이터 추론

 

- 새로운 데이터를 추론하기 전에 

  학습데이터에 했던 전처리과정을 새로운 데이터 셋에 동일하게 적용 한 뒤 추론한다.

 

(1) 추론 메소드


- predict() - 회귀이든 분류이든 해당 함수 사용

 

    1) 분류: 각 클래스 별 확률 반환
    2) 회귀: 최종 예측 결과


- 분류문제일때 predict() 결과에서 class label 출력하고자 한다면?

   모델 출력값을 후처리해서 최종 예측 label 을 확인한다.   

 

      1) 이진 분류(binary classification)
          - `numpy.where(model.predict(x) > 0.5, 1, 0).astype("int32")`
      2) 다중클래스 분류(multi-class classification)
          - `numpy.argmax(model.predict(x), axis=1)`

 

 

 

(2) 새로운 데이터 셋 추론 예시

 

- input image(X) 에 필요한 전처리  : 28x28, float32, 0 ~ 1 정규화, 검은색바탕에 흰 글씨

 

- X_test 의 처음 5개 데이터를 이용해 새로운 데이터라 하고 추론
     = >  X_test 는 test_image 를 전처리한 데이터셋(unit8 ->float32 변환, 0 ~ 1 사이로 정규화)

               = > 전처리 해줄 필요 없음.

 

new_image = X_test[:5] # 28x28 이미지 5개
new_image.shape
(5, 28, 28)

 

 

- predict(예측할 데이터의 Feature)    == > (데이터개수, Feature shape) 을 반환


   X_test   (5,28,28)  == > result (5,10) : 5개 이미지에 대해 0~9 사이의 값으로 예측한다.

                                                             (onehotencoding = > 10개의 열)

 

result = model.predict(new_image)
result.shape
1/1 [==============================] - 0s 361ms/step
(5, 10)
 
 
 
 
- 0 번 데이터에 대한 예측결과 
 
     => (10,)   : 0 ~ 9 일 각각의 확률을 반환
 
     = > 이 값을 통해 label을 확인할 수 있어야 한다.
 
 
result[0]
 
array([1.9226378e-09, 1.5068100e-08, 1.8938231e-07, 2.4677180e-05,
       1.1303006e-14, 3.4873035e-12, 1.2096423e-13, 9.9997413e-01,
       7.7752998e-11, 9.2615835e-07], dtype=float32)
 
 

(3) 예측 결과 (각 class 별 확률) 을 통해 label 을 확인

 

- 가장 높은 확률이 있는 index 를 받아야 한다.

     = >  (5, 10) 에서 수평 방향으로 10개 중에 제일 큰 것.

                  => axis = - 1 :마지막 축 기준으로 max 값을 가진 index 를 조회 (5,10)   마지막 축은 1번축

# 가장 높은 확률
print(np.max(result[0]))

# 가장 높은 확률이 있는 index
np.argmax(result, axis=-1)

 

 

- label 확인

 

result_label = np.argmax(result, axis=-1)
result_label
array([7, 2, 1, 0, 4], dtype=int64)

 

- 시각화 .imshow()

 

idx = 0
plt.imshow(test_image[idx], cmap='gray')
plt.title(f'{test_label[idx]} : {result_label[idx]}')

plt.show()