オッサンはDesktopが好き

自作PCや機械学習、自転車のことを脈絡無く書きます

Deep Learningの計算過程を見える化してみる

Deep Learningの学習モデルが答えを出すメカニズムは、
ブラックボックスと言われます
AIが行う判断の理由は、わからないというわけですね
とは言え、ニューラルネットワークは、紐解けば、
重みとバイアスから構成されるパラメタを使った行列計算です
その計算(=数値の遷移)の中に、
Deep Learningが行う判断の理由があるはずです
それを可視化することで、
Deep Leaningの開発やデバッグを効率化しようと考えました

mnistに戻って調べてみます
mnistのサンプルは、下図のように2層の畳み込み層と、全結合層をつないでいます

f:id:changlikesdesktop:20190317054521p:plain:w400

以前に作った*1ソースも同じです
それぞれの層での計算結果をテキスト出力します
tensorflowに学習モデルを出力する機能がありますが、
バイナリになるのが嫌いなので、テキストで出します

def inference(x, keep_prob):
    x = tf.reshape(x, shape=[-1, IMG_SIZE, IMG_SIZE, 1])
    with tf.variable_scope("conv_1"):
        conv_1, model_conv1 = conv2d(x, [5, 5, 1, 32], [32])
        pool_1 = max_pool(conv_1)
    with tf.variable_scope("conv_2"):
        conv_2, model_conv2 = conv2d(pool_1, [5, 5, 32, 64], [64])
        pool_2 = max_pool(conv_2)
    with tf.variable_scope("fc"):
        pool_2_flat = tf.reshape(pool_2, [-1, 7*7* 64])
        fc_1, model_fc1 = layer(pool_2_flat, [7*7*64, 1024], [1024])
        # apply dropout
        fc_1_drop = tf.nn.dropout(fc_1, keep_prob)
    with tf.variable_scope("output"):
        output, model_fc2 = layer(fc_1_drop, [1024, 10], [10])
    
    model = {'W_conv1': model_conv1['Weight'], 'b_conv1': model_conv1['bias'], \
             'W_conv2': model_conv2['Weight'], 'b_conv2': model_conv2['bias'], \
             'W_fc1': model_fc1['Weight'], 'b_fc1': model_fc1['bias'], \
             'W_fc2': model_fc2['Weight'], 'b_fc2': model_fc2['bias']}
    return output, model

def write_result(output, y, model):
    return output, y, model

def write_model(W_conv1, b_conv1, W_conv2, b_conv2, W_fc1, b_fc1, W_fc2, b_fc2, output, label):
    output_np = np.zeros([TEST_DATA_SIZE, OUTPUT_SIZE])
    for k in range(TEST_DATA_SIZE): 
        for j in range(OUTPUT_SIZE):
            output_np[k, j] = output[k, j]
    np.savetxt('./model/output.txt', output_np)

    for k in range(TEST_DATA_SIZE): 
        for j in range(OUTPUT_SIZE):
            output_np[k, j] = label[k, j]
    np.savetxt('./model/testLabel.txt', output_np)

    W_conv1_np = np.zeros(5*5*1*32)
    for i in range(5):
        for j in range(5):
            for k in range(1):
                for l in range(32):
                    W_conv1_np[i*5 + j + 25*l] = W_conv1[i,j,k,l]
    np.savetxt('./model/W_conv1.txt', W_conv1_np)
    (省略)

if __name__=='__main__':    
    with tf.device("/gpu:0"):
        with tf.Graph().as_default():
            with tf.variable_scope("scope_model"):
                (省略)              
                output, model = inference(x, keep_prob)
      (省略)
                test_op = write_result(output, y, model)
                (省略)
                out, lbl, mdl = sess.run(test_op, feed_dict={x: testImages, y: testLabels, keep_prob: 1})
                (省略)
                write_model(mdl['W_conv1'], mdl['b_conv1'], mdl['W_conv2'], mdl['b_conv2'], mdl['W_fc1'], mdl['b_fc1'], mdl['W_fc2'], mdl['b_fc2'], out, lbl)

placeholderが絡む数値を出力するのに苦戦しました
write_result()という空の関数をSessionに入れ込むことで、
ニューラルネットワークのパラメタを取り出しています
もっとスマートなやり方があるかも知れません

出力したパラメタを使って、この画像を分類してみます
f:id:changlikesdesktop:20190317055927j:plain

ソースはこっち*2に置きます
各層での計算結果は次のようになりました

畳み込み1層目
f:id:changlikesdesktop:20190317055810p:plain:w400

Pool 1層目
f:id:changlikesdesktop:20190317060130p:plain:w400

畳み込み2層目
f:id:changlikesdesktop:20190317061350p:plain:w400

Pool 2層目
f:id:changlikesdesktop:20190317061421p:plain:w400

全結合層
f:id:changlikesdesktop:20190317061437p:plain:w400

出力層
f:id:changlikesdesktop:20190317061521p:plain:w400

一層目では、"3"という文字を崩さずにむしろ強調処理をかけているように見えます
単純に元画像からの変化が少ないのかも知れませんが、
もし、画像のエッジ処理のようなことをネットワークが自前で構築しているとしたら、
なかなか大した事のように思います
ただ、2層目以降になると、データの流れがわからないですね(汗)
全結像層の計算結果が、出力層(0, 0, 0, 1, 0, 0, 0, 0, 0, 0)になる構造なんて全く理解できません(笑)

ただ、無駄では無かったと思います
前回失敗した傷検出*3では、今回の1層目のように、
傷部分を強調するような途中計算になることが強く予想されます
それを頼りに、デバッグしていけば良いわけです
少し前が開けてきました