Python では数値計算を行うための for 文は基本的に遅くなるため NG です。
for 文を使わないで計算できるようにしましょう。
問題設定
BatchSize=30, LabelDim=75, OutputDim=50
モデルの入力 data: (BatchSize, 16, 16, 1) の配列
モデルの出力 Y_pred: (BatchSize, OutputDim) の配列
ラベル labels: (BatchSize, LabelDim) の配列
サンプルコード
テスト用に入力及びラベルを作成する。
python
1 import numpy as np
2
3 batch_size = 30 # バッチサイズ
4 input_shape = ( 16 , 16 , 1 ) # モデルの入力サイズ
5 label_dim = 75 # ラベルの次元数
6 output_dim = 50 # 出力の次元数
7
8 # ダミーの入力データ及びラベルを作成する。
9 data = np . random . randn ( batch_size , * input_shape ) # データ
10 labels = np . random . randn ( batch_size , label_dim ) # ラベル
11 print ( 'data.shape' , data . shape ) # data.shape (30, 16, 16, 1)
12 print ( 'labels.shape' , labels . shape ) # labels.shape (30, 75)
テスト用に簡単なモデルを作成する。
python
1 import tensorflow as tf
2 from keras . models import Sequential
3 from keras . layers import Conv2D , GlobalMaxPooling2D
4
5 # 入力が (BatchSize, 16, 16, 1) で出力が (BatchSize, 50) のダミーのモデルを作成する。
6 model = Sequential ( )
7 model . add ( Conv2D ( output_dim , kernel_size = ( 3 , 3 ) , input_shape = input_shape ) )
8 model . add ( GlobalMaxPooling2D ( ) )
9 model . summary ( )
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 14, 14, 50) 500
_________________________________________________________________
global_max_pooling2d_1 (Glob (None, 50) 0
=================================================================
Total params: 500
Trainable params: 500
Non-trainable params: 0
_________________________________________________________________
損失関数の定義
python
1 def custom_loss ( labels , Y_pred ) :
2 # ラベルから Y_true を抽出する。
3 mask = tf . tile ( [ True , True , False ] , ( label_dim // 3 , ) )
4 Y_true = tf . boolean_mask ( labels , mask , axis = 1 )
5
6 # ラベルから重み係数を抽出する。
7 mask = tf . math . logical_not ( mask )
8 coeff = tf . boolean_mask ( labels , mask , axis = 1 )
9
10 # 重み係数を作成する。
11 ones = tf . ones_like ( coeff )
12 W = tf . reshape (
13 tf . concat ( [ ones [ . . . , tf . newaxis ] , coeff [ . . . , tf . newaxis ] ] , axis = - 1 ) ,
14 [ tf . shape ( ones ) [ 0 ] , - 1 ] )
15
16 return tf . reduce_mean ( ( Y_true - Y_pred ) ** 2 * W )
損失関数の値計算
model.compile(loss=custom_loss, optimizer='adam')
model.fit(data, labels, epochs=3)
loss1 = model.evaluate(data, labels)
print(loss1) # 0.6780742406845093
numpy での計算
# モデルの出力
Y_pred = model.predict(data)
# ラベルから Y_true を抽出する。
mask = np.ones(labels.shape[1], dtype=bool)
mask[2::3] = False
Y_true = labels[:, mask]
# ラベルから重み係数を抽出する。
mask = np.logical_not(mask)
coeff = labels[:, mask]
# 重み係数を作成する。
W = np.ones_like(Y_true)
W[:, 1::2] = coeff
loss2 = np.mean((Y_true - Y_pred) ** 2 * W)
# 計算した値が numpy と TensorFlow で一致するか
print(np.allclose(loss1, loss2))
追記
各処理がなにをやっているのか簡単な例を追加しました。
TensorFlow のテンソルに対して、numpy の indexing のような操作ができないようなので、少々わかりづらいやり方を取らざる得なくなっています。
python
1 import tensorflow as tf
2
3 with tf . Session ( ) as sess :
4 label_dim = 6
5 labels = tf . constant ( [ [ 1 , 2 , 3 , 4 , 5 , 6 ] ,
6 [ 7 , 8 , 9 , 10 , 11 , 12 ] ,
7 [ 13 , 14 , 15 , 16 , 17 , 18 ] ] )
8
9 # ラベルから Y_true を抽出する。
10 mask = tf . tile ( [ True , True , False ] , ( label_dim // 3 , ) )
11 print ( mask . eval ( ) ) # [ True True False True True False]
12
13 # 各サンプルの 1, 2, 4, 5 列目を抜き出している。
14 Y_true = tf . boolean_mask ( labels , mask , axis = 1 )
15 print ( Y_true . eval ( ) )
16 # [[ 1 2 4 5]
17 # [ 7 8 10 11]
18 # [13 14 16 17]]
19
20 # ラベルから重み係数を抽出する。
21 mask = tf . math . logical_not ( mask )
22 print ( mask . eval ( ) ) # [False False True False False True]
23
24 # 各サンプルの 3, 6 列目を抜き出している。
25 coeff = tf . boolean_mask ( labels , mask , axis = 1 )
26 print ( coeff . eval ( ) )
27 # [[ 3 6]
28 # [ 9 12]
29 # [15 18]]
30
31 # 末尾に次元を1つ追加している。ones のほうも同様
32 print ( tf . shape ( coeff ) . eval ( ) ) # [3 2]
33 print ( tf . shape ( coeff [ . . . , tf . newaxis ] ) . eval ( ) ) # [3 2 1]
34
35 # 次元を1つ増やし、axis=-1 で結合している。
36 ones = tf . ones_like ( coeff )
37 concat = tf . concat ( [ ones [ . . . , tf . newaxis ] , coeff [ . . . , tf . newaxis ] ] , axis = - 1 )
38 print ( concat . eval ( ) )
39 # [[[ 1 3]
40 # [ 1 6]]
41 # [[ 1 9]
42 # [ 1 12]]
43 # [[ 1 15]
44 # [ 1 18]]]
45
46 # [3 2 2] を [3, 4] に戻している。
47 W = tf . reshape ( concat , [ tf . shape ( ones ) [ 0 ] , - 1 ] )
48 print ( tf . shape ( concat ) . eval ( ) ) # [3 2 2]
49 print ( tf . shape ( W ) . eval ( ) ) # [3 4]
50
51 print ( W . eval ( ) )
52 # [[ 1 3 1 6]
53 # [ 1 9 1 12]
54 # [ 1 15 1 18]]