chapter_9_3

LSTM 신경망 훈련하기

  • RNN은 실무에서 안씀!!!!!!!!!!!
  • 나온 배경
    • 문장이 길면, 학습 능력이 떨어짐
    • Long Short-Term Memory
  • 단기 기억을 오래 기억하기 위해 고안됨.

데이터 불러오기

1
2
3
4
5
6
7
8
from tensorflow.keras.datasets import imdb
from sklearn.model_selection import train_test_split

(train_input, train_target), (test_input, test_target) = imdb.load_data(
num_words=500)

train_input, val_input, train_target, val_target = train_test_split(
train_input, train_target, test_size=0.2, random_state=42)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
17465344/17464789 [==============================] - 0s 0us/step
17473536/17464789 [==============================] - 0s 0us/step

padding

1
2
3
4
from tensorflow.keras.preprocessing.sequence import pad_sequences

train_seq = pad_sequences(train_input, maxlen=100)
val_seq = pad_sequences(val_input, maxlen=100)

모형 만들기

1
2
3
4
5
6
7
from tensorflow import keras
model = keras.Sequential()
model.add(keras.layers.Embedding(500, 16, input_length = 100))
model.add(keras.layers.LSTM(8)) # SimpleRNN
model.add(keras.layers.Dense(1, activation='sigmoid'))

model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                /images/chapter_9_3/output Shape              Param #   
=================================================================
 embedding (Embedding)       (None, 100, 16)           8000      
                                                                 
 lstm (LSTM)                 (None, 8)                 800       
                                                                 
 dense (Dense)               (None, 1)                 9         
                                                                 
=================================================================
Total params: 8,809
Trainable params: 8,809
Non-trainable params: 0
_________________________________________________________________
1
2
3
4
5
6
7
8
9
10
11
12
13
rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
model.compile(optimizer=rmsprop, loss='binary_crossentropy',
metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-lstm-model.h5',
save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
restore_best_weights=True)

# epochs = 100
history = model.fit(train_seq, train_target, epochs=10, batch_size=64,
validation_data=(val_seq, val_target),
callbacks=[checkpoint_cb, early_stopping_cb])
Epoch 1/10
313/313 [==============================] - 13s 36ms/step - loss: 0.6922 - accuracy: 0.5400 - val_loss: 0.6909 - val_accuracy: 0.5952
Epoch 2/10
313/313 [==============================] - 12s 39ms/step - loss: 0.6886 - accuracy: 0.6219 - val_loss: 0.6855 - val_accuracy: 0.6436
Epoch 3/10
313/313 [==============================] - 10s 33ms/step - loss: 0.6778 - accuracy: 0.6671 - val_loss: 0.6655 - val_accuracy: 0.6896
Epoch 4/10
313/313 [==============================] - 11s 35ms/step - loss: 0.6173 - accuracy: 0.7113 - val_loss: 0.5780 - val_accuracy: 0.7118
Epoch 5/10
313/313 [==============================] - 11s 34ms/step - loss: 0.5591 - accuracy: 0.7294 - val_loss: 0.5511 - val_accuracy: 0.7362
Epoch 6/10
313/313 [==============================] - 13s 43ms/step - loss: 0.5346 - accuracy: 0.7499 - val_loss: 0.5300 - val_accuracy: 0.7488
Epoch 7/10
313/313 [==============================] - 10s 33ms/step - loss: 0.5143 - accuracy: 0.7640 - val_loss: 0.5139 - val_accuracy: 0.7618
Epoch 8/10
313/313 [==============================] - 10s 33ms/step - loss: 0.4964 - accuracy: 0.7748 - val_loss: 0.4970 - val_accuracy: 0.7722
Epoch 9/10
313/313 [==============================] - 10s 33ms/step - loss: 0.4816 - accuracy: 0.7828 - val_loss: 0.4844 - val_accuracy: 0.7806
Epoch 10/10
313/313 [==============================] - 11s 35ms/step - loss: 0.4691 - accuracy: 0.7908 - val_loss: 0.4739 - val_accuracy: 0.7808

손실 곡선 추가

1
2
3
4
5
6
7
8
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()

png

순환층에 드롭아웃 적용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
model2 = keras.Sequential()

model2.add(keras.layers.Embedding(500, 16, input_length=100))
# 드롭아웃 추가
model2.add(keras.layers.LSTM(8, dropout=0.3))
model2.add(keras.layers.Dense(1, activation='sigmoid'))

rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
model2.compile(optimizer=rmsprop, loss='binary_crossentropy',
metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-dropout-model.h5',
save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
restore_best_weights=True)

# epcohs = 100
history = model2.fit(train_seq, train_target, epochs=10, batch_size=64,
validation_data=(val_seq, val_target),
callbacks=[checkpoint_cb, early_stopping_cb])

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
313/313 [==============================] - 14s 37ms/step - loss: 0.6929 - accuracy: 0.5109 - val_loss: 0.6926 - val_accuracy: 0.5336
Epoch 2/10
313/313 [==============================] - 11s 34ms/step - loss: 0.6915 - accuracy: 0.5725 - val_loss: 0.6908 - val_accuracy: 0.5904
Epoch 3/10
313/313 [==============================] - 11s 34ms/step - loss: 0.6887 - accuracy: 0.6108 - val_loss: 0.6868 - val_accuracy: 0.6312
Epoch 4/10
313/313 [==============================] - 13s 40ms/step - loss: 0.6818 - accuracy: 0.6508 - val_loss: 0.6774 - val_accuracy: 0.6510
Epoch 5/10
313/313 [==============================] - 11s 35ms/step - loss: 0.6623 - accuracy: 0.6740 - val_loss: 0.6441 - val_accuracy: 0.6678
Epoch 6/10
313/313 [==============================] - 11s 35ms/step - loss: 0.6024 - accuracy: 0.7145 - val_loss: 0.5710 - val_accuracy: 0.7374
Epoch 7/10
313/313 [==============================] - 12s 38ms/step - loss: 0.5601 - accuracy: 0.7370 - val_loss: 0.5479 - val_accuracy: 0.7512
Epoch 8/10
313/313 [==============================] - 11s 35ms/step - loss: 0.5387 - accuracy: 0.7502 - val_loss: 0.5285 - val_accuracy: 0.7600
Epoch 9/10
313/313 [==============================] - 11s 34ms/step - loss: 0.5189 - accuracy: 0.7624 - val_loss: 0.5089 - val_accuracy: 0.7714
Epoch 10/10
313/313 [==============================] - 11s 35ms/step - loss: 0.5008 - accuracy: 0.7746 - val_loss: 0.4928 - val_accuracy: 0.7758

