(tensorflow v2.3.0)
2020/11/17 - [ML & DL/tensorflow] - Hand SIGNS 분류 예제 (1)
Hand SIGNS 분류 예제 (1)
(tensorflow v2.3.0) Coursera Deep Learning 특화과정에 실습으로 진행했던 SIGNS dataset을 가지고, 위 이미지와 같이 분류를 해보도록 할 것입니다. Coursera 실습은 tensorflow 1로 진행되는데, 2버전에 맞추..
junstar92.tistory.com
이번에는 이전 게시글에서 사용했던 dataset으로 Convolutional Neural Network(ConvNet) 구조로 모델을 구현하고 학습을 해보려고 합니다.
그리고, 이전 게시글에서 언급했듯이 label data를 one_hot 처리를 해서 loss object로 'categorical entropy'를 사용할 것입니다.
1. Dataset 준비
준비과정은 이전과 유사하며, Y_train과 Y_test에 대해서 tf.one_hot 함수를 통해 각 라벨에 해당하는 확률로 변경시켜 줍니다. 즉, 각 라벨에 해당하는 열은 1이고 나머지 열은 0이 됩니다.
import tensorflow as tf import matplotlib.pyplot as plt import h5py import numpy as np import math 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 data (signs) X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = load_dataset() # Example of a picture index = 6 plt.imshow(X_train_orig[index]) print ("y = " + str(np.squeeze(Y_train_orig[index]))) X_train = X_train_orig.astype(np.float32)/255. X_test = X_test_orig.astype(np.float32)/255. Y_train = np.array(tf.one_hot(Y_train_orig, 6)) Y_test = np.array(tf.one_hot(Y_test_orig, 6)) 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))

Y_train과 Y_test가 각 라벨의 확률을 나타내기 때문에, (m, 6)의 형태로 나타납니다. 6은 라벨 class의 개수입니다.
그리고 mini-batch를 사용해서 최적화를 할 예정인데, 직접 mini_batch를 무작위로 섞는 함수를 사용합니다.
def random_mini_batches(X, Y, mini_batch_size = 64, seed = 0): m = X.shape[0] mini_batches = [] np.random.seed(seed) # Step 1: Shuffle (X, Y) permutation = list(np.random.permutation(m)) shuffled_X = X[permutation,:,:,:] shuffled_Y = Y[permutation,:] # Step 2: Partition (shuffled_X, shuffled_Y) num_complete_minibatches = math.floor(m/mini_batch_size) for k in range(0, num_complete_minibatches): mini_batch_X = shuffled_X[k * mini_batch_size:k * mini_batch_size + mini_batch_size,:,:,:] mini_batch_Y = shuffled_Y[k * mini_batch_size:k * mini_batch_size + mini_batch_size,:] mini_batch = (mini_batch_X, mini_batch_Y) mini_batches.append(mini_batch) # Handling the end case if m % mini_batch_size != 0: mini_batch_X = shuffled_X[num_complete_minibatches * mini_batch_size:m,:,:,:] mini_batch_Y = shuffled_Y[num_complete_minibatches * mini_batch_size:m,:] mini_batch = (mini_batch_X, mini_batch_Y) mini_batches.append(mini_batch) return mini_batches
오늘 두 가지의 방법으로 동일한 모델을 구현할 예정인데, 첫 번째 방법에서는 이 함수를 사용하고, 두 번째 방법에서는 이 함수를 사용하지 않고, mini-batch를 사용해서 학습해보도록 하겠습니다.
시작하기에 앞서서 구현해야될 ConvNet의 구조는 다음과 같습니다.
Conv2D(F=8, stride=1, padding='same') -> ReLU -> Max pool(filter=(8,8), strides=(8,8)
-> Conv2D(F=16, stride=1, padding='same') -> ReLU -> Max pool(filter=(4,4), strides=(4,4)
-> Flatten -> Softmax(6 units)
2-1. tf.keras.Model을 상속받는 Model Class 구현 및 학습
www.tensorflow.org/tutorials/quickstart/advanced?hl=ko
텐서플로 2.0 시작하기: 전문가용 | TensorFlow Core
Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 공식 영문 문서의 내용과 일치하지 않을 수
www.tensorflow.org
위 튜토리얼에서 알려주는 방법을 토대로 구현을 해보았습니다.
class ConvNet(tf.keras.Model): def __init__(self): super(ConvNet, self).__init__() self.conv1 = tf.keras.layers.Conv2D(8, (4, 4), strides=(1,1), padding='same', activation='relu', kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0)) self.maxpool1 = tf.keras.layers.MaxPool2D(pool_size=(8,8), strides=(8,8), padding='same') self.conv2 = tf.keras.layers.Conv2D(16, (2, 2), strides=(1,1), padding='same', activation='relu', kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0)) self.maxpool2 = tf.keras.layers.MaxPool2D(pool_size=(4,4), strides=(4,4), padding='same') self.flatten = tf.keras.layers.Flatten() self.d1 = tf.keras.layers.Dense(6, activation='softmax', kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0)) def call(self, x): x = self.conv1(x) x = self.maxpool1(x) x = self.conv2(x) x = self.maxpool2(x) x = self.flatten(x) x = self.d1(x) return x
class 생성자를 통해서 layer들을 만들어주고, call을 통해 를 계산합니다. 이 예측값은 각 라벨의 확률을 의미합니다.
다음으로는 학습을 진행할 함수를 구현합니다.
# Model def Model_1(X_train, Y_train, X_test, Y_test, learning_rate=0.009, num_epochs=100, minibatch_size=64, print_cost=True): tf.random.set_seed(4) (m, n_H0, n_W0, n_C0) = X_train.shape seed = 3 costs = [] model = ConvNet() loss_object = tf.keras.losses.CategoricalCrossentropy() optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate) train_loss = tf.keras.metrics.Mean(name='train_loss') train_accuracy = tf.keras.metrics.CategoricalCrossentropy(name='train_accuracy') for epoch in range(num_epochs): minibatch_cost=0 num_minibatches = int(m/minibatch_size) seed = seed + 1 minibatches = random_mini_batches(X_train, Y_train, minibatch_size, seed) for minibatch in minibatches: (minibatch_X, minibatch_Y) = minibatch with tf.GradientTape() as tape: pred = model(minibatch_X) cost = loss_object(minibatch_Y, pred) grads = tape.gradient(cost, model.trainable_variables) optimizer.apply_gradients(zip(grads, model.trainable_variables)) train_loss(cost) train_accuracy(minibatch_Y, pred) minibatch_cost += cost / num_minibatches if print_cost and epoch % 5 == 0: print(f'Cost after epoch {epoch}: {minibatch_cost}') costs.append(minibatch_cost) # plot the cost plt.plot(np.squeeze(costs)) plt.ylabel('cost') plt.xlabel('iterations (per tens)') plt.title("Learning rate =" + str(learning_rate)) plt.show() # training set y_pred = model(X_train) y_pred = tf.math.argmax(y_pred, 1) y_true = tf.math.argmax(Y_train, 1) train_acc = tf.math.reduce_mean(tf.cast(tf.equal(y_pred, y_true), tf.float32)) y_pred = model(X_test) y_pred = tf.math.argmax(y_pred, 1) y_true = tf.math.argmax(Y_test, 1) test_acc = tf.math.reduce_mean(tf.cast(tf.equal(y_pred, y_true), tf.float32)) print(f'Train acc : {train_acc}') print(f'Test acc : {test_acc}') return model
이전글에서 구현하던 방식과는 다르게 model class를 직접 구현하고, 직접 예측 확률값과 loss를 구한 다음에 tf.GradientTape()를 사용해 연산을 기록하고 자동으로 미분항를 구하고 있습니다.
그리고 학습을 진행합니다. learning_rate(0.009), epochs(100), minibatch_size(64)는 기본값으로 사용했습니다.
m = Model_1(X_train, Y_train, X_test, Y_test)

