在windows下執行Felzenszwalb的Deformable Part Model(DPM)原始碼voc-release3.1來訓練自己的模型

masikkk發表於2014-04-15

我的環境

DPM原始碼版本:voc-release3.1

VOC開發包版本:VOC2007_devkit_08-Jun

使用的訓練資料集:VOC2007

                                 包括:訓練驗證集:VOC2007_trainval_06-Nov(438MB)

                                             測試集:VOC2007_test_06-Nov-2007(430MB)

Matlab版本:MatlabR2012b

c++編譯器:VS2010

系統:Win7 32位

 

        為什麼不使用voc-release4.01呢?因為第4版中加入了目標檢測語法(Grammars),並且還使用了非對稱部件分部等等,雖然準確度提高了,但原始碼變得更加複雜,不利於原始碼分析。而相對來說第3版精簡了不少,更容易分析。

        首先需要下載voc-release3.1和VOCdevkit開發包:

        Deformable Part Model 第三版voc-release3.1下載:http://cs.brown.edu/~pff/latent-release3/

        PASCAL VOC 2007 資料集及開發包下載:http://pascallin.ecs.soton.ac.uk/challenges/VOC/voc2007/index.html

   注意不要只下載訓練驗證集VOC2007_trainval_06-Nov,還要下載測試集VOC2007_test_06-Nov-2007,然後把測試集中的xml標註檔案拷貝到VOCdevkit\VOC2007\Annotations資料夾內,把測試集中的jpeg圖片拷貝到VOCdevkit\VOC2007\JPEGImages資料夾內,這樣整個資料集才完整,否則在訓練或測試的時候可能出現找不到需要讀取的標註檔案或圖片的錯誤。


       有關Deformable Part Model參見論文

       A Discriminatively Trained, Multiscale,Deformable Part Model[CVPR 2008]的中文翻譯

       Object Detection with Discriminatively Trained Part Based Models[PAMI 2010]的中文翻譯 

       及 有關可變形部件模型(Deformable Part Model)的一些說明

       Pedro Felzenszwalb的個人主頁:http://cs.brown.edu/~pff/



1、修改globals.m中的一些全域性變數(主要是目錄設定)

cachedir= 'D:\DPMtrain\VOCCache\';

% 訓練好的模型結果和中間資料的檔案目錄,此目錄可以自己任意指定

 

tmpdir ='D:\DPMtrain\VOCtemp\';

% 訓練中用到的臨時檔案的目錄,臨時檔案可能會很大,此目錄可以自己任意指定

 

VOCdevkit =['H:\文件檔案\工作\█計算機視覺█\資料集\PascalVOC\VOC2007\VOCdevkit'];

% PASCAL VOC 開發包目錄,定位到VOC開發包所在目錄

 

2、修改VOCdevkit開發包中的VOCinit.m檔案,設定資料集目錄

我們使用VOC2007資料集,所以將VOC2006標識設為false(預設就是false)。

如果將解壓出來的VOC2007資料集的資料夾放在VOCdevkit目錄下的話,就不用再修改VOCinit.m中的目錄設定了,因為程式碼中預設就是這樣的目錄安排。但如果想把資料集放到其他地方,可以修改VOCopts.datadir目錄,指向資料集所在目錄。

我是直接將VOC2007資料夾放在了VOCdevkit下,目錄結構如下:

-VOCdevkit

   -local

      -VOC2006

      -VOC2007

   -results

      -VOC2006

      -VOC2007

   -VOC2007

      -Annotations

      -ImageSets

      -JPEGImages

      -SegmentationClass

      -SegmentationObject

   -VOCcode

 

 

3、pascal_data.m 從PASCAL資料集中獲取指定類別目標的訓練資料

分析程式碼時,為了避免載入整個資料集,所以我在VOCdevkit\VOC2007\ImageSets\Main中新建了兩個圖片檔案列表:

trainval_smallset.txt和train_smallset.txt

每個裡面只有一兩百個檔名,分別拷貝自trainval.txt和train.txt。

對應的,在pascal_data.m中修改下程式碼,載入我新建的小檔案集:

載入正樣本列表改為:

