【darknet源碼解析-04】matrix.h 和 matrix.c

本系列爲darknet源碼解析,本次解析src/matrix.h 與 src/matrix.c 兩個。

matrix.h 中的包含的代碼如下:

#ifndef MATRIX_H
#define MATRIX_H
#include "darknet.h"

// 將矩陣m中的數據拷貝到內存中
matrix copy_matrix(matrix m);
// 可視化
void print_matrix(matrix m);

//從矩陣中採樣m行數據,返回採樣後的結果;
matrix hold_out_matrix(matrix *m, int n);

// 矩陣的行數和列數進行resize操作,resize矩陣大小是size * size
matrix resize_matrix(matrix m, int size);
//獲取矩陣中某一列數據,並把該列刪除掉
float *pop_column(matrix *m, int c);

#endif

首先,我們分析matrix.h 中的源碼,基礎數據結構list 定義在 darknet.h 中,其定義如下:

typedef struct matrix{
    int rows, cols; // 行數,列數
    float **vals; // 二維float數組
} matrix;

 matrix.c 中函數的詳細分析如下,

#include "matrix.h"
#include "utils.h"
#include "blas.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>


/**
 * 釋放矩陣的存儲空間
 * @param matrix 待釋放存儲空間矩陣 m
 */
void free_matrix(matrix m)
{
    int i;
    // 逐行釋放存儲空間;
    for(i = 0; i < m.rows; ++i) free(m.vals[i]);
    free(m.vals);
}


/**
 * emmm...... 暫時不能理解這波操作,先空在這裏,看上層怎麼用???
 * @param truth 矩陣
 * @param guess
 * @param k 取top的個數
 * @return 查找的準確率
 */

float matrix_topk_accuracy(matrix truth, matrix guess, int k)
{
    // 申請k個int類型存儲空間,
    int *indexes = calloc(k, sizeof(int));
    int n = truth.cols;// n 保存truth矩陣的列數
    int i,j;
    int correct = 0;
    for(i = 0; i < truth.rows; ++i){

        // top_K 查找guess.vals[i] 數組中 top-k 個, index保存在 indexes
        top_k(guess.vals[i], n, k, indexes);

        // 逐一遍歷這top-k個數據
        for(j = 0; j < k; ++j){
            int class = indexes[j]; // 取index
            if(truth.vals[i][class]){ //對應 truth.vals[i][class] 位置是否非0
                ++correct; // 非0 就表示滿足
                break;
            }
        }
    }
    free(indexes); // 釋放index
    return (float)correct/truth.rows; // 返回比例,看樣子是準確度。
}

/**
 * 矩陣與常數乘法操作,將矩陣m中每個元素都放大scale倍
 * @param m 矩陣
 * @param scale 乘子
 */
void scale_matrix(matrix m, float scale)
{
    int i,j;
    for(i = 0; i < m.rows; ++i){
        for(j = 0; j < m.cols; ++j){
            m.vals[i][j] *= scale;
        }
    }
}


/**
 * 矩陣的行數和列數進行resize操作,resize矩陣大小是size * size
 * @param m 待調整矩陣
 * @param size 調整後的矩陣大小爲 size * size
 * @return resize後的矩陣
 */
matrix resize_matrix(matrix m, int size)
{
    int i;
    if (m.rows == size) return m; // 如果矩陣行數不發現變化,則不做任何調整;
    if (m.rows < size) { // 調整後矩陣行數變多

        /* 函數名:recalloc
           函數原型:extern void *realloc (void *mem_address, unsigned int newsize);
           函數功能:動態調整內存,先判斷當前的指針是否有足夠的連續空間,如果有,則擴大mem_address指向的地址,
           並且將mem_address返回,如果空間不夠,先按照newsize指定的大小分配空間,將原有數據全部拷貝到新分配的內存區域,
           而後對原來meme_address所指向的內存區域進行釋放【這裏是自動釋放,不需要手動釋放】,同時返回新分配內存區域的首地址。
           如果失敗則返回空指針NULL;
        */
        m.vals = realloc(m.vals, size*sizeof(float*)); // 重新申請內存空間
        for (i = m.rows; i < size; ++i) {
            m.vals[i] = calloc(m.cols, sizeof(float)); // 每一行的存儲空間也要重新申請,每一行保存size 個數據
        }
    } else if (m.rows > size) { // 調整後矩陣行數減少
        for (i = size; i < m.rows; ++i) { // 釋放多餘的存儲空間
            free(m.vals[i]); 
        }
        m.vals = realloc(m.vals, size*sizeof(float*)); // 分配每一行存儲空間,每一行保存size 個數據
    }
    m.rows = size;
    return m; // m是一個size*size 大小的矩陣
}


/**
 * 兩個矩陣的加法操作
 * @param from 矩陣
 * @param to 矩陣
 * 最後結果寫入到矩陣 to 中
 */
void matrix_add_matrix(matrix from, matrix to)
{
    assert(from.rows == to.rows && from.cols == to.cols);
    int i,j;
    for(i = 0; i < from.rows; ++i){
        for(j = 0; j < from.cols; ++j){
            to.vals[i][j] += from.vals[i][j];
        }
    }
}


/**
 * 將矩陣m中的數據拷貝到內存中
 * @param m 待拷貝數據
 * @return 返回存儲在內存中矩陣
 */
