● 과대적합, 과소적합을 막기 위한 방법들

  • 모델의 크기 축소
  • 초기화
  • 옵티마이저
  • 배치 정규화
  • 규제화

 

1. 모델의 크기 축소

  • 가장 단순한 방법
  • 모델의 크기를 줄인다는 것은 학습 파라미터의 수를 줄이는 것
# 데이터 준비
from tensorflow.keras.datasets import imdb
import numpy as np

(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words = 10000)

def vectorize_seq(seqs, dim = 10000):
    results = np.zeros((len(seqs), dim))
    for i, seq in enumerate(seqs):
        results[i, seq] = 1.
    
    return results

x_train = vectorize_seq(train_data)
x_test = vectorize_seq(test_data)

y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
# 모델1
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

model_1 = Sequential([Dense(16, activation = 'relu', input_shape = (10000, ), name = 'input'),
                      Dense(16, activation = 'relu', name = 'hidden'),
                      Dense(1, activation = 'sigmoid', name = 'output')])
model_1.summary()

# 출력 결과
Model: "sequential_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input (Dense)               (None, 16)                160016    
                                                                 
 hidden (Dense)              (None, 16)                272       
                                                                 
 output (Dense)              (None, 1)                 17        
                                                                 
=================================================================
Total params: 160,305
Trainable params: 160,305
Non-trainable params: 0
_________________________________________________________________
# 모델2
model_2 = Sequential([Dense(7, activation = 'relu', input_shape = (10000, ), name = 'input2'),
                      Dense(7, activation = 'relu', name = 'hidden2'),
                      Dense(1, activation = 'sigmoid', name = 'output2')])
model_2.summary()

# 출력 결과
odel: "sequential_3"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input2 (Dense)              (None, 7)                 70007     
                                                                 
 hidden2 (Dense)             (None, 7)                 56        
                                                                 
 output2 (Dense)             (None, 1)                 8         
                                                                 
=================================================================
Total params: 70,071
Trainable params: 70,071
Non-trainable params: 0
_________________________________________________________________
  • 모델1과 모델2 차이점은 모델의 크기 차이
# 모델 학습
model_1.compile(optimizer = 'rmsprop',
                loss = 'binary_crossentropy',
                metrics = ['acc'])
model_2.compile(optimizer = 'rmsprop',
                loss = 'binary_crossentropy',
                metrics = ['acc'])

model_1_hist = model_1.fit(x_train, y_train,
                           epochs = 20,
                           batch_size = 512,
                           validation_data = (x_test, y_test))
model_2_hist = model_2.fit(x_train, y_train,
                           epochs = 20,
                           batch_size = 512,
                           validation_data = (x_test, y_test))
# 비교
epochs = range(1, 21)
model_1_val_loss = model_1_hist.history['val_loss']
model_2_val_loss = model_2_hist.history['val_loss']

import matplotlib.pyplot as plt

plt.plot(epochs, model_1_val_loss, 'r+', label = 'Model_1')
plt.plot(epochs, model_2_val_loss, 'bo', label = 'Model_2')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.grid()
plt.show()

  • model_2(더 작은 모델)이 조금 더 나중에 과대적합 발생

 

 

2. 모델의 크기 축소(2)

# 모델 구성
model_3 = Sequential([Dense(1024, activation = 'relu', input_shape = (10000, ), name = 'input3'),
                      Dense(1024, activation = 'relu', name = 'hidden3'),
                      Dense(1, activation = 'sigmoid', name = 'output3')])

model_3.compile(optimizer = 'rmsprop',
                loss = 'binary_crossentropy',
                metrics = ['acc'])

model_3.summary()

# 출력 결과
Model: "sequential_5"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input3 (Dense)              (None, 1024)              10241024  
                                                                 
 hidden3 (Dense)             (None, 1024)              1049600   
                                                                 
 output3 (Dense)             (None, 1)                 1025      
                                                                 
=================================================================
Total params: 11,291,649
Trainable params: 11,291,649
Non-trainable params: 0
_________________________________________________________________
# 모델 학습
model_3_hist = model_3.fit(x_train, y_train,
                           epochs = 20,
                           batch_size = 512,
                           validation_data = (x_test, y_test))
# 시각화
model_3_val_loss = model_3_hist.history['val_loss']

plt.plot(epochs, model_1_val_loss, 'r+', label = 'Model_1')
plt.plot(epochs, model_2_val_loss, 'r+', label = 'Model_2')
plt.plot(epochs, model_3_val_loss, 'r+', label = 'Model_3')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.grid()
plt.show()

  • 볼륨이 큰 신경망일수록 빠르게 훈련데이터 모델링 가능(학습 손실이 낮아짐)
  • 과대적합에는 더욱 민감해짐
  • 이는 학습-검증 데이터의 손실을 보면 알 수 있음
# 학습 데이터의 loss 값도 비교
model_1_train_loss = model_1_hist.history['loss']
model_2_train_loss = model_2_hist.history['loss']
model_3_train_loss = model_3_hist.history['loss']

plt.plot(epochs, model_1_train_loss, 'r+', label = 'Model_1')
plt.plot(epochs, model_2_train_loss, 'r+', label = 'Model_2')
plt.plot(epochs, model_3_train_loss, 'r+', label = 'Model_3')
plt.xlabel('Epochs')
plt.ylabel('Training Loss')
plt.legend()
plt.grid()
plt.show()

 

 

3. 가중치 초기화

  - 초기화 전략

  • Glorot Initialization(Xavier)
    • 활성화 함수
      • 없음
      • tanh
      • sigmoid
      • softmax
  • He Initialization
    • 활성화 함수
      • ReLU
      • LeakyReLU
      • ELU 등
from tensorflow.keras.layers import Dense, LeakyReLU, Activation
from tensorflow.keras.models import Sequential

model = Sequential([Dense(30, kernel_initializer = 'he_normal', input_shape = [10, 10]),
                    LeakyReLU(alpha = 0.2),
                    Dense(1, kernel_initializer = 'he_normal'),
                    Activation('softmax')])
model.summary()

# 출력 결과
Model: "sequential_6"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense (Dense)               (None, 10, 30)            330       
                                                                 
 leaky_re_lu (LeakyReLU)     (None, 10, 30)            0         
                                                                 
 dense_1 (Dense)             (None, 10, 1)             31        
                                                                 
 activation (Activation)     (None, 10, 1)             0         
                                                                 
=================================================================
Total params: 361
Trainable params: 361
Non-trainable params: 0
_________________________________________________________________

 

 

4. 고속 옵티마이저

  - 모멘텀 최적화

$$ v \leftarrow \alpha v - \gamma \frac{\partial L}{\partial W} $$

$$ W \leftarrow W + v $$

  • \(\alpha\): 관성계수
  • \(v\): 속도
  • \(\gamma\): 학습률
  • \(\frac{\partial L}{\partial W}\): 손실함수에 대한 미분
import tensorflow as tf
from tensorflow.keras.optimizers import SGD

# momentum 값이 관성계수(알파값)
optimizer = SGD(learning_rate = 0.001, momentum = 0.9)

 

  - 네스테로프(Nesterov)

  • 모멘텀의 방향으로 조금 앞선 곳에서 손실함수의 미분을 구함
  • 시간이 지날수록 조금 더 빨리 최솟값에 도달
    \(m \leftarrow \beta m - \eta \bigtriangledown_{\theta}J(\theta + \beta m)\)
    \(\theta \leftarrow \theta + m\)
  • \(h\): 기존의 기울기를 제곱하여 더한 값
  • \(\eta\): 학습률
  • \(\bigtriangledown_{\theta}J(\theta)\): \(\theta\)에 대한 미분(그라디언트)

http://cs231n.stanford.edu/slides/2019/cs231n_2019_lecture08.pdf

optimizer = SGD(learning_rate = 0.001, momentum = 0.9, nesterov = True)

 

  - AdaGrad

  • 보통 간단한 모델에는 효과 좋을 수는 있으나, 심층 신경망 모델에서는 사용 X(사용하지 않는 것이 좋은 것으로 밝혀짐)
    \(h \leftarrow h+\frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W}\)
    \(W \leftarrow W+\gamma \frac{1}{\sqrt{h}} \frac{\partial L}{\partial W}\)
  • \(h\): 기존의 기울기를제곱하여 더한 값
  • \(\gamma\): 학습률
  • \(\frac{\partial L}{\partial W}\): \(W\)에 대한 미분
from tensorflow.keras.optimizers import Adagrad

optimizer = Adagrad(learning_rate = 0.001)

 

  - RMSprop

$$ s \leftarrow \beta s+(1-\beta)\bigtriangledown_{\theta}J(\theta) \otimes \bigtriangledown_{\theta}J(\theta) $$

$$ \theta \leftarrow \theta - \eta \bigtriangledown_{\theta}J(\theta)\oslash \sqrt{s+\epsilon} $$

  • \(s\): 그라디언트의 제곱을 감쇠율을 곱한 후 더함
  • \(\eta\): 학습률
  • \(\bigtriangledown_{\theta}J(\theta)\): 손실함수의 미분값
from tensorflow.keras.optimizers import RMSprop

optimizer = RMSprop(learning_rate = 0.001, rho = 0.9)

 

  - Adam

$$ m \leftarrow \beta_{1}m-(1-\beta_{1})\frac{\partial L}{\partial W} $$

$$ s \leftarrow \beta_{2}s+(1-\beta_{2}\frac{\partial L}{\partial W}\odot\frac{\partial L}{\partial W} $$

$$ \hat{m} \leftarrow \frac{m}{1-\beta^{t}_{1}} $$

$$ \hat{s} \leftarrow \frac{s}{1-\beta^{t}_{2}} $$

$$ W \leftarrow W+\gamma \hat{m} \oslash \sqrt{\hat{s}+\epsilon} $$

  • \(\beta\): 지수 평균의 업데이트 계수
  • \(\gamma\): 학습률
  • \(\beta_{1} \approx 0.9, \beta_{2} \approx 0.999 \)
  • \( \frac{\partial L}{\partial W} \): \(W\)에 대한 미분
from tensorflow.keras.optimizers import Adam

# beta_1과 beta_2에 지정한 값은 디폴트 값으로, 어느정도 가장 좋은 값이라고 증명된 값
optimizer = Adam(learning_rate = 0.001, beta_1 = 0.9, beta_2 = 0.999)

 

 

5. 배치 정규화

  • 모델에 주입되는 샘플들을 균일하게 만드는 방법
  • 학습 후 새로운 데이터에 잘 일반화 할 수 있도록 도와줌
  • 데이터 전처리 단계에서 진행해도 되지만 정규화가 되어서 layer에 들어갔다는 보장이 없음
  • 주로 Dense 또는 Conv2D Layer 후, 활성화 함수 이전에 놓임
from tensorflow.keras.layers import BatchNormalization, Dense, Activation
from tensorflow.keras.utils import plot_model

model = Sequential()
model.add(Dense(32, input_shape = (28 * 28, ), kernel_initializer = 'he_normal'))
model.add(BatchNormalization())
model.add(Activation('relu'))

model.summary()
plot_model(model, show_shapes = True)

# 출력 결과
Model: "sequential_9"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_4 (Dense)             (None, 32)                25120     
                                                                 
 batch_normalization_1 (Batc  (None, 32)               128       
 hNormalization)                                                 
                                                                 
 activation_2 (Activation)   (None, 32)                0         
                                                                 
=================================================================
Total params: 25,248
Trainable params: 25,184
Non-trainable params: 64
_________________________________________________________________

 

 

6. 규제화

  • 복잡한 네트워크 일수록 네트워크 복잡도에 제한을 두어 가중치가 작은 값을 가지도록 함
  • 가중치의 분포가 더 균일하게 됨
  • 네트워크 손실함수에 큰 가중치에 연관된 비용을 추가
    • L1 규제: 가중치의 절댓값에 비례하는 비용이 추가
    • L2 규제: 가중치의 제곱에 비례한느 비용이 추가(흔히 가중치 감쇠라고도 불림)
    • 위의 두 규제가 합쳐진 경우도 존재
# l2 모델 구성
from tensorflow.keras.regularizers import l1, l2, l1_l2

l2_model = Sequential([Dense(16, kernel_regularizer = l2(0.001), activation = 'relu', input_shape = (10000, )),
                       Dense(16, kernel_regularizer = l2(0.001), activation = 'relu'),
                       Dense(1, activation = 'sigmoid')])
l2_model.compile(optimizer = 'rmsprop',
                 loss = 'binary_crossentropy',
                 metrics = ['acc'])
l2_model.summary()
plot_model(l2_model, show_shapes = True)

# 출력 결과
Model: "sequential_10"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_5 (Dense)             (None, 16)                160016    
                                                                 
 dense_6 (Dense)             (None, 16)                272       
                                                                 
 dense_7 (Dense)             (None, 1)                 17        
                                                                 
=================================================================
Total params: 160,305
Trainable params: 160,305
Non-trainable params: 0
_________________________________________________________________

# l2 모델 학습
l2_model_hist = l2_model.fit(x_train, y_train,
                             epochs = 20,
                             batch_size = 512,
                             validation_data = (x_test, y_test))
# l2 모델 시각화
l2_model_val_loss = l2_model_hist.history['val_loss']

epochs = range(1, 21)
plt.plot(epochs, model_1_val_loss, 'r+', label = 'Model_1')
plt.plot(epochs, l2_model_val_loss, 'bo', label = 'Model_L2-regularized')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.grid()
plt.show()

 

# l1 모델 구성
l1_model = Sequential([Dense(16, kernel_regularizer = l1(0.001), activation = 'relu', input_shape = (10000, )),
                       Dense(16, kernel_regularizer = l1(0.001), activation = 'relu'),
                       Dense(1, activation = 'sigmoid')])
l1_model.compile(optimizer = 'rmsprop',
                 loss = 'binary_crossentropy',
                 metrics = ['acc'])
l1_model.summary()
plot_model(l1_model, show_shapes = True)


# 출력 결과
Model: "sequential_17"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_14 (Dense)            (None, 16)                160016    
                                                                 
 dense_15 (Dense)            (None, 16)                272       
                                                                 
 dense_16 (Dense)            (None, 1)                 17        
                                                                 
=================================================================
Total params: 160,305
Trainable params: 160,305
Non-trainable params: 0
_________________________________________________________________

# l1 모델 학습
l1_model_hist = l1_model.fit(x_train, y_train,
                             epochs = 20,
                             batch_size = 512,
                             validation_data = (x_test, y_test))
# l1 모델 시각화
l1_model_val_loss = l1_model_hist.history['val_loss']

epochs = range(1, 21)
plt.plot(epochs, model_1_val_loss, 'r+', label = 'Model_1')
plt.plot(epochs, l1_model_val_loss, 'bo', label = 'Model_L1-regularized')
plt.plot(epochs, l2_model_val_loss, 'g--', label = 'Model_L2-regularized')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.grid()
plt.show()

 

# l1_l2 모델 구성
l1_l2_model = Sequential([Dense(16, kernel_regularizer = l1_l2(l1 = 0.0001, l2 = 0.0001), activation = 'relu', input_shape = (10000, )),
                          Dense(16, kernel_regularizer = l1_l2(l1 = 0.0001, l2 = 0.0001), activation = 'relu'),
                          Dense(1, activation = 'sigmoid')])
l1_l2_model.compile(optimizer = 'rmsprop',
                    loss = 'binary_crossentropy',
                    metrics = ['acc'])
l1_l2_model.summary()
plot_model(l1_l2_model, show_shapes = True)

# 출력 결과
l1_l2_model = Sequential([Dense(16, kernel_regularizer = l1_l2(l1 = 0.0001, l2 = 0.0001), activation = 'relu', input_shape = (10000, )),
                          Dense(16, kernel_regularizer = l1_l2(l1 = 0.0001, l2 = 0.0001), activation = 'relu'),
                          Dense(1, activation = 'sigmoid')])
l1_l2_model.compile(optimizer = 'rmsprop',
                    loss = 'binary_crossentropy',
                    metrics = ['acc'])
l1_l2_model.summary()
plot_model(l1_l2_model, show_shapes = True)

 

# l1_l2 모델 학습
l1_l2_model_hist = l1_l2_model.fit(x_train, y_train,
                                   epochs = 20,
                                   batch_size = 512,
                                   validation_data = (x_test, y_test))
# l1_l2 모델 시각화
l1_l2_model_val_loss = l1_l2_model_hist.history['val_loss']

epochs = range(1, 21)
plt.plot(epochs, model_1_val_loss, 'r+', label = 'Model_1')
plt.plot(epochs, l1_l2_model_val_loss, 'ko', label = 'Model_L1_L2-regularized')
plt.plot(epochs, l1_model_val_loss, 'bo', label = 'Model_L1-regularized')
plt.plot(epochs, l2_model_val_loss, 'g--', label = 'Model_L2-regularized')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.grid()
plt.show()

 

 

7. 드롭아웃(Dropout)

  • 신경망을 위해 사용되는 규제 기법 중 가장 효과적이고 널리 사용되는 방법
  • 신경망의 레이어에 드롭아웃을 적용하면 훈련하는 동안 무작위로 층의 일부 특성(노드)를 제외
    • 예를 들어, 벡터 [1.0, 3.2, 0.6, 0.8, 1.1]에 대해 드롭아웃을 적용하면 무작위로 0으로 바뀜
      ([0, 3.2, 0.6, 0.8, 0]과 같이 바뀜)
    • 보통 0.2~0.5 사이의 비율로 지정됨
  • 테스트 단계에서는 그 어떤 노드도 드롭아웃 되지 않음
    • 대신 해당 레이어의 출력 노드를 드롭아웃 비율에 맞게 줄여줌
# 모델 구성
from tensorflow.keras.layers import Dropout

dropout_model = Sequential([Dense(16, activation = 'relu', input_shape = (10000, )),
                             Dropout(0.5),
                             Dense(16, activation = 'relu'),
                             Dropout(0.5),
                             Dense(1, activation = 'sigmoid')])
dropout_model.compile(optimizer = 'rmsprop',
                      loss = 'binary_crossentropy',
                      metrics = ['acc'])
dropout_model.summary()
plot_model(dropout_model, show_shapes = True)

# 출력 결과
Model: "sequential_19"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_20 (Dense)            (None, 16)                160016    
                                                                 
 dropout (Dropout)           (None, 16)                0         
                                                                 
 dense_21 (Dense)            (None, 16)                272       
                                                                 
 dropout_1 (Dropout)         (None, 16)                0         
                                                                 
 dense_22 (Dense)            (None, 1)                 17        
                                                                 
=================================================================
Total params: 160,305
Trainable params: 160,305
Non-trainable params: 0
_________________________________________________________________

# 모델 학습
dropout_model_hist = dropout_model.fit(x_train, y_train,
                                       epochs = 20,
                                       batch_size = 512,
                                       validation_data = (x_test, y_test))
# 시각화
dropout_model_val_loss = dropout_model_hist.history['val_loss']

epochs = range(1, 21)
plt.plot(epochs, model_1_val_loss, 'r+', label = 'Model_1')
plt.plot(epochs, dropout_model_val_loss, 'co', label = 'Model_Dropout')
plt.xlabel('Epochs')
plt.ylabel('Validation Loss')
plt.legend()
plt.grid()
plt.show()

https://www.tensorflow.org/tutorials/keras/classification?hl=ko

1. modules import

import tensorflow as tf
from tensorflow.keras.datasets.fashion_mnist import load_data
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras import models
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.utils import plot_model

from sklearn.model_selection import train_test_split

import numpy as np
import matplotlib.pyplot as plt

 

 

2. 데이터셋 로드

tf.random.set_seed(111)

(x_train_full, y_train_full), (x_test, y_test) = load_data()

x_train, x_val, y_train, y_val = train_test_split(x_train_full, y_train_full, test_size = 0.3, random_state = 111)

print("학습 데이터: {}\t레이블: {}".format(x_train_full.shape, y_train_full.shape))
print("학습 데이터: {}\t레이블: {}".format(x_train.shape, y_train.shape))
print("검증 데이터: {}\t레이블: {}".format(x_val.shape, y_val.shape))
print("테스트 데이터: {}\t레이블: {}".format(x_test.shape, y_test.shape))

# 출력 결과
학습 데이터: (60000, 28, 28)	레이블: (60000,)
학습 데이터: (42000, 28, 28)	레이블: (42000,)
검증 데이터: (18000, 28, 28)	레이블: (18000,)
테스트 데이터: (10000, 28, 28)	레이블: (10000,)

 

 

3. 데이터 확인

# 정답의 집합
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'bag', 'Ankle boot']