png

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

chapter_9_2

순환 신경망으로 IMDB 리뷰 분류

  • 혹시, 자연어 처리, 감정분석 관심 있으면 강사님께 개인적으로 연락!

  • 주제 : 긍정리뷰 부정리뷰 분류

  • 501p

    • 텍스트 자체가 신경망에 전달하지 않는다! (문자열 –> 수식에 적용 X)

    • 문자열을 수식으로 정하는 규칙이 매우 가변적임. (토근화, Tokenizing)

    • He follows the cat. He loves the cat.
      10 11 12 13 10 14 12 13

    • 고양이를 따라간다. He follows the cat.
      10 11 12 13 14 15

  • RNN, LSTM 알고리즘

    • 영어권 사람들이 만들었어요.
    • 자연어처리와 관련된 많은 알고리즘
      • 영어권 사람들이 만듬
  • 한글 != 영어

    • 성과 내려면 제품(=돈) (네이버)
1
2
from tensorflow.keras.datasets import imdb
(train_input, train_target), (test_input, test_target) = imdb.load_data(num_words = 500)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
17465344/17464789 [==============================] - 0s 0us/step
17473536/17464789 [==============================] - 0s 0us/step
  • 데이터 크기 확인 (1차원 배열)
  • 텍스트의 길이가 다 다르기 때문에 1차원 배열로 정리 가능
1
print(train_input.shape, test_input.shape)
(25000,) (25000,)
  • 문장의 길이가 다 다르다!
1
2
3
print(len(train_input[0]))
print(len(train_input[1]))
print(len(train_input[2]))
218
189
141
  • Raw 데이터 전처리 -> 토큰화 작업이 끝난 상황 (문자열 –> 숫자로 바뀜)
1
print(train_input[0])
[1, 14, 22, 16, 43, 2, 2, 2, 2, 65, 458, 2, 66, 2, 4, 173, 36, 256, 5, 25, 100, 43, 2, 112, 50, 2, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 2, 2, 17, 2, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2, 19, 14, 22, 4, 2, 2, 469, 4, 22, 71, 87, 12, 16, 43, 2, 38, 76, 15, 13, 2, 4, 22, 17, 2, 17, 12, 16, 2, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2, 2, 16, 480, 66, 2, 33, 4, 130, 12, 16, 38, 2, 5, 25, 124, 51, 36, 135, 48, 25, 2, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 2, 15, 256, 4, 2, 7, 2, 5, 2, 36, 71, 43, 2, 476, 26, 400, 317, 46, 7, 4, 2, 2, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2, 56, 26, 141, 6, 194, 2, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 2, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 2, 88, 12, 16, 283, 5, 16, 2, 113, 103, 32, 15, 16, 2, 19, 178, 32]
  • Target 데이터 출력
    • 0은 부정리뷰
    • 1은 긍정리뷰
1
print(train_target[:20])
[1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 1 0 1]

데이터셋 분리

1
2
3
4
5
6
from sklearn.model_selection import train_test_split 
train_input, val_input, train_target, val_target = train_test_split(
train_input, train_target, test_size = 0.2, random_state=42
)

train_input.shape, val_input.shape, train_target.shape, val_target.shape
((20000,), (5000,), (20000,), (5000,))

데이터 시각화

  • 각 리뷰의 평균단어의 갯수
1
2
3
4
5
import numpy as np
#temp_list = [len(x) for x in train_input]
# print(temp_list)
lengths = np.array([len(x) for x in train_input])
print(np.mean(lengths), np.median(lengths))
239.00925 178.0
1
2
3
4
5
import matplotlib.pyplot as plt 
plt.hist(lengths)
plt.xlabel('length')
plt.ylabel('frequency')
plt.show()

png

  • 짧은 단어 100개만 사용
  • 모든 길이를 100에 맞춘다.
    • “패딩”
  • 데이터의 갯수는 20000, 전체 길이는 100으로 맞춤
1
2
3
4
from tensorflow.keras.preprocessing.sequence import pad_sequences 
train_seq = pad_sequences(train_input, maxlen = 100)

print(train_seq.shape)
(20000, 100)
1
print(train_seq[5])
[  0   0   0   0   1   2 195  19  49   2   2 190   4   2 352   2 183  10
  10  13  82  79   4   2  36  71 269   8   2  25  19  49   7   4   2   2
   2   2   2  10  10  48  25  40   2  11   2   2  40   2   2   5   4   2
   2  95  14 238  56 129   2  10  10  21   2  94 364 352   2   2  11 190
  24 484   2   7  94 205 405  10  10  87   2  34  49   2   7   2   2   2
   2   2 290   2  46  48  64  18   4   2]
1
2
print(train_input[0][-10:])
print(train_seq[0])
[6, 2, 46, 7, 14, 20, 10, 10, 470, 158]
[ 10   4  20   9   2 364 352   5  45   6   2   2  33 269   8   2 142   2
   5   2  17  73  17 204   5   2  19  55   2   2  92  66 104  14  20  93
  76   2 151  33   4  58  12 188   2 151  12 215  69 224 142  73 237   6
   2   7   2   2 188   2 103  14  31  10  10 451   7   2   5   2  80  91
   2  30   2  34  14  20 151  50  26 131  49   2  84  46  50  37  80  79
   6   2  46   7  14  20  10  10 470 158]
1
val_seq = pad_sequences(val_input, maxlen = 100)

순환 신경망 만들기

1
2
3
4
from tensorflow import keras
model = keras.Sequential()
model.add(keras.layers.SimpleRNN(8, input_shape=(100, 500)))
model.add(keras.layers.Dense(1, activation='sigmoid'))
  • 원핫 인코딩 적용
    • 매칭이 성공하면 1을 출력
