(tensorflow v2.3.0)
Coursera Deep Learning 특화과정에 실습으로 진행했던 SIGNS dataset을 가지고, 위 이미지와 같이 분류를 해보도록 할 것입니다. Coursera 실습은 tensorflow 1로 진행되는데, 2버전에 맞추어서 한 번 구현해보도록 하겠습니다.
신경망은 이전에 고양이 분류와 같이 입력을 flatten해주고, 2개의 hidden layer를 갖도록 할 것이고, 우리가 구현해야 할 신경망의 구조는 다음과 같습니다.
Input -> linear(25 units) -> relu -> linear(12 units) -> relu -> softmax(6 units)
1. Dataset 준비
import tensorflow as tf
import numpy as np
import h5py
import matplotlib.pyplot as plt
%matplotlib inline
def load_dataset():
train_dataset = h5py.File('train_signs.h5', "r")
train_set_x_orig = np.array(train_dataset["train_set_x"][:]) # your train set features
train_set_y_orig = np.array(train_dataset["train_set_y"][:]) # your train set labels
test_dataset = h5py.File('test_signs.h5', "r")
test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # your test set features
test_set_y_orig = np.array(test_dataset["test_set_y"][:]) # your test set labels
classes = np.array(test_dataset["list_classes"][:]) # the list of classes
return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes
# Loading the dataset
X_train_orig, Y_train, X_test_orig, Y_test, classes = load_dataset()
# Example of a picture
index = 0
plt.imshow(X_train_orig[index])
print ("y = " + str(np.squeeze(Y_train_orig[index])))
dataset을 읽어들이고, 샘플 이미지를 살펴보면, 64x64x3의 값을 가지는 이미지라는 것을 확인할 수 있습니다.
그리고, flatten과 normalization 과정을 수행합니다.
# Flatten
X_train_flatten = X_train_orig.reshape(X_train_orig.shape[0], -1)
X_test_flatten = X_test_orig.reshape(X_test_orig.shape[0], -1)
# normalization
X_train = X_train_flatten / 255.
X_test = X_test_flatten / 255.
print ("number of training examples = " + str(X_train.shape[0]))
print ("number of test examples = " + str(X_test.shape[0]))
print ("X_train shape: " + str(X_train.shape))
print ("Y_train shape: " + str(Y_train.shape))
print ("X_test shape: " + str(X_test.shape))
print ("Y_test shape: " + str(Y_test.shape))
2. Model 구현
앞서 언급했듯이 2개의 hidden layer를 갖도록 하며, hidden layer들은 각각 25, 12개의 입력을 갖도록 할 것입니다.
# input -> linear(25) -> relu -> linear(12) -> relu -> softmax(6)
layers_dims = [12288, 25, 12, 6]
그리고, tensorflow로 모델을 구현하면 다음과 같습니다.
def Model(train_x, train_y, test_x, test_y, layers_dims,
learning_rate=0.0001, num_epochs=1500, batch_size=32):
tf.random.set_seed(2)
L = len(layers_dims)
costs = []
optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate)
initializer=tf.keras.initializers.glorot_uniform(seed=2)
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(train_x.shape[1],))
])
for i in range(1, L):
if i != L-1:
model.add(tf.keras.layers.Dense(layers_dims[i],
activation='relu',
kernel_initializer=initializer))
else:
model.add(tf.keras.layers.Dense(layers_dims[i],
activation='softmax',
kernel_initializer=initializer))
model.summary()
model.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
hist = model.fit(train_x, train_y, batch_size=batch_size, epochs=num_epochs, verbose=0)
for i in range(0, num_epochs, 5):
costs.append(hist.history['loss'][i])
if i % 100 == 0:
print(f"Cost after epoch {i}: {hist.history['loss'][i]}")
#plot the cost
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('epochs (per 5)')
plt.title('Learning rate ='+ str(learning_rate))
plt.show()
train_acc = hist.history['accuracy'][-1]
model.evaluate(test_x, test_y)
y_pred = model.predict(test_x)
test_acc = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(y_pred, 1), tf.cast(test_y, tf.int64)), tf.float32))
print (f"Training Accuracy: {train_acc*100}%")
print (f"Test Accuracy: {test_acc*100}%")
return model, hist
모델의 loss object는 'sparse_categorical_crossentropy'를 사용했습니다. output이 각 label이 될 확률을 구하는 softmax logistic regression이기 때문에, 예측값은 각 label에 해당하는 확률이 될 것입니다.
+) 'categorical_crossentropy'와는 차이가 있는데, 학습에 사용하는 y_true이 label값이라면 'sparse_categorical_crossentropy'를 사용하면되고, 만약 학습에 사용하는 y_true의 형태가 각 label의 확률(one_hot 처리를 했는 경우)이라면, 'categorical_crossentropy'를 사용해야 합니다. 'sparse_categorical_crossentropy'의 경우에는 loss를 구할 때, 예측한 y_pred가 각 label에 대한 확률이기 때문에 y_true를 one_hot시켜서 구하게 됩니다.
다음에 똑같은 dataset을 다른 방법으로 구현해 볼 예정인데, 그때 'categorical_crossentropy'를 사용해보겠습니다.
참조 : tf.keras.losses.sparse_categorical_crossentropy, tf.keras.losses.categorical_crossentropy
3. 학습
m1, h1 = Model(X_train, Y_train, X_test, Y_test, layers_dims)
기본 설정값(learning_rate=0.0001, num_epochs=1500, batch_size=32)으로 학습을 해보았습니다.
Coursera 실습과 동일한 hyperparameter설정으로 했는데, Train 정확도에서 큰 차이를 보이고 있습니다.(실습에서는 99%로 overfitting됨)
아마 random seed와 초기화 방법에 따라서 학습 결과가 조금씩 달라지는 것으로 추측됩니다. 여기서 operation seed는 초기화에 사용되는 random seed입니다.
그래서, global seed와 operation seed를 다르게 시험을 몇 번 해보았는데, 꽤 상이한 결과들이 많이 나왔습니다.
global seed와 operation seed를 바꾸어 주었더니, train 정확도가 100%가 나와서 완벽하게 overfitting된 모습을 볼 수 도 있었습니다. 그래도 1080개의 샘플을 통해서 이정도 학습이면, 꽤나 괜찮은 결과라고도 볼 수 있을 것 같은데... 전혀 새로운 사진들로 테스트해보면 어떨지 궁금하긴 하네요.
- learning rate 변경 실험(0.0005으로 변경)
첫번째 학습(learning_rate=0.0001, global seed=2, operation seed=2)에서 learning rate를 바꾸면 어떻게 되는지 궁금해져서 실험을 해보았습니다.
m2, h2 = Model(X_train, Y_train, X_test, Y_test, layers_dims, learning_rate=0.0005)
cost가 1.79에서 계속 머물면서, 학습이 잘 되지 않는 모습을 보여주고 있습니다.
4. L2 Regularization 적용
두 번째 학습(global seed=3, operation seed=2)에서 과적합된 모습을 보여주고 있는데, L2 Regularization을 적용하면 결과가 어떻게 될 지 시도해보겠습니다.
L2 Regularization을 적용한 모델은 아래처럼 구현하였습니다. 기본 lambda 값은 0.01입니다.
def Model_L2(train_x, train_y, test_x, test_y, layers_dims, learning_rate=0.0001,
num_epochs=1500, batch_size=32, lambd=0.01):
tf.random.set_seed(3)
L = len(layers_dims)
costs = []
optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate)
initializer=tf.keras.initializers.glorot_uniform(seed=0)
regularizer=tf.keras.regularizers.l2(lambd)
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(train_x.shape[1],))
])
for i in range(1, L):
if i != L-1:
model.add(tf.keras.layers.Dense(layers_dims[i],
activation='relu',
kernel_initializer=initializer,
kernel_regularizer=regularizer))
else:
model.add(tf.keras.layers.Dense(layers_dims[i],
activation='softmax',
kernel_initializer=initializer))
model.summary()
model.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
hist = model.fit(train_x, train_y, batch_size=batch_size, epochs=num_epochs, verbose=0)
for i in range(0, num_epochs, 5):
costs.append(hist.history['loss'][i])
if i % 100 == 0:
print(f"Cost after epoch {i}: {hist.history['loss'][i]}")
#plot the cost
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('epochs (per 5)')
plt.title('Learning rate ='+ str(learning_rate))
plt.show()
train_acc = hist.history['accuracy'][-1]
model.evaluate(test_x, test_y)
y_pred = model.predict(test_x)
test_acc = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(y_pred, 1), tf.cast(test_y, tf.int64)), tf.float32))
print (f"Training Accuracy: {train_acc*100}%")
print (f"Test Accuracy: {test_acc*100}%")
return model, hist
lambda값을 증가시키면서 학습해보도록 하겠습니다.
- lambda = 0.01
m3, h3 = Model_L2(X_train, Y_train, X_test, Y_test, layers_dims)
L2 Regularization을 적용하지 않았을 때(83.3%)보다 88.3%의 Test 정확도로 Test Dataset에서는 조금 더 성능이 좋아진 것 같습니다만, 여전히 Train 정확도가 99%로 매우 높고, Overfitting의 확률이 있어보입니다.
- lambda = 0.03
m4, h4 = Model_L2(X_train, Y_train, X_test, Y_test, layers_dims, lambd=0.03)
드라마틱한 결과는 없었고, 0.01일 때와 크게 다르지 않았습니다.
- lambda = 0.1
m5, h5 = Model_L2(X_train, Y_train, X_test, Y_test, layers_dims, lambd=0.1)
lambda값을 증가시킨다고 더 좋아지지는 않는 것 같습니다.
5. Dropout 적용
dropout을 적용하면 어떻게 될 지 궁금해서 L2 Regularization 대신, dropout을 적용해서 학습해보았습니다.
def Model_dropout(train_x, train_y, test_x, test_y, layers_dims, learning_rate=0.0001,
num_epochs=1500, batch_size=32, keep_prob=0.8):
tf.random.set_seed(3)
L = len(layers_dims)
costs = []
optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate)
initializer=tf.keras.initializers.glorot_uniform(seed=0)
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(train_x.shape[1],))
])
for i in range(1, L):
if i != L-1:
model.add(tf.keras.layers.Dense(layers_dims[i],
activation='relu',
kernel_initializer=initializer))
if keep_prob != 1.0:
model.add(tf.keras.layers.Dropout(1-keep_prob))
else:
model.add(tf.keras.layers.Dense(layers_dims[i],
activation='softmax',
kernel_initializer=initializer))
model.summary()
model.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
hist = model.fit(train_x, train_y, batch_size=batch_size, epochs=num_epochs, verbose=0)
for i in range(0, num_epochs, 5):
costs.append(hist.history['loss'][i])
if i % 100 == 0:
print(f"Cost after epoch {i}: {hist.history['loss'][i]}")
#plot the cost
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('epochs (per 5)')
plt.title('Learning rate ='+ str(learning_rate))
plt.show()
train_acc = hist.history['accuracy'][-1]
model.evaluate(test_x, test_y)
y_pred = model.predict(test_x)
test_acc = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(y_pred, 1), tf.cast(test_y, tf.int64)), tf.float32))
print (f"Training Accuracy: {train_acc*100}%")
print (f"Test Accuracy: {test_acc*100}%")
return model, hist
- keep_prob = 0.8
m6, h6 = Model_dropout(X_train, Y_train, X_test, Y_test, layers_dims)
Train 정확도 74.4%, Test 정확도 77.5%로 overfitting은 해결한 것 같습니다.
추가로, 위 결과에서 반복횟수를 2000으로 증가시켜서 한 번 테스트해보았습니다.
성능이 더 떨어졌습니다. cost가 요동치는 것으로 보아, 반복횟수보다는 learning rate에 의해서 overshooting이 발생한 것으로 추측됩니다.
마지막으로 learning_rate를 10분의1로 줄여서 테스트해보았습니다.
m8, h8 = Model_dropout(X_train, Y_train, X_test, Y_test, layers_dims, learning_rate=0.00001)
테스트 성능이 조금 더 향상되었습니다...!
강의에서 hyperparameter는 여러가지 시도해보는 것이라고 했는데, 이렇게 직접 실험을 통해서 여러가지를 시도해보니 확실히 와닿는 것 같네요. 하지만 아직 어떤 값이 직감적으로 더 괜찮을 지 감은 오지 않는 것 같습니다...
다음에는 같은 dataset을 가지고, Convolutional Neural Network를 사용해서 구현해보도록 하겠습니다.
'ML & DL > tensorflow' 카테고리의 다른 글
MNIST dataset 예제(ConvNet, VGG16) (0) | 2020.11.22 |
---|---|
Hand SIGNS 분류 예제 (2) - CNN 구조 사용 (0) | 2020.11.17 |
batch GD (with momentum, adam) 비교 (0) | 2020.11.16 |
Regularization 적용에 따른 학습 비교 (0) | 2020.11.16 |
Initialization에 따른 학습 비교 (0) | 2020.11.15 |
댓글