# 첫번째 데이터의 정답 확인
class_names[y_train[0]]

# 출력 결과
'Pullover'
plt.figure()
plt.imshow(x_train[0])
plt.colorbar()
plt.grid()
plt.show()

# 랜덤하게 4개의 데이터 추출하여 출력
num_sample = 4
random_idxs = np.random.randint(60000, size =num_sample)
plt.figure(figsize = (15, 10))
for i, idx in enumerate(random_idxs):
    image = x_train_full[idx, :]
    label = y_train_full[idx]

    plt.subplot(1, len(random_idxs), i+1)
    plt.imshow(image)
    plt.title("Index: {}, Label: {}".format(idx, class_names[label]))

 

 

4. 데이터 전처리

  • Normalization
  • flatten
  • oss = 'sparse_categorical_crossentropy
# Normalization
x_train = (x_train.reshape(-1, 28*28)) / 255.
x_val = (x_val.reshape(-1, 28*28)) / 255.
x_test = (x_test.reshape(-1, 28*28)) / 255.

 

 

5. 모델 구성(함수형 API)

input = Input(shape = (784, ), name = 'input')
hidden1 = Dense(256, activation = 'relu', name = 'hidden1')(input)
hidden2 = Dense(128, activation = 'relu', name = 'hidden2')(hidden1)
hidden3 = Dense(64, activation = 'relu', name = 'hidden3')(hidden2)
hidden4 = Dense(32, activation = 'relu', name = 'hidden4')(hidden3)
output = Dense(10, activation = 'softmax', name = 'output')(hidden4)
model = Model(inputs = [input], outputs = [output])
model.summary()

# 출력 결과
Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input (InputLayer)          [(None, 784)]             0         
                                                                 
 hidden1 (Dense)             (None, 256)               200960    
                                                                 
 hidden2 (Dense)             (None, 128)               32896     
                                                                 
 hidden3 (Dense)             (None, 64)                8256      
                                                                 
 hidden4 (Dense)             (None, 32)                2080      
                                                                 
 output (Dense)              (None, 10)                330       
                                                                 
=================================================================
Total params: 244,522
Trainable params: 244,522
Non-trainable params: 0
_________________________________________________________________
plot_model(model)

 

 

6. 모델 컴파일

model.compile(loss = 'sparse_categorical_crossentropy',
              optimizer = RMSprop(learning_rate = 0.01),
              metrics = ['acc'])

 

 

7. 모델 학습

  • 모델 시각화를 위해 history 변수에 학습과정 저장
history = model.fit(x_train, y_train,
                    epochs = 10,
                    batch_size = 128,
                    validation_data = (x_val, y_val))

# 출력 결과
Epoch 1/10
329/329 [==============================] - 16s 33ms/step - loss: 0.8969 - acc: 0.6897 - val_loss: 0.5580 - val_acc: 0.7997
Epoch 2/10
329/329 [==============================] - 6s 19ms/step - loss: 0.5179 - acc: 0.8132 - val_loss: 0.5554 - val_acc: 0.8124
Epoch 3/10
329/329 [==============================] - 5s 15ms/step - loss: 0.4643 - acc: 0.8321 - val_loss: 0.7202 - val_acc: 0.7992
Epoch 4/10
329/329 [==============================] - 5s 14ms/step - loss: 0.4484 - acc: 0.8414 - val_loss: 0.5157 - val_acc: 0.7810
Epoch 5/10
329/329 [==============================] - 5s 15ms/step - loss: 0.4242 - acc: 0.8497 - val_loss: 0.5527 - val_acc: 0.8212
Epoch 6/10
329/329 [==============================] - 5s 15ms/step - loss: 0.4175 - acc: 0.8523 - val_loss: 0.6034 - val_acc: 0.8197
Epoch 7/10
329/329 [==============================] - 5s 15ms/step - loss: 0.4107 - acc: 0.8566 - val_loss: 0.6612 - val_acc: 0.8046
Epoch 8/10
329/329 [==============================] - 5s 15ms/step - loss: 0.4029 - acc: 0.8594 - val_loss: 0.6940 - val_acc: 0.7671
Epoch 9/10
329/329 [==============================] - 5s 14ms/step - loss: 0.3955 - acc: 0.8603 - val_loss: 0.5032 - val_acc: 0.8444
Epoch 10/10
329/329 [==============================] - 5s 14ms/step - loss: 0.3969 - acc: 0.8653 - val_loss: 0.5266 - val_acc: 0.8257

 

 

8. 학습 결과 시각화

history_dict = history.history

loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss) + 1)
fig = plt.figure(figsize = (10, 5))

ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, loss, color = 'blue', label = 'train_loss')
ax1.plot(epochs, val_loss, color = 'red', label = 'val_loss')
ax1.set_title('Train and Validation Loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('Loss')
ax1.grid()
ax1.legend()

acc = history_dict['acc']
val_acc = history_dict['val_acc']

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, acc, color = 'blue', label = 'train_acc')
ax2.plot(epochs, val_acc, color = 'red', label = 'val_acc')
ax2.set_title('Train and Validation Accuracy')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Accuracy')
ax2.grid()
ax2.legend()

  • 검증데이터(val_loss, val_acc)가 일정하지 않고 튀는 현상 발생
  • 다른 옵티마이저로 실행
    • 데이터셋 로드 - 데이터 전처리 - 모델 구성 다시 진행
from tensorflow.keras.optimizers import SGD

model.compile(loss = 'sparse_categorical_crossentropy',
              optimizer = SGD(learning_rate = 0.01),
              metrics = ['acc'])

history2 = model.fit(x_train, y_train,
                     epochs = 10,
                     batch_size = 128,
                     validation_data = (x_val, y_val))

# 출력 결과
Epoch 1/10
329/329 [==============================] - 13s 32ms/step - loss: 0.3495 - acc: 0.8706 - val_loss: 0.3795 - val_acc: 0.8644
Epoch 2/10
329/329 [==============================] - 9s 27ms/step - loss: 0.3172 - acc: 0.8811 - val_loss: 0.3691 - val_acc: 0.8689
Epoch 3/10
329/329 [==============================] - 6s 19ms/step - loss: 0.3072 - acc: 0.8848 - val_loss: 0.3621 - val_acc: 0.8713
Epoch 4/10
329/329 [==============================] - 8s 25ms/step - loss: 0.3017 - acc: 0.8864 - val_loss: 0.3590 - val_acc: 0.8728
Epoch 5/10
329/329 [==============================] - 7s 23ms/step - loss: 0.2977 - acc: 0.8880 - val_loss: 0.3572 - val_acc: 0.8728
Epoch 6/10
329/329 [==============================] - 7s 21ms/step - loss: 0.2950 - acc: 0.8888 - val_loss: 0.3548 - val_acc: 0.8733
Epoch 7/10
329/329 [==============================] - 4s 12ms/step - loss: 0.2925 - acc: 0.8896 - val_loss: 0.3542 - val_acc: 0.8756
Epoch 8/10
329/329 [==============================] - 3s 11ms/step - loss: 0.2903 - acc: 0.8904 - val_loss: 0.3526 - val_acc: 0.8756
Epoch 9/10
329/329 [==============================] - 3s 10ms/step - loss: 0.2887 - acc: 0.8911 - val_loss: 0.3520 - val_acc: 0.8757
Epoch 10/10
329/329 [==============================] - 3s 11ms/step - loss: 0.2870 - acc: 0.8915 - val_loss: 0.3526 - val_acc: 0.8756
# 다시 시각화
history_dict = history2.history

loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss) + 1)
fig = plt.figure(figsize = (10, 5))

ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, loss, color = 'blue', label = 'train_loss')
ax1.plot(epochs, val_loss, color = 'red', label = 'val_loss')
ax1.set_title('Train and Validation Loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('Loss')
ax1.grid()
ax1.legend()

acc = history_dict['acc']
val_acc = history_dict['val_acc']

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, acc, color = 'blue', label = 'train_acc')
ax2.plot(epochs, val_acc, color = 'red', label = 'val_acc')
ax2.set_title('Train and Validation Accuracy')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Accuracy')
ax2.grid()
ax2.legend()

  • 학습 데이터의 loss값과 정확도가 검증 데이터의 loss값과 정확도와 차이가 있어보이지만 값으로 보면 큰 차이는 아님
  • loss값의 차이는 가장 큰 구간에서 0.06정도, 정확도는 가장 큰 구간이 0.025정

 

 

9. 모델 평가(1)

  • optimizer: SGD()로 학습한 모델
  • evaluate()
model.evaluate(x_test, y_test)

# 출력 결과
313/313 [==============================] - 2s 6ms/step - loss: 0.3862 - acc: 0.8661
[0.38618436455726624, 0.866100013256073]

 

 

10. 학습된 모델을 통해 값 예측

pred_ys = model.predict(x_test)

print(pred_ys.shape)
np.set_printoptions(precision = 7)
print(pred_ys[0])

# 출력 결과
# 정답 집합 10개 각각이 정답일 확률을 표시
(10000, 10)
[4.2854483e-21 1.0930411e-15 1.6151620e-17 3.9182383e-11 2.9266587e-15
 3.3629590e-03 4.9878759e-17 1.0700015e-03 2.2493745e-13 9.9556702e-01]
# 10개의 정답 집합 각각에 속할 확률 중 가장 높은 확률을 가진 값을 정답으로 채택하고 결과 확인
arg_pred_y = np.argmax(pred_ys, axis = 1)
plt.imshow(x_test[0].reshape(-1, 28))
plt.title('Predicted Class: {}'.format(class_names[arg_pred_y[0]]))
plt.show()

# 이미지 출력
def plot_image(i, pred_ys, y_test, img):
    pred_ys, y_test, img = pred_ys[i], y_test[i], img[i]
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])
    
    plt.imshow(img, cmap = plt.cm.binary)

    predicted_label = np.argmax(pred_ys)
    if predicted_label == y_test:
        color = 'blue'
    else:
        color = 'red'
    
    plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
                                         100 * np.max(pred_ys),
                                         class_names[y_test]),
                                         color = color)