1
2
train_oh = keras.utils.to_categorical(train_seq)
print(train_oh.shape)
(20000, 100, 500)
1
print(train_oh[0][0][:12])
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
1
print(np.sum(train_oh[0][0]))
1.0
  • 1이 출력되었으므로 성공
  • 이제 검증데이터에 적용한다.
1
val_oh = keras.utils.to_categorical(val_seq)
1
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 simple_rnn (SimpleRNN)      (None, 8)                 4072      
                                                                 
 dense (Dense)               (None, 1)                 9         
                                                                 
=================================================================
Total params: 4,081
Trainable params: 4,081
Non-trainable params: 0
_________________________________________________________________
1
2
3
4
5
6
7
8
9
10
11
12
13
14
"""
rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
model.compile(optimizer=rmsprop, loss='binary_crossentropy',
metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-simplernn-model.h5',
save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
restore_best_weights=True)

history = model.fit(train_oh, train_target, epochs=100, batch_size=64,
validation_data=(val_oh, val_target),
callbacks=[checkpoint_cb, early_stopping_cb])
"""
"\nrmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)\nmodel.compile(optimizer=rmsprop, loss='binary_crossentropy', \n              metrics=['accuracy'])\n\ncheckpoint_cb = keras.callbacks.ModelCheckpoint('best-simplernn-model.h5', \n                                                save_best_only=True)\nearly_stopping_cb = keras.callbacks.EarlyStopping(patience=3,\n                                                  restore_best_weights=True)\n\nhistory = model.fit(train_oh, train_target, epochs=100, batch_size=64,\n                    validation_data=(val_oh, val_target),\n                    callbacks=[checkpoint_cb, early_stopping_cb])\n"
  • 514p
    • 문제점 발생: 토큰 1개를 500차원으로 늘림.. –> 데이터 크기가 500배 커짐
1
2
3
4
5
6
7
from tensorflow import keras
model2 = keras.Sequential()
model2.add(keras.layers.Embedding(500, 16, input_length = 100))
model2.add(keras.layers.SimpleRNN(8))
model2.add(keras.layers.Dense(1, activation='sigmoid'))

model2.summary()
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding (Embedding)       (None, 100, 16)           8000      
                                                                 
 simple_rnn_1 (SimpleRNN)    (None, 8)                 200       
                                                                 
 dense_1 (Dense)             (None, 1)                 9         
                                                                 
=================================================================
Total params: 8,209
Trainable params: 8,209
Non-trainable params: 0
_________________________________________________________________
1
2
3
4
5
6
7
8
9
10
11
12
13
14
'''
rmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)
model2.compile(optimizer=rmsprop, loss='binary_crossentropy',
metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-embedding-model.h5',
save_best_only=True)
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3,
restore_best_weights=True)

history = model2.fit(train_seq, train_target, epochs=100, batch_size=64,
validation_data=(val_seq, val_target),
callbacks=[checkpoint_cb, early_stopping_cb])
'''
"\nrmsprop = keras.optimizers.RMSprop(learning_rate=1e-4)\nmodel2.compile(optimizer=rmsprop, loss='binary_crossentropy', \n               metrics=['accuracy'])\n\ncheckpoint_cb = keras.callbacks.ModelCheckpoint('best-embedding-model.h5', \n                                                save_best_only=True)\nearly_stopping_cb = keras.callbacks.EarlyStopping(patience=3,\n                                                  restore_best_weights=True)\n\nhistory = model2.fit(train_seq, train_target, epochs=100, batch_size=64,\n                     validation_data=(val_seq, val_target),\n                     callbacks=[checkpoint_cb, early_stopping_cb])\n "
  • Reference : 혼자 공부하는 머신러닝 + 딥러닝

chapter_9_1

순차 데이터와 순환 신경망

통계

  • 초급 레벨 : 기초통계 (t.test, 분산분석, 회귀분석 등)
  • 중급 레벨 : 시계열 분석 / 베이지안 / 비모수검정
  • 시계열 데이터 : 주식/ 날씨 / 매장 매출
    • R로 공부할 것

텍스트

  • 텍스트 마이닝 (데이터 분석가)
    • 대표 적으로 감정분석 (긍정 / 부정 분류)
    • 문자열 : 인코딩하는 방법론이 존재
  • 자연어 처리 (개발자에 해당)
    • 챗봇
    • 자동 번역
  • 기본 딥러닝 알고리즘 / RNN & LSTM
    • 현실에서 쓸까? 안쓴다!
  • 자료
  • 분야 선정
    • 영상인식, 이미지 분류, 음성, 자연어

순환 신경망

  • 이미지는 픽셀값이 어느정도 고정이 되어 있음

    • 28x28로 정의 / 모든 데이터는 28x28 맞출 수 있음
  • 텍스트

    • 값이 고정이 불가함
  • 494p

  • I am a boy(1, 4, 3)

  • I am a handsome boy(1, 4, 1, 2)

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

Scikitlearn_pipeline_tutorial

데이터 누수 방지 위한 모델링 기법 : 파이프라인 구축

  • 수능 시험 = 최종 테스트 데이터
  • 모의고사 또는 과거 기출문제 = 검증데이터
  • 교과서 문제지 = 훈련 데이터
  • 머신러닝 엔지니어 : MLOps (선행되어야 하는 코드 조건, Pipeline 형태로 구축)
    • 머신러닝 코드 자동화 가능! 운영 가능!
    • 개발업계의 최상위 연봉자!

데이터 불러오기

