(tensorflow v2.1.0 - jupyter notebook)
이번에는 Coursera Deep Learning 특화과정 두번째 강의 1주차에서 실습한 initialization 선택에 따른 학습비교를 tensorflow에서 구현해보도록 하겠습니다.
2020/09/26 - [Coursera 강의/Deep Learning] - [실습] Initialization 초기화
초기화하는 방법에 따라서, Gradient Descent의 수렴 속도를 증가시킬 수 있고 training error를 더 낮게 학습할 수 있습니다.
실습에서는 numpy를 사용해서 1) 0으로 초기화, 2) random 초기화(np.random.randn), 3) he 초기화를 사용했습니다.
우선 테스트하기 전 필요한 함수들과 data를 읽어오겠습니다.
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import sklearn
import sklearn.datasets
%matplotlib inline
plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
def load_dataset():
np.random.seed(1)
train_X, train_Y = sklearn.datasets.make_circles(n_samples=300, noise=.05)
np.random.seed(2)
test_X, test_Y = sklearn.datasets.make_circles(n_samples=100, noise=.05)
# Visualize the data
plt.scatter(train_X[:, 0], train_X[:, 1], c=train_Y, s=40, cmap=plt.cm.Spectral);
return train_X, train_Y, test_X, test_Y
# load image dataset: blue/red dots in circles
train_X, train_Y, test_X, test_Y = load_dataset()
def plot_decision_boundary(model, X, y):
# Set min and max values and give it some padding
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
h = 0.01
# Generate a grid of points with distance h between them
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
# Predict the function value for the whole grid
Z = model(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# Plot the contour and training examples
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
plt.ylabel('x2')
plt.xlabel('x1')
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)
plt.show()
그리고 매번 동일한 랜덤값을 얻기 위한 global seed 설정과 operation seed로 사용할 값을 지정합니다.
def set_global_seed():
tf.random.set_seed(2)
SEED = 1
1. Neural Network Model 구현
테스트에 사용할 NN은 3개의 layer를 가진 NN입니다.
input -> 1st layer(10 units, relu) -> 2nd layer(5 units, relu) -> output layer(sigmoid) -> output 로 이루어져있습니다.
layers_dims = [train_X.shape[1], 10, 5, 1]
def Model(layers_dims, learning_rate=0.01, initialization='random'):
set_global_seed()
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Input(shape=(layers_dims[0],)))
if initialization=='zeros':
initializer=init_zeros
elif initialization=='random':
initializer=init_random
elif initialization=='he':
initializer=init_he
n_layer = len(layers_dims)
for i in range(1, n_layer):
if i != n_layer-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='sigmoid', kernel_initializer=initializer
))
model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate),
loss='binary_crossentropy',
metrics=['accuracy'])
# print('파라미터: ', model.get_weights()[0])
return model
각각의 초기화는 init_zero, init_random, init_he를 통해서 정해지고, 자세한 확인은 초기화 class를 만들고나서 해보도록 하겠습니다. bias_initializer는 설정하지 않으면 자동으로 0으로 초기화되므로 따로 설정하지 않았습니다.
그리고 모델을 생성할 때마다 동일하게 초기화하기 위해서 global seed를 모델을 생성하기 전에 지정해주도록 했습니다.
2. Initialization Class 구현
initializer는 tf.keras.initializers.Initializer를 상속하는 class를 생성해서 model의 layer를 추가할때, kernel_initializer의 인자로 사용할 것입니다.
zero initialization
class init_zeros(tf.keras.initializers.Initializer):
def __call__(self, shape, dtype=None):
return tf.zeros(shape, dtype=dtype, name=None)
zero 초기화 class구현은 간단합니다. __init__을 따로 정의할 필요는 없고, 해당 class를 사용할 때, __call__을 통해서 파라미터를 반환하게 되는데, 단순 0으로 초기화할 것이므로 tf.zeros를 통해서 0으로 초기화된 Tensor를 반환하면 됩니다. 직접 구현하지 않고, tf.keras.initializers.Zeros()를 사용해도 됩니다 !
random initialization
실습에서 random 초기화에 np.random.randn을 사용했습니다. 이 함수는 평균이 0, 분산이 1인 가우시안 정규분포로 이 값들 중에서 임의로 초기화를 하게 됩니다. 테스트를 정확하게 하기 위해서 tf.random.normal에 seed를 설정해주었습니다. 그리고 초기화한 파라미터에 10을 곱해주었습니다.
class init_random(tf.keras.initializers.Initializer):
def __init__(self, mean=0, stddev=1):
self.mean = mean
self.stddev = stddev
def __call__(self, shape, dtype=None):
return tf.random.normal(
shape, mean=self.mean, stddev=self.stddev, seed=SEED, dtype=dtype)*10
he initialization
'He initialization'은 논문 저자의 이름을 딴 초기화 방법으로 Xavier initialization과 유사합니다.
평균 0, 분산 1인 표준분포에서 \(\sqrt{\frac{2}{\text{dim of the prev layer}}}\)을 곱해주면 됩니다.
class init_he(tf.keras.initializers.Initializer):
def __init__(self, mean=0, stddev=1):
self.mean = mean
self.stddev = stddev
def __call__(self, shape, dtype=None):
return tf.random.normal(
shape, mean=self.mean, stddev=self.stddev, seed=SEED, dtype=dtype)*np.sqrt(2/shape[0])
이렇게 3개의 초기화 class를 만들어주었습니다. 각각의 모델을 생성하게 되면, 다음과 같이 초기화된 것을 확인할 수 있습니다.
Model(layers_dims, initialization='zeros')
Model(layers_dims, initialization='random')
Model(layers_dims, initialization='he')
이제부턴 print는 주석처리하고 진행하도록 합니다 !
3. 학습
1) zero initialization의 경우
zero_init_model=Model(layers_dims, initialization='zeros')
hist_zero = zero_init_model.fit(train_X, train_Y, epochs=15000, verbose=0)
# plot the cost
plt.plot(np.squeeze(hist_zero.history['loss']))
plt.ylabel('cost')
plt.xlabel('iterations')
plt.title("Learning rate =" + str(0.01))
plt.ylim([0, 1])
plt.show()
print(f"After training, Cost: {hist_zero.history['loss'][-1]} - Accuracy: {hist_zero.history['accuracy'][-1]}")
딥러닝에서 0으로 초기화하는 경우 네트워크는 break symmetry에 실패하기 때문에, 각 layer들의 unit이 모두 같은 값으로 학습이 되어서 모든 layer가 unit이 1개인 것처럼 동작하게 됩니다.
plt.title("Model with Zeros initialization")
axes = plt.gca()
axes.set_xlim([-1.5,1.5])
axes.set_ylim([-1.5,1.5])
plot_decision_boundary(lambda x: zero_init_model.predict(x) > 0.5, train_X, train_Y)
What you should remember:
- The weights W[l] should be initialized randomly to break symmetry.
- It is however okay to initialize the biases b[l] to zeros. Symmetry is still broken so long as W[l] is initialized randomly.
2) random initialization
random 초기화로 수행하면 어떻게 되는지 살펴보겠습니다.
random_init_model = Model(layers_dims, initialization='random')
random_model_hist = random_init_model.fit(train_X, train_Y, epochs=15000, verbose=0)
# plot the cost
plt.plot(np.squeeze(hist_zero.history['loss']))
plt.ylabel('cost')
plt.xlabel('iterations')
plt.title("Learning rate =" + str(0.01))
plt.ylim([0, 1])
plt.show()
print(f"After training, Cost: {hist_zero.history['loss'][-1]} - Accuracy: {hist_zero.history['accuracy'][-1]}")
zero_init_model.evaluate(test_X, test_Y)
이전보다는 꽤 괜찮은 결과를 얻었습니다.
plt.title("Model with Random initialization")
axes = plt.gca()
axes.set_xlim([-1.5,1.5])
axes.set_ylim([-1.5,1.5])
plot_decision_boundary(lambda x: random_init_model.predict(x) > 0.5, train_X, train_Y)
3) He initialization
마지막으로 he initialization을 사용해서 학습해보도록 하겠습니다.
he_init_model = Model(layers_dims, initialization='he')
hist_he = he_init_model.fit(train_X, train_Y, epochs=15000, verbose=0)
# plot the cost
plt.plot(np.squeeze(hist_he.history['loss']))
plt.ylabel('cost')
plt.xlabel('iterations')
plt.title("Learning rate =" + str(0.01))
plt.ylim([0, 1])
plt.show()
print(f"After training, Cost: {hist_he.history['loss'][-1]} - Accuracy: {hist_he.history['accuracy'][-1]}")
he_init_model.evaluate(test_X, test_Y)
99%의 training 정확도와 94%의 test 정확도를 얻었습니다. 이전과 비교하면 확실히 좋은 결과를 얻게 되었습니다. 아래 decision boundary를 확인해도 꽤 정확한 것을 볼 수 있습니다.
plt.title("Model with He initialization")
axes = plt.gca()
axes.set_xlim([-1.5,1.5])
axes.set_ylim([-1.5,1.5])
plot_decision_boundary(lambda x: he_init_model.predict(x) > 0.5, train_X, train_Y)
3개의 초기화 방법에 대한 학습 결과 비교(동일한 iteration, learning rate)
Model | Train accuracy | Problem/Comment |
3-layer NN with zeros initialization | 45.3% | fails to break symmetry |
3-layer NN with large random initialization | 74.6% | too large weights |
3-layer NN with He initialization | 99.6% | recommended method |
확실히 초기화를 어떻게 하느냐에 따라서 학습에 많은 영향을 미치는 것으로 확인이 됩니다.
keras에서는 초기화 방법을 설정하지 않으면 기본적으로 tf.kears.initializiers.GlorotNormal이 적용되는데, 이것은 Xavier normal initilizer를 의미합니다.
'ML & DL > tensorflow' 카테고리의 다른 글
batch GD (with momentum, adam) 비교 (0) | 2020.11.16 |
---|---|
Regularization 적용에 따른 학습 비교 (0) | 2020.11.16 |
Cat Classification (2) : L-layers Neural Network (0) | 2020.11.15 |
Cat Classification (1) : simple neural network (0) | 2020.11.15 |
tensorflow에서 random seed 설정 (1) | 2020.11.14 |
댓글