Skip to content

Commit

Permalink
impl parallel hard negative mining
Browse files Browse the repository at this point in the history
  • Loading branch information
luoyetx committed Oct 25, 2015
1 parent 739cf76 commit c9aaf98
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 16 deletions.
1 change: 1 addition & 0 deletions include/jda/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class Config {

double scale_factor; // hard negative mining parameters
int x_step, y_step;
int mining_pool_size;

std::string train_pos_txt; // a text file for train positive dataset
std::string train_neg_txt; // a text file for train negative dataset
Expand Down
4 changes: 3 additions & 1 deletion include/jda/data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ class NegGenerator {
* state, it may be very hard to generate enough hard negative samples, we may fail with
* real size smaller than `int size`. We will give back all negative training samples with
* their scores and current shapes for further training.
*
* :Update: OpenMP supported hard negative mining, we may have `real size` > `size`
*/
int Generate(JoinCascador& joincascador, int size, \
int Generate(const JoinCascador& joincascador, int size, \
std::vector<cv::Mat>& imgs, std::vector<double>& scores, \
std::vector<cv::Mat_<double> >& shapes);

Expand Down
3 changes: 2 additions & 1 deletion src/jda/btcart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ void BoostCart::Train(DataSet& pos, DataSet& neg) {
DataSet::UpdateWeights(pos, neg);
int landmark_id = k % landmark_n;
cart.Initialize(stage, landmark_id);
LOG("Current Positive DataSet Size is %d", pos.size);
LOG("Current Negative DataSet Size is %d", neg.size);
// train cart
TIMER_BEGIN
LOG("Train %d th Cart", k + 1);
Expand All @@ -64,7 +66,6 @@ void BoostCart::Train(DataSet& pos, DataSet& neg) {
double neg_drop_rate = static_cast<double>(neg_n - neg.size) / \
static_cast<double>(neg_n) * 100.;
LOG("Pos drop rate = %.2lf%%, Neg drop rate = %.2lf%%", pos_drop_rate, neg_drop_rate);
LOG("Current Positive DataSet Size is %d", pos.size);
neg_rejected += neg_n - neg.size;
LOG("Current Negative DataSet Reject Size is %d", neg_rejected);
}
Expand Down
1 change: 1 addition & 0 deletions src/jda/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ Config::Config() {
img_q_height = img_q_width = 40;
x_step = y_step = 20;
scale_factor = 0.8;
mining_pool_size = 5000;
esp = 2.2e-16;
int feats[5] = { 1000, 1000, 1000, 1000, 1000 };
double nps[5] = { 1, 1, 1, 1, 1 };
Expand Down
44 changes: 30 additions & 14 deletions src/jda/data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ void DataSet::MoreNegSamples(int pos_size, double rate) {
vector<Mat_<double> > shapes_;
const int extra_size = neg_generator.Generate(*joincascador, size_, \
imgs_, scores_, shapes_);
LOG("We have mined %d hard negative samples", extra_size);
const int expanded = imgs.size() + imgs_.size();
imgs.reserve(expanded);
imgs_half.reserve(expanded);
Expand Down Expand Up @@ -295,26 +296,41 @@ void DataSet::LoadDataSet(DataSet& pos, DataSet& neg) {
NegGenerator::NegGenerator() {}
NegGenerator::~NegGenerator() {}

int NegGenerator::Generate(JoinCascador& joincascador, int size, \
int NegGenerator::Generate(const JoinCascador& joincascador, int size, \
vector<Mat>& imgs, vector<double>& scores, \
vector<Mat_<double> >& shapes) {
imgs.clear();
scores.clear();
shapes.clear();
imgs.reserve(size);
scores.reserve(size);
shapes.reserve(size);
double score = 0;
Mat_<double> shape;
imgs.reserve(size * 2); // enough memory to overflow
scores.reserve(size * 2);
shapes.reserve(size * 2);

int pool_size = Config::GetInstance().mining_pool_size;
vector<Mat> region_pool(pool_size);
vector<double> score_pool(pool_size);
vector<Mat_<double> > shape_pool(pool_size);
vector<bool> used(pool_size, false);
while (size > 0) {
// We generate one by one to validate whether it is hard enough
Mat region = NextImage();
bool is_face = joincascador.Validate(region, score, shape);
if (is_face) {
imgs.push_back(region);
scores.push_back(score);
shapes.push_back(shape);
size--;
// We generate a negative sample pool for validation
for (int i = 0; i < pool_size; i++) {
region_pool[i] = NextImage();
}

#pragma omp parallel for
for (int i = 0; i < pool_size; i++) {
bool is_face = joincascador.Validate(region_pool[i], score_pool[i], shape_pool[i]);
if (is_face) used[i] = true;
}

// collect
for (int i = 0; i < pool_size; i++) {
if (used[i]) {
imgs.push_back(region_pool[i]);
scores.push_back(score_pool[i]);
shapes.push_back(shape_pool[i]);
size--;
}
}
}
return imgs.size();
Expand Down

0 comments on commit c9aaf98

Please sign in to comment.