1
2
3
4
import pandas as pd
import numpy as np
data = pd.read_csv('https://raw.githubusercontent.com/MicrosoftDocs/ml-basics/master/data/daily-bike-share.csv')
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 731 entries, 0 to 730
Data columns (total 14 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   instant     731 non-null    int64  
 1   dteday      731 non-null    object 
 2   season      731 non-null    int64  
 3   yr          731 non-null    int64  
 4   mnth        731 non-null    int64  
 5   holiday     731 non-null    int64  
 6   weekday     731 non-null    int64  
 7   workingday  731 non-null    int64  
 8   weathersit  731 non-null    int64  
 9   temp        731 non-null    float64
 10  atemp       731 non-null    float64
 11  hum         731 non-null    float64
 12  windspeed   731 non-null    float64
 13  rentals     731 non-null    int64  
dtypes: float64(4), int64(9), object(1)
memory usage: 80.1+ KB

데이터 추출

1
2
3
4
cols = ['season', 'mnth', 'holiday', 'weekday', 'workingday', 'weathersit', 'temp', 'atemp', 'hum', 'windspeed', 'rentals']
data = data[cols]
data.info()
# data['mnth'].value_counts()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 731 entries, 0 to 730
Data columns (total 11 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   season      731 non-null    int64  
 1   mnth        731 non-null    int64  
 2   holiday     731 non-null    int64  
 3   weekday     731 non-null    int64  
 4   workingday  731 non-null    int64  
 5   weathersit  731 non-null    int64  
 6   temp        731 non-null    float64
 7   atemp       731 non-null    float64
 8   hum         731 non-null    float64
 9   windspeed   731 non-null    float64
 10  rentals     731 non-null    int64  
dtypes: float64(4), int64(7)
memory usage: 62.9 KB
  • Data Preprocessing
    • 결측치 수동으로 채우거나
    • 불필요한 변수를 제거하거나
    • 이상치를 제거하거나
    • 파생변수를 만들거나 등

기본 : 데이터 불러오기 -> 데이터 전처리 -> 피처공학(원핫-인코딩) -> 데이터셋 분리 -> 모델링 코드 -> 모델평가

파이프라인 : 데이터 불러오기 -> 데이터 전처리 -> 데이터셋 분리 -> 파이프라인 구축(피처공학, 모델링 코드) -> 모델 평가

데이터 셋 분리

1
2
3
4
5
from sklearn.model_selection import train_test_split
X = data.drop('rentals',axis=1)
y = data['rentals']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=123)
  • Feature Engineering
    • 기존 : 개별적으로 코드 작성
    • 현재 : Pipeline 코드로 추가할 것

Pipeline 구축

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from sklearn.preprocessing import StandardScaler, OrdinalEncoder, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline

# 데이터 타입 3가지
# 수치형 데이터, 문자열 데이터
# 문자열 데이터 : 범주형(명목형, 서열형 데이터로 구분됨)
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='mean'))
,('scaler', StandardScaler())
])

ordinal_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='constant'))
,('ordEncoder', OrdinalEncoder())
])

onehot_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='constant'))
,('oheEncoder', OneHotEncoder())
])

# 수치형 데이터 및 Categorical 데이터 컬럼 분리

numeric_features = ['temp', 'atemp', 'hum', 'windspeed']
ordinal_features = ['holiday', 'weekday', 'workingday', 'weathersit']
onehot_features = ['season', 'mnth']

# numeric_features = data.select_dtypes(include=['int64', 'float64']).columns
# categorical_features = data.select_dtypes(include=['object']).drop(['Loan_Status'], axis=1).columns

preprocessor = ColumnTransformer(
transformers=[
('numeric', numeric_transformer, numeric_features)
, ('ord_categorical', ordinal_transformer, ordinal_features)
, ('ohe_categorical', onehot_transformer, onehot_features)
])

모델 적용

  • 전처리가 끝났으니 모델을 적용한다.
1
2
3
4
5
6
7
8
9
from sklearn.ensemble import RandomForestRegressor

pipeline = Pipeline(steps = [
('preprocessor', preprocessor)
,('regressor', RandomForestRegressor())
])

rf_model = pipeline.fit(X_train, y_train)
print(rf_model)
Pipeline(steps=[('preprocessor',
                 ColumnTransformer(transformers=[('numeric',
                                                  Pipeline(steps=[('imputer',
                                                                   SimpleImputer()),
                                                                  ('scaler',
                                                                   StandardScaler())]),
                                                  ['temp', 'atemp', 'hum',
                                                   'windspeed']),
                                                 ('ord_categorical',
                                                  Pipeline(steps=[('imputer',
                                                                   SimpleImputer(strategy='constant')),
                                                                  ('ordEncoder',
                                                                   OrdinalEncoder())]),
                                                  ['holiday', 'weekday',
                                                   'workingday',
                                                   'weathersit']),
                                                 ('ohe_categorical',
                                                  Pipeline(steps=[('imputer',
                                                                   SimpleImputer(strategy='constant')),
                                                                  ('oheEncoder',
                                                                   OneHotEncoder())]),
                                                  ['season', 'mnth'])])),
                ('regressor', RandomForestRegressor())])
  • 파이프라인끼리 연결시켜서 길게 늘이는 원리
    • 데이터가 라인을 따라 흐르게 된다. 자동화

모델 평가

1
2
3
from sklearn.metrics import r2_score
predictions = rf_model.predict(X_test)
print (r2_score(y_test, predictions))
0.7728368422640097

다중 모형 개발

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.ensemble import RandomForestRegressor
from sklearn.tree import DecisionTreeRegressor

regressors = [
RandomForestRegressor()
,DecisionTreeRegressor()
]

# regressors = [pipe_rf, pipe_dt]
for regressor in regressors:
pipeline = Pipeline(steps = [
('preprocessor', preprocessor)
,('regressor',regressor)
])
model = pipeline.fit(X_train, y_train)
predictions = model.predict(X_test)
print(regressor)
print(f'Model r2 score:{r2_score(predictions, y_test)}')
RandomForestRegressor()
Model r2 score:0.74544998600902
DecisionTreeRegressor()
Model r2 score:0.6365934810580942

chapter_8_1

