質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

深層学習

深層学習は、多数のレイヤのニューラルネットワークによる機械学習手法。人工知能研究の一つでディープラーニングとも呼ばれています。コンピューター自体がデータの潜在的な特徴を汲み取り、効率的で的確な判断を実現することができます。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

1865閲覧

[ArcFace実装] ラベル情報を付加せずに画像だけを推論に用いることのできる1入力モデルを構築したい

harug

総合スコア28

Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

深層学習

深層学習は、多数のレイヤのニューラルネットワークによる機械学習手法。人工知能研究の一つでディープラーニングとも呼ばれています。コンピューター自体がデータの潜在的な特徴を汲み取り、効率的で的確な判断を実現することができます。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2023/01/12 16:04

編集2023/01/16 17:40

前提

現在,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

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

なにか2入力でないといけない理由

ArcFaceLayerを用いた学習時にラベルデータ(質問コードで言うyinput)が必要だからです.また,モデル運用時には不必要であることが原理からも明確です.

画像データのみの1入力のモデルに変更できるのであれば,どのようにソースコードを変更すればよいか

参考にされたQiitaの記事の関数create_predict_modelや参考にされたGitHubのコードtest.pyの項目で,いずれもArcFaceLayerを除去し,単一画像入力で良くなるようなモデル変更(というより新たなモデル定義)を行っています.

したがって,これらを倣ってOptunaに返すべきscore[0]ことval_lossはArcFaceLayerが取り除かれたモデルで算出された値である方が正しい評価になると考えます.

投稿2023/01/12 17:25

編集2023/01/12 23:29
PondVillege

総合スコア1579

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

harug

2023/01/13 06:00

ご回答いただき,ありがとうございます. > 返すべきscore[0]ことval_lossはArcFaceLayerが取り除かれたモデルで算出された値である方が正しい評価になると考えます. なぜその方が正しい評価になるのでしょうか. 私的には,ArcFaceLayerによって距離学習を行ったためArcFaceLayerなしのモデルで評価してしまうと距離学習で得られた情報が考慮されないのではないかと思うのですが...勉強不足のため教えていただきたいです. また,ArcFaceLayerを取り除いたものに出力層を追加し,そこから算出されるval_lossをOptunaに返し評価するという解釈でよろしいでしょうか?
PondVillege

2023/01/13 07:55

ArcFaceLayerはあくまで距離学習を行うためのアタッチメントでしかなく,最適化された予測値を吐き出すのは質問コードで言うところのResNet50V2です.ArcFaceLayerの出力がMargin Penaltyのない予測であることは,原理からも明確です. > ArcFaceLayerを取り除いたものに出力層を追加し,そこから算出されるval_lossをOptunaに返し評価するという解釈 出力層はもともと付いていますのでArcFaceLayerを取り除くだけで大丈夫です(これも原理からわかる話です).ただ,いずれのコードもArcFaceLayerを取り除いた後に損失関数を与えていません.ので,val_lossを算出するには再度model.compile()やmodel.fit()を行うことになってしまいます.したがってval_lossを算出するのは面倒なので,Optunaに返すのは,他のAccuracy等のMetricsが良いと思います.これに関しては言い方が少し悪かったかもしれません.ArcFaceLayerなしで評価できる値を渡しましょう.
harug

2023/01/15 05:23

ArcFaceLayerありで学習させた後,そこで得られた重みをloadさせたうえで出力層を含むArcFaceLayer以降の全ての層を削除し,新たに出力層を追加するということを行いました. しかし,それでは予測結果が全て0に近い数値として出力されてしまっていたため,元の学習済みの重みを保持したまま出力層を追加する方法があれば教えていただきたいです. また,Oputunaに返す値は,model.evaluate()ではなくmodel.prredict()なりを使用し,正解率を求めればよいという解釈でよろしいでしょうか.
PondVillege

2023/01/15 09:02

> 出力層を含むArcFaceLayer以降の全ての層を削除し,新たに出力層を追加する 新たに出力層を追加しまっては再学習の必要がありますしArcFaceLayerを使った意味がなくなります.いずれの参考サイトも,新たな出力層を追加してないことがわかるはずです. > Oputunaに返す値は,model.evaluate()ではなくmodel.predict() model.evaluate()でもmode.predict()でも良いと思います.正解率を最適化したいなら正解率を返すべきですし,F1 score等他のMetricsを最適化したいならそれを返すべきでしょう.
harug

2023/01/15 12:05

参考サイトによると,ArcFaceLayer以降の全ての層を削除したものをpredict用のモデルとしていたため,私のソースコードでそれを行うとpredict用のモデルの最終出力は dense = Dense(512, activation="relu", name="hidden")(flat) の出力となってしまいます. 512次元ベクトルを出力するモデルでは,どのように正解率を出せばよいのでしょうか.
PondVillege

2023/01/15 16:22

GitHub実装の方を見ると Dense(512, kernel_initializer='he_normal') を通した後が出力になっているようですし,合わせるとしたら活性化関数をlinearにするだけで良いのではないでしょうか. 少なくとも,それで作成されたmodelインスタンスからmodel.predict()やmodel.evaluate()ができるはずです.
harug

2023/01/16 16:08 編集

dense = Dense(512, activation="relu", name="hidden")(flat) を dense = Dense(512, activation="linear", name="hidden")(flat) に変更し, model = Model(inputs=<元のmodel>.input, outputs=<元のmodel>.get_layer(index=-5).output) として再作成したモデルでmodel.predict()やmodel.evaluate()ができるという解釈でよろしいでしょうか. <追記> 上記の方法でモデルを再作成し,model.predict(x_test)を実行すると問題なく512次元ベクトルが出力されましたが,model.evaluate(x_test, y_test)を実行すると想定通り RuntimeError: You must compile your model before training/testing. Use model.compile(optimizer, loss). が発生しました. 上記でps_aux_grepさんがおっしゃっているOptunaにAccuracyを返す場合は tf.keras.metrics.categorical_accuracy(y_true, y_pred) を使用して求めてあげればよいのでしょうか.
harug

2023/01/16 05:21

また,推論時に最終的なsoftmaxの値を取得したい場合は,上記の512次元ベクトルを出力するモデルではその値を取得することは可能なのでしょうか.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問