이전 글에서 random seed를 설정했음에도 불구하고, 실행할 때마다 결과가 동일하지 않은 경우가 있었습니다.
여기저기서 찾아본 결과,
제가 파악한 것으로는 일단 진행되는 세션에서 random seed가 정상적으로 동작은 했고, 그 랜덤의 시퀀스가 일정하게 유지되는 것으로 파악됩니다. 즉, c나 c++에서 random seed를 동일하게 설정하면, 난수를 생성할 때마다 같은 결과의 값들의 시퀀스로 이루어지는 것처럼, tensorflow에서도 동일한 것으로 확인됩니다.
그리고, random seed가 주피터노트북에서는 cell별로 따로 동작한다는 것으로 보입니다.
그래서 아래처럼 tf.random.set_seed로 random seed를 설정하게 되면, 모델을 생성(파라미터 초기화)할때마다 다른 파라미터를 얻게되는 것입니다.(참고로 tf.random.set_seed는 global random seed를 설정해주는 함수입니다. 아래에서 추가로 설명하겠습니다.)
import tensorflow as tf
tf.random.set_seed(2)
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(2,)),
tf.keras.layers.Dense(4, activation='tanh'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
print('모델의 parameters', model.get_weights())
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(2,)),
tf.keras.layers.Dense(4, activation='tanh'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
print('모델의 parameters', model.get_weights())
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(2,)),
tf.keras.layers.Dense(4, activation='tanh'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
print('모델의 parameters', model.get_weights())
위와 같이 3번의 같은 모델을 생성했지만, 초기화된 파라미터는 모두 다른 것을 볼 수 있습니다.
하지만, 모델을 생성하기 전에 명시적으로 random seed를 다시 설정해준다면,
tf.random.set_seed(2)
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(2,)),
tf.keras.layers.Dense(4, activation='tanh'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
print('모델의 parameters', model.get_weights())
매 실행마다 동일한 결과가 나오게 됩니다 !
tensorflow의 random seed에서 주의할 점이 있는데,
tensorflow에서의 random seed 설정은 사실 두 가지 seed에 의존한다는 것입니다. 하나가 global seed이고, 다른 하나는 operation-level seed입니다. operation-level seed는 initializer나 random.uniform과 같은 함수 내에서 따로 seed를 지정할 수 있는 것들을 의미합니다.(아래 tf.keras.initializers.GlorotUniform 참조)
따라서 두 가지 seed 설정에 따른 동작은 다음과 같습니다.
1. global seed와 operation seed를 모두 설정하지 않은 경우
-> 무작위로 seed를 선택해 사용됩니다.(매번 다른 결과)
2. global seed는 설정했지만, operation seed는 설정하지 않은 경우
-> global seed와 연관되어서 고유한 랜덤 시퀀스를 얻습니다. 즉, 매번 다른 결과이긴 하지만, 그 결과는 일정한 시퀀스를 따라서 얻게 됩니다. (seed를 세팅했을 때, 프로그램을 껏다켜도 seed가 동일하면 랜덤값의 순서는 동일한 것과 유사한 것으로 확인됩니다.)
3. operation seed는 설정했지만, global seed를 설정하지 않은 경우
-> 매번 다른 결과가 나타납니다.
4. global seed와 operation seed를 모두 설정한 경우, 두 seed를 정해서 같이 사용하게 되면 random 시퀀스를 결정해버리기 때문에 매번 동일한 결과를 얻을 수 있습니다.
공식문서의 예제를 가지고 다시 설명해보겠습니다.
global seed를 설정했음에도, 실행할 때마다 생성되는 난수가 다르다는 것을 볼 수 있습니다. 하지만 랜덤하게 생성되는 난수가 동일한 시퀀스를 가진다는 것을 확인할 수 있습니다.
이처럼 난수를 생성하기 전에 global seed를 설정하면, 난수의 시퀀스를 초기화하게 됩니다.
그리고 아직 @tf.function 데코레이터를 자세히 사용해보지는 않았는데, @tf.function는 프로그램을 다시 실행하는 것과 동일하게 결과가 나오게 됩니다. 따라서 아래와 같은 경우,
seed 설정을 처음에만 해주고, 난수 생성을 연속해서 4번한 것과 동일하지만, 같은 결과를 얻을 수 있습니다.
이제 operation seed를 설정해보도록 하겠습니다. 다시 global seed를 초기화를 해주고, operation seed를 설정해서 난수를 생성하면하고, 다시 global seed와 operation seed를 설정해서 동일한 코드를 수행하면 다음과 같은 결과를 얻을 수 있습니다.
하지만, @tf.function으로 감싸서 operation seed를 설정한다면, 결과는 조금 다르게 나옵니다. 이 경우에는 같은 operation function이 더 이상 같은 counter를 가지지 않기 때문이라고 합니다.
설명이 조금 부족할 수 있는데, 자세한 내용은 www.tensorflow.org/api_docs/python/tf/random/set_seed?hl=ko 를 참조하시길 바랍니다 !
위 내용에 따라서 tf.keras.initializers.GlorotUniform(seed=1)를 사용하더라도 global seed를 설정해주어야지 동일한 결과를 얻을 수 있습니다.
그래서 이전게시글에서 마지막에 아래와 같은 Model 함수로 사용을 했는데, tf.random.set_seed를 통해 global random seed를 설정해주고, operation seed(intializer의 seed)를 설정해주면 학습을 수행할 때마다 동일하게 초기화를 해서 동일한 결과를 얻을 수 있습니다... !(아래는 위에서 tf.random.set_seed(2)가 적용된 결과입니다.)
initializer = tf.keras.initializers.GlorotUniform(seed=1)
def Model(X, Y, epochs=10000, hidden_units=4, activation='tanh', opt='SGD'):
model = tf.keras.models.Sequential([
tf.keras.layers.Dense(hidden_units, activation=activation, kernel_initializer=initializer),
tf.keras.layers.Dense(1, activation='sigmoid', kernel_initializer=initializer)
])
if opt == 'SGD':
optimizer = tf.keras.optimizers.SGD(learning_rate=1.2)
elif opt == 'Adam':
optimizer = tf.keras.optimizers.Adam(learning_rate=1.2)
model.compile(optimizer=optimizer,
loss='binary_crossentropy',
metrics=['accuracy'])
history = model.fit(X, Y, epochs=epochs, verbose=0)
print(f'Result of activation:{activation}, optimizer:{opt}, num of hidden units:{hidden_units}')
model.evaluate(X, Y)
return model, history
아래 코드를 반복 실행해본 결과는 이전 결과와 정확하게 같았습니다.(사실 같은 initializer를 사용해서, 같은 counter를 공유한다고 생각해서 다르게 결과가 나올 줄 알았는데, 각 model마다 같은 counter를 사용하지 않는 것으로 보이며, @tf.function으로 감싼것과 같은 결과를 나타내고 있습니다.)
hidden_layer_sizes = [1, 2, 3, 4, 5, 20, 50]
for i, n_h in enumerate(hidden_layer_sizes):
plt.subplot(5, 2, i+1)
plt.title(f'Hidden Layer of size {n_h}')
m, h = Model(X, Y, epochs=5000, hidden_units=n_h, activation='tanh', opt='SGD')
plot_decision_boundary(lambda x: m.predict(x) > 0.5, X, Y)
아직 조금 익숙하지 않아서 헷갈리는 부분이 많지만, 아마 사용하면서 익혀보도록 하겠습니다.
'ML & DL > tensorflow' 카테고리의 다른 글
Cat Classification (2) : L-layers Neural Network (0) | 2020.11.15 |
---|---|
Cat Classification (1) : simple neural network (0) | 2020.11.15 |
Logistic Regression with 1 hidden layer(planar data classification) (0) | 2020.11.13 |
Logistic Regression 예제(iris classification) (0) | 2020.11.13 |
Linear Regression 간단한 예제 (4) | 2020.11.10 |
댓글