Kaggleに挑戦-7
課題1:GitHubのexperiencor / image-to-3d-bboxのプログラムコードを理解すること
課題2:3D Bounding Box Estimation Using Deep Learning and Geometry, A. Mousavian et.al., arXiv:1612.00496v2 [cs.CV] 10 Apr 2017を理解すること
今日の課題
*入力データの前処理
*損失関数の理解
*入力データ:教師データ:画像とラベル?
元画像サイズ:1382 x 512 ピクセルのカラー画像:教師画像は、おそらく、bounding box内の画像:モデルにinputする画像サイズ:cv2.resizeで224 x 224に変更する。
img = cv2.resize(img, (NORM_H, NORM_W)) # MORM_H=224, NORM_W=224
img = img - np.array([[[103.939, 116.779, 123.68]]])
2行目の引き算は、BGRの平均値を差し引いているのだが、理由は、こうする方が、学習速度が速くなり、精度も良くなるとのこと。さらに、注意点として、カラー画像を読み込む場合、OpenCVのimread()では行(高さ)x列(幅)x色(3)のNumPy配列ndarrayとして読み込まれる。さらに、色の順番はBGR(青、緑、赤)となる。
*bounding box内の画像を取り込む:labelの4番目から7番目までは、2D bounfing boxの left, top, right, bottomのピクセル位置である。ピクセル座標は左上が(0,0)だから、topがymin、bottomがymaxになるのかな。
obj = {'name':line[0], # the type of object : 'Car', 'Van', 'Truck', 'Pedestrian',・・・
'image':image_file,
'xmin':int(float(line[4])), # left
'ymin':int(float(line[5])), # top
'xmax':int(float(line[6])), # right
'ymax':int(float(line[7])), # bottom
'dims':np.array([float(number) for number in line[8:11]]), # 3D object dimensions
'new_alpha': new_alpha
}
このピクセル座標で画像を切り取るところ。
def prepare_input_and_output(train_inst):
xmin = train_inst['xmin']
ymin = train_inst['ymin']
xmax = train_inst['xmax']
ymax = train_inst['ymax']
img = cv2.imread(image_dir + train_inst['image']) # 'image':image_file
img = copy.deepcopy(img[ymin:ymax+1,xmin:xmax+1]).astype(np.float32)
1行目は関数を定義している。この関数に引き渡されるのはall_objs[key]で、all_objs[key] は、ラベルデータファイルの中にある、物体のタイプに対する15個の数値データ(整数もしくは浮動小数点)を含む。内部構造は現時点では理解できていない。
6行目は、元画像を読みこんでいる。
7行目で、2D bounding box内の画像を切り取っている。
この切り取った画像を224 x 224 pixelsにリサイズして、モデルに入力するのか?
それでは学習にならない。元画像の中から、車を検出できるようにするためには、車の画像がどこにあるかを教えなければならない。どのようにして教えるのか。
車の中心の座標を教えるのか。
車を囲む4角形bounding boxの座標を教えるのか。
指し示すか、領域を囲むかだな。
元画像を入力して、ランダムな数値から始めて、正しい座標との差が小さくなるように、学習させるのかな。
今回は3Dだから、dimensionで形状、orientationで車の走行方向、confidenceで???に関するパラメータを正しく値付けされた数値に合うように学習させるということかな。
*いやあ、くじけそうだな。このまま100日間続けて、このプログラムコードと論文を完全に理解できると信じて、やり続けてみよう。100日後は、2020年3月末だな。満67歳になってしまうな。まあ、100歳まで現役AI研究者としてやっていくつもりだから、どうってことないかな。
*モデルの出力が3つ(dimension, orientation, confidence)あるのに、プログラム中で、明示的に損失関数が計算されているのは、orientation lossだけって、なぜだろう。
def orientation_loss(y_true, y_pred):
# Find number of anchors
anchors = tf.reduce_sum(tf.square(y_true), axis=2)
anchors = tf.greater(anchors, tf.constant(0.5))
anchors = tf.reduce_sum(tf.cast(anchors, tf.float32), 1)
# Define the loss
loss = -(y_true[ : , : , 0]*y_pred[ : , : , 0] + y_true[ : , : , 1]*y_pred[ : , : , 1])
loss = tf.reduce_sum(loss, axis=1)
loss = loss / anchors
return tf.reduce_mean(loss)
そのorientation_lossを呼び出しているのは、model.compileの中のlossの中だけだが、関数を指定しているだけで引数がない。関数側の変数は、y_trueとy_predictで、y_predictはモデルからの出力だとしても、y_trueはプログラム中には明示されていない。
model.compileのlossだが、'dimension'と'confidence'は、共に、’mean_square_error'となっている。
*モデルの3つの分岐(CNNの後に連結されている3つの全結合層)を眺めてみよう。
dimension = Dense(512)(x)
dimension = LeakyReLU(alpha=0.1)(dimension)
dimension = Dropout(0.5)(dimension)
dimension = Dense(3)(dimension)
dimension = LeakyReLU(alpha=0.1, name='dimension')(dimension)
最後の層Dense(3)の全結合層の3つのユニットは、3D object dimensionsのheightとwidthとlengthに対応すると思われる。
orientation = Dense(256)(x)
orientation = LeakyReLU(alpha=0.1)(orientation)
orientation = Dropout(0.5)(orientation)
orientation = Dense(BIN*2)(orientation)
orientation = LeakyReLU(alpha=0.1)(orientation)
orientation = Reshape*1(orientation)
orientation = Lambda(l2_normalize, name='orientation')(orientation)
ここには2つの難しそうな概念をもつ記号が入っている。BINとl2(L2)である。
論文には、4.1. MultiBin Orientation Estimationという章/節があって、なにやら小難しいことを述べておる。 L2損失は、マルチモーダル解析においては、平均的な損失を小さくすることを促進するが、シングルモードに対しては、不適切な結果をもたらすことがあるとのこと。
Faster R-CNNとSSDのような物体検出において、bounding boxは直接回帰するのではなく、bounding box の空間をanchar boxと呼ばれる離散モードに分割し、各ancor boxに適用する必要がある連続オフセットを見積もる。・・・何のことかわからん。
配向を見積もるために、似たようなアイデアであるMultiBin architectureを提案する。配向角を分割し、それを、n個の重なり合うbinに分割する。
それぞれのbinに対して、CNNネットワークは、配向角を得るために、binのcenter rayに対する配向に適用するために必要な残余回転角補正と、配向角がi番目のbinに入るconfidence確率ciを見積もる。
残余回転角(residual ratation)は、2つの値、角度のsinとcosで表現される。結果としてそれぞれのbin iに対して3つの出力(ci, cos(Δθi), sin(Δθi))が得られる。
2次元入力のトップにL2ノルム層を適用することによって有効なcosineとsineの値が得られる。MultiBin配向に対する全損失は次式のようになる。Lθ=Lconf + w x Lloc
confidence loss Lconfは、各binのconfidenceのsoftmax lossによって与えられる。
論文中の式(3), 式(4) および式 (5)なども合わせると、orientation loss, localization loss, loss for dimension estimation等のおおよその姿がみえてきた。
3つ目の分岐は、confidenceだ。
confidence = Dense(256)(x)
confidence = LeakyReLU(alpha=0.1)(confidence)
confidence = Dropout(0.5)(confidence)
confidence = Dense(BIN, activation='softmax', name='confidence')(confidence)
モデルの最後に、CNNと3つの分岐を統合する命令がある。
model = Model(inputs, outputs=[dimension, orientation, confidence])
*わかったような気になるだけでは、決して、まともなプログラムは書けない。まだまだ序の口にも達していない。
急がず、焦らず、一歩づつ。
*1:BIN,-1