이전 게시글에서 Regularization을 적용했을 때와 유사한 결과가 나오고 있습니다. 아직 overfitting이 되었을 가능성이 있어 보이기도 하네요.
모델의 구성을 살펴보면 아래처럼 구성된 것을 볼 수 있습니다.

2-2. keras sequential model을 통한 구현 및 학습
두 번째 방법으로 구현을 해보도록 하겠습니다. 이 방법은 이전 게시글들에서 사용하던 방법과 동일합니다.
def Model_2(X_train, Y_train, X_test, Y_test, learning_rate=0.009, num_epochs=100, minibatch_size=64): tf.random.set_seed(0) seed = 3 (m, n_H0, n_W0, n_C0) = X_train.shape n_y = Y_train.shape[1] costs = [] # initialize parameters #parameters = initialize_parameters() # setting optimizer optimizer = tf.optimizers.Adam(learning_rate=learning_rate) # CONV2D -> RELU -> MAXPOOL -> CONV2D -> RELU -> MAXPOOL -> FLATTEN -> FULLYCONNECTED model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(8, (4, 4), strides=(1,1), padding="SAME", activation='relu', input_shape=(64, 64, 3), kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0)), tf.keras.layers.MaxPool2D(pool_size=(8,8), strides=(8,8), padding="SAME"), tf.keras.layers.Conv2D(16, (2, 2), strides=(1,1), padding="SAME", activation='relu', kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0)), tf.keras.layers.MaxPool2D(pool_size=(4,4), strides=(4,4), padding="SAME"), tf.keras.layers.Flatten(), tf.keras.layers.Dense(6, activation='softmax', kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0)) ]) model.summary() model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy']) hist = model.fit(X_train, Y_train, epochs=num_epochs, batch_size=minibatch_size, verbose=1) costs = hist.history['loss'] for epoch in range(0, num_epochs, 5): print(f'Cost after epoch {epoch}: {costs[epoch]}') # plot the cost plt.plot(np.squeeze(costs)) plt.ylabel('cost') plt.xlabel('iterations (per tens)') plt.title("Learning rate =" + str(learning_rate)) plt.show() train_acc = hist.history['accuracy'][-1] y_pred = model.predict(X_test) y_pred = tf.math.argmax(y_pred, 1) y_true = tf.math.argmax(Y_test, 1) test_acc = tf.math.reduce_mean(tf.cast(tf.equal(y_pred, y_true), tf.float32)) print(f'Train acc : {train_acc}') print(f'Test acc : {test_acc}') return model
그리고 학습을 진행합니다.
m2 = Model_2(X_train, Y_train, X_test, Y_test)

