前提
現在,pythonを用いてResNet50v2+ArcFaceのモデルを実装しています.
こちらのサイトと,こちらのコードを参考にしモデルを構築しました.
実現したいこと
以下に現在使用しているコードを示します.
現在使用しているモデルでは学習データに,[画像データ,それに対応するone-hotベクトル化したクラスラベル]の2入力を必要とするモデルとなっています.
しかしこれでは推論時に,未知画像に対しそれに対応するクラスラベルもいちいち決めて入力しないといけません.
そのため,このモデルを画像データのみの1入力のモデルに変更した方が利便性が向上すると考えたため,これを実現したいです.
該当のソースコード
現在使用しているソースコードです.
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# shape=(1024, self.output_dim), 28 initializer='uniform', 29 trainable=True) 30 super(Arcfacelayer, self).build(input_shape) 31 32 33 # mainの処理 34 def call(self, x): 35 36 y = x[1] 37 x_normalize = tf.math.l2_normalize(x[0]) # x = x'/ ||x'||2 38 k_normalize = tf.math.l2_normalize(self.kernel) # Wj = Wj' / ||Wj'||2 39 40 cos_m = K.cos(self.m) 41 sin_m = K.sin(self.m) 42 th = K.cos(np.pi - self.m) 43 mm = K.sin(np.pi - self.m) * self.m 44 45 cosine = K.dot(x_normalize, k_normalize) # W.Txの内積 46 sine = K.sqrt(1.0 - K.square(cosine)) 47 48 phi = cosine * cos_m - sine * sin_m #cos(θ+m)の加法定理 49 50 if self.easy_margin: 51 phi = tf.where(cosine > 0, phi, cosine) 52 53 else: 54 phi = tf.where(cosine > th, phi, cosine - mm) 55 56 # 正解クラス:cos(θ+m) 他のクラス:cosθ 57 output = (y * phi) + ((1.0 - y) * cosine) 58 output *= self.s 59 60 return output 61 62 def compute_output_shape(self, input_shape): 63 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 7 input_model = Sequential() 8 input_model.add(InputLayer(input_shape=input_tensor)) 9 input_model.add(Conv2D(3, (7, 7), padding='same')) 10 input_model.add(BatchNormalization()) 11 input_model.add(Activation('relu')) 12 13 resnet50v2 = ResNet50V2(include_top=False, weights=None, input_tensor=input_model.output) 14 15# DLしてある重みの読み込み 16 resnet50v2.load_weights('save_model(weights_imagenet)/weights_imagenet.hdf5', by_name=True) 17 18 flat = Flatten()(resnet50v2.layers[-1].output) 19 dense = Dense(512, activation="relu", name="hidden")(flat) 20 21 x = BatchNormalization()(dense) 22 23 yinput = Input(shape=(num_classes,)) #ArcFaceで使用 24 25 s_cos = Arcfacelayer(num_classes, s, m)([x,yinput]) #outputをクラス数と同じ数に 26 prediction = Activation('softmax')(s_cos) 27 28 model = Model(inputs=[resnet50v2.input,yinput], outputs=prediction) 29 30 return model
次のコードを用いてoptunaを使用した学習を行います.
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)
なお使用しているデータの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)
最後に
ResNet50v2 + ArcFaceのモデル定義時の25行目を次のように変更すると
python
1s_cos = Arcfacelayer(num_classes, s, m)(x)
Arcfacelayer定義中のcallメソッド(buildメソッドも?)に渡される引数の形状が変わってしまうため,それに伴いコードの書き換えを行う必要が出てくると思いますがどのように変更すればいいのか分かりません.
現状,参考にしたソースコードが上記のように2入力を必要とするものであったためこのようなモデルを構築したのですが,なにか2入力でないといけない理由があるのでしょうか.
もし,画像データのみの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
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2023/01/13 06:00
2023/01/13 07:55
2023/01/15 05:23
2023/01/15 09:02
2023/01/15 12:05
2023/01/15 16:22
2023/01/16 16:08 編集
2023/01/16 05:21