# 전체 정답 집합 중 해당 데이터를 정답으로 예측한 확률 표시
def plot_value_array(i, pred_ys, true_label):
    pred_ys, true_label = pred_ys[i], true_label[i]
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])
    thisplot = plt.bar(range(10), pred_ys, color = '#777777')
    plt.ylim([0, 1])
    predicted_label = np.argmax(pred_ys)

    thisplot[predicted_label].set_color('red')
    thisplot[true_label].set_color('blue')
# 첫번째 데이터 정답 확인
i = 0
plt.figure(figsize = (8, 4))
plt.subplot(1, 2, 1)
plot_image(i, pred_ys, y_test, x_test.reshape(-1, 28, 28))
plt.subplot(1, 2, 2)
plot_value_array(i, pred_ys, y_test)
plt.show()

# 랜덤으로 추출하여 정답 확인
num_rows = 5
num_cols = 3
num_images = num_rows * num_cols

random_num = np.random.randint(10000, size = num_images)
plt.figure(figsize = (2 * 2 * num_cols, 2 * num_rows))
for idx, num in enumerate(random_num):
    plt.subplot(num_rows, 2 * num_cols, 2 * idx + 1)
    plot_image(num, pred_ys, y_test, x_test.reshape(-1, 28, 28))
    plt.subplot(num_rows, 2 * num_cols, 2 * idx + 2)
    plot_value_array(num, pred_ys, y_test)

plt.show()

 

 

11. 모델 평가(2)

  • optimizer: SGD()로 학습한 모델
  • 혼동 행렬(Confusion Matrix)
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
from tensorflow.keras.utils import to_categorical

y_test_che = to_categorical(y_test)
plt.figure(figsize = (8, 8))
cm2 = confusion_matrix(np.argmax(y_test_che, axis = 1), np.argmax(pred_ys, axis = -1))
sns.heatmap(cm2, annot = True, fmt = 'd', cmap = 'Blues')
plt.xlabel("Predicted Label")
plt.ylabel("True Label")

 

 

12. 모델 평가(3)

  • optimizer: SGD()로 학습한 모델
  • 분류 보고서
print(classification_report(np.argmax(y_test_che, axis = -1), np.argmax(pred_ys, axis = -1)))

# 출력 결과
              precision    recall  f1-score   support

           0       0.78      0.85      0.81      1000
           1       0.99      0.96      0.98      1000
           2       0.75      0.81      0.78      1000
           3       0.86      0.88      0.87      1000
           4       0.77      0.75      0.76      1000
           5       0.97      0.95      0.96      1000
           6       0.68      0.57      0.62      1000
           7       0.93      0.96      0.94      1000
           8       0.96      0.97      0.96      1000
           9       0.96      0.95      0.96      1000

    accuracy                           0.87     10000
   macro avg       0.86      0.87      0.86     10000
weighted avg       0.86      0.87      0.86     10000

1. modules import

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

import tensorflow as tf
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.models import Model
from tensorflow.keras.utils import get_file, plot_model

 

 

2. 데이터 로드

# 해당 주소의 데이터를 다운로드
dataset_path = get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")

# 열 이름 지정
column_names = ['MPG', 'Cylinders', 'Displacement', 'Horsepower', 'Weight','Acceleration','Model Year', 'Origin']

# 지정된 열이름을 사용하여 데이터를 판다스 데이터프레임 형식으로 로드
raw_dataset = pd.read_csv(dataset_path, names = column_names,
                          na_values = '?', comment = '\t',
                          sep = ' ', skipinitialspace = True)

 

 

3. 데이터 확인

# raw data바로 사용하지 않고 copy()하여 사용
dataset = raw_dataset.copy()
dataset

 

 

4. 데이터 전처리

  • 해당 데이터는 일부 데이터가 누락되어 있음
dataset.isna().sum()

# 출력 결과
MPG             0
Cylinders       0
Displacement    0
Horsepower      6
Weight          0
Acceleration    0
Model Year      0
Origin          0
dtype: int64
  • 누락된 행 삭제
# Horsepower에 6개의 결측값이 있으므로 결측값 제거
dataset = dataset.dropna()
  • 'Origin' 범주형 데이터
    • 원-핫 인코딩 진행
origin = dataset.pop('Origin')
dataset['USA'] = (origin == 1) * 1.0
dataset['Europe'] = (origin == 2) * 1.0
dataset['Japan'] = (origin == 3) * 1.0
dataset

 

 

4-1. 검증 데이터셋 생성

# train 데이터로 전체 데이터의 0.8을 추출
# 전체 데이터에서 train 데이터를 drop시킨 나머지를 test 데이터로 지정
train_dataset = dataset.sample(frac = 0.8, random_state = 0)
test_dataset = dataset.drop(train_dataset.index)

 

 

4-2. 데이터 조사

sns.pairplot(train_dataset[['MPG', 'Cylinders', 'Displacement', 'Horsepower', 'Weight']], diag_kind = 'kde')

# 데이터의 통계정보
train_stats = train_dataset.describe()
# MPG는 정답이기 때문에 통계 정보에서 제외
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats

 

 

4-3. 데이터의 특성과 레이블 분리

train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')

 

 

4-4. 데이터 정규화

# 통계정보에서 평균과 표준편차를 가져와서 각 데이터 값에서 평균을 빼고 표준편차로 나눠 정규화
def normalization(x):
    return (x - train_stats['mean']) / train_stats['std']

normed_train_data = normalization(train_dataset)
normed_test_data = normalization(test_dataset)

 

 

5. 모델 구성

def build_model():
    input = Input(shape = len(train_dataset.keys()), name = 'input')
    hidden1 = Dense(64,activation = 'relu', name = 'dense1')(input)
    hidden2 = Dense(64, activation = 'relu', name = 'dense2')(hidden1)
    output = Dense(1, name = 'output')(hidden2)

    model = Model(inputs = [input], outputs = [output])

    model.compile(loss = 'mse',
                  optimizer = RMSprop(0.001),
                  metrics = ['mae', 'mse'])
    
    return model
model = build_model()
model.summary()

# 출력 결과
Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input (InputLayer)          [(None, 9)]               0         
                                                                 
 dense1 (Dense)              (None, 64)                640       
                                                                 
 dense2 (Dense)              (None, 64)                4160      
                                                                 
 output (Dense)              (None, 1)                 65        
                                                                 
=================================================================
Total params: 4,865
Trainable params: 4,865
Non-trainable params: 0
_________________________________________________________________
plot_model(model)

 

 

6. 샘플 데이터 확인

sample_batch = normed_train_data[:10]
sample_result = model.predict(sample_batch)
sample_batch

  • 정규화가 잘 된 데이터 값을 확인할 수 있음

 

 

7. 모델 학습

epochs = 1000
history = model.fit(normed_train_data, train_labels,
                    epochs = epochs, validation_split = 0.2)

 

 

8. 모델 학습 시각화

# 학습 과정에서 생겼던 각 반복의 loss값과 mae, mse 값을 모두 데이터프레임 형태로 저장
hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
hist

# 위의 데이터프레임을 사용하여 하나의 함수로 시각화 생성
def plot_history(history):
    hist = pd.DataFrame(history.history)
    hist['epoch'] = history.epoch

    plt.figure(figsize = (12, 6))
    
    plt.subplot(1, 2, 1)
    plt.xlabel('Epochs')
    plt.ylabel('MPG Mean Absolute Error')
    plt.plot(hist['epoch'], hist['mae'], label = 'Train Error')
    plt.plot(hist['epoch'], hist['val_mae'], label = 'Val Error')
    plt.ylim([0, 5])
    plt.legend()

    plt.subplot(1, 2, 2)
    plt.xlabel('Epochs')
    plt.ylabel('MPG Mean Squared Error')
    plt.plot(hist['epoch'], hist['mse'], label = 'Train Error')
    plt.plot(hist['epoch'], hist['val_mse'], label = 'Val Error')
    plt.ylim([0, 20])
    plt.legend()

    plt.show()

plot_history(history)

  • 검증데이터의 오차(Val Error)값이 mae, mse 값 모두 일정 값 이하로 더이상 떨어지지 않음
  • 학습을 더 진행해봤자 검증데이터의 오차가 줄어들지 않으면 의미가 없고
    train 데이터의 오차만 줄어들어 둘 사이 간격이 벌어지면 오히려 모델이 train 데이터에 과대적합될 수 있음

 

 

9. EarlyStopping을 이용한 규제화

from tensorflow.keras.callbacks import EarlyStopping

model = build_model()

# 10번의 성능 향상을 보고 그 동안 성능 향상이 이뤄지지 않으면 stop
early_stop = EarlyStopping(monitor = 'val_loss', patience = 10)

history = model.fit(normed_train_data, train_labels, epochs = epochs,
                    validation_split = 0.2, callbacks = [early_stop])

  • 1000번 다 반복되지 않고 91번째에서 성능 향상이 없다고 판단되어 학습 중지
plot_history(history)

 

 

10. 모델 평가

# test 데이터를 모델에 넣어 나온 loss와 mae, mse 값 저장
loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose = 2)
print(mae)

# 출력 결과
# 1.88정도의 mpg 오차내에서 예측
3/3 - 0s - loss: 5.7125 - mae: 1.8831 - mse: 5.7125 - 61ms/epoch - 20ms/step
1.8831140995025635

 

 

11. 학습된 모델을 통한 예측

# 예측
test_pred = model.predict(normed_test_data).flatten()

# 예측된 값과 실제 값의 산점도를 그려 선형성을 만족하는지 확인
plt.scatter(test_labels, test_pred)
plt.xlabel('True Values')
plt.ylabel('Predictions')
plt.axis('equal')
plt.axis('square')
plt.grid()
plt.xlim([0, plt.xlim()[1]])
plt.ylim([0, plt.ylim()[1]])
plt.plot([-50, 50], [-50, 50])
plt.show()

# 잘못 예측한 값은 어느정도 되는지 시각화
error = test_pred - test_labels
plt.hist(error, bins = 30)
plt.xlabel('Prediction Error')
plt.grid()
plt.ylabel('Count')
plt.show()

1. modules import

import tensorflow as tf
from tensorflow.keras.datasets.boston_housing import load_data
from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.utils import plot_model

from sklearn.model_selection import train_test_split

import numpy as np
import matplotlib.pyplot as plt

 

 

2. 데이터 로드

  • 데이터의 수가 적기 때문에 테스트 데이터의 비율을 20%로 지정
  • 13개의 특성을 가짐
  • 각각의 특성이 모두 다른 스케일, 즉 단위가 모두 다름
    • 범죄율: 0~1 사이의 값
    • 방의 개수: 3~9 사이의 값
  • 정답 레이블은 주택 가격의 중간 가격($1000 단위)
tf.random.set_seed(111)
(x_train_full, y_train_full), (x_test, y_test) = load_data(path = 'boston_housing.npz',
                                                           test_split = 0.2,
                                                           seed = 111)
# 가장 첫번째 train 데이터의 독립변수들
print(x_train_full[0])

# 출력 결과
[2.8750e-02 2.8000e+01 1.5040e+01 0.0000e+00 4.6400e-01 6.2110e+00
 2.8900e+01 3.6659e+00 4.0000e+00 2.7000e+02 1.8200e+01 3.9633e+02
 6.2100e+00]
# 가장 첫번째 train 데이터의 종속변수
print(y_train_full[0])

# 출력 결과
25.0

 

 

3. 데이터 확인

print('학습 데이터: {}\t레이블: {}'.format(x_train_full.shape, y_train_full.shape))
print('테스트 데이터: {}\t레이블: {}'.format(x_test.shape, y_test.shape))

# 출력 결과
학습 데이터: (404, 13)	레이블: (404,)
테스트 데이터: (102, 13)	레이블: (102,)

 

 

4. 데이터 전처리

  • standardization
  • 특성의 단위가 모두 다르기 때문에 동일한 범위로 조정
# 정규화하기 위해 평균과 표준편차를 구한 후, 각 값에서 평균을 빼고 표준편차로 나눠줌
mean = np.mean(x_train_full, axis = 0)
std = np.std(x_train_full, axis = 0)

x_train_preprocessed = (x_train_full - mean) / std
x_test = (x_test - mean) / std

x_train, x_val, y_train, y_val = train_test_split(x_train_preprocessed, y_train_full, test_size = 0.3, random_state = 111)
print("학습 데이터: {}\t레이블: {}".format(x_train_full.shape, y_train_full.shape))
print("학습 데이터: {}\t레이블: {}".format(x_train.shape, y_train.shape))
print("검증 데이터: {}\t레이블: {}".format(x_val.shape, y_val.shape))
print("테스트 데이터: {}\t레이블: {}".format(x_test.shape, y_test.shape))

# 출력 결과
학습 데이터: (404, 13)	레이블: (404,)
학습 데이터: (282, 13)	레이블: (282,)
검증 데이터: (122, 13)	레이블: (122,)
테스트 데이터: (102, 13)	레이블: (102,)

 

 

5. 모델 구성

  • 학습 데이터가 매우 적은 경우에 모델의 깊이를 깊게 할수록 과대적합이 일어날 확률이 높음
model = Sequential([Dense(100, activation = 'relu', input_shape = (13, ), name = 'dense1'),
                    Dense(64, activation = 'relu', name = 'dense2'),
                    Dense(32, activation = 'relu', name = 'dense3'),
                    Dense(1, name = 'output')])

model.summary()

# 출력 결과
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense1 (Dense)              (None, 100)               1400      
                                                                 
 dense2 (Dense)              (None, 64)                6464      
                                                                 
 dense3 (Dense)              (None, 32)                2080      
                                                                 
 output (Dense)              (None, 1)                 33        
                                                                 
=================================================================
Total params: 9,977
Trainable params: 9,977
Non-trainable params: 0
_________________________________________________________________
plot_model(model)

 

 

6. 모델 컴파일

  • 회귀 문제에서는 주로 평균제곱오차(MSE)를 손실함수로,
    평균절대오차(MAE)를 평가지표로 많이 사용
model.compile(loss = 'mse',
              optimizer = Adam(learning_rate = 1e-2),
              metrics = ['mae'])

 

 

7. 모델 학습

history = model.fit(x_train, y_train, epochs = 300,
                    validation_data = (x_val, y_val))

 

 

8. 모델 평가

  • evaluate()
model.evaluate(x_test, y_test)

# 출력 결과
4/4 [==============================] - 0s 5ms/step - loss: 14.7238 - mae: 2.6094
[14.723812103271484, 2.609373092651367]
history_dict = history.history

loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss) + 1)
fig = plt.figure(figsize= (12, 6))

ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, loss, color = 'blue', label = 'train_loss')
ax1.plot(epochs, val_loss, color = 'red', label = 'val_loss')
ax1.set_title('Train and Validation Loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('Loss')
ax1.grid()
ax1.legend()

mae = history_dict['mae']
val_mae = history_dict['val_mae']

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, mae, color = 'blue', label = 'train_mae')
ax2.plot(epochs, val_mae, color = 'red', label = 'val_mae')
ax2.set_title('Train and Validation MAE')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('MAE')
ax2.grid()
ax2.legend()

 

 

9. K-Fold 교차 검증

  • 데이터셋의 크기가 매우 작은 경우에 [훈련, 검증, 테스트] 데이터로 나누게 되면 과소적합이 일어날 확률이 높음
  • 이를 해결하기 위해 K-Fold 교차 검증 실행

https://scikit-learn.org/stable/modules/cross_validation.html

 

 

10. 모델 재구성

  • K-Fold 교차검증을 위한 재구성
  • train 데이터를 5개로 나눔