합성곱 신경망

  • 코드보다는 용어 정리가 중요하다

  • 더 나은 정확도를 위한 발전 과정

    • 로지스틱 회귀 (일반 ML 모형) : 81%
    • 인공신경망 (딥러닝 초기 모형) : 87%
    • 합성곱 (Convolution, CNN)
  • 합성곱 (CNN)

    • 이미지의 특성을 뽑아내는 과정
    • 합성곱에서는 뉴런이 입력층 위를 이동하면서 출력을 만든다. = 도장을 연상하라
    • 합성곱 층의 뉴런에 있는 가중치 개수는 하이퍼 파라미터이다.
      • 발전사 : alexnet -> resnet -> efficientnet
      • 채널, 이미지의 너비, 크기 (파라미터 튜닝)
      • Vision Transformer
    • 비디오
      • 객체인식(Object Detection)
      • Yolo 논문
  • RNN / LSTM (자연어 처리)

    • 구글 2017년 Transformer (논문)
  • 필터 (filter)

    • 합성곱에서의 뉴런
      • 뉴런 개수를 이야기할 때 필터라 칭한다.
      • 합성곱에서는 완전 연결 신경망과 달리 뉴런을 필터라 부른다.
    • 혹은 커널(kernel)이라 부른다.
      • 입력에 곱해지는 가중치를의미할 때 커널이라 부른다.

합성곱의 장점

  • 기존 : 1차원 배열에서만 연산이 가능
  • 2차원 배열에도 연산을 할 수 있도록 구현
    • 입력이 2차원 배열이 되므로 필터도 2차원이다.
    • 선형대수를 공부해야 하나요??
1
2
3
4
from tensorflow import keras
keras.layers.Conv2D(10, # 필터(즉, 도장)의 개수
kernel_size=(3,3), # 필터에 사용할 커널의 크기
activation = 'relu') # 활성화 함수 지정
<keras.layers.convolutional.Conv2D at 0x7f6c99df5c90>

패딩 (padding)

  • 입력 배열의 주위를 가상의 원소로 채우는 것.
  • 실제 입력값이 아니기 때문에 패딩은 0으로 채운다.
  • 실제 값은 0으로 채워져 있기에 계산에 영향을 미치지 않는다.
    • 세임 패딩 (same padding) : 입력 주위에 0으로 패딩 하는 것
    • 밸리드 패딩 (valid padding) : 패딩 없이 순수한 입력 배열에서만 합성곱하여 특성 맵을 마드는 것

패딩의 목적

  • 배열의 크기를 조정하더라도 이미지 원 특성이 손실되는 것을 방지하는 것

스트라이드 (stride)

  • 기존에 합성곱 연산은 좌우, 위아래로 한 칸씩 이동했다.
    • 두 칸씩 건너뛸 수도 있다.
    • 이런 이동의 크기를 ‘스트라이드’라고 한다.
  • 두 칸씩 이동하면 특성 맵의 크기가 더 작아진다.
    • 커널 도장을 찍는 횟수가 줄어들기 때문.
  • 디폴트는 1칸 이동이다.
1
2
3
4
5
keras.layers.Conv2D(10,                         # 필터(즉, 도장)의 개수
kernel_size=(3,3), # 필터에 사용할 커널의 크기
activation='relu', # 활성화 함수 지정
padding = 'same', # 세임 패딩
strides = 1) # 1칸씩 이동
<keras.layers.convolutional.Conv2D at 0x7f6c992ba8d0>

풀링 (pooling)

  • 값을 추출
  • 100 x 100 이미지 –> (수치로) 주요 이미지의 특성만 뽑은 후, 원 이미지와 같게 만듬 (50 x 50)
  • 합성곱 층에서 만든 특성 맵의 가로세로 크기를 줄이는 역할을 수행한다.
    • 특성맵의 크기를 줄이지는 않는다.
  • 합성곱처럼 입력 위를 지나가면서 도장을 찍는다.
    • 하지만, 풀링에는 가중치가 없다.
  • 최대 풀링 (max pooling)
    • 도장을 찍은 영역에서 가장 큰 값을 고른다.
  • 평균 풀링 (average pooling)
    • 도장을 찍은 영역에서 평균값을 계산한다.
  • 특성 맵이 여러 개라면 동일한 작업을 반복한다.
    • 즉, 10개의 특성 맵이 있다면 풀링을 거친 특성맵도 10개가 된다.
  • 풀링 영역은 풀링의 크기만큼 이동한다.
    • 즉, 겹치지 않고 이동한다.
    • 풀링의 크기가 (2,2)이면 가로세로 두 칸씩 이동한다.
    • 풀링은 가중치가 없고 풀링 크기와 스트라이드가 같다.
1
2
3
keras.layers.MaxPooling2D(2,                 # 풀링의 크기. 대부분은 2로 둔다.
strides=2, # 2칸씩 이동. 풀링의 크기와 같게 설정된다.
padding='valid') # 풀링은 패딩을 하지 않으므로 'valid'로 지정.
<keras.layers.pooling.MaxPooling2D at 0x7f6c99253e90>
  • 기억할 점
    • 풀링은 가로세로 방향으로만 진행한다.
    • 특성 맵의 개수는 변하지 않고 그대로이다.

합성곱 신경망의 전체 구조

  • p437
  • 1단계 : 이미지 데이터 입력
  • 2단계 : 합성곱 층
    • (1) kernel_size + padding
    • (2) 활성화 함수 적용
    • (3) 각각의 특성맵을 산출
  • 3단계 : 풀링층
    • (1) Max Pooling : 최댓값 추출
    • (2) 최종 특성맵
  • 위 과정을 계속 반복하는 것이 CNN 알고리즘
  • 4단계 : 밀집층 (Fully Connected Layer)
    • Chapter 7장
    • 3차원 배열을 1차원으로 펼친다. (Flatten 클래스)
    • 출력층의 입력이 된다.
  • 5단계 : 분류 예측값을 산출 (Softmax 활성화 함수)
    • 지정한 활성화 함수를 거쳐 최종 예측 확률이 된다.

주요 키워드 : 사전학습(Pretrained) / 전이학습 (Transfer Learning) / 파인 튜닝(Fine Tuning)

  • 다른 사람이 작성한 학습 코드를 사용한다.

  • 파인 튜닝 : 미세 조정하는 것이다.

    • 캐글 경진대회에서 주로 사용.
  • Reference : 혼자 공부하는 머신러닝 + 딥러닝

chapter_8_1_2

08-2. 합성곱 신경망을 이용한 이미지 분류

패션 MNIST 데이터 불러오기

  • 데이터 스케일을 0 ~ 255 사이 0 ~ 1 로 표준화
  • 훈련 데이터 / 검증 데이터 분류
  • 완전 연결 신경망 (Fully Connected Layer)

