딥러닝 공부

골빈해커의 3분 딥러닝 텐서플로맛_ch.5 텐서보드와 모델 재사용 (텐서플로우 2.0)

Woonys 2021. 7. 31. 15:28
반응형

이번 챕터는 간단하게 쉬어가자는(?) 본문 내용과 달리 생각 외로 시간을 많이 썼던 챕터였다. 그도 그럴 것이 1) colab으로 돌리면서 2) 버전 2.5로 돌리면서 차이가 많이 났기 때문이다. 하지만 용케 잘 풀어냈으니! 잘 따라오기만 하면 된다. 편하게 가보자!

 

이번 챕터에서는 학습시킨 모델을 저장하고 다시 불러와 사용하는 방법, 그리고 텐서플로우의 장점이라 불리우는 텐서보드를 배운다. 텐서보드는 내가 모델에 학습시킨 이력을 시각화해 추적할 수 있는 그래프 보드라고 생각하면 되겠다.

 

5.1 학습 모델 저장하고 재사용하기

시작이니 쌈빡하게 import부터 시작하자.

import tensorflow as tf
import numpy as np

이번에는 앞 챕터에서 풀었던 예제인 조류/포유류 분류 문제를 다시 풀어보겠다. 간략하게 하기 위해 x_data, y_data를 한 넘파이 array로 합친 뒤 분리하겠다.

 

# data = x_data, y_data
data = np.transpose([
	[0,0,1,0,0],
    [1,0,0,1,0],
    [1,1,0,0,1],
    [0,0,1,0,0],
    [0,0,1,0,0],
    [0,1,0,0,1]
])

x_data = np.transpose(data[0:2]) # 0열, 1열까지 x_data
y_data = np.transpose(data[2:]) #2열부터 4열까지 y_data
x_data = x_data.astype(float) #부동소수화
y_data = y_data.astype(float) #부동소수화

print(x_data, y_data)

이를 출력하면 다음과 같다.

 

x_data = [[0. 0.] [1. 0.] [1. 1.] [0. 0.] [0. 0.] [0. 1.]]

y_data = [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.] [1. 0. 0.] [1. 0. 0.] [0. 0. 1.]]

 

이번에는 모델을 만들어보자. 객체를 이용해 모델에 대한 함수를 제작해본다. (왜 객체로 만드는지는 뒤에서 알 수 있다.)

#모델 객체 생성

