Skip to content

Commit

Permalink
Merge pull request #659 from vigsterkr/latent
Browse files Browse the repository at this point in the history
Switch from function ptrs to CLatentModel Since handling function pointers in SWIG is quite complex switching to CLatentModel for implementing PSI and infer latent variable functions
  • Loading branch information
lisitsyn committed Jul 22, 2012
2 parents 0e9c608 + 924dc20 commit 532a5dd
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 157 deletions.
75 changes: 43 additions & 32 deletions examples/undocumented/libshogun/classifier_latent_svm.cpp
Expand Up @@ -33,44 +33,56 @@ struct CHOGFeatures : public CLatentData
virtual const char* get_name() const { return "HOGFeatures"; }
};

static void psi_hog(CLatentLinearMachine& llm, CLatentData* f, CLatentData* l, float64_t* psi)
class CObjectDetector: public CLatentModel
{
CHOGFeatures* hf = (CHOGFeatures*) f;
CBoundingBox* bb = (CBoundingBox*) l;
for (int i = 0; i < llm.get_psi_size(); ++i)
{
psi[i] = hf->hog[bb->x_pos][bb->y_pos][i];
}
}
public:
CObjectDetector() {}
CObjectDetector(CLatentFeatures* feat, CLatentLabels* labels) : CLatentModel(feat, labels) {}

static CLatentData* infer_latent_variable(CLatentLinearMachine& llm, CLatentData* f)
{
int32_t pos_x = 0, pos_y = 0;
int32_t w_dim;
float64_t max_score;
virtual ~CObjectDetector() {}

SGVector<float64_t> w = llm.get_w();
CHOGFeatures* hf = dynamic_cast<CHOGFeatures*> (f);
for (int i = 0; i < hf->width; ++i)
{
for (int j = 0; j < hf->height; ++j)
virtual int32_t get_dim() const { return HOG_SIZE; }

virtual SGVector<float64_t> get_psi_feature_vector(index_t idx)
{
float64_t score = w.dot(w.vector, hf->hog[i][j], w.vlen);
CHOGFeatures* hf = (CHOGFeatures*) m_features->get_sample(idx);
CBoundingBox* bb = (CBoundingBox*) m_labels->get_latent_label(idx);
SGVector<float64_t> psi_v(get_dim());
for (int i = 0; i < psi_v.vlen; ++i)
{
psi_v.vector[i] = hf->hog[bb->x_pos][bb->y_pos][i];
}
return psi_v;
}

virtual CLatentData* infer_latent_variable(const SGVector<float64_t>& w, index_t idx)
{
int32_t pos_x = 0, pos_y = 0;
float64_t max_score;

if (score > max_score)
CHOGFeatures* hf = (CHOGFeatures*) m_features->get_sample(idx);
for (int i = 0; i < hf->width; ++i)
{
pos_x = i;
pos_y = j;
max_score = score;
for (int j = 0; j < hf->height; ++j)
{
float64_t score = w.dot(w.vector, hf->hog[i][j], w.vlen);

if (score > max_score)
{
pos_x = i;
pos_y = j;
max_score = score;
}
}
}
SG_SDEBUG("%d %d %f\n", pos_x, pos_y, max_score);
CBoundingBox* h = new CBoundingBox(pos_x, pos_y);
SG_REF(h);

return h;
}
}
SG_SDEBUG("%d %d %f\n", pos_x, pos_y, max_score);
CBoundingBox* h = new CBoundingBox(pos_x, pos_y);
SG_REF(h);

return h;
}
};

