前提
現在,pythonを用いてResNet50v2+ArcFaceのモデルを実装しています.
Qiitaのサイトと,GitHubのコードを参考にしモデルを構築しました.
ハイパーパラメータなどはOputunaで最適化を行い,最終的にベストな重みのみを保存するようなプログラムを組んでいます.
実現したいこと
参考サイトではいづれも推論用モデル(Qiitaのサイトではpredict_model)を,ベストな重みをロードしたモデルからArcFaceLayer以降を全て削除し,ベースネットの最後のconvの出力を最終出力とするようなモデルとしていました.(今までは最終出力をSoftmaxなどとするモデルを扱ってきたので少し困惑しました.)
そこで,predict_mdelを用いて以下を実現したいと思っています.
① 各データ間の距離の値を取得したい
これは例えば,predict_mdelに2つのデータをそれぞれ入力し得られた特徴ベクトルを用いて,その都度cos類似度などで類似度を計算してあげる必要があるのでしょうか.
② 入力画像に対する出力層のsoftmaxの値を取得したい
どのクラスにも属さない未知画像に対し,最終的なsoftmaxの値(クラス数次元のベクトル)をpredict_modelから取得したいのですがその方法が分かりません.
該当のソースコード
以下に現在使用しているソースコードを示します.
python
1# 総クラス数,エポック数,入力画像のサイズを指定 2num_classes = 29 3epochs = 100 4height = 110 # 像のピクセル数(縦) 5width = 110 # 像のピクセル数(横)
python
1class Arcfacelayer(Layer): 2 # s:softmaxの温度パラメータ, m:margin 3 def __init__(self, output_dim, s, m, easy_margin=False): 4 self.output_dim = output_dim 5 self.s = s 6 self.m = m 7 self.easy_margin = easy_margin 8 super(Arcfacelayer, self).__init__() 9 10 def get_config(self): 11 config = { 12 "output_dim" : self.output_dim, 13 "s" : self.s, 14 "m" : self.m, 15 "easy_margin" : self.easy_margin 16 } 17 base_config = super().get_config() 18 return dict(list(base_config.items()) + list(config.items())) 19 20 21 # 重みの作成 22 def build(self, input_shape): 23 print(f'buildのinput_shape:{input_shape}') 24 # Create a trainable weight variable for this layer. 25 self.kernel = self.add_weight(name='kernel', 26 shape=(input_shape[0][1], self.output_dim), 27 initializer='uniform', 28 trainable=True) 29 super(Arcfacelayer, self).build(input_shape) 30 31 32 # mainの処理 33 def call(self, x): 34 35 y = x[1] 36 x_normalize = tf.math.l2_normalize(x[0]) # x = x'/ ||x'||2 37 k_normalize = tf.math.l2_normalize(self.kernel) # Wj = Wj' / ||Wj'||2 38 39 cos_m = K.cos(self.m) 40 sin_m = K.sin(self.m) 41 th = K.cos(np.pi - self.m) 42 mm = K.sin(np.pi - self.m) * self.m 43 44 cosine = K.dot(x_normalize, k_normalize) # W.Txの内積 45 sine = K.sqrt(1.0 - K.square(cosine)) 46 47 phi = cosine * cos_m - sine * sin_m #cos(θ+m)の加法定理 48 49 if self.easy_margin: 50 phi = tf.where(cosine > 0, phi, cosine) 51 52 else: 53 phi = tf.where(cosine > th, phi, cosine - mm) 54 55 # 正解クラス:cos(θ+m) 他のクラス:cosθ 56 output = (y * phi) + ((1.0 - y) * cosine) 57 output *= self.s 58 59 return output 60 61 def compute_output_shape(self, input_shape): 62 return (input_shape[0][0], self.output_dim) #入力[x,y]のためx[0]はinput_shape[0][0]
python
1# ResNet50v2 + ArcFace定義 2# 学習に使用 3def create_arcface_with_resnet50v2(input_shape, s, m): 4 # ResNet50V2の入力層の前に独自の入力層を追加 5 input_tensor = input_shape 6 print(input_tensor) 7 8 input_model = Sequential() 9 input_model.add(InputLayer(input_shape=input_tensor)) 10 input_model.add(Conv2D(3, (7, 7), padding='same')) 11 input_model.add(BatchNormalization()) 12 input_model.add(Activation('relu')) 13 14 resnet50v2 = ResNet50V2(include_top=False, weights=None, input_tensor=input_model.output) 15 16# DLしてある重みの読み込み 17 resnet50v2.load_weights('save_model(weights_imagenet)/weights_imagenet.hdf5', by_name=True) 18 19 20 flat = Flatten()(resnet50v2.layers[-1].output) 21 dense = Dense(512, activation="relu", kernel_initializer="he_normal", name="hidden")(flat) 22 23 x = BatchNormalization()(dense) 24 25 yinput = Input(shape=(num_classes,)) #ArcFaceで使用 26 27 s_cos = Arcfacelayer(num_classes, s, m)([x,yinput]) #outputをクラス数と同じ数に 28 prediction = Dense(num_classes, activation="softmax")(s_cos) 29 30 model = Model(inputs=[resnet50v2.input,yinput], outputs=prediction) 31 32 return model 33
現在は次のコードを用いてoptunaを使用し,val_lossを最適化した学習を行っています.
python
1# モデル定義 2def trainer(**param): 3 4 5 lr = param['lr'] 6 batch_size = param['batch_size'] 7 s = param['s'] 8 m = param['m'] 9 10 11 model = create_arcface_with_resnet50v2(input_shape, s, m) 12 13 14 # 自動的に学習をやめる 15 early_stopping = EarlyStopping(monitor='val_loss', 16 min_delta=0.0, 17 patience=3, 18 mode='min' 19 ) 20 # 最良の重みとモデルを保存 21 fpath = 'Weight/{}/{}_{}_{}_{}_({{epoch:02d}}-{{loss:.2f}}-{{accuracy:.2f}}-{{val_loss:.5f}}-{{val_accuracy:.2f}}).hdf5'.format(f_elem, batch_size, lr, s, m) 22 model_checkpoint = ModelCheckpoint(filepath=fpath, monitor='val_loss', save_best_only=True, mode='min') 23 # 学習中に学習率を調整 24 lr_scheduler = LearningRateScheduler(lr_schedul, verbose=1) 25 26 27 adam = Adam(learning_rate=lr) 28 model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy']) 29 30 31 32 try: 33 34 H = model.fit([x_train, y_train], y_train, # ここの[x_train, y_train]をx_trainにしたい 35 epochs=epochs, 36 verbose=1, 37 steps_per_epoch = math.ceil(len(x_train)/batch_size), 38 validation_data=([x_test, y_test], y_test), 39 callbacks=[early_stopping, model_checkpoint, lr_scheduler]) 40 41 part = f_elem.split('/')[1] 42 M_path = 'Model/{}/{}_{}_{}_{}_model_{}.h5'.format(f_elem, batch_size, lr, s, m, part) 43 model.save(M_path) 44 45 #テストデータで精度を確認 46 score = model.evaluate([x_test, y_test], y_test) 47 48 return score[0] # vall_lossが最小であるモデルを探すため 49 50 except Exception as e: 51 print(e)
推論用のモデルです.
python
1model = create_arcface_with_resnet50v2(input_shape, s=best_s, m=best_m) # best_sとbest_mはOptunaで最適化された値 2model.load_weights(W_path) # W_pathはOptunaの学習で得たベストの重みのパス 3 4predict_model = Model(model.get_layer(index=0).input, model.get_layer(index=-5).output)
なお使用しているデータのshapeは以下のようになっています.
x_train.shape → (1566, 110, 110, 1)
y_train.shape → (1566, 29) # one-hot
x_test.shape → (88, 110, 110, 1)
y_test.shape → (88, 29) # one-hot
input_shape → (110, 110, 1)
どなたか上記➀, ②を実現できる方法やアドバイスなどあれば教えていただきたいです.
補足情報(FW/ツールのバージョンなど)
ubuntu 20.04
Python 3.8.10
tensorflow-gpu 2.5.3
keras 2.8.0
numpy 1.19.5
jupyter lab 2.3.2
あなたの回答
tips
プレビュー