14#include "bayesnet/utils/TensorUtils.h"
21XSp2de::XSp2de(
int spIndex1,
int spIndex2)
22 : superParent1_{ spIndex1 }
23 , superParent2_{ spIndex2 }
28 , semaphore_{ CountingSemaphore::getInstance() }
29 , Classifier(Network())
31 validHyperparameters = {
"parent1",
"parent2" };
37void XSp2de::setHyperparameters(
const nlohmann::json &hyperparameters_)
39 auto hyperparameters = hyperparameters_;
40 if (hyperparameters.contains(
"parent1")) {
41 superParent1_ = hyperparameters[
"parent1"];
42 hyperparameters.erase(
"parent1");
44 if (hyperparameters.contains(
"parent2")) {
45 superParent2_ = hyperparameters[
"parent2"];
46 hyperparameters.erase(
"parent2");
49 Classifier::setHyperparameters(hyperparameters);
55void XSp2de::fitx(torch::Tensor & X, torch::Tensor & y,
56 torch::Tensor & weights_,
const Smoothing_t smoothing)
69 trainModel(weights_, smoothing);
76void XSp2de::buildModel(
const torch::Tensor &weights)
82 states_.resize(nFeatures_);
83 for (
int f = 0; f < nFeatures_; f++) {
85 states_[f] = dataset[f].max().item<
int>() + 1;
88 statesClass_ = dataset[-1].max().item<
int>() + 1;
91 classCounts_.resize(statesClass_, 0.0);
94 sp1FeatureCounts_.resize(states_[superParent1_] * statesClass_, 0.0);
97 sp2FeatureCounts_.resize(states_[superParent2_] * statesClass_, 0.0);
102 childOffsets_.resize(nFeatures_, -1);
105 for (
int f = 0; f < nFeatures_; f++) {
106 if (f == superParent1_ || f == superParent2_) {
108 childOffsets_[f] = -1;
111 childOffsets_[f] = totalSize;
115 totalSize += (states_[f] * statesClass_
116 * states_[superParent1_]
117 * states_[superParent2_]);
119 childCounts_.resize(totalSize, 0.0);
125void XSp2de::trainModel(
const torch::Tensor &weights,
126 const bayesnet::Smoothing_t smoothing)
129 for (
int i = 0; i < m; i++) {
130 std::vector<int> instance(nFeatures_ + 1);
131 for (
int f = 0; f < nFeatures_; f++) {
132 instance[f] = dataset[f][i].item<
int>();
134 instance[nFeatures_] = dataset[-1][i].item<
int>();
135 double w = weights[i].item<
double>();
136 addSample(instance, w);
141 case bayesnet::Smoothing_t::ORIGINAL:
144 case bayesnet::Smoothing_t::LAPLACE:
152 initializer_ = std::numeric_limits<double>::max() / (nFeatures_ * nFeatures_);
155 computeProbabilities();
161void XSp2de::addSample(
const std::vector<int> &instance,
double weight)
166 int c = instance.back();
168 classCounts_[c] += weight;
170 int sp1Val = instance[superParent1_];
171 int sp2Val = instance[superParent2_];
174 sp1FeatureCounts_[sp1Val * statesClass_ + c] += weight;
177 sp2FeatureCounts_[sp2Val * statesClass_ + c] += weight;
180 for (
int f = 0; f < nFeatures_; f++) {
181 if (f == superParent1_ || f == superParent2_)
184 int childVal = instance[f];
185 int offset = childOffsets_[f];
191 int blockSizeSp2 = states_[superParent2_]
194 int blockSizeChild = states_[f] * statesClass_;
197 + sp1Val*blockSizeSp2
198 + sp2Val*blockSizeChild
199 + childVal*statesClass_
201 childCounts_[idx] += weight;
208void XSp2de::computeProbabilities()
210 double totalCount = std::accumulate(classCounts_.begin(),
211 classCounts_.end(), 0.0);
214 classPriors_.resize(statesClass_, 0.0);
215 if (totalCount <= 0.0) {
217 double unif = 1.0 /
static_cast<double>(statesClass_);
218 for (
int c = 0; c < statesClass_; c++) {
219 classPriors_[c] = unif;
222 for (
int c = 0; c < statesClass_; c++) {
224 (classCounts_[c] + alpha_)
225 / (totalCount + alpha_ * statesClass_);
230 sp1FeatureProbs_.resize(sp1FeatureCounts_.size());
231 int sp1Card = states_[superParent1_];
232 for (
int spVal = 0; spVal < sp1Card; spVal++) {
233 for (
int c = 0; c < statesClass_; c++) {
234 double denom = classCounts_[c] + alpha_ * sp1Card;
235 double num = sp1FeatureCounts_[spVal * statesClass_ + c] + alpha_;
236 sp1FeatureProbs_[spVal * statesClass_ + c] =
237 (denom <= 0.0 ? 0.0 : num / denom);
242 sp2FeatureProbs_.resize(sp2FeatureCounts_.size());
243 int sp2Card = states_[superParent2_];
244 for (
int spVal = 0; spVal < sp2Card; spVal++) {
245 for (
int c = 0; c < statesClass_; c++) {
246 double denom = classCounts_[c] + alpha_ * sp2Card;
247 double num = sp2FeatureCounts_[spVal * statesClass_ + c] + alpha_;
248 sp2FeatureProbs_[spVal * statesClass_ + c] =
249 (denom <= 0.0 ? 0.0 : num / denom);
254 childProbs_.resize(childCounts_.size());
256 for (
int f = 0; f < nFeatures_; f++) {
257 if (f == superParent1_ || f == superParent2_)
260 int fCard = states_[f];
261 int sp1Card_ = states_[superParent1_];
262 int sp2Card_ = states_[superParent2_];
263 int childBlockSizeSp2 = sp2Card_ * fCard * statesClass_;
264 int childBlockSizeF = fCard * statesClass_;
266 int blockSize = fCard * sp1Card_ * sp2Card_ * statesClass_;
267 for (
int sp1Val = 0; sp1Val < sp1Card_; sp1Val++) {
268 for (
int sp2Val = 0; sp2Val < sp2Card_; sp2Val++) {
269 for (
int childVal = 0; childVal < fCard; childVal++) {
270 for (
int c = 0; c < statesClass_; c++) {
273 + sp1Val*childBlockSizeSp2
274 + sp2Val*childBlockSizeF
275 + childVal*statesClass_
277 double num = childCounts_[idx] + alpha_;
286 double sumSp1Sp2C = 0.0;
288 for (
int cv = 0; cv < fCard; cv++) {
290 + sp1Val*childBlockSizeSp2
291 + sp2Val*childBlockSizeF
292 + cv*statesClass_ + c;
293 sumSp1Sp2C += childCounts_[idx2];
295 double denom = sumSp1Sp2C + alpha_ * fCard;
296 childProbs_[idx] = (denom <= 0.0 ? 0.0 : num / denom);
308std::vector<double> XSp2de::predict_proba(
const std::vector<int> &instance)
const
311 throw std::logic_error(CLASSIFIER_NOT_FITTED);
313 std::vector<double> probs(statesClass_, 0.0);
315 int sp1Val = instance[superParent1_];
316 int sp2Val = instance[superParent2_];
319 for (
int c = 0; c < statesClass_; c++) {
320 double pC = classPriors_[c];
321 double pSp1C = sp1FeatureProbs_[sp1Val * statesClass_ + c];
322 double pSp2C = sp2FeatureProbs_[sp2Val * statesClass_ + c];
323 probs[c] = pC * pSp1C * pSp2C * initializer_;
328 for (
int f = 0; f < nFeatures_; f++) {
329 if (f == superParent1_ || f == superParent2_)
332 int valF = instance[f];
333 int fCard = states_[f];
334 int sp1Card = states_[superParent1_];
335 int sp2Card = states_[superParent2_];
336 int blockSizeSp2 = sp2Card * fCard * statesClass_;
337 int blockSizeF = fCard * statesClass_;
341 + sp1Val*blockSizeSp2
344 for (
int c = 0; c < statesClass_; c++) {
345 probs[c] *= childProbs_[base + c];
347 offset += (fCard * sp1Card * sp2Card * statesClass_);
358std::vector<std::vector<double>> XSp2de::predict_proba(std::vector<std::vector<int>> &test_data)
360 int test_size = test_data[0].size();
361 int sample_size = test_data.size();
362 std::vector<std::vector<double>> probabilities(
363 test_size, std::vector<double>(statesClass_, 0.0));
366 int chunk_size = std::min(150,
int(test_size / semaphore_.getMaxCount()) + 1);
367 std::vector<std::thread> threads;
369 auto worker = [&](
const std::vector<std::vector<int>> &samples,
373 std::vector<std::vector<double>> &predictions) {
374 std::string threadName =
375 "XSp2de-" + std::to_string(begin) +
"-" + std::to_string(chunk);
376#if defined(__linux__)
377 pthread_setname_np(pthread_self(), threadName.c_str());
379 pthread_setname_np(threadName.c_str());
382 std::vector<int> instance(sample_size);
383 for (
int sample = begin; sample < begin + chunk; ++sample) {
384 for (
int feature = 0; feature < sample_size; ++feature) {
385 instance[feature] = samples[feature][sample];
387 predictions[sample] = predict_proba(instance);
389 semaphore_.release();
392 for (
int begin = 0; begin < test_size; begin += chunk_size) {
393 int chunk = std::min(chunk_size, test_size - begin);
394 semaphore_.acquire();
395 threads.emplace_back(worker, test_data, begin, chunk, sample_size,
396 std::ref(probabilities));
398 for (
auto &th : threads) {
401 return probabilities;
407int XSp2de::predict(
const std::vector<int> &instance)
const
409 auto p = predict_proba(instance);
410 return static_cast<int>(
411 std::distance(p.begin(), std::max_element(p.begin(), p.end()))
418std::vector<int> XSp2de::predict(std::vector<std::vector<int>> &test_data)
420 auto probabilities = predict_proba(test_data);
421 std::vector<int> predictions(probabilities.size(), 0);
423 for (
size_t i = 0; i < probabilities.size(); i++) {
424 predictions[i] =
static_cast<int>(
425 std::distance(probabilities[i].begin(),
426 std::max_element(probabilities[i].begin(),
427 probabilities[i].end()))
436torch::Tensor XSp2de::predict(torch::Tensor &X)
438 auto X_ = TensorUtils::to_matrix(X);
439 auto result_v = predict(X_);
440 return torch::tensor(result_v, torch::kInt32);
446torch::Tensor XSp2de::predict_proba(torch::Tensor &X)
448 auto X_ = TensorUtils::to_matrix(X);
449 auto result_v = predict_proba(X_);
450 int n_samples = X.size(1);
451 torch::Tensor result =
452 torch::zeros({ n_samples, statesClass_ }, torch::kDouble);
453 for (
int i = 0; i < (int)result_v.size(); ++i) {
454 result.index_put_({ i,
"..." }, torch::tensor(result_v[i]));
462float XSp2de::score(torch::Tensor &X, torch::Tensor &y)
464 torch::Tensor y_pred = predict(X);
465 return (y_pred == y).sum().item<
float>() / y.size(0);
471float XSp2de::score(std::vector<std::vector<int>> &X, std::vector<int> &y)
473 auto y_pred = predict(X);
475 for (
size_t i = 0; i < y_pred.size(); ++i) {
476 if (y_pred[i] == y[i]) {
480 return static_cast<float>(correct) /
static_cast<float>(y_pred.size());
486void XSp2de::normalize(std::vector<double> &v)
const
489 for (
auto &val : v) {
493 for (
auto &val : v) {
502std::string XSp2de::to_string()
const
504 std::ostringstream oss;
505 oss <<
"----- XSp2de Model -----\n"
506 <<
"nFeatures_ = " << nFeatures_ <<
"\n"
507 <<
"superParent1_ = " << superParent1_ <<
"\n"
508 <<
"superParent2_ = " << superParent2_ <<
"\n"
509 <<
"statesClass_ = " << statesClass_ <<
"\n\n";
512 for (
auto s : states_) oss << s <<
" ";
515 oss <<
"classCounts_:\n";
516 for (
auto v : classCounts_) oss << v <<
" ";
517 oss <<
"\nclassPriors_:\n";
518 for (
auto v : classPriors_) oss << v <<
" ";
519 oss <<
"\nsp1FeatureCounts_ (size=" << sp1FeatureCounts_.size() <<
")\n";
520 for (
auto v : sp1FeatureCounts_) oss << v <<
" ";
521 oss <<
"\nsp2FeatureCounts_ (size=" << sp2FeatureCounts_.size() <<
")\n";
522 for (
auto v : sp2FeatureCounts_) oss << v <<
" ";
523 oss <<
"\nchildCounts_ (size=" << childCounts_.size() <<
")\n";
524 for (
auto v : childCounts_) oss << v <<
" ";
526 oss <<
"\nchildOffsets_:\n";
527 for (
auto c : childOffsets_) oss << c <<
" ";
529 oss <<
"\n----------------------------------------\n";
536int XSp2de::getNumberOfNodes()
const
539 return nFeatures_ + 1;
542int XSp2de::getClassNumStates()
const
547int XSp2de::getNFeatures()
const
552int XSp2de::getNumberOfStates()
const
556 return std::accumulate(states_.begin(), states_.end(), 0) * nFeatures_;
559int XSp2de::getNumberOfEdges()
const
571 return 3 * nFeatures_ - 4;