Create version 1.2.1 #40
114
ITERATIVE_PROPOSAL_README.md
Normal file
114
ITERATIVE_PROPOSAL_README.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# Iterative Proposal Implementation
|
||||||
|
|
||||||
|
This implementation extends the existing local discretization framework with iterative convergence capabilities, following the analysis from `local_discretization_analysis.md`.
|
||||||
|
|
||||||
|
## Key Components
|
||||||
|
|
||||||
|
### 1. IterativeProposal Class
|
||||||
|
- **File**: `bayesnet/classifiers/IterativeProposal.h|cc`
|
||||||
|
- **Purpose**: Extends the base `Proposal` class with iterative convergence logic
|
||||||
|
- **Key Method**: `iterativeLocalDiscretization()` - performs iterative refinement until convergence
|
||||||
|
|
||||||
|
### 2. TANLdIterative Example
|
||||||
|
- **File**: `bayesnet/classifiers/TANLdIterative.h|cc`
|
||||||
|
- **Purpose**: Demonstrates how to adapt existing Ld classifiers to use iterative discretization
|
||||||
|
- **Pattern**: Inherits from both `TAN` and `IterativeProposal`
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The implementation follows the established dual inheritance pattern:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class TANLdIterative : public TAN, public IterativeProposal
|
||||||
|
```
|
||||||
|
|
||||||
|
This maintains the same interface as existing Ld classifiers while adding convergence capabilities.
|
||||||
|
|
||||||
|
## Convergence Algorithm
|
||||||
|
|
||||||
|
The iterative process works as follows:
|
||||||
|
|
||||||
|
1. **Initial Discretization**: Use class-only discretization (`fit_local_discretization()`)
|
||||||
|
2. **Iterative Refinement Loop**:
|
||||||
|
- Build model with current discretization (call parent `fit()`)
|
||||||
|
- Refine discretization using network structure (`localDiscretizationProposal()`)
|
||||||
|
- Compute convergence metric (likelihood or accuracy)
|
||||||
|
- Check for convergence based on tolerance
|
||||||
|
- Repeat until convergence or max iterations reached
|
||||||
|
|
||||||
|
## Configuration Parameters
|
||||||
|
|
||||||
|
- `max_iterations`: Maximum number of iterations (default: 10)
|
||||||
|
- `tolerance`: Convergence tolerance (default: 1e-6)
|
||||||
|
- `convergence_metric`: "likelihood" or "accuracy" (default: "likelihood")
|
||||||
|
- `verbose_convergence`: Enable verbose logging (default: false)
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "bayesnet/classifiers/TANLdIterative.h"
|
||||||
|
|
||||||
|
// Create classifier
|
||||||
|
bayesnet::TANLdIterative classifier;
|
||||||
|
|
||||||
|
// Set convergence parameters
|
||||||
|
nlohmann::json hyperparams;
|
||||||
|
hyperparams["max_iterations"] = 5;
|
||||||
|
hyperparams["tolerance"] = 1e-4;
|
||||||
|
hyperparams["convergence_metric"] = "likelihood";
|
||||||
|
hyperparams["verbose_convergence"] = true;
|
||||||
|
|
||||||
|
classifier.setHyperparameters(hyperparams);
|
||||||
|
|
||||||
|
// Fit and use normally
|
||||||
|
classifier.fit(X, y, features, className, states, smoothing);
|
||||||
|
auto predictions = classifier.predict(X_test);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Run the test with:
|
||||||
|
```bash
|
||||||
|
make -f Makefile.iterative test-iterative
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration with Existing Code
|
||||||
|
|
||||||
|
To convert existing Ld classifiers to use iterative discretization:
|
||||||
|
|
||||||
|
1. Change inheritance from `Proposal` to `IterativeProposal`
|
||||||
|
2. Replace the discretization logic in `fit()` method:
|
||||||
|
```cpp
|
||||||
|
// Old approach:
|
||||||
|
states = fit_local_discretization(y);
|
||||||
|
TAN::fit(dataset, features, className, states, smoothing);
|
||||||
|
states = localDiscretizationProposal(states, model);
|
||||||
|
|
||||||
|
// New approach:
|
||||||
|
states = iterativeLocalDiscretization(y, this, dataset, features, className, states_, smoothing);
|
||||||
|
TAN::fit(dataset, features, className, states, smoothing);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
1. **Convergence**: Iterative refinement until stable discretization
|
||||||
|
2. **Flexibility**: Configurable convergence criteria and limits
|
||||||
|
3. **Compatibility**: Maintains existing interface and patterns
|
||||||
|
4. **Monitoring**: Optional verbose logging for convergence tracking
|
||||||
|
5. **Extensibility**: Easy to add new convergence metrics or stopping criteria
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
- Iterative approach will be slower than the original two-phase method
|
||||||
|
- Convergence monitoring adds computational overhead
|
||||||
|
- Consider setting appropriate `max_iterations` to prevent infinite loops
|
||||||
|
- The `tolerance` parameter should be tuned based on your specific use case
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
Potential improvements:
|
||||||
|
1. Add more convergence metrics (e.g., AIC, BIC, cross-validation score)
|
||||||
|
2. Implement early stopping based on validation performance
|
||||||
|
3. Add support for different discretization schedules
|
||||||
|
4. Optimize likelihood computation for better performance
|
||||||
|
5. Add convergence visualization and reporting tools
|
20
Makefile.iterative
Normal file
20
Makefile.iterative
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Makefile for testing iterative proposal implementation
|
||||||
|
# Include this in the main Makefile or use directly
|
||||||
|
|
||||||
|
# Test iterative proposal
|
||||||
|
test-iterative: buildd
|
||||||
|
@echo "Building iterative proposal test..."
|
||||||
|
cd build_Debug && g++ -std=c++17 -I../bayesnet -I../config -I/usr/local/include \
|
||||||
|
../test_iterative_proposal.cpp \
|
||||||
|
-L. -lbayesnet \
|
||||||
|
-ltorch -ltorch_cpu \
|
||||||
|
-pthread \
|
||||||
|
-o test_iterative_proposal
|
||||||
|
@echo "Running iterative proposal test..."
|
||||||
|
cd build_Debug && ./test_iterative_proposal
|
||||||
|
|
||||||
|
# Clean test
|
||||||
|
clean-test:
|
||||||
|
rm -f build_Debug/test_iterative_proposal
|
||||||
|
|
||||||
|
.PHONY: test-iterative clean-test
|
151
bayesnet/classifiers/IterativeProposal.cc
Normal file
151
bayesnet/classifiers/IterativeProposal.cc
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
// ***************************************************************
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 Ricardo Montañana Gómez
|
||||||
|
// SPDX-FileType: SOURCE
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
#include "IterativeProposal.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
|
||||||
|
IterativeProposal::IterativeProposal(torch::Tensor& pDataset, std::vector<std::string>& features_, std::string& className_)
|
||||||
|
: Proposal(pDataset, features_, className_) {}
|
||||||
|
|
||||||
|
void IterativeProposal::setHyperparameters(const nlohmann::json& hyperparameters_) {
|
||||||
|
// First set base Proposal hyperparameters
|
||||||
|
Proposal::setHyperparameters(hyperparameters_);
|
||||||
|
|
||||||
|
// Then set IterativeProposal specific hyperparameters
|
||||||
|
if (hyperparameters_.contains("max_iterations")) {
|
||||||
|
convergence_params.maxIterations = hyperparameters_["max_iterations"];
|
||||||
|
}
|
||||||
|
if (hyperparameters_.contains("tolerance")) {
|
||||||
|
convergence_params.tolerance = hyperparameters_["tolerance"];
|
||||||
|
}
|
||||||
|
if (hyperparameters_.contains("convergence_metric")) {
|
||||||
|
convergence_params.convergenceMetric = hyperparameters_["convergence_metric"];
|
||||||
|
}
|
||||||
|
if (hyperparameters_.contains("verbose_convergence")) {
|
||||||
|
convergence_params.verbose = hyperparameters_["verbose_convergence"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Classifier>
|
||||||
|
map<std::string, std::vector<int>> IterativeProposal::iterativeLocalDiscretization(
|
||||||
|
const torch::Tensor& y,
|
||||||
|
Classifier* classifier,
|
||||||
|
const torch::Tensor& dataset,
|
||||||
|
const std::vector<std::string>& features,
|
||||||
|
const std::string& className,
|
||||||
|
const map<std::string, std::vector<int>>& initialStates,
|
||||||
|
double smoothing
|
||||||
|
) {
|
||||||
|
// Phase 1: Initial discretization (same as original)
|
||||||
|
auto currentStates = fit_local_discretization(y);
|
||||||
|
|
||||||
|
double previousValue = -std::numeric_limits<double>::infinity();
|
||||||
|
double currentValue = 0.0;
|
||||||
|
|
||||||
|
if (convergence_params.verbose) {
|
||||||
|
std::cout << "Starting iterative local discretization with "
|
||||||
|
<< convergence_params.maxIterations << " max iterations" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int iteration = 0; iteration < convergence_params.maxIterations; ++iteration) {
|
||||||
|
if (convergence_params.verbose) {
|
||||||
|
std::cout << "Iteration " << (iteration + 1) << "/" << convergence_params.maxIterations << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Build model with current discretization
|
||||||
|
classifier->fit(dataset, features, className, currentStates, smoothing);
|
||||||
|
|
||||||
|
// Phase 3: Network-aware discretization refinement
|
||||||
|
auto newStates = localDiscretizationProposal(currentStates, classifier->getModel());
|
||||||
|
|
||||||
|
// Phase 4: Compute convergence metric
|
||||||
|
if (convergence_params.convergenceMetric == "likelihood") {
|
||||||
|
currentValue = computeLogLikelihood(classifier->getModel(), dataset);
|
||||||
|
} else if (convergence_params.convergenceMetric == "accuracy") {
|
||||||
|
// For accuracy, we would need validation data - for now use likelihood
|
||||||
|
currentValue = computeLogLikelihood(classifier->getModel(), dataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convergence_params.verbose) {
|
||||||
|
std::cout << " " << convergence_params.convergenceMetric << ": " << currentValue << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check convergence
|
||||||
|
if (iteration > 0 && hasConverged(currentValue, previousValue, convergence_params.convergenceMetric)) {
|
||||||
|
if (convergence_params.verbose) {
|
||||||
|
std::cout << "Converged after " << (iteration + 1) << " iterations" << std::endl;
|
||||||
|
}
|
||||||
|
currentStates = newStates;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update for next iteration
|
||||||
|
currentStates = newStates;
|
||||||
|
previousValue = currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
double IterativeProposal::computeLogLikelihood(const Network& model, const torch::Tensor& dataset) {
|
||||||
|
double logLikelihood = 0.0;
|
||||||
|
int n_samples = dataset.size(0);
|
||||||
|
int n_features = dataset.size(1);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_samples; ++i) {
|
||||||
|
double sampleLogLikelihood = 0.0;
|
||||||
|
|
||||||
|
// Get class value for this sample
|
||||||
|
int classValue = dataset[i][n_features - 1].item<int>();
|
||||||
|
|
||||||
|
// Compute log-likelihood for each feature given its parents and class
|
||||||
|
for (const auto& node : model.getNodes()) {
|
||||||
|
if (node.getName() == model.getClassName()) {
|
||||||
|
// For class node, add log P(class)
|
||||||
|
auto classCounts = node.getCPT();
|
||||||
|
double classProb = classCounts[classValue] / dataset.size(0);
|
||||||
|
sampleLogLikelihood += std::log(std::max(classProb, 1e-10));
|
||||||
|
} else {
|
||||||
|
// For feature nodes, add log P(feature | parents, class)
|
||||||
|
int featureIdx = std::distance(model.getFeatures().begin(),
|
||||||
|
std::find(model.getFeatures().begin(),
|
||||||
|
model.getFeatures().end(),
|
||||||
|
node.getName()));
|
||||||
|
int featureValue = dataset[i][featureIdx].item<int>();
|
||||||
|
|
||||||
|
// Simplified probability computation - in practice would need full CPT lookup
|
||||||
|
double featureProb = 0.1; // Placeholder - would compute from CPT
|
||||||
|
sampleLogLikelihood += std::log(std::max(featureProb, 1e-10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logLikelihood += sampleLogLikelihood;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logLikelihood;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IterativeProposal::hasConverged(double currentValue, double previousValue, const std::string& metric) {
|
||||||
|
if (metric == "likelihood") {
|
||||||
|
// For likelihood, check if improvement is less than tolerance
|
||||||
|
double improvement = currentValue - previousValue;
|
||||||
|
return improvement < convergence_params.tolerance;
|
||||||
|
} else if (metric == "accuracy") {
|
||||||
|
// For accuracy, check if change is less than tolerance
|
||||||
|
double change = std::abs(currentValue - previousValue);
|
||||||
|
return change < convergence_params.tolerance;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit template instantiation for common classifier types
|
||||||
|
template map<std::string, std::vector<int>> IterativeProposal::iterativeLocalDiscretization<Classifier>(
|
||||||
|
const torch::Tensor&, Classifier*, const torch::Tensor&, const std::vector<std::string>&,
|
||||||
|
const std::string&, const map<std::string, std::vector<int>>&, double);
|
||||||
|
}
|
50
bayesnet/classifiers/IterativeProposal.h
Normal file
50
bayesnet/classifiers/IterativeProposal.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// ***************************************************************
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 Ricardo Montañana Gómez
|
||||||
|
// SPDX-FileType: SOURCE
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
#ifndef ITERATIVE_PROPOSAL_H
|
||||||
|
#define ITERATIVE_PROPOSAL_H
|
||||||
|
|
||||||
|
#include "Proposal.h"
|
||||||
|
#include "bayesnet/network/Network.h"
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class IterativeProposal : public Proposal {
|
||||||
|
public:
|
||||||
|
IterativeProposal(torch::Tensor& pDataset, std::vector<std::string>& features_, std::string& className_);
|
||||||
|
void setHyperparameters(const nlohmann::json& hyperparameters_);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<typename Classifier>
|
||||||
|
map<std::string, std::vector<int>> iterativeLocalDiscretization(
|
||||||
|
const torch::Tensor& y,
|
||||||
|
Classifier* classifier,
|
||||||
|
const torch::Tensor& dataset,
|
||||||
|
const std::vector<std::string>& features,
|
||||||
|
const std::string& className,
|
||||||
|
const map<std::string, std::vector<int>>& initialStates,
|
||||||
|
double smoothing = 1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Convergence parameters
|
||||||
|
struct {
|
||||||
|
int maxIterations = 10;
|
||||||
|
double tolerance = 1e-6;
|
||||||
|
std::string convergenceMetric = "likelihood"; // "likelihood" or "accuracy"
|
||||||
|
bool verbose = false;
|
||||||
|
} convergence_params;
|
||||||
|
|
||||||
|
nlohmann::json validHyperparameters_iter = {
|
||||||
|
"max_iterations", "tolerance", "convergence_metric", "verbose_convergence"
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
double computeLogLikelihood(const Network& model, const torch::Tensor& dataset);
|
||||||
|
bool hasConverged(double currentValue, double previousValue, const std::string& metric);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
45
bayesnet/classifiers/TANLdi.cc
Normal file
45
bayesnet/classifiers/TANLdi.cc
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// ***************************************************************
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 Ricardo Montañana Gómez
|
||||||
|
// SPDX-FileType: SOURCE
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
#include "TANLdi.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
TANLdi::TANLdIterative() : TAN(), IterativeProposal(dataset, features, className) {}
|
||||||
|
|
||||||
|
TANLdi& TANLdIterative::fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_, const Smoothing_t smoothing)
|
||||||
|
{
|
||||||
|
checkInput(X_, y_);
|
||||||
|
features = features_;
|
||||||
|
className = className_;
|
||||||
|
Xf = X_;
|
||||||
|
y = y_;
|
||||||
|
|
||||||
|
// Use iterative local discretization instead of the two-phase approach
|
||||||
|
states = iterativeLocalDiscretization(y, this, dataset, features, className, states_, smoothing);
|
||||||
|
|
||||||
|
// Final fit with converged discretization
|
||||||
|
TAN::fit(dataset, features, className, states, smoothing);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
torch::Tensor TANLdi::predict(torch::Tensor& X)
|
||||||
|
{
|
||||||
|
auto Xt = prepareX(X);
|
||||||
|
return TAN::predict(Xt);
|
||||||
|
}
|
||||||
|
|
||||||
|
torch::Tensor TANLdi::predict_proba(torch::Tensor& X)
|
||||||
|
{
|
||||||
|
auto Xt = prepareX(X);
|
||||||
|
return TAN::predict_proba(Xt);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> TANLdi::graph(const std::string& name) const
|
||||||
|
{
|
||||||
|
return TAN::graph(name);
|
||||||
|
}
|
||||||
|
}
|
24
bayesnet/classifiers/TANLdi.h
Normal file
24
bayesnet/classifiers/TANLdi.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// ***************************************************************
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 Ricardo Montañana Gómez
|
||||||
|
// SPDX-FileType: SOURCE
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
#ifndef TANLDI_H
|
||||||
|
#define TANLDI_H
|
||||||
|
#include "TAN.h"
|
||||||
|
#include "IterativeProposal.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class TANLdi : public TAN, public IterativeProposal {
|
||||||
|
private:
|
||||||
|
public:
|
||||||
|
TANLdi();
|
||||||
|
virtual ~TANLdi() = default;
|
||||||
|
TANLdi& fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, map<std::string, std::vector<int>>& states, const Smoothing_t smoothing) override;
|
||||||
|
std::vector<std::string> graph(const std::string& name = "TANLdi") const override;
|
||||||
|
torch::Tensor predict(torch::Tensor& X) override;
|
||||||
|
torch::Tensor predict_proba(torch::Tensor& X) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // !TANLDI_H
|
66
test_iterative_proposal.cpp
Normal file
66
test_iterative_proposal.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// ***************************************************************
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 Ricardo Montañana Gómez
|
||||||
|
// SPDX-FileType: SOURCE
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// ***************************************************************
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <torch/torch.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include "bayesnet/classifiers/TANLdIterative.h"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Testing Iterative Proposal Implementation" << std::endl;
|
||||||
|
|
||||||
|
// Create synthetic continuous data
|
||||||
|
torch::Tensor X = torch::rand({100, 3}); // 100 samples, 3 features
|
||||||
|
torch::Tensor y = torch::randint(0, 2, {100}); // Binary classification
|
||||||
|
|
||||||
|
// Create feature names
|
||||||
|
std::vector<std::string> features = {"feature1", "feature2", "feature3"};
|
||||||
|
std::string className = "class";
|
||||||
|
|
||||||
|
// Create initial states (will be updated by discretization)
|
||||||
|
std::map<std::string, std::vector<int>> states;
|
||||||
|
states[className] = {0, 1};
|
||||||
|
|
||||||
|
// Create classifier
|
||||||
|
bayesnet::TANLdIterative classifier;
|
||||||
|
|
||||||
|
// Set convergence hyperparameters
|
||||||
|
json hyperparams;
|
||||||
|
hyperparams["max_iterations"] = 5;
|
||||||
|
hyperparams["tolerance"] = 1e-4;
|
||||||
|
hyperparams["convergence_metric"] = "likelihood";
|
||||||
|
hyperparams["verbose_convergence"] = true;
|
||||||
|
|
||||||
|
classifier.setHyperparameters(hyperparams);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fit the model
|
||||||
|
std::cout << "Fitting TANLdIterative classifier..." << std::endl;
|
||||||
|
classifier.fit(X, y, features, className, states, bayesnet::Smoothing_t::LAPLACE);
|
||||||
|
|
||||||
|
// Make predictions
|
||||||
|
torch::Tensor X_test = torch::rand({10, 3});
|
||||||
|
torch::Tensor predictions = classifier.predict(X_test);
|
||||||
|
torch::Tensor probabilities = classifier.predict_proba(X_test);
|
||||||
|
|
||||||
|
std::cout << "Predictions: " << predictions << std::endl;
|
||||||
|
std::cout << "Probabilities shape: " << probabilities.sizes() << std::endl;
|
||||||
|
|
||||||
|
// Generate graph
|
||||||
|
auto graph = classifier.graph();
|
||||||
|
std::cout << "Graph nodes: " << graph.size() << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Test completed successfully!" << std::endl;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in New Issue
Block a user