from sklearn.model_selection import KFold
from tensorflow.keras.layers import Input
from tensorflow.keras.models import Model

tf.random.set_seed(111)
(x_train_full, y_train_full), (x_test, y_test) = load_data(path = 'boston_housing.npz',
                                                           test_split = 0.2,
                                                           seed = 111)

mean = np.mean(x_train_full, axis = 0)
std = np.std(x_train_full, axis = 0)

x_train_preprocessed = (x_train_full - mean) / std
x_test = (x_test - mean) / std

# 3개로 나누는 KFold 모델 생성
k = 3
kfold = KFold(n_splits = k, random_state = 111, shuffle = True)

# 모델 생성
def build_model():
    input = Input(shape = (13, ), name = 'input')
    hidden1 = Dense(100, activation = 'relu', input_shape = (13, ), name = 'dense1')(input)
    hidden2 = Dense(64, activation = 'relu', name = 'dense2')(hidden1)
    hidden3 = Dense(32, activation = 'relu', name = 'dense3')(hidden2)
    output = Dense(1, name = 'output')(hidden3)

    model = Model(inputs = [input], outputs = [output])

    model.compile(loss = 'mse',
                  optimizer = 'adam',
                  metrics = ['mae'])
    return model

# mae값을 저장할 리스트
mae_list = []

# 각 fold마다 학습 진행
for train_idx, val_idx in kfold.split(x_train):
    x_train_fold, x_val_fold = x_train[train_idx], x_train[val_idx]
    y_train_fold, y_val_fold = y_train_full[train_idx], y_train_full[val_idx]

    model = build_model()
    model.fit(x_train_fold, y_train_fold, epochs = 300,
              validation_data = (x_val_fold, y_val_fold))
    
    _, test_mae = model.evaluate(x_test, y_test)
    mae_list.append(test_mae)

print(mae_list)
print(np.mean(mae_list))

# 출력 결과
# 기준이 $1000이므로 $8000정도의 오차범위가 존재한다는 의미
[9.665495872497559, 8.393745422363281, 8.736763954162598]
8.932001749674479

6. MNIST 예제를 통해 모델 구성하기

  • keras.datasets에 포함되어 있는 데이터셋

https://www.tensorflow.org/datasets/catalog/mnist?hl=ko

  - modules import

import tensorflow as tf
from tensorflow.keras.datasets.mnist import load_data
from tensorflow.keras.models import Sequential
from tensorflow.keras import models
from tensorflow.keras.layers import Dense, Input, Flatten
from tensorflow.keras.utils import to_categorical, plot_model

from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt

 

  - 데이터셋 로드

  • MNIST 데이터셋을 로드
  • Train Data 중 30%를 검증 데이터(validation data)로 사용
tf.random.set_seed(111)
(x_train_full, y_train_full), (x_test, y_test) = load_data(path = 'mnist.npz')
x_train, x_val, y_train, y_val = train_test_split(x_train_full, y_train_full, test_size = 0.3, random_state = 111)

 

  - 데이터 확인

# 데이터 형태 확인
num_x_train = (x_train.shape[0])
num_x_val = (x_val.shape[0])
num_x_test = (x_test.shape[0])

print("학습 데이터: {}\t레이블: {}".format(x_train_full.shape, y_train_full.shape))
# train과 val 데이터로 split 한 뒤의 데이터
print("학습 데이터: {}\t레이블: {}".format(x_train.shape, y_train.shape))
print("검증데이터: {}\t레이블: {}".format(x_val.shape, y_val.shape))
print("테스트 데이터: {}\t레이블: {}".format(x_test.shape, y_test.shape))

# 출력 결과
학습 데이터: (60000, 28, 28)	레이블: (60000,)
학습 데이터: (42000, 28, 28)	레이블: (42000,)
검증데이터: (18000, 28, 28)	레이블: (18000,)
테스트 데이터: (10000, 28, 28)	레이블: (10000,
# 랜덤으로 5개의 데이터 추출하여 데이터 확인
num_sample = 5
random_idxs = np.random.randint(60000, size = num_sample)

plt.figure(figsize = (14, 8))
for i, idx in enumerate(random_idxs):
    img = x_train_full[idx, :]
    label = y_train_full[idx]

    plt.subplot(1, len(random_idxs), i+1)
    plt.imshow(img)
    plt.title("Indes: {}, Label: {}".format(idx, label))

 

  - 데이터 전처리

  • Normalization
# 최대 255의 값으로 이루어진 x데이터를 255로 나누어 0과 1사이로 정규화
x_train = x_train / 255.
x_val = x_val / 255.
x_test = x_test / 255.

# 정수형인 y 레이블을 원-핫인코딩
y_train = to_categorical(y_train)
y_val = to_categorical(y_val)
y_test = to_categorical(y_test)

 

  - 모델 구성(Sequential)

model = Sequential([Input(shape = (28, 28), name = 'input'),
                    Flatten(input_shape = [28, 28], name= 'flatten'),
                    Dense(100, activation = 'relu', name = 'dense1'),
                    Dense(64, activation = 'relu', name = 'dense2'),
                    Dense(32, activation = 'relu', name = 'dense3'),
                    Dense(10, activation = 'softmax', name = 'output')])

model.summary()

# 출력 결과
Model: "sequential_3"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense1 (Dense)              (None, 100)               78500     
                                                                 
 dense2 (Dense)              (None, 64)                6464      
                                                                 
 dense3 (Dense)              (None, 32)                2080      
                                                                 
 output (Dense)              (None, 10)                330       
                                                                 
=================================================================
Total params: 87,374
Trainable params: 87,374
Non-trainable params: 0
_________________________________________________________________
# 각 레이어의 형태까지 출력
plot_model(model, show_shapes = True)

 

  - 모델 컴파일

model.compile(loss = 'categorical_crossentropy',
              optimizer = 'sgd',
              metrics = ['accuracy'])

 

  - 모델 학습

  • 모델 시각화를 위해 history라는 변수에 학습 과정을 담음
history = model.fit(x_train, y_train,
                    epochs = 50,
                    batch_size = 128,
                    validation_data = (x_val, y_val))

 

 

  - 학습 결과 시각화

# history에 학습 과정이 저장되어 있는지 확인
history.history.keys()

# 출력 결과
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
history_dict = history.history

loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss) + 1)
fig = plt.figure(figsize = (12, 6))

ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, loss, color = 'blue', label = 'train_loss')
ax1.plot(epochs, val_loss, color = 'red', label = 'val_loss')
ax1.set_title('Train and Validation Loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('Loss')
ax1.grid()
ax1.legend()

accuracy = history_dict['accuracy']
val_accuracy = history_dict['val_accuracy']

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, accuracy, color = 'blue', label = 'train_accuracy')
ax2.plot(epochs, val_accuracy, color = 'red', label = 'val_accuracy')
ax2.set_title('Train and Validation Accuracy')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Accauracy')
ax2.grid()
ax2.legend()

plt.show()

 

  - 모델 평가(1)

  • evaluate()
model.evaluate(x_test, y_test)

# 출력 결과
313/313 [==============================] - 2s 6ms/step - loss: 0.1235 - accuracy: 0.9609
[0.12354850769042969, 0.9609000086784363]

 

  - 학습된 모델을 통해 값 예측

pred_ys = model.predict(x_test)
print(pred_ys.shape)

np.set_printoptions(precision = 7)
# 가장 첫번째(인덱스가 0인)데이터가 0~9까지 총 10개의 정답 각각에 속할 확률 출력
print(pred_ys[0])

# 출력 결과
# 7일 확률이 0.99로 가장 높음
313/313 [==============================] - 3s 9ms/step
(10000, 10)
[3.5711932e-06 3.6218420e-08 4.6535680e-04 5.5227923e-04 1.9860077e-07
 2.2765586e-07 2.0107932e-12 9.9897194e-01 1.8151616e-06 4.6298537e-06]
arg_pred_y = np.argmax(pred_ys, axis = 1)

plt.imshow(x_test[0])
plt.title("predicted label: {}".format(arg_pred_y[0]))
plt.show()

 

  - 모델 평가(2)

  • 혼돈행렬(Confusion Matrix)
from sklearn.metrics import confusion_matrix

plt.figure(figsize = (8, 8))
cm = confusion_matrix(np.argmax(y_test, axis = -1), np.argmax(pred_ys, axis = -1))
sns.heatmap(cm, annot = True, fmt = 'd', cmap = 'Blues')
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.show()

 

  - 모델 평가(3)

  • 분류 보고서
from sklearn.metrics import classification_report

print(classification_report(np.argmax(y_test, axis = -1), np.argmax(pred_ys, axis = -1)))

 

 

7. 모델 저장과 복원

  • save()
  • load_model()
  • (주의)
    Sequential API, 함수형 API에서는 모델의 저장 및 로드가 가능하지만 서브클래싱 방식으로는 할 수 없음
  • 서브클래싱 방식
    아래의 두 가지를 통해 모델의 파아미터만 저장 및 로드
save_weights()
load_weights()
  • JSON 형식
    • model.to_json() (저장)
    • tf.keras.models.model_from_json(file_path) (복원)
  • YAML로 직렬화
    • model.to_yaml() (저장)
    • tf.keras.models.model_from_yaml(file_path) (복원)
# 모델 저장
model.save('mnist_model.h5')
# 모델 복원
loaded_model = models.load_model('mnist_model.h5')
loaded_model.summary()

# 출력 결과
Model: "sequential_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense1 (Dense)              (None, 100)               78500     
                                                                 
 dense2 (Dense)              (None, 64)                6464      
                                                                 
 dense3 (Dense)              (None, 32)                2080      
                                                                 
 output (Dense)              (None, 10)                330       
                                                                 
=================================================================
Total params: 87,374
Trainable params: 87,374
Non-trainable params: 0
_________________________________________________________________

 

 

8. 콜백

  • fit() 함수의 callbacks 매개변수를 사용하여 케라스가 훈련의 시작이나 끝에 호출할 객체 리스트를 지정할 수 있음
  • 여러 개 사용 하능
  • ModelCheckpoint
    • tf.keras.callbacks.ModelCheckpoint
    • 정기적으로 모델의 체크포인트를 저장하고, 문제가 발생할 때 복구하는데 사용
  • EarlyStopping
    • tf.keras.callbacks.EarlyStopping
    • 검증 성능이 한동안 개선되지 않을 경우 학습을 중단할 때 사용
  • LearningRateSchedular
    • tf.keras.callbacks.LearningRateScheduler
    • 최적화를 하는 동안 학습률(learning_rate)를 동적으로 변경할 때 사용
  • TensorBoard
    • tf.keras.callbacks.TensorBoard
    • 모델의 경과를 모니터링할 때 사용
# 위의 MNIST 데이터와 모델 그대로 사용(함수로 묶어서 사용)
(x_train_full, y_train_full), (x_test, y_test) = load_data(path = 'mnist.npz')
x_train, x_val, y_train, y_val = train_test_split(x_train_full, y_train_full, test_size = 0.3, random_state = 111)

print("학습 데이터: {}\t레이블: {}".format(x_train_full.shape, y_train_full.shape))
print("학습 데이터: {}\t레이블: {}".format(x_train.shape, y_train.shape))
print("검증데이터: {}\t레이블: {}".format(x_val.shape, y_val.shape))
print("테스트 데이터: {}\t레이블: {}".format(x_test.shape, y_test.shape))

x_train = x_train / 255.
x_val = x_val / 255.
x_test = x_test / 255.

y_train = to_categorical(y_train)
y_val = to_categorical(y_val)
y_test = to_categorical(y_test)

def build_model():
    model = Sequential([Input(shape = (28, 28), name = 'input'),
                        Flatten(input_shape = [28, 28], name= 'flatten'),
                        Dense(100, activation = 'relu', name = 'dense1'),
                        Dense(64, activation = 'relu', name = 'dense2'),
                        Dense(32, activation = 'relu', name = 'dense3'),
                        Dense(10, activation = 'softmax', name = 'output')])
    
    model.compile(loss = 'categorical_crossentropy',
                  optimizer = 'sgd',
                  metrics = ['accuracy'])

    return model

model = build_model()
model.summary()

# 출력 결과
학습 데이터: (60000, 28, 28)	레이블: (60000,)
학습 데이터: (42000, 28, 28)	레이블: (42000,)
검증데이터: (18000, 28, 28)	레이블: (18000,)
테스트 데이터: (10000, 28, 28)	레이블: (10000,)
Model: "sequential_3"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense1 (Dense)              (None, 100)               78500     
                                                                 
 dense2 (Dense)              (None, 64)                6464      
                                                                 
 dense3 (Dense)              (None, 32)                2080      
                                                                 
 output (Dense)              (None, 10)                330       
                                                                 
=================================================================
Total params: 87,374
Trainable params: 87,374
Non-trainable params: 0
_________________________________________________________________
# 콜백 라이브러리 import
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, LearningRateScheduler, TensorBoard

 

  - ModelCheckpoint

check_point_cb = ModelCheckpoint('keras_mnist_model.h5')
history = model.fit(x_train, y_train, epochs = 10, callbacks = [check_point_cb])

# 출력 결과
Epoch 1/10
1313/1313 [==============================] - 8s 5ms/step - loss: 0.8994 - accuracy: 0.7532
Epoch 2/10
1313/1313 [==============================] - 6s 5ms/step - loss: 0.3294 - accuracy: 0.9059
Epoch 3/10
1313/1313 [==============================] - 4s 3ms/step - loss: 0.2614 - accuracy: 0.9247
Epoch 4/10
1313/1313 [==============================] - 4s 3ms/step - loss: 0.2208 - accuracy: 0.9360
Epoch 5/10
1313/1313 [==============================] - 4s 3ms/step - loss: 0.1919 - accuracy: 0.9437
Epoch 6/10
1313/1313 [==============================] - 4s 3ms/step - loss: 0.1696 - accuracy: 0.9507
Epoch 7/10
1313/1313 [==============================] - 4s 3ms/step - loss: 0.1511 - accuracy: 0.9565
Epoch 8/10
1313/1313 [==============================] - 4s 3ms/step - loss: 0.1356 - accuracy: 0.9611
Epoch 9/10
1313/1313 [==============================] - 4s 3ms/step - loss: 0.1229 - accuracy: 0.9646
Epoch 10/10
1313/1313 [==============================] - 4s 3ms/step - loss: 0.1122 - accuracy: 0.9674
loaded_model = load_model('keras_mnist_model.h5')
loaded_model.summary()

# 출력 결과
Model: "sequential_3"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense1 (Dense)              (None, 100)               78500     
                                                                 
 dense2 (Dense)              (None, 64)                6464      
                                                                 
 dense3 (Dense)              (None, 32)                2080      
                                                                 
 output (Dense)              (None, 10)                330       
                                                                 
=================================================================
Total params: 87,374
Trainable params: 87,374
Non-trainable params: 0
_________________________________________________________________
  • 최상의 모델만을 저장
    • save_best_only = True
model = build_model()

cp = ModelCheckpoint('keras_best_model.h5', save_best_only = True)

history = model.fit(x_train, y_train, epochs = 10,
                    validation_data = (x_val, y_val), callbacks = [cp])

