DPM(Defomable Parts Model) 源碼分析-訓練(三)

DPM(Defomable Parts Model)原理

首先調用格式:

example:
pascal('person', 2);   % train and evaluate a 2 component person model

pascal_train.m

function model = pascal_train(cls, n) % n=2

% model = pascal_train(cls)
% Train a model using the PASCAL dataset.

globals; 
%----------讀取正負樣本-----------------------
% pos.im,neg.im存儲了圖像路徑,pos.x1..pos.y2爲box,負樣本無box
[pos, neg] = pascal_data(cls);

% 按照長寬比,分成等量的兩部分? 即將 component label  固定,phase2時,該值爲latent variable。  spos爲索引
spos = split(pos, n);

% -----------phase 1 : train root filters using warped positives & random negatives-----------
try
  load([cachedir cls '_random']);
catch
% -----------------------------phas 1--------------------------------
% 初始化 rootfilters
  for i=1:n
    models{i} = initmodel(spos{i});
	%---------train-------------
	% model.rootfilters{i}.w
	% model.offsets{i}.w
    models{i} = train(cls, models{i}, spos{i}, neg, 1, 1, 1, 1, 2^28);

  end
  save([cachedir cls '_random'], 'models');
end

% -----------------phase2-------------------------------------------
% :merge models and train using latent detections & hard negatives
try 
  load([cachedir cls '_hard']);
catch
  model = mergemodels(models);
  model = train(cls, model, pos, neg(1:200), 0, 0, 2, 2, 2^28, true, 0.7);
  save([cachedir cls '_hard'], 'model');
end
%----------------phase 3----------------------------------------------
% add parts and update models using latent detections & hard negatives.
try 
  load([cachedir cls '_parts']);
catch
  for i=1:n
    model = addparts(model, i, 6);
  end 
  % use more data mining iterations in the beginning
  model = train(cls, model, pos, neg(1:200), 0, 0, 1, 4, 2^30, true, 0.7);
  model = train(cls, model, pos, neg(1:200), 0, 0, 6, 2, 2^30, true, 0.7, true);
  save([cachedir cls '_parts'], 'model');
end

% update models using full set of negatives.
try 
  load([cachedir cls '_mine']);
catch
  model = train(cls, model, pos, neg, 0, 0, 1, 3, 2^30, true, 0.7, true, ...
                0.003*model.numcomponents, 2);
  save([cachedir cls '_mine'], 'model');
end

% train bounding box prediction
try
  load([cachedir cls '_final']);
catch
 % 論文中說用最小二乘,怎麼直接相除了,都不考慮矩陣的奇異性
  model = trainbox(cls, model, pos, 0.7);
  save([cachedir cls '_final'], 'model');
end

initmodel.m

function model = initmodel(pos, sbin, size)

% model = initmodel(pos, sbin, size)
% Initialize model structure.
%
% If not supplied the dimensions of the model template are computed
% from statistics in the postive examples.
% 
% This should be documented! :-)
% model.sbin         8
% model.interval     10
% model.numblocks     phase 1 :單獨訓練rootfilter時爲2,offset,rootfilter;phase 2,爲 4 
% model.numcomponents  1
% model.blocksizes     (1)=1,(2)= root.h*root.w/2*31
% model.regmult        0,1
% model.learnmult      20,1
% model.maxsize        root 的size 
% model.minsize
% model.rootfilters{i}
%   .size  				以sbin爲單位,尺寸爲綜合各樣本的h/w,area計算出來的
%   .w
%   .blocklabel        blocklabel是爲編號,offset(2),rootfilter(2),partfilter(12 or less),def (12 same as part)雖然意義不同但是放在一起統一編號
% model.partfilters{i}
%   .w
%   .blocklabel
% model.defs{i}
%   .anchor
%   .w
%   .blocklabel
% model.offsets{i}
%   .w               0
%   .blocklabel       1
% model.components{i}
%   .rootindex    1
%   .parts{j}
%     .partindex
%     .defindex
%   .offsetindex    1
%   .dim             2 + model.blocksizes(1) + model.blocksizes(2)
%   .numblocks       2

% pick mode of aspect ratios
h = [pos(:).y2]' - [pos(:).y1]' + 1;
w = [pos(:).x2]' - [pos(:).x1]' + 1;
xx = -2:.02:2;
filter = exp(-[-100:100].^2/400); % e^-25,e^25
aspects = hist(log(h./w), xx); %
aspects = convn(aspects, filter, 'same');
[peak, I] = max(aspects);
aspect = exp(xx(I)); %濾波後最大的h/w,作爲最典型的h/w