ids= textread(sprintf(VOCopts.imgsetpath, 'trainval_smallset'), '%s');

負樣本列表對應修改。

 

這裡要注意下:VOCdevkit\VOC2007\ImageSets\Main中有幾個列表檔案容易混淆:

train.txt 是所有用來訓練的圖片檔案的檔名列表

trianval.txt是所有用來訓練和驗證的圖片檔案的檔名列表

val.txt是所有用來驗證的圖片檔案的檔名列表

上面三個集合對應所有目標類別的訓練和驗證。

還有幾個train_train.txt,train_trainval.txt,train.val.txt

這三個是“火車”類別的訓練、訓練+驗證、驗證圖片集,

所以要分清楚。

 

4、修改rewritedat.m檔案中的兩個系統呼叫命令

在matlab命令列中執行pascal(‘person’,1),即訓練含一個元件(component)模型的人體類別DPM模型,看看出什麼錯誤,一點一點來修改。

錯誤如下:

'mv' 不是內部或外部命令,也不是可執行的程式 

Error in rewritedat (line 38)

所以來修改下rewritedat.m檔案

將第13行的 unix(['mv ' datfile ' ' oldfile]);

替換為:system(['move ' datfile ' ' oldfile]);

第44行的 unix(['cp ' inffile ' ' oldfile]);

替換為:system(['copy ' inffile ' ' oldfile]);

 

5、修改train.m檔案

修改完rewritedat.m檔案後,再次呼叫pascal(‘person’,1),這次的問題是:

executing: ./learn 0.0020 1.0000 D:\DPMtrain\VOCtemp\person.hdr D:\DPMtrain\VOCtemp\person.dat D:\DPMtrain\VOCtemp\person.mod D:\DPMtrain\VOCtemp\person.inf D:\DPMtrain\VOCtemp\person.lob
'.' 不是內部或外部命令,也不是可執行的程式或批處理檔案。 

這又是一個linux下的系統呼叫,所以需要修改train.m,

將第123行的    cmd = sprintf('./learn %.4f %.4f %s %s %s %s %s', ...
                                                   C, J, hdrfile, datfile, modfile, inffile, lobfile);

命令字串中的./去掉,變成:

cmd = sprintf('learn %.4f %.4f %s %s %s %s %s', ...
                           C, J, hdrfile, datfile, modfile, inffile, lobfile);

將第128行的    status = unix(cmd);

替換為: status = system(cmd);

修改完這部分後,還是會提示'learn' 不是內部或外部命令,也不是可執行的程式 ,因為我們還沒有編譯learn.cc呢。


6、編譯learn.cc

首先將learn.cc改名為learn.cpp,在VS2010中新建一個空的控制檯工程,新增原檔案learn.cpp

嘗試編譯,錯誤提示為:Cannot open include file: 'sys/time.h': No such file or directoryc:\test\dpm_learn\dpm_learn\learn.cpp5

這是linux中的目錄格式,直接將第5行的#include <sys/time.h> 改為 #include <time.h>,再次編譯,然後就出現了一大堆錯誤,有以下幾點:

(1) windows中的time.h檔案中沒有結構體timeval的定義,也沒有gettimeofday函式,從網上找了一段程式碼新增到learn.cpp中。

(2) 上一步加入的程式碼需要新增標頭檔案windows.h,而包含進這個標頭檔案後,就包含了系統中定義的min和max函式,所以需要註釋掉learn.cpp中定義的min和max函式,否則出錯。

(3) windows中沒有drand48和srand48的定義,把網友部落格中自己寫的這兩個函式新增進去。

(4) windows中沒有INFINITY的定義,自己定義一個。

(5) main函式中的int buf[labelsize+2];出錯,原因是VS中的c++編譯器不允許用變數指定陣列長度,改為使用new動態分配:

int *buf = new int[labelsize+2]; ,同時,在同一作用域最後delete [] buf;

以上5點修改完後,就沒有錯誤了,當然還有些警告,不過不用管,編譯執行,生成learn.exe可執行檔案,拷貝到voc-release3.1目錄下,等待訓練時被matlab程式碼通過系統呼叫來執行。

