kensun0 / Joint-Cascade-Face-Detection-and-Alignment

ECCV 2014 "Joint cascade face detection and alignment"
46 stars 35 forks source link

question about training #5

Open hanweijing opened 9 years ago

hanweijing commented 9 years ago

When I run the training program, I found the recall rate is always 1 and the false positive rate is always 0.9x. Is the false positive rate too high even in later stages(question 1) such that it has less ability to reject neg window? And I read your code, the threshold for splitting tree node is set as value of random sample. Is this right(question 2)? And the last question 3 is: the number of pos and neg sample is not very equal, why not to perturb mean shape? hope your reply, thanks!

kensun0 commented 9 years ago

A1: you know 1^50=1, 0.9^50=0.0052. the printf function output the reslut when a sample went through only one tree. A2: random forest, two points, random subset(sample), random node(feature). you can read https://en.wikipedia.org/wiki/Random_forest. Also, you can select a node by another optimization way, but it is not a random forest. In fact, i do not kown whther the node is selected by random forest way or optimization way in original paper. A3: you can set the number of neg and pos, i think more neg samples lead to less false positive, and i like low false pos rate more than high true pos rate. in train, mean shape has been perturbed. in test has not.

hanweijing commented 9 years ago

And another wondering point: why last two field of fea[6] use two random landmark? It seems global learning under global shape constrain. I think this lost "local learning" meaning which i suggest just use random offsets pair nearby one landmark.

kensun0 commented 9 years ago

in paper, it said that “To generate a feature, we randomly choose an image scale, pick up two random facial points in the current shape, generate two random offsets with respect to the points and take the difference of the two offsetted pixels as the feature.”

hanweijing commented 9 years ago

I found some problem about "perform hard sample" and wonder whether the process is right. It needs your check. The neg sample which score is big than RandomForest[0].rfs[0][0] will keep as hard negative sample. This is maybe not right. Can you check this? thanks.

kensun0 commented 9 years ago

if (currentfi[n]<rfs[i][j].threshold && ground_truthfaces[n]==-1), then remove neg samples. That what you have said. RandomForest[0].rfs_[0][0] can not exclude neg samples with big score.

Maybe i do not make your problem clear.

hanweijing commented 9 years ago

还是中文表述吧,if (currentfi[n]<rfs[i][j].threshold && ground_truthfaces[n]==-1)用来剔除不符合的负样本,但是在生成新的随机样本来替换该负样本时,似乎判断的逻辑有问题:新的随机生成的负样本在逐个通过RandomForest[s].rfs_[r][t]检测时,如果一旦if (tmpfi>=RandomForest[s].rfs_[r][t].threshold) ,就被保存下来作为补充的负样本。也就是说当前已训练n个弱分类器,新增的随机样本被逐个检验,你在该样本第一次满足tmpfi>=RandomForest[s].rfs_[r][t].threshold时就终止并把它补充进来替换当前的负样本,而不是测试它是否能够全部通过n个弱分类器。不知道我说得对不对。

kensun0 commented 9 years ago