% pick 20 percentile area
areas = sort(h.*w);
area = areas(floor(length(areas) * 0.2)); % 比它大的,可以縮放,比該尺寸小的呢?
area = max(min(area, 5000), 3000); %限制在 3000-5000

% pick dimensions
w = sqrt(area/aspect);
h = w*aspect;

% size of HOG features
if nargin < 4
  model.sbin = 8;
else
  model.sbin = sbin;
end

% size of root filter
if nargin < 5
  model.rootfilters{1}.size = [round(h/model.sbin) round(w/model.sbin)];
else
  model.rootfilters{1}.size = size;
end

% set up offset 
model.offsets{1}.w = 0;
model.offsets{1}.blocklabel = 1;
model.blocksizes(1) = 1;
model.regmult(1) = 0;
model.learnmult(1) = 20;
model.lowerbounds{1} = -100;

% set up root filter
model.rootfilters{1}.w = zeros([model.rootfilters{1}.size 31]);
height = model.rootfilters{1}.size(1);
% root filter is symmetricf
width = ceil(model.rootfilters{1}.size(2)/2);  % ??? /2
model.rootfilters{1}.blocklabel = 2;
model.blocksizes(2) = width * height * 31;
model.regmult(2) = 1;
model.learnmult(2) = 1;
model.lowerbounds{2} = -100*ones(model.blocksizes(2),1);

% set up one component model
model.components{1}.rootindex = 1;
model.components{1}.offsetindex = 1;
model.components{1}.parts = {};
model.components{1}.dim = 2 + model.blocksizes(1) + model.blocksizes(2);
model.components{1}.numblocks = 2;

% initialize the rest of the model structure
model.interval = 10;
model.numcomponents = 1;
model.numblocks = 2;
model.partfilters = {};
model.defs = {};
model.maxsize = model.rootfilters{1}.size;
model.minsize = model.rootfilters{1}.size;


 

learn.cc

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <errno.h>

/*
 * Optimize LSVM objective function via gradient descent.
 *
 * We use an adaptive cache mechanism.  After a negative example
 * scores beyond the margin multiple times it is removed from the
 * training set for a fixed number of iterations.
 */

// Data File Format
// EXAMPLE*
// 
// EXAMPLE:
//  long label          ints
//  blocks              int
//  dim                 int
//  DATA{blocks}
//
// DATA:
//  block label         float
//  block data          floats
//
// Internal Binary Format
//  len           int (byte length of EXAMPLE)
//  EXAMPLE       <see above>
//  unique flag   byte

// number of iterations
#define ITER 5000000

// small cache parameters
#define INCACHE 3
#define WAIT 10