–> 2차원 배열 -> 1차원 배열 (최종 분류값 도출)
–> 완전 연결 신경망과 달리, 합성곱에서는 2차원 이미지를 그대로 사용한다.

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

합성곱 신경망 만들기

  • 446p
  • 437p 그림을 코드로 구현하는 내용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
model = keras.Sequential()
# 합성곱 층
model.add(keras.layers.Conv2D(32, kernel_size=3, activation = 'relu', # Conv2D() 는 합성곱 층을 만든다.
padding = 'same', input_shape = (28, 28, 1))) # 합성곱의 필터 32이므로 특성 맵의 깊이는 32

# 풀링층
model.add(keras.layers.MaxPooling2D(2)) # (2,2) 풀링을 적용하여 합성곱 층의 특성 맵의 크기가 절반이 된다.

# 합성곱 층
model.add(keras.layers.Conv2D(64, kernel_size=(3,3), activation = 'relu', # 합성곱의 필터 64이므로 특성 맵의 깊이는 64
padding = 'same'))

# 풀링층
model.add(keras.layers.MaxPooling2D(2)) # (2,2) 풀링을 적용하여 합성곱 층의 특성 맵의 크기가 절반이 된다.

# 완전연결층 (밀집층 = Fully Connected Layer)
# Chapter 7장 내용
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')) # 출력측

model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                /images/chapter_8_1_2/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로 늘어난다.
  • 반면 특성 맵의 가로세로 크기는
    • 첫 번째 풀링 층에서 절반으로 줄어든다.
    • 두 번째 풀링층에서 다시 절반으로 더 줄어든다.
  • Flatten 클래스에서 (7,7,64) 크기의 특성 맵을 1차원 배열로 펼친다.
    • (7,7,64) -> (3136,)

모델 파라미터 개수 계산

  • 첫 번째 합성곱 층

    • 32개 필터, 커널 크기(3,3), 깊이1, 필터마다 하나의 절편 -> 3x3x1x32 + 32 = 320개
  • 두 번째 합성곱 층

    • 64개 필터, 커널 크기(3,3), 깊이32, 필터마다 하나의 절편 -> 3x3x32x64 + 64 = 18,496개
  • Flatten 즉, 은닉층

    • (3136,) 개의 1차원 배열, 100개의 뉴런 -> 3136x100 + 100 = 313,700개
  • 텐서플로 : https://www.tensorflow.org/hub

  • 필요한 것을 찾아서 가져다 사용할 수 있다.

  • 층의 구성을 그림으로 표현해 본다.

  • keras.uitls 패키지의 plot_model() 함수 사용

1
keras.utils.plot_model(model)

png

  • 박스 안에서

    • 왼쪽 : 층의 이름
    • 오른쪽 : 클래스
  • inputLayer 클래스

    • 케라스가 자동으로 추가해주는 입력층의 역할.
    • Conv2D 클래스의 input_shape 매개변수를 사용.
  • 층의 구성을 그림으로 표현해 본다.

  • keras.uitls 패키지의 plot_model() 함수 사용

  • show_shapes 매개변수를 True로 설정하면 그림에 입력과 출력의 크기를 표시한다.

1
keras.utils.plot_model(model, show_shapes = True)

png

  • 지금까지 한 것은 모델 정의
  • 모델 컴파일 후, 훈련
    • 7장 내용
    • Adam 옵티마이저를 사용
    • 조기 종료 기법을 구현 : ModelCheckpoint 콜백과 EarlyStopping 콜백을 함께 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
import tensorflow as tf
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)

with tf.device('/device:GPU:0'): # GPU 잡는 법
history = model.fit(train_scaled, train_target, epochs=10,
validation_data=(val_scaled, val_target),
callbacks=[checkpoint_cb, early_stopping_cb])
Epoch 1/10
1500/1500 [==============================] - 59s 39ms/step - loss: 0.4968 - accuracy: 0.8231 - val_loss: 0.3245 - val_accuracy: 0.8799
Epoch 2/10
1500/1500 [==============================] - 55s 37ms/step - loss: 0.3304 - accuracy: 0.8809 - val_loss: 0.2726 - val_accuracy: 0.8967
Epoch 3/10
1500/1500 [==============================] - 55s 37ms/step - loss: 0.2833 - accuracy: 0.8987 - val_loss: 0.2461 - val_accuracy: 0.9072
Epoch 4/10
1500/1500 [==============================] - 55s 37ms/step - loss: 0.2534 - accuracy: 0.9069 - val_loss: 0.2360 - val_accuracy: 0.9119
Epoch 5/10
1500/1500 [==============================] - 55s 37ms/step - loss: 0.2311 - accuracy: 0.9165 - val_loss: 0.2258 - val_accuracy: 0.9170
Epoch 6/10
1500/1500 [==============================] - 55s 37ms/step - loss: 0.2104 - accuracy: 0.9224 - val_loss: 0.2346 - val_accuracy: 0.9157
Epoch 7/10
1500/1500 [==============================] - 55s 37ms/step - loss: 0.1916 - accuracy: 0.9275 - val_loss: 0.2132 - val_accuracy: 0.9234
Epoch 8/10
1500/1500 [==============================] - 55s 37ms/step - loss: 0.1757 - accuracy: 0.9343 - val_loss: 0.2152 - val_accuracy: 0.9220
Epoch 9/10
1500/1500 [==============================] - 56s 37ms/step - loss: 0.1619 - accuracy: 0.9393 - val_loss: 0.2172 - val_accuracy: 0.9247
  • 훈련 세트의 정확도가 이전에 비해 증가했다.
  • 손실 그래프를 그린다.
    • 조기 종료가 잘 이루어졌는지 확인하자.
1
2
3
4
5
6
7
import matplotlib.pyplot as plt
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.xlabel('loss')
plt.legend(['train', 'val'])
plt.show()

png

  • 그래프를 기반으로 9번째 에포크를 최적으로 생각할 수 잇다.
  • 세트에 대한 성능을 평가해본다.
1
model.evaluate(val_scaled, val_target)
375/375 [==============================] - 5s 14ms/step - loss: 0.2132 - accuracy: 0.9234





[0.21322399377822876, 0.9234166741371155]
  • 좌측 파일 선택 -> best-cnn-model.h5 다운로드

