天天看點

C代碼版本的MTCNN 從tensorflow權重參數生成bin檔案摘要如何生成bin檔案權重檔案寫入順序調試方法介紹C代碼修改其他問題

C代碼版本的MTCNN 從tensorflow權重參數生成bin檔案

  • 摘要
  • 如何生成bin檔案
  • 權重檔案寫入順序
    • 1、conv層權重參數
    • 2、conv層偏置、prelu層的寫入
    • 3、fc層
  • 調試方法介紹
  • C代碼修改
  • 其他問題

摘要

MTCNN是一個優秀的人臉檢測模型,在網上有各種架構下的版本,在項目中需要使用MTCNN的C代碼版本,該版本的作者并沒有提供生成代碼運作的txt權重參數檔案的程式,同時使用txt檔案來存參數,檔案的體積比較大,在項目中我們需要使用tensorflow訓練出來的權重參數給模型運作,是以就涉及到如何把tensorflow訓練出來的權重檔案轉化為bin檔案,用于替代程式中的txt檔案

如何生成bin檔案

以pnet.bin的生成為例:

PnetOutFile = "Pnet.bin"
PnetBinFile = open(PnetOutFile,'wb')
PnetBinFile.write(struct.pack('f', Pconv1_w[y,x,i,j]))
PnetBinFile.close()
           

其中的“f”是寫入浮點數的意思,Pconv1_w[y,x,i,j]是我們從tensorflow權重檔案中取出來的參數,每執行一次則向bin檔案中存入一個參數,存取都是按順序的。最後記得close檔案

權重檔案寫入順序

1、conv層權重參數

Pconv1_w = sess.run(pnet_var[0])
ky, kx, num_in_map, num_out_kerns = Pconv1_w.shape
print(0,Pconv1_w.shape)
    for j in range(num_out_kerns):
        for i in range(num_in_map):
            for y in range(ky):
                for x in range(kx):
                    PnetBinFile.write(struct.pack('f', Pconv1_w[y,x,i,j]))
           

卷積層通過sess.run讀取到了一個權重矩陣Pconv1_w[y,x,i,j],這個矩陣是四維的,次元分别為y方向權重、x方向權重、輸入通道、輸出通道。

我們需要把這個四維矩陣轉化為一列,存入bin檔案中,用于給c代碼讀取。

2、conv層偏置、prelu層的寫入

#Rconv1_b
    Rconv1_b = sess.run(rnet_var[1])
    print(1,Rconv1_b.shape)
    for item in range(0,len(Rconv1_b[:])):
        RnetBinFile.write(struct.pack('f',Rconv1_b[item]))
#RPReLU1
    RPReLU1 = sess.run(rnet_var[2])
    print(2,RPReLU1.shape)
    for item in range(0,len(RPReLU1[:])):
        RnetBinFile.write(struct.pack('f',RPReLU1[item]))
           

這兩個層都是一維的原理相同,比較簡單,直接按順序寫就好了

3、fc層

#Rconv4_w  (576,128)
    Rconv4_w = sess.run(rnet_var[9])
    print(9,Rconv4_w.shape)
    num_in_map, num_out_kerns = Rconv4_w.shape
    RfcTemp = []
    RfcTemp2 = []
    for j in range(num_out_kerns):#128
        for i in range(num_in_map):#576
            RfcTemp.append(Rconv4_w[i,j])
            for n in range(64):
                for k in range(9):
                    RfcTemp2.append(RfcTemp[n + k*64])
            for l in range(576):
                RnetBinFile.write(struct.pack('f', RfcTemp2[l]))
            RfcTemp = []
            RfcTemp2 = []    
           

這一層比較難,需要做順序的調換

做調換的原因是Tensorflow架構和C代碼下的全連接配接輸出一直不一緻,原因在于把上一層卷積出來的結果reshape成一列的過程不正确,C代碼中并沒有寫這個reshape成一列的函數,在存儲中上一層輸出的資料conv3_out->pdata在記憶體中就是存層一列的,C代碼把它直接送入fc層,導緻了reshape順序的錯誤。

詳細的排列順序如下:在TF或C代碼中,Rnet的FC前一層(PReLU3)的輸出為(3,3, 64),對其進行重排如下:每個channel有9個參數,一共64個kernel,把每個kernel同個位置的參數取出,按順序排列在一起。如下圖:

C代碼版本的MTCNN 從tensorflow權重參數生成bin檔案摘要如何生成bin檔案權重檔案寫入順序調試方法介紹C代碼修改其他問題

知道了c代碼和TF的排列順序,就可以按照順序去做轉化了

調試方法介紹

在調試過程中需要我們需要一層一層調,先保證TF和C代碼的輸入一緻,通過相同的層,如果輸出一緻的話,那麼權重的順序就排列對了,在c代碼中直接使用自帶的權重列印函數pBoxShow把資料列印出來:

feature2Matrix(this->rgb, this->conv1_matrix, this->conv1_wb);
    convolution(this->conv1_wb, this->rgb, this->conv1_out, this->conv1_matrix);
    prelu(this->conv1_out, this->conv1_wb->pbias, this->prelu_gmma1->pdata);
    pBoxShow(this->conv1_out);
           

在TF中,我們采用這樣的政策,把我們要看的層後面的操作屏蔽,然後輸出我們要看的層的輸出作為最後的網絡輸出:

C代碼版本的MTCNN 從tensorflow權重參數生成bin檔案摘要如何生成bin檔案權重檔案寫入順序調試方法介紹C代碼修改其他問題

這樣網絡輸出就是prelu1層操作後的結果,然後再列印出來:

out = pnet(img)
           im_data = np.array(out)
           for c in range(3):
               print("channel:",c)
               tempImg = []
               for y in range(17):
                   for x in range(21):
                       tempImg.append(im_data[y,x,c])
                   print(tempImg)
                   tempImg = []
               print("\n")
           

C代碼修改

網絡的輸出結果一緻後,運作程式發現框可以準确預測,可是關鍵并不能準确預測。

這是因為在TF架構和C代碼中對于網絡最終輸出資料轉換成坐标的程式有差異,直接使用會造成關鍵點不正确

解決方法:修改Onet中的關鍵點的計算代碼

C代碼版本的MTCNN 從tensorflow權重參數生成bin檔案摘要如何生成bin檔案權重檔案寫入順序調試方法介紹C代碼修改其他問題
C代碼版本的MTCNN 從tensorflow權重參數生成bin檔案摘要如何生成bin檔案權重檔案寫入順序調試方法介紹C代碼修改其他問題

其他問題

C代碼中也有一些缺陷:

network.cpp代碼中的maxpooling()函數有缺陷,在pooling過程中如果遇見需要補零的feature map,隻對x方向補零,卻沒有y方向補零,導緻最後一行的maxpooling輸出有差錯

解決辦法:在network.cpp中增加了y方向的補零代碼

C代碼版本的MTCNN 從tensorflow權重參數生成bin檔案摘要如何生成bin檔案權重檔案寫入順序調試方法介紹C代碼修改其他問題

繼續閱讀