matrix copy_matrix(matrix m)
{
    matrix c = {0};
    c.rows = m.rows;
    c.cols = m.cols;
    c.vals = calloc(c.rows, sizeof(float *));
    int i;
    for(i = 0; i < c.rows; ++i){
        c.vals[i] = calloc(c.cols, sizeof(float));
        // copy_cpu() 定義在 blas.h 中
        // void copy_cpu(int N, float *X, int INCX, float *Y, int INCY);

        /* blas.c 中
         * void copy_cpu(int N, float *X, int INCX, float *Y, int INCY)
           {
                int i;
                for(i = 0; i < N; ++i) Y[i*INCY] = X[i*INCX];
           }
         */
        // 將矩陣m的內容拷貝到矩陣c中
        copy_cpu(c.cols, m.vals[i], 1, c.vals[i], 1);
    }
    return c;
}



/**
 * 矩陣初始化
 * @param rows 行數
 * @param cols 列數
 * @return 初始化結果
 */
matrix make_matrix(int rows, int cols)
{
    int i;
    matrix m;
    m.rows = rows;
    m.cols = cols;
    m.vals = calloc(m.rows, sizeof(float *));// 申請存儲空間
    for(i = 0; i < m.rows; ++i){
        m.vals[i] = calloc(m.cols, sizeof(float));
    }
    return m;
}


/**
 * 從矩陣中採樣m行數據,返回採樣後的結果;
 * @param m 待採樣的數據
 * @param n 抽取的行數
 * @return 採樣後的結果;
 */
matrix hold_out_matrix(matrix *m, int n)
{
    int i;
    matrix h;
    h.rows = n;
    h.cols = m->cols;
    h.vals = calloc(h.rows, sizeof(float *)); //申請新矩陣的存儲空間
    for(i = 0; i < n; ++i){
        int index = rand()%m->rows; // 隨機抽取一行
        h.vals[i] = m->vals[index];
        m->vals[index] = m->vals[--(m->rows)]; // 把最後一行的數據覆蓋到 index行上
    }
    return h;
}

/**
 * 獲取矩陣中某一列數據,並把該列刪除掉
 * @param m 待刪除矩陣
 * @param c 列的index
 * @return 返回指定列
 */
float *pop_column(matrix *m, int c)
{
    //
    float *col = calloc(m->rows, sizeof(float));
    int i, j;

    for(i = 0; i < m->rows; ++i){
        col[i] = m->vals[i][c]; // 逐行獲取第c列
        for(j = c; j < m->cols-1; ++j){
            m->vals[i][j] = m->vals[i][j+1]; // 將c+1 到 m.cols-1列向左平移
        }
    }
    --m->cols; // 列數自減1
    return col;
}

/**
 * 讀取文件,並將文件中的數據加載到矩陣matrix中
 * @param filename  文件的存儲位置
 * @return 矩陣
 */
matrix csv_to_matrix(char *filename)
{
    FILE *fp = fopen(filename, "r");
    if(!fp) file_error(filename); // 如果不能打印文件,保存退出程序

    matrix m;
    m.cols = -1;

    char *line;

    int n = 0;
    int size = 1024;
    m.vals = calloc(size, sizeof(float*));
    while((line = fgetl(fp))){ // fgetl 讀取文件中一行數據
        // 統計字符串中有多少個 ',' 字符和 '\0', 遇到第一個'\0'字符結束;
        // 相當於統計line中包含多少個數據,其實這就是矩陣的列數
        if(m.cols == -1) m.cols = count_fields(line);

        if(n == size){ //如果處理到第1024行,需要重新擴充 m.vals的存儲空間
            size *= 2;
            m.vals = realloc(m.vals, size*sizeof(float*));
        }
        m.vals[n] = parse_fields(line, m.cols); // 解析字符數組中m.cols個float小數,返回是一個float類型指針
        free(line); // 釋放line存儲空間
        ++n; //統計處理行數
    }
    m.vals = realloc(m.vals, n*sizeof(float*)); // 之前開闢的空間是1024的整數倍,此時需要根據n來實際分配內存空間
    m.rows = n;
    return m; // 返回結果
}

/**
 * 可視化打印矩陣m,打印格式按照csv格式
 * @param m
 */
void matrix_to_csv(matrix m)
{
    int i, j;

    for(i = 0; i < m.rows; ++i){
        for(j = 0; j < m.cols; ++j){
            if(j > 0) printf(",");
            printf("%.17g", m.vals[i][j]); //自動選擇合適的表示法輸出
        }
        printf("\n");
    }
}

/**
 * 可視化打印矩陣m
 * @param m
 */
void print_matrix(matrix m)
{
    int i, j;
    printf("%d X %d Matrix:\n",m.rows, m.cols); //打印行和列數
    printf(" __");
    for(j = 0; j < 16*m.cols-1; ++j) printf(" ");
    printf("__ \n");

    printf("|  ");
    for(j = 0; j < 16*m.cols-1; ++j) printf(" ");
    printf("  |\n");

    for(i = 0; i < m.rows; ++i){
        printf("|  ");
        for(j = 0; j < m.cols; ++j){
            printf("%15.7f ", m.vals[i][j]);
        }
        printf(" |\n");
    }
    printf("|__");
    for(j = 0; j < 16*m.cols-1; ++j) printf(" ");
    printf("__|\n");
}

完,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章