def create_model():
	model = tf.keras.models.Sequential([
    	tf.keras.layers.Dense(10, activation = 'relu', kernel_initializer = 'random_uniform', bias_initializer = 'zeros'),
        tf.keras.layers.Dense(20, activation = 'relu', kernel_initializer = 'random_uniform', bias_initializer = 'zeros'),
        tf.keras.layers.Dense(3, activation = 'softmax', kernel_initializer = 'random_uniform', bias_initializer = 'zeros')
    ])
    model.compile(optimizer = 'adam',
    	loss = tf.losses.CategoricalCrossentropy(from_logits=True),
        metrics = (['accuracy'])
	return model
    
model = create_model()

model.fit(x_data, y_data, epochs=100)

출력값은 아래와 같다.

usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/backend.py:4870: UserWarning: "`categorical_crossentropy` received `from_logits=True`, but the `output` argument was produced by a sigmoid or softmax activation and thus does not represent logits. Was this intended?" '"`categorical_crossentropy` received `from_logits=True`, but '

Epoch 1/100 1/1 [==============================] - 0s 439ms/step - loss: 1.0986 - accuracy: 0.6667
Epoch 2/100 1/1 [==============================] - 0s 4ms/step - loss: 1.0983 - accuracy: 0.5000
Epoch 3/100 1/1 [==============================] - 0s 12ms/step - loss: 1.0979 - accuracy: 0.5000
Epoch 4/100 1/1 [==============================] - 0s 9ms/step - loss: 1.0975 - accuracy: 0.5000

...

Epoch 100/100 1/1 [==============================] - 0s 6ms/step - loss: 0.9798 - accuracy: 0.8333
<tensorflow.python.keras.callbacks.History at 0x7fb8750ad390>

 

최종적으로 loss: 1.0986 => 0.9798, accuracy: 0.6667 => 0.8333으로 학습이 진행된 것을 볼 수 있다.

 

그리고 맨 아랫줄을 보면 학습시킨 결과가 callbacks.History 메소드로 임시 메모리에 저장된 것을 확인할 수 있다.

<tensorflow.python.keras.callbacks.History at 0x7fb8750ad390>

 

자, 여기서 epoch 100번을 돌려 학습시킨 신경망을 고대로 가져다가 다른 곳에서 재학습한다고 생각해보자. 그럼 모델을 저장해야겠지? 아래에서는 저장하는 방법에 대해 써보겠다. 여기서는 체크포인트를 이용한다.

import os

checkpoint_path = 'training_1/cp.ckpt' #training_1이라는 디렉토리에 cp.ckpt라는 파일로 저장
checkpoint_dir = os.path.dirname(checkpoint_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath = checkpoint_path,
			save_weights_only = True,
            verbose = 1 # verbose는 학습 과정을 볼 수 있음 (epoch 횟수 및 loss, accuracy 변화가 아웃풋으로 출력)

#위에서 학습시켰던 모델 불러 오기
model.fit(x_data,
	y_data,
    epochs=10,
    callbacks = [cp_callback]) #위에서 학습시켰던 모델 불러오기

코드를 보면 알겠지만 callback이라는 메소드를 활용해서 이전에 학습시킨 결과를 불러오는 것을 알 수 있다. 그런데 이상하다? 위에서 썼던 변수를 끌어오는 게 아닌데 어떻게 불러올 수 있지? 내 뇌피셜이지만 앞서 callbacks.History 메소드로 임시 메모리에 저장되어 있던 애가 cp_callback 변수 지정시 callbacks.ModelCheckpoint 메소드로 checkpoint_path 내에 저장되는 것으로 보인다.

 

무튼, 출력값을 보자.

 

Epoch 1/10 1/1 [==============================] - 0s 6ms/step - loss: 0.9766 - accuracy: 0.8333
Epoch 00001: saving model to training_2/cp.ckpt

Epoch 2/10 1/1 [==============================] - 0s 6ms/step - loss: 0.9734 - accuracy: 0.8333
Epoch 00002: saving model to training_2/cp.ckpt

Epoch 3/10 1/1 [==============================] - 0s 6ms/step - loss: 0.9702 - accuracy: 0.8333

...

Epoch 10/10 1/1 [==============================] - 0s 5ms/step - loss: 0.9457 - accuracy: 0.8333
Epoch 00010: saving model to training_2/cp.ckpt
<tensorflow.python.keras.callbacks.History at 0x7fb874fc4090>

먼저 가장 첫줄부터 보자. loss값이 0.9766, accuracy가 0.8333부터 시작한다. 이전에 epoch 100번 돌렸을 떄 output은 loss: 0.9798 - accuracy: 0.8333 임을 보면, 이전에 학습했던 모델을 고대로 불러와 연속적으로 학습하고 있음을 알 수 있다.

 

이어서 아랫줄을 보면 Epoch 00001: saving model to training_2/cp.ckpt이라고 나온다. 잘 저장이 됐다는 뜻이지.

 

요약해서 결과를 보자. 앞서 신경망 모델을 객체로 만들었다. 새 모델과 저장된 모델 사이 loss와 accuracy를 비교해보자.

 

먼저 새 모델이다. 앞서 만들었던 모델 객체를 활용해 새 모델을 만든다.

 

model_new = create_model()
loss, acc = model_new.evaluate(x_data, y_data, verbose=2)

print("훈련되지 않은 모델의 정확도: {:5.2f}%".format(100*acc))

 

1/1 - 0s - loss: 1.0985 - accuracy: 1.0000 훈련되지 않은 모델의 정확도: 100.00%

음..accuracy가 뭔가 좀 이상한데..;; 일단 loss 값을 보면 학습이 전혀 되지 않았음을 알 수 있다.

 

이어서 앞서 저장했던 모델을 새 모델에 넣어보자.

 

model_new.load_weights(checkpoint_path) #이전에 학습했던 웨이트를 체크포인트에서 불러오기

loss, acc = model1.evaluate(x_data, y_data, verbose=2)
1/1 - 0s - loss: 0.9420 - accuracy: 0.8333

loss가 줄어들고 accuracy는 ...그래 뭐 아무튼 불러왔다!

 

5.2 텐서보드

5.1에서 내용이 엄청 길어져 따로 쓸까 했는데 이왕 쓴 김에 연달아 써보겠다.

 

텐서보드 관련해서 구글링하면 좋은 자료들이 많으니 여기서는 간단히 왜 써야하는지 정도 요약하고 바로 코드 리뷰로 넘어간다.

 

텐서보드를 쓰는 이유는 아래와 같이 크게 3가지다.

  1. 복잡한 모델 or 데이터로 학습할 때: 말 그대로 복잡한 과정이다 보니 내부에서 학습이 어떻게 일어나고 있는지를 뜯어볼 필요가 있다.
  2. 실험에서 통제 변인을 조작할 때: 학습 실험을 돌릴 때 Dense layer를 추가하거나 Optimizer를 바꾸는 등 여러 변인을 조작하고 그에 따른 학습결과를 관찰한다. 이때 각 변인이 결과에 어떤 영향을 미치는지, 각 변인 간 상관관계는 어떻게 되는지 등을 시각화해주는 게 텐서보드다.
  3. 신경망 내부 node 상태 & weight 분포 파악: 2와 비슷한데, 신경망 내부에서 어떤 상황이 벌어지고 있는지 더 미시적으로 뜯어보는데도 텐서보드가 필요하다.

사실 요약하면 딱 한 가지 이유다. 실험과정을 시각화해서 보기 위해.

 

바로 코드로 넘어간다.

 

import datetime #텐서보드 버전관리를 년/월/일로 하기 위해 datetime import

log_dir = 'logs/my_board/' + datetime.datetime.now().strftime('%Y%m%d-%H%M%S')

#앞에서 학습시켰던 결과를 불러오는 콜백을 이어받아 텐서보드에서
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir = log_dir, histogram_freq = 1)
#학습시키기

model.fit(x_data, y_data,
		epochs = 100,
        	callbacks = [tensorboard_callback])

출력 결과는 아래와 같다.

 

Epoch 1/100
1/1 [==============================] - 0s 18ms/step - loss: 0.9420 - accuracy: 0.8333
Epoch 2/100
1/1 [==============================] - 0s 39ms/step - loss: 0.9383 - accuracy: 0.8333
Epoch 3/100
1/1 [==============================] - 0s 5ms/step - loss: 0.9345 - accuracy: 0.8333

...

Epoch 100/100
1/1 [==============================] - 0s 8ms/step - loss: 0.5132 - accuracy: 0.8333
<tensorflow.python.keras.callbacks.History at 0x7fb873e95c90>

앞서 5.1에서 학습시켰던 모델을 불러와 고대로 다시 학습을 돌린다. 결과는 위와 같이 나온다. 여기서 학습시킨 결과는 텐서보드 콜백에 저장된다.

 

이제 대망의 텐서보드를 불러와보자.

 

%load_ext tensorboard

%tensorboard --logdir {log_dir} #위의 코드에서 해당 시간에 학습돌린 결과를 텐서보드로 불러오기

출력값을 보자. 이제 텐서보드 웹 창이 쫜!하고 나타나면 되는데...

 


Reusing TensorBoard on port 6006 (pid 915), started 0:29:49 ago. (Use '!kill 915' to kill it.)

403. That’s an error.

That’s all we know.


잉? 근데 결과가 이상하다.

 

기나긴 삽질 끝에 알아낸 방법은 아래와 같다. 아마 코랩과 같이 노트북으로 돌리다 보니 발생한 결과인듯하다.

빠르게 문제를 해결한 건 유튜브 링크를 통해서였는데 이 글을 쓰면서 다시 서치해보니 텐서플로 공식 사이트에서 아예 어떻게 하면 되는지 다 소개해놨더라.. 자세한 설명은 아래 링크를 들어가서 배우도록 하자.

 

이 글에서는 첫번째 링크인 유튜브에서 해결한 방법을 소개한다. 인도인이 얘기해주니 억양에 잘 귀기울이도록 하자 (근데 보니까 텐서플로 공식 사이트에서랑 큰 차이가 없는듯하니 공식 사이트 보는 게 더 빠를듯)

 

코드는 링크 아래에 있다.

 

https://www.tensorflow.org/tensorboard/tensorboard_in_notebooks

 

노트북에서 TensorBoard 사용  |  TensorFlow

TensorBoard는 Colab 및 Jupyter 와 같은 노트북 환경에서 직접 사용할 수 있습니다. 이는 결과를 공유하고, TensorBoard를 기존 워크 플로에 통합하고, 로컬에 아무것도 설치하지 않고 TensorBoard를 사용하는

www.tensorflow.org

 

#텐서보드에서 notebook import

from tensorboard import notebook
notebook.list()

 

#텐서보드 불러오기
!tensorboard dev upload \
  --logdir {log_dir} \
  --name "(My latest experiment" \
  --one_shot

이를 실행하면 아래와 같이 출력된다. continue 창이 뜨면서 y/n을 선택하는 란에 yes를 입력하자. 

 

2021-07-27 12:57:38.309986: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0

***** TensorBoard Uploader *****

This will upload your TensorBoard logs to https://tensorboard.dev/ from
the following directory:

logs/my_board/20210727-121410

This TensorBoard will be visible to everyone. Do not upload sensitive
data.

Your use of this service is subject to Google's Terms of Service
<https://policies.google.com/terms> and Privacy Policy
<https://policies.google.com/privacy>, and TensorBoard.dev's Terms of Service
<https://tensorboard.dev/policy/terms/>.

This notice will not be shown again while you are logged into the uploader.
To log out, run `tensorboard dev auth revoke`.

Continue? (yes/NO) yes

 

가장 마지막 줄의 링크를 누르면 텐서보드가 나타난다.

Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=373649185512-8v619h5kft38l4456nm2dj4ubeqsrvh6.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=tjZBXczz0eA33eu0Whur13DkpVX5Mm&prompt=consent&access_type=offline
Enter the authorization code: 4/1AX4XfWjrpxfLIQndVwsLvRSxiLaLDm2_Qqth1hOW9mkh5aS0-dGElLhPUNA

Data for the "text" plugin is now uploaded to TensorBoard.dev! Note that uploaded data is public. If you do not want to upload data for this plugin, use the "--plugins" command line argument.

New experiment created. View your TensorBoard at: https://tensorboard.dev/experiment/Y5DaPC8tQYaOZlJodyoi6w/

[2021-07-27T12:58:46] Started scanning logdir.
[2021-07-27T12:58:47] Total uploaded: 200 scalars, 600 tensors (288.3 kB), 1 binary objects (37.9 kB)
[2021-07-27T12:58:47] Done scanning logdir.


Done. View your TensorBoard at https://tensorboard.dev/experiment/Y5DaPC8tQYaOZlJodyoi6w/
[ ]

 

 

 

이렇게 챕터 5도 끝!

반응형