// error checking
#define check(e) \
(e ? (void)0 : (printf("%s:%u error: %s\n%s\n", __FILE__, __LINE__, #e, strerror(errno)), exit(1)))

// number of non-zero blocks in example ex
#define NUM_NONZERO(ex) (((int *)ex)[labelsize+1])

// float pointer to data segment of example ex
#define EX_DATA(ex) ((float *)(ex + sizeof(int)*(labelsize+3)))

// class label (+1 or -1) for the example
#define LABEL(ex) (((int *)ex)[1])

// block label (converted to 0-based index)
#define BLOCK_IDX(data) (((int)data[0])-1)

int labelsize;
int dim;

// comparison function for sorting examples 
// 參見 http://blog.sina.com.cn/s/blog_5155e8d401009145.html
int comp(const void *a, const void *b) {
  // sort by extended label first, and whole example second...
  
  //逐字節比較的,當buf1<buf2時,返回值<0,當buf1=buf2時,返回值=0,當buf1>buf2時,返回值>0
  // 先比較這五個量 [label id level x y],也就是說按照 樣本類別->id->level->x->y排序樣本
  int c = memcmp(*((char **)a) + sizeof(int), 
		 *((char **)b) + sizeof(int), 
		 labelsize*sizeof(int));// 5
  if (c) //label 不相等
    return c;
  
  // labels are the same ,怎麼可能會一樣呢 id在正負樣本集內從1開始是遞增的啊  phase 2 階段同一張圖片產生的樣本,id都是一樣的
  int alen = **((int **)a);
  int blen = **((int **)b);
  if (alen == blen) //長度一樣
    return memcmp(*((char **)a) + sizeof(int), 
		  *((char **)b) + sizeof(int), 
		  alen); //真霸氣,所有字節都比較……
  return ((alen < blen) ? -1 : 1);//按長度排序
}

// a collapsed example is a sequence of examples
struct collapsed {
  char **seq;
  int num;
};

// set of collapsed examples
struct data {
  collapsed *x;
  int num;
  int numblocks;
  int *blocksizes;
  float *regmult;
  float *learnmult;
};

// seed the random number generator with the current time
void seed_time() {
 struct timeval tp;
 check(gettimeofday(&tp, NULL) == 0);
 srand48((long)tp.tv_usec);
}

static inline double min(double x, double y) { return (x <= y ? x : y); }
static inline double max(double x, double y) { return (x <= y ? y : x); }

// gradient descent
//---------------參照論文公式17 後的步驟---------------------------------------
void gd(double C, double J, data X, double **w, double **lb) {
//  C=0.0002, J=1, X, w==0, lb==-100);
//    
  int num = X.num; //組數
  
  // state for random permutations
  int *perm = (int *)malloc(sizeof(int)*X.num);
  check(perm != NULL);

  // state for small cache
  int *W = (int *)malloc(sizeof(int)*num);
  check(W != NULL);
  for (int j = 0; j < num; j++)
    W[j] = 0;

  int t = 0;
  while (t < ITER) {  // 5000000 ,霸氣……
    // pick random permutation
    for (int i = 0; i < num; i++) //組數
      perm[i] = i;
	//-------打亂順序-----
	// 論文中是隨機選擇一個樣本,這裏是隨機排好序,再順序取。
	// 類似於隨機取,但是這裏能保證取到全部樣本,避免單個樣本重複被抽到,重複作用
    for (int swapi = 0; swapi < num; swapi++) {
      int swapj = (int)(drand48()*(num-swapi)) + swapi; //drand48 產生 0-1之間的均勻分佈
      int tmp = perm[swapi];
      perm[swapi] = perm[swapj];
      perm[swapj] = tmp;
    }

    // count number of examples in the small cache
    int cnum = 0; //下面的循環部分的實際循環次數
    for (int i = 0; i < num; i++) {
      if (W[i] <= INCACHE) // 3
		cnum++;
    }
	//-------------------------------------------------------
    for (int swapi = 0; swapi < num; swapi++) {
      // select example
      int i = perm[swapi];
      collapsed x = X.x[i];

      // skip if example is not in small cache
	  //負樣本分對一次+1,分錯一次清爲0
	  //連續三次都分對了,那麼這個樣本很有可能是 easy 樣本
	  //直接讓他罰停四次迭代
      if (W[i] > INCACHE) { //3
			W[i]--;
			continue;
      }

      // learning rate
      double T = t + 1000.0; //學習率,直接1/t太大了
      double rateX = cnum * C / T;
      double rateR = 1.0 / T;

      if (t % 10000 == 0) {
		printf(".");
		fflush(stdout);	//清除文件緩衝區,文件以寫方式打開時將緩衝區內容寫入文件
      }
      t++;
      
      // compute max over latent placements
	  //  -----step 3----
      int M = -1;
      double V = 0;
	  // 組內循環,選擇 Zi=argmax β*f 即文中的第3部
	  // 訓練rootfiter時,x.num=1,因爲隨機產生的負樣本其id不同
      for (int m = 0; m < x.num; m++) { 
		double val = 0;
		char *ptr = x.seq[m];
		float *data = EX_DATA(ptr); //特徵數據的地址 第9個數據開始,
		//後面跟着是 block1 label | block2 data|block2 lable | block2 data  
		//                 1      |       1    |     2       |  h*w/2*31個float
		int blocks = NUM_NONZERO(ptr); // phase 1,phase 2 : 2 個,offset,rootfilter
		for (int j = 0; j < blocks; j++) {
		  int b = BLOCK_IDX(data); // 
		  data++;
		  for (int k = 0; k < X.blocksizes[b]; k++)//(1)=1,(2)= root.h*root.w/2*31
			val += w[b][k] * data[k]; //第一次循環是0
		  data += X.blocksizes[b];
		}
		if (M < 0 || val > V) {
		  M = m;
		  V = val;
		}
      }
      
      // update model
	  //-----step.4 也算了step.5 的一半 ---------------
	  // 梯度下降,減小 w
      for (int j = 0; j < X.numblocks; j++) {// 2
		double mult = rateR * X.regmult[j] * X.learnmult[j]; // 0,1  20,1,1/T,對於block2,學習率at就是 1/t,block 1 爲0
		for (int k = 0; k < X.blocksizes[j]; k++) {
		  w[j][k] -= mult * w[j][k]; //不管是分對了,還是分錯了,都要減掉 at*β,見公式17下的4,5 
		}
      }
      char *ptr = x.seq[M];
      int label = LABEL(ptr);
	  //----step.5----------分錯了,往梯度的負方向移動
      if (label * V < 1.0) 
	  {
		W[i] = 0;
		float *data = EX_DATA(ptr);
		int blocks = NUM_NONZERO(ptr);
		for (int j = 0; j < blocks; j++) {
			int b = BLOCK_IDX(data);
			//  yi*cnum * C / T*1,見論文中 公式16,17
			double mult = (label > 0 ? J : -1) * rateX * X.learnmult[b];       
			data++;
			for (int k = 0; k < X.blocksizes[b]; k++)
				w[b][k] += mult * data[k];
			data += X.blocksizes[b];
		}
	  } else if (label == -1) 
	  {
			if (W[i] == INCACHE) //3
				W[i] = WAIT; //10
			else
				W[i]++;
	  }
    }

    // apply lowerbounds
    for (int j = 0; j < X.numblocks; j++) {
      for (int k = 0; k < X.blocksizes[j]; k++) {
		w[j][k] = max(w[j][k], lb[j][k]);
      }
    }

  }

  free(perm);
  free(W);
}

// score examples
double *score(data X, char **examples, int num, double **w) {
  double *s = (double *)malloc(sizeof(double)*num);
  check(s != NULL);
  for (int i = 0; i < num; i++) {
    s[i] = 0.0;
    float *data = EX_DATA(examples[i]);
    int blocks = NUM_NONZERO(examples[i]);
    for (int j = 0; j < blocks; j++) {
      int b = BLOCK_IDX(data);
      data++;
      for (int k = 0; k < X.blocksizes[b]; k++)
        s[i] += w[b][k] * data[k];
      data += X.blocksizes[b];
    }
  }
  return s;  
}

// merge examples with identical labels
void collapse(data *X, char **examples, int num) {
//&X, sorted, num_unique
  collapsed *x = (collapsed *)malloc(sizeof(collapsed)*num);
  check(x != NULL);
  int i = 0;
  x[0].seq = examples;
  x[0].num = 1;
  for (int j = 1; j < num; j++) {
    if (!memcmp(x[i].seq[0]+sizeof(int), examples[j]+sizeof(int), 
		labelsize*sizeof(int))) {
      x[i].num++; //如果label 五個量相同
    } else {
      i++;
      x[i].seq = &(examples[j]);
      x[i].num = 1;
    }
  }
  X->x = x;
  X->num = i+1;  
}

//調用參數 C=0.0002, J=1, hdrfile, datfile, modfile, inffile, lobfile
int main(int argc, char **argv) {  
  seed_time();
  int count;
  data X;

  // command line arguments
  check(argc == 8);
  double C = atof(argv[1]);
  double J = atof(argv[2]);
  char *hdrfile = argv[3];
  char *datfile = argv[4];
  char *modfile = argv[5];
  char *inffile = argv[6];
  char *lobfile = argv[7];

  // read header file
  FILE *f = fopen(hdrfile, "rb");
  check(f != NULL);
  int header[3];
  count = fread(header, sizeof(int), 3, f);
  check(count == 3);
  int num = header[0]; //正負樣本總數
  labelsize = header[1]; // labelsize = 5;  [label id level x y]
  X.numblocks = header[2]; // 2
  X.blocksizes = (int *)malloc(X.numblocks*sizeof(int)); //(1)=1,(2)= root.h*root.w/2*31
  count = fread(X.blocksizes, sizeof(int), X.numblocks, f);
  check(count == X.numblocks);
  X.regmult = (float *)malloc(sizeof(float)*X.numblocks); //0 ,1
  check(X.regmult != NULL);
  count = fread(X.regmult, sizeof(float), X.numblocks, f);
  check(count == X.numblocks);
  X.learnmult = (float *)malloc(sizeof(float)*X.numblocks);//20, 1
  check(X.learnmult != NULL);
  count = fread(X.learnmult, sizeof(float), X.numblocks, f);
  check(count == X.numblocks);
  check(num != 0);
  fclose(f);
  printf("%d examples with label size %d and %d blocks\n",
	 num, labelsize, X.numblocks);
  printf("block size, regularization multiplier, learning rate multiplier\n");
  dim = 0;
  for (int i = 0; i < X.numblocks; i++) {
    dim += X.blocksizes[i];
    printf("%d, %.2f, %.2f\n", X.blocksizes[i], X.regmult[i], X.learnmult[i]);
  }

  // ---------------從 datfile 讀取  正負 examples----------------
  // examples [i] 存儲了第i個樣本的信息 長度爲 1 int + 7 int +dim 個float + 1 byte
  // 1 int 		legth 樣本包括信息頭在內的總字節長度
  // 7 int 		[1/-1 id 0 0 0 2 dim] ,id爲樣本編號,[label id level centry_x centry_y],2是block個數
  // dim float 	feature,dim=2+1+root.h*root.w/2*31,意義如下
  //  		 block1 label | block2 data|block2 lable | block2 data
  //               1      |       1    |     2       |  h*w/2*31個float
  // 1 byte 	unique=0
  f = fopen(datfile, "rb");
  check(f != NULL);
  printf("Reading examples\n");
  
  //+,-example數據
  char **examples = (char **)malloc(num*sizeof(char *)); 
  
  check(examples != NULL);
    for (int i = 0; i < num; i++) {
    // we use an extra byte in the end of each example to mark unique
    // we use an extra int at the start of each example to store the 
    // example's byte length (excluding unique flag and this int)
	//[legth label id level x y  unique] unique=0
    int buf[labelsize+2]; 
	//寫入時的值爲[1/-1 i 0 0 0 2 dim] 
    count = fread(buf, sizeof(int), labelsize+2, f);
    check(count == labelsize+2);
    // byte length of an example's data segment
	
	//---前面七個是頭,後面dim個float是樣本特徵數據,dim=2+1+root.h*root.w/2*31
    int len = sizeof(int)*(labelsize+2) + sizeof(float)*buf[labelsize+1];	
    // memory for data, an initial integer, and a final byte
    examples[i] = (char *)malloc(sizeof(int)+len+1);
	
    check(examples[i] != NULL);
    // set data segment's byte length
    ((int *)examples[i])[0] = len;
    // set the unique flag to zero
    examples[i][sizeof(int)+len] = 0;
    // copy label data into example
    for (int j = 0; j < labelsize+2; j++)
      ((int *)examples[i])[j+1] = buf[j];
    // read the rest of the data segment into the example
    count = fread(examples[i]+sizeof(int)*(labelsize+3), 1, 
		  len-sizeof(int)*(labelsize+2), f);
    check(count == len-sizeof(int)*(labelsize+2));
  }
  fclose(f);
  printf("done\n");

  // sort
  printf("Sorting examples\n");
  char **sorted = (char **)malloc(num*sizeof(char *));
  check(sorted != NULL);
  memcpy(sorted, examples, num*sizeof(char *));
  
  //qsort 庫函數,真正的比較函數爲 comp
  //從小到大,快速排序
  //依次按照 樣本類別->id->level->cx->cy  排序樣本
  //如果前面五個量都一樣……
  //1.等長度,比較所有字節;
  //2.誰長誰小,長度不同是因爲不同的component的 尺寸不一致 
  
  qsort(sorted, num, sizeof(char *), comp); 
  printf("done\n");

  // find unique examples
  // 唯一的樣本,unique flag=1,
  // 相同的樣本第一個樣本的unique flag爲1,其餘爲0 ,有的樣本的位置被,unique替代了,但是並沒有完全刪除掉
  int i = 0;
  int len = *((int *)sorted[0]); //負樣本的第一個
  sorted[0][sizeof(int)+len] = 1; // unique flag 置 1
  for (int j = 1; j < num; j++) {
    int alen = *((int *)sorted[i]);
    int blen = *((int *)sorted[j]);
    if (alen != blen || memcmp(sorted[i] + sizeof(int), sorted[j] + sizeof(int), alen)) //component不同 || 不同樣本
	{
      i++;
      sorted[i] = sorted[j];
      sorted[i][sizeof(int)+blen] = 1; //標記爲 unique
    }
  }
  int num_unique = i+1;
  printf("%d unique examples\n", num_unique);

  // -------------------collapse examples----------------
  // 前面是找完全不一樣的樣本,這裏是分組
  // label 的五個量 [label id level centry_x centry_y] 相同的分爲一組,在detect時,寫入了datfile 
  // 負樣本的 cx,cy都是相對於整張圖片的,正樣本是相對於剪切後的圖像
  // 前面五個全相同,
  // 對於phase1 不可能,因爲正負樣本的id都不相同
  // 對於phase2 正樣本只保留了最有可能是正樣本的樣本,只有一種情況,
  // rootfilter1,rootfilter2在同一張圖片(id相同),檢測出來的 Hard負樣本 的cx,cy相同,因此一組最多應該只能出現2個 (待驗證)
  // 原因是此時的latent variable 爲(cx,cy,component),上述情況相下,我們只能保留component1或者component2
  // 後續訓練時,這兩個量是連續使用的,爲什麼呢??
  // collapse.seq(char **) 記錄了每一組的第一個樣本
  // collapse.num 每組的個數
  // X.num 組數
  // X.x=&collapse[0],也就是第一個 collapse的地址
  collapse(&X, sorted, num_unique);
  printf("%d collapsed examples\n", X.num);

  // initial model
  // 讀modfile文件,得到w的初始值。phase 1 初始化爲全 0,phase 2 爲上一次訓練的結果……
  double **w = (double **)malloc(sizeof(double *)*X.numblocks);//2
  check(w != NULL);
  f = fopen(modfile, "rb");
  for (int i = 0; i < X.numblocks; i++) {
    w[i] = (double *)malloc(sizeof(double)*X.blocksizes[i]); //(1)=1,(2)= root.h*root.w/2*31
    check(w[i] != NULL);
    count = fread(w[i], sizeof(double), X.blocksizes[i], f);
    check(count == X.blocksizes[i]);
  }
  fclose(f);

  // lower bounds
  // 讀lobfile文件,初始化爲全 濾波器參數下線-100 ……
  double **lb = (double **)malloc(sizeof(double *)*X.numblocks);
  check(lb != NULL);
  f = fopen(lobfile, "rb");
  for (int i = 0; i < X.numblocks; i++) {
    lb[i] = (double *)malloc(sizeof(double)*X.blocksizes[i]);
    check(lb[i] != NULL);
    count = fread(lb[i], sizeof(double), X.blocksizes[i], f);
    check(count == X.blocksizes[i]);
  }
  fclose(f);
  

  printf("Training");
  //-------------------------------- train -------------------------------
  //-----梯度下降發訓練參數 w,參見論文 公式17 後面的步驟
  gd(C, J, X, w, lb);
  printf("done\n");

  // save model
  printf("Saving model\n");
  f = fopen(modfile, "wb");
  check(f != NULL);
  //   存儲 block1,block2的訓練結果,w
  for (int i = 0; i < X.numblocks; i++) {
    count = fwrite(w[i], sizeof(double), X.blocksizes[i], f);
    check(count == X.blocksizes[i]);
  }
  fclose(f);

  // score examples
  // ---所有的樣本都的得分,沒有乘以 label y 
  printf("Scoring\n");
  double *s = score(X, examples, num, w);

  // ---------Write info file-------------
  printf("Writing info file\n");
  f = fopen(inffile, "w");
  check(f != NULL);
  for (int i = 0; i < num; i++) {
    int len = ((int *)examples[i])[0];
    // label, score, unique flag
    count = fprintf(f, "%d\t%f\t%d\n", ((int *)examples[i])[1], s[i], 
                    (int)examples[i][sizeof(int)+len]);
    check(count > 0);
  }
  fclose(f);
  
  printf("Freeing memory\n");
  for (int i = 0; i < X.numblocks; i++) {
    free(w[i]);
    free(lb[i]);
  }
  free(w);
  free(lb);
  free(s);
  for (int i = 0; i < num; i++)
    free(examples[i]);
  free(examples);
  free(sorted);
  free(X.x);
  free(X.blocksizes);
  free(X.regmult);
  free(X.learnmult);

  return 0;
}


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