修改完後的完整learn.cpp檔案為:

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

#include <time.h> //windows中用以替代sys/time.h
#include <windows.h>//windows中用以替代sys/time.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)

//windows中沒有INFINITY的定義,自己定義一個
#define INFINITY 0xFFFFFFFFF  


int labelsize;
int dim;

//windows下沒有gettimeofday函式,從網上找的一個替代函式  
int gettimeofday(struct timeval *tp, void *tzp)  
{  
	time_t clock;  
	struct tm tm;  
	SYSTEMTIME wtm;  
	GetLocalTime(&wtm);  
	tm.tm_year     = wtm.wYear - 1900;  
	tm.tm_mon     = wtm.wMonth - 1;  
	tm.tm_mday     = wtm.wDay;  
	tm.tm_hour     = wtm.wHour;  
	tm.tm_min     = wtm.wMinute;  
	tm.tm_sec     = wtm.wSecond;  
	tm. tm_isdst    = -1;  
	clock = mktime(&tm);  
	tp->tv_sec = clock;  
	tp->tv_usec = wtm.wMilliseconds * 1000;  
	return (0);  
}  

//參照網上自己寫的drand48和srand48函式

#define MNWZ 0x100000000    
#define ANWZ 0x5DEECE66D    
#define CNWZ 0xB16   

static unsigned long long seed = 1;  

double drand48(void)    
{    
	seed = (ANWZ * seed + CNWZ) & 0xFFFFFFFFFFFFLL;    
	unsigned int x = seed >> 16;    
	return  ((double)x / (double)MNWZ);       
}  

//static unsigned long long seed = 1;  

void srand48(unsigned int i)    
{    
	seed  = (((long long int)i) << 16) | rand();    
}  

// comparison function for sorting examples 
int comp(const void *a, const void *b) {
  // sort by extended label first, and whole example second...
  int c = memcmp(*((char **)a) + sizeof(int), 
		 *((char **)b) + sizeof(int), 
		 labelsize*sizeof(int));
  if (c)
    return c;
  
  // labels are the same  
  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);
}

//包含include.h後,系統中有min和max函式的定義,所以註釋掉下面的定義,否則出錯
//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
void gd(double C, double J, data X, double **w, double **lb) {
  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) {
    // 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;
      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)
	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
      if (W[i] > INCACHE) {
	W[i]--;
	continue;
      }

      // learning rate
      double T = t + 1000.0;
      double rateX = cnum * C / T;
      double rateR = 1.0 / T;

      if (t % 10000 == 0) {
	printf(".");
	fflush(stdout);
      }
      t++;
      
      // compute max over latent placements
      int M = -1;
      double V = 0;
      for (int m = 0; m < x.num; m++) {
	double val = 0;
	char *ptr = x.seq[m];
	float *data = EX_DATA(ptr);
	int blocks = NUM_NONZERO(ptr);
	for (int j = 0; j < blocks; j++) {
	  int b = BLOCK_IDX(data);
	  data++;
	  for (int k = 0; k < X.blocksizes[b]; k++)
	    val += w[b][k] * data[k];
	  data += X.blocksizes[b];
	}
	if (M < 0 || val > V) {
	  M = m;
	  V = val;
	}
      }
      
      // update model
      for (int j = 0; j < X.numblocks; j++) {
	double mult = rateR * X.regmult[j] * X.learnmult[j];
	for (int k = 0; k < X.blocksizes[j]; k++) {
	  w[j][k] -= mult * w[j][k];
	}
      }
      char *ptr = x.seq[M];
      int label = LABEL(ptr);
      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);
	  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)
	  W[i] = WAIT;
	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) {
  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++;
    } else {
      i++;
      x[i].seq = &(examples[j]);
      x[i].num = 1;
    }
  }
  X->x = x;
  X->num = i+1;  
}

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];
  X.numblocks = header[2];
  X.blocksizes = (int *)malloc(X.numblocks*sizeof(int));
  count = fread(X.blocksizes, sizeof(int), X.numblocks, f);
  check(count == X.numblocks);
  X.regmult = (float *)malloc(sizeof(float)*X.numblocks);
  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);
  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]);
  }

  // read examples
  f = fopen(datfile, "rb");
  check(f != NULL);
  printf("Reading examples\n");
  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)

    //int buf[labelsize+2]; //windows下不支援這樣分配,換成new動態分配
	  int *buf = new int[labelsize+2]; //動態分配

    count = fread(buf, sizeof(int), labelsize+2, f);
    check(count == labelsize+2);
    // byte length of an example's data segment
    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));

	delete [] buf;  //刪除buf
  }
  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(sorted, num, sizeof(char *), comp);
  printf("done\n");

  // find unique examples
  int i = 0;
  int len = *((int *)sorted[0]);
  sorted[0][sizeof(int)+len] = 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)) {
      i++;
      sorted[i] = sorted[j];
      sorted[i][sizeof(int)+blen] = 1;
    }
  }
  int num_unique = i+1;
  printf("%d unique examples\n", num_unique);

  // collapse examples
  collapse(&X, sorted, num_unique);
  printf("%d collapsed examples\n", X.num);

  // initial model
  double **w = (double **)malloc(sizeof(double *)*X.numblocks);
  check(w != NULL);
  f = fopen(modfile, "rb");
  for (int i = 0; i < X.numblocks; i++) {
    w[i] = (double *)malloc(sizeof(double)*X.blocksizes[i]);
    check(w[i] != NULL);
    count = fread(w[i], sizeof(double), X.blocksizes[i], f);
    check(count == X.blocksizes[i]);
  }
  fclose(f);

  // lower bounds
  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);
  
  // train
  printf("Training");
  gd(C, J, X, w, lb);
  printf("done\n");

  // save model
  printf("Saving model\n");
  f = fopen(modfile, "wb");
  check(f != NULL);
  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
  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;
}


