현재 대학원 졸업을 위해 시각화 실험을 하고 있다.
나의 목적은 t-SNE를 사용한 학습된 모델의 시각화가 목적이었다.
따라서 분류 레이어 이전까지(gap
전까지)의 레이어만을 사용해 결과물을 확보하고자 하였다.
그러던 중 발견했던 에러와 해결방법을 공유하고자 한다.
진행사항 전부를 나열했으니 해결방법을 바로 보고 싶다면,
[03. 해결방법]부터 보면 된다.
나의 모델 구성이다.
졸업논문 내용이라 모델 코드를 공개할 수 없는 점 양해 부탁드린다.
(대부분의 내용을 삭제한 모델 코드이며, 포스팅에 지장은 없다.)
class Model(tf.keras.Model):
def __init__(self, num_classes):
super().__init__()
self.conv = Sequential([
])
self.module = # tensorflow.keras.layers가 아닌 custom layer
self.gap = GlobalAveragePooling1D()
self.dropout = Dropout(0.2)
self.classifier = Dense(num_classes, activation='softmax')
def call(self, inputs):
x = inputs
x = self.conv(x)
x = self.module(x)
gap = self.gap(capsule)
drop = self.dropout(gap)
output_softmax = self.classifier(drop)
return output_softmax
다음과 같은 코드를 사용해 레이어 슬라이싱을 진행했다.
# 이미 학습된 모델을 불러온다.
origin = Model(len(CLASS_LABELS))
origin.build(input_shape=x_test.shape)
origin.load_weights(h5_path)
model = tf.keras.Model(inputs=origin.layers[0].input, outputs=origin.layers[-4].output) # error
그랬더니 마지막 줄에서 에러가 발생했다.
(나머지는 고정된 문자열이고, 'module'은 본인이 설정한 레이어의 변수 이름으로 값이 바뀐다.)
AttributeError: Layer module has no inbound nodes.
stackoverflow, tensorflow github issue page를 살펴보니,
tensorflow-2.x 버전에서 비슷한 문제를 겪은 사람들이 많이 있었다.
발견했던 몇 가지 해결 방법은 다음과 같다.
origin.layers[-4].output
을 origin.get_output_at(-4)
로 대체RuntimeError: The layer model has never been called and thus has no defined output.
K.Model([origin.layers[0].input], [origin.layers[-4].output])
RuntimeError: The layer model has never been called and thus has no defined output.
상기 언급된 해결방법은 keras의 레이어라면 해결 가능하다고 한다.
from tensorflow.keras.layers
그러나 직접 구성한 custom layer라면 이 방법은 통하지 않았다.
내가 참고한 자료들은 다음과 같다.
1. Saving & loading only the model's weights values
2. How do I get the weights of a layer in Keras?
우선 다음 두 API를 살펴보자.
get_weights()
로부터 반환된 numpy 배열에서 레이어의 가중치 설정다시 말해,
get_weights()
로 가중치와 바이어스 값을 구해서,
set_weights()
로 가중치와 바이어스 값을 덮어 씌우면 된다.
### 원본 모델
class Model(tf.keras.Model):
def __init__(self, num_classes):
super().__init__()
self.conv = Sequential([
])
self.module = # tensorflow.keras.layers가 아닌 custom layer
self.gap = GlobalAveragePooling1D()
self.dropout = Dropout(0.2)
self.classifier = Dense(num_classes, activation='softmax')
def call(self, inputs):
x = inputs
x = self.conv(x)
x = self.module(x)
gap = self.gap(capsule)
drop = self.dropout(gap)
output_softmax = self.classifier(drop)
return output_softmax
## 슬라이싱을 위한 모델 구조 선언
## module 이후의 레이어가 전부 지워진 모습을 볼 수 있다.
class Model2(tf.keras.Model):
def __init__(self, num_classes):
super().__init__()
self.conv = Sequential([
])
self.module = # tensorflow.keras.layers가 아닌 custom layer
def call(self, inputs):
x = inputs
x = self.conv(x)
x = self.module(x)
return x
load_weights()
는 가중치만 저장되어 있다.fit()
하거나 predict()
를 하려면 위와 같이 모델 구조를 똑같이 구성해야 한다.model.build(input_shape)
를 반드시 해줘야 함.origin = Model(len(CLASS_LABELS))
origin.build(input_shape=data.shape)
origin.load_weights(h5_path)
get_weights()
사용layer[0].get_weights()
와 같이 코드를 작성하면 0번째 위치에 있는 레이어의 가중치를 얻을 수 있다.def get_layers_weights(origin_model, until):
"""get weights of model
Args:
origin_model (keras.Model): Original model (+weights)
until (int): Index of wanted final layer
Returns:
list: weights for each layer
"""
ret = []
copy_layers = origin_model.layers[:until]
for cur_layer in copy_layers:
ret.append(cur_layer.get_weights())
return ret
origin_layers_weights = get_layers_weights(origin, until=-3)
test_model = Model2(len(CLASS_LABELS))
test_model.build(input_shape=data.shape)
test_model_layers = test_model.layers
for i, cur_test_layer in enumerate(test_model_layers):
cur_test_layer.set_weights(origin_layers_weights[i])
predictions = test_model(x_test, training=False)
print(predictions)
모델의 뼈대는 vscode에서 Tensorflow 2.0 Snippets
extention을 설치하면 매우 간단히 만들 수 있다.
## tf:ctrl:model 이걸 입력하면 윈도우 박스에 model block이 보일 것이다.
## enter 키를 입력하면 아래와 같은 구조가 만들어진다.
class MyModel(tf.keras.Model):
def __init__(self):
super(MyModel, self).__init__()
self.dense1 = tf.keras.layers.[Dense Like Layer]
def call(self, inputs):
x = self.dense1(inputs)
return x