08-3. 합성곱 신경망 시각화

  • 교재 465p
  • 사전 학습 = 이전에 만든 모델이 어떤 가중치를 학습했는지 확인하기 위해 체크포인트 파일을 읽는다.
  • model.layers
    • 케라스 모델에 추가한 층을 출력한다.
1
2
3
4
5
6
7
from tensorflow import keras

# 사전학습 진행
model2 = keras.models.load_model("best-cnn-model.h5")

#keras.utils.plot_model(model2, show_shapes = True)
model.layers
[<keras.layers.convolutional.Conv2D at 0x7fe1487e19d0>,
 <keras.layers.pooling.MaxPooling2D at 0x7fe1d0495b50>,
 <keras.layers.convolutional.Conv2D at 0x7fe148c92590>,
 <keras.layers.pooling.MaxPooling2D at 0x7fe1487fa9d0>,
 <keras.layers.core.flatten.Flatten at 0x7fe1446bad10>,
 <keras.layers.core.dense.Dense at 0x7fe1446ba210>,
 <keras.layers.core.dropout.Dropout at 0x7fe14465cf50>,
 <keras.layers.core.dense.Dense at 0x7fe14465dcd0>]
  • 합성곱 층의 가중치를 확인 가능
  • 우선 첫 번째 합성곱 층의 가중치를 조사한다.
1
2
conv = model.layers[0]
print(conv.weights[0].shape, conv.weights[1].shape) # 가중치, 절편
(3, 3, 1, 32) (32,)
1
2
conv_weights = conv.weights[0].numpy()
print(conv_weights.mean(), conv_weights.std()) # 가중치 배열의 평균, 표쥰편차
-0.038952995 0.26509935
  • 이 가중치가 어떤 분표를 가졌는지 보기 쉽게 히스토그램으로 그린다.
1
2
3
4
plt.hist(conv_weights.reshape(-1, 1))
plt.xlabel('weight')
plt.ylabel('count')
plt.show()

png

  • 이 가중치가 어떤 의미인지 시각화 해보자.
  • 468p
  • 32개의 커널을 16개씩 2줄로 출력한다.
1
2
3
4
5
6
7
8
ig, axs = plt.subplots(2, 16, figsize=(15,2))

for i in range(2):
for j in range(16):
axs[i, j].imshow(conv_weights[:,:,0,i*16 + j], vmin=-0.5, vmax=0.5) # vmin, vmax는 맷플롯립의 컬러맵으로 표현할 범위를 지정
axs[i, j].axis('off')

plt.show()

png

  • 색이 밝은지 어두운지를 통해 가중치를 판단할 수 있다.

이번에는 훈련하지 않은 빈 합성곱 신경망을 만든다.

  • 먼저 Conv2D 층을 하나 추가한다.
1
2
3
4
no_training_model = keras.Sequential()

no_training_model.add(keras.layers.Conv2D(32, kernel_size=3, activation='relu',
padding='same', input_shape=(28,28,1)))
  • 첫 번째 Conv2D층의 가중치를 no_training_conv 변수에 저장한다.
1
2
3
no_training_conv = no_training_model.layers[0]

print(no_training_conv.weights[0].shape)
(3, 3, 1, 32)
  • 가중치의 평균과 표준편차를 확인한다.
1
2
no_training_weights = no_training_conv.weights[0].numpy()
print(no_training_weights.mean(), no_training_weights.std())
0.011464282 0.08503365
  • 이 가중치 배열을 히스토그램으로 표현한다.
1
2
3
4
plt.hist(no_training_weights.reshape(-1, 1))
plt.xlabel('weight')
plt.ylabel('count')
plt.show()

png

  • 그래프가 이전과 확실히 다르다.
  • 이 가중치 값을 맷플롯립의 imshow() 함수를 사용해 이전처럼 그림으로 출력한다.
1
2
3
4
5
6
7
8
ig, axs = plt.subplots(2, 16, figsize=(15,2))

for i in range(2):
for j in range(16):
axs[i, j].imshow(no_training_weights[:,:,0,i*16 + j], vmin=-0.5, vmax=0.5) # vmin, vmax는 맷플롯립의 컬러맵으로 표현할 범위를 지정
axs[i, j].axis('off')

plt.show()

png

  • 전체적으로 가중치가 밋밋하게 초기화되었다.
  • 이 그림을 훈련이 끝난 이전 가중치와 비교해보자.
  • 합성곱 신경망이 패현MNIST 데이터셋의 부류 정확도를 높이기 위해 유용한 패턴을 학습했다는 사실을 눈치챌 수 있다.

함수형 API

  • 474p
  • 특성 맵 시각화
    • 케라스로 패현 MNIST 데이터셋을 읽은 후 훈련 세트에 있는 첫 번째 샘플을 그려본다.
1
2
3
4
5
print(model.input)
conv_acti = keras.Model(model.input, model.layers[0]./images/chapter_8_1_2/output)
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
plt.imshow(train_input[0], cmap='gray_r')
plt.show()
KerasTensor(type_spec=TensorSpec(shape=(None, 28, 28, 1), dtype=tf.float32, name='conv2d_input'), name='conv2d_input', description="created by layer 'conv2d_input'")

png

  • 앵클 부트다.
  • 이 샘플을 conv_acti 모델에 주입하여 Conv2D 층이 만드는 특성 맵을 출력한다.
  • 08-2장에서 했던 것처럼 전처리를 진행한다.
  • feature_maps의 크기를 확인한다.
1
2
3
inputs = train_input[0:1].reshape(-1, 28, 28, 1)/255.0
feature_maps = conv_acti.predict(inputs)
print(feature_maps.shape)
(1, 28, 28, 32)
  • same 패딩과 32개의 필터를 사용한 합성곱 층의 출력이므로 (28,28,32)이다.
  • 샘플을 하나 입력했기에 1이다.
  • 앞에서와 같이 맷플롯립의 imshow함수로 특성 맵을 그린다.
    • 32개의 특성 맵을 4개의 행으로 나누어 그린다.
1
2
3
4
5
6
7
8
fig, axs = plt.subplots(4, 8, figsize=(15,8))