7 陣列下標越界錯誤

陣列越界錯誤有好幾處,我注意到的有:

(1)pascal_train.m中

合併模型並進行LSVM訓練部分

即註釋 %merge models and train using latent detections & hard negatives下的

model = train(cls, model, pos, neg(1:200), 0, 0, 2, 2, 2^28, true, 0.7);

原因是這裡我的負樣本集neg中的負樣本數目沒有達到200個,所以這裡改為:

model = train(cls, model, pos, neg(1:min(length(neg),200)), 0, 0, 2, 2, 2^28, true, 0.7);


還有新增部件更新模型部分

即註釋% add parts and update models using latent detections & hard negatives.下的兩處呼叫train函式的地方,

都將neg(1:200)改為neg(1:min(length(neg),200))


(2)網友pozen提出的rewritedat.m中可能出現的下標越界情況,

將28行左右的dim = info(end);改為:

    if length(info) == 0
        dim = 0;
    else
        dim = info(end);
    end

將38行左右的dim = y(end);改為:

    if length(y) == 0
        dim = 0;
    else
        dim = y(end);
    end


參考:

http://blog.csdn.net/pozen/article/details/7103412

http://blog.csdn.net/dreamd1987/article/details/7399151


自己用少量資料訓練了一個單元件人體模型,擷取trainval.txt中的前50個圖片檔名做正樣本,train.txt中的前300個做負樣本,經過pascal.data函式處理後,獲得了含176個負樣本的neg陣列,含45個正樣本的pos陣列。learn.cc中的迭代次數我沒改,還是每次train迭代500萬次(感覺時間都花在這裡了,如果只是做測試的話,可以修改learn.cc中的迭代次數ITER值),訓練過程用了大概1小時左右吧,訓練完後用PASCAL開發包中的評價函式做了評價,正確率和召回率都是0,平均精度AP也是0,在預料之中。

訓練完後,最終結果在cachedir目錄中,很多中間資料也在這個目錄中,如下:

其中person_final.mat就是訓練好的最終模型。原始碼中訓練的每個階段都會將中間資料儲存下來,所以即使某一階段出現了錯誤,下次重新執行時自動載入上次儲存的資料,而不用再次計算,非常方便。


訓練出來的模型的視覺化如下:


參考

在windows下執行Felzenszwalb的Deformable Part Model(DPM)原始碼voc-release3.1來訓練自己的模型


相關文章