# 출력 결과
Epoch 1/10
1313/1313 [==============================] - 12s 8ms/step - loss: 0.8529 - accuracy: 0.7606 - val_loss: 0.3651 - val_accuracy: 0.8924
Epoch 2/10
1313/1313 [==============================] - 8s 6ms/step - loss: 0.3209 - accuracy: 0.9075 - val_loss: 0.2884 - val_accuracy: 0.9141
Epoch 3/10
1313/1313 [==============================] - 11s 8ms/step - loss: 0.2551 - accuracy: 0.9254 - val_loss: 0.2353 - val_accuracy: 0.9296
Epoch 4/10
1313/1313 [==============================] - 13s 10ms/step - loss: 0.2147 - accuracy: 0.9371 - val_loss: 0.2123 - val_accuracy: 0.9366
Epoch 5/10
1313/1313 [==============================] - 10s 8ms/step - loss: 0.1869 - accuracy: 0.9451 - val_loss: 0.1972 - val_accuracy: 0.9410
Epoch 6/10
1313/1313 [==============================] - 12s 9ms/step - loss: 0.1661 - accuracy: 0.9513 - val_loss: 0.1818 - val_accuracy: 0.9450
Epoch 7/10
1313/1313 [==============================] - 10s 8ms/step - loss: 0.1491 - accuracy: 0.9566 - val_loss: 0.1700 - val_accuracy: 0.9488
Epoch 8/10
1313/1313 [==============================] - 5s 4ms/step - loss: 0.1350 - accuracy: 0.9608 - val_loss: 0.1476 - val_accuracy: 0.9552
Epoch 9/10
1313/1313 [==============================] - 5s 4ms/step - loss: 0.1229 - accuracy: 0.9637 - val_loss: 0.1414 - val_accuracy: 0.9572
Epoch 10/10
1313/1313 [==============================] - 6s 4ms/step - loss: 0.1128 - accuracy: 0.9663 - val_loss: 0.1337 - val_accuracy: 0.9595

 

  - EarlyStopping

  • 일정 에포크(patience)동안 검증 세트에 대한 점수가 오르지 않으면 학습을 멈춤
  • 모델이 향상되지 않으면 학습이 자동으로 중지되므로, epochs 숫자를 크게 해도 무방
  • 학습이 끝난 후의 최상의 가중치를 복원하기 때문에 모델을 따로 복원할 필요없음
model = build_model()

cp = ModelCheckpoint('keras_best_model2.h5', save_best_only = True)
early_stopping_cb = EarlyStopping(patience = 3, monitor = 'val_loss',
                                  restore_best_weights = True)

# 50번의 반복을 하며 가장 성능 좋은 모델을 저장함
# validation loss값을 보며 더 이상 돌지 않아도 된다고 판단하면 학습 중단
history = model.fit(x_train, y_train, epochs = 50,
                    validation_data = (x_val, y_val), callbacks = [cp, early_stopping_cb])

  • 30번만에 학습을 중단하고 최적의 값으로 판

 

  - LearningRateSchedular

def scheduler(epoch, learning_rate):
    if epoch < 10:
        return learning_rate
    else:
        return learning_rate * tf.math.exp(-0.1)

# learning rate 설정 전
model = build_model()
round(model.optimizer.lr.numpy(), 5)

# 출력 결과
0.01


# learning rate 설정 후
lr_scheduler_cb = LearningRateScheduler(scheduler)

history = model.fit(x_train, y_train, epochs = 15,
                    callbacks = [lr_scheduler_cb], verbose = 0)
round(model.optimizer.lr.numpy(), 5)

# 출력 결과
0.00607

 

  - Tensorboard

  • 텐서보드를 이용하여 학습과정 모니터링
  • 텐서보드를 사용하기 위해 logs 폴더를 만들고, 학습이 진행되는 동안 로그 파일을 생성
TensorBoard(log_dir = '.logs', histogram_freq = 0, write_graph = True, write_images = True)

# 출력 결과
<keras.callbacks.TensorBoard at 0x1c034d086a0>
log_dir = '.logs'

tensor_board_cb = [TensorBoard(log_dir = log_dir, histogram_freq = 1, write_graph = True, write_images = True)]

model = build_model()
model.fit(x_train, y_train, batch_size = 32, validation_data = (x_val, y_val),
          epochs = 30, callbacks = [tensor_board_cb])

# 출력 결과

 

  • load하는데 시간 소요
    • load가 안된다면 port 번호를 바꿔서 실행
    • ex) %tensorboard --logdir {log_dir} port 8000
%load_ext tensorboard
%tensorboard --logdir {log_dir}

  • 웹 환경에서 다양한 지표를 그래프로 확인 가능

● 케라스

  • 파이썬으로 작성된 고수준 신경망 API로 TensorFlow, CNTK, Theano와 함께 사용 가능
  • 사용자 친화성, 모듈성, 확장성을 통해 빠르고 간편한 프로토타이핑 가능
  • 컨볼루션 신경망, 순환 신경망, 둘의 조합까지 모두 지원
  • CPU와 GPU에서 매끄럽게 실행
import numpy as np
import tensorflow as tf

 

  - 레이어들을 import하는 방식(1)

  • 일일히 import 하지 않아도 됨
  • 코드가 다소 길어질 수 있음
# keras까지만 import 하여 이후 필요한 것은 keras.**으로 사용
from tensorflow import keras

keras.__version__

# 출력 결과
'2.11.0'
keras.layers.Dense(10, activation = 'sigmoid')

# 출력 결과
<keras.layers.core.dense.Dense at 0x1eaa357ecd0>
keras.Model()

# 출력 결과
<keras.engine.training.Model at 0x1eabdafa0d0>
keras.models.Sequential()

# 출력 결과
<keras.engine.sequential.Sequential at 0x1eabda0feb0>

 

  - 레이어들을 import하는 방식(2)

  • 일일히 import 해야함
  • 코드가 간결
# keras.layers, keras.models 등으로 사용할 레이어들을 한번에 import
from tensorflow.keras.layers import Dense, Input, Flatten, Activation
from tensorflow.keras.models import Sequential
from tensorflow.keras import Model
Dense(10, activation = 'relu')

# 출력 결과
<keras.layers.core.dense.Dense at 0x1eaa3455730>
Flatten(input_shape = [28, 28])

# 출력 결과
<keras.layers.reshaping.flatten.Flatten at 0x1eabdafae80>
X_train = np.random.randn(5500, 2)

Input(shape = X_train.shape[1:])

# 출력 결과
<KerasTensor: shape=(None, 2) dtype=float32 (created by layer 'input_1')>

 

 

1. 주요 레이어

  - Dense

  • Fully-Connected Layer
  • 노드수(유닛 수), 활성화 함수 등을 지정
  • name을 통한 레이어 간 구분 가능
  • 기본적으로 'Glorot uniform' 가중치(Xavier 분포 초기화), zeros bias로 초기화
    • kernel_initializer 인자를 통해 다른 가중치 초기화를 진행할 수 있음
dense = Dense(10, activation = 'relu', name = 'Dense Layer')
dense

# 출력 결과
<keras.layers.core.dense.Dense at 0x1eabdb1e760>
dense2 = Dense(15, activation = 'softmax')
dense2

# 출력 결과
<keras.layers.core.dense.Dense at 0x1eabdbd4a60>
  • 단 한줄로 레이어 선언이 끝남, keras의 장점

 

  - Activation

  • Dense layer에서 미리 활성화 함수를 지정할 수도 있지만 때에 따라서 따로 레이어를 만들어줄 수 있음
dense = Dense(10, kernel_initializer = 'he_normal', name = 'Dense Layer')
dense = Activation(dense)
dense

# 출력 결과
<keras.layers.core.activation.Activation at 0x1eabdb1e7c0>

 

  - Flatten

  • 배치 크기(또는 데이터 크기)를 제외하고 데이터를 1차원으로 쭉 펼치는 작업
  • 예시
(128, 3, 2, 2) -> (128, 12)
Flatten(input_shape = (28, 28))

# 출력 결과
<keras.layers.reshaping.flatten.Flatten at 0x1eabda5ddc0>

 

  - input

  • 모델의 입력을 정의
  • shape, dtype을 포함
  • 하나의 모델은 여러 개의 입력을 가질 수 있음
  • summary() 메소드를 통해서는 보이지 않음
input_1 = Input(shape = (28, 28), dtype = tf.float32)
input_2 = Input(shape = (8,), dtype = tf.int32)

input_1

# 출력 결과
<KerasTensor: shape=(None, 28, 28) dtype=float32 (created by layer 'input_2')>


input_2

# 출력 결과
<KerasTensor: shape=(None, 8) dtype=int32 (created by layer 'input_3')>

 

 

2. 모델 구성 방법

  • Sequential()
  • 서브클래싱(Subclassing)
  • 함수형 API

 

  - Sequential()

  • 모델이 순차적으로 진행할 때 사용
  • 간단한 방법
    • Sequential 객체 생성 후, add를 통한 방법
    • Sequential 인자에 한번에 추가
  • 다중 입력 및 출력이 존재하는 등의 복잡한 모델을 구성할 수 없음
from tensorflow.keras.layers import Dense, Input, Flatten
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.utils import plot_model

model = Sequential()
# Input 레이어 - Dense 레이어(relu 활성화 함수) - Dense 레이어(relu 활성화 함수) - Dense 레이어(softmax 활성화 함수) 연결
model.add(Input(shape = (28, 28)))
model.add(Dense(300, activation = 'relu'))
model.add(Dense(100, activation = 'relu'))
model.add(Dense(10, activation = 'softmax'))
  • 모델 구조 확인(model 객체의 summary() 이용)
model.summary()

# 출력 결과
# Input 레이어는 나타나지 않으므로 추가한 Dense 레이어만 표시됨
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_5 (Dense)             (None, 28, 300)           8700      
                                                                 
 dense_6 (Dense)             (None, 28, 100)           30100     
                                                                 
 dense_7 (Dense)             (None, 28, 10)            1010      
                                                                 
=================================================================
Total params: 39,810
Trainable params: 39,810
Non-trainable params: 0
_________________________________________________________________
plot_model(model)

# model의 레이어 한번에 정의하기
model = Sequential([Input(shape = (28, 28), name = 'Input'),
                    Dense(300, activation = 'relu', name = 'Dense1'),
                    Dense(100, activation = 'relu', name = 'Dense2'),
                    Dense(10, activation = 'softmax', name = 'Output')])
model.summary()

# 출력 결과
Model: "sequential_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 Dense1 (Dense)              (None, 28, 300)           8700      
                                                                 
 Dense2 (Dense)              (None, 28, 100)           30100     
                                                                 
 Output (Dense)              (None, 28, 10)            1010      
                                                                 
=================================================================
Total params: 39,810
Trainable params: 39,810
Non-trainable params: 0
_________________________________________________________________
plot_model(model)

 

  - 함수형 API

  • 가장 권장되는 방법
  • 모델을 복잡하고 유연하게 구성 가능
  • 다중 입출력을 다룰 수 있음
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Flatten, Dense
from tensorflow.keras.utils import plot_model

inputs = Input(shape = (28, 28, 1))

x = Flatten(input_shape = (28, 28, 1))(inputs)
x = Dense(300, activation = 'relu')(x)
x = Dense(100, activation = 'relu')(x)
x = Dense(10, activation = 'softmax')(x)

model = Model(inputs = inputs, outputs = x)
model.summary()

# 출력 결과
Model: "model_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_6 (InputLayer)        [(None, 28, 28, 1)]       0         
                                                                 
 flatten_3 (Flatten)         (None, 784)               0         
                                                                 
 dense_6 (Dense)             (None, 300)               235500    
                                                                 
 dense_7 (Dense)             (None, 100)               30100     
                                                                 
 dense_8 (Dense)             (None, 10)                1010      
                                                                 
=================================================================
Total params: 266,610
Trainable params: 266,610
Non-trainable params: 0
_________________________________________________________________
plot_model(model)

from tensorflow.keras.layers import Concatenate

# wide & deep 모델
# 입력 하나를 wide 모델과 deep 모델에 분산시켜야함
# 다중 입력 필요
input_layer = Input(shape = (28, 28))
hidden1 = Dense(100, activation = 'relu')(input_layer)
hidden2 = Dense(30, activation = 'relu')(hidden1)
concat = Concatenate()([input_layer, hidden2])
output = Dense(1)(concat)

model = Model(inputs = [input_layer], outputs = [output])
model.summary()

# 출력 결과
Model: "model_2"
__________________________________________________________________________________________________
 Layer (type)                   Output Shape         Param #     Connected to                     
==================================================================================================
 input_8 (InputLayer)           [(None, 28, 28)]     0           []                               
                                                                                                  
 dense_12 (Dense)               (None, 28, 100)      2900        ['input_8[0][0]']                
                                                                                                  
 dense_13 (Dense)               (None, 28, 30)       3030        ['dense_12[0][0]']               
                                                                                                  
 concatenate_1 (Concatenate)    (None, 28, 58)       0           ['input_8[0][0]',                
                                                                  'dense_13[0][0]']               
                                                                                                  
 dense_14 (Dense)               (None, 28, 1)        59          ['concatenate_1[0][0]']          
                                                                                                  
==================================================================================================
Total params: 5,989
Trainable params: 5,989
Non-trainable params: 0
__________________________________________________________________________________________________
plot_model(model)

# 다중 입력
input_1 = Input(shape = (10, 10), name = 'input_1')
input_2 = Input(shape = (10, 28), name = 'input_2')

hidden1 = Dense(100, activation = 'relu')(input_2)
hidden2 = Dense(10, activation = 'relu')(hidden1)
concat = Concatenate()([input_1, hidden2])
output = Dense(1, activation = 'sigmoid', name= 'output')(concat)

model = Model(inputs = [input_1, input_2], outputs = [output])
model.summary()

# 출력 결과
Model: "model_3"
__________________________________________________________________________________________________
 Layer (type)                   Output Shape         Param #     Connected to                     
==================================================================================================
 input_2 (InputLayer)           [(None, 10, 28)]     0           []                               
                                                                                                  
 dense_17 (Dense)               (None, 10, 100)      2900        ['input_2[0][0]']                
                                                                                                  
 input_1 (InputLayer)           [(None, 10, 10)]     0           []                               
                                                                                                  
 dense_18 (Dense)               (None, 10, 10)       1010        ['dense_17[0][0]']               
                                                                                                  
 concatenate_3 (Concatenate)    (None, 10, 20)       0           ['input_1[0][0]',                
                                                                  'dense_18[0][0]']               
                                                                                                  
 output (Dense)                 (None, 10, 1)        21          ['concatenate_3[0][0]']          
                                                                                                  
==================================================================================================
Total params: 3,931
Trainable params: 3,931
Non-trainable params: 0
__________________________________________________________________________________________________
plot_model(model)

# 다중 출력
input_ = Input(shape = (10, 10), name = 'input_')

hidden1 = Dense(100, activation = 'relu')(input_)
hidden2 = Dense(10, activation = 'relu')(hidden1)

output = Dense(1, activation = 'sigmoid', name = 'main_output')(hidden2)
sub_out = Dense(1, name = 'sum_output')(hidden2)

model = Model(inputs = [input_], outputs = [output, sub_out])
model.summary()

# 출력 결과
Model: "model_4"
__________________________________________________________________________________________________
 Layer (type)                   Output Shape         Param #     Connected to                     