if (currentfi[n]>=RandomForest[s].rfs[r][t].threshold) ,之前没被排除掉的负样本继续存在。 后面你说的就是我要做的事情,貌似CODE也是这么做的吧?或者我做错了?如果有BUG请告知我。THX for (int s=0;s<=stages;++s) { for (int r=0;r<=i;++r) { for (int t=0;t<=j;++t) { //get score Mat rotation; float scale; SimilarityTransform(ProjectShape(current_shapes[n],bounding_boxs[n]),meanshape,rotation,scale); int bincode=0; float score=0; GetResultfromTree(RandomForest[s].rfs_[r][t],images[augmented_images[n]],current_shapes[n],bounding_boxs[n],rotation,scale,&bincode,&score); tmp_fi+=score; if (tmpfi>=RandomForest[s].rfs_[r][t].threshold) { tmp_isface=false; break; } } if(!tmp_isface)break; } if(!tmp_isface)break; } if (!tmp_isface) { current_fi[n]=tmp_fi; current_weight[n]=exp(0.0-ground_truth_faces[n]*current_fi[n]); break; }

kensun0 commented 9 years ago

“你在该样本第一次满足tmpfi>=RandomForest[s].rfs_[r][t].threshold时就终止并把它补充进来替换当前的负样本,而不是测试它是否能够全部通过n个弱分类器” 咕~~(╯﹏╰)b,我不知道你是在描述我的做法,还是说我应该这样做呢。。。或者你可以说下应该怎么做。 我还是没搞清问题。。。

hanweijing commented 9 years ago

“你在该样本第一次满足tmpfi>=RandomForest[s].rfs_[r][t].threshold时就终止并把它补充进来替换当前的负样本,而不是测试它是否能够全部通过n个弱分类器” 这个是你代码的做法,我觉得应该是检测随机的这个样本是否能够通过全部n个弱分类器,把能通过的保留下来替换。

kensun0 commented 9 years ago

是的,你是对的,我稍后修改这个问题。

kensun0 commented 9 years ago

我修改了代码,请帮忙看看是否正确,谢谢

hanweijing commented 9 years ago

主要修改的是两个地方吧:(1). if (tmpfi<RandomForest[s].rfs_[r][t].threshold); (2). if (tmp_isface) {current_fi[n]=tmp_fi; current_weight[n]=exp(0.0-ground_truth_faces[n]*current_fi[n]); break;} 。 我试过这个修改,好像会很容易负样本搜集不足,训练会很慢。可能需要遍历搜集负样本。

kensun0 commented 9 years ago

应该会这样,相对之前来说找负样本更难了。 可以增加负样本分辨率或者增加负样本。 另外,还有个BUG,PERFORM HARD SAMPLE每次的STAGE完成之后没有做GLOBALREGREESION,这也可能是导致负样本不足的原因 : (
我暂时没精力修改,如果你愿意修改可以通知我,我加进来,谢谢。

hanweijing commented 9 years ago

好的,我研究一下你说的另外一个bug。

larsoncs commented 9 years ago

to kensun0, 的确新的代码,寻找负样本更难了,半天不动,估计是找不到负样本,这个会不会和你把召回率一直设为1有关?

kensun0 commented 9 years ago

我测试了下,正样本1W,负样本24W的情况,在stage=0,40棵树,修改前后的耗时如下 原始版本,492S 修改版本,572S

提供一个负样本地址,其中非人脸大约8000张,我扩大了30倍,详见 line 683 in LBFRegressor.cpp http://www.vision.caltech.edu/Image_Datasets/Caltech101/

召回率不设1也行,但是怎么保证5000颗树后的总体召回呢?

larsoncs commented 9 years ago

for (int k=0;k<10;++k),你原文是10倍吧?我是按照你的10倍来的,1.6w的负样本,扩大10倍就是16w,但是卡住了啊,训练几分钟就卡住了。为啥你的没有卡住呢?

kensun0 commented 9 years ago

不清楚,你先确定是慢还是卡住。慢的话,没什么可说的。 卡住有可能,有时会因为图像的分辨率问题导致WHILE出不去,这个问题比较麻烦,你得跟踪一下看看。我太久没做这个东西了,有点忘记了,好像是找不到符合大小的区域?

kensun0 commented 9 years ago

如果是卡住,着重看下是不是卡在这 getRandomBox do { if (img_height>MINHEIGHT) { box_height= random_generator.uniform(MINHEIGHT_1.0,(double)img_height_0.8); } box_width = box_height_old_box.width/old_box.height; } while (box_width > img_width_0.8);

larsoncs commented 9 years ago

to kensun0, tmpfi的值总是小于RandomForest[s].rfs_[r][t].threshold if (tmpfi<RandomForest[s].rfs_[r][t].threshold) 这样的话,程序老是在while(1){}这个循环里面啊,跳不出去。这是啥原因导致的?负样本的问题? while(1) { RNG random_generator(getTickCount()); int tmp_idx=n;

                    BoundingBox new_box;
                    while (tmp_idx==n)
                    {
                        tmp_idx = random_generator.uniform(0.0,ground_truth_faces.size()-1);
                    }

                    getRandomBox(images[augmented_images[n]],bounding_boxs[tmp_idx], new_box);
                    Mat_<float> temp = ProjectShape(ground_truth_shapes[tmp_idx], bounding_boxs[tmp_idx]);
                    ground_truth_shapes[n] = ReProjectShape(temp, new_box);

                    temp = ProjectShape(current_shapes[tmp_idx], bounding_boxs[tmp_idx]);
                    current_shapes[n]=ReProjectShape(temp, new_box);

                    bounding_boxs[n]=new_box;

                    bool tmp_isface=true;
                    float tmp_fi=0;
                    for (int s=0;s<=stages;++s)
                    {
                        for (int r=0;r<=i;++r)
                        {
                            for (int t=0;t<=j;++t)
                            {
                                //get score 
                                Mat_<float> rotation;
                                float scale;
                                SimilarityTransform(ProjectShape(current_shapes[n],bounding_boxs[n]),mean_shape,rotation,scale);
                                int bincode=0;
                                float score=0;
                                GetResultfromTree(RandomForest_[s].rfs_[r][t],images[augmented_images[n]],current_shapes[n],bounding_boxs[n],rotation,scale,&bincode,&score);
                                tmp_fi+=score;
                                if (tmp_fi<RandomForest_[s].rfs_[r][t].threshold)
                                {
                                    tmp_isface=false;
                                    break;
                                }
                            }
                            if(!tmp_isface)break;
                        }
                        if(!tmp_isface)break;
                    }
                    if (tmp_isface)
                    {
                        current_fi[n]=tmp_fi;
                        current_weight[n]=exp(0.0-ground_truth_faces[n]*current_fi[n]);
                        break;
                    }
                }                                    
kensun0 commented 9 years ago

你这不是快找到问题了吗,继续( ⊙ o ⊙ )! 我添加一个新的负样本,不是增加图片,是在原来的那些负样本中滑动(改变窗口),只有全部位置都滑动过了,才会出现找不到可以新增的负样本问题,即无法出循环。你在这个方向上找找吧,我猜是因为尺寸小,或者设置不对,导致可滑动的有效位置不足。

hanweijing commented 9 years ago

补充负样本时的3层for循环(变量s,r,t控制的循环)似乎有点问题,是不是应该是先用(cur_stage-1)_num_landmarks_num_trees个tree先检测score是否通过(此处每次stage还需更新shape),然后再用(cur_landmark_id-1)*num_trees个tree检测score是否通过,最后用cur_tree_id个tree检验score是否通过?

kensun0 commented 9 years ago

是的。谢谢。 我修改了部分,但是那个更新SHAPE的操作,我没精力做了。。。 修改后,还是上次的参数 之前:572S 现在:2888S

larsoncs commented 9 years ago

这里不是更新了负样本的shape吗?难道我理解错了? getRandomBox(images[augmented_images[n]],bounding_boxs[tmp_idx], newbox); Mat temp = ProjectShape(ground_truth_shapes[tmp_idx], bounding_boxs[tmp_idx]); ground_truth_shapes[n] = ReProjectShape(temp, new_box);

                    temp = ProjectShape(current_shapes[tmp_idx], bounding_boxs[tmp_idx]);
                    current_shapes[n]=ReProjectShape(temp, new_box);

                    bounding_boxs[n]=new_box;
kensun0 commented 9 years ago

这个是BOX相对坐标系和图像坐标系的转换,不是更改SHAPE。

kensun0 commented 9 years ago

我更改了负样本的生成过程,包括每个STAGE后的形状回归。 主要是针对已经找不到负样本的图片进行旋转或者用已经在内存中的图替换,但是寻找负样本还是挺慢,再不行就需要从外界读取一张图片替换内存中的了。。。有什么好的建议吗?

larsoncs commented 9 years ago

负样本不够,我觉得只能从外界获取足够的负样本加载到内存。

hanweijing commented 9 years ago

这个方法感觉寻找负样本的速度是个问题,困惑中~

kensun0 commented 9 years ago

主要是之前我在一个负样本上画了10倍的窗体,导致负样本消耗过快。。。最近改的这个不增加窗体了,训练速度好些,不过手头负样本不足,导致训练后的模型排除负样本变慢了。