本系列爲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");
}
完,