for i in range(4):
for j in range(8):
axs[i, j].imshow(feature_maps[0,:,:,i*8 + j])
axs[i, j].axis('off')

plt.show()

png

  • 두 번째 합성곱 층이 많든 특성 맵도 같은 방식으로 확인할 수 있다.
  • 먼저 model 객체의 입력과 두 번째 합성곱 층인 model.layers[2]의 출력을 연결한 conv2_acti 모델을 만든다.
  • 그 다음 첫 샘플을 conv2_acti 모델의 predict() 메서드에 전달한다.
  • 첫 번째 풀링 층에서 가로세로 크기가 줄반으로 줄고, 두 번째 합성곱 층의 필터 개수는 64개이므로 (14,14,64) 가 된다.
  • 64개의 특성 맵을 8개씩 나누어 imshow()함수로 그린다.
1
2
3
4
5
6
7
8
9
10
11
12
conv2_acti = keras.Model(model.input, model.layers[2]./images/chapter_8_1_2/output)
feature_maps = conv2_acti.predict(train_input[0:1].reshape(-1, 28, 28, 1)/255.0)
print(feature_maps.shape)

fig, axs = plt.subplots(8, 8, figsize=(12,12))

for i in range(8):
for j in range(8):
axs[i, j].imshow(feature_maps[0,:,:,i*8 + j])
axs[i, j].axis('off')

plt.show()
(1, 14, 14, 64)

png

  • 이번 특성 맵은 시각적으로 이해하기 어렵다.

  • 두 번째 합성곱 층의 필터 크기는 (3,3,32)인데 (14,14,32)인 특성 맵에서 어떤 부위를 감지하는지 직관적으로 이해하기 어렵다.

    • 478p 그림 참고
  • 이런 현상은 합성곱 층을 많이 쌓을수록 심해진다.

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

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 : 혼자 공부하는 머신러닝 + 딥러닝

chapter_6_2

k-평균

  • 각각의 픽셀값 (3차원 -> 1차원 배열) 평균 구함

    • 픽셀의 평균값은 활용해서 사과, 바나나, 파인애플에 근사한 이미지를 추출한 것
  • 어떻게 평균값을 구할 수 있을까?

    • k-평균 알고리즘 (k-Means) 알고리즘
    • 평균값 = Cluster Center = Centroid

데이터 불러오기

다음을 참고하라 : http://bit.ly/hg-06-2

1
!wget https://bit.ly/fruits_300_data -O fruits_300.npy
--2022-03-31 02:17:17--  https://bit.ly/fruits_300_data
Resolving bit.ly (bit.ly)... 67.199.248.11, 67.199.248.10
Connecting to bit.ly (bit.ly)|67.199.248.11|:443... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://github.com/rickiepark/hg-mldl/raw/master/fruits_300.npy [following]
--2022-03-31 02:17:17--  https://github.com/rickiepark/hg-mldl/raw/master/fruits_300.npy
Resolving github.com (github.com)... 192.30.255.112
Connecting to github.com (github.com)|192.30.255.112|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/rickiepark/hg-mldl/master/fruits_300.npy [following]
--2022-03-31 02:17:17--  https://raw.githubusercontent.com/rickiepark/hg-mldl/master/fruits_300.npy
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.109.133, 185.199.108.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3000128 (2.9M) [application/octet-stream]
Saving to: ‘fruits_300.npy’

fruits_300.npy      100%[===================>]   2.86M  --.-KB/s    in 0.05s   

2022-03-31 02:17:17 (56.9 MB/s) - ‘fruits_300.npy’ saved [3000128/3000128]
  • 넘파이 파일을 불러옴
1
2
3
4
5
6
import numpy as np
import matplotlib.pyplot as plt

fruits = np.load('fruits_300.npy')
print(fruits.shape)
print(fruits.ndim)
(300, 100, 100)
3
  • 3차원 (샘플개수, 너비, 높이)
  • 2차원 (샘플개수, 너비 x 높이)
1
2
fruits_2d = fruits.reshape(-1, 100*100)
fruits_2d.shape
(300, 10000)
  • k-평균 알고리즘 활용
1
2
3
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state = 42)
km.fit(fruits_2d)
KMeans(n_clusters=3, random_state=42)
  • 모형학습 후, labels
1
print(km.labels_)
[2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 0 2 0 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 0 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 1 1 1 1 1 0 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 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 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]
  • 직접 샘플의 개수 확인
1
print(np.unique(km.labels_, return_counts=True))
(array([0, 1, 2], dtype=int32), array([111,  98,  91]))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import matplotlib.pyplot as plt

def draw_fruits(arr, ratio=1):
n = len(arr) # n은 샘플 개수입니다
# 한 줄에 10개씩 이미지를 그립니다. 샘플 개수를 10으로 나누어 전체 행 개수를 계산합니다.
rows = int(np.ceil(n/10))
# 행이 1개 이면 열 개수는 샘플 개수입니다. 그렇지 않으면 10개입니다.
cols = n if rows < 2 else 10
fig, axs = plt.subplots(rows, cols,
figsize=(cols*ratio, rows*ratio), squeeze=False)
for i in range(rows):
for j in range(cols):
if i*10 + j < n: # n 개까지만 그립니다.
axs[i, j].imshow(arr[i*10 + j], cmap='gray_r')
axs[i, j].axis('off')
plt.show()
1
draw_fruits(fruits[km.labels_==0])

png

클러스터 중심

1
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)

png

1
print(km.transform(fruits_2d[100:101]))
[[3393.8136117  8837.37750892 5267.70439881]]
1
print(km.predict(fruits_2d[100:101]))
[0]
1
draw_fruits(fruits[100:101])

png

최적의 k-평균 찾기

1
2
3
4
5
6
7
inertia = []
for k in range(2, 7):
km = KMeans(n_clusters = k, random_state=42)
km.fit(fruits_2d)
inertia.append(km.inertia_)
plt.plot(range(2, 7), inertia)
plt.show()

png

  • 위 결과 최적의 k-평균은 3.0 정도 된다.

  • chapter6. 비지도학습은 잘 안 쓰인다. 시각화 문법만 유의해서 살펴보자.

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