==================================================================================================
 input_ (InputLayer)            [(None, 10, 10)]     0           []                               
                                                                                                  
 dense_20 (Dense)               (None, 10, 100)      1100        ['input_[0][0]']                 
                                                                                                  
 dense_21 (Dense)               (None, 10, 10)       1010        ['dense_20[0][0]']               
                                                                                                  
 main_output (Dense)            (None, 10, 1)        11          ['dense_21[0][0]']               
                                                                                                  
 sum_output (Dense)             (None, 10, 1)        11          ['dense_21[0][0]']               
                                                                                                  
==================================================================================================
Total params: 2,132
Trainable params: 2,132
Non-trainable params: 0
__________________________________________________________________________________________________
plot_model(model)

# 다중 입력, 다중 출력
input_1 = Input(shape = (10, 10), name = 'input_1')
input_2 = Input(shape = (10, 28), name = 'input_2')

hidden1 = Dense(100, activation = 'relu')(input_2)
hidden2 = Dense(10, activation = 'relu')(hidden1)
concat = Concatenate()([input_1, hidden2])
output = Dense(1, activation = 'sigmoid', name = 'main_output')(concat)
sub_out = Dense(1, name = 'sum_output')(hidden2)

model = Model(inputs = [input_1, input_2], outputs = [output, sub_out])
model.summary()

# 출력 결과
Model: "model_5"
__________________________________________________________________________________________________
 Layer (type)                   Output Shape         Param #     Connected to                     
==================================================================================================
 input_2 (InputLayer)           [(None, 10, 28)]     0           []                               
                                                                                                  
 dense_26 (Dense)               (None, 10, 100)      2900        ['input_2[0][0]']                
                                                                                                  
 input_1 (InputLayer)           [(None, 10, 10)]     0           []                               
                                                                                                  
 dense_27 (Dense)               (None, 10, 10)       1010        ['dense_26[0][0]']               
                                                                                                  
 concatenate_6 (Concatenate)    (None, 10, 20)       0           ['input_1[0][0]',                
                                                                  'dense_27[0][0]']               
                                                                                                  
 main_output (Dense)            (None, 10, 1)        21          ['concatenate_6[0][0]']          
                                                                                                  
 sum_output (Dense)             (None, 10, 1)        11          ['dense_27[0][0]']               
                                                                                                  
==================================================================================================
Total params: 3,942
Trainable params: 3,942
Non-trainable params: 0
__________________________________________________________________________________________________
plot_model(model)

 

  - 서브클래싱(Subclassing)

  • 커스터마이징에 최적화된 방법
  • Model 클래스를 상속받아 Model이 포함하는 기능을 사용할 수 있음
    • fit(), evaluate(), predict()
    • save(), load()
  • 주로 call() 메소드 안에서 원하는 계산 가능
    • for, if, 저수준 연산 등
  • 권장되는 방법은 아니지만 어떤 모델의 구현 코드를 참고할 때 해석할 수 있어야 함
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Flatten, Dense
from tensorflow.keras.utils import plot_model

class MyModel(Model):
    def __init__(self, units = 30, activation = 'relu', **kwargs):
        super(MyModel, self).__init__(**kwargs)

        self.dense_layer1 = Dense(300, activation = activation)
        self.dense_layer2 = Dense(100, activation = activation)
        self.dense_layer3 = Dense(units, activation = activation)

        self.output_layer = Dense(10, activation = 'softmax')
    
    def call(self, inputs):
        x = self.dense_layer1(inputs)
        x = self.dense_layer2(x)
        x = self.dense_layer3(x)
        x = self.output_layer(x)
        return x

 

 

3. 모델 가중치 확인

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Flatten, Dense
from tensorflow.keras.utils import plot_model

inputs = Input(shape = (28, 28, 1))

x = Flatten(input_shape = (28, 28, 1))(inputs)
x = Dense(300, activation = 'relu')(x)
x = Dense(100, activation = 'relu')(x)
x = Dense(10, activation = 'softmax')(x)

model = Model(inputs = inputs, outputs = x)
model.summary()

# 출력 결과
Model: "model_7"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_9 (InputLayer)        [(None, 28, 28, 1)]       0         
                                                                 
 flatten_4 (Flatten)         (None, 784)               0         
                                                                 
 dense_28 (Dense)            (None, 300)               235500    
                                                                 
 dense_29 (Dense)            (None, 100)               30100     
                                                                 
 dense_30 (Dense)            (None, 10)                1010      
                                                                 
=================================================================
Total params: 266,610
Trainable params: 266,610
Non-trainable params: 0
_________________________________________________________________
  • 모델의 레이어들이 리스트로 표현됨
model.layers

# 출력 결과
[<keras.engine.input_layer.InputLayer at 0x1b51b66a0a0>,
 <keras.layers.reshaping.flatten.Flatten at 0x1b51d577c10>,
 <keras.layers.core.dense.Dense at 0x1b51d54a250>,
 <keras.layers.core.dense.Dense at 0x1b51d54a970>,
 <keras.layers.core.dense.Dense at 0x1b51d5997c0>]


# 위의 레이어 충 2번 인덱스의 레이어의 이름 출력
hidden_2 = model.layers[2]
hidden_2.name

# 출력 결과
'dense_28'
  • 위의 layer name 참고
# hidden_2(두번째 레이어)의 이름이 dense_28인지 확인
model.get_layer('dense_28') is hidden_2

# 출력 결과
True
# hidden_2에서 계산된 가중치(weights)와 편향(biases) 출력
weights, biases = hidden_2.get_weights()
weights

# 출력 결과
array([[-0.04896989,  0.00405482,  0.02683295, ..., -0.07056625,
        -0.04177827,  0.00787882],
       [-0.05326419, -0.0303001 ,  0.00274137, ..., -0.02837748,
         0.05391636, -0.04700017],
       [-0.05597277,  0.03437241, -0.00236335, ...,  0.04641164,
         0.04785167, -0.02015593],
       ...,
       [ 0.04056622, -0.05879534,  0.01665498, ...,  0.02095272,
         0.05407439, -0.069397  ],
       [-0.02786053, -0.02436828, -0.03833799, ...,  0.06946112,
        -0.02327226,  0.04145432],
       [ 0.00797608, -0.07426289,  0.04644973, ...,  0.02902658,
        -0.04708448, -0.02754754]], dtype=float32)


biases

# 출력 결과
array([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., 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., 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.], dtype=float32)


print(weights.shape)
print(biases.shape)

# 출력 결과
# hidden_2 레이어의 units를 300으로 지정했으므로 가중치와 편향도 각각 300개
(784, 300)
(300,)

 

 

4. 모델 컴파일(compile)

  • 모델을 구성한 후, 사용할 손실함수(loss), 옵티마이저(optimizer)를 지정
model.compile(loss = 'sparse_categorical_crossentropy',
              optimizer = 'sgd',
              metrics = ['accuracy'])

 

  - 참고1

  - loss

  • keras.losses.square_categorical_crossentropy등과 같이 지정 가능
    • sparse_categorical_crossentropy클래스가 배타적
      즉, MNIST 예제에서 (0, 1, 2, ..., 9)와 같은 방식으로 구분되어 있을 때 사용
    • categorical_crossentropy클래스가 원-핫 인코딩 방식으로 되어있을 때 사용
    • binary_crossentropy 이진 분류를 수행할 때 사용

  - optimizer

  • kera.optimizer.SGD() 등과 같이 사용
    • 보통 옵티마이저의 튜닝을 위해 따로 객체를 생성하여 컴파일
optimizer = keras.optimizer.SGD(learning_rate = 1e-5)
model.compile(...,
                      optimizer = optimizer,
                      ...)
  • keras.optimizer.Adam()도 많이 사용

  - metrics

  • 모니터링할 지표
  • 주로 'accuracy', 'acc'로도 가능

 

  - 참고2-컴파일 설정 예시

  • 평균 제곱 오차 회귀 문제
model.compile(loss = 'mse',
                      optimizer = RMSpop(),
                      metrics = ['mae'])
  • 이진 분류
model.compile(loss = 'binary_crossentropy',
                      optimizer = RMSpop(),
                      metrics = ['accuracy'])
  • 다항 분류
model.compile(loss = 'categorical_crossentropy',
                      optimizer = RMSpop(),
                      metrics = ['accuarcy'])

 

 

5. 모델 훈련, 평가 및 예측

  • fit() 함수
    • train_data(학습 데이터), train_label(데이터의 정답)
    • epochs
    • batch_size
    • validation_data
  • evaluate() 함수
    • 테스트 데이터
  • predict() 임의의 데이터를 인자로 넣어 예측 가능

● 텐서플로우 회귀 분석 모델

  • 다양한 방법이 존재
  • 텐서플로우2만 사용하는 방법, 케라스를 이용한 방법, 둘을 모두 섞은 방법 등

 

1. 선형 회귀

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

# 하이퍼 파라미터
learning_rate = 0.01
training_steps = 1000

# 데이터(연습용으로 랜덤 생성)
X = np.random.randn(50)
# Y에는 랜덤값을 더해 노이즈도 약간 추가
Y = 2 * X + np.random.randn(50)

# 가중치와 편향
W = tf.Variable(np.random.randn(), name = 'weight')
b = tf.Variable(np.random.randn(), name = 'bias')

# 선형회귀 함수
def linear_regression(x):
    return W * x + b

# 손실함수
def mean_square(y_pred, y_true):
    return tf.reduce_mean(tf.square(y_pred - y_true))

# 옵티마이저
optimizer = tf.optimizers.SGD(learning_rate

# 옵티마이저 실행 함수
def run_optimization():
    with tf.GradientTape() as tape:
        pred = linear_regression(X)
        loss = mean_square(pred, Y)
    
    gradients = tape.gradient(loss, [W, b])

    optimizer.apply_gradients(zip(gradients, [W, b]))

# 지정한 step만큼(1000번) 반복
for step in range(1, training_steps + 1):
    run_optimization()

    if step % 50 == 0:
        pred = linear_regression(X)
        loss = mean_square(pred, Y)
        print("step: {:4d}\tloss:{:.4f}\tW: {:.4f}\tb: {:.4f}".format(step, loss, W.numpy(), b.numpy()))

# 출력 결과
step:   50	loss:0.9240	W: 2.0819	b: 0.0301
step:  100	loss:0.9101	W: 2.1498	b: 0.0728
step:  150	loss:0.9084	W: 2.1726	b: 0.0893
step:  200	loss:0.9082	W: 2.1803	b: 0.0956
step:  250	loss:0.9082	W: 2.1829	b: 0.0980
step:  300	loss:0.9082	W: 2.1838	b: 0.0990
step:  350	loss:0.9082	W: 2.1841	b: 0.0993
step:  400	loss:0.9082	W: 2.1842	b: 0.0994
step:  450	loss:0.9082	W: 2.1842	b: 0.0995
step:  500	loss:0.9082	W: 2.1842	b: 0.0995
step:  550	loss:0.9082	W: 2.1842	b: 0.0995
step:  600	loss:0.9082	W: 2.1842	b: 0.0995
step:  650	loss:0.9082	W: 2.1842	b: 0.0995
step:  700	loss:0.9082	W: 2.1842	b: 0.0995
step:  750	loss:0.9082	W: 2.1842	b: 0.0995
step:  800	loss:0.9082	W: 2.1842	b: 0.0995
step:  850	loss:0.9082	W: 2.1842	b: 0.0995
step:  900	loss:0.9082	W: 2.1842	b: 0.0995
step:  950	loss:0.9082	W: 2.1842	b: 0.0995
step: 1000	loss:0.9082	W: 2.1842	b: 0.0995
# 시각화
plt.plot(X, Y, 'ro', label = 'Data')
plt.plot(X, np.array(W * X + b), label = 'Fitted Line')
plt.legend()
plt.grid()
plt.show()

 

 

2. 다항 회귀

  - modules import

from tensorflow.keras.optimizers import Adam

 

  - Hyper Parameters

epochs = 1000
learning_rate = 0.04

 

  - 변수 지정

a = tf.Variable(np.random.randn())
b = tf.Variable(np.random.randn())
c = tf.Variable(np.random.randn())

print(a.numpy())
print(b.numpy())
print(c.numpy())

# 출력 결과
0.74045
-0.027472999
-1.99301

 

  - 데이터 지정

X = np.random.randn(50)
Y = X**2 + X * np.random.randn(50)
# 데이터 분포 시각화
line_x = np.arange(min(X), max(X), 0.001)
line_y = a * line_x ** 2 + b * line_x + c

x_ = np.arange(-4., 4., 0.001)
y_ = a * x_ ** 2 + b * x_ + c

plt.scatter(X, Y, label = 'X data')
plt.plot(x_, y_, 'g--', label = 'origin line')
plt.plot(line_x, line_y, 'r--', label = 'before train')
plt.xlim(-4., 4.)
plt.legend()
plt.grid()
plt.show()

 

  - Util Functions

def compute_loss():
    pred_y = a * (np.array(X) ** 2) + b * np.array(X) + c
    loss = tf.reduce_mean((Y - pred_y) ** 2)
    return loss

 

  - Optimizer

optimizer = Adam(learning_rate = learning_rate)

 

  - 학습

for epoch in range(1, epochs + 1, 1):
    optimizer.minimize(compute_loss, var_list = [a, b, c])

    if epoch % 100 == 0:
        print("epoch: {:4d}\ta: {:.4f}\tb: {:.4f}\tc: {:.4f}".format(epoch, a.numpy(), b.numpy(), c.numpy()))

# 출력 결과
epoch:  100	a: 0.7958	b: -0.4986	c: 0.1659
epoch:  200	a: 0.7958	b: -0.4986	c: 0.1659
epoch:  300	a: 0.7958	b: -0.4986	c: 0.1659
epoch:  400	a: 0.7958	b: -0.4986	c: 0.1659
epoch:  500	a: 0.7958	b: -0.4986	c: 0.1659
epoch:  600	a: 0.7958	b: -0.4986	c: 0.1659
epoch:  700	a: 0.7958	b: -0.4986	c: 0.1659
epoch:  800	a: 0.7958	b: -0.4986	c: 0.1659
epoch:  900	a: 0.7958	b: -0.4986	c: 0.1659
epoch: 1000	a: 0.7958	b: -0.4986	c: 0.1659

 

  - 학습 후의 회귀선

line_x = np.arange(min(X), max(X), 0.001)
line_y = a * line_x ** 2 + b * line_x + c

plt.scatter(X, Y, label = 'X data')
plt.plot(x_, y_, 'g--', label = 'origin line')
plt.plot(line_x, line_y, 'r--', label = 'after train')
plt.xlim(-4., 4.)
plt.legend()
plt.grid()
plt.show()

 

 

3. 로지스틱 회귀

  • 다항 분류, MNIST

 

  - modules import

from tensorflow.keras.datasets import mnist

 

  - Hyper Parameters

# 데이터가 0~9, 총 10개의 숫자로 이루어져 있으므로 클래스의 개수는 10개
num_classes = 10
# 데이터가 28*28의 이미지로 되어 있으므로 한 개당 총 픽셀 수는 784개
num_features = 784

learning_rate = 0.1
training_steps = 1000
batch_size = 256

 

  - 데이터 로드

(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 데이터 전처리
# 데이터 타입 변환
x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)

# 28*28의 이미지 데이터를 1행 784열의 1차원 데이터로 평탄화
x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])

# mnist 데이터의 각 값은 한 픽셀이 가질 수 있는 색의 값으로,
# 0~255의 값을 가지므로 각 값을 255로 나누면 모든 값을 0~1사이의 값으로 정규화할 수 있음
x_train, x_test = x_train / 255., x_test / 255.

 

  - tf.data API 사용

