[TOC]
本次解析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");
}