이전 게시글과 유사한 현상이 ConvNet에서도 발생했습니다. 아마, 초기화가 적절하지 않았기 때문에 발생했다고 추측하고 global seed를 0에서 2로 변경해서 다시 학습을 진행해보았습니다.
global seed = 2(tf.random.set_seed(2))
m3 = Model_2(X_train, Y_train, X_test, Y_test)

결과가 정상적인 성능으로 나왔습니다. class로 구현한 것보다 조금 덜 overfitting해보이며, Test 성능은 거의 동일하게 나오고 있습니다.
- Regularization 적용
두 번째 모델을 가지고 L2 Regularization을 적용해보도록 하겠습니다.
def Model_2_Reg(X_train, Y_train, X_test, Y_test, learning_rate=0.009, num_epochs=100, minibatch_size=64, lambd=0.01): tf.random.set_seed(2) seed = 3 (m, n_H0, n_W0, n_C0) = X_train.shape n_y = Y_train.shape[1] costs = [] # initialize parameters #parameters = initialize_parameters() # setting optimizer optimizer = tf.optimizers.Adam(learning_rate=learning_rate) # setting regularizer regularizer = tf.keras.regularizers.L2(lambd) # CONV2D -> RELU -> MAXPOOL -> CONV2D -> RELU -> MAXPOOL -> FLATTEN -> FULLYCONNECTED model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(8, (4, 4), strides=(1,1), padding="SAME", activation='relu', input_shape=(64, 64, 3), kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0), kernel_regularizer=regularizer), tf.keras.layers.MaxPool2D(pool_size=(8,8), strides=(8,8), padding="SAME"), tf.keras.layers.Conv2D(16, (2, 2), strides=(1,1), padding="SAME", activation='relu', kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0), kernel_regularizer=regularizer), tf.keras.layers.MaxPool2D(pool_size=(4,4), strides=(4,4), padding="SAME"), tf.keras.layers.Flatten(), tf.keras.layers.Dense(6, activation='softmax', kernel_initializer=tf.keras.initializers.GlorotUniform(seed=0), kernel_regularizer=regularizer) ]) model.summary() model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy']) hist = model.fit(X_train, Y_train, epochs=num_epochs, batch_size=minibatch_size, verbose=1) costs = hist.history['loss'] for epoch in range(0, num_epochs, 5): print(f'Cost after epoch {epoch}: {costs[epoch]}') # plot the cost plt.plot(np.squeeze(costs)) plt.ylabel('cost') plt.xlabel('iterations (per tens)') plt.title("Learning rate =" + str(learning_rate)) plt.show() train_acc = hist.history['accuracy'][-1] y_pred = model.predict(X_test) y_pred = tf.math.argmax(y_pred, 1) y_true = tf.math.argmax(Y_test, 1) test_acc = tf.math.reduce_mean(tf.cast(tf.equal(y_pred, y_true), tf.float32)) print(f'Train acc : {train_acc}') print(f'Test acc : {test_acc}') return model
m4 = Model_2(X_train, Y_train, X_test, Y_test)

lambda의 값이 너무 커서 파라미터의 영향을 너무 감소시켜버린 것 같습니다.
- lambda = 0.001
m5 = Model_2_Reg(X_train, Y_train, X_test, Y_test, lambd=0.001)

lambda 값을 감소시켰더니, 테스트 성능이 조금 증가했습니다만, 학습 성능도 증가해버렸네요. Regularization의 효과가 쪼금은 나타난 것 같습니다. 그렇지만 dataset의 크기가 작은 편이기 때문에 overfitting을 줄이기에는 조금 힘들어 보이긴 하네요.. 이 부분은 나중에 성능을 확인해볼 수 있는 큰 dataset이 있을 때, 다시 비교해보도록 하겠습니다.
마지막으로 epochs를 200으로 증가시켜서 학습을 해보았습니다.
m6 = Model_2_Reg(X_train, Y_train, X_test, Y_test, num_epochs=200, lambd=0.001)

거의 90%의 테스트 성능을 달성했습니다 !
'ML & DL > tensorflow' 카테고리의 다른 글
Hand SIGNS 분류 예제 (3) - ResNet (0) | 2020.11.24 |
---|---|
MNIST dataset 예제(ConvNet, VGG16) (0) | 2020.11.22 |
Hand SIGNS 분류 예제 (1) (0) | 2020.11.17 |
batch GD (with momentum, adam) 비교 (0) | 2020.11.16 |
Regularization 적용에 따른 학습 비교 (0) | 2020.11.16 |
댓글