train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)

 

  - 변수 지정

W = tf.Variable(tf.random.normal([num_features, num_classes]), name = 'weight')
b = tf.Variable(tf.zeros([num_classes]), name = 'bias')

 

  - Util Functions

def logistic_regression(x):
    return tf.nn.softmax(tf.matmul(x, W) + b)

def cross_entropy(pred_y, true_y):
    true_y = tf.one_hot(true_y, depth = num_classes)
    # 클립을 꽂은 것처럼 출력값의 범위를 미리 지정
    pred_y = tf.clip_by_value(pred_y, 1e-9, 1.)

    return tf.reduce_mean(-tf.reduce_sum(true_y * tf.math.log(pred_y), 1))

def accuracy(y_pred, y_true):
    correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))
    return tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

 

  - Optimizer

optimizer = tf.optimizers.SGD(learning_rate)

def run_optimization(x, y):
    with tf.GradientTape() as tape:
        pred = logistic_regression(x)
        loss = cross_entropy(pred, y)

    gradients = tape.gradient(loss, [W, b])

    optimizer.apply_gradients(zip(gradients, [W, b]))

 

  - 학습 진행

for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):
    run_optimization(batch_x, batch_y)

    if step % 50 == 0:
        pred = logistic_regression(batch_x)
        loss = cross_entropy(pred, batch_y)
        acc = accuracy(pred, batch_y)
        print("step: {:4d}\tloss: {:.4f}\taccuracy: {:.4f}".format(step, loss, acc))

# 출력 결과
step:   50	loss: 5.8780	accuracy: 0.3086
step:  100	loss: 3.2118	accuracy: 0.4609
step:  150	loss: 2.5498	accuracy: 0.5508
step:  200	loss: 2.7539	accuracy: 0.5508
step:  250	loss: 2.6145	accuracy: 0.5625
step:  300	loss: 1.5360	accuracy: 0.6719
step:  350	loss: 1.8810	accuracy: 0.6602
step:  400	loss: 1.4833	accuracy: 0.7305
step:  450	loss: 1.4094	accuracy: 0.7109
step:  500	loss: 1.4736	accuracy: 0.7266
step:  550	loss: 1.4690	accuracy: 0.7188
step:  600	loss: 1.5128	accuracy: 0.6875
step:  650	loss: 1.3078	accuracy: 0.7695
step:  700	loss: 1.1052	accuracy: 0.7969
step:  750	loss: 1.1313	accuracy: 0.7500
step:  800	loss: 0.8837	accuracy: 0.8086
step:  850	loss: 0.9336	accuracy: 0.7891
step:  900	loss: 1.0169	accuracy: 0.7812
step:  950	loss: 0.8714	accuracy: 0.8047
step: 1000	loss: 0.9229	accuracy: 0.8086

 

  - 테스트

pred = logistic_regression(x_test)
print("Test Accuracy: {}".format(accuracy(pred, y_test)))

# 출력 결과
Test Accuracy: 0.8057000041007996
  • train 데이터 정확도는 위에서 최종적으로 0.8086으로 나왔고
    test 데이터 정확도는 0.8057로 나

 

  - 시각화

num_images = 5
test_images = x_test[:num_images]
predictions = logistic_regression(test_images)

plt.figure(figsize = (30, 8))
for i in range(1, num_images + 1):
    plt.subplot(1, num_classes, i)
    plt.imshow(np.reshape(test_images[i-1], [28, 28]), cmap = 'gray')
    # 예측한 것 중 가장 높은 값을 정답으로 해야함(argmax 사용)
    plt.title("Model prediction: {}".format(np.argmax(predictions.numpy()[i-1])))

plt.show()

제목에 해당 글자를 예측한 값이 정답으로 잘 표현된 것을 확인

 

● 텐서플로우(Tensorflow) 기초

  • 가장 널리 쓰이는 딥러닝 프레임워크 중 하나
  • 구글이 주도적으로 개발하는 플랫폼
  • 파이썬, C++ API를 기본적으로 제공,
    자바스크립트, 자바, 고, 스위프트 등 다양한 프로그래밍 언어를 지원
  • tf.keras를 중심으로 고수준 API 통합
  • TPU(Tensor Processing Unit) 지원
    • TPU는 GPU보다 전력을 적게 소모, 경제적
    • 일반적으로 32비트(float32)로 수행되는 곱셈 연산을 16비트(float16)로 낮춤

 

1. 텐서플로우 아키텍쳐

https://developers.google.com/machine-learning/crash-course/first-steps-with-tensorflow/toolkit?hl=ko

 

 

2. 텐서플로우 시작하기

  - 텐서플로우 라이브러리

import numpy as np
import tensorflow as tf

print(tf.__version__)

# 출력 결과
2.11.0

 

  - 텐서의 객체

  • 타입(Type): string, float32, float16, int32, int8 등
  • 형상(Shape): 0, 1, 2차원 등의 데이터 차원
  • 축(Rank): 차원의 개수

 

  - 텐서의 차원과 연산

a = tf.constant(2)
print(tf.rank(a))
print(a)

# 출력 결과
tf.Tensor(0, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)
  • rank는 0으로 나와 차원이 0차원임을 의미
  • a값은 2
  • default 타입은 int32
b = tf.constant([2, 3])
print(tf.rank(b))
print(b)

# 출력 결과
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor([2 3], shape=(2,), dtype=int32)
  • rank는 1로 나와 차원이 1차원임을 의미
  • b값은 [2, 3]
c = tf.constant([[2, 3], [6, 7]])
print(tf.rank(c))
print(c)

# 출력 결과
tf.Tensor(2, shape=(), dtype=int32)
tf.Tensor(
[[2 3]
 [6 7]], shape=(2, 2), dtype=int32)
  • rank는 2로 나와 차원이 2차원임을 의미
  • c값은
    [[2, 3]
      [6, 7]]
d = tf.constant(['Hello'])
print(tf.rank(d))
print(d)

# 출력 결과
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor([b'Hello'], shape=(1,), dtype=string)
  • rank는 1로 나와 차원이 1차원임을 의미
  • d값은 'Hello'
  • 타입은 string

 

  - 난수 생성

rand = tf.random.uniform([1], 0, 1)
print(rand.shape)
print(rand)

# 출력 결과
(1,)
tf.Tensor([0.21540296], shape=(1,), dtype=float32)
  • random의 분포는 uniform(균등) 분포
  • shape은 [1]
  • 값의 최소값은 0, 최대값은 1(0에서 1 사이의 숫자 중 랜덤 추출)
rand2 = tf.random.normal([1, 2], 0, 1)
print(rand2.shape)
print(rand2)

# 출력 결과
(1, 2)
tf.Tensor([[ 0.801973  -0.2971729]], shape=(1, 2), dtype=float32)
  • random의 분포는 normal(정규) 분포
  • shape은 [1, 2]
  • 값의 분포의 평균은 0, 표준편차는 1
rand3 = tf.random.normal(shape = (3, 2), mean = 0, stddev = 1)
print(rand3.shape)
print(rand3)

# 출력 결과
(3, 2)
tf.Tensor(
[[-0.4018749   0.1871953 ]
 [-0.5250586   0.95744073]
 [ 2.2473423  -0.62274617]], shape=(3, 2), dtype=float32)
  • 정규분포에서 랜덤 값 추출하는 함수의 인수를 직접 지정하는 방법

 

  - 즉시 실행 모드(Eager Mode) 지원

  • 즉시 실행 모드를 통해 텐서플로우를 파이썬처럼 사용할 수 있음
  • 1.x버전에서는 '그래프'를 생성하고, 초기화 한 뒤에 세션을 통해 값을 흐르게 하는 작업을 진행해야함
a = tf.constant(2)
b = tf.constant(3)

print(tf.add(a, b))
print(a + b)

# 출력 결과
tf.Tensor(5, shape=(), dtype=int32)
tf.Tensor(5, shape=(), dtype=int32)


print(tf.subtract(a, b))
print(a - b)

# 출력 결과
tf.Tensor(-1, shape=(), dtype=int32)
tf.Tensor(-1, shape=(), dtype=int32)


print(tf.multiply(a, b))
print(a * b)

# 출력 결과
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(6, shape=(), dtype=int32)
  • add()와 +, subtract()와 -, multiply()와 *는 같은 기능

 

  - 텐서플로우 ↔ 넘파이

  • .numpy 사용 시 텐서플로우 데이터를 넘파이 형식으로 변환
c = tf.add(a, b).numpy()
print(type(c))

# 출력 결과
<class 'numpy.int32'>
  • tf.convert_to_tensor()를 사용해 넘파이 데이터를 텐서플로우 형식으로 변환
c_square = np.square(c, dtype = np.float32)
c_tensor = tf.convert_to_tensor(c_square)
print(c_tensor)
print(type(c_tensor))

# 출력 결과
tf.Tensor(25.0, shape=(), dtype=float32)
<class 'tensorflow.python.framework.ops.EagerTensor'>

 

  - 넘파이처럼 사용하기

t = tf.constant([[1., 2., 3.], [4., 5., 6.]])
print(t.shape)
print(t.dtype)

# 출력 결과
(2, 3)
<dtype: 'float32'>
# 슬라이싱
print(t[:, 1:])

# 출력 결과
tf.Tensor(
[[2. 3.]
 [5. 6.]], shape=(2, 2), dtype=float32)
 

# 각 행에서 인데스가 1인 값(2., 5.)만 추출
 t[..., 1, tf.newaxis]
 
 # 출력 결과
 <tf.Tensor: shape=(2, 1), dtype=float32, numpy=
array([[2.],
       [5.]], dtype=float32)>
# 연산
# 각 원소값에 10씩 더하기
t + 10

# 출력 결과
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[11., 12., 13.],
       [14., 15., 16.]], dtype=float32)>
       

# 각 원소값의 제곱값 출력
tf.square(t)

# 출력 결과
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[ 1.,  4.,  9.],
       [16., 25., 36.]], dtype=float32)>
# tensor 곱
# 원래 t 행렬(2*3) * 전치된 행렬(3*2) = 2*2 행렬
t @ tf.transpose(t)

# 출력 결과
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[14., 32.],
       [32., 77.]], dtype=float32)>

 

  - 타입 변환

  • 데이터 타입이 다르면 연산 불가능

  • 텐서의 기본 dtype
    • float형 텐서: float32
    • int형 텐서: int32
  • 연산 시 텐서의 타입을 맞춰줘야 함
    • float32 ~ float32
    • int32 ~ int32
    • float32 ~ int32 (x)
  • 타입 변환에는 tf.cast() 사용
# float64 타입의 데이터
t = tf.constant(30., dtype = tf.float64)
# float32 타입의 데이터(default)
t2 = tf.constant(4.)


# 잘못된 예시
# float63와 float32로 각각 타입이 다르므로 연산 불가능
print(t + t2)

# 출력 결과
InvalidArgumentError: cannot compute AddV2 as input #1(zero-based) was expected to be a double tensor but is a float tensor [Op:AddV2]


# float64 타입의 데이터를 float32 형식으로 변환
print(t2 + tf.cast(t, tf.float32))

# 출력 결과
tf.Tensor(34.0, shape=(), dtype=float32)

 

  - AutoGraph(오토그래프)

  • Tensorflow가 작업을 좀 더 빠르게 동작하게 하기 위한 방법으로 Graph로 만들어 연산을 진행
  • tf.Graph
  • 유연성이 있음
    • 모바일 애플리케이션, 임베디드 기기, 백엔드 서버와 같이 Python 인터프리터가 없는 환경에서 Tensorflow 사용 가능
코드

 

  - @tf.function

  • 자동으로 그래프를 생성(Auto Graph)
  • 그래프로 변환하여 사용 -> GPU 연산 가능
  • 파이썬으로 구성된 함수를 텐서플로우의 그래프 형태로 다루고 싶을 때 사용가능
  • 원본 함수가 필요하다면 (tf.function).python_function()
@tf.function
def my_function(x):
    return x**2 - 10 * x + 3

print(my_function(2))
print(my_function(tf.constant(2)))

# 출력 결과
tf.Tensor(-13, shape=(), dtype=int32)
tf.Tensor(-13, shape=(), dtype=int32)
# tf.function으로 지정하지 않았을 경우
def my_function_(x):
    return x**2 - 10 * x + 3

print(my_function_(2))
print(my_function_(tf.constant(2)))

# 출력 결과
# 일반적인 값을 넣으면 일반적인 값으로
# tf.constant값을 넣으면 tensor값으로 출력
-13
tf.Tensor(-13, shape=(), dtype=int32)
# 일반 함수를 tensor함수로 변환하기
tf_my_func = tf.function(my_function_)
print(tf_my_func(2))
print(tf_my_func(tf.constant(2)))

# 출력 결과
tf.Tensor(-13, shape=(), dtype=int32)
tf.Tensor(-13, shape=(), dtype=int32)


# tensor 함수를 일반 함수로 변환하기
tf_my_func.python_function(2)

# 출력 결과
-13
def function_to_get_faster(x, y, b):
    # 행렬곱
    x = tf.matmul(x, y)
    x = x + b
    return x

a_function_that_uses_a_graph = tf.function(function_to_get_faster)

x1 = tf.constant([[1., 2.]])
y1 = tf.constant([[2.], [3.]])
b1 = tf.constant(4.)

a_function_that_uses_a_graph(x1, y1, b1).numpy()

# 출력 결과
array([[12.]], dtype=float32)
# 파이썬 함수
def inner_function(x, y, b):
    x = tf.matmul(x, y)
    x = x + b
    return x

# tensor 함수
@tf.function
def outer_function(x):
    y = tf.constant([[2.], [3.]])
    b = tf.constant(4.)
    return inner_function(x, y, b)

# 파이썬 함수와 tensor 함수의 혼합
outer_function(tf.constant([[1., 2.]])).numpy()
  • 텐서플로우가 tf.function으로 변환한 코드
print(tf.autograph.to_code(my_function.python_function))
print(tf.autograph.to_code(tf_my_func.python_function))
print(tf.autograph.to_code(outer_function.python_function))

