void build_factor_graph(MultilabelParameter param, SGMatrix<float64_t> feats, SGMatrix<int32_t> labels, CFactorGraphFeatures * fg_feats, CFactorGraphLabels * fg_labels, const DynArray<CTableFactorType *>& v_ftp_u, const DynArray<CTableFactorType *>& v_ftp_t) { int32_t num_sample = labels.num_cols; int32_t num_classes = labels.num_rows; int32_t dim = feats.num_rows; SGMatrix< int32_t > mat_edges = get_edge_list(param.graph_type, num_classes); int32_t num_edges = mat_edges.num_rows; // prepare features and labels in factor graph for (int32_t n = 0; n < num_sample; n++) { SGVector<int32_t> vc(num_classes); SGVector<int32_t>::fill_vector(vc.vector, vc.vlen, NUM_STATUS); CFactorGraph * fg = new CFactorGraph(vc); float64_t * pfeat = feats.get_column_vector(n); SGVector<float64_t> feat_i(dim); memcpy(feat_i.vector, pfeat, dim * sizeof(float64_t)); // add unary factors for (int32_t u = 0; u < num_classes; u++) { SGVector<int32_t> var_index_u(1); var_index_u[0] = u; CFactor * fac_u = new CFactor(v_ftp_u[u], var_index_u, feat_i); fg->add_factor(fac_u); } // add pairwise factors for (int32_t t = 0; t < num_edges; t++) { SGVector<float64_t> data_t(1); data_t[0] = 1.0; SGVector<int32_t> var_index_t = mat_edges.get_row_vector(t); CFactor * fac_t = new CFactor(v_ftp_t[t], var_index_t, data_t); fg->add_factor(fac_t); } // add factor graph instance fg_feats->add_sample(fg); // add label int32_t * plabs = labels.get_column_vector(n); SGVector<int32_t> states_gt(num_classes); memcpy(states_gt.vector, plabs, num_classes * sizeof(int32_t)); SGVector<float64_t> loss_weights(num_classes); SGVector<float64_t>::fill_vector(loss_weights.vector, loss_weights.vlen, 1.0/num_classes); CFactorGraphObservation * fg_obs = new CFactorGraphObservation(states_gt, loss_weights); fg_labels->add_label(fg_obs); } }
SGVector< float64_t > CFactorGraphModel::get_joint_feature_vector(int32_t feat_idx, CStructuredData* y) { // factor graph instance CFactorGraphFeatures* mf = CFactorGraphFeatures::obtain_from_generic(m_features); CFactorGraph* fg = mf->get_sample(feat_idx); // ground truth states CFactorGraphObservation* fg_states = CFactorGraphObservation::obtain_from_generic(y); SGVector<int32_t> states = fg_states->get_data(); // initialize psi SGVector<float64_t> psi(get_dim()); psi.zero(); // construct unnormalized psi CDynamicObjectArray* facs = fg->get_factors(); for (int32_t fi = 0; fi < facs->get_num_elements(); ++fi) { CFactor* fac = dynamic_cast<CFactor*>(facs->get_element(fi)); CTableFactorType* ftype = fac->get_factor_type(); int32_t id = ftype->get_type_id(); SGVector<int32_t> w_map = get_params_mapping(id); ASSERT(w_map.size() == ftype->get_w_dim()); SGVector<float64_t> dat = fac->get_data(); int32_t dat_size = dat.size(); ASSERT(w_map.size() == dat_size * ftype->get_num_assignments()); int32_t ei = ftype->index_from_universe_assignment(states, fac->get_variables()); for (int32_t di = 0; di < dat_size; di++) psi[w_map[ei*dat_size + di]] += dat[di]; SG_UNREF(ftype); SG_UNREF(fac); } // negation (-E(x,y) = <w,phi(x,y)>) psi.scale(-1.0); SG_UNREF(facs); SG_UNREF(fg); return psi; }
CFactorGraph::CFactorGraph(const CFactorGraph &fg) { register_parameters(); m_cardinalities = fg.get_cardinalities(); // No need to unref and ref in this case m_factors = fg.get_factors(); m_datasources = fg.get_factor_data_sources(); m_dset = fg.get_disjoint_set(); m_has_cycle = !(fg.is_acyclic_graph()); m_num_edges = fg.get_num_edges(); }
// E(x_i, y; w) - E(x_i, y_i; w) >= L(y_i, y) - xi_i // xi_i >= max oracle // max oracle := argmax_y { L(y_i, y) - E(x_i, y; w) + E(x_i, y_i; w) } // := argmin_y { -L(y_i, y) + E(x_i, y; w) } - E(x_i, y_i; w) // we do energy minimization in inference, so get back to max oracle value is: // [ L(y_i, y_star) - E(x_i, y_star; w) ] + E(x_i, y_i; w) CResultSet* CFactorGraphModel::argmax(SGVector<float64_t> w, int32_t feat_idx, bool const training) { // factor graph instance CFactorGraphFeatures* mf = CFactorGraphFeatures::obtain_from_generic(m_features); CFactorGraph* fg = mf->get_sample(feat_idx); // prepare factor graph fg->connect_components(); if (m_inf_type == TREE_MAX_PROD) { ASSERT(fg->is_tree_graph()); } if (m_verbose) SG_SPRINT("shogun\n------ example %d\n", feat_idx); // update factor parameters w_to_fparams(w); fg->compute_energies(); if (m_verbose) { SG_SPRINT("energy table before loss-aug:\n"); fg->evaluate_energies(); } // prepare CResultSet CResultSet* ret = new CResultSet(); SG_REF(ret); // y_truth CFactorGraphObservation* y_truth = CFactorGraphObservation::obtain_from_generic(m_labels->get_label(feat_idx)); SGVector<int32_t> states_gt = y_truth->get_data(); // E(x_i, y_i; w) ret->psi_truth = get_joint_feature_vector(feat_idx, y_truth); float64_t energy_gt = fg->evaluate_energy(states_gt); ret->score = energy_gt; // - min_y [ E(x_i, y; w) - delta(y_i, y) ] CMAPInference infer_met(fg, m_inf_type); if (training) { fg->loss_augmentation(y_truth); // wrong assignments -delta() if (m_verbose) { SG_SPRINT("energy table after loss-aug:\n"); fg->evaluate_energies(); } } infer_met.inference(); // y_star CFactorGraphObservation* y_star = infer_met.get_structured_outputs(); SGVector<int32_t> states_star = y_star->get_data(); ret->argmax = y_star; ret->psi_pred = get_joint_feature_vector(feat_idx, y_star); float64_t l_energy_pred = fg->evaluate_energy(states_star); ret->score -= l_energy_pred; ret->delta = delta_loss(y_truth, y_star); if (m_verbose) { float64_t dot_pred = SGVector< float64_t >::dot(w.vector, ret->psi_pred.vector, w.vlen); float64_t dot_truth = SGVector< float64_t >::dot(w.vector, ret->psi_truth.vector, w.vlen); float64_t slack = dot_pred + ret->delta - dot_truth; SG_SPRINT("shogun\n"); w.display_vector("w"); ret->psi_pred.display_vector("psi_pred"); states_star.display_vector("state_pred"); SG_SPRINT("dot_pred = %f, energy_pred = %f, delta = %f\n\n", dot_pred, l_energy_pred, ret->delta); ret->psi_truth.display_vector("psi_truth"); states_gt.display_vector("state_gt"); SG_SPRINT("dot_truth = %f, energy_gt = %f\n\n", dot_truth, energy_gt); SG_SPRINT("slack = %f, score = %f\n\n", slack, ret->score); } SG_UNREF(y_truth); SG_UNREF(fg); return ret; }