static void read_dataset(char* fname, CLatentFeatures*& feats, CLatentLabels*& labels)
{
Expand Down Expand Up @@ -176,9 +188,8 @@ int main(int argc, char** argv)
/* train the classifier */
float64_t C = 10.0;

CLatentLinearMachine llm(C, train_feats, train_labels, HOG_SIZE);
llm.set_psi(psi_hog);
llm.set_infer(infer_latent_variable);
CObjectDetector* od = new CObjectDetector(train_feats, train_labels);
CLatentLinearMachine llm(od, C);
llm.train();

// CLatentFeatures* test_feats = NULL;
Expand Down
135 changes: 43 additions & 92 deletions src/shogun/classifier/svm/LatentLinearMachine.cpp
Expand Up @@ -18,40 +18,29 @@
using namespace shogun;

CLatentLinearMachine::CLatentLinearMachine()
: argmax_h(NULL),
psi(NULL),
infer(NULL)
: CLinearMachine()
{
init();
}

CLatentLinearMachine::~CLatentLinearMachine()
{
SG_UNREF(m_latent_feats);
}

CLatentLinearMachine::CLatentLinearMachine(float64_t C,
CLatentFeatures* traindat,
CLabels* trainlab,
index_t psi_size)
: argmax_h(NULL),
psi(NULL),
infer(NULL),
m_latent_feats(traindat)
CLatentLinearMachine::CLatentLinearMachine(CLatentModel* model, float64_t C)
: CLinearMachine()
{
ASSERT(traindat != NULL);
ASSERT(trainlab != NULL);

init();
m_C1 = m_C2 = C;
m_psi_size = psi_size;
set_model(model);

set_labels(trainlab);
set_w(SGVector<float64_t> (m_psi_size));
index_t feat_dim = m_model->get_dim();
set_w(SGVector<float64_t> (feat_dim));

/* create the temporal storage for PSI features */
SGMatrix<float64_t> psi_m(m_psi_size, m_latent_feats->get_num_vectors());
((CDenseFeatures<float64_t>*)features)->set_feature_matrix(psi_m);
SGMatrix<float64_t> psi_m(feat_dim, m_model->get_num_vectors());
((CDenseFeatures<float64_t>*) features)->set_feature_matrix(psi_m);
}

CLatentLinearMachine::~CLatentLinearMachine()
{
SG_UNREF(m_model);
}

CLatentLabels* CLatentLinearMachine::apply()
Expand All @@ -64,94 +53,56 @@ CLatentLabels* CLatentLinearMachine::apply()

CLatentLabels* CLatentLinearMachine::apply(CFeatures* data)
{
if (m_model == NULL)
SG_ERROR("LatentModel is not set!\n");

CLatentFeatures* lf = CLatentFeatures::obtain_from_generic(data);
int32_t num_examples = lf->get_num_vectors();
SGMatrix<float64_t> psi_matrix(m_psi_size, num_examples);
CDenseFeatures<float64_t> psi_feats(psi_matrix);
m_model->set_features(lf);
index_t num_examples = m_model->get_num_vectors();
CLatentLabels* labels = new CLatentLabels(num_examples);

for (int i = 0; i < num_examples; ++i)
for (index_t i = 0; i < num_examples; ++i)
{
/* find h for the example */
CLatentData* x = lf->get_sample(i);
CLatentData* h = infer(*this, x);
CLatentData* h = m_model->infer_latent_variable(w, i);
labels->set_latent_label(i, h);
SGVector<float64_t> psi_feat = psi_feats.get_feature_vector(i);
SGVector<float64_t> psi_feat = m_model->get_psi_feature_vector(i);

/* calculate and set y for the example */
psi(*this, x, h, psi_feat.vector);
float64_t y = w.dot(w.vector, psi_feat.vector, w.vlen);
labels->set_label(i, y);
}

return labels;
}

void CLatentLinearMachine::set_argmax(argmax_func usr_argmax)
{
ASSERT(usr_argmax != NULL);
argmax_h = usr_argmax;
}

void CLatentLinearMachine::set_psi(psi_func usr_psi)
void CLatentLinearMachine::set_model(CLatentModel* latent_model)
{
ASSERT(usr_psi != NULL);
psi = usr_psi;
ASSERT(latent_model != NULL);
SG_UNREF(m_model);
SG_REF(latent_model);
m_model = latent_model;
}

void CLatentLinearMachine::default_argmax_h(CLatentLinearMachine& llm,
void* userData)
{
SGVector<float64_t> w = llm.get_w();
CLatentFeatures* features = llm.get_latent_features();
CLatentLabels* labels = CLatentLabels::obtain_from_generic(llm.get_labels());

int32_t num = features->get_num_vectors();
ASSERT(num > 0);

/* argmax_h only for positive examples */
for (int i = 0; i < num; ++i)
{
if (labels->get_label(i) == 1)
{
/* infer h and set it for the argmax_h <w,psi(x,h)> */
CLatentData* latent_data = llm.infer(llm, features->get_sample(i));
labels->set_latent_label(i, latent_data);
}
}
SG_UNREF(features);
}

void CLatentLinearMachine::set_infer(infer_func usr_infer)
{
ASSERT(usr_infer != NULL);
infer = usr_infer;
}

void CLatentLinearMachine::compute_psi()
void CLatentLinearMachine::cache_psi_vectors()
{
ASSERT(features != NULL);
int32_t num_vectors = features->get_num_vectors();
CLatentLabels* labels = CLatentLabels::obtain_from_generic(m_labels);
for (int i = 0; i < num_vectors; ++i)
index_t num_vectors = features->get_num_vectors();
for (index_t i = 0; i < num_vectors; ++i)
{
SGVector<float64_t> psi_feat = dynamic_cast<CDenseFeatures<float64_t>*>(features)->get_feature_vector(i);
CLatentData* h = labels->get_latent_label(i);
CLatentData* x = m_latent_feats->get_sample(i);
psi(*this, x, h, psi_feat.vector);
SGVector<float64_t> psi_feat =
dynamic_cast<CDenseFeatures<float64_t>*>(features)->get_feature_vector(i);
memcpy(psi_feat.vector, m_model->get_psi_feature_vector(i).vector, psi_feat.vlen*sizeof(float64_t));
}
}

bool CLatentLinearMachine::train_machine(CFeatures* data)
{
if (psi == NULL)
SG_ERROR("The PSI function is not implemented!\n");

if (infer == NULL)
SG_ERROR("The Infer function is not implemented!\n");
if (m_model == NULL)
SG_ERROR("LatentModel is not set!\n");

SG_DEBUG("Initialise PSI (x,h)\n");
compute_psi();
cache_psi_vectors();

/*
* define variables for calculating the stopping
Expand All @@ -169,10 +120,13 @@ bool CLatentLinearMachine::train_machine(CFeatures* data)
SG_DEBUG("iteration: %d\n", iter);
/* do the SVM optimisation with fixed h* */
SG_DEBUG("Do the inner loop of CCCP: optimize for w for fixed h*\n");

/* TODO: change code that it can support structural SVM! */
CSVMOcas svm(m_C1, features, m_labels);
CLatentLabels* labels = m_model->get_labels();
CSVMOcas svm(m_C1, features, labels);
svm.set_epsilon(inner_eps);
svm.train();
SG_UNREF(labels);

/* calculate the decrement */
primal_obj = svm.compute_primal_objective();
Expand All @@ -192,10 +146,10 @@ bool CLatentLinearMachine::train_machine(CFeatures* data)
SG_DEBUG("Find and set h_i = argmax_h (w, psi(x_i,h))\n");
SGVector<float64_t> cur_w = svm.get_w();
memcpy(w.vector, cur_w.vector, cur_w.vlen*sizeof(float64_t));
argmax_h(*this, NULL);
m_model->argmax_h(w);

SG_DEBUG("Recalculating PSI (x,h) with the new h variables\n");
compute_psi();
cache_psi_vectors();

/* increment iteration counter */
iter++;
Expand All @@ -211,15 +165,12 @@ void CLatentLinearMachine::init()
m_max_iter = 400;
features = new CDenseFeatures<float64_t> ();
SG_REF(features);

if (argmax_h == NULL)
set_argmax(default_argmax_h);
m_model = NULL;

m_parameters->add(&m_C1, "C1", "Cost constant 1.");
m_parameters->add(&m_C2, "C2", "Cost constant 2.");
m_parameters->add(&m_epsilon, "epsilon", "Convergence precision.");
m_parameters->add(&m_max_iter, "max_iter", "Maximum iterations.");
m_parameters->add(&m_psi_size, "psi_size", "PSI feature vector dimension.");
m_parameters->add((CSGObject**) &m_latent_feats, "latent_feats", "Latent features");
m_parameters->add((CSGObject**) &m_model, "latent_model", "Latent Model.");
}

0 comments on commit 532a5dd

Please sign in to comment.