# 출력 결과
def tf__my_function(x):
    with ag__.FunctionScope('my_function', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()
        try:
            do_return = True
            retval_ = ag__.ld(x) ** 2 - 10 * ag__.ld(x) + 3
        except:
            do_return = False
            raise
        return fscope.ret(retval_, do_return)

def tf__my_function_(x):
    with ag__.FunctionScope('my_function_', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()
        try:
            do_return = True
            retval_ = ag__.ld(x) ** 2 - 10 * ag__.ld(x) + 3
        except:
            do_return = False
            raise
        return fscope.ret(retval_, do_return)

def tf__outer_function(x):
    with ag__.FunctionScope('outer_function', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()
        y = ag__.converted_call(ag__.ld(tf).constant, ([[2.0], [3.0]],), None, fscope)
        b = ag__.converted_call(ag__.ld(tf).constant, (4.0,), None, fscope)
        try:
            do_return = True
            retval_ = ag__.converted_call(ag__.ld(inner_function), (ag__.ld(x), ag__.ld(y), ag__.ld(b)), None, fscope)
        except:
            do_return = False
            raise
        return fscope.ret(retval_, do_return)
  • 속도 향상
import timeit

class SequentialModel(tf.keras.Model):
    def __init__(self, **kwargs):
        super(SequentialModel, self).__init__(**kwargs)
        self.flatten = tf.keras.layers.Flatten(input_shape = (28, 28))
        self.dense_1 = tf.keras.layers.Dense(128, activation = 'relu')
        self.dropout = tf.keras.layers.Dropout(0.2)
        self.dense_2 = tf.keras.layers.Dense(10)
    
    def call(self, x):
        x = self.flatten(x)
        x = self.dense_1(x)
        x = self.dropout(x)
        x = self.dense_2(x)
        return x
    
input_data = tf.random.uniform([60, 28, 28])

eager_model = SequentialModel()
graph_model = tf.function(eager_model)

print("Eager time:", timeit.timeit(lambda: eager_model(input_data), number = 10000))
print("Graph time:", timeit.timeit(lambda: graph_model(input_data), number = 10000))

# 출력 결과
Eager time: 70.29523700000004
Graph time: 35.47442840000008
  • 시간 효율성에서 즉시 실행 모드(eager mode)보다 그래프 모드(graph mode)가 더 빠름

 

  - 변수 생성

  • tf.Variable
  • 딥러닝 모델 학습 시, 그래프 연산이 필요할 때 사용
X = tf.Variable(20.)
print(X)

# 출력 결과
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=20.0>

 

  - AutoGrad(자동 미분)

  • tf.GradientTape API 사용
  • tf.Variable 같은 일부 입력에 대한 기울기 계산
    • 기본적으로 한번만 사용됨
  • 변수가 포함된 연산만 기록
x = tf.Variable(3.)
with tf.GradientTape() as tape:
    y = x**2

dy_dx = tape.gradient(y, x)
dy_dx.numpy()

# 출력 결과
6.0


# 한번만 연산할 수 있으므로 다른 값으로 바꿔 돌리면 오류 발생
x2 = tf.Variable(4)
dy_dx = tape.gradient(y, x2)
dy_dx.numpy()

# 출력 결과
 A non-persistent GradientTape can only be used to compute one set of gradients (or jacobians)
x = tf.Variable(2.)
y = tf.Variable(3.)
with tf.GradientTape() as tape:
    y_sq = y**2
    z = x**2 + tf.stop_gradient(y_sq)

grad = tape.gradient(z, {'x': x, 'y': y})

# x에 대한 미분
print('dz/dx:', grad['x'])
# y에 대한 미분
print('dz/dy:', grad['y'])

# 출력 결과
dz/dx: tf.Tensor(4.0, shape=(), dtype=float32)
dz/dy: None

 

  • tf.GradientTape를 지속가능하게 사용하고 싶을 때
weights = tf.Variable(tf.random.normal((3, 2)), name = 'weights')
biases = tf.Variable(tf.zeros(2, dtype = tf.float32), name = 'biases')
x = [[1., 2., 3.]]

# 값을 영구적으로 상용할 수 있도록 persistent 옵션 주기
with tf.GradientTape(persistent = True) as tape:
    y = x @ weights + biases
    loss = tf.reduce_mean(y**2)

[dl_dw, dl_db] = tape.gradient(loss, [weights, biases])

print(weights.shape)
print(dl_dw.shape)

# 출력 결과
(3, 2)
(3, 2)

 

 

3. 간단한 신경망 구조

  - 뉴런

  • 입력 → 연산 → 활성화 함수 → 출력
def sigmoid(x):
    return (1 / (1 + np.exp(-x)))

def Neuron(x, W, bias = 0):
    z = x * W + bias
    return sigmoid(z)

# tensor데이터 입력(형태에 맞게 random값을 연습용으로 생성)
x = tf.random.normal((1, 2), 0, 1)
W = tf.random.normal((1, 2), 0, 1)

print('x.shape:', x.shape)
print('W.shape:', W.shape)

print(x)
print(W)

print(Neuron(x, W))


# 출력 결과
x.shape: (1, 2)
W.shape: (1, 2)
tf.Tensor([[0.7348223  0.89396125]], shape=(1, 2), dtype=float32)
tf.Tensor([[ 0.69655097 -0.5962996 ]], shape=(1, 2), dtype=float32)
[[0.625238   0.36980146]]

 

  - 퍼셉트론 학습 알고리즘(가중치 업데이트)

$$ w^{(nextstep)}=w+\eta(y-\widetilde{y})x $$

  • \(w\): 가중치
  • \(\eta\): 학습률
  • \(y\): 정답 레이블
  • \(\widetilde{y}\): 예측 레이블
# 한 번의 뉴런을 거쳤을 때
x = 1
y = 0
W = tf.random.normal([1], 0, 1)
print(Neuron(x, W))
print('y:', y)

# 출력 결과
[0.8723855]
y: 0
# 1000번 반복
for i in range(1000):
    output = Neuron(x, W)
    error = y - output
    # learning rate 0.1
    W = W + x * 0.1 * error

    if i % 100 == 99:
        print('{}\t{}\t{}'.format(i+1, error, output))

# 출력 결과
100	[-0.13575228]	[0.13575228]
200	[-0.06054739]	[0.06054739]
300	[-0.03837379]	[0.03837379]
400	[-0.02797328]	[0.02797328]
500	[-0.02197183]	[0.02197183]
600	[-0.01807551]	[0.01807551]
700	[-0.01534558]	[0.01534558]
800	[-0.01332812]	[0.01332812]
900	[-0.01177713]	[0.01177713]
1000	[-0.01054805]	[0.01054805]
# 다른 뉴런 정의
def Neuron2(x, W, bias = 0):
    z = tf.matmul(x, W, transpose_b = True) + bias
    return sigmoid(z)

x = tf.random.normal((1, 3), 0, 1)
y = tf.ones(1)
W = tf.random.normal((1, 3), 0, 1)

print(Neuron2(x, W))
print('y:', y)

# 출력 결과
[[0.03847362]]
y: tf.Tensor([1.], shape=(1,), dtype=float32)


# 1000번 반복
for i in range(1000):
    output = Neuron2(x, W)
    error = y - output
    W = W + x * 0.1 * error

    if i % 100 == 99:
        print('{}\t{}\t{}'.format(i+1, error, output))

# 출력 결과
100	[[0.0317629]]	[[0.9682371]]
200	[[0.01478064]]	[[0.98521936]]
300	[[0.00960499]]	[[0.990395]]
400	[[0.00710833]]	[[0.99289167]]
500	[[0.00564009]]	[[0.9943599]]
600	[[0.00467372]]	[[0.9953263]]
700	[[0.0039897]]	[[0.9960103]]
800	[[0.00348008]]	[[0.9965199]]
900	[[0.00308585]]	[[0.99691415]]
1000	[[0.00277168]]	[[0.9972283]]
# 가중치 추가
x = tf.random.normal((1, 3), 0, 1)
weights = tf.random.normal((1, 3), 0, 1)
bias = tf.zeros((1, 1))

y = tf.ones((1, ))

print("x\t: {}\nweights\t: {}\nbias\t: {}".format(x, weights, bias))

# 출력 결과
x	: [[ 1.2705061   1.3811893  -0.14885783]]
weights	: [[-1.1372662  -0.28991228 -1.5178484 ]]
bias	: [[0.]]


# 1000번 반복
for i in range(1000):
    output = Neuron2(x, weights, bias = bias)
    error = y - output
    weights = weights + x * 0.1 * error
    bias = bias + 1 * 0.1 * error

    if i % 100 == 99:
        print('{}\t{}\t{}'.format(i+1, error, output))

# 출력 결과
100	[[0.02399486]]	[[0.97600514]]
200	[[0.01155555]]	[[0.98844445]]
300	[[0.00759584]]	[[0.99240416]]
400	[[0.00565416]]	[[0.99434584]]
500	[[0.00450194]]	[[0.99549806]]
600	[[0.00373942]]	[[0.9962606]]
700	[[0.00319755]]	[[0.99680245]]
800	[[0.00279278]]	[[0.9972072]]
900	[[0.00247878]]	[[0.9975212]]
1000	[[0.00222832]]	[[0.9977717]]


# 초기값과 바뀐 weights값과 bias 값
print("x\t: {}\nweights\t: {}\nbias\t: {}".format(x, weights, bias))

# 출력 결과
x	: [[ 1.2705061   1.3811893  -0.14885783]]
weights	: [[ 1.0225528  2.058065  -1.7709007]]
bias	: [[1.6999679]]

  - AND 게이트

X = np.array([[1, 1], [1, 0], [0, 1], [0, 0]])
Y = np.array([[1], [0], [0], [0]])

W = tf.random.normal([2], 0, 1)
b = tf.random.normal([1], 0, 1)
b_x = 1

for i in range(2000):
    error_sum = 0

    for j in range(4):
        output = sigmoid(np.sum(X[j] * W) + b_x + b)
        error = Y[j][0] - output
        W = W + X[j] * 0.1 * error
        b = b + b_x * 0.1 * error
        error_sum += error
    
    if i % 200 == 0:
        print("Epoch {:4d}\tError Sum: {}".format(i, error_sum))

print('\n가중치\t:{}'.format(W))
print("편향\t: {}".format(b))

# 출력 결과
Epoch    0	Error Sum: [-2.8165922]
Epoch  200	Error Sum: [-0.11301513]
Epoch  400	Error Sum: [-0.06664746]
Epoch  600	Error Sum: [-0.04715659]
Epoch  800	Error Sum: [-0.03637875]
Epoch 1000	Error Sum: [-0.02955558]
Epoch 1200	Error Sum: [-0.02485914]
Epoch 1400	Error Sum: [-0.02143379]
Epoch 1600	Error Sum: [-0.01882876]
Epoch 1800	Error Sum: [-0.01678012]

가중치	:[6.9653535 6.968502 ]
편향	: [-11.627143]
# 평가
for i in range(4):
    print("X: {}  Y: {}  Output: {}".format(X[i], Y[i], sigmoid(np.sum(X[i] * W) + b)))

# 출력 결과
# 정답인 [1, 1]에 대해서만 0.9이상의 높은 output을 가짐
X: [1 1]  Y: [1]  Output: [0.9094314]
X: [1 0]  Y: [0]  Output: [0.00936108]
X: [0 1]  Y: [0]  Output: [0.00939032]
X: [0 0]  Y: [0]  Output: [8.92056e-06]

 

  - OR 게이트

X2 = np.array([[1, 1], [1, 0], [0, 1], [0, 0]])
Y2 = np.array([[1], [1], [1], [0]])

W2 = tf.random.normal([2], 0, 1)
b2 = tf.random.normal([1], 0, 1)
b_x = 1

for i in range(2000):
    error_sum = 0

    for j in range(4):
        output = sigmoid(np.sum(X2[j] * W2) + b_x + b2)
        error = Y2[j][0] - output
        W2 = W2 + X2[j] * 0.1 * error
        b2 = b2 + b_x * 0.1 * error
        error_sum += error
    
    if i % 200 == 0:
        print("Epoch {:4d}\tError Sum: {}".format(i, error_sum))
print('\n가중치\t:{}'.format(W2))
print("편향\t: {}".format(b2))

# 출력 결과
Epoch    0	Error Sum: [0.5757617]
Epoch  200	Error Sum: [-0.05437072]
Epoch  400	Error Sum: [-0.02711956]
Epoch  600	Error Sum: [-0.0179592]
Epoch  800	Error Sum: [-0.01337508]
Epoch 1000	Error Sum: [-0.01063495]
Epoch 1200	Error Sum: [-0.0088167]
Epoch 1400	Error Sum: [-0.00752456]
Epoch 1600	Error Sum: [-0.00655934]
Epoch 1800	Error Sum: [-0.00581255]

가중치	:[8.188078 8.187768]
편향	: [-4.628335]
# 평가
for i in range(4):
    print("X: {}  Y: {}  Output: {}".format(X2[i], Y2[i], sigmoid(np.sum(X2[i] * W2) + b2)))

# 출력 결과
# 정답인 [1, 1], [1, 0], [0, 1]에 대해서 0.97 이상의 높은 output 가짐
X: [1 1]  Y: [1]  Output: [0.99999213]
X: [1 0]  Y: [1]  Output: [0.9723406]
X: [0 1]  Y: [1]  Output: [0.97233236]
X: [0 0]  Y: [0]  Output: [0.00967647]

 

  - XOR 게이트

X3 = np.array([[1, 1], [1, 0], [0, 1], [0, 0]])
Y3 = np.array([[0], [1], [1], [0]])

W3 = tf.random.normal([2], 0, 1)
b3 = tf.random.normal([1], 0, 1)
b_x = 1

for i in range(2000):
    error_sum = 0

    for j in range(4):
        output = sigmoid(np.sum(X3[j] * W3) + b_x + b3)
        error = Y3[j][0] - output
        W3 = W3 + X3[j] * 0.1 * error
        b3 = b3 + b_x * 0.1 * error
        error_sum += error
    
    if i % 200 == 0:
        print("Epoch {:4d}\tError Sum: {}".format(i, error_sum))
print('\n가중치\t:{}'.format(W3))
print("편향\t: {}".format(b3))

# 출력 결과
Epoch    0	Error Sum: [1.3929144]
Epoch  200	Error Sum: [0.00576425]
Epoch  400	Error Sum: [0.00023448]
Epoch  600	Error Sum: [9.596348e-06]
Epoch  800	Error Sum: [6.556511e-07]
Epoch 1000	Error Sum: [5.364418e-07]
Epoch 1200	Error Sum: [5.364418e-07]
Epoch 1400	Error Sum: [5.364418e-07]
Epoch 1600	Error Sum: [5.364418e-07]
Epoch 1800	Error Sum: [5.364418e-07]

가중치	:[5.1282957e-02 1.1660159e-06]
편향	: [-1.0000018]
# 평가
for i in range(4):
    print("X: {}  Y: {}  Output: {}".format(X3[i], Y3[i], sigmoid(np.sum(X3[i] * W3) + b3)))

# 출력 결과
# 학습이 잘 되지 않음
X: [1 1]  Y: [0]  Output: [0.2791428]
X: [1 0]  Y: [1]  Output: [0.27914253]
X: [0 1]  Y: [1]  Output: [0.2689413]
X: [0 0]  Y: [0]  Output: [0.26894107]

 

# 레이어 추가
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense

np.random.seed(111)

X4 = np.array([[1, 1], [1, 0], [0, 1], [0, 0]])
Y4 = np.array([[0], [1], [1], [0]])

model = Sequential([Dense(units = 2, activation = 'sigmoid', input_shape = (2, )),
                    Dense(units = 1, activation = 'sigmoid'),])
model.compile(optimizer = tf.keras.optimizers.SGD(learning_rate = 0.1), loss = 'mse')

model.summary()

# 출력 결과
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_6 (Dense)             (None, 2)                 6         
                                                                 
 dense_7 (Dense)             (None, 1)                 3         
                                                                 
=================================================================
Total params: 9
Trainable params: 9
Non-trainable params: 0
_________________________________________________________________
history = model.fit(X4, Y4, epochs = 2000, batch_size = 1, verbose = 1)

# XOR 게이트 학습 결과
model.predict(X4)

# 출력 결과
# 실제 정답인 0, 1, 1, 0에 가깝게 나온 것을 확인
1/1 [==============================] - 0s 122ms/step
array([[0.20889895],
       [0.84853107],
       [0.84807545],
       [0.11541054]], dtype=float32)

 

  - XOR 게이트의 'LOSS' 시각화

plt.plot(history.history['loss'])

 

+ Recent posts