darknet中weights文件存儲格式


以下內容根據個人理解整理而成,如有錯誤,歡迎指出,不勝感激。

0. 寫在前面

本文對darkent保存的.weights文件進行分析,以便后續將權值進行導出。

  • 復習所涉及的c語言知識:sprinf(), fwrite()&fread(), FILE類型
  • .weights中權值的存儲格式

1. sprinf(), fwrite()&fread(), FILE類型

sprinf():
sprinf將一個格式化的字符串輸出到一個目的字符串buff中:

// 在darknet中的使用
char buff[256];
sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i);

fwrite()&fread():
fwrite以二進制方式向文件流中寫入數據:

// buffer: 數據源地址
// size:  每個單元字節數
// count: 總計單元數
// stream: 文件流指針
size_t fwrite(void* buffer, size_t size, size_t count, FILE * stream);

// 在darknet中的使用
fwrite(l.weights, sizeof(float), num, fp);

fread與fwrite類似,只不過buffer變為目的地址

size_t fread(void* buffer, size_t size, size_t count, FILE * stream);

FILE:
使用fopen( )函數可以創建一個新的文件或者打開一個已有的文件,這個調用會初始化一個FILE類型的對象,FILE類型包含了所有用來控制流的必要的信息。

FILE *fp = fopen(filename, "wb");
int b = fclose( FILE *fp );

2. .weights中權值的存儲格式

以下內容主要以conv層為例

detector.ctrain_detector()函數末尾,保存權值的相關代碼如下:

// buff只是一個字符串,並沒有實際創建文件
char buff[256];
sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i);

save_weights(net, buff);

通過向上追溯,可以在parser.c中找到函數save_weights(),並進一步追蹤到save_weights_upto(),主要代碼注釋如下:

// *filename: 即為前面的buff字符串
// cutoff: 網絡層數
void save_weights_upto(network net, char *filename, int cutoff)
{
    // 初始化一個文件讀寫流
    FILE *fp = fopen(filename, "wb");
    if(!fp) file_error(filename);

    // 以下三個變量在version.h中定義
    // #define MAJOR_VERSION 0
    // #define MINOR_VERSION 2
    // #define PATCH_VERSION 5
    int major = MAJOR_VERSION;
    int minor = MINOR_VERSION;
    int revision = PATCH_VERSION;

    fwrite(&major, sizeof(int), 1, fp);
    fwrite(&minor, sizeof(int), 1, fp);
    fwrite(&revision, sizeof(int), 1, fp);
    // net.seen用於記錄訓練時一共經歷了多少張圖片
    // 可根據該參數及cfg中對batch的配置,得出當前迭代次數
    fwrite(net.seen, sizeof(uint64_t), 1, fp);

    // 逐層保存權值
    int i;
    for(i = 0; i < net.n && i < cutoff; ++i){
        layer l = net.layers[i];
        if(l.type == CONVOLUTIONAL && l.share_layer == NULL){
            save_convolutional_weights(l, fp);
        } if(l.type == CONNECTED){
            save_connected_weights(l, fp);
        } if(l.type == BATCHNORM){
            save_batchnorm_weights(l, fp);
        }
    }
    fclose(fp);
}

在具體分析save_convolutional_weights()函數之前,首先要分析convolutional_layer.c中的make_convolutional_layer()函數,該函數根據每個卷積層的配置,為當前層參數分配相應數量的內存,主要代碼注釋如下:

// 卷積核個數
l.n = n;

// 卷積核權重總個數:n*c*size*size  groups是分組卷積時的參數,默認為1
l.nweights = (c / groups) * n * size * size;

// 為卷積核權值、偏置、BN參數分配內存
l.weights = (float*)calloc(l.nweights, sizeof(float));
l.biases = (float*)calloc(n, sizeof(float));
l.scales = (float*)calloc(n, sizeof(float));
l.rolling_mean = (float*)calloc(n, sizeof(float));
l.rolling_variance = (float*)calloc(n, sizeof(float));

再來看parser.c中的save_convolutional_weights()函數就比較容易理解:

void save_convolutional_weights(layer l, FILE *fp)
{
    int num = l.nweights;

    // 有BN的卷積層應該是不需要bias的
    fwrite(l.biases, sizeof(float), l.n, fp);
    if (l.batch_normalize){
        fwrite(l.scales, sizeof(float), l.n, fp);
        fwrite(l.rolling_mean, sizeof(float), l.n, fp);
        fwrite(l.rolling_variance, sizeof(float), l.n, fp);
    }
    fwrite(l.weights, sizeof(float), num, fp);
}

3. 總結

從以上分析可以看出,.weights文件實際上就是一個字節流,我們只需要根據其保存時的順序,每次讀取相應字節數量的內容即可將其解析出來。

要注意,對卷積權重,這里是一維形式進行存儲,就相當於將一個N*C*H*W的tensor展開成一維向量。

Reference

code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM