Compare commits
260 Commits
boostAode
...
46e929ff4d
Author | SHA1 | Date | |
---|---|---|---|
46e929ff4d | |||
d858e26e4b
|
|||
0ee3eaed53
|
|||
093c197f0a
|
|||
78d7ea7c4d
|
|||
d6af1ffe8e
|
|||
20669dd161
|
|||
272dbad4f3
|
|||
8bccc3e4bc
|
|||
903b143338
|
|||
f10d0daf2e
|
|||
d39a17089e
|
|||
2e325cd114 | |||
fc3d63b7db
|
|||
43dc79a345
|
|||
b8589bcd0a | |||
3007e22a7d
|
|||
02e456befb
|
|||
8477698d8d
|
|||
52abd2d670
|
|||
3116eaa763
|
|||
443e5cc882
|
|||
e1c4221c11
|
|||
a63a35df3f
|
|||
c7555dac3f
|
|||
f3b8150e2c
|
|||
03f8b8653b
|
|||
2163e95c4a
|
|||
b33da34655
|
|||
e17aee7bdb
|
|||
37c31ee4c2
|
|||
80afdc06f7
|
|||
|
666782217e | ||
55af0714cd
|
|||
6ef5ca541a
|
|||
4364317411 | |||
65a96851ef
|
|||
722da7f781
|
|||
b1833a5feb
|
|||
41a0bd4ddd
|
|||
9ab4fc7d76
|
|||
beadb7465f
|
|||
652e5f623f
|
|||
b7fef9a99d
|
|||
343269d48c
|
|||
21c4c6df51
|
|||
702f086706
|
|||
981bc8f98b
|
|||
e0b7b2d316
|
|||
9b9e91e856 | |||
18e8e84284
|
|||
7de11b0e6d
|
|||
9b8db37a4b
|
|||
49b26bd04b
|
|||
b5b5b48864
|
|||
19586a3a5a
|
|||
ffe6d37436
|
|||
b73f4be146
|
|||
dbf2f35502
|
|||
db9e80a70e
|
|||
40ae4ad7f9
|
|||
234342f2de
|
|||
aa0936abd1
|
|||
f0d6f0cc38
|
|||
cc316bb8d3
|
|||
0723564e66
|
|||
2e95e8999d
|
|||
fb9b395748
|
|||
03e4437fea
|
|||
33cd32c639
|
|||
c460ef46ed
|
|||
dee9c674da
|
|||
e3f6dc1e0b
|
|||
460d20a402
|
|||
8dbbb65a2f
|
|||
d06bf187b2
|
|||
4addaefb47
|
|||
82964190f6
|
|||
4fefe9a1d2
|
|||
7c12dd25e5
|
|||
c713c0b1df
|
|||
64069a6cb7
|
|||
ba2a3f9523 | |||
f94e2d6a27
|
|||
2121ba9b98
|
|||
8b7b59d42b
|
|||
bbe5302ab1
|
|||
c2eb727fc7
|
|||
fb347ed5b9
|
|||
b657762c0c
|
|||
495d8a8528
|
|||
4628e48d3c
|
|||
5876be4b24
|
|||
dc3400197f
|
|||
26d3a57782
|
|||
4f3a04058f
|
|||
89c4613591
|
|||
28f3d87e32 | |||
e8d2c9fc0b
|
|||
d3cb580387
|
|||
f088df14fd
|
|||
e2249eace7
|
|||
64f5a7f14a
|
|||
408db2aad5
|
|||
e03efb5f63
|
|||
f617886133
|
|||
69ad660040
|
|||
431b3a3aa5
|
|||
6a23e2cc26
|
|||
f6e00530be
|
|||
f9258e43b9
|
|||
92820555da
|
|||
5a3af51826
|
|||
a8f9800631
|
|||
84cec0c1e0
|
|||
130139f644
|
|||
651f84b562
|
|||
553ab0fa22
|
|||
4975feabff
|
|||
32293af69f
|
|||
858664be2d
|
|||
1f705f6018
|
|||
7bcd2eed06
|
|||
833acefbb3
|
|||
26b649ebae
|
|||
080eddf9cd
|
|||
04e754b2f5
|
|||
38423048bd
|
|||
64fc97b892
|
|||
2c2159f192
|
|||
6765552a7c
|
|||
f72aa5b9a6 | |||
fa7fe081ad
|
|||
660e783517
|
|||
b35532dd9e
|
|||
6ef49385ea
|
|||
6d5a25cdc8
|
|||
d00b08cbe8
|
|||
977ff6fddb
|
|||
54b8939f35
|
|||
5022a4dc90
|
|||
40d1dad5d8
|
|||
47e2b138c5
|
|||
e7ded68267
|
|||
ca833a34f5
|
|||
df9b4c48d2
|
|||
f288bbd6fa
|
|||
7d8aca4f59
|
|||
8fdad78a8c
|
|||
e3ae073333
|
|||
4b732e76c2
|
|||
fe5fead27e
|
|||
8c3864f3c8
|
|||
1287160c47
|
|||
2f58807322
|
|||
17e079edd5
|
|||
b9e0028e9d
|
|||
e0d39fe631
|
|||
36b0277576
|
|||
da8d018ec4
|
|||
5f0676691c
|
|||
3448fb1299
|
|||
5e938d5cca
|
|||
55e742438f
|
|||
c4ae3fe429
|
|||
93e4ff94db
|
|||
57c27f739c
|
|||
a434d7f1ae
|
|||
294666c516
|
|||
fd04e78ad9
|
|||
66ec1b343b
|
|||
bb423da42f
|
|||
db17c14042
|
|||
a4401cb78f
|
|||
9d3d9cc6c6
|
|||
cfcf3c16df
|
|||
85202260f3
|
|||
82acb3cab5
|
|||
623ceed396 | |||
926de2bebd
|
|||
71704e3547
|
|||
3b06534327
|
|||
ac89a451e3
|
|||
00c6cf663b
|
|||
5043c12be8
|
|||
11320e2cc7
|
|||
ce66483b65
|
|||
cab8e14b2d
|
|||
f0d0abe891
|
|||
dcba146e12
|
|||
3ea0285119
|
|||
e3888e1503 | |||
06de13df98
|
|||
de4fa6a04f
|
|||
3a7bf4e672
|
|||
cd0bc02a74
|
|||
c8597a794e
|
|||
b30416364d
|
|||
3a16589220
|
|||
c4f9187e2a
|
|||
c4d0a5b4e6
|
|||
7bfafe555f
|
|||
337b6f7e79
|
|||
5fa0b957dd
|
|||
67252fc41d
|
|||
94ae9456a0
|
|||
781993e326
|
|||
8257a6ae39
|
|||
fc81730dfc | |||
d8734ff082
|
|||
03533461c8
|
|||
68f22a673d
|
|||
b9bc0088f3
|
|||
c280e254ca
|
|||
3d0f29fda3
|
|||
20a6ebab7c
|
|||
925f71166c
|
|||
f69f415b92
|
|||
1bdfbd1620
|
|||
06fb135526
|
|||
501ea0ab4e
|
|||
847c6761d7
|
|||
6030885fc3
|
|||
89df7f4db0
|
|||
41257ed566
|
|||
506369e46b
|
|||
d908f389f5
|
|||
5a7c8f1818
|
|||
64fc7bd9dd
|
|||
0b7beda78c
|
|||
05b670dfc0
|
|||
de62d42b74
|
|||
edb957d22e
|
|||
4de5cb4c6c | |||
c35030f137
|
|||
182b07ed90
|
|||
7806f961e2
|
|||
7c3e315ae7
|
|||
284ef6dfd1
|
|||
1c6af619b5
|
|||
86ffdfd6f3
|
|||
d82148079d
|
|||
067430fd1b
|
|||
f5d0d16365 | |||
97ca8ac084
|
|||
1c1385b768
|
|||
35432b6294
|
|||
c59dd30e53
|
|||
d2da0ddb88
|
|||
8066701c3c
|
|||
0f66ac73d0
|
|||
4370bf51d7
|
|||
2b7353b9e0
|
|||
b686b3c9c3
|
|||
2dd04a6c44
|
|||
1da83662d0
|
|||
3ac9593c65
|
|||
6b317accf1
|
|||
4964aab722
|
|||
7a6ec73d63 |
@@ -5,6 +5,7 @@ Checks: '-*,
|
|||||||
cppcoreguidelines-*,
|
cppcoreguidelines-*,
|
||||||
modernize-*,
|
modernize-*,
|
||||||
performance-*,
|
performance-*,
|
||||||
|
-modernize-use-nodiscard,
|
||||||
-cppcoreguidelines-pro-type-vararg,
|
-cppcoreguidelines-pro-type-vararg,
|
||||||
-modernize-use-trailing-return-type,
|
-modernize-use-trailing-return-type,
|
||||||
-bugprone-exception-escape'
|
-bugprone-exception-escape'
|
||||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -31,7 +31,10 @@
|
|||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
build/
|
build/**
|
||||||
|
build_*/**
|
||||||
*.dSYM/**
|
*.dSYM/**
|
||||||
cmake-build*/**
|
cmake-build*/**
|
||||||
.idea
|
.idea
|
||||||
|
puml/**
|
||||||
|
.vscode/settings.json
|
||||||
|
14
.gitmodules
vendored
14
.gitmodules
vendored
@@ -1,12 +1,20 @@
|
|||||||
[submodule "lib/mdlp"]
|
[submodule "lib/mdlp"]
|
||||||
path = lib/mdlp
|
path = lib/mdlp
|
||||||
url = https://github.com/rmontanana/mdlp
|
url = https://github.com/rmontanana/mdlp
|
||||||
|
main = main
|
||||||
|
update = merge
|
||||||
[submodule "lib/catch2"]
|
[submodule "lib/catch2"]
|
||||||
path = lib/catch2
|
path = lib/catch2
|
||||||
|
main = v2.x
|
||||||
|
update = merge
|
||||||
url = https://github.com/catchorg/Catch2.git
|
url = https://github.com/catchorg/Catch2.git
|
||||||
[submodule "lib/argparse"]
|
|
||||||
path = lib/argparse
|
|
||||||
url = https://github.com/p-ranav/argparse
|
|
||||||
[submodule "lib/json"]
|
[submodule "lib/json"]
|
||||||
path = lib/json
|
path = lib/json
|
||||||
url = https://github.com/nlohmann/json.git
|
url = https://github.com/nlohmann/json.git
|
||||||
|
master = master
|
||||||
|
update = merge
|
||||||
|
[submodule "lib/folding"]
|
||||||
|
path = lib/folding
|
||||||
|
url = https://github.com/rmontanana/folding
|
||||||
|
main = main
|
||||||
|
update = merge
|
||||||
|
18
.vscode/c_cpp_properties.json
vendored
Normal file
18
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Mac",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**"
|
||||||
|
],
|
||||||
|
"defines": [],
|
||||||
|
"macFrameworkPath": [
|
||||||
|
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
|
||||||
|
],
|
||||||
|
"cStandard": "c17",
|
||||||
|
"cppStandard": "c++17",
|
||||||
|
"compileCommands": "${workspaceFolder}/cmake-build-release/compile_commands.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
60
.vscode/launch.json
vendored
60
.vscode/launch.json
vendored
@@ -5,67 +5,21 @@
|
|||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "sample",
|
"name": "sample",
|
||||||
"program": "${workspaceFolder}/build/sample/BayesNetSample",
|
"program": "${workspaceFolder}/build_release/sample/bayesnet_sample",
|
||||||
"args": [
|
"args": [
|
||||||
"-d",
|
"${workspaceFolder}/tests/data/glass.arff"
|
||||||
"iris",
|
|
||||||
"-m",
|
|
||||||
"KDB",
|
|
||||||
"-s",
|
|
||||||
"271",
|
|
||||||
"-p",
|
|
||||||
"/Users/rmontanana/Code/discretizbench/datasets/",
|
|
||||||
],
|
],
|
||||||
//"cwd": "${workspaceFolder}/build/sample/",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "experiment",
|
"name": "test",
|
||||||
"program": "${workspaceFolder}/build/src/Platform/main",
|
"program": "${workspaceFolder}/build_debug/tests/unit_tests_bayesnet",
|
||||||
"args": [
|
"args": [
|
||||||
"-m",
|
//"-c=\"Metrics Test\"",
|
||||||
"BoostAODE",
|
// "-s",
|
||||||
"-p",
|
|
||||||
"/Users/rmontanana/Code/discretizbench/datasets",
|
|
||||||
"--discretize",
|
|
||||||
"--stratified",
|
|
||||||
"-d",
|
|
||||||
"iris"
|
|
||||||
],
|
],
|
||||||
"cwd": "/Users/rmontanana/Code/discretizbench",
|
"cwd": "${workspaceFolder}/build_debug/tests",
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "manage",
|
|
||||||
"program": "${workspaceFolder}/build/src/Platform/manage",
|
|
||||||
"args": [
|
|
||||||
"-n",
|
|
||||||
"20"
|
|
||||||
],
|
|
||||||
"cwd": "/Users/rmontanana/Code/discretizbench",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "list",
|
|
||||||
"program": "${workspaceFolder}/build/src/Platform/list",
|
|
||||||
"args": [],
|
|
||||||
"cwd": "/Users/rmontanana/Code/discretizbench",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Build & debug active file",
|
|
||||||
"type": "cppdbg",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceFolder}/build/bayesnet",
|
|
||||||
"args": [],
|
|
||||||
"stopAtEntry": false,
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"environment": [],
|
|
||||||
"externalConsole": false,
|
|
||||||
"MIMode": "lldb",
|
|
||||||
"preLaunchTask": "CMake: build"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
109
.vscode/settings.json
vendored
109
.vscode/settings.json
vendored
@@ -1,109 +0,0 @@
|
|||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"*.rmd": "markdown",
|
|
||||||
"*.py": "python",
|
|
||||||
"vector": "cpp",
|
|
||||||
"__bit_reference": "cpp",
|
|
||||||
"__bits": "cpp",
|
|
||||||
"__config": "cpp",
|
|
||||||
"__debug": "cpp",
|
|
||||||
"__errc": "cpp",
|
|
||||||
"__hash_table": "cpp",
|
|
||||||
"__locale": "cpp",
|
|
||||||
"__mutex_base": "cpp",
|
|
||||||
"__node_handle": "cpp",
|
|
||||||
"__nullptr": "cpp",
|
|
||||||
"__split_buffer": "cpp",
|
|
||||||
"__string": "cpp",
|
|
||||||
"__threading_support": "cpp",
|
|
||||||
"__tuple": "cpp",
|
|
||||||
"array": "cpp",
|
|
||||||
"atomic": "cpp",
|
|
||||||
"bitset": "cpp",
|
|
||||||
"cctype": "cpp",
|
|
||||||
"chrono": "cpp",
|
|
||||||
"clocale": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"compare": "cpp",
|
|
||||||
"complex": "cpp",
|
|
||||||
"concepts": "cpp",
|
|
||||||
"cstdarg": "cpp",
|
|
||||||
"cstddef": "cpp",
|
|
||||||
"cstdint": "cpp",
|
|
||||||
"cstdio": "cpp",
|
|
||||||
"cstdlib": "cpp",
|
|
||||||
"cstring": "cpp",
|
|
||||||
"ctime": "cpp",
|
|
||||||
"cwchar": "cpp",
|
|
||||||
"cwctype": "cpp",
|
|
||||||
"exception": "cpp",
|
|
||||||
"initializer_list": "cpp",
|
|
||||||
"ios": "cpp",
|
|
||||||
"iosfwd": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"locale": "cpp",
|
|
||||||
"memory": "cpp",
|
|
||||||
"mutex": "cpp",
|
|
||||||
"new": "cpp",
|
|
||||||
"optional": "cpp",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"ratio": "cpp",
|
|
||||||
"sstream": "cpp",
|
|
||||||
"stdexcept": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"string_view": "cpp",
|
|
||||||
"system_error": "cpp",
|
|
||||||
"tuple": "cpp",
|
|
||||||
"type_traits": "cpp",
|
|
||||||
"typeinfo": "cpp",
|
|
||||||
"unordered_map": "cpp",
|
|
||||||
"variant": "cpp",
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"iostream": "cpp",
|
|
||||||
"iomanip": "cpp",
|
|
||||||
"numeric": "cpp",
|
|
||||||
"set": "cpp",
|
|
||||||
"__tree": "cpp",
|
|
||||||
"deque": "cpp",
|
|
||||||
"list": "cpp",
|
|
||||||
"map": "cpp",
|
|
||||||
"unordered_set": "cpp",
|
|
||||||
"any": "cpp",
|
|
||||||
"condition_variable": "cpp",
|
|
||||||
"forward_list": "cpp",
|
|
||||||
"fstream": "cpp",
|
|
||||||
"stack": "cpp",
|
|
||||||
"thread": "cpp",
|
|
||||||
"__memory": "cpp",
|
|
||||||
"filesystem": "cpp",
|
|
||||||
"*.toml": "toml",
|
|
||||||
"utility": "cpp",
|
|
||||||
"__verbose_abort": "cpp",
|
|
||||||
"bit": "cpp",
|
|
||||||
"random": "cpp",
|
|
||||||
"*.tcc": "cpp",
|
|
||||||
"functional": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"memory_resource": "cpp",
|
|
||||||
"format": "cpp",
|
|
||||||
"valarray": "cpp",
|
|
||||||
"regex": "cpp",
|
|
||||||
"span": "cpp",
|
|
||||||
"cfenv": "cpp",
|
|
||||||
"cinttypes": "cpp",
|
|
||||||
"csetjmp": "cpp",
|
|
||||||
"future": "cpp",
|
|
||||||
"queue": "cpp",
|
|
||||||
"typeindex": "cpp",
|
|
||||||
"shared_mutex": "cpp",
|
|
||||||
"*.ipp": "cpp",
|
|
||||||
"cassert": "cpp",
|
|
||||||
"charconv": "cpp",
|
|
||||||
"source_location": "cpp",
|
|
||||||
"ranges": "cpp"
|
|
||||||
},
|
|
||||||
"cmake.configureOnOpen": false,
|
|
||||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
|
|
||||||
}
|
|
54
CHANGELOG.md
Normal file
54
CHANGELOG.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.0.4]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Change _ascending_ hyperparameter to _order_ with these possible values _{"asc", "desc", "rand"}_, Default is _"desc"_.
|
||||||
|
- Add the _predict_single_ hyperparameter to control if only the last model created is used to predict in boost training or the whole ensemble (all the models built so far). Default is true.
|
||||||
|
- sample app to show how to use the library (make sample)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change the library structure adding folders for each group of classes (classifiers, ensembles, etc).
|
||||||
|
- The significances of the models generated under the feature selection algorithm are now computed after all the models have been generated and an α<sub>t</sub> value is computed and assigned to each model.
|
||||||
|
|
||||||
|
## [1.0.3]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Voting / probability aggregation in Ensemble classes
|
||||||
|
- predict_proba method in Classifier
|
||||||
|
- predict_proba method in BoostAODE
|
||||||
|
- predict_voting parameter in BoostAODE constructor to use voting or probability to predict (default is voting)
|
||||||
|
- hyperparameter predict_voting to AODE, AODELd and BoostAODE (Ensemble child classes)
|
||||||
|
- tests to check predict & predict_proba coherence
|
||||||
|
|
||||||
|
## [1.0.2] - 2024-02-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix bug in BoostAODE: do not include the model if epsilon sub t is greater than 0.5
|
||||||
|
- Fix bug in BoostAODE: compare accuracy with previous accuracy instead of the first of the ensemble if convergence true
|
||||||
|
|
||||||
|
## [1.0.1] - 2024-02-12
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Notes in Classifier class
|
||||||
|
- BoostAODE: Add note with used features in initialization with feature selection
|
||||||
|
- BoostAODE: Add note with the number of models
|
||||||
|
- BoostAODE: Add note with the number of features used to create models if not all features are used
|
||||||
|
- Test version number in TestBayesModels
|
||||||
|
- Add tests with feature_select and notes on BoostAODE
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Network predict test
|
||||||
|
- Network predict_proba test
|
||||||
|
- Network score test
|
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.20)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
project(BayesNet
|
project(BayesNet
|
||||||
VERSION 0.1.0
|
VERSION 1.0.4
|
||||||
DESCRIPTION "Bayesian Network and basic classifiers Library."
|
DESCRIPTION "Bayesian Network and basic classifiers Library."
|
||||||
HOMEPAGE_URL "https://github.com/rmontanana/bayesnet"
|
HOMEPAGE_URL "https://github.com/rmontanana/bayesnet"
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
@@ -24,7 +24,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||||
# Options
|
# Options
|
||||||
# -------
|
# -------
|
||||||
option(ENABLE_CLANG_TIDY "Enable to add clang tidy." OFF)
|
option(ENABLE_CLANG_TIDY "Enable to add clang tidy." OFF)
|
||||||
@@ -34,15 +34,21 @@ option(CODE_COVERAGE "Collect coverage from test library" OFF)
|
|||||||
# CMakes modules
|
# CMakes modules
|
||||||
# --------------
|
# --------------
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
|
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
|
||||||
|
|
||||||
include(AddGitSubmodule)
|
include(AddGitSubmodule)
|
||||||
|
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
MESSAGE("Debug mode")
|
||||||
|
set(ENABLE_TESTING ON)
|
||||||
|
set(CODE_COVERAGE ON)
|
||||||
|
endif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
|
||||||
|
|
||||||
if (CODE_COVERAGE)
|
if (CODE_COVERAGE)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
include(CodeCoverage)
|
include(CodeCoverage)
|
||||||
MESSAGE("Code coverage enabled")
|
MESSAGE("Code coverage enabled")
|
||||||
set(CMAKE_C_FLAGS " ${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
|
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -O0 -g")
|
||||||
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
|
SET(GCC_COVERAGE_LINK_FLAGS " ${GCC_COVERAGE_LINK_FLAGS} -lgcov --coverage")
|
||||||
SET(GCC_COVERAGE_LINK_FLAGS " ${GCC_COVERAGE_LINK_FLAGS} -lgcov --coverage")
|
|
||||||
endif (CODE_COVERAGE)
|
endif (CODE_COVERAGE)
|
||||||
|
|
||||||
if (ENABLE_CLANG_TIDY)
|
if (ENABLE_CLANG_TIDY)
|
||||||
@@ -53,28 +59,20 @@ endif (ENABLE_CLANG_TIDY)
|
|||||||
# ---------------------------------------------
|
# ---------------------------------------------
|
||||||
# include(FetchContent)
|
# include(FetchContent)
|
||||||
add_git_submodule("lib/mdlp")
|
add_git_submodule("lib/mdlp")
|
||||||
add_git_submodule("lib/argparse")
|
|
||||||
add_git_submodule("lib/json")
|
add_git_submodule("lib/json")
|
||||||
|
|
||||||
# Subdirectories
|
# Subdirectories
|
||||||
# --------------
|
# --------------
|
||||||
add_subdirectory(config)
|
add_subdirectory(config)
|
||||||
add_subdirectory(lib/Files)
|
add_subdirectory(lib/Files)
|
||||||
add_subdirectory(src/BayesNet)
|
|
||||||
add_subdirectory(src/Platform)
|
|
||||||
add_subdirectory(sample)
|
add_subdirectory(sample)
|
||||||
|
add_subdirectory(src)
|
||||||
file(GLOB BayesNet_HEADERS CONFIGURE_DEPENDS ${BayesNet_SOURCE_DIR}/src/BayesNet/*.h ${BayesNet_SOURCE_DIR}/BayesNet/*.hpp)
|
|
||||||
file(GLOB BayesNet_SOURCES CONFIGURE_DEPENDS ${BayesNet_SOURCE_DIR}/src/BayesNet/*.cc ${BayesNet_SOURCE_DIR}/src/BayesNet/*.cpp)
|
|
||||||
file(GLOB Platform_SOURCES CONFIGURE_DEPENDS ${BayesNet_SOURCE_DIR}/src/Platform/*.cc ${BayesNet_SOURCE_DIR}/src/Platform/*.cpp)
|
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
# -------
|
# -------
|
||||||
|
|
||||||
if (ENABLE_TESTING)
|
if (ENABLE_TESTING)
|
||||||
MESSAGE("Testing enabled")
|
MESSAGE("Testing enabled")
|
||||||
add_git_submodule("lib/catch2")
|
add_git_submodule("lib/catch2")
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif (ENABLE_TESTING)
|
endif (ENABLE_TESTING)
|
||||||
|
95
Makefile
95
Makefile
@@ -1,6 +1,26 @@
|
|||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
.PHONY: coverage setup help build test
|
.PHONY: coverage setup help buildr buildd test clean debug release sample
|
||||||
|
|
||||||
|
f_release = build_release
|
||||||
|
f_debug = build_debug
|
||||||
|
app_targets = BayesNet
|
||||||
|
test_targets = unit_tests_bayesnet
|
||||||
|
n_procs = -j 16
|
||||||
|
|
||||||
|
define ClearTests
|
||||||
|
@for t in $(test_targets); do \
|
||||||
|
if [ -f $(f_debug)/tests/$$t ]; then \
|
||||||
|
echo ">>> Cleaning $$t..." ; \
|
||||||
|
rm -f $(f_debug)/tests/$$t ; \
|
||||||
|
fi ; \
|
||||||
|
done
|
||||||
|
@nfiles="$(find . -name "*.gcda" -print0)" ; \
|
||||||
|
if test "${nfiles}" != "" ; then \
|
||||||
|
find . -name "*.gcda" -print0 | xargs -0 rm 2>/dev/null ;\
|
||||||
|
fi ;
|
||||||
|
endef
|
||||||
|
|
||||||
|
|
||||||
setup: ## Install dependencies for tests and coverage
|
setup: ## Install dependencies for tests and coverage
|
||||||
@if [ "$(shell uname)" = "Darwin" ]; then \
|
@if [ "$(shell uname)" = "Darwin" ]; then \
|
||||||
@@ -12,48 +32,61 @@ setup: ## Install dependencies for tests and coverage
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
dependency: ## Create a dependency graph diagram of the project (build/dependency.png)
|
dependency: ## Create a dependency graph diagram of the project (build/dependency.png)
|
||||||
cd build && cmake .. --graphviz=dependency.dot && dot -Tpng dependency.dot -o dependency.png
|
@echo ">>> Creating dependency graph diagram of the project...";
|
||||||
|
$(MAKE) debug
|
||||||
|
cd $(f_debug) && cmake .. --graphviz=dependency.dot && dot -Tpng dependency.dot -o dependency.png
|
||||||
|
|
||||||
build: ## Build the main and BayesNetSample
|
buildd: ## Build the debug targets
|
||||||
cmake --build build -t main -t BayesNetSample -t manage -t list -j 32
|
cmake --build $(f_debug) -t $(app_targets) $(n_procs)
|
||||||
|
|
||||||
clean: ## Clean the debug info
|
buildr: ## Build the release targets
|
||||||
@echo ">>> Cleaning Debug BayesNet ...";
|
cmake --build $(f_release) -t $(app_targets) $(n_procs)
|
||||||
find . -name "*.gcda" -print0 | xargs -0 rm
|
|
||||||
|
clean: ## Clean the tests info
|
||||||
|
@echo ">>> Cleaning Debug BayesNet tests...";
|
||||||
|
$(call ClearTests)
|
||||||
@echo ">>> Done";
|
@echo ">>> Done";
|
||||||
|
|
||||||
debug: ## Build a debug version of the project
|
debug: ## Build a debug version of the project
|
||||||
@echo ">>> Building Debug BayesNet ...";
|
@echo ">>> Building Debug BayesNet...";
|
||||||
@if [ -d ./build ]; then rm -rf ./build; fi
|
@if [ -d ./$(f_debug) ]; then rm -rf ./$(f_debug); fi
|
||||||
@mkdir build;
|
@mkdir $(f_debug);
|
||||||
cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -D ENABLE_TESTING=ON -D CODE_COVERAGE=ON; \
|
@cmake -S . -B $(f_debug) -D CMAKE_BUILD_TYPE=Debug -D ENABLE_TESTING=ON -D CODE_COVERAGE=ON
|
||||||
cmake --build build -j 32;
|
|
||||||
@echo ">>> Done";
|
@echo ">>> Done";
|
||||||
|
|
||||||
release: ## Build a Release version of the project
|
release: ## Build a Release version of the project
|
||||||
@echo ">>> Building Release BayesNet ...";
|
@echo ">>> Building Release BayesNet...";
|
||||||
@if [ -d ./build ]; then rm -rf ./build; fi
|
@if [ -d ./$(f_release) ]; then rm -rf ./$(f_release); fi
|
||||||
@mkdir build;
|
@mkdir $(f_release);
|
||||||
cmake -S . -B build -D CMAKE_BUILD_TYPE=Release; \
|
@cmake -S . -B $(f_release) -D CMAKE_BUILD_TYPE=Release
|
||||||
cmake --build build -t main -t BayesNetSample -t manage -t list -j 32;
|
|
||||||
@echo ">>> Done";
|
@echo ">>> Done";
|
||||||
|
|
||||||
test: ## Run tests
|
fname = "tests/data/iris.arff"
|
||||||
@echo "* Running tests...";
|
sample: ## Build sample
|
||||||
find . -name "*.gcda" -print0 | xargs -0 rm
|
@echo ">>> Building Sample...";
|
||||||
@cd build; \
|
cmake --build $(f_release) -t bayesnet_sample $(n_procs)
|
||||||
cmake --build . --target unit_tests ;
|
$(f_release)/sample/bayesnet_sample $(fname)
|
||||||
@cd build/tests; \
|
@echo ">>> Done";
|
||||||
./unit_tests;
|
|
||||||
|
opt = ""
|
||||||
|
test: ## Run tests (opt="-s") to verbose output the tests, (opt="-c='Test Maximum Spanning Tree'") to run only that section
|
||||||
|
@echo ">>> Running BayesNet & Platform tests...";
|
||||||
|
@$(MAKE) clean
|
||||||
|
@cmake --build $(f_debug) -t $(test_targets) $(n_procs)
|
||||||
|
@for t in $(test_targets); do \
|
||||||
|
if [ -f $(f_debug)/tests/$$t ]; then \
|
||||||
|
cd $(f_debug)/tests ; \
|
||||||
|
./$$t $(opt) ; \
|
||||||
|
fi ; \
|
||||||
|
done
|
||||||
|
@echo ">>> Done";
|
||||||
|
|
||||||
coverage: ## Run tests and generate coverage report (build/index.html)
|
coverage: ## Run tests and generate coverage report (build/index.html)
|
||||||
@echo "*Building tests...";
|
@echo ">>> Building tests with coverage..."
|
||||||
find . -name "*.gcda" -print0 | xargs -0 rm
|
@$(MAKE) test
|
||||||
@cd build; \
|
@gcovr $(f_debug)/tests
|
||||||
cmake --build . --target unit_tests ;
|
@echo ">>> Done";
|
||||||
@cd build/tests; \
|
|
||||||
./unit_tests;
|
|
||||||
gcovr ;
|
|
||||||
|
|
||||||
help: ## Show help message
|
help: ## Show help message
|
||||||
@IFS=$$'\n' ; \
|
@IFS=$$'\n' ; \
|
||||||
|
31
README.md
31
README.md
@@ -1,5 +1,32 @@
|
|||||||
# BayesNet
|
# BayesNet
|
||||||
|
|
||||||
Bayesian Network Classifier with libtorch from scratch
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
## 1. Introduction
|
Bayesian Network Classifiers using libtorch from scratch
|
||||||
|
|
||||||
|
### Release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make release
|
||||||
|
make buildr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug & Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make debug
|
||||||
|
make test
|
||||||
|
make coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sample app
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make release
|
||||||
|
make sample
|
||||||
|
make sample fname=tests/data/glass.arff
|
||||||
|
```
|
||||||
|
|
||||||
|
## Models
|
||||||
|
|
||||||
|
### [BoostAODE](docs/BoostAODE.md)
|
||||||
|
12
TAN_iris.dot
12
TAN_iris.dot
@@ -1,12 +0,0 @@
|
|||||||
digraph BayesNet {
|
|
||||||
label=<BayesNet >
|
|
||||||
fontsize=30
|
|
||||||
fontcolor=blue
|
|
||||||
labelloc=t
|
|
||||||
layout=circo
|
|
||||||
class [shape=circle, fontcolor=red, fillcolor=lightblue, style=filled ]
|
|
||||||
class -> sepallength class -> sepalwidth class -> petallength class -> petalwidth petallength [shape=circle]
|
|
||||||
petallength -> sepallength petalwidth [shape=circle]
|
|
||||||
sepallength [shape=circle]
|
|
||||||
sepallength -> sepalwidth sepalwidth [shape=circle]
|
|
||||||
sepalwidth -> petalwidth }
|
|
@@ -7,7 +7,8 @@
|
|||||||
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR @
|
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR @
|
||||||
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH @
|
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH @
|
||||||
|
|
||||||
static constexpr std::string_view project_name = " @PROJECT_NAME@ ";
|
static constexpr std::string_view project_name = "@PROJECT_NAME@";
|
||||||
static constexpr std::string_view project_version = "@PROJECT_VERSION@";
|
static constexpr std::string_view project_version = "@PROJECT_VERSION@";
|
||||||
static constexpr std::string_view project_description = "@PROJECT_DESCRIPTION@";
|
static constexpr std::string_view project_description = "@PROJECT_DESCRIPTION@";
|
||||||
static constexpr std::string_view git_sha = "@GIT_SHA@";
|
static constexpr std::string_view git_sha = "@GIT_SHA@";
|
||||||
|
static constexpr std::string_view data_path = "@BayesNet_SOURCE_DIR@/tests/data/";
|
@@ -1 +0,0 @@
|
|||||||
null
|
|
@@ -1,25 +0,0 @@
|
|||||||
Type Si
|
|
||||||
Type Fe
|
|
||||||
Type RI
|
|
||||||
Type Na
|
|
||||||
Type Ba
|
|
||||||
Type Ca
|
|
||||||
Type Al
|
|
||||||
Type K
|
|
||||||
Type Mg
|
|
||||||
Fe RI
|
|
||||||
Fe Ba
|
|
||||||
Fe Ca
|
|
||||||
RI Na
|
|
||||||
RI Ba
|
|
||||||
RI Ca
|
|
||||||
RI Al
|
|
||||||
RI K
|
|
||||||
RI Mg
|
|
||||||
Ba Ca
|
|
||||||
Ba Al
|
|
||||||
Ca Al
|
|
||||||
Ca K
|
|
||||||
Ca Mg
|
|
||||||
Al K
|
|
||||||
K Mg
|
|
@@ -1,645 +0,0 @@
|
|||||||
class att215
|
|
||||||
class att25
|
|
||||||
class att131
|
|
||||||
class att95
|
|
||||||
class att122
|
|
||||||
class att17
|
|
||||||
class att28
|
|
||||||
class att5
|
|
||||||
class att121
|
|
||||||
class att214
|
|
||||||
class att197
|
|
||||||
class att116
|
|
||||||
class att182
|
|
||||||
class att60
|
|
||||||
class att168
|
|
||||||
class att178
|
|
||||||
class att206
|
|
||||||
class att89
|
|
||||||
class att77
|
|
||||||
class att209
|
|
||||||
class att73
|
|
||||||
class att126
|
|
||||||
class att16
|
|
||||||
class att74
|
|
||||||
class att27
|
|
||||||
class att61
|
|
||||||
class att20
|
|
||||||
class att101
|
|
||||||
class att85
|
|
||||||
class att76
|
|
||||||
class att137
|
|
||||||
class att211
|
|
||||||
class att143
|
|
||||||
class att14
|
|
||||||
class att40
|
|
||||||
class att210
|
|
||||||
class att155
|
|
||||||
class att170
|
|
||||||
class att160
|
|
||||||
class att23
|
|
||||||
class att162
|
|
||||||
class att203
|
|
||||||
class att164
|
|
||||||
class att107
|
|
||||||
class att62
|
|
||||||
class att42
|
|
||||||
class att71
|
|
||||||
class att128
|
|
||||||
class att138
|
|
||||||
class att83
|
|
||||||
class att171
|
|
||||||
class att92
|
|
||||||
class att163
|
|
||||||
class att49
|
|
||||||
class att161
|
|
||||||
class att158
|
|
||||||
class att176
|
|
||||||
class att11
|
|
||||||
class att145
|
|
||||||
class att4
|
|
||||||
class att172
|
|
||||||
class att196
|
|
||||||
class att58
|
|
||||||
class att68
|
|
||||||
class att169
|
|
||||||
class att80
|
|
||||||
class att32
|
|
||||||
class att175
|
|
||||||
class att87
|
|
||||||
class att88
|
|
||||||
class att159
|
|
||||||
class att18
|
|
||||||
class att52
|
|
||||||
class att98
|
|
||||||
class att136
|
|
||||||
class att150
|
|
||||||
class att156
|
|
||||||
class att110
|
|
||||||
class att100
|
|
||||||
class att63
|
|
||||||
class att148
|
|
||||||
class att90
|
|
||||||
class att167
|
|
||||||
class att35
|
|
||||||
class att205
|
|
||||||
class att51
|
|
||||||
class att21
|
|
||||||
class att142
|
|
||||||
class att46
|
|
||||||
class att134
|
|
||||||
class att39
|
|
||||||
class att102
|
|
||||||
class att208
|
|
||||||
class att130
|
|
||||||
class att149
|
|
||||||
class att96
|
|
||||||
class att75
|
|
||||||
class att118
|
|
||||||
class att78
|
|
||||||
class att213
|
|
||||||
class att112
|
|
||||||
class att38
|
|
||||||
class att174
|
|
||||||
class att189
|
|
||||||
class att70
|
|
||||||
class att179
|
|
||||||
class att59
|
|
||||||
class att79
|
|
||||||
class att15
|
|
||||||
class att47
|
|
||||||
class att124
|
|
||||||
class att34
|
|
||||||
class att54
|
|
||||||
class att191
|
|
||||||
class att86
|
|
||||||
class att56
|
|
||||||
class att151
|
|
||||||
class att66
|
|
||||||
class att173
|
|
||||||
class att44
|
|
||||||
class att198
|
|
||||||
class att139
|
|
||||||
class att216
|
|
||||||
class att129
|
|
||||||
class att152
|
|
||||||
class att69
|
|
||||||
class att81
|
|
||||||
class att50
|
|
||||||
class att153
|
|
||||||
class att41
|
|
||||||
class att204
|
|
||||||
class att188
|
|
||||||
class att26
|
|
||||||
class att13
|
|
||||||
class att117
|
|
||||||
class att114
|
|
||||||
class att10
|
|
||||||
class att64
|
|
||||||
class att200
|
|
||||||
class att9
|
|
||||||
class att3
|
|
||||||
class att119
|
|
||||||
class att45
|
|
||||||
class att104
|
|
||||||
class att140
|
|
||||||
class att30
|
|
||||||
class att183
|
|
||||||
class att146
|
|
||||||
class att141
|
|
||||||
class att202
|
|
||||||
class att194
|
|
||||||
class att24
|
|
||||||
class att147
|
|
||||||
class att8
|
|
||||||
class att212
|
|
||||||
class att123
|
|
||||||
class att166
|
|
||||||
class att187
|
|
||||||
class att127
|
|
||||||
class att190
|
|
||||||
class att105
|
|
||||||
class att106
|
|
||||||
class att184
|
|
||||||
class att82
|
|
||||||
class att2
|
|
||||||
class att135
|
|
||||||
class att154
|
|
||||||
class att111
|
|
||||||
class att115
|
|
||||||
class att99
|
|
||||||
class att22
|
|
||||||
class att84
|
|
||||||
class att207
|
|
||||||
class att94
|
|
||||||
class att177
|
|
||||||
class att103
|
|
||||||
class att93
|
|
||||||
class att201
|
|
||||||
class att43
|
|
||||||
class att36
|
|
||||||
class att12
|
|
||||||
class att125
|
|
||||||
class att165
|
|
||||||
class att180
|
|
||||||
class att195
|
|
||||||
class att157
|
|
||||||
class att48
|
|
||||||
class att6
|
|
||||||
class att113
|
|
||||||
class att193
|
|
||||||
class att91
|
|
||||||
class att72
|
|
||||||
class att31
|
|
||||||
class att132
|
|
||||||
class att33
|
|
||||||
class att57
|
|
||||||
class att144
|
|
||||||
class att192
|
|
||||||
class att185
|
|
||||||
class att37
|
|
||||||
class att53
|
|
||||||
class att120
|
|
||||||
class att186
|
|
||||||
class att199
|
|
||||||
class att65
|
|
||||||
class att108
|
|
||||||
class att133
|
|
||||||
class att29
|
|
||||||
class att19
|
|
||||||
class att7
|
|
||||||
class att97
|
|
||||||
class att67
|
|
||||||
class att55
|
|
||||||
class att1
|
|
||||||
class att109
|
|
||||||
class att181
|
|
||||||
att215 att25
|
|
||||||
att215 att131
|
|
||||||
att215 att95
|
|
||||||
att25 att131
|
|
||||||
att25 att121
|
|
||||||
att25 att73
|
|
||||||
att25 att61
|
|
||||||
att25 att85
|
|
||||||
att25 att169
|
|
||||||
att25 att13
|
|
||||||
att131 att95
|
|
||||||
att131 att122
|
|
||||||
att131 att17
|
|
||||||
att131 att28
|
|
||||||
att131 att121
|
|
||||||
att131 att214
|
|
||||||
att131 att116
|
|
||||||
att131 att126
|
|
||||||
att131 att143
|
|
||||||
att95 att122
|
|
||||||
att95 att17
|
|
||||||
att95 att28
|
|
||||||
att95 att5
|
|
||||||
att95 att214
|
|
||||||
att95 att116
|
|
||||||
att95 att60
|
|
||||||
att95 att143
|
|
||||||
att95 att155
|
|
||||||
att95 att71
|
|
||||||
att122 att182
|
|
||||||
att122 att170
|
|
||||||
att17 att5
|
|
||||||
att17 att197
|
|
||||||
att17 att89
|
|
||||||
att17 att77
|
|
||||||
att17 att161
|
|
||||||
att28 att206
|
|
||||||
att28 att16
|
|
||||||
att28 att76
|
|
||||||
att28 att172
|
|
||||||
att28 att124
|
|
||||||
att28 att64
|
|
||||||
att5 att197
|
|
||||||
att5 att89
|
|
||||||
att5 att209
|
|
||||||
att121 att73
|
|
||||||
att214 att178
|
|
||||||
att214 att58
|
|
||||||
att214 att142
|
|
||||||
att197 att209
|
|
||||||
att197 att101
|
|
||||||
att116 att182
|
|
||||||
att116 att60
|
|
||||||
att116 att168
|
|
||||||
att116 att178
|
|
||||||
att116 att206
|
|
||||||
att116 att126
|
|
||||||
att116 att16
|
|
||||||
att116 att27
|
|
||||||
att116 att20
|
|
||||||
att116 att211
|
|
||||||
att116 att164
|
|
||||||
att116 att128
|
|
||||||
att182 att27
|
|
||||||
att182 att14
|
|
||||||
att60 att168
|
|
||||||
att60 att156
|
|
||||||
att168 att156
|
|
||||||
att168 att96
|
|
||||||
att178 att20
|
|
||||||
att178 att58
|
|
||||||
att178 att142
|
|
||||||
att178 att130
|
|
||||||
att206 att74
|
|
||||||
att206 att170
|
|
||||||
att206 att158
|
|
||||||
att89 att77
|
|
||||||
att89 att137
|
|
||||||
att89 att149
|
|
||||||
att89 att173
|
|
||||||
att77 att137
|
|
||||||
att77 att161
|
|
||||||
att209 att101
|
|
||||||
att209 att41
|
|
||||||
att73 att61
|
|
||||||
att73 att157
|
|
||||||
att126 att162
|
|
||||||
att126 att138
|
|
||||||
att126 att150
|
|
||||||
att16 att74
|
|
||||||
att16 att76
|
|
||||||
att16 att40
|
|
||||||
att16 att4
|
|
||||||
att74 att14
|
|
||||||
att74 att62
|
|
||||||
att27 att171
|
|
||||||
att61 att85
|
|
||||||
att61 att169
|
|
||||||
att20 att211
|
|
||||||
att20 att210
|
|
||||||
att20 att164
|
|
||||||
att20 att176
|
|
||||||
att101 att41
|
|
||||||
att85 att13
|
|
||||||
att76 att40
|
|
||||||
att76 att160
|
|
||||||
att137 att149
|
|
||||||
att211 att210
|
|
||||||
att211 att162
|
|
||||||
att211 att171
|
|
||||||
att211 att163
|
|
||||||
att211 att175
|
|
||||||
att211 att79
|
|
||||||
att143 att155
|
|
||||||
att143 att23
|
|
||||||
att143 att71
|
|
||||||
att143 att83
|
|
||||||
att143 att11
|
|
||||||
att14 att98
|
|
||||||
att40 att160
|
|
||||||
att40 att4
|
|
||||||
att40 att196
|
|
||||||
att40 att52
|
|
||||||
att210 att42
|
|
||||||
att210 att114
|
|
||||||
att155 att23
|
|
||||||
att155 att203
|
|
||||||
att155 att107
|
|
||||||
att155 att11
|
|
||||||
att170 att158
|
|
||||||
att160 att52
|
|
||||||
att23 att203
|
|
||||||
att162 att138
|
|
||||||
att162 att18
|
|
||||||
att162 att150
|
|
||||||
att162 att90
|
|
||||||
att162 att174
|
|
||||||
att203 att107
|
|
||||||
att203 att49
|
|
||||||
att203 att59
|
|
||||||
att203 att191
|
|
||||||
att203 att119
|
|
||||||
att164 att62
|
|
||||||
att164 att42
|
|
||||||
att164 att128
|
|
||||||
att164 att92
|
|
||||||
att164 att163
|
|
||||||
att164 att176
|
|
||||||
att164 att145
|
|
||||||
att164 att68
|
|
||||||
att164 att80
|
|
||||||
att164 att98
|
|
||||||
att164 att110
|
|
||||||
att164 att205
|
|
||||||
att164 att21
|
|
||||||
att164 att213
|
|
||||||
att164 att112
|
|
||||||
att164 att38
|
|
||||||
att164 att56
|
|
||||||
att164 att44
|
|
||||||
att107 att59
|
|
||||||
att107 att47
|
|
||||||
att107 att191
|
|
||||||
att71 att83
|
|
||||||
att71 att167
|
|
||||||
att71 att35
|
|
||||||
att128 att92
|
|
||||||
att138 att18
|
|
||||||
att83 att167
|
|
||||||
att171 att87
|
|
||||||
att171 att159
|
|
||||||
att171 att63
|
|
||||||
att171 att51
|
|
||||||
att171 att39
|
|
||||||
att171 att75
|
|
||||||
att163 att49
|
|
||||||
att163 att175
|
|
||||||
att163 att87
|
|
||||||
att163 att79
|
|
||||||
att163 att151
|
|
||||||
att163 att139
|
|
||||||
att163 att187
|
|
||||||
att163 att91
|
|
||||||
att161 att173
|
|
||||||
att176 att145
|
|
||||||
att176 att172
|
|
||||||
att176 att68
|
|
||||||
att176 att80
|
|
||||||
att176 att32
|
|
||||||
att176 att110
|
|
||||||
att176 att205
|
|
||||||
att176 att21
|
|
||||||
att176 att134
|
|
||||||
att176 att56
|
|
||||||
att4 att196
|
|
||||||
att4 att88
|
|
||||||
att4 att136
|
|
||||||
att4 att100
|
|
||||||
att4 att148
|
|
||||||
att4 att208
|
|
||||||
att172 att112
|
|
||||||
att172 att184
|
|
||||||
att196 att88
|
|
||||||
att196 att136
|
|
||||||
att196 att100
|
|
||||||
att196 att208
|
|
||||||
att58 att46
|
|
||||||
att68 att32
|
|
||||||
att32 att200
|
|
||||||
att87 att159
|
|
||||||
att87 att63
|
|
||||||
att87 att75
|
|
||||||
att87 att15
|
|
||||||
att87 att99
|
|
||||||
att159 att195
|
|
||||||
att18 att90
|
|
||||||
att18 att102
|
|
||||||
att18 att78
|
|
||||||
att18 att198
|
|
||||||
att52 att124
|
|
||||||
att98 att86
|
|
||||||
att150 att174
|
|
||||||
att150 att66
|
|
||||||
att156 att96
|
|
||||||
att156 att216
|
|
||||||
att156 att204
|
|
||||||
att156 att24
|
|
||||||
att156 att84
|
|
||||||
att100 att148
|
|
||||||
att63 att51
|
|
||||||
att63 att3
|
|
||||||
att63 att183
|
|
||||||
att90 att102
|
|
||||||
att90 att78
|
|
||||||
att167 att35
|
|
||||||
att167 att179
|
|
||||||
att35 att179
|
|
||||||
att51 att39
|
|
||||||
att51 att3
|
|
||||||
att21 att134
|
|
||||||
att21 att213
|
|
||||||
att21 att38
|
|
||||||
att21 att189
|
|
||||||
att21 att129
|
|
||||||
att21 att81
|
|
||||||
att21 att117
|
|
||||||
att21 att9
|
|
||||||
att142 att46
|
|
||||||
att142 att130
|
|
||||||
att142 att118
|
|
||||||
att142 att10
|
|
||||||
att142 att202
|
|
||||||
att142 att190
|
|
||||||
att142 att106
|
|
||||||
att46 att70
|
|
||||||
att46 att34
|
|
||||||
att46 att166
|
|
||||||
att134 att2
|
|
||||||
att102 att54
|
|
||||||
att130 att118
|
|
||||||
att130 att10
|
|
||||||
att130 att202
|
|
||||||
att149 att125
|
|
||||||
att96 att216
|
|
||||||
att96 att24
|
|
||||||
att75 att15
|
|
||||||
att75 att99
|
|
||||||
att118 att70
|
|
||||||
att78 att198
|
|
||||||
att213 att189
|
|
||||||
att38 att50
|
|
||||||
att38 att26
|
|
||||||
att174 att54
|
|
||||||
att174 att66
|
|
||||||
att174 att30
|
|
||||||
att189 att86
|
|
||||||
att189 att129
|
|
||||||
att189 att69
|
|
||||||
att189 att81
|
|
||||||
att189 att153
|
|
||||||
att189 att117
|
|
||||||
att189 att9
|
|
||||||
att189 att45
|
|
||||||
att189 att105
|
|
||||||
att70 att34
|
|
||||||
att59 att47
|
|
||||||
att79 att151
|
|
||||||
att79 att139
|
|
||||||
att79 att187
|
|
||||||
att79 att127
|
|
||||||
att79 att103
|
|
||||||
att79 att43
|
|
||||||
att79 att91
|
|
||||||
att79 att19
|
|
||||||
att124 att64
|
|
||||||
att54 att114
|
|
||||||
att54 att30
|
|
||||||
att191 att119
|
|
||||||
att86 att194
|
|
||||||
att56 att44
|
|
||||||
att56 att152
|
|
||||||
att56 att50
|
|
||||||
att56 att188
|
|
||||||
att56 att26
|
|
||||||
att56 att104
|
|
||||||
att56 att140
|
|
||||||
att56 att146
|
|
||||||
att56 att194
|
|
||||||
att56 att8
|
|
||||||
att56 att2
|
|
||||||
att56 att133
|
|
||||||
att56 att1
|
|
||||||
att173 att125
|
|
||||||
att173 att113
|
|
||||||
att44 att152
|
|
||||||
att44 att188
|
|
||||||
att44 att200
|
|
||||||
att44 att212
|
|
||||||
att44 att1
|
|
||||||
att139 att103
|
|
||||||
att139 att43
|
|
||||||
att139 att31
|
|
||||||
att139 att199
|
|
||||||
att139 att7
|
|
||||||
att216 att204
|
|
||||||
att216 att36
|
|
||||||
att216 att12
|
|
||||||
att216 att180
|
|
||||||
att216 att108
|
|
||||||
att129 att69
|
|
||||||
att152 att140
|
|
||||||
att69 att153
|
|
||||||
att81 att45
|
|
||||||
att153 att141
|
|
||||||
att41 att53
|
|
||||||
att204 att12
|
|
||||||
att13 att157
|
|
||||||
att114 att6
|
|
||||||
att114 att186
|
|
||||||
att10 att190
|
|
||||||
att64 att184
|
|
||||||
att200 att104
|
|
||||||
att9 att146
|
|
||||||
att9 att141
|
|
||||||
att9 att177
|
|
||||||
att9 att37
|
|
||||||
att9 att133
|
|
||||||
att9 att109
|
|
||||||
att9 att181
|
|
||||||
att3 att183
|
|
||||||
att3 att147
|
|
||||||
att3 att123
|
|
||||||
att3 att135
|
|
||||||
att3 att111
|
|
||||||
att45 att105
|
|
||||||
att45 att177
|
|
||||||
att45 att93
|
|
||||||
att45 att201
|
|
||||||
att45 att193
|
|
||||||
att45 att37
|
|
||||||
att45 att97
|
|
||||||
att140 att8
|
|
||||||
att30 att6
|
|
||||||
att183 att147
|
|
||||||
att183 att123
|
|
||||||
att202 att166
|
|
||||||
att202 att106
|
|
||||||
att202 att82
|
|
||||||
att24 att84
|
|
||||||
att24 att36
|
|
||||||
att147 att135
|
|
||||||
att8 att212
|
|
||||||
att166 att82
|
|
||||||
att187 att127
|
|
||||||
att187 att115
|
|
||||||
att127 att115
|
|
||||||
att105 att93
|
|
||||||
att106 att154
|
|
||||||
att82 att154
|
|
||||||
att82 att22
|
|
||||||
att135 att111
|
|
||||||
att135 att207
|
|
||||||
att154 att22
|
|
||||||
att154 att94
|
|
||||||
att111 att207
|
|
||||||
att22 att94
|
|
||||||
att84 att48
|
|
||||||
att177 att165
|
|
||||||
att103 att195
|
|
||||||
att103 att109
|
|
||||||
att93 att201
|
|
||||||
att93 att165
|
|
||||||
att93 att193
|
|
||||||
att93 att33
|
|
||||||
att201 att33
|
|
||||||
att201 att57
|
|
||||||
att36 att180
|
|
||||||
att36 att72
|
|
||||||
att36 att132
|
|
||||||
att36 att144
|
|
||||||
att125 att113
|
|
||||||
att125 att185
|
|
||||||
att125 att65
|
|
||||||
att125 att29
|
|
||||||
att180 att48
|
|
||||||
att180 att72
|
|
||||||
att180 att192
|
|
||||||
att180 att108
|
|
||||||
att6 att186
|
|
||||||
att113 att185
|
|
||||||
att113 att53
|
|
||||||
att193 att97
|
|
||||||
att91 att31
|
|
||||||
att91 att19
|
|
||||||
att72 att132
|
|
||||||
att72 att192
|
|
||||||
att31 att199
|
|
||||||
att31 att67
|
|
||||||
att132 att144
|
|
||||||
att132 att120
|
|
||||||
att33 att57
|
|
||||||
att144 att120
|
|
||||||
att185 att65
|
|
||||||
att199 att7
|
|
||||||
att199 att67
|
|
||||||
att199 att55
|
|
||||||
att65 att29
|
|
||||||
att67 att55
|
|
||||||
att109 att181
|
|
@@ -1,859 +0,0 @@
|
|||||||
class att215
|
|
||||||
class att25
|
|
||||||
class att131
|
|
||||||
class att95
|
|
||||||
class att122
|
|
||||||
class att17
|
|
||||||
class att28
|
|
||||||
class att5
|
|
||||||
class att121
|
|
||||||
class att214
|
|
||||||
class att197
|
|
||||||
class att116
|
|
||||||
class att182
|
|
||||||
class att60
|
|
||||||
class att168
|
|
||||||
class att178
|
|
||||||
class att206
|
|
||||||
class att89
|
|
||||||
class att77
|
|
||||||
class att209
|
|
||||||
class att73
|
|
||||||
class att126
|
|
||||||
class att16
|
|
||||||
class att74
|
|
||||||
class att27
|
|
||||||
class att61
|
|
||||||
class att20
|
|
||||||
class att101
|
|
||||||
class att85
|
|
||||||
class att76
|
|
||||||
class att137
|
|
||||||
class att211
|
|
||||||
class att143
|
|
||||||
class att14
|
|
||||||
class att40
|
|
||||||
class att210
|
|
||||||
class att155
|
|
||||||
class att170
|
|
||||||
class att160
|
|
||||||
class att23
|
|
||||||
class att162
|
|
||||||
class att203
|
|
||||||
class att164
|
|
||||||
class att107
|
|
||||||
class att62
|
|
||||||
class att42
|
|
||||||
class att71
|
|
||||||
class att128
|
|
||||||
class att138
|
|
||||||
class att83
|
|
||||||
class att171
|
|
||||||
class att92
|
|
||||||
class att163
|
|
||||||
class att49
|
|
||||||
class att161
|
|
||||||
class att158
|
|
||||||
class att176
|
|
||||||
class att11
|
|
||||||
class att145
|
|
||||||
class att4
|
|
||||||
class att172
|
|
||||||
class att196
|
|
||||||
class att58
|
|
||||||
class att68
|
|
||||||
class att169
|
|
||||||
class att80
|
|
||||||
class att32
|
|
||||||
class att175
|
|
||||||
class att87
|
|
||||||
class att88
|
|
||||||
class att159
|
|
||||||
class att18
|
|
||||||
class att52
|
|
||||||
class att98
|
|
||||||
class att136
|
|
||||||
class att150
|
|
||||||
class att156
|
|
||||||
class att110
|
|
||||||
class att100
|
|
||||||
class att63
|
|
||||||
class att148
|
|
||||||
class att90
|
|
||||||
class att167
|
|
||||||
class att35
|
|
||||||
class att205
|
|
||||||
class att51
|
|
||||||
class att21
|
|
||||||
class att142
|
|
||||||
class att46
|
|
||||||
class att134
|
|
||||||
class att39
|
|
||||||
class att102
|
|
||||||
class att208
|
|
||||||
class att130
|
|
||||||
class att149
|
|
||||||
class att96
|
|
||||||
class att75
|
|
||||||
class att118
|
|
||||||
class att78
|
|
||||||
class att213
|
|
||||||
class att112
|
|
||||||
class att38
|
|
||||||
class att174
|
|
||||||
class att189
|
|
||||||
class att70
|
|
||||||
class att179
|
|
||||||
class att59
|
|
||||||
class att79
|
|
||||||
class att15
|
|
||||||
class att47
|
|
||||||
class att124
|
|
||||||
class att34
|
|
||||||
class att54
|
|
||||||
class att191
|
|
||||||
class att86
|
|
||||||
class att56
|
|
||||||
class att151
|
|
||||||
class att66
|
|
||||||
class att173
|
|
||||||
class att44
|
|
||||||
class att198
|
|
||||||
class att139
|
|
||||||
class att216
|
|
||||||
class att129
|
|
||||||
class att152
|
|
||||||
class att69
|
|
||||||
class att81
|
|
||||||
class att50
|
|
||||||
class att153
|
|
||||||
class att41
|
|
||||||
class att204
|
|
||||||
class att188
|
|
||||||
class att26
|
|
||||||
class att13
|
|
||||||
class att117
|
|
||||||
class att114
|
|
||||||
class att10
|
|
||||||
class att64
|
|
||||||
class att200
|
|
||||||
class att9
|
|
||||||
class att3
|
|
||||||
class att119
|
|
||||||
class att45
|
|
||||||
class att104
|
|
||||||
class att140
|
|
||||||
class att30
|
|
||||||
class att183
|
|
||||||
class att146
|
|
||||||
class att141
|
|
||||||
class att202
|
|
||||||
class att194
|
|
||||||
class att24
|
|
||||||
class att147
|
|
||||||
class att8
|
|
||||||
class att212
|
|
||||||
class att123
|
|
||||||
class att166
|
|
||||||
class att187
|
|
||||||
class att127
|
|
||||||
class att190
|
|
||||||
class att105
|
|
||||||
class att106
|
|
||||||
class att184
|
|
||||||
class att82
|
|
||||||
class att2
|
|
||||||
class att135
|
|
||||||
class att154
|
|
||||||
class att111
|
|
||||||
class att115
|
|
||||||
class att99
|
|
||||||
class att22
|
|
||||||
class att84
|
|
||||||
class att207
|
|
||||||
class att94
|
|
||||||
class att177
|
|
||||||
class att103
|
|
||||||
class att93
|
|
||||||
class att201
|
|
||||||
class att43
|
|
||||||
class att36
|
|
||||||
class att12
|
|
||||||
class att125
|
|
||||||
class att165
|
|
||||||
class att180
|
|
||||||
class att195
|
|
||||||
class att157
|
|
||||||
class att48
|
|
||||||
class att6
|
|
||||||
class att113
|
|
||||||
class att193
|
|
||||||
class att91
|
|
||||||
class att72
|
|
||||||
class att31
|
|
||||||
class att132
|
|
||||||
class att33
|
|
||||||
class att57
|
|
||||||
class att144
|
|
||||||
class att192
|
|
||||||
class att185
|
|
||||||
class att37
|
|
||||||
class att53
|
|
||||||
class att120
|
|
||||||
class att186
|
|
||||||
class att199
|
|
||||||
class att65
|
|
||||||
class att108
|
|
||||||
class att133
|
|
||||||
class att29
|
|
||||||
class att19
|
|
||||||
class att7
|
|
||||||
class att97
|
|
||||||
class att67
|
|
||||||
class att55
|
|
||||||
class att1
|
|
||||||
class att109
|
|
||||||
class att181
|
|
||||||
att215 att25
|
|
||||||
att215 att131
|
|
||||||
att215 att95
|
|
||||||
att215 att17
|
|
||||||
att215 att214
|
|
||||||
att215 att143
|
|
||||||
att25 att131
|
|
||||||
att25 att95
|
|
||||||
att25 att122
|
|
||||||
att25 att121
|
|
||||||
att25 att73
|
|
||||||
att25 att61
|
|
||||||
att25 att85
|
|
||||||
att25 att169
|
|
||||||
att25 att13
|
|
||||||
att25 att157
|
|
||||||
att131 att95
|
|
||||||
att131 att122
|
|
||||||
att131 att17
|
|
||||||
att131 att28
|
|
||||||
att131 att5
|
|
||||||
att131 att121
|
|
||||||
att131 att214
|
|
||||||
att131 att116
|
|
||||||
att131 att182
|
|
||||||
att131 att60
|
|
||||||
att131 att126
|
|
||||||
att131 att16
|
|
||||||
att131 att27
|
|
||||||
att131 att20
|
|
||||||
att131 att143
|
|
||||||
att131 att155
|
|
||||||
att95 att122
|
|
||||||
att95 att17
|
|
||||||
att95 att28
|
|
||||||
att95 att5
|
|
||||||
att95 att121
|
|
||||||
att95 att214
|
|
||||||
att95 att197
|
|
||||||
att95 att116
|
|
||||||
att95 att60
|
|
||||||
att95 att168
|
|
||||||
att95 att178
|
|
||||||
att95 att143
|
|
||||||
att95 att155
|
|
||||||
att95 att23
|
|
||||||
att95 att71
|
|
||||||
att95 att167
|
|
||||||
att122 att28
|
|
||||||
att122 att182
|
|
||||||
att122 att170
|
|
||||||
att17 att5
|
|
||||||
att17 att197
|
|
||||||
att17 att89
|
|
||||||
att17 att77
|
|
||||||
att17 att209
|
|
||||||
att17 att137
|
|
||||||
att17 att161
|
|
||||||
att17 att41
|
|
||||||
att28 att206
|
|
||||||
att28 att16
|
|
||||||
att28 att76
|
|
||||||
att28 att40
|
|
||||||
att28 att210
|
|
||||||
att28 att160
|
|
||||||
att28 att172
|
|
||||||
att28 att124
|
|
||||||
att28 att64
|
|
||||||
att5 att197
|
|
||||||
att5 att89
|
|
||||||
att5 att77
|
|
||||||
att5 att209
|
|
||||||
att5 att101
|
|
||||||
att121 att73
|
|
||||||
att121 att61
|
|
||||||
att214 att116
|
|
||||||
att214 att178
|
|
||||||
att214 att206
|
|
||||||
att214 att58
|
|
||||||
att214 att142
|
|
||||||
att214 att46
|
|
||||||
att197 att89
|
|
||||||
att197 att209
|
|
||||||
att197 att101
|
|
||||||
att116 att182
|
|
||||||
att116 att60
|
|
||||||
att116 att168
|
|
||||||
att116 att178
|
|
||||||
att116 att206
|
|
||||||
att116 att73
|
|
||||||
att116 att126
|
|
||||||
att116 att16
|
|
||||||
att116 att74
|
|
||||||
att116 att27
|
|
||||||
att116 att20
|
|
||||||
att116 att211
|
|
||||||
att116 att164
|
|
||||||
att116 att128
|
|
||||||
att116 att92
|
|
||||||
att116 att176
|
|
||||||
att116 att68
|
|
||||||
att182 att27
|
|
||||||
att182 att14
|
|
||||||
att60 att168
|
|
||||||
att60 att156
|
|
||||||
att60 att96
|
|
||||||
att168 att126
|
|
||||||
att168 att156
|
|
||||||
att168 att96
|
|
||||||
att168 att216
|
|
||||||
att178 att20
|
|
||||||
att178 att211
|
|
||||||
att178 att58
|
|
||||||
att178 att142
|
|
||||||
att178 att130
|
|
||||||
att178 att166
|
|
||||||
att206 att74
|
|
||||||
att206 att170
|
|
||||||
att206 att158
|
|
||||||
att89 att77
|
|
||||||
att89 att137
|
|
||||||
att89 att149
|
|
||||||
att89 att173
|
|
||||||
att77 att137
|
|
||||||
att77 att161
|
|
||||||
att77 att149
|
|
||||||
att209 att101
|
|
||||||
att209 att41
|
|
||||||
att73 att61
|
|
||||||
att73 att85
|
|
||||||
att73 att13
|
|
||||||
att73 att157
|
|
||||||
att126 att162
|
|
||||||
att126 att138
|
|
||||||
att126 att18
|
|
||||||
att126 att150
|
|
||||||
att16 att74
|
|
||||||
att16 att76
|
|
||||||
att16 att40
|
|
||||||
att16 att4
|
|
||||||
att16 att196
|
|
||||||
att16 att136
|
|
||||||
att74 att14
|
|
||||||
att74 att62
|
|
||||||
att27 att171
|
|
||||||
att27 att63
|
|
||||||
att61 att85
|
|
||||||
att61 att169
|
|
||||||
att20 att76
|
|
||||||
att20 att211
|
|
||||||
att20 att210
|
|
||||||
att20 att170
|
|
||||||
att20 att164
|
|
||||||
att20 att128
|
|
||||||
att20 att176
|
|
||||||
att20 att80
|
|
||||||
att101 att41
|
|
||||||
att85 att169
|
|
||||||
att85 att13
|
|
||||||
att76 att14
|
|
||||||
att76 att40
|
|
||||||
att76 att160
|
|
||||||
att76 att4
|
|
||||||
att76 att52
|
|
||||||
att137 att161
|
|
||||||
att137 att149
|
|
||||||
att137 att173
|
|
||||||
att137 att125
|
|
||||||
att211 att210
|
|
||||||
att211 att162
|
|
||||||
att211 att164
|
|
||||||
att211 att62
|
|
||||||
att211 att42
|
|
||||||
att211 att171
|
|
||||||
att211 att163
|
|
||||||
att211 att175
|
|
||||||
att211 att79
|
|
||||||
att211 att151
|
|
||||||
att211 att43
|
|
||||||
att143 att155
|
|
||||||
att143 att23
|
|
||||||
att143 att203
|
|
||||||
att143 att71
|
|
||||||
att143 att83
|
|
||||||
att143 att11
|
|
||||||
att14 att98
|
|
||||||
att40 att160
|
|
||||||
att40 att4
|
|
||||||
att40 att196
|
|
||||||
att40 att88
|
|
||||||
att40 att52
|
|
||||||
att210 att162
|
|
||||||
att210 att42
|
|
||||||
att210 att114
|
|
||||||
att155 att23
|
|
||||||
att155 att203
|
|
||||||
att155 att107
|
|
||||||
att155 att11
|
|
||||||
att170 att158
|
|
||||||
att160 att52
|
|
||||||
att160 att124
|
|
||||||
att23 att203
|
|
||||||
att23 att107
|
|
||||||
att23 att71
|
|
||||||
att23 att11
|
|
||||||
att162 att138
|
|
||||||
att162 att18
|
|
||||||
att162 att150
|
|
||||||
att162 att90
|
|
||||||
att162 att102
|
|
||||||
att162 att174
|
|
||||||
att162 att66
|
|
||||||
att203 att107
|
|
||||||
att203 att49
|
|
||||||
att203 att59
|
|
||||||
att203 att47
|
|
||||||
att203 att191
|
|
||||||
att203 att119
|
|
||||||
att164 att62
|
|
||||||
att164 att42
|
|
||||||
att164 att128
|
|
||||||
att164 att171
|
|
||||||
att164 att92
|
|
||||||
att164 att163
|
|
||||||
att164 att158
|
|
||||||
att164 att176
|
|
||||||
att164 att145
|
|
||||||
att164 att172
|
|
||||||
att164 att58
|
|
||||||
att164 att68
|
|
||||||
att164 att80
|
|
||||||
att164 att32
|
|
||||||
att164 att98
|
|
||||||
att164 att156
|
|
||||||
att164 att110
|
|
||||||
att164 att205
|
|
||||||
att164 att21
|
|
||||||
att164 att134
|
|
||||||
att164 att213
|
|
||||||
att164 att112
|
|
||||||
att164 att38
|
|
||||||
att164 att189
|
|
||||||
att164 att56
|
|
||||||
att164 att44
|
|
||||||
att164 att152
|
|
||||||
att164 att8
|
|
||||||
att107 att83
|
|
||||||
att107 att49
|
|
||||||
att107 att59
|
|
||||||
att107 att47
|
|
||||||
att107 att191
|
|
||||||
att42 att138
|
|
||||||
att42 att54
|
|
||||||
att42 att114
|
|
||||||
att71 att83
|
|
||||||
att71 att167
|
|
||||||
att71 att35
|
|
||||||
att71 att179
|
|
||||||
att128 att92
|
|
||||||
att128 att112
|
|
||||||
att138 att18
|
|
||||||
att138 att150
|
|
||||||
att83 att167
|
|
||||||
att83 att35
|
|
||||||
att171 att87
|
|
||||||
att171 att159
|
|
||||||
att171 att63
|
|
||||||
att171 att51
|
|
||||||
att171 att39
|
|
||||||
att171 att75
|
|
||||||
att92 att163
|
|
||||||
att92 att145
|
|
||||||
att92 att56
|
|
||||||
att163 att49
|
|
||||||
att163 att175
|
|
||||||
att163 att87
|
|
||||||
att163 att79
|
|
||||||
att163 att151
|
|
||||||
att163 att139
|
|
||||||
att163 att187
|
|
||||||
att163 att127
|
|
||||||
att163 att103
|
|
||||||
att163 att91
|
|
||||||
att49 att37
|
|
||||||
att161 att173
|
|
||||||
att161 att113
|
|
||||||
att176 att145
|
|
||||||
att176 att172
|
|
||||||
att176 att68
|
|
||||||
att176 att80
|
|
||||||
att176 att32
|
|
||||||
att176 att175
|
|
||||||
att176 att98
|
|
||||||
att176 att110
|
|
||||||
att176 att205
|
|
||||||
att176 att21
|
|
||||||
att176 att134
|
|
||||||
att176 att213
|
|
||||||
att176 att56
|
|
||||||
att4 att196
|
|
||||||
att4 att88
|
|
||||||
att4 att136
|
|
||||||
att4 att100
|
|
||||||
att4 att148
|
|
||||||
att4 att208
|
|
||||||
att172 att112
|
|
||||||
att172 att184
|
|
||||||
att196 att88
|
|
||||||
att196 att136
|
|
||||||
att196 att100
|
|
||||||
att196 att148
|
|
||||||
att196 att208
|
|
||||||
att58 att142
|
|
||||||
att58 att46
|
|
||||||
att58 att34
|
|
||||||
att68 att32
|
|
||||||
att80 att38
|
|
||||||
att32 att110
|
|
||||||
att32 att21
|
|
||||||
att32 att44
|
|
||||||
att32 att200
|
|
||||||
att175 att87
|
|
||||||
att175 att159
|
|
||||||
att175 att79
|
|
||||||
att175 att187
|
|
||||||
att175 att115
|
|
||||||
att87 att159
|
|
||||||
att87 att63
|
|
||||||
att87 att51
|
|
||||||
att87 att75
|
|
||||||
att87 att15
|
|
||||||
att87 att99
|
|
||||||
att159 att75
|
|
||||||
att159 att15
|
|
||||||
att159 att195
|
|
||||||
att18 att90
|
|
||||||
att18 att102
|
|
||||||
att18 att78
|
|
||||||
att18 att198
|
|
||||||
att52 att124
|
|
||||||
att52 att64
|
|
||||||
att98 att86
|
|
||||||
att136 att100
|
|
||||||
att136 att208
|
|
||||||
att150 att90
|
|
||||||
att150 att174
|
|
||||||
att150 att66
|
|
||||||
att156 att205
|
|
||||||
att156 att96
|
|
||||||
att156 att216
|
|
||||||
att156 att204
|
|
||||||
att156 att24
|
|
||||||
att156 att84
|
|
||||||
att156 att36
|
|
||||||
att156 att12
|
|
||||||
att156 att108
|
|
||||||
att100 att148
|
|
||||||
att63 att51
|
|
||||||
att63 att39
|
|
||||||
att63 att3
|
|
||||||
att63 att183
|
|
||||||
att63 att147
|
|
||||||
att90 att102
|
|
||||||
att90 att78
|
|
||||||
att167 att35
|
|
||||||
att167 att179
|
|
||||||
att35 att179
|
|
||||||
att51 att39
|
|
||||||
att51 att3
|
|
||||||
att51 att183
|
|
||||||
att21 att134
|
|
||||||
att21 att213
|
|
||||||
att21 att38
|
|
||||||
att21 att189
|
|
||||||
att21 att129
|
|
||||||
att21 att81
|
|
||||||
att21 att153
|
|
||||||
att21 att117
|
|
||||||
att21 att9
|
|
||||||
att142 att46
|
|
||||||
att142 att130
|
|
||||||
att142 att118
|
|
||||||
att142 att70
|
|
||||||
att142 att10
|
|
||||||
att142 att202
|
|
||||||
att142 att190
|
|
||||||
att142 att106
|
|
||||||
att46 att130
|
|
||||||
att46 att118
|
|
||||||
att46 att70
|
|
||||||
att46 att34
|
|
||||||
att46 att166
|
|
||||||
att46 att82
|
|
||||||
att134 att2
|
|
||||||
att39 att3
|
|
||||||
att102 att78
|
|
||||||
att102 att174
|
|
||||||
att102 att54
|
|
||||||
att102 att198
|
|
||||||
att130 att118
|
|
||||||
att130 att10
|
|
||||||
att130 att202
|
|
||||||
att130 att190
|
|
||||||
att130 att106
|
|
||||||
att149 att125
|
|
||||||
att96 att216
|
|
||||||
att96 att204
|
|
||||||
att96 att24
|
|
||||||
att75 att15
|
|
||||||
att75 att99
|
|
||||||
att118 att70
|
|
||||||
att118 att10
|
|
||||||
att118 att202
|
|
||||||
att78 att198
|
|
||||||
att213 att189
|
|
||||||
att213 att129
|
|
||||||
att213 att69
|
|
||||||
att213 att81
|
|
||||||
att38 att50
|
|
||||||
att38 att26
|
|
||||||
att174 att54
|
|
||||||
att174 att66
|
|
||||||
att174 att30
|
|
||||||
att189 att86
|
|
||||||
att189 att129
|
|
||||||
att189 att69
|
|
||||||
att189 att81
|
|
||||||
att189 att153
|
|
||||||
att189 att117
|
|
||||||
att189 att9
|
|
||||||
att189 att45
|
|
||||||
att189 att141
|
|
||||||
att189 att105
|
|
||||||
att70 att34
|
|
||||||
att70 att154
|
|
||||||
att179 att59
|
|
||||||
att59 att47
|
|
||||||
att59 att191
|
|
||||||
att59 att119
|
|
||||||
att79 att86
|
|
||||||
att79 att151
|
|
||||||
att79 att139
|
|
||||||
att79 att187
|
|
||||||
att79 att127
|
|
||||||
att79 att103
|
|
||||||
att79 att43
|
|
||||||
att79 att193
|
|
||||||
att79 att91
|
|
||||||
att79 att19
|
|
||||||
att124 att64
|
|
||||||
att54 att114
|
|
||||||
att54 att30
|
|
||||||
att54 att6
|
|
||||||
att191 att119
|
|
||||||
att86 att194
|
|
||||||
att56 att44
|
|
||||||
att56 att152
|
|
||||||
att56 att50
|
|
||||||
att56 att188
|
|
||||||
att56 att26
|
|
||||||
att56 att200
|
|
||||||
att56 att104
|
|
||||||
att56 att140
|
|
||||||
att56 att146
|
|
||||||
att56 att194
|
|
||||||
att56 att8
|
|
||||||
att56 att2
|
|
||||||
att56 att133
|
|
||||||
att56 att1
|
|
||||||
att151 att139
|
|
||||||
att66 att30
|
|
||||||
att173 att125
|
|
||||||
att173 att113
|
|
||||||
att173 att185
|
|
||||||
att44 att152
|
|
||||||
att44 att50
|
|
||||||
att44 att188
|
|
||||||
att44 att200
|
|
||||||
att44 att104
|
|
||||||
att44 att140
|
|
||||||
att44 att194
|
|
||||||
att44 att212
|
|
||||||
att44 att1
|
|
||||||
att139 att26
|
|
||||||
att139 att99
|
|
||||||
att139 att103
|
|
||||||
att139 att43
|
|
||||||
att139 att91
|
|
||||||
att139 att31
|
|
||||||
att139 att199
|
|
||||||
att139 att7
|
|
||||||
att216 att204
|
|
||||||
att216 att24
|
|
||||||
att216 att84
|
|
||||||
att216 att36
|
|
||||||
att216 att12
|
|
||||||
att216 att180
|
|
||||||
att216 att108
|
|
||||||
att129 att69
|
|
||||||
att152 att188
|
|
||||||
att152 att140
|
|
||||||
att69 att153
|
|
||||||
att69 att9
|
|
||||||
att69 att177
|
|
||||||
att81 att45
|
|
||||||
att81 att105
|
|
||||||
att153 att117
|
|
||||||
att153 att141
|
|
||||||
att41 att53
|
|
||||||
att204 att12
|
|
||||||
att204 att180
|
|
||||||
att188 att146
|
|
||||||
att188 att212
|
|
||||||
att13 att157
|
|
||||||
att114 att6
|
|
||||||
att114 att186
|
|
||||||
att10 att190
|
|
||||||
att64 att184
|
|
||||||
att200 att104
|
|
||||||
att9 att45
|
|
||||||
att9 att146
|
|
||||||
att9 att141
|
|
||||||
att9 att177
|
|
||||||
att9 att37
|
|
||||||
att9 att133
|
|
||||||
att9 att109
|
|
||||||
att9 att181
|
|
||||||
att3 att183
|
|
||||||
att3 att147
|
|
||||||
att3 att123
|
|
||||||
att3 att135
|
|
||||||
att3 att111
|
|
||||||
att45 att105
|
|
||||||
att45 att177
|
|
||||||
att45 att93
|
|
||||||
att45 att201
|
|
||||||
att45 att165
|
|
||||||
att45 att193
|
|
||||||
att45 att33
|
|
||||||
att45 att37
|
|
||||||
att45 att133
|
|
||||||
att45 att97
|
|
||||||
att140 att8
|
|
||||||
att30 att6
|
|
||||||
att30 att186
|
|
||||||
att183 att147
|
|
||||||
att183 att123
|
|
||||||
att183 att135
|
|
||||||
att146 att2
|
|
||||||
att202 att166
|
|
||||||
att202 att106
|
|
||||||
att202 att82
|
|
||||||
att24 att84
|
|
||||||
att24 att36
|
|
||||||
att24 att132
|
|
||||||
att147 att123
|
|
||||||
att147 att135
|
|
||||||
att147 att111
|
|
||||||
att147 att207
|
|
||||||
att8 att212
|
|
||||||
att166 att82
|
|
||||||
att166 att22
|
|
||||||
att166 att94
|
|
||||||
att187 att127
|
|
||||||
att187 att115
|
|
||||||
att127 att115
|
|
||||||
att105 att184
|
|
||||||
att105 att93
|
|
||||||
att105 att201
|
|
||||||
att106 att154
|
|
||||||
att82 att154
|
|
||||||
att82 att22
|
|
||||||
att135 att111
|
|
||||||
att135 att207
|
|
||||||
att154 att22
|
|
||||||
att154 att94
|
|
||||||
att111 att207
|
|
||||||
att99 att195
|
|
||||||
att22 att94
|
|
||||||
att84 att48
|
|
||||||
att177 att93
|
|
||||||
att177 att165
|
|
||||||
att177 att181
|
|
||||||
att103 att195
|
|
||||||
att103 att97
|
|
||||||
att103 att109
|
|
||||||
att93 att201
|
|
||||||
att93 att165
|
|
||||||
att93 att193
|
|
||||||
att93 att33
|
|
||||||
att93 att57
|
|
||||||
att201 att33
|
|
||||||
att201 att57
|
|
||||||
att43 att31
|
|
||||||
att36 att180
|
|
||||||
att36 att48
|
|
||||||
att36 att72
|
|
||||||
att36 att132
|
|
||||||
att36 att144
|
|
||||||
att125 att113
|
|
||||||
att125 att185
|
|
||||||
att125 att65
|
|
||||||
att125 att29
|
|
||||||
att180 att48
|
|
||||||
att180 att72
|
|
||||||
att180 att192
|
|
||||||
att180 att108
|
|
||||||
att48 att72
|
|
||||||
att6 att186
|
|
||||||
att113 att185
|
|
||||||
att113 att53
|
|
||||||
att113 att65
|
|
||||||
att193 att97
|
|
||||||
att91 att31
|
|
||||||
att91 att199
|
|
||||||
att91 att19
|
|
||||||
att72 att132
|
|
||||||
att72 att144
|
|
||||||
att72 att192
|
|
||||||
att72 att120
|
|
||||||
att31 att199
|
|
||||||
att31 att7
|
|
||||||
att31 att67
|
|
||||||
att31 att55
|
|
||||||
att31 att1
|
|
||||||
att132 att144
|
|
||||||
att132 att120
|
|
||||||
att33 att57
|
|
||||||
att144 att192
|
|
||||||
att144 att120
|
|
||||||
att185 att53
|
|
||||||
att185 att65
|
|
||||||
att185 att29
|
|
||||||
att199 att19
|
|
||||||
att199 att7
|
|
||||||
att199 att67
|
|
||||||
att199 att55
|
|
||||||
att199 att109
|
|
||||||
att65 att29
|
|
||||||
att7 att67
|
|
||||||
att67 att55
|
|
||||||
att109 att181
|
|
||||||
|
|
@@ -1,859 +0,0 @@
|
|||||||
class att215
|
|
||||||
class att25
|
|
||||||
class att131
|
|
||||||
class att95
|
|
||||||
class att122
|
|
||||||
class att17
|
|
||||||
class att28
|
|
||||||
class att5
|
|
||||||
class att121
|
|
||||||
class att214
|
|
||||||
class att197
|
|
||||||
class att116
|
|
||||||
class att182
|
|
||||||
class att60
|
|
||||||
class att168
|
|
||||||
class att178
|
|
||||||
class att206
|
|
||||||
class att89
|
|
||||||
class att77
|
|
||||||
class att209
|
|
||||||
class att73
|
|
||||||
class att126
|
|
||||||
class att16
|
|
||||||
class att74
|
|
||||||
class att27
|
|
||||||
class att61
|
|
||||||
class att20
|
|
||||||
class att101
|
|
||||||
class att85
|
|
||||||
class att76
|
|
||||||
class att137
|
|
||||||
class att211
|
|
||||||
class att143
|
|
||||||
class att14
|
|
||||||
class att40
|
|
||||||
class att210
|
|
||||||
class att155
|
|
||||||
class att170
|
|
||||||
class att160
|
|
||||||
class att23
|
|
||||||
class att162
|
|
||||||
class att203
|
|
||||||
class att164
|
|
||||||
class att107
|
|
||||||
class att62
|
|
||||||
class att42
|
|
||||||
class att71
|
|
||||||
class att128
|
|
||||||
class att138
|
|
||||||
class att83
|
|
||||||
class att171
|
|
||||||
class att92
|
|
||||||
class att163
|
|
||||||
class att49
|
|
||||||
class att161
|
|
||||||
class att158
|
|
||||||
class att176
|
|
||||||
class att11
|
|
||||||
class att145
|
|
||||||
class att4
|
|
||||||
class att172
|
|
||||||
class att196
|
|
||||||
class att58
|
|
||||||
class att68
|
|
||||||
class att169
|
|
||||||
class att80
|
|
||||||
class att32
|
|
||||||
class att175
|
|
||||||
class att87
|
|
||||||
class att88
|
|
||||||
class att159
|
|
||||||
class att18
|
|
||||||
class att52
|
|
||||||
class att98
|
|
||||||
class att136
|
|
||||||
class att150
|
|
||||||
class att156
|
|
||||||
class att110
|
|
||||||
class att100
|
|
||||||
class att63
|
|
||||||
class att148
|
|
||||||
class att90
|
|
||||||
class att167
|
|
||||||
class att35
|
|
||||||
class att205
|
|
||||||
class att51
|
|
||||||
class att21
|
|
||||||
class att142
|
|
||||||
class att46
|
|
||||||
class att134
|
|
||||||
class att39
|
|
||||||
class att102
|
|
||||||
class att208
|
|
||||||
class att130
|
|
||||||
class att149
|
|
||||||
class att96
|
|
||||||
class att75
|
|
||||||
class att118
|
|
||||||
class att78
|
|
||||||
class att213
|
|
||||||
class att112
|
|
||||||
class att38
|
|
||||||
class att174
|
|
||||||
class att189
|
|
||||||
class att70
|
|
||||||
class att179
|
|
||||||
class att59
|
|
||||||
class att79
|
|
||||||
class att15
|
|
||||||
class att47
|
|
||||||
class att124
|
|
||||||
class att34
|
|
||||||
class att54
|
|
||||||
class att191
|
|
||||||
class att86
|
|
||||||
class att56
|
|
||||||
class att151
|
|
||||||
class att66
|
|
||||||
class att173
|
|
||||||
class att44
|
|
||||||
class att198
|
|
||||||
class att139
|
|
||||||
class att216
|
|
||||||
class att129
|
|
||||||
class att152
|
|
||||||
class att69
|
|
||||||
class att81
|
|
||||||
class att50
|
|
||||||
class att153
|
|
||||||
class att41
|
|
||||||
class att204
|
|
||||||
class att188
|
|
||||||
class att26
|
|
||||||
class att13
|
|
||||||
class att117
|
|
||||||
class att114
|
|
||||||
class att10
|
|
||||||
class att64
|
|
||||||
class att200
|
|
||||||
class att9
|
|
||||||
class att3
|
|
||||||
class att119
|
|
||||||
class att45
|
|
||||||
class att104
|
|
||||||
class att140
|
|
||||||
class att30
|
|
||||||
class att183
|
|
||||||
class att146
|
|
||||||
class att141
|
|
||||||
class att202
|
|
||||||
class att194
|
|
||||||
class att24
|
|
||||||
class att147
|
|
||||||
class att8
|
|
||||||
class att212
|
|
||||||
class att123
|
|
||||||
class att166
|
|
||||||
class att187
|
|
||||||
class att127
|
|
||||||
class att190
|
|
||||||
class att105
|
|
||||||
class att106
|
|
||||||
class att184
|
|
||||||
class att82
|
|
||||||
class att2
|
|
||||||
class att135
|
|
||||||
class att154
|
|
||||||
class att111
|
|
||||||
class att115
|
|
||||||
class att99
|
|
||||||
class att22
|
|
||||||
class att84
|
|
||||||
class att207
|
|
||||||
class att94
|
|
||||||
class att177
|
|
||||||
class att103
|
|
||||||
class att93
|
|
||||||
class att201
|
|
||||||
class att43
|
|
||||||
class att36
|
|
||||||
class att12
|
|
||||||
class att125
|
|
||||||
class att165
|
|
||||||
class att180
|
|
||||||
class att195
|
|
||||||
class att157
|
|
||||||
class att48
|
|
||||||
class att6
|
|
||||||
class att113
|
|
||||||
class att193
|
|
||||||
class att91
|
|
||||||
class att72
|
|
||||||
class att31
|
|
||||||
class att132
|
|
||||||
class att33
|
|
||||||
class att57
|
|
||||||
class att144
|
|
||||||
class att192
|
|
||||||
class att185
|
|
||||||
class att37
|
|
||||||
class att53
|
|
||||||
class att120
|
|
||||||
class att186
|
|
||||||
class att199
|
|
||||||
class att65
|
|
||||||
class att108
|
|
||||||
class att133
|
|
||||||
class att29
|
|
||||||
class att19
|
|
||||||
class att7
|
|
||||||
class att97
|
|
||||||
class att67
|
|
||||||
class att55
|
|
||||||
class att1
|
|
||||||
class att109
|
|
||||||
class att181
|
|
||||||
att215 att25
|
|
||||||
att215 att131
|
|
||||||
att215 att95
|
|
||||||
att215 att17
|
|
||||||
att215 att214
|
|
||||||
att215 att143
|
|
||||||
att25 att131
|
|
||||||
att25 att95
|
|
||||||
att25 att122
|
|
||||||
att25 att121
|
|
||||||
att25 att73
|
|
||||||
att25 att61
|
|
||||||
att25 att85
|
|
||||||
att25 att169
|
|
||||||
att25 att13
|
|
||||||
att25 att157
|
|
||||||
att131 att95
|
|
||||||
att131 att122
|
|
||||||
att131 att17
|
|
||||||
att131 att28
|
|
||||||
att131 att5
|
|
||||||
att131 att121
|
|
||||||
att131 att214
|
|
||||||
att131 att116
|
|
||||||
att131 att182
|
|
||||||
att131 att60
|
|
||||||
att131 att126
|
|
||||||
att131 att16
|
|
||||||
att131 att27
|
|
||||||
att131 att20
|
|
||||||
att131 att143
|
|
||||||
att131 att155
|
|
||||||
att95 att122
|
|
||||||
att95 att17
|
|
||||||
att95 att28
|
|
||||||
att95 att5
|
|
||||||
att95 att121
|
|
||||||
att95 att214
|
|
||||||
att95 att197
|
|
||||||
att95 att116
|
|
||||||
att95 att60
|
|
||||||
att95 att168
|
|
||||||
att95 att178
|
|
||||||
att95 att143
|
|
||||||
att95 att155
|
|
||||||
att95 att23
|
|
||||||
att95 att71
|
|
||||||
att95 att167
|
|
||||||
att122 att28
|
|
||||||
att122 att182
|
|
||||||
att122 att170
|
|
||||||
att17 att5
|
|
||||||
att17 att197
|
|
||||||
att17 att89
|
|
||||||
att17 att77
|
|
||||||
att17 att209
|
|
||||||
att17 att137
|
|
||||||
att17 att161
|
|
||||||
att17 att41
|
|
||||||
att28 att206
|
|
||||||
att28 att16
|
|
||||||
att28 att76
|
|
||||||
att28 att40
|
|
||||||
att28 att210
|
|
||||||
att28 att160
|
|
||||||
att28 att172
|
|
||||||
att28 att124
|
|
||||||
att28 att64
|
|
||||||
att5 att197
|
|
||||||
att5 att89
|
|
||||||
att5 att77
|
|
||||||
att5 att209
|
|
||||||
att5 att101
|
|
||||||
att121 att73
|
|
||||||
att121 att61
|
|
||||||
att214 att116
|
|
||||||
att214 att178
|
|
||||||
att214 att206
|
|
||||||
att214 att58
|
|
||||||
att214 att142
|
|
||||||
att214 att46
|
|
||||||
att197 att89
|
|
||||||
att197 att209
|
|
||||||
att197 att101
|
|
||||||
att116 att182
|
|
||||||
att116 att60
|
|
||||||
att116 att168
|
|
||||||
att116 att178
|
|
||||||
att116 att206
|
|
||||||
att116 att73
|
|
||||||
att116 att126
|
|
||||||
att116 att16
|
|
||||||
att116 att74
|
|
||||||
att116 att27
|
|
||||||
att116 att20
|
|
||||||
att116 att211
|
|
||||||
att116 att164
|
|
||||||
att116 att128
|
|
||||||
att116 att92
|
|
||||||
att116 att176
|
|
||||||
att116 att68
|
|
||||||
att182 att27
|
|
||||||
att182 att14
|
|
||||||
att60 att168
|
|
||||||
att60 att156
|
|
||||||
att60 att96
|
|
||||||
att168 att126
|
|
||||||
att168 att156
|
|
||||||
att168 att96
|
|
||||||
att168 att216
|
|
||||||
att178 att20
|
|
||||||
att178 att211
|
|
||||||
att178 att58
|
|
||||||
att178 att142
|
|
||||||
att178 att130
|
|
||||||
att178 att166
|
|
||||||
att206 att74
|
|
||||||
att206 att170
|
|
||||||
att206 att158
|
|
||||||
att89 att77
|
|
||||||
att89 att137
|
|
||||||
att89 att149
|
|
||||||
att89 att173
|
|
||||||
att77 att137
|
|
||||||
att77 att161
|
|
||||||
att77 att149
|
|
||||||
att209 att101
|
|
||||||
att209 att41
|
|
||||||
att73 att61
|
|
||||||
att73 att85
|
|
||||||
att73 att13
|
|
||||||
att73 att157
|
|
||||||
att126 att162
|
|
||||||
att126 att138
|
|
||||||
att126 att18
|
|
||||||
att126 att150
|
|
||||||
att16 att74
|
|
||||||
att16 att76
|
|
||||||
att16 att40
|
|
||||||
att16 att4
|
|
||||||
att16 att196
|
|
||||||
att16 att136
|
|
||||||
att74 att14
|
|
||||||
att74 att62
|
|
||||||
att27 att171
|
|
||||||
att27 att63
|
|
||||||
att61 att85
|
|
||||||
att61 att169
|
|
||||||
att20 att76
|
|
||||||
att20 att211
|
|
||||||
att20 att210
|
|
||||||
att20 att170
|
|
||||||
att20 att164
|
|
||||||
att20 att128
|
|
||||||
att20 att176
|
|
||||||
att20 att80
|
|
||||||
att101 att41
|
|
||||||
att85 att169
|
|
||||||
att85 att13
|
|
||||||
att76 att14
|
|
||||||
att76 att40
|
|
||||||
att76 att160
|
|
||||||
att76 att4
|
|
||||||
att76 att52
|
|
||||||
att137 att161
|
|
||||||
att137 att149
|
|
||||||
att137 att173
|
|
||||||
att137 att125
|
|
||||||
att211 att210
|
|
||||||
att211 att162
|
|
||||||
att211 att164
|
|
||||||
att211 att62
|
|
||||||
att211 att42
|
|
||||||
att211 att171
|
|
||||||
att211 att163
|
|
||||||
att211 att175
|
|
||||||
att211 att79
|
|
||||||
att211 att151
|
|
||||||
att211 att43
|
|
||||||
att143 att155
|
|
||||||
att143 att23
|
|
||||||
att143 att203
|
|
||||||
att143 att71
|
|
||||||
att143 att83
|
|
||||||
att143 att11
|
|
||||||
att14 att98
|
|
||||||
att40 att160
|
|
||||||
att40 att4
|
|
||||||
att40 att196
|
|
||||||
att40 att88
|
|
||||||
att40 att52
|
|
||||||
att210 att162
|
|
||||||
att210 att42
|
|
||||||
att210 att114
|
|
||||||
att155 att23
|
|
||||||
att155 att203
|
|
||||||
att155 att107
|
|
||||||
att155 att11
|
|
||||||
att170 att158
|
|
||||||
att160 att52
|
|
||||||
att160 att124
|
|
||||||
att23 att203
|
|
||||||
att23 att107
|
|
||||||
att23 att71
|
|
||||||
att23 att11
|
|
||||||
att162 att138
|
|
||||||
att162 att18
|
|
||||||
att162 att150
|
|
||||||
att162 att90
|
|
||||||
att162 att102
|
|
||||||
att162 att174
|
|
||||||
att162 att66
|
|
||||||
att203 att107
|
|
||||||
att203 att49
|
|
||||||
att203 att59
|
|
||||||
att203 att47
|
|
||||||
att203 att191
|
|
||||||
att203 att119
|
|
||||||
att164 att62
|
|
||||||
att164 att42
|
|
||||||
att164 att128
|
|
||||||
att164 att171
|
|
||||||
att164 att92
|
|
||||||
att164 att163
|
|
||||||
att164 att158
|
|
||||||
att164 att176
|
|
||||||
att164 att145
|
|
||||||
att164 att172
|
|
||||||
att164 att58
|
|
||||||
att164 att68
|
|
||||||
att164 att80
|
|
||||||
att164 att32
|
|
||||||
att164 att98
|
|
||||||
att164 att156
|
|
||||||
att164 att110
|
|
||||||
att164 att205
|
|
||||||
att164 att21
|
|
||||||
att164 att134
|
|
||||||
att164 att213
|
|
||||||
att164 att112
|
|
||||||
att164 att38
|
|
||||||
att164 att189
|
|
||||||
att164 att56
|
|
||||||
att164 att44
|
|
||||||
att164 att152
|
|
||||||
att164 att8
|
|
||||||
att107 att83
|
|
||||||
att107 att49
|
|
||||||
att107 att59
|
|
||||||
att107 att47
|
|
||||||
att107 att191
|
|
||||||
att42 att138
|
|
||||||
att42 att54
|
|
||||||
att42 att114
|
|
||||||
att71 att83
|
|
||||||
att71 att167
|
|
||||||
att71 att35
|
|
||||||
att71 att179
|
|
||||||
att128 att92
|
|
||||||
att128 att112
|
|
||||||
att138 att18
|
|
||||||
att138 att150
|
|
||||||
att83 att167
|
|
||||||
att83 att35
|
|
||||||
att171 att87
|
|
||||||
att171 att159
|
|
||||||
att171 att63
|
|
||||||
att171 att51
|
|
||||||
att171 att39
|
|
||||||
att171 att75
|
|
||||||
att92 att163
|
|
||||||
att92 att145
|
|
||||||
att92 att56
|
|
||||||
att163 att49
|
|
||||||
att163 att175
|
|
||||||
att163 att87
|
|
||||||
att163 att79
|
|
||||||
att163 att151
|
|
||||||
att163 att139
|
|
||||||
att163 att187
|
|
||||||
att163 att127
|
|
||||||
att163 att103
|
|
||||||
att163 att91
|
|
||||||
att49 att37
|
|
||||||
att161 att173
|
|
||||||
att161 att113
|
|
||||||
att176 att145
|
|
||||||
att176 att172
|
|
||||||
att176 att68
|
|
||||||
att176 att80
|
|
||||||
att176 att32
|
|
||||||
att176 att175
|
|
||||||
att176 att98
|
|
||||||
att176 att110
|
|
||||||
att176 att205
|
|
||||||
att176 att21
|
|
||||||
att176 att134
|
|
||||||
att176 att213
|
|
||||||
att176 att56
|
|
||||||
att4 att196
|
|
||||||
att4 att88
|
|
||||||
att4 att136
|
|
||||||
att4 att100
|
|
||||||
att4 att148
|
|
||||||
att4 att208
|
|
||||||
att172 att112
|
|
||||||
att172 att184
|
|
||||||
att196 att88
|
|
||||||
att196 att136
|
|
||||||
att196 att100
|
|
||||||
att196 att148
|
|
||||||
att196 att208
|
|
||||||
att58 att142
|
|
||||||
att58 att46
|
|
||||||
att58 att34
|
|
||||||
att68 att32
|
|
||||||
att80 att38
|
|
||||||
att32 att110
|
|
||||||
att32 att21
|
|
||||||
att32 att44
|
|
||||||
att32 att200
|
|
||||||
att175 att87
|
|
||||||
att175 att159
|
|
||||||
att175 att79
|
|
||||||
att175 att187
|
|
||||||
att175 att115
|
|
||||||
att87 att159
|
|
||||||
att87 att63
|
|
||||||
att87 att51
|
|
||||||
att87 att75
|
|
||||||
att87 att15
|
|
||||||
att87 att99
|
|
||||||
att159 att75
|
|
||||||
att159 att15
|
|
||||||
att159 att195
|
|
||||||
att18 att90
|
|
||||||
att18 att102
|
|
||||||
att18 att78
|
|
||||||
att18 att198
|
|
||||||
att52 att124
|
|
||||||
att52 att64
|
|
||||||
att98 att86
|
|
||||||
att136 att100
|
|
||||||
att136 att208
|
|
||||||
att150 att90
|
|
||||||
att150 att174
|
|
||||||
att150 att66
|
|
||||||
att156 att205
|
|
||||||
att156 att96
|
|
||||||
att156 att216
|
|
||||||
att156 att204
|
|
||||||
att156 att24
|
|
||||||
att156 att84
|
|
||||||
att156 att36
|
|
||||||
att156 att12
|
|
||||||
att156 att108
|
|
||||||
att100 att148
|
|
||||||
att63 att51
|
|
||||||
att63 att39
|
|
||||||
att63 att3
|
|
||||||
att63 att183
|
|
||||||
att63 att147
|
|
||||||
att90 att102
|
|
||||||
att90 att78
|
|
||||||
att167 att35
|
|
||||||
att167 att179
|
|
||||||
att35 att179
|
|
||||||
att51 att39
|
|
||||||
att51 att3
|
|
||||||
att51 att183
|
|
||||||
att21 att134
|
|
||||||
att21 att213
|
|
||||||
att21 att38
|
|
||||||
att21 att189
|
|
||||||
att21 att129
|
|
||||||
att21 att81
|
|
||||||
att21 att153
|
|
||||||
att21 att117
|
|
||||||
att21 att9
|
|
||||||
att142 att46
|
|
||||||
att142 att130
|
|
||||||
att142 att118
|
|
||||||
att142 att70
|
|
||||||
att142 att10
|
|
||||||
att142 att202
|
|
||||||
att142 att190
|
|
||||||
att142 att106
|
|
||||||
att46 att130
|
|
||||||
att46 att118
|
|
||||||
att46 att70
|
|
||||||
att46 att34
|
|
||||||
att46 att166
|
|
||||||
att46 att82
|
|
||||||
att134 att2
|
|
||||||
att39 att3
|
|
||||||
att102 att78
|
|
||||||
att102 att174
|
|
||||||
att102 att54
|
|
||||||
att102 att198
|
|
||||||
att130 att118
|
|
||||||
att130 att10
|
|
||||||
att130 att202
|
|
||||||
att130 att190
|
|
||||||
att130 att106
|
|
||||||
att149 att125
|
|
||||||
att96 att216
|
|
||||||
att96 att204
|
|
||||||
att96 att24
|
|
||||||
att75 att15
|
|
||||||
att75 att99
|
|
||||||
att118 att70
|
|
||||||
att118 att10
|
|
||||||
att118 att202
|
|
||||||
att78 att198
|
|
||||||
att213 att189
|
|
||||||
att213 att129
|
|
||||||
att213 att69
|
|
||||||
att213 att81
|
|
||||||
att38 att50
|
|
||||||
att38 att26
|
|
||||||
att174 att54
|
|
||||||
att174 att66
|
|
||||||
att174 att30
|
|
||||||
att189 att86
|
|
||||||
att189 att129
|
|
||||||
att189 att69
|
|
||||||
att189 att81
|
|
||||||
att189 att153
|
|
||||||
att189 att117
|
|
||||||
att189 att9
|
|
||||||
att189 att45
|
|
||||||
att189 att141
|
|
||||||
att189 att105
|
|
||||||
att70 att34
|
|
||||||
att70 att154
|
|
||||||
att179 att59
|
|
||||||
att59 att47
|
|
||||||
att59 att191
|
|
||||||
att59 att119
|
|
||||||
att79 att86
|
|
||||||
att79 att151
|
|
||||||
att79 att139
|
|
||||||
att79 att187
|
|
||||||
att79 att127
|
|
||||||
att79 att103
|
|
||||||
att79 att43
|
|
||||||
att79 att193
|
|
||||||
att79 att91
|
|
||||||
att79 att19
|
|
||||||
att124 att64
|
|
||||||
att54 att114
|
|
||||||
att54 att30
|
|
||||||
att54 att6
|
|
||||||
att191 att119
|
|
||||||
att86 att194
|
|
||||||
att56 att44
|
|
||||||
att56 att152
|
|
||||||
att56 att50
|
|
||||||
att56 att188
|
|
||||||
att56 att26
|
|
||||||
att56 att200
|
|
||||||
att56 att104
|
|
||||||
att56 att140
|
|
||||||
att56 att146
|
|
||||||
att56 att194
|
|
||||||
att56 att8
|
|
||||||
att56 att2
|
|
||||||
att56 att133
|
|
||||||
att56 att1
|
|
||||||
att151 att139
|
|
||||||
att66 att30
|
|
||||||
att173 att125
|
|
||||||
att173 att113
|
|
||||||
att173 att185
|
|
||||||
att44 att152
|
|
||||||
att44 att50
|
|
||||||
att44 att188
|
|
||||||
att44 att200
|
|
||||||
att44 att104
|
|
||||||
att44 att140
|
|
||||||
att44 att194
|
|
||||||
att44 att212
|
|
||||||
att44 att1
|
|
||||||
att139 att26
|
|
||||||
att139 att99
|
|
||||||
att139 att103
|
|
||||||
att139 att43
|
|
||||||
att139 att91
|
|
||||||
att139 att31
|
|
||||||
att139 att199
|
|
||||||
att139 att7
|
|
||||||
att216 att204
|
|
||||||
att216 att24
|
|
||||||
att216 att84
|
|
||||||
att216 att36
|
|
||||||
att216 att12
|
|
||||||
att216 att180
|
|
||||||
att216 att108
|
|
||||||
att129 att69
|
|
||||||
att152 att188
|
|
||||||
att152 att140
|
|
||||||
att69 att153
|
|
||||||
att69 att9
|
|
||||||
att69 att177
|
|
||||||
att81 att45
|
|
||||||
att81 att105
|
|
||||||
att153 att117
|
|
||||||
att153 att141
|
|
||||||
att41 att53
|
|
||||||
att204 att12
|
|
||||||
att204 att180
|
|
||||||
att188 att146
|
|
||||||
att188 att212
|
|
||||||
att13 att157
|
|
||||||
att114 att6
|
|
||||||
att114 att186
|
|
||||||
att10 att190
|
|
||||||
att64 att184
|
|
||||||
att200 att104
|
|
||||||
att9 att45
|
|
||||||
att9 att146
|
|
||||||
att9 att141
|
|
||||||
att9 att177
|
|
||||||
att9 att37
|
|
||||||
att9 att133
|
|
||||||
att9 att109
|
|
||||||
att9 att181
|
|
||||||
att3 att183
|
|
||||||
att3 att147
|
|
||||||
att3 att123
|
|
||||||
att3 att135
|
|
||||||
att3 att111
|
|
||||||
att45 att105
|
|
||||||
att45 att177
|
|
||||||
att45 att93
|
|
||||||
att45 att201
|
|
||||||
att45 att165
|
|
||||||
att45 att193
|
|
||||||
att45 att33
|
|
||||||
att45 att37
|
|
||||||
att45 att133
|
|
||||||
att45 att97
|
|
||||||
att140 att8
|
|
||||||
att30 att6
|
|
||||||
att30 att186
|
|
||||||
att183 att147
|
|
||||||
att183 att123
|
|
||||||
att183 att135
|
|
||||||
att146 att2
|
|
||||||
att202 att166
|
|
||||||
att202 att106
|
|
||||||
att202 att82
|
|
||||||
att24 att84
|
|
||||||
att24 att36
|
|
||||||
att24 att132
|
|
||||||
att147 att123
|
|
||||||
att147 att135
|
|
||||||
att147 att111
|
|
||||||
att147 att207
|
|
||||||
att8 att212
|
|
||||||
att166 att82
|
|
||||||
att166 att22
|
|
||||||
att166 att94
|
|
||||||
att187 att127
|
|
||||||
att187 att115
|
|
||||||
att127 att115
|
|
||||||
att105 att184
|
|
||||||
att105 att93
|
|
||||||
att105 att201
|
|
||||||
att106 att154
|
|
||||||
att82 att154
|
|
||||||
att82 att22
|
|
||||||
att135 att111
|
|
||||||
att135 att207
|
|
||||||
att154 att22
|
|
||||||
att154 att94
|
|
||||||
att111 att207
|
|
||||||
att99 att195
|
|
||||||
att22 att94
|
|
||||||
att84 att48
|
|
||||||
att177 att93
|
|
||||||
att177 att165
|
|
||||||
att177 att181
|
|
||||||
att103 att195
|
|
||||||
att103 att97
|
|
||||||
att103 att109
|
|
||||||
att93 att201
|
|
||||||
att93 att165
|
|
||||||
att93 att193
|
|
||||||
att93 att33
|
|
||||||
att93 att57
|
|
||||||
att201 att33
|
|
||||||
att201 att57
|
|
||||||
att43 att31
|
|
||||||
att36 att180
|
|
||||||
att36 att48
|
|
||||||
att36 att72
|
|
||||||
att36 att132
|
|
||||||
att36 att144
|
|
||||||
att125 att113
|
|
||||||
att125 att185
|
|
||||||
att125 att65
|
|
||||||
att125 att29
|
|
||||||
att180 att48
|
|
||||||
att180 att72
|
|
||||||
att180 att192
|
|
||||||
att180 att108
|
|
||||||
att48 att72
|
|
||||||
att6 att186
|
|
||||||
att113 att185
|
|
||||||
att113 att53
|
|
||||||
att113 att65
|
|
||||||
att193 att97
|
|
||||||
att91 att31
|
|
||||||
att91 att199
|
|
||||||
att91 att19
|
|
||||||
att72 att132
|
|
||||||
att72 att144
|
|
||||||
att72 att192
|
|
||||||
att72 att120
|
|
||||||
att31 att199
|
|
||||||
att31 att7
|
|
||||||
att31 att67
|
|
||||||
att31 att55
|
|
||||||
att31 att1
|
|
||||||
att132 att144
|
|
||||||
att132 att120
|
|
||||||
att33 att57
|
|
||||||
att144 att192
|
|
||||||
att144 att120
|
|
||||||
att185 att53
|
|
||||||
att185 att65
|
|
||||||
att185 att29
|
|
||||||
att199 att19
|
|
||||||
att199 att7
|
|
||||||
att199 att67
|
|
||||||
att199 att55
|
|
||||||
att199 att109
|
|
||||||
att65 att29
|
|
||||||
att7 att67
|
|
||||||
att67 att55
|
|
||||||
att109 att181
|
|
||||||
|
|
BIN
diagrams/BayesNet.pdf
Executable file
BIN
diagrams/BayesNet.pdf
Executable file
Binary file not shown.
71
docs/BoostAODE.md
Normal file
71
docs/BoostAODE.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# BoostAODE Algorithm Operation
|
||||||
|
|
||||||
|
The algorithm is based on the AdaBoost algorithm with some new proposals that can be activated using the following hyperparameters.
|
||||||
|
|
||||||
|
## Hyperparameters
|
||||||
|
|
||||||
|
The hyperparameters defined in the algorithm are:
|
||||||
|
|
||||||
|
- ***repeatSparent*** (*boolean*): Allows dataset variables to be repeated as parents of an *SPODE*. Default value: *false*.
|
||||||
|
|
||||||
|
- ***maxModels*** (*int*): Maximum number of models (*SPODEs*) to build. This hyperparameter is only taken into account if ***repeatSparent*** is set to *true*. Default value: *0*.
|
||||||
|
|
||||||
|
- ***order*** (*{"asc", "desc", "rand"}*): Sets the order (ascending/descending/random) in which dataset variables will be processed to choose the parents of the *SPODEs*. Default value: *"desc"*.
|
||||||
|
|
||||||
|
- ***convergence*** (*boolean*): Sets whether the convergence of the result will be used as a termination condition. If this hyperparameter is set to true, the training dataset passed to the model is divided into two sets, one serving as training data and the other as a test set (so the original test partition will become a validation partition in this case). The partition is made by taking the first partition generated by a process of generating a 5 fold partition with stratification using a predetermined seed. The exit condition used in this *convergence* is that the difference between the accuracy obtained by the current model and that obtained by the previous model is greater than *1e-4*; otherwise, one will be added to the number of models that worsen the result (see next hyperparameter). Default value: *false*.
|
||||||
|
|
||||||
|
- ***tolerance*** (*int*): Sets the maximum number of models that can worsen the result without constituting a termination condition. Default value: *0*.
|
||||||
|
|
||||||
|
- ***select_features*** (*{"IWSS", "FCBF", "CFS", ""}*): Selects the variable selection method to be used to build initial models for the ensemble that will be included without considering any of the other exit conditions. Once the models of the selected variables are built, the algorithm will update the weights using the ensemble and set the significance of all the models built with the same α<sub>t</sub>. Default value: *""*.
|
||||||
|
|
||||||
|
- ***threshold*** (*double*): Sets the necessary value for the IWSS and FCBF algorithms to function. Accepted values are:
|
||||||
|
- IWSS: $threshold \in [0, 0.5]$
|
||||||
|
- FCBF: $threshold \in [10^{-7}, 1]$
|
||||||
|
|
||||||
|
Default value is *-1* so every time any of those algorithms are called, the threshold has to be set to the desired value.
|
||||||
|
|
||||||
|
- ***predict_voting*** (*boolean*): Sets whether the algorithm will use *model voting* to predict the result. If set to false, the weighted average of the probabilities of each model's prediction will be used. Default value: *true*.
|
||||||
|
|
||||||
|
- ***predict_single*** (*boolean*): Sets whether the algorithm will use single-model prediction in the learning process. If set to *false*, all models trained up to that point will be used to calculate the prediction necessary to update the weights in the learning process. Default value: *true*.
|
||||||
|
|
||||||
|
## Operation
|
||||||
|
|
||||||
|
The algorithm performs the following steps:
|
||||||
|
|
||||||
|
1. **Initialization**
|
||||||
|
|
||||||
|
- If ***select_features*** is set, as many *SPODEs* are created as variables selected by the corresponding feature selection algorithm, and these variables are marked as used.
|
||||||
|
|
||||||
|
- Initial weights of the examples are set to *1/m*.
|
||||||
|
|
||||||
|
1. **Main Training Loop:**
|
||||||
|
|
||||||
|
- Variables are sorted by mutual information order with the class variable and processed in ascending, descending or random order, according to the value of the *order* hyperparameter. If it is random, the variables are shuffled.
|
||||||
|
|
||||||
|
- If the parent repetition is not established, the variable is marked as used.
|
||||||
|
|
||||||
|
- A *SPODE* is created using the selected variable as the parent.
|
||||||
|
|
||||||
|
- The model is trained, and the class variable corresponding to the training dataset is calculated. The calculation can be done using the last trained model or the set of models trained up to that point, according to the value of the *predict_single* hyperparameter.
|
||||||
|
|
||||||
|
- The weights associated with the examples are updated using this expression:
|
||||||
|
|
||||||
|
- w<sub>i</sub> · e<sup>α<sub>t</sub></sup> (if the example has been misclassified)
|
||||||
|
|
||||||
|
- w<sub>i</sub> · e<sup>-α<sub>t</sub></sup> (if the example has been correctly classified)
|
||||||
|
|
||||||
|
- The model significance is set to α<sub>t</sub>.
|
||||||
|
|
||||||
|
- If the ***convergence*** hyperparameter is set, the accuracy value on the test dataset that we separated in an initial step is calculated.
|
||||||
|
|
||||||
|
1. **Exit Conditions:**
|
||||||
|
|
||||||
|
- ε<sub>t</sub> > 0.5 => misclassified examples are penalized.
|
||||||
|
|
||||||
|
- Number of models with worse accuracy greater than ***tolerance*** and ***convergence*** established.
|
||||||
|
|
||||||
|
- There are no more variables to create models, and ***repeatSparent*** is not set.
|
||||||
|
|
||||||
|
- Number of models > ***maxModels*** if ***repeatSparent*** is set.
|
||||||
|
|
||||||
|
### [Proposal for *predict_single = false*](./BoostAODE_train_predict.pdf)
|
BIN
docs/BoostAODE_train_predict.odp
Normal file
BIN
docs/BoostAODE_train_predict.odp
Normal file
Binary file not shown.
BIN
docs/BoostAODE_train_predict.pdf
Normal file
BIN
docs/BoostAODE_train_predict.pdf
Normal file
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
filter = src/
|
filter = src/
|
||||||
exclude-directories = build/lib/
|
exclude-directories = build_debug/lib/
|
||||||
print-summary = yes
|
print-summary = yes
|
||||||
sort-percentage = yes
|
sort = uncovered-percent
|
||||||
|
@@ -4,11 +4,9 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
ArffFiles::ArffFiles() = default;
|
ArffFiles::ArffFiles() = default;
|
||||||
|
|
||||||
vector<string> ArffFiles::getLines() const
|
std::vector<std::string> ArffFiles::getLines() const
|
||||||
{
|
{
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
@@ -18,48 +16,48 @@ unsigned long int ArffFiles::getSize() const
|
|||||||
return lines.size();
|
return lines.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<pair<string, string>> ArffFiles::getAttributes() const
|
std::vector<std::pair<std::string, std::string>> ArffFiles::getAttributes() const
|
||||||
{
|
{
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ArffFiles::getClassName() const
|
std::string ArffFiles::getClassName() const
|
||||||
{
|
{
|
||||||
return className;
|
return className;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ArffFiles::getClassType() const
|
std::string ArffFiles::getClassType() const
|
||||||
{
|
{
|
||||||
return classType;
|
return classType;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<vector<float>>& ArffFiles::getX()
|
std::vector<std::vector<float>>& ArffFiles::getX()
|
||||||
{
|
{
|
||||||
return X;
|
return X;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<int>& ArffFiles::getY()
|
std::vector<int>& ArffFiles::getY()
|
||||||
{
|
{
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArffFiles::loadCommon(string fileName)
|
void ArffFiles::loadCommon(std::string fileName)
|
||||||
{
|
{
|
||||||
ifstream file(fileName);
|
std::ifstream file(fileName);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
throw invalid_argument("Unable to open file");
|
throw std::invalid_argument("Unable to open file");
|
||||||
}
|
}
|
||||||
string line;
|
std::string line;
|
||||||
string keyword;
|
std::string keyword;
|
||||||
string attribute;
|
std::string attribute;
|
||||||
string type;
|
std::string type;
|
||||||
string type_w;
|
std::string type_w;
|
||||||
while (getline(file, line)) {
|
while (getline(file, line)) {
|
||||||
if (line.empty() || line[0] == '%' || line == "\r" || line == " ") {
|
if (line.empty() || line[0] == '%' || line == "\r" || line == " ") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.find("@attribute") != string::npos || line.find("@ATTRIBUTE") != string::npos) {
|
if (line.find("@attribute") != std::string::npos || line.find("@ATTRIBUTE") != std::string::npos) {
|
||||||
stringstream ss(line);
|
std::stringstream ss(line);
|
||||||
ss >> keyword >> attribute;
|
ss >> keyword >> attribute;
|
||||||
type = "";
|
type = "";
|
||||||
while (ss >> type_w)
|
while (ss >> type_w)
|
||||||
@@ -74,35 +72,35 @@ void ArffFiles::loadCommon(string fileName)
|
|||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
if (attributes.empty())
|
if (attributes.empty())
|
||||||
throw invalid_argument("No attributes found");
|
throw std::invalid_argument("No attributes found");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArffFiles::load(const string& fileName, bool classLast)
|
void ArffFiles::load(const std::string& fileName, bool classLast)
|
||||||
{
|
{
|
||||||
int labelIndex;
|
int labelIndex;
|
||||||
loadCommon(fileName);
|
loadCommon(fileName);
|
||||||
if (classLast) {
|
if (classLast) {
|
||||||
className = get<0>(attributes.back());
|
className = std::get<0>(attributes.back());
|
||||||
classType = get<1>(attributes.back());
|
classType = std::get<1>(attributes.back());
|
||||||
attributes.pop_back();
|
attributes.pop_back();
|
||||||
labelIndex = static_cast<int>(attributes.size());
|
labelIndex = static_cast<int>(attributes.size());
|
||||||
} else {
|
} else {
|
||||||
className = get<0>(attributes.front());
|
className = std::get<0>(attributes.front());
|
||||||
classType = get<1>(attributes.front());
|
classType = std::get<1>(attributes.front());
|
||||||
attributes.erase(attributes.begin());
|
attributes.erase(attributes.begin());
|
||||||
labelIndex = 0;
|
labelIndex = 0;
|
||||||
}
|
}
|
||||||
generateDataset(labelIndex);
|
generateDataset(labelIndex);
|
||||||
}
|
}
|
||||||
void ArffFiles::load(const string& fileName, const string& name)
|
void ArffFiles::load(const std::string& fileName, const std::string& name)
|
||||||
{
|
{
|
||||||
int labelIndex;
|
int labelIndex;
|
||||||
loadCommon(fileName);
|
loadCommon(fileName);
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (int i = 0; i < attributes.size(); ++i) {
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
if (attributes[i].first == name) {
|
if (attributes[i].first == name) {
|
||||||
className = get<0>(attributes[i]);
|
className = std::get<0>(attributes[i]);
|
||||||
classType = get<1>(attributes[i]);
|
classType = std::get<1>(attributes[i]);
|
||||||
attributes.erase(attributes.begin() + i);
|
attributes.erase(attributes.begin() + i);
|
||||||
labelIndex = i;
|
labelIndex = i;
|
||||||
found = true;
|
found = true;
|
||||||
@@ -110,19 +108,19 @@ void ArffFiles::load(const string& fileName, const string& name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
throw invalid_argument("Class name not found");
|
throw std::invalid_argument("Class name not found");
|
||||||
}
|
}
|
||||||
generateDataset(labelIndex);
|
generateDataset(labelIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArffFiles::generateDataset(int labelIndex)
|
void ArffFiles::generateDataset(int labelIndex)
|
||||||
{
|
{
|
||||||
X = vector<vector<float>>(attributes.size(), vector<float>(lines.size()));
|
X = std::vector<std::vector<float>>(attributes.size(), std::vector<float>(lines.size()));
|
||||||
auto yy = vector<string>(lines.size(), "");
|
auto yy = std::vector<std::string>(lines.size(), "");
|
||||||
auto removeLines = vector<int>(); // Lines with missing values
|
auto removeLines = std::vector<int>(); // Lines with missing values
|
||||||
for (size_t i = 0; i < lines.size(); i++) {
|
for (size_t i = 0; i < lines.size(); i++) {
|
||||||
stringstream ss(lines[i]);
|
std::stringstream ss(lines[i]);
|
||||||
string value;
|
std::string value;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
int xIndex = 0;
|
int xIndex = 0;
|
||||||
while (getline(ss, value, ',')) {
|
while (getline(ss, value, ',')) {
|
||||||
@@ -146,21 +144,21 @@ void ArffFiles::generateDataset(int labelIndex)
|
|||||||
y = factorize(yy);
|
y = factorize(yy);
|
||||||
}
|
}
|
||||||
|
|
||||||
string ArffFiles::trim(const string& source)
|
std::string ArffFiles::trim(const std::string& source)
|
||||||
{
|
{
|
||||||
string s(source);
|
std::string s(source);
|
||||||
s.erase(0, s.find_first_not_of(" '\n\r\t"));
|
s.erase(0, s.find_first_not_of(" '\n\r\t"));
|
||||||
s.erase(s.find_last_not_of(" '\n\r\t") + 1);
|
s.erase(s.find_last_not_of(" '\n\r\t") + 1);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<int> ArffFiles::factorize(const vector<string>& labels_t)
|
std::vector<int> ArffFiles::factorize(const std::vector<std::string>& labels_t)
|
||||||
{
|
{
|
||||||
vector<int> yy;
|
std::vector<int> yy;
|
||||||
yy.reserve(labels_t.size());
|
yy.reserve(labels_t.size());
|
||||||
map<string, int> labelMap;
|
std::map<std::string, int> labelMap;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const string& label : labels_t) {
|
for (const std::string& label : labels_t) {
|
||||||
if (labelMap.find(label) == labelMap.end()) {
|
if (labelMap.find(label) == labelMap.end()) {
|
||||||
labelMap[label] = i++;
|
labelMap[label] = i++;
|
||||||
}
|
}
|
||||||
|
@@ -4,31 +4,29 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
class ArffFiles {
|
class ArffFiles {
|
||||||
private:
|
private:
|
||||||
vector<string> lines;
|
std::vector<std::string> lines;
|
||||||
vector<pair<string, string>> attributes;
|
std::vector<std::pair<std::string, std::string>> attributes;
|
||||||
string className;
|
std::string className;
|
||||||
string classType;
|
std::string classType;
|
||||||
vector<vector<float>> X;
|
std::vector<std::vector<float>> X;
|
||||||
vector<int> y;
|
std::vector<int> y;
|
||||||
void generateDataset(int);
|
void generateDataset(int);
|
||||||
void loadCommon(string);
|
void loadCommon(std::string);
|
||||||
public:
|
public:
|
||||||
ArffFiles();
|
ArffFiles();
|
||||||
void load(const string&, bool = true);
|
void load(const std::string&, bool = true);
|
||||||
void load(const string&, const string&);
|
void load(const std::string&, const std::string&);
|
||||||
vector<string> getLines() const;
|
std::vector<std::string> getLines() const;
|
||||||
unsigned long int getSize() const;
|
unsigned long int getSize() const;
|
||||||
string getClassName() const;
|
std::string getClassName() const;
|
||||||
string getClassType() const;
|
std::string getClassType() const;
|
||||||
static string trim(const string&);
|
static std::string trim(const std::string&);
|
||||||
vector<vector<float>>& getX();
|
std::vector<std::vector<float>>& getX();
|
||||||
vector<int>& getY();
|
std::vector<int>& getY();
|
||||||
vector<pair<string, string>> getAttributes() const;
|
std::vector<std::pair<std::string, std::string>> getAttributes() const;
|
||||||
static vector<int> factorize(const vector<string>& labels_t);
|
static std::vector<int> factorize(const std::vector<std::string>& labels_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
Submodule lib/argparse deleted from b0930ab028
Submodule lib/catch2 updated: 4acc51828f...ed6ac8a629
1
lib/folding
Submodule
1
lib/folding
Submodule
Submodule lib/folding added at 37316a54e0
2
lib/json
2
lib/json
Submodule lib/json updated: 5d2754306d...0457de21cf
@@ -1,7 +1,14 @@
|
|||||||
include_directories(${BayesNet_SOURCE_DIR}/src/Platform)
|
include_directories(
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/src/BayesNet)
|
${BayesNet_SOURCE_DIR}/src
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/Files)
|
${BayesNet_SOURCE_DIR}/src/classifiers
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/mdlp)
|
${BayesNet_SOURCE_DIR}/src/ensembles
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/argparse/include)
|
${BayesNet_SOURCE_DIR}/src/bayesian_network
|
||||||
add_executable(BayesNetSample sample.cc ${BayesNet_SOURCE_DIR}/src/Platform/Folding.cc ${BayesNet_SOURCE_DIR}/src/Platform/Models.cc)
|
${BayesNet_SOURCE_DIR}/src/utils
|
||||||
target_link_libraries(BayesNetSample BayesNet ArffFiles mdlp "${TORCH_LIBRARIES}")
|
${BayesNet_SOURCE_DIR}/src/feature_selection
|
||||||
|
${BayesNet_SOURCE_DIR}/lib/Files
|
||||||
|
${BayesNet_SOURCE_DIR}/lib/mdlp
|
||||||
|
${BayesNet_SOURCE_DIR}/lib/json/include
|
||||||
|
${CMAKE_BINARY_DIR}/configured_files/include
|
||||||
|
)
|
||||||
|
add_executable(bayesnet_sample sample.cc)
|
||||||
|
target_link_libraries(bayesnet_sample ArffFiles BayesNet)
|
281
sample/sample.cc
281
sample/sample.cc
@@ -1,251 +1,62 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <argparse/argparse.hpp>
|
|
||||||
#include "ArffFiles.h"
|
#include "ArffFiles.h"
|
||||||
#include "BayesMetrics.h"
|
|
||||||
#include "CPPFImdlp.h"
|
#include "CPPFImdlp.h"
|
||||||
#include "Folding.h"
|
#include "BoostAODE.h"
|
||||||
#include "Models.h"
|
|
||||||
#include "modelRegister.h"
|
|
||||||
|
|
||||||
|
std::vector<mdlp::labels_t> discretizeDataset(std::vector<mdlp::samples_t>& X, mdlp::labels_t& y)
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
const string PATH = "../../data/";
|
|
||||||
|
|
||||||
pair<vector<mdlp::labels_t>, map<string, int>> discretize(vector<mdlp::samples_t>& X, mdlp::labels_t& y, vector<string> features)
|
|
||||||
{
|
{
|
||||||
vector<mdlp::labels_t>Xd;
|
std::vector<mdlp::labels_t> Xd;
|
||||||
map<string, int> maxes;
|
|
||||||
|
|
||||||
auto fimdlp = mdlp::CPPFImdlp();
|
auto fimdlp = mdlp::CPPFImdlp();
|
||||||
for (int i = 0; i < X.size(); i++) {
|
for (int i = 0; i < X.size(); i++) {
|
||||||
fimdlp.fit(X[i], y);
|
fimdlp.fit(X[i], y);
|
||||||
mdlp::labels_t& xd = fimdlp.transform(X[i]);
|
mdlp::labels_t& xd = fimdlp.transform(X[i]);
|
||||||
maxes[features[i]] = *max_element(xd.begin(), xd.end()) + 1;
|
|
||||||
Xd.push_back(xd);
|
Xd.push_back(xd);
|
||||||
}
|
}
|
||||||
return { Xd, maxes };
|
return Xd;
|
||||||
|
}
|
||||||
|
tuple<torch::Tensor, torch::Tensor, std::vector<std::string>, std::string, map<std::string, std::vector<int>>> loadDataset(const std::string& name, bool class_last)
|
||||||
|
{
|
||||||
|
auto handler = ArffFiles();
|
||||||
|
handler.load(name, class_last);
|
||||||
|
// Get Dataset X, y
|
||||||
|
std::vector<mdlp::samples_t>& X = handler.getX();
|
||||||
|
mdlp::labels_t& y = handler.getY();
|
||||||
|
// Get className & Features
|
||||||
|
auto className = handler.getClassName();
|
||||||
|
std::vector<std::string> features;
|
||||||
|
auto attributes = handler.getAttributes();
|
||||||
|
transform(attributes.begin(), attributes.end(), back_inserter(features), [](const auto& pair) { return pair.first; });
|
||||||
|
torch::Tensor Xd;
|
||||||
|
auto states = map<std::string, std::vector<int>>();
|
||||||
|
auto Xr = discretizeDataset(X, y);
|
||||||
|
Xd = torch::zeros({ static_cast<int>(Xr.size()), static_cast<int>(Xr[0].size()) }, torch::kInt32);
|
||||||
|
for (int i = 0; i < features.size(); ++i) {
|
||||||
|
states[features[i]] = std::vector<int>(*max_element(Xr[i].begin(), Xr[i].end()) + 1);
|
||||||
|
auto item = states.at(features[i]);
|
||||||
|
iota(begin(item), end(item), 0);
|
||||||
|
Xd.index_put_({ i, "..." }, torch::tensor(Xr[i], torch::kInt32));
|
||||||
|
}
|
||||||
|
states[className] = std::vector<int>(*max_element(y.begin(), y.end()) + 1);
|
||||||
|
iota(begin(states.at(className)), end(states.at(className)), 0);
|
||||||
|
return { Xd, torch::tensor(y, torch::kInt32), features, className, states };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool file_exists(const std::string& name)
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
if (FILE* file = fopen(name.c_str(), "r")) {
|
if (argc < 2) {
|
||||||
fclose(file);
|
std::cerr << "Usage: " << argv[0] << " <file_name>" << std::endl;
|
||||||
return true;
|
return 1;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
std::string file_name = argv[1];
|
||||||
pair<vector<vector<int>>, vector<int>> extract_indices(vector<int> indices, vector<vector<int>> X, vector<int> y)
|
torch::Tensor X, y;
|
||||||
{
|
std::vector<std::string> features;
|
||||||
vector<vector<int>> Xr; // nxm
|
std::string className;
|
||||||
vector<int> yr;
|
map<std::string, std::vector<int>> states;
|
||||||
for (int col = 0; col < X.size(); ++col) {
|
auto clf = bayesnet::BoostAODE(false); // false for not using voting in predict
|
||||||
Xr.push_back(vector<int>());
|
std::cout << "Library version: " << clf.getVersion() << std::endl;
|
||||||
}
|
tie(X, y, features, className, states) = loadDataset(file_name, true);
|
||||||
for (auto index : indices) {
|
clf.fit(X, y, features, className, states);
|
||||||
for (int col = 0; col < X.size(); ++col) {
|
auto score = clf.score(X, y);
|
||||||
Xr[col].push_back(X[col][index]);
|
std::cout << "File: " << file_name << " score: " << score << std::endl;
|
||||||
}
|
return 0;
|
||||||
yr.push_back(y[index]);
|
|
||||||
}
|
|
||||||
return { Xr, yr };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
map<string, bool> datasets = {
|
|
||||||
{"diabetes", true},
|
|
||||||
{"ecoli", true},
|
|
||||||
{"glass", true},
|
|
||||||
{"iris", true},
|
|
||||||
{"kdd_JapaneseVowels", false},
|
|
||||||
{"letter", true},
|
|
||||||
{"liver-disorders", true},
|
|
||||||
{"mfeat-factors", true},
|
|
||||||
};
|
|
||||||
auto valid_datasets = vector<string>();
|
|
||||||
transform(datasets.begin(), datasets.end(), back_inserter(valid_datasets),
|
|
||||||
[](const pair<string, bool>& pair) { return pair.first; });
|
|
||||||
argparse::ArgumentParser program("BayesNetSample");
|
|
||||||
program.add_argument("-d", "--dataset")
|
|
||||||
.help("Dataset file name")
|
|
||||||
.action([valid_datasets](const std::string& value) {
|
|
||||||
if (find(valid_datasets.begin(), valid_datasets.end(), value) != valid_datasets.end()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
throw runtime_error("file must be one of {diabetes, ecoli, glass, iris, kdd_JapaneseVowels, letter, liver-disorders, mfeat-factors}");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
program.add_argument("-p", "--path")
|
|
||||||
.help(" folder where the data files are located, default")
|
|
||||||
.default_value(string{ PATH }
|
|
||||||
);
|
|
||||||
program.add_argument("-m", "--model")
|
|
||||||
.help("Model to use " + platform::Models::instance()->toString())
|
|
||||||
.action([](const std::string& value) {
|
|
||||||
static const vector<string> choices = platform::Models::instance()->getNames();
|
|
||||||
if (find(choices.begin(), choices.end(), value) != choices.end()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
throw runtime_error("Model must be one of " + platform::Models::instance()->toString());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
program.add_argument("--discretize").help("Discretize input dataset").default_value(false).implicit_value(true);
|
|
||||||
program.add_argument("--dumpcpt").help("Dump CPT Tables").default_value(false).implicit_value(true);
|
|
||||||
program.add_argument("--stratified").help("If Stratified KFold is to be done").default_value(false).implicit_value(true);
|
|
||||||
program.add_argument("--tensors").help("Use tensors to store samples").default_value(false).implicit_value(true);
|
|
||||||
program.add_argument("-f", "--folds").help("Number of folds").default_value(5).scan<'i', int>().action([](const string& value) {
|
|
||||||
try {
|
|
||||||
auto k = stoi(value);
|
|
||||||
if (k < 2) {
|
|
||||||
throw runtime_error("Number of folds must be greater than 1");
|
|
||||||
}
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
catch (const runtime_error& err) {
|
|
||||||
throw runtime_error(err.what());
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
throw runtime_error("Number of folds must be an integer");
|
|
||||||
}});
|
|
||||||
program.add_argument("-s", "--seed").help("Random seed").default_value(-1).scan<'i', int>();
|
|
||||||
bool class_last, stratified, tensors, dump_cpt;
|
|
||||||
string model_name, file_name, path, complete_file_name;
|
|
||||||
int nFolds, seed;
|
|
||||||
try {
|
|
||||||
program.parse_args(argc, argv);
|
|
||||||
file_name = program.get<string>("dataset");
|
|
||||||
path = program.get<string>("path");
|
|
||||||
model_name = program.get<string>("model");
|
|
||||||
complete_file_name = path + file_name + ".arff";
|
|
||||||
stratified = program.get<bool>("stratified");
|
|
||||||
tensors = program.get<bool>("tensors");
|
|
||||||
nFolds = program.get<int>("folds");
|
|
||||||
seed = program.get<int>("seed");
|
|
||||||
dump_cpt = program.get<bool>("dumpcpt");
|
|
||||||
class_last = datasets[file_name];
|
|
||||||
if (!file_exists(complete_file_name)) {
|
|
||||||
throw runtime_error("Data File " + path + file_name + ".arff" + " does not exist");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const exception& err) {
|
|
||||||
cerr << err.what() << endl;
|
|
||||||
cerr << program;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Begin Processing
|
|
||||||
*/
|
|
||||||
auto ypred = torch::tensor({ 1,2,3,2,2,3,4,5,2,1 });
|
|
||||||
auto y = torch::tensor({ 0,0,0,0,2,3,4,0,0,0 });
|
|
||||||
auto weights = torch::ones({ 10 }, kDouble);
|
|
||||||
auto mask = ypred == y;
|
|
||||||
cout << "ypred:" << ypred << endl;
|
|
||||||
cout << "y:" << y << endl;
|
|
||||||
cout << "weights:" << weights << endl;
|
|
||||||
cout << "mask:" << mask << endl;
|
|
||||||
double value_to_add = 0.5;
|
|
||||||
weights += mask.to(torch::kDouble) * value_to_add;
|
|
||||||
cout << "New weights:" << weights << endl;
|
|
||||||
auto masked_weights = weights * mask.to(weights.dtype());
|
|
||||||
double sum_of_weights = masked_weights.sum().item<double>();
|
|
||||||
cout << "Sum of weights: " << sum_of_weights << endl;
|
|
||||||
//weights.index_put_({ mask }, weights + 10);
|
|
||||||
// auto handler = ArffFiles();
|
|
||||||
// handler.load(complete_file_name, class_last);
|
|
||||||
// // Get Dataset X, y
|
|
||||||
// vector<mdlp::samples_t>& X = handler.getX();
|
|
||||||
// mdlp::labels_t& y = handler.getY();
|
|
||||||
// // Get className & Features
|
|
||||||
// auto className = handler.getClassName();
|
|
||||||
// vector<string> features;
|
|
||||||
// auto attributes = handler.getAttributes();
|
|
||||||
// transform(attributes.begin(), attributes.end(), back_inserter(features),
|
|
||||||
// [](const pair<string, string>& item) { return item.first; });
|
|
||||||
// // Discretize Dataset
|
|
||||||
// auto [Xd, maxes] = discretize(X, y, features);
|
|
||||||
// maxes[className] = *max_element(y.begin(), y.end()) + 1;
|
|
||||||
// map<string, vector<int>> states;
|
|
||||||
// for (auto feature : features) {
|
|
||||||
// states[feature] = vector<int>(maxes[feature]);
|
|
||||||
// }
|
|
||||||
// states[className] = vector<int>(maxes[className]);
|
|
||||||
// auto clf = platform::Models::instance()->create(model_name);
|
|
||||||
// clf->fit(Xd, y, features, className, states);
|
|
||||||
// if (dump_cpt) {
|
|
||||||
// cout << "--- CPT Tables ---" << endl;
|
|
||||||
// clf->dump_cpt();
|
|
||||||
// }
|
|
||||||
// auto lines = clf->show();
|
|
||||||
// for (auto line : lines) {
|
|
||||||
// cout << line << endl;
|
|
||||||
// }
|
|
||||||
// cout << "--- Topological Order ---" << endl;
|
|
||||||
// auto order = clf->topological_order();
|
|
||||||
// for (auto name : order) {
|
|
||||||
// cout << name << ", ";
|
|
||||||
// }
|
|
||||||
// cout << "end." << endl;
|
|
||||||
// auto score = clf->score(Xd, y);
|
|
||||||
// cout << "Score: " << score << endl;
|
|
||||||
// auto graph = clf->graph();
|
|
||||||
// auto dot_file = model_name + "_" + file_name;
|
|
||||||
// ofstream file(dot_file + ".dot");
|
|
||||||
// file << graph;
|
|
||||||
// file.close();
|
|
||||||
// cout << "Graph saved in " << model_name << "_" << file_name << ".dot" << endl;
|
|
||||||
// cout << "dot -Tpng -o " + dot_file + ".png " + dot_file + ".dot " << endl;
|
|
||||||
// string stratified_string = stratified ? " Stratified" : "";
|
|
||||||
// cout << nFolds << " Folds" << stratified_string << " Cross validation" << endl;
|
|
||||||
// cout << "==========================================" << endl;
|
|
||||||
// torch::Tensor Xt = torch::zeros({ static_cast<int>(Xd.size()), static_cast<int>(Xd[0].size()) }, torch::kInt32);
|
|
||||||
// torch::Tensor yt = torch::tensor(y, torch::kInt32);
|
|
||||||
// for (int i = 0; i < features.size(); ++i) {
|
|
||||||
// Xt.index_put_({ i, "..." }, torch::tensor(Xd[i], torch::kInt32));
|
|
||||||
// }
|
|
||||||
// float total_score = 0, total_score_train = 0, score_train, score_test;
|
|
||||||
// Fold* fold;
|
|
||||||
// if (stratified)
|
|
||||||
// fold = new StratifiedKFold(nFolds, y, seed);
|
|
||||||
// else
|
|
||||||
// fold = new KFold(nFolds, y.size(), seed);
|
|
||||||
// for (auto i = 0; i < nFolds; ++i) {
|
|
||||||
// auto [train, test] = fold->getFold(i);
|
|
||||||
// cout << "Fold: " << i + 1 << endl;
|
|
||||||
// if (tensors) {
|
|
||||||
// auto ttrain = torch::tensor(train, torch::kInt64);
|
|
||||||
// auto ttest = torch::tensor(test, torch::kInt64);
|
|
||||||
// torch::Tensor Xtraint = torch::index_select(Xt, 1, ttrain);
|
|
||||||
// torch::Tensor ytraint = yt.index({ ttrain });
|
|
||||||
// torch::Tensor Xtestt = torch::index_select(Xt, 1, ttest);
|
|
||||||
// torch::Tensor ytestt = yt.index({ ttest });
|
|
||||||
// clf->fit(Xtraint, ytraint, features, className, states);
|
|
||||||
// auto temp = clf->predict(Xtraint);
|
|
||||||
// score_train = clf->score(Xtraint, ytraint);
|
|
||||||
// score_test = clf->score(Xtestt, ytestt);
|
|
||||||
// } else {
|
|
||||||
// auto [Xtrain, ytrain] = extract_indices(train, Xd, y);
|
|
||||||
// auto [Xtest, ytest] = extract_indices(test, Xd, y);
|
|
||||||
// clf->fit(Xtrain, ytrain, features, className, states);
|
|
||||||
// score_train = clf->score(Xtrain, ytrain);
|
|
||||||
// score_test = clf->score(Xtest, ytest);
|
|
||||||
// }
|
|
||||||
// if (dump_cpt) {
|
|
||||||
// cout << "--- CPT Tables ---" << endl;
|
|
||||||
// clf->dump_cpt();
|
|
||||||
// }
|
|
||||||
// total_score_train += score_train;
|
|
||||||
// total_score += score_test;
|
|
||||||
// cout << "Score Train: " << score_train << endl;
|
|
||||||
// cout << "Score Test : " << score_test << endl;
|
|
||||||
// cout << "-------------------------------------------------------------------------------" << endl;
|
|
||||||
// }
|
|
||||||
// cout << "**********************************************************************************" << endl;
|
|
||||||
// cout << "Average Score Train: " << total_score_train / nFolds << endl;
|
|
||||||
// cout << "Average Score Test : " << total_score / nFolds << endl;return 0;
|
|
||||||
}
|
|
41
src/BaseClassifier.h
Normal file
41
src/BaseClassifier.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef BASE_H
|
||||||
|
#define BASE_H
|
||||||
|
#include <torch/torch.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <vector>
|
||||||
|
namespace bayesnet {
|
||||||
|
enum status_t { NORMAL, WARNING, ERROR };
|
||||||
|
class BaseClassifier {
|
||||||
|
public:
|
||||||
|
// X is nxm std::vector, y is nx1 std::vector
|
||||||
|
virtual BaseClassifier& fit(std::vector<std::vector<int>>& X, std::vector<int>& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states) = 0;
|
||||||
|
// X is nxm tensor, y is nx1 tensor
|
||||||
|
virtual BaseClassifier& fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states) = 0;
|
||||||
|
virtual BaseClassifier& fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states) = 0;
|
||||||
|
virtual BaseClassifier& fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights) = 0;
|
||||||
|
virtual ~BaseClassifier() = default;
|
||||||
|
torch::Tensor virtual predict(torch::Tensor& X) = 0;
|
||||||
|
std::vector<int> virtual predict(std::vector<std::vector<int >>& X) = 0;
|
||||||
|
torch::Tensor virtual predict_proba(torch::Tensor& X) = 0;
|
||||||
|
std::vector<std::vector<double>> virtual predict_proba(std::vector<std::vector<int >>& X) = 0;
|
||||||
|
status_t virtual getStatus() const = 0;
|
||||||
|
float virtual score(std::vector<std::vector<int>>& X, std::vector<int>& y) = 0;
|
||||||
|
float virtual score(torch::Tensor& X, torch::Tensor& y) = 0;
|
||||||
|
int virtual getNumberOfNodes()const = 0;
|
||||||
|
int virtual getNumberOfEdges()const = 0;
|
||||||
|
int virtual getNumberOfStates() const = 0;
|
||||||
|
int virtual getClassNumStates() const = 0;
|
||||||
|
std::vector<std::string> virtual show() const = 0;
|
||||||
|
std::vector<std::string> virtual graph(const std::string& title = "") const = 0;
|
||||||
|
virtual std::string getVersion() = 0;
|
||||||
|
std::vector<std::string> virtual topological_order() = 0;
|
||||||
|
std::vector<std::string> virtual getNotes() const = 0;
|
||||||
|
void virtual dump_cpt()const = 0;
|
||||||
|
virtual void setHyperparameters(const nlohmann::json& hyperparameters) = 0;
|
||||||
|
std::vector<std::string>& getValidHyperparameters() { return validHyperparameters; }
|
||||||
|
protected:
|
||||||
|
virtual void trainModel(const torch::Tensor& weights) = 0;
|
||||||
|
std::vector<std::string> validHyperparameters;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
@@ -1,18 +0,0 @@
|
|||||||
#include "AODE.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
AODE::AODE() : Ensemble() {}
|
|
||||||
void AODE::buildModel(const torch::Tensor& weights)
|
|
||||||
{
|
|
||||||
models.clear();
|
|
||||||
for (int i = 0; i < features.size(); ++i) {
|
|
||||||
models.push_back(std::make_unique<SPODE>(i));
|
|
||||||
}
|
|
||||||
n_models = models.size();
|
|
||||||
significanceModels = vector<double>(n_models, 1.0);
|
|
||||||
}
|
|
||||||
vector<string> AODE::graph(const string& title) const
|
|
||||||
{
|
|
||||||
return Ensemble::graph(title);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,40 +0,0 @@
|
|||||||
#include "AODELd.h"
|
|
||||||
#include "Models.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
AODELd::AODELd() : Ensemble(), Proposal(dataset, features, className) {}
|
|
||||||
AODELd& AODELd::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
|
||||||
{
|
|
||||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
|
||||||
features = features_;
|
|
||||||
className = className_;
|
|
||||||
Xf = X_;
|
|
||||||
y = y_;
|
|
||||||
// Fills vectors Xv & yv with the data from tensors X_ (discretized) & y
|
|
||||||
states = fit_local_discretization(y);
|
|
||||||
// We have discretized the input data
|
|
||||||
// 1st we need to fit the model to build the normal TAN structure, TAN::fit initializes the base Bayesian network
|
|
||||||
Ensemble::fit(dataset, features, className, states);
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
}
|
|
||||||
void AODELd::buildModel(const torch::Tensor& weights)
|
|
||||||
{
|
|
||||||
models.clear();
|
|
||||||
for (int i = 0; i < features.size(); ++i) {
|
|
||||||
models.push_back(std::make_unique<SPODELd>(i));
|
|
||||||
}
|
|
||||||
n_models = models.size();
|
|
||||||
}
|
|
||||||
void AODELd::trainModel(const torch::Tensor& weights)
|
|
||||||
{
|
|
||||||
for (const auto& model : models) {
|
|
||||||
model->fit(Xf, y, features, className, states);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vector<string> AODELd::graph(const string& name) const
|
|
||||||
{
|
|
||||||
return Ensemble::graph(name);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
#ifndef AODELD_H
|
|
||||||
#define AODELD_H
|
|
||||||
#include "Ensemble.h"
|
|
||||||
#include "Proposal.h"
|
|
||||||
#include "SPODELd.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
class AODELd : public Ensemble, public Proposal {
|
|
||||||
protected:
|
|
||||||
void trainModel(const torch::Tensor& weights) override;
|
|
||||||
void buildModel(const torch::Tensor& weights) override;
|
|
||||||
public:
|
|
||||||
AODELd();
|
|
||||||
AODELd& fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_) override;
|
|
||||||
virtual ~AODELd() = default;
|
|
||||||
vector<string> graph(const string& name = "AODE") const override;
|
|
||||||
static inline string version() { return "0.0.1"; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif // !AODELD_H
|
|
@@ -1,32 +0,0 @@
|
|||||||
#ifndef BASE_H
|
|
||||||
#define BASE_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <vector>
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
class BaseClassifier {
|
|
||||||
protected:
|
|
||||||
virtual void trainModel(const torch::Tensor& weights) = 0;
|
|
||||||
public:
|
|
||||||
// X is nxm vector, y is nx1 vector
|
|
||||||
virtual BaseClassifier& fit(vector<vector<int>>& X, vector<int>& y, vector<string>& features, string className, map<string, vector<int>>& states) = 0;
|
|
||||||
// X is nxm tensor, y is nx1 tensor
|
|
||||||
virtual BaseClassifier& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) = 0;
|
|
||||||
virtual BaseClassifier& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states) = 0;
|
|
||||||
virtual BaseClassifier& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights) = 0;
|
|
||||||
virtual ~BaseClassifier() = default;
|
|
||||||
torch::Tensor virtual predict(torch::Tensor& X) = 0;
|
|
||||||
vector<int> virtual predict(vector<vector<int>>& X) = 0;
|
|
||||||
float virtual score(vector<vector<int>>& X, vector<int>& y) = 0;
|
|
||||||
float virtual score(torch::Tensor& X, torch::Tensor& y) = 0;
|
|
||||||
int virtual getNumberOfNodes()const = 0;
|
|
||||||
int virtual getNumberOfEdges()const = 0;
|
|
||||||
int virtual getNumberOfStates() const = 0;
|
|
||||||
vector<string> virtual show() const = 0;
|
|
||||||
vector<string> virtual graph(const string& title = "") const = 0;
|
|
||||||
const string inline getVersion() const { return "0.1.0"; };
|
|
||||||
vector<string> virtual topological_order() = 0;
|
|
||||||
void virtual dump_cpt()const = 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,32 +0,0 @@
|
|||||||
#ifndef BAYESNET_METRICS_H
|
|
||||||
#define BAYESNET_METRICS_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
using namespace torch;
|
|
||||||
class Metrics {
|
|
||||||
private:
|
|
||||||
Tensor samples; // nxm tensor used to fit the model
|
|
||||||
vector<string> features;
|
|
||||||
string className;
|
|
||||||
int classNumStates = 0;
|
|
||||||
vector<double> scoresKBest;
|
|
||||||
vector<int> featuresKBest; // sorted indices of the features
|
|
||||||
double entropy(const Tensor& feature, const Tensor& weights);
|
|
||||||
double conditionalEntropy(const Tensor& firstFeature, const Tensor& secondFeature, const Tensor& weights);
|
|
||||||
vector<pair<string, string>> doCombinations(const vector<string>&);
|
|
||||||
public:
|
|
||||||
Metrics() = default;
|
|
||||||
Metrics(const torch::Tensor& samples, const vector<string>& features, const string& className, const int classNumStates);
|
|
||||||
Metrics(const vector<vector<int>>& vsamples, const vector<int>& labels, const vector<string>& features, const string& className, const int classNumStates);
|
|
||||||
vector<int> SelectKBestWeighted(const torch::Tensor& weights, unsigned k = 0);
|
|
||||||
vector<double> getScoresKBest() const;
|
|
||||||
double mutualInformation(const Tensor& firstFeature, const Tensor& secondFeature, const Tensor& weights);
|
|
||||||
vector<float> conditionalEdgeWeights(vector<float>& weights); // To use in Python
|
|
||||||
Tensor conditionalEdge(const torch::Tensor& weights);
|
|
||||||
vector<pair<int, int>> maximumSpanningTree(const vector<string>& features, const Tensor& weights, const int root);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,82 +0,0 @@
|
|||||||
#include "BoostAODE.h"
|
|
||||||
#include "BayesMetrics.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
BoostAODE::BoostAODE() : Ensemble() {}
|
|
||||||
void BoostAODE::buildModel(const torch::Tensor& weights)
|
|
||||||
{
|
|
||||||
// Models shall be built in trainModel
|
|
||||||
}
|
|
||||||
void BoostAODE::trainModel(const torch::Tensor& weights)
|
|
||||||
{
|
|
||||||
models.clear();
|
|
||||||
n_models = 0;
|
|
||||||
int max_models = .1 * n > 10 ? .1 * n : n;
|
|
||||||
Tensor weights_ = torch::full({ m }, 1.0 / m, torch::kFloat64);
|
|
||||||
auto X_ = dataset.index({ torch::indexing::Slice(0, dataset.size(0) - 1), "..." });
|
|
||||||
auto y_ = dataset.index({ -1, "..." });
|
|
||||||
bool exitCondition = false;
|
|
||||||
bool repeatSparent = false;
|
|
||||||
vector<int> featuresUsed;
|
|
||||||
// Step 0: Set the finish condition
|
|
||||||
// if not repeatSparent a finish condition is run out of features
|
|
||||||
// n_models == max_models
|
|
||||||
int numClasses = states[className].size();
|
|
||||||
while (!exitCondition) {
|
|
||||||
// Step 1: Build ranking with mutual information
|
|
||||||
auto featureSelection = metrics.SelectKBestWeighted(weights_, n); // Get all the features sorted
|
|
||||||
auto feature = featureSelection[0];
|
|
||||||
unique_ptr<Classifier> model;
|
|
||||||
if (!repeatSparent) {
|
|
||||||
if (n_models == 0) {
|
|
||||||
models.resize(n); // Resize for n==nfeatures SPODEs
|
|
||||||
significanceModels.resize(n);
|
|
||||||
}
|
|
||||||
bool found = false;
|
|
||||||
for (int i = 0; i < featureSelection.size(); ++i) {
|
|
||||||
if (find(featuresUsed.begin(), featuresUsed.end(), i) != featuresUsed.end()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
feature = i;
|
|
||||||
featuresUsed.push_back(feature);
|
|
||||||
n_models++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
exitCondition = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
model = std::make_unique<SPODE>(feature);
|
|
||||||
model->fit(dataset, features, className, states, weights_);
|
|
||||||
auto ypred = model->predict(X_);
|
|
||||||
// Step 3.1: Compute the classifier amout of say
|
|
||||||
auto mask_wrong = ypred != y_;
|
|
||||||
auto masked_weights = weights_ * mask_wrong.to(weights_.dtype());
|
|
||||||
double wrongWeights = masked_weights.sum().item<double>();
|
|
||||||
double significance = wrongWeights == 0 ? 1 : 0.5 * log((1 - wrongWeights) / wrongWeights);
|
|
||||||
// Step 3.2: Update weights for next classifier
|
|
||||||
// Step 3.2.1: Update weights of wrong samples
|
|
||||||
weights_ += mask_wrong.to(weights_.dtype()) * exp(significance) * weights_;
|
|
||||||
// Step 3.3: Normalise the weights
|
|
||||||
double totalWeights = torch::sum(weights_).item<double>();
|
|
||||||
weights_ = weights_ / totalWeights;
|
|
||||||
// Step 3.4: Store classifier and its accuracy to weigh its future vote
|
|
||||||
if (!repeatSparent) {
|
|
||||||
models[feature] = std::move(model);
|
|
||||||
significanceModels[feature] = significance;
|
|
||||||
} else {
|
|
||||||
models.push_back(std::move(model));
|
|
||||||
significanceModels.push_back(significance);
|
|
||||||
n_models++;
|
|
||||||
}
|
|
||||||
exitCondition = n_models == max_models;
|
|
||||||
}
|
|
||||||
weights.copy_(weights_);
|
|
||||||
}
|
|
||||||
vector<string> BoostAODE::graph(const string& title) const
|
|
||||||
{
|
|
||||||
return Ensemble::graph(title);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,16 +0,0 @@
|
|||||||
#ifndef BOOSTAODE_H
|
|
||||||
#define BOOSTAODE_H
|
|
||||||
#include "Ensemble.h"
|
|
||||||
#include "SPODE.h"
|
|
||||||
namespace bayesnet {
|
|
||||||
class BoostAODE : public Ensemble {
|
|
||||||
protected:
|
|
||||||
void buildModel(const torch::Tensor& weights) override;
|
|
||||||
void trainModel(const torch::Tensor& weights) override;
|
|
||||||
public:
|
|
||||||
BoostAODE();
|
|
||||||
virtual ~BoostAODE() {};
|
|
||||||
vector<string> graph(const string& title = "BoostAODE") const override;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,8 +0,0 @@
|
|||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/mdlp)
|
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/Files)
|
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/src/BayesNet)
|
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/src/Platform)
|
|
||||||
add_library(BayesNet bayesnetUtils.cc Network.cc Node.cc BayesMetrics.cc Classifier.cc
|
|
||||||
KDB.cc TAN.cc SPODE.cc Ensemble.cc AODE.cc TANLd.cc KDBLd.cc SPODELd.cc AODELd.cc BoostAODE.cc
|
|
||||||
Mst.cc Proposal.cc ${BayesNet_SOURCE_DIR}/src/Platform/Models.cc)
|
|
||||||
target_link_libraries(BayesNet mdlp "${TORCH_LIBRARIES}")
|
|
@@ -1,52 +0,0 @@
|
|||||||
#ifndef CLASSIFIER_H
|
|
||||||
#define CLASSIFIER_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include "BaseClassifier.h"
|
|
||||||
#include "Network.h"
|
|
||||||
#include "BayesMetrics.h"
|
|
||||||
using namespace std;
|
|
||||||
using namespace torch;
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
class Classifier : public BaseClassifier {
|
|
||||||
private:
|
|
||||||
void buildDataset(torch::Tensor& y);
|
|
||||||
Classifier& build(vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights);
|
|
||||||
protected:
|
|
||||||
bool fitted;
|
|
||||||
int m, n; // m: number of samples, n: number of features
|
|
||||||
Network model;
|
|
||||||
Metrics metrics;
|
|
||||||
vector<string> features;
|
|
||||||
string className;
|
|
||||||
map<string, vector<int>> states;
|
|
||||||
Tensor dataset; // (n+1)xm tensor
|
|
||||||
void checkFitParameters();
|
|
||||||
virtual void buildModel(const torch::Tensor& weights) = 0;
|
|
||||||
void trainModel(const torch::Tensor& weights) override;
|
|
||||||
public:
|
|
||||||
Classifier(Network model);
|
|
||||||
virtual ~Classifier() = default;
|
|
||||||
Classifier& fit(vector<vector<int>>& X, vector<int>& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
|
||||||
Classifier& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
|
||||||
Classifier& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
|
||||||
Classifier& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights) override;
|
|
||||||
void addNodes();
|
|
||||||
int getNumberOfNodes() const override;
|
|
||||||
int getNumberOfEdges() const override;
|
|
||||||
int getNumberOfStates() const override;
|
|
||||||
Tensor predict(Tensor& X) override;
|
|
||||||
vector<int> predict(vector<vector<int>>& X) override;
|
|
||||||
float score(Tensor& X, Tensor& y) override;
|
|
||||||
float score(vector<vector<int>>& X, vector<int>& y) override;
|
|
||||||
vector<string> show() const override;
|
|
||||||
vector<string> topological_order() override;
|
|
||||||
void dump_cpt() const override;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,139 +0,0 @@
|
|||||||
#include "Ensemble.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace torch;
|
|
||||||
|
|
||||||
Ensemble::Ensemble() : Classifier(Network()) {}
|
|
||||||
|
|
||||||
void Ensemble::trainModel(const torch::Tensor& weights)
|
|
||||||
{
|
|
||||||
n_models = models.size();
|
|
||||||
for (auto i = 0; i < n_models; ++i) {
|
|
||||||
// fit with vectors
|
|
||||||
models[i]->fit(dataset, features, className, states);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vector<int> Ensemble::voting(Tensor& y_pred)
|
|
||||||
{
|
|
||||||
auto y_pred_ = y_pred.accessor<int, 2>();
|
|
||||||
vector<int> y_pred_final;
|
|
||||||
for (int i = 0; i < y_pred.size(0); ++i) {
|
|
||||||
vector<double> votes(y_pred.size(1), 0);
|
|
||||||
for (int j = 0; j < y_pred.size(1); ++j) {
|
|
||||||
votes[y_pred_[i][j]] += significanceModels[j];
|
|
||||||
}
|
|
||||||
// argsort in descending order
|
|
||||||
auto indices = argsort(votes);
|
|
||||||
y_pred_final.push_back(indices[0]);
|
|
||||||
}
|
|
||||||
return y_pred_final;
|
|
||||||
}
|
|
||||||
Tensor Ensemble::predict(Tensor& X)
|
|
||||||
{
|
|
||||||
if (!fitted) {
|
|
||||||
throw logic_error("Ensemble has not been fitted");
|
|
||||||
}
|
|
||||||
Tensor y_pred = torch::zeros({ X.size(1), n_models }, kInt32);
|
|
||||||
//Create a threadpool
|
|
||||||
auto threads{ vector<thread>() };
|
|
||||||
mutex mtx;
|
|
||||||
for (auto i = 0; i < n_models; ++i) {
|
|
||||||
threads.push_back(thread([&, i]() {
|
|
||||||
auto ypredict = models[i]->predict(X);
|
|
||||||
lock_guard<mutex> lock(mtx);
|
|
||||||
y_pred.index_put_({ "...", i }, ypredict);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
for (auto& thread : threads) {
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
return torch::tensor(voting(y_pred));
|
|
||||||
}
|
|
||||||
vector<int> Ensemble::predict(vector<vector<int>>& X)
|
|
||||||
{
|
|
||||||
if (!fitted) {
|
|
||||||
throw logic_error("Ensemble has not been fitted");
|
|
||||||
}
|
|
||||||
long m_ = X[0].size();
|
|
||||||
long n_ = X.size();
|
|
||||||
vector<vector<int>> Xd(n_, vector<int>(m_, 0));
|
|
||||||
for (auto i = 0; i < n_; i++) {
|
|
||||||
Xd[i] = vector<int>(X[i].begin(), X[i].end());
|
|
||||||
}
|
|
||||||
Tensor y_pred = torch::zeros({ m_, n_models }, kInt32);
|
|
||||||
for (auto i = 0; i < n_models; ++i) {
|
|
||||||
y_pred.index_put_({ "...", i }, torch::tensor(models[i]->predict(Xd), kInt32));
|
|
||||||
}
|
|
||||||
return voting(y_pred);
|
|
||||||
}
|
|
||||||
float Ensemble::score(Tensor& X, Tensor& y)
|
|
||||||
{
|
|
||||||
if (!fitted) {
|
|
||||||
throw logic_error("Ensemble has not been fitted");
|
|
||||||
}
|
|
||||||
auto y_pred = predict(X);
|
|
||||||
int correct = 0;
|
|
||||||
for (int i = 0; i < y_pred.size(0); ++i) {
|
|
||||||
if (y_pred[i].item<int>() == y[i].item<int>()) {
|
|
||||||
correct++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (double)correct / y_pred.size(0);
|
|
||||||
}
|
|
||||||
float Ensemble::score(vector<vector<int>>& X, vector<int>& y)
|
|
||||||
{
|
|
||||||
if (!fitted) {
|
|
||||||
throw logic_error("Ensemble has not been fitted");
|
|
||||||
}
|
|
||||||
auto y_pred = predict(X);
|
|
||||||
int correct = 0;
|
|
||||||
for (int i = 0; i < y_pred.size(); ++i) {
|
|
||||||
if (y_pred[i] == y[i]) {
|
|
||||||
correct++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (double)correct / y_pred.size();
|
|
||||||
}
|
|
||||||
vector<string> Ensemble::show() const
|
|
||||||
{
|
|
||||||
auto result = vector<string>();
|
|
||||||
for (auto i = 0; i < n_models; ++i) {
|
|
||||||
auto res = models[i]->show();
|
|
||||||
result.insert(result.end(), res.begin(), res.end());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
vector<string> Ensemble::graph(const string& title) const
|
|
||||||
{
|
|
||||||
auto result = vector<string>();
|
|
||||||
for (auto i = 0; i < n_models; ++i) {
|
|
||||||
auto res = models[i]->graph(title + "_" + to_string(i));
|
|
||||||
result.insert(result.end(), res.begin(), res.end());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
int Ensemble::getNumberOfNodes() const
|
|
||||||
{
|
|
||||||
int nodes = 0;
|
|
||||||
for (auto i = 0; i < n_models; ++i) {
|
|
||||||
nodes += models[i]->getNumberOfNodes();
|
|
||||||
}
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
int Ensemble::getNumberOfEdges() const
|
|
||||||
{
|
|
||||||
int edges = 0;
|
|
||||||
for (auto i = 0; i < n_models; ++i) {
|
|
||||||
edges += models[i]->getNumberOfEdges();
|
|
||||||
}
|
|
||||||
return edges;
|
|
||||||
}
|
|
||||||
int Ensemble::getNumberOfStates() const
|
|
||||||
{
|
|
||||||
int nstates = 0;
|
|
||||||
for (auto i = 0; i < n_models; ++i) {
|
|
||||||
nstates += models[i]->getNumberOfStates();
|
|
||||||
}
|
|
||||||
return nstates;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
#ifndef ENSEMBLE_H
|
|
||||||
#define ENSEMBLE_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include "Classifier.h"
|
|
||||||
#include "BayesMetrics.h"
|
|
||||||
#include "bayesnetUtils.h"
|
|
||||||
using namespace std;
|
|
||||||
using namespace torch;
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
class Ensemble : public Classifier {
|
|
||||||
private:
|
|
||||||
Ensemble& build(vector<string>& features, string className, map<string, vector<int>>& states);
|
|
||||||
protected:
|
|
||||||
unsigned n_models;
|
|
||||||
vector<unique_ptr<Classifier>> models;
|
|
||||||
vector<double> significanceModels;
|
|
||||||
void trainModel(const torch::Tensor& weights) override;
|
|
||||||
vector<int> voting(Tensor& y_pred);
|
|
||||||
public:
|
|
||||||
Ensemble();
|
|
||||||
virtual ~Ensemble() = default;
|
|
||||||
Tensor predict(Tensor& X) override;
|
|
||||||
vector<int> predict(vector<vector<int>>& X) override;
|
|
||||||
float score(Tensor& X, Tensor& y) override;
|
|
||||||
float score(vector<vector<int>>& X, vector<int>& y) override;
|
|
||||||
int getNumberOfNodes() const override;
|
|
||||||
int getNumberOfEdges() const override;
|
|
||||||
int getNumberOfStates() const override;
|
|
||||||
vector<string> show() const override;
|
|
||||||
vector<string> graph(const string& title) const override;
|
|
||||||
vector<string> topological_order() override
|
|
||||||
{
|
|
||||||
return vector<string>();
|
|
||||||
}
|
|
||||||
void dump_cpt() const override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,19 +0,0 @@
|
|||||||
#ifndef KDBLD_H
|
|
||||||
#define KDBLD_H
|
|
||||||
#include "KDB.h"
|
|
||||||
#include "Proposal.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
class KDBLd : public KDB, public Proposal {
|
|
||||||
private:
|
|
||||||
public:
|
|
||||||
explicit KDBLd(int k);
|
|
||||||
virtual ~KDBLd() = default;
|
|
||||||
KDBLd& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
|
||||||
vector<string> graph(const string& name = "KDB") const override;
|
|
||||||
Tensor predict(Tensor& X) override;
|
|
||||||
static inline string version() { return "0.0.1"; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif // !KDBLD_H
|
|
@@ -1,35 +0,0 @@
|
|||||||
#ifndef MST_H
|
|
||||||
#define MST_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
using namespace torch;
|
|
||||||
class MST {
|
|
||||||
private:
|
|
||||||
Tensor weights;
|
|
||||||
vector<string> features;
|
|
||||||
int root = 0;
|
|
||||||
public:
|
|
||||||
MST() = default;
|
|
||||||
MST(const vector<string>& features, const Tensor& weights, const int root);
|
|
||||||
vector<pair<int, int>> maximumSpanningTree();
|
|
||||||
};
|
|
||||||
class Graph {
|
|
||||||
private:
|
|
||||||
int V; // number of nodes in graph
|
|
||||||
vector <pair<float, pair<int, int>>> G; // vector for graph
|
|
||||||
vector <pair<float, pair<int, int>>> T; // vector for mst
|
|
||||||
vector<int> parent;
|
|
||||||
public:
|
|
||||||
explicit Graph(int V);
|
|
||||||
void addEdge(int u, int v, float wt);
|
|
||||||
int find_set(int i);
|
|
||||||
void union_set(int u, int v);
|
|
||||||
void kruskal_algorithm();
|
|
||||||
void display_mst();
|
|
||||||
vector <pair<float, pair<int, int>>> get_mst() { return T; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,58 +0,0 @@
|
|||||||
#ifndef NETWORK_H
|
|
||||||
#define NETWORK_H
|
|
||||||
#include "Node.h"
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
class Network {
|
|
||||||
private:
|
|
||||||
map<string, unique_ptr<Node>> nodes;
|
|
||||||
bool fitted;
|
|
||||||
float maxThreads = 0.95;
|
|
||||||
int classNumStates;
|
|
||||||
vector<string> features; // Including classname
|
|
||||||
string className;
|
|
||||||
double laplaceSmoothing;
|
|
||||||
torch::Tensor samples; // nxm tensor used to fit the model
|
|
||||||
bool isCyclic(const std::string&, std::unordered_set<std::string>&, std::unordered_set<std::string>&);
|
|
||||||
vector<double> predict_sample(const vector<int>&);
|
|
||||||
vector<double> predict_sample(const torch::Tensor&);
|
|
||||||
vector<double> exactInference(map<string, int>&);
|
|
||||||
double computeFactor(map<string, int>&);
|
|
||||||
void completeFit(const map<string, vector<int>>& states, const torch::Tensor& weights);
|
|
||||||
void checkFitData(int n_features, int n_samples, int n_samples_y, const vector<string>& featureNames, const string& className, const map<string, vector<int>>& states, const torch::Tensor& weights);
|
|
||||||
void setStates(const map<string, vector<int>>&);
|
|
||||||
public:
|
|
||||||
Network();
|
|
||||||
explicit Network(float);
|
|
||||||
explicit Network(Network&);
|
|
||||||
torch::Tensor& getSamples();
|
|
||||||
float getmaxThreads();
|
|
||||||
void addNode(const string&);
|
|
||||||
void addEdge(const string&, const string&);
|
|
||||||
map<string, std::unique_ptr<Node>>& getNodes();
|
|
||||||
vector<string> getFeatures() const;
|
|
||||||
int getStates() const;
|
|
||||||
vector<pair<string, string>> getEdges() const;
|
|
||||||
int getNumEdges() const;
|
|
||||||
int getClassNumStates() const;
|
|
||||||
string getClassName() const;
|
|
||||||
void fit(const vector<vector<int>>& input_data, const vector<int>& labels, const vector<float>& weights, const vector<string>& featureNames, const string& className, const map<string, vector<int>>& states);
|
|
||||||
void fit(const torch::Tensor& X, const torch::Tensor& y, const torch::Tensor& weights, const vector<string>& featureNames, const string& className, const map<string, vector<int>>& states);
|
|
||||||
void fit(const torch::Tensor& samples, const torch::Tensor& weights, const vector<string>& featureNames, const string& className, const map<string, vector<int>>& states);
|
|
||||||
vector<int> predict(const vector<vector<int>>&); // Return mx1 vector of predictions
|
|
||||||
torch::Tensor predict(const torch::Tensor&); // Return mx1 tensor of predictions
|
|
||||||
torch::Tensor predict_tensor(const torch::Tensor& samples, const bool proba);
|
|
||||||
vector<vector<double>> predict_proba(const vector<vector<int>>&); // Return mxn vector of probabilities
|
|
||||||
torch::Tensor predict_proba(const torch::Tensor&); // Return mxn tensor of probabilities
|
|
||||||
double score(const vector<vector<int>>&, const vector<int>&);
|
|
||||||
vector<string> topological_sort();
|
|
||||||
vector<string> show() const;
|
|
||||||
vector<string> graph(const string& title) const; // Returns a vector of strings representing the graph in graphviz format
|
|
||||||
void initialize();
|
|
||||||
void dump_cpt() const;
|
|
||||||
inline string version() { return "0.1.0"; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,37 +0,0 @@
|
|||||||
#ifndef NODE_H
|
|
||||||
#define NODE_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
class Node {
|
|
||||||
private:
|
|
||||||
string name;
|
|
||||||
vector<Node*> parents;
|
|
||||||
vector<Node*> children;
|
|
||||||
int numStates; // number of states of the variable
|
|
||||||
torch::Tensor cpTable; // Order of indices is 0-> node variable, 1-> 1st parent, 2-> 2nd parent, ...
|
|
||||||
vector<int64_t> dimensions; // dimensions of the cpTable
|
|
||||||
public:
|
|
||||||
vector<pair<string, string>> combinations(const vector<string>&);
|
|
||||||
explicit Node(const string&);
|
|
||||||
void clear();
|
|
||||||
void addParent(Node*);
|
|
||||||
void addChild(Node*);
|
|
||||||
void removeParent(Node*);
|
|
||||||
void removeChild(Node*);
|
|
||||||
string getName() const;
|
|
||||||
vector<Node*>& getParents();
|
|
||||||
vector<Node*>& getChildren();
|
|
||||||
torch::Tensor& getCPT();
|
|
||||||
void computeCPT(const torch::Tensor& dataset, const vector<string>& features, const double laplaceSmoothing, const torch::Tensor& weights);
|
|
||||||
int getNumStates() const;
|
|
||||||
void setNumStates(int);
|
|
||||||
unsigned minFill();
|
|
||||||
vector<string> graph(const string& clasName); // Returns a vector of strings representing the graph in graphviz format
|
|
||||||
float getFactorValue(map<string, int>&);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,29 +0,0 @@
|
|||||||
#ifndef PROPOSAL_H
|
|
||||||
#define PROPOSAL_H
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include "Network.h"
|
|
||||||
#include "CPPFImdlp.h"
|
|
||||||
#include "Classifier.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
class Proposal {
|
|
||||||
public:
|
|
||||||
Proposal(torch::Tensor& pDataset, vector<string>& features_, string& className_);
|
|
||||||
virtual ~Proposal();
|
|
||||||
protected:
|
|
||||||
torch::Tensor prepareX(torch::Tensor& X);
|
|
||||||
map<string, vector<int>> localDiscretizationProposal(const map<string, vector<int>>& states, Network& model);
|
|
||||||
map<string, vector<int>> fit_local_discretization(const torch::Tensor& y);
|
|
||||||
torch::Tensor Xf; // X continuous nxm tensor
|
|
||||||
torch::Tensor y; // y discrete nx1 tensor
|
|
||||||
map<string, mdlp::CPPFImdlp*> discretizers;
|
|
||||||
private:
|
|
||||||
torch::Tensor& pDataset; // (n+1)xm tensor
|
|
||||||
vector<string>& pFeatures;
|
|
||||||
string& pClassName;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,19 +0,0 @@
|
|||||||
#ifndef SPODELD_H
|
|
||||||
#define SPODELD_H
|
|
||||||
#include "SPODE.h"
|
|
||||||
#include "Proposal.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
class SPODELd : public SPODE, public Proposal {
|
|
||||||
public:
|
|
||||||
explicit SPODELd(int root);
|
|
||||||
virtual ~SPODELd() = default;
|
|
||||||
SPODELd& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
|
||||||
SPODELd& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
|
||||||
vector<string> graph(const string& name = "SPODE") const override;
|
|
||||||
Tensor predict(Tensor& X) override;
|
|
||||||
static inline string version() { return "0.0.1"; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif // !SPODELD_H
|
|
@@ -1,19 +0,0 @@
|
|||||||
#ifndef TANLD_H
|
|
||||||
#define TANLD_H
|
|
||||||
#include "TAN.h"
|
|
||||||
#include "Proposal.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
class TANLd : public TAN, public Proposal {
|
|
||||||
private:
|
|
||||||
public:
|
|
||||||
TANLd();
|
|
||||||
virtual ~TANLd() = default;
|
|
||||||
TANLd& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
|
||||||
vector<string> graph(const string& name = "TAN") const override;
|
|
||||||
Tensor predict(Tensor& X) override;
|
|
||||||
static inline string version() { return "0.0.1"; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif // !TANLD_H
|
|
@@ -1,27 +0,0 @@
|
|||||||
|
|
||||||
#include "bayesnetUtils.h"
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
using namespace torch;
|
|
||||||
// Return the indices in descending order
|
|
||||||
vector<int> argsort(vector<double>& nums)
|
|
||||||
{
|
|
||||||
int n = nums.size();
|
|
||||||
vector<int> indices(n);
|
|
||||||
iota(indices.begin(), indices.end(), 0);
|
|
||||||
sort(indices.begin(), indices.end(), [&nums](int i, int j) {return nums[i] > nums[j];});
|
|
||||||
return indices;
|
|
||||||
}
|
|
||||||
vector<vector<int>> tensorToVector(Tensor& tensor)
|
|
||||||
{
|
|
||||||
// convert mxn tensor to nxm vector
|
|
||||||
vector<vector<int>> result;
|
|
||||||
// Iterate over cols
|
|
||||||
for (int i = 0; i < tensor.size(1); ++i) {
|
|
||||||
auto col_tensor = tensor.index({ "...", i });
|
|
||||||
auto col = vector<int>(col_tensor.data_ptr<int>(), col_tensor.data_ptr<int>() + tensor.size(0));
|
|
||||||
result.push_back(col);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
#ifndef BAYESNET_UTILS_H
|
|
||||||
#define BAYESNET_UTILS_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <vector>
|
|
||||||
namespace bayesnet {
|
|
||||||
using namespace std;
|
|
||||||
using namespace torch;
|
|
||||||
vector<int> argsort(vector<double>& nums);
|
|
||||||
vector<vector<int>> tensorToVector(Tensor& tensor);
|
|
||||||
}
|
|
||||||
#endif //BAYESNET_UTILS_H
|
|
18
src/CMakeLists.txt
Normal file
18
src/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
include_directories(
|
||||||
|
${BayesNet_SOURCE_DIR}/lib/mdlp
|
||||||
|
${BayesNet_SOURCE_DIR}/lib/Files
|
||||||
|
${BayesNet_SOURCE_DIR}/lib/folding
|
||||||
|
${BayesNet_SOURCE_DIR}/lib/json/include
|
||||||
|
${BayesNet_SOURCE_DIR}/src
|
||||||
|
${BayesNet_SOURCE_DIR}/src/feature_selection
|
||||||
|
${BayesNet_SOURCE_DIR}/src/bayesian_network
|
||||||
|
${BayesNet_SOURCE_DIR}/src/classifiers
|
||||||
|
${BayesNet_SOURCE_DIR}/src/ensembles
|
||||||
|
${BayesNet_SOURCE_DIR}/src/utils
|
||||||
|
${CMAKE_BINARY_DIR}/configured_files/include
|
||||||
|
)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE Sources "*.cc")
|
||||||
|
|
||||||
|
add_library(BayesNet ${Sources})
|
||||||
|
target_link_libraries(BayesNet mdlp "${TORCH_LIBRARIES}")
|
@@ -1,10 +0,0 @@
|
|||||||
#ifndef BESTRESULT_H
|
|
||||||
#define BESTRESULT_H
|
|
||||||
#include <string>
|
|
||||||
class BestResult {
|
|
||||||
public:
|
|
||||||
static std::string title() { return "STree_default (linear-ovo)"; }
|
|
||||||
static double score() { return 22.109799; }
|
|
||||||
static std::string scoreName() { return "accuracy"; }
|
|
||||||
};
|
|
||||||
#endif
|
|
@@ -1,12 +0,0 @@
|
|||||||
include_directories(${BayesNet_SOURCE_DIR}/src/BayesNet)
|
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/src/Platform)
|
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/Files)
|
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/mdlp)
|
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/argparse/include)
|
|
||||||
include_directories(${BayesNet_SOURCE_DIR}/lib/json/include)
|
|
||||||
add_executable(main main.cc Folding.cc platformUtils.cc Experiment.cc Datasets.cc Models.cc Report.cc)
|
|
||||||
add_executable(manage manage.cc Results.cc Report.cc)
|
|
||||||
add_executable(list list.cc platformUtils Datasets.cc)
|
|
||||||
target_link_libraries(main BayesNet ArffFiles mdlp "${TORCH_LIBRARIES}")
|
|
||||||
target_link_libraries(manage "${TORCH_LIBRARIES}")
|
|
||||||
target_link_libraries(list ArffFiles mdlp "${TORCH_LIBRARIES}")
|
|
@@ -1,14 +0,0 @@
|
|||||||
#ifndef COLORS_H
|
|
||||||
#define COLORS_H
|
|
||||||
class Colors {
|
|
||||||
public:
|
|
||||||
static std::string MAGENTA() { return "\033[1;35m"; }
|
|
||||||
static std::string BLUE() { return "\033[1;34m"; }
|
|
||||||
static std::string CYAN() { return "\033[1;36m"; }
|
|
||||||
static std::string GREEN() { return "\033[1;32m"; }
|
|
||||||
static std::string YELLOW() { return "\033[1;33m"; }
|
|
||||||
static std::string RED() { return "\033[1;31m"; }
|
|
||||||
static std::string WHITE() { return "\033[1;37m"; }
|
|
||||||
static std::string RESET() { return "\033[0m"; }
|
|
||||||
};
|
|
||||||
#endif // COLORS_H
|
|
@@ -1,266 +0,0 @@
|
|||||||
#include "Datasets.h"
|
|
||||||
#include "platformUtils.h"
|
|
||||||
#include "ArffFiles.h"
|
|
||||||
namespace platform {
|
|
||||||
void Datasets::load()
|
|
||||||
{
|
|
||||||
ifstream catalog(path + "/all.txt");
|
|
||||||
if (catalog.is_open()) {
|
|
||||||
string line;
|
|
||||||
while (getline(catalog, line)) {
|
|
||||||
vector<string> tokens = split(line, ',');
|
|
||||||
string name = tokens[0];
|
|
||||||
string className = tokens[1];
|
|
||||||
datasets[name] = make_unique<Dataset>(path, name, className, discretize, fileType);
|
|
||||||
}
|
|
||||||
catalog.close();
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Unable to open catalog file. [" + path + "/all.txt" + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vector<string> Datasets::getNames()
|
|
||||||
{
|
|
||||||
vector<string> result;
|
|
||||||
transform(datasets.begin(), datasets.end(), back_inserter(result), [](const auto& d) { return d.first; });
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
vector<string> Datasets::getFeatures(const string& name) const
|
|
||||||
{
|
|
||||||
if (datasets.at(name)->isLoaded()) {
|
|
||||||
return datasets.at(name)->getFeatures();
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map<string, vector<int>> Datasets::getStates(const string& name) const
|
|
||||||
{
|
|
||||||
if (datasets.at(name)->isLoaded()) {
|
|
||||||
return datasets.at(name)->getStates();
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Datasets::loadDataset(const string& name) const
|
|
||||||
{
|
|
||||||
if (datasets.at(name)->isLoaded()) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
datasets.at(name)->load();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
string Datasets::getClassName(const string& name) const
|
|
||||||
{
|
|
||||||
if (datasets.at(name)->isLoaded()) {
|
|
||||||
return datasets.at(name)->getClassName();
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int Datasets::getNSamples(const string& name) const
|
|
||||||
{
|
|
||||||
if (datasets.at(name)->isLoaded()) {
|
|
||||||
return datasets.at(name)->getNSamples();
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int Datasets::getNClasses(const string& name)
|
|
||||||
{
|
|
||||||
if (datasets.at(name)->isLoaded()) {
|
|
||||||
auto className = datasets.at(name)->getClassName();
|
|
||||||
if (discretize) {
|
|
||||||
auto states = getStates(name);
|
|
||||||
return states.at(className).size();
|
|
||||||
}
|
|
||||||
auto [Xv, yv] = getVectors(name);
|
|
||||||
return *max_element(yv.begin(), yv.end()) + 1;
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vector<int> Datasets::getClassesCounts(const string& name) const
|
|
||||||
{
|
|
||||||
if (datasets.at(name)->isLoaded()) {
|
|
||||||
auto [Xv, yv] = datasets.at(name)->getVectors();
|
|
||||||
vector<int> counts(*max_element(yv.begin(), yv.end()) + 1);
|
|
||||||
for (auto y : yv) {
|
|
||||||
counts[y]++;
|
|
||||||
}
|
|
||||||
return counts;
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pair<vector<vector<float>>&, vector<int>&> Datasets::getVectors(const string& name)
|
|
||||||
{
|
|
||||||
if (!datasets[name]->isLoaded()) {
|
|
||||||
datasets[name]->load();
|
|
||||||
}
|
|
||||||
return datasets[name]->getVectors();
|
|
||||||
}
|
|
||||||
pair<vector<vector<int>>&, vector<int>&> Datasets::getVectorsDiscretized(const string& name)
|
|
||||||
{
|
|
||||||
if (!datasets[name]->isLoaded()) {
|
|
||||||
datasets[name]->load();
|
|
||||||
}
|
|
||||||
return datasets[name]->getVectorsDiscretized();
|
|
||||||
}
|
|
||||||
pair<torch::Tensor&, torch::Tensor&> Datasets::getTensors(const string& name)
|
|
||||||
{
|
|
||||||
if (!datasets[name]->isLoaded()) {
|
|
||||||
datasets[name]->load();
|
|
||||||
}
|
|
||||||
return datasets[name]->getTensors();
|
|
||||||
}
|
|
||||||
bool Datasets::isDataset(const string& name) const
|
|
||||||
{
|
|
||||||
return datasets.find(name) != datasets.end();
|
|
||||||
}
|
|
||||||
Dataset::Dataset(const Dataset& dataset) : path(dataset.path), name(dataset.name), className(dataset.className), n_samples(dataset.n_samples), n_features(dataset.n_features), features(dataset.features), states(dataset.states), loaded(dataset.loaded), discretize(dataset.discretize), X(dataset.X), y(dataset.y), Xv(dataset.Xv), Xd(dataset.Xd), yv(dataset.yv), fileType(dataset.fileType)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
string Dataset::getName() const
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
string Dataset::getClassName() const
|
|
||||||
{
|
|
||||||
return className;
|
|
||||||
}
|
|
||||||
vector<string> Dataset::getFeatures() const
|
|
||||||
{
|
|
||||||
if (loaded) {
|
|
||||||
return features;
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int Dataset::getNFeatures() const
|
|
||||||
{
|
|
||||||
if (loaded) {
|
|
||||||
return n_features;
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int Dataset::getNSamples() const
|
|
||||||
{
|
|
||||||
if (loaded) {
|
|
||||||
return n_samples;
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map<string, vector<int>> Dataset::getStates() const
|
|
||||||
{
|
|
||||||
if (loaded) {
|
|
||||||
return states;
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pair<vector<vector<float>>&, vector<int>&> Dataset::getVectors()
|
|
||||||
{
|
|
||||||
if (loaded) {
|
|
||||||
return { Xv, yv };
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pair<vector<vector<int>>&, vector<int>&> Dataset::getVectorsDiscretized()
|
|
||||||
{
|
|
||||||
if (loaded) {
|
|
||||||
return { Xd, yv };
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pair<torch::Tensor&, torch::Tensor&> Dataset::getTensors()
|
|
||||||
{
|
|
||||||
if (loaded) {
|
|
||||||
buildTensors();
|
|
||||||
return { X, y };
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Dataset not loaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Dataset::load_csv()
|
|
||||||
{
|
|
||||||
ifstream file(path + "/" + name + ".csv");
|
|
||||||
if (file.is_open()) {
|
|
||||||
string line;
|
|
||||||
getline(file, line);
|
|
||||||
vector<string> tokens = split(line, ',');
|
|
||||||
features = vector<string>(tokens.begin(), tokens.end() - 1);
|
|
||||||
className = tokens.back();
|
|
||||||
for (auto i = 0; i < features.size(); ++i) {
|
|
||||||
Xv.push_back(vector<float>());
|
|
||||||
}
|
|
||||||
while (getline(file, line)) {
|
|
||||||
tokens = split(line, ',');
|
|
||||||
for (auto i = 0; i < features.size(); ++i) {
|
|
||||||
Xv[i].push_back(stof(tokens[i]));
|
|
||||||
}
|
|
||||||
yv.push_back(stoi(tokens.back()));
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("Unable to open dataset file.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Dataset::computeStates()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < features.size(); ++i) {
|
|
||||||
states[features[i]] = vector<int>(*max_element(Xd[i].begin(), Xd[i].end()) + 1);
|
|
||||||
iota(begin(states[features[i]]), end(states[features[i]]), 0);
|
|
||||||
}
|
|
||||||
states[className] = vector<int>(*max_element(yv.begin(), yv.end()) + 1);
|
|
||||||
iota(begin(states[className]), end(states[className]), 0);
|
|
||||||
}
|
|
||||||
void Dataset::load_arff()
|
|
||||||
{
|
|
||||||
auto arff = ArffFiles();
|
|
||||||
arff.load(path + "/" + name + ".arff", className);
|
|
||||||
// Get Dataset X, y
|
|
||||||
Xv = arff.getX();
|
|
||||||
yv = arff.getY();
|
|
||||||
// Get className & Features
|
|
||||||
className = arff.getClassName();
|
|
||||||
auto attributes = arff.getAttributes();
|
|
||||||
transform(attributes.begin(), attributes.end(), back_inserter(features), [](const auto& attribute) { return attribute.first; });
|
|
||||||
}
|
|
||||||
void Dataset::load()
|
|
||||||
{
|
|
||||||
if (loaded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fileType == CSV) {
|
|
||||||
load_csv();
|
|
||||||
} else if (fileType == ARFF) {
|
|
||||||
load_arff();
|
|
||||||
}
|
|
||||||
if (discretize) {
|
|
||||||
Xd = discretizeDataset(Xv, yv);
|
|
||||||
computeStates();
|
|
||||||
}
|
|
||||||
n_samples = Xv[0].size();
|
|
||||||
n_features = Xv.size();
|
|
||||||
loaded = true;
|
|
||||||
}
|
|
||||||
void Dataset::buildTensors()
|
|
||||||
{
|
|
||||||
if (discretize) {
|
|
||||||
X = torch::zeros({ static_cast<int>(n_features), static_cast<int>(n_samples) }, torch::kInt32);
|
|
||||||
} else {
|
|
||||||
X = torch::zeros({ static_cast<int>(n_features), static_cast<int>(n_samples) }, torch::kFloat32);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < features.size(); ++i) {
|
|
||||||
if (discretize) {
|
|
||||||
X.index_put_({ i, "..." }, torch::tensor(Xd[i], torch::kInt32));
|
|
||||||
} else {
|
|
||||||
X.index_put_({ i, "..." }, torch::tensor(Xv[i], torch::kFloat32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y = torch::tensor(yv, torch::kInt32);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,68 +0,0 @@
|
|||||||
#ifndef DATASETS_H
|
|
||||||
#define DATASETS_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
namespace platform {
|
|
||||||
using namespace std;
|
|
||||||
enum fileType_t { CSV, ARFF };
|
|
||||||
class Dataset {
|
|
||||||
private:
|
|
||||||
string path;
|
|
||||||
string name;
|
|
||||||
fileType_t fileType;
|
|
||||||
string className;
|
|
||||||
int n_samples{ 0 }, n_features{ 0 };
|
|
||||||
vector<string> features;
|
|
||||||
map<string, vector<int>> states;
|
|
||||||
bool loaded;
|
|
||||||
bool discretize;
|
|
||||||
torch::Tensor X, y;
|
|
||||||
vector<vector<float>> Xv;
|
|
||||||
vector<vector<int>> Xd;
|
|
||||||
vector<int> yv;
|
|
||||||
void buildTensors();
|
|
||||||
void load_csv();
|
|
||||||
void load_arff();
|
|
||||||
void computeStates();
|
|
||||||
public:
|
|
||||||
Dataset(const string& path, const string& name, const string& className, bool discretize, fileType_t fileType) : path(path), name(name), className(className), discretize(discretize), loaded(false), fileType(fileType) {};
|
|
||||||
explicit Dataset(const Dataset&);
|
|
||||||
string getName() const;
|
|
||||||
string getClassName() const;
|
|
||||||
vector<string> getFeatures() const;
|
|
||||||
map<string, vector<int>> getStates() const;
|
|
||||||
pair<vector<vector<float>>&, vector<int>&> getVectors();
|
|
||||||
pair<vector<vector<int>>&, vector<int>&> getVectorsDiscretized();
|
|
||||||
pair<torch::Tensor&, torch::Tensor&> getTensors();
|
|
||||||
int getNFeatures() const;
|
|
||||||
int getNSamples() const;
|
|
||||||
void load();
|
|
||||||
const bool inline isLoaded() const { return loaded; };
|
|
||||||
};
|
|
||||||
class Datasets {
|
|
||||||
private:
|
|
||||||
string path;
|
|
||||||
fileType_t fileType;
|
|
||||||
map<string, unique_ptr<Dataset>> datasets;
|
|
||||||
bool discretize;
|
|
||||||
void load(); // Loads the list of datasets
|
|
||||||
public:
|
|
||||||
explicit Datasets(const string& path, bool discretize = false, fileType_t fileType = ARFF) : path(path), discretize(discretize), fileType(fileType) { load(); };
|
|
||||||
vector<string> getNames();
|
|
||||||
vector<string> getFeatures(const string& name) const;
|
|
||||||
int getNSamples(const string& name) const;
|
|
||||||
string getClassName(const string& name) const;
|
|
||||||
int getNClasses(const string& name);
|
|
||||||
vector<int> getClassesCounts(const string& name) const;
|
|
||||||
map<string, vector<int>> getStates(const string& name) const;
|
|
||||||
pair<vector<vector<float>>&, vector<int>&> getVectors(const string& name);
|
|
||||||
pair<vector<vector<int>>&, vector<int>&> getVectorsDiscretized(const string& name);
|
|
||||||
pair<torch::Tensor&, torch::Tensor&> getTensors(const string& name);
|
|
||||||
bool isDataset(const string& name) const;
|
|
||||||
void loadDataset(const string& name) const;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,62 +0,0 @@
|
|||||||
#ifndef DOTENV_H
|
|
||||||
#define DOTENV_H
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include "platformUtils.h"
|
|
||||||
namespace platform {
|
|
||||||
class DotEnv {
|
|
||||||
private:
|
|
||||||
std::map<std::string, std::string> env;
|
|
||||||
std::string trim(const std::string& str)
|
|
||||||
{
|
|
||||||
std::string result = str;
|
|
||||||
result.erase(result.begin(), std::find_if(result.begin(), result.end(), [](int ch) {
|
|
||||||
return !std::isspace(ch);
|
|
||||||
}));
|
|
||||||
result.erase(std::find_if(result.rbegin(), result.rend(), [](int ch) {
|
|
||||||
return !std::isspace(ch);
|
|
||||||
}).base(), result.end());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
DotEnv()
|
|
||||||
{
|
|
||||||
std::ifstream file(".env");
|
|
||||||
if (!file.is_open()) {
|
|
||||||
std::cerr << "File .env not found" << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
std::string line;
|
|
||||||
while (std::getline(file, line)) {
|
|
||||||
line = trim(line);
|
|
||||||
if (line.empty() || line[0] == '#') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::istringstream iss(line);
|
|
||||||
std::string key, value;
|
|
||||||
if (std::getline(iss, key, '=') && std::getline(iss, value)) {
|
|
||||||
env[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::string get(const std::string& key)
|
|
||||||
{
|
|
||||||
return env[key];
|
|
||||||
}
|
|
||||||
std::vector<int> getSeeds()
|
|
||||||
{
|
|
||||||
auto seeds = std::vector<int>();
|
|
||||||
auto seeds_str = env["seeds"];
|
|
||||||
seeds_str = trim(seeds_str);
|
|
||||||
seeds_str = seeds_str.substr(1, seeds_str.size() - 2);
|
|
||||||
auto seeds_str_split = split(seeds_str, ',');
|
|
||||||
transform(seeds_str_split.begin(), seeds_str_split.end(), back_inserter(seeds), [](const std::string& str) {
|
|
||||||
return stoi(str);
|
|
||||||
});
|
|
||||||
return seeds;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,184 +0,0 @@
|
|||||||
#include "Experiment.h"
|
|
||||||
#include "Datasets.h"
|
|
||||||
#include "Models.h"
|
|
||||||
#include "Report.h"
|
|
||||||
|
|
||||||
namespace platform {
|
|
||||||
using json = nlohmann::json;
|
|
||||||
string get_date()
|
|
||||||
{
|
|
||||||
time_t rawtime;
|
|
||||||
tm* timeinfo;
|
|
||||||
time(&rawtime);
|
|
||||||
timeinfo = std::localtime(&rawtime);
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << std::put_time(timeinfo, "%Y-%m-%d");
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
string get_time()
|
|
||||||
{
|
|
||||||
time_t rawtime;
|
|
||||||
tm* timeinfo;
|
|
||||||
time(&rawtime);
|
|
||||||
timeinfo = std::localtime(&rawtime);
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << std::put_time(timeinfo, "%H:%M:%S");
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
string Experiment::get_file_name()
|
|
||||||
{
|
|
||||||
string result = "results_" + score_name + "_" + model + "_" + platform + "_" + get_date() + "_" + get_time() + "_" + (stratified ? "1" : "0") + ".json";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
json Experiment::build_json()
|
|
||||||
{
|
|
||||||
json result;
|
|
||||||
result["title"] = title;
|
|
||||||
result["date"] = get_date();
|
|
||||||
result["time"] = get_time();
|
|
||||||
result["model"] = model;
|
|
||||||
result["version"] = model_version;
|
|
||||||
result["platform"] = platform;
|
|
||||||
result["score_name"] = score_name;
|
|
||||||
result["language"] = language;
|
|
||||||
result["language_version"] = language_version;
|
|
||||||
result["discretized"] = discretized;
|
|
||||||
result["stratified"] = stratified;
|
|
||||||
result["folds"] = nfolds;
|
|
||||||
result["seeds"] = randomSeeds;
|
|
||||||
result["duration"] = duration;
|
|
||||||
result["results"] = json::array();
|
|
||||||
for (const auto& r : results) {
|
|
||||||
json j;
|
|
||||||
j["dataset"] = r.getDataset();
|
|
||||||
j["hyperparameters"] = r.getHyperparameters();
|
|
||||||
j["samples"] = r.getSamples();
|
|
||||||
j["features"] = r.getFeatures();
|
|
||||||
j["classes"] = r.getClasses();
|
|
||||||
j["score_train"] = r.getScoreTrain();
|
|
||||||
j["score_test"] = r.getScoreTest();
|
|
||||||
j["score"] = r.getScoreTest();
|
|
||||||
j["score_std"] = r.getScoreTestStd();
|
|
||||||
j["score_train_std"] = r.getScoreTrainStd();
|
|
||||||
j["score_test_std"] = r.getScoreTestStd();
|
|
||||||
j["train_time"] = r.getTrainTime();
|
|
||||||
j["train_time_std"] = r.getTrainTimeStd();
|
|
||||||
j["test_time"] = r.getTestTime();
|
|
||||||
j["test_time_std"] = r.getTestTimeStd();
|
|
||||||
j["time"] = r.getTestTime() + r.getTrainTime();
|
|
||||||
j["time_std"] = r.getTestTimeStd() + r.getTrainTimeStd();
|
|
||||||
j["scores_train"] = r.getScoresTrain();
|
|
||||||
j["scores_test"] = r.getScoresTest();
|
|
||||||
j["times_train"] = r.getTimesTrain();
|
|
||||||
j["times_test"] = r.getTimesTest();
|
|
||||||
j["nodes"] = r.getNodes();
|
|
||||||
j["leaves"] = r.getLeaves();
|
|
||||||
j["depth"] = r.getDepth();
|
|
||||||
result["results"].push_back(j);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
void Experiment::save(const string& path)
|
|
||||||
{
|
|
||||||
json data = build_json();
|
|
||||||
ofstream file(path + "/" + get_file_name());
|
|
||||||
file << data;
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Experiment::report()
|
|
||||||
{
|
|
||||||
json data = build_json();
|
|
||||||
Report report(data);
|
|
||||||
report.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Experiment::show()
|
|
||||||
{
|
|
||||||
json data = build_json();
|
|
||||||
cout << data.dump(4) << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Experiment::go(vector<string> filesToProcess, const string& path)
|
|
||||||
{
|
|
||||||
cout << "*** Starting experiment: " << title << " ***" << endl;
|
|
||||||
for (auto fileName : filesToProcess) {
|
|
||||||
cout << "- " << setw(20) << left << fileName << " " << right << flush;
|
|
||||||
cross_validation(path, fileName);
|
|
||||||
cout << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Experiment::cross_validation(const string& path, const string& fileName)
|
|
||||||
{
|
|
||||||
auto datasets = platform::Datasets(path, discretized, platform::ARFF);
|
|
||||||
// Get dataset
|
|
||||||
auto [X, y] = datasets.getTensors(fileName);
|
|
||||||
auto states = datasets.getStates(fileName);
|
|
||||||
auto features = datasets.getFeatures(fileName);
|
|
||||||
auto samples = datasets.getNSamples(fileName);
|
|
||||||
auto className = datasets.getClassName(fileName);
|
|
||||||
cout << " (" << setw(5) << samples << "," << setw(3) << features.size() << ") " << flush;
|
|
||||||
// Prepare Result
|
|
||||||
auto result = Result();
|
|
||||||
auto [values, counts] = at::_unique(y);
|
|
||||||
result.setSamples(X.size(1)).setFeatures(X.size(0)).setClasses(values.size(0));
|
|
||||||
int nResults = nfolds * static_cast<int>(randomSeeds.size());
|
|
||||||
auto accuracy_test = torch::zeros({ nResults }, torch::kFloat64);
|
|
||||||
auto accuracy_train = torch::zeros({ nResults }, torch::kFloat64);
|
|
||||||
auto train_time = torch::zeros({ nResults }, torch::kFloat64);
|
|
||||||
auto test_time = torch::zeros({ nResults }, torch::kFloat64);
|
|
||||||
auto nodes = torch::zeros({ nResults }, torch::kFloat64);
|
|
||||||
auto edges = torch::zeros({ nResults }, torch::kFloat64);
|
|
||||||
auto num_states = torch::zeros({ nResults }, torch::kFloat64);
|
|
||||||
Timer train_timer, test_timer;
|
|
||||||
int item = 0;
|
|
||||||
for (auto seed : randomSeeds) {
|
|
||||||
cout << "(" << seed << ") doing Fold: " << flush;
|
|
||||||
Fold* fold;
|
|
||||||
if (stratified)
|
|
||||||
fold = new StratifiedKFold(nfolds, y, seed);
|
|
||||||
else
|
|
||||||
fold = new KFold(nfolds, y.size(0), seed);
|
|
||||||
for (int nfold = 0; nfold < nfolds; nfold++) {
|
|
||||||
auto clf = Models::instance()->create(model);
|
|
||||||
setModelVersion(clf->getVersion());
|
|
||||||
train_timer.start();
|
|
||||||
auto [train, test] = fold->getFold(nfold);
|
|
||||||
auto train_t = torch::tensor(train);
|
|
||||||
auto test_t = torch::tensor(test);
|
|
||||||
auto X_train = X.index({ "...", train_t });
|
|
||||||
auto y_train = y.index({ train_t });
|
|
||||||
auto X_test = X.index({ "...", test_t });
|
|
||||||
auto y_test = y.index({ test_t });
|
|
||||||
cout << nfold + 1 << ", " << flush;
|
|
||||||
clf->fit(X_train, y_train, features, className, states);
|
|
||||||
nodes[item] = clf->getNumberOfNodes();
|
|
||||||
edges[item] = clf->getNumberOfEdges();
|
|
||||||
num_states[item] = clf->getNumberOfStates();
|
|
||||||
train_time[item] = train_timer.getDuration();
|
|
||||||
auto accuracy_train_value = clf->score(X_train, y_train);
|
|
||||||
test_timer.start();
|
|
||||||
auto accuracy_test_value = clf->score(X_test, y_test);
|
|
||||||
test_time[item] = test_timer.getDuration();
|
|
||||||
accuracy_train[item] = accuracy_train_value;
|
|
||||||
accuracy_test[item] = accuracy_test_value;
|
|
||||||
// Store results and times in vector
|
|
||||||
result.addScoreTrain(accuracy_train_value);
|
|
||||||
result.addScoreTest(accuracy_test_value);
|
|
||||||
result.addTimeTrain(train_time[item].item<double>());
|
|
||||||
result.addTimeTest(test_time[item].item<double>());
|
|
||||||
item++;
|
|
||||||
}
|
|
||||||
cout << "end. " << flush;
|
|
||||||
delete fold;
|
|
||||||
}
|
|
||||||
result.setScoreTest(torch::mean(accuracy_test).item<double>()).setScoreTrain(torch::mean(accuracy_train).item<double>());
|
|
||||||
result.setScoreTestStd(torch::std(accuracy_test).item<double>()).setScoreTrainStd(torch::std(accuracy_train).item<double>());
|
|
||||||
result.setTrainTime(torch::mean(train_time).item<double>()).setTestTime(torch::mean(test_time).item<double>());
|
|
||||||
result.setNodes(torch::mean(nodes).item<double>()).setLeaves(torch::mean(edges).item<double>()).setDepth(torch::mean(num_states).item<double>());
|
|
||||||
result.setDataset(fileName);
|
|
||||||
addResult(result);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,114 +0,0 @@
|
|||||||
#ifndef EXPERIMENT_H
|
|
||||||
#define EXPERIMENT_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <string>
|
|
||||||
#include <chrono>
|
|
||||||
#include "Folding.h"
|
|
||||||
#include "BaseClassifier.h"
|
|
||||||
#include "TAN.h"
|
|
||||||
#include "KDB.h"
|
|
||||||
#include "AODE.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
namespace platform {
|
|
||||||
using json = nlohmann::json;
|
|
||||||
class Timer {
|
|
||||||
private:
|
|
||||||
chrono::high_resolution_clock::time_point begin;
|
|
||||||
public:
|
|
||||||
Timer() = default;
|
|
||||||
~Timer() = default;
|
|
||||||
void start() { begin = chrono::high_resolution_clock::now(); }
|
|
||||||
double getDuration()
|
|
||||||
{
|
|
||||||
chrono::high_resolution_clock::time_point end = chrono::high_resolution_clock::now();
|
|
||||||
chrono::duration<double> time_span = chrono::duration_cast<chrono::duration<double>>(end - begin);
|
|
||||||
return time_span.count();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
class Result {
|
|
||||||
private:
|
|
||||||
string dataset, hyperparameters, model_version;
|
|
||||||
int samples{ 0 }, features{ 0 }, classes{ 0 };
|
|
||||||
double score_train{ 0 }, score_test{ 0 }, score_train_std{ 0 }, score_test_std{ 0 }, train_time{ 0 }, train_time_std{ 0 }, test_time{ 0 }, test_time_std{ 0 };
|
|
||||||
float nodes{ 0 }, leaves{ 0 }, depth{ 0 };
|
|
||||||
vector<double> scores_train, scores_test, times_train, times_test;
|
|
||||||
public:
|
|
||||||
Result() = default;
|
|
||||||
Result& setDataset(const string& dataset) { this->dataset = dataset; return *this; }
|
|
||||||
Result& setHyperparameters(const string& hyperparameters) { this->hyperparameters = hyperparameters; return *this; }
|
|
||||||
Result& setSamples(int samples) { this->samples = samples; return *this; }
|
|
||||||
Result& setFeatures(int features) { this->features = features; return *this; }
|
|
||||||
Result& setClasses(int classes) { this->classes = classes; return *this; }
|
|
||||||
Result& setScoreTrain(double score) { this->score_train = score; return *this; }
|
|
||||||
Result& setScoreTest(double score) { this->score_test = score; return *this; }
|
|
||||||
Result& setScoreTrainStd(double score_std) { this->score_train_std = score_std; return *this; }
|
|
||||||
Result& setScoreTestStd(double score_std) { this->score_test_std = score_std; return *this; }
|
|
||||||
Result& setTrainTime(double train_time) { this->train_time = train_time; return *this; }
|
|
||||||
Result& setTrainTimeStd(double train_time_std) { this->train_time_std = train_time_std; return *this; }
|
|
||||||
Result& setTestTime(double test_time) { this->test_time = test_time; return *this; }
|
|
||||||
Result& setTestTimeStd(double test_time_std) { this->test_time_std = test_time_std; return *this; }
|
|
||||||
Result& setNodes(float nodes) { this->nodes = nodes; return *this; }
|
|
||||||
Result& setLeaves(float leaves) { this->leaves = leaves; return *this; }
|
|
||||||
Result& setDepth(float depth) { this->depth = depth; return *this; }
|
|
||||||
Result& addScoreTrain(double score) { scores_train.push_back(score); return *this; }
|
|
||||||
Result& addScoreTest(double score) { scores_test.push_back(score); return *this; }
|
|
||||||
Result& addTimeTrain(double time) { times_train.push_back(time); return *this; }
|
|
||||||
Result& addTimeTest(double time) { times_test.push_back(time); return *this; }
|
|
||||||
const float get_score_train() const { return score_train; }
|
|
||||||
float get_score_test() { return score_test; }
|
|
||||||
const string& getDataset() const { return dataset; }
|
|
||||||
const string& getHyperparameters() const { return hyperparameters; }
|
|
||||||
const int getSamples() const { return samples; }
|
|
||||||
const int getFeatures() const { return features; }
|
|
||||||
const int getClasses() const { return classes; }
|
|
||||||
const double getScoreTrain() const { return score_train; }
|
|
||||||
const double getScoreTest() const { return score_test; }
|
|
||||||
const double getScoreTrainStd() const { return score_train_std; }
|
|
||||||
const double getScoreTestStd() const { return score_test_std; }
|
|
||||||
const double getTrainTime() const { return train_time; }
|
|
||||||
const double getTrainTimeStd() const { return train_time_std; }
|
|
||||||
const double getTestTime() const { return test_time; }
|
|
||||||
const double getTestTimeStd() const { return test_time_std; }
|
|
||||||
const float getNodes() const { return nodes; }
|
|
||||||
const float getLeaves() const { return leaves; }
|
|
||||||
const float getDepth() const { return depth; }
|
|
||||||
const vector<double>& getScoresTrain() const { return scores_train; }
|
|
||||||
const vector<double>& getScoresTest() const { return scores_test; }
|
|
||||||
const vector<double>& getTimesTrain() const { return times_train; }
|
|
||||||
const vector<double>& getTimesTest() const { return times_test; }
|
|
||||||
};
|
|
||||||
class Experiment {
|
|
||||||
private:
|
|
||||||
string title, model, platform, score_name, model_version, language_version, language;
|
|
||||||
bool discretized{ false }, stratified{ false };
|
|
||||||
vector<Result> results;
|
|
||||||
vector<int> randomSeeds;
|
|
||||||
int nfolds{ 0 };
|
|
||||||
float duration{ 0 };
|
|
||||||
json build_json();
|
|
||||||
public:
|
|
||||||
Experiment() = default;
|
|
||||||
Experiment& setTitle(const string& title) { this->title = title; return *this; }
|
|
||||||
Experiment& setModel(const string& model) { this->model = model; return *this; }
|
|
||||||
Experiment& setPlatform(const string& platform) { this->platform = platform; return *this; }
|
|
||||||
Experiment& setScoreName(const string& score_name) { this->score_name = score_name; return *this; }
|
|
||||||
Experiment& setModelVersion(const string& model_version) { this->model_version = model_version; return *this; }
|
|
||||||
Experiment& setLanguage(const string& language) { this->language = language; return *this; }
|
|
||||||
Experiment& setLanguageVersion(const string& language_version) { this->language_version = language_version; return *this; }
|
|
||||||
Experiment& setDiscretized(bool discretized) { this->discretized = discretized; return *this; }
|
|
||||||
Experiment& setStratified(bool stratified) { this->stratified = stratified; return *this; }
|
|
||||||
Experiment& setNFolds(int nfolds) { this->nfolds = nfolds; return *this; }
|
|
||||||
Experiment& addResult(Result result) { results.push_back(result); return *this; }
|
|
||||||
Experiment& addRandomSeed(int randomSeed) { randomSeeds.push_back(randomSeed); return *this; }
|
|
||||||
Experiment& setDuration(float duration) { this->duration = duration; return *this; }
|
|
||||||
string get_file_name();
|
|
||||||
void save(const string& path);
|
|
||||||
void cross_validation(const string& path, const string& fileName);
|
|
||||||
void go(vector<string> filesToProcess, const string& path);
|
|
||||||
void show();
|
|
||||||
void report();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,95 +0,0 @@
|
|||||||
#include "Folding.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <map>
|
|
||||||
Fold::Fold(int k, int n, int seed) : k(k), n(n), seed(seed)
|
|
||||||
{
|
|
||||||
random_device rd;
|
|
||||||
random_seed = default_random_engine(seed == -1 ? rd() : seed);
|
|
||||||
srand(seed == -1 ? time(0) : seed);
|
|
||||||
}
|
|
||||||
KFold::KFold(int k, int n, int seed) : Fold(k, n, seed), indices(vector<int>(n))
|
|
||||||
{
|
|
||||||
iota(begin(indices), end(indices), 0); // fill with 0, 1, ..., n - 1
|
|
||||||
shuffle(indices.begin(), indices.end(), random_seed);
|
|
||||||
}
|
|
||||||
pair<vector<int>, vector<int>> KFold::getFold(int nFold)
|
|
||||||
{
|
|
||||||
if (nFold >= k || nFold < 0) {
|
|
||||||
throw out_of_range("nFold (" + to_string(nFold) + ") must be less than k (" + to_string(k) + ")");
|
|
||||||
}
|
|
||||||
int nTest = n / k;
|
|
||||||
auto train = vector<int>();
|
|
||||||
auto test = vector<int>();
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
if (i >= nTest * nFold && i < nTest * (nFold + 1)) {
|
|
||||||
test.push_back(indices[i]);
|
|
||||||
} else {
|
|
||||||
train.push_back(indices[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { train, test };
|
|
||||||
}
|
|
||||||
StratifiedKFold::StratifiedKFold(int k, torch::Tensor& y, int seed) : Fold(k, y.numel(), seed)
|
|
||||||
{
|
|
||||||
n = y.numel();
|
|
||||||
this->y = vector<int>(y.data_ptr<int>(), y.data_ptr<int>() + n);
|
|
||||||
build();
|
|
||||||
}
|
|
||||||
StratifiedKFold::StratifiedKFold(int k, const vector<int>& y, int seed)
|
|
||||||
: Fold(k, y.size(), seed)
|
|
||||||
{
|
|
||||||
this->y = y;
|
|
||||||
n = y.size();
|
|
||||||
build();
|
|
||||||
}
|
|
||||||
void StratifiedKFold::build()
|
|
||||||
{
|
|
||||||
stratified_indices = vector<vector<int>>(k);
|
|
||||||
int fold_size = n / k;
|
|
||||||
// Compute class counts and indices
|
|
||||||
auto class_indices = map<int, vector<int>>();
|
|
||||||
vector<int> class_counts(*max_element(y.begin(), y.end()) + 1, 0);
|
|
||||||
for (auto i = 0; i < n; ++i) {
|
|
||||||
class_counts[y[i]]++;
|
|
||||||
class_indices[y[i]].push_back(i);
|
|
||||||
}
|
|
||||||
// Shuffle class indices
|
|
||||||
for (auto& [cls, indices] : class_indices) {
|
|
||||||
shuffle(indices.begin(), indices.end(), random_seed);
|
|
||||||
}
|
|
||||||
// Assign indices to folds
|
|
||||||
for (auto label = 0; label < class_counts.size(); ++label) {
|
|
||||||
auto num_samples_to_take = class_counts[label] / k;
|
|
||||||
if (num_samples_to_take == 0)
|
|
||||||
continue;
|
|
||||||
auto remainder_samples_to_take = class_counts[label] % k;
|
|
||||||
for (auto fold = 0; fold < k; ++fold) {
|
|
||||||
auto it = next(class_indices[label].begin(), num_samples_to_take);
|
|
||||||
move(class_indices[label].begin(), it, back_inserter(stratified_indices[fold])); // ##
|
|
||||||
class_indices[label].erase(class_indices[label].begin(), it);
|
|
||||||
}
|
|
||||||
while (remainder_samples_to_take > 0) {
|
|
||||||
int fold = (rand() % static_cast<int>(k));
|
|
||||||
if (stratified_indices[fold].size() == fold_size + 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto it = next(class_indices[label].begin(), 1);
|
|
||||||
stratified_indices[fold].push_back(*class_indices[label].begin());
|
|
||||||
class_indices[label].erase(class_indices[label].begin(), it);
|
|
||||||
remainder_samples_to_take--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pair<vector<int>, vector<int>> StratifiedKFold::getFold(int nFold)
|
|
||||||
{
|
|
||||||
if (nFold >= k || nFold < 0) {
|
|
||||||
throw out_of_range("nFold (" + to_string(nFold) + ") must be less than k (" + to_string(k) + ")");
|
|
||||||
}
|
|
||||||
vector<int> test_indices = stratified_indices[nFold];
|
|
||||||
vector<int> train_indices;
|
|
||||||
for (int i = 0; i < k; ++i) {
|
|
||||||
if (i == nFold) continue;
|
|
||||||
train_indices.insert(train_indices.end(), stratified_indices[i].begin(), stratified_indices[i].end());
|
|
||||||
}
|
|
||||||
return { train_indices, test_indices };
|
|
||||||
}
|
|
@@ -1,37 +0,0 @@
|
|||||||
#ifndef FOLDING_H
|
|
||||||
#define FOLDING_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <random>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
class Fold {
|
|
||||||
protected:
|
|
||||||
int k;
|
|
||||||
int n;
|
|
||||||
int seed;
|
|
||||||
default_random_engine random_seed;
|
|
||||||
public:
|
|
||||||
Fold(int k, int n, int seed = -1);
|
|
||||||
virtual pair<vector<int>, vector<int>> getFold(int nFold) = 0;
|
|
||||||
virtual ~Fold() = default;
|
|
||||||
int getNumberOfFolds() { return k; }
|
|
||||||
};
|
|
||||||
class KFold : public Fold {
|
|
||||||
private:
|
|
||||||
vector<int> indices;
|
|
||||||
public:
|
|
||||||
KFold(int k, int n, int seed = -1);
|
|
||||||
pair<vector<int>, vector<int>> getFold(int nFold) override;
|
|
||||||
};
|
|
||||||
class StratifiedKFold : public Fold {
|
|
||||||
private:
|
|
||||||
vector<int> y;
|
|
||||||
vector<vector<int>> stratified_indices;
|
|
||||||
void build();
|
|
||||||
public:
|
|
||||||
StratifiedKFold(int k, const vector<int>& y, int seed = -1);
|
|
||||||
StratifiedKFold(int k, torch::Tensor& y, int seed = -1);
|
|
||||||
pair<vector<int>, vector<int>> getFold(int nFold) override;
|
|
||||||
};
|
|
||||||
#endif
|
|
@@ -1,54 +0,0 @@
|
|||||||
#include "Models.h"
|
|
||||||
namespace platform {
|
|
||||||
using namespace std;
|
|
||||||
// Idea from: https://www.codeproject.com/Articles/567242/AplusC-2b-2bplusObjectplusFactory
|
|
||||||
Models* Models::factory = nullptr;;
|
|
||||||
Models* Models::instance()
|
|
||||||
{
|
|
||||||
//manages singleton
|
|
||||||
if (factory == nullptr)
|
|
||||||
factory = new Models();
|
|
||||||
return factory;
|
|
||||||
}
|
|
||||||
void Models::registerFactoryFunction(const string& name,
|
|
||||||
function<bayesnet::BaseClassifier* (void)> classFactoryFunction)
|
|
||||||
{
|
|
||||||
// register the class factory function
|
|
||||||
functionRegistry[name] = classFactoryFunction;
|
|
||||||
}
|
|
||||||
shared_ptr<bayesnet::BaseClassifier> Models::create(const string& name)
|
|
||||||
{
|
|
||||||
bayesnet::BaseClassifier* instance = nullptr;
|
|
||||||
|
|
||||||
// find name in the registry and call factory method.
|
|
||||||
auto it = functionRegistry.find(name);
|
|
||||||
if (it != functionRegistry.end())
|
|
||||||
instance = it->second();
|
|
||||||
// wrap instance in a shared ptr and return
|
|
||||||
if (instance != nullptr)
|
|
||||||
return shared_ptr<bayesnet::BaseClassifier>(instance);
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
vector<string> Models::getNames()
|
|
||||||
{
|
|
||||||
vector<string> names;
|
|
||||||
transform(functionRegistry.begin(), functionRegistry.end(), back_inserter(names),
|
|
||||||
[](const pair<string, function<bayesnet::BaseClassifier* (void)>>& pair) { return pair.first; });
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
string Models::toString()
|
|
||||||
{
|
|
||||||
string result = "";
|
|
||||||
for (const auto& pair : functionRegistry) {
|
|
||||||
result += pair.first + ", ";
|
|
||||||
}
|
|
||||||
return "{" + result.substr(0, result.size() - 2) + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
Registrar::Registrar(const string& name, function<bayesnet::BaseClassifier* (void)> classFactoryFunction)
|
|
||||||
{
|
|
||||||
// register the class factory function
|
|
||||||
Models::instance()->registerFactoryFunction(name, classFactoryFunction);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,37 +0,0 @@
|
|||||||
#ifndef MODELS_H
|
|
||||||
#define MODELS_H
|
|
||||||
#include <map>
|
|
||||||
#include "BaseClassifier.h"
|
|
||||||
#include "AODE.h"
|
|
||||||
#include "TAN.h"
|
|
||||||
#include "KDB.h"
|
|
||||||
#include "SPODE.h"
|
|
||||||
#include "TANLd.h"
|
|
||||||
#include "KDBLd.h"
|
|
||||||
#include "SPODELd.h"
|
|
||||||
#include "AODELd.h"
|
|
||||||
#include "BoostAODE.h"
|
|
||||||
namespace platform {
|
|
||||||
class Models {
|
|
||||||
private:
|
|
||||||
map<string, function<bayesnet::BaseClassifier* (void)>> functionRegistry;
|
|
||||||
static Models* factory; //singleton
|
|
||||||
Models() {};
|
|
||||||
public:
|
|
||||||
Models(Models&) = delete;
|
|
||||||
void operator=(const Models&) = delete;
|
|
||||||
// Idea from: https://www.codeproject.com/Articles/567242/AplusC-2b-2bplusObjectplusFactory
|
|
||||||
static Models* instance();
|
|
||||||
shared_ptr<bayesnet::BaseClassifier> create(const string& name);
|
|
||||||
void registerFactoryFunction(const string& name,
|
|
||||||
function<bayesnet::BaseClassifier* (void)> classFactoryFunction);
|
|
||||||
vector<string> getNames();
|
|
||||||
string toString();
|
|
||||||
|
|
||||||
};
|
|
||||||
class Registrar {
|
|
||||||
public:
|
|
||||||
Registrar(const string& className, function<bayesnet::BaseClassifier* (void)> classFactoryFunction);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,11 +0,0 @@
|
|||||||
#ifndef PATHS_H
|
|
||||||
#define PATHS_H
|
|
||||||
#include <string>
|
|
||||||
namespace platform {
|
|
||||||
class Paths {
|
|
||||||
public:
|
|
||||||
static std::string datasets() { return "datasets/"; }
|
|
||||||
static std::string results() { return "results/"; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,115 +0,0 @@
|
|||||||
#include <sstream>
|
|
||||||
#include <locale>
|
|
||||||
#include "Report.h"
|
|
||||||
#include "BestResult.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace platform {
|
|
||||||
string headerLine(const string& text)
|
|
||||||
{
|
|
||||||
int n = MAXL - text.length() - 3;
|
|
||||||
n = n < 0 ? 0 : n;
|
|
||||||
return "* " + text + string(n, ' ') + "*\n";
|
|
||||||
}
|
|
||||||
string Report::fromVector(const string& key)
|
|
||||||
{
|
|
||||||
stringstream oss;
|
|
||||||
string sep = "";
|
|
||||||
oss << "[";
|
|
||||||
for (auto& item : data[key]) {
|
|
||||||
oss << sep << item.get<double>();
|
|
||||||
sep = ", ";
|
|
||||||
}
|
|
||||||
oss << "]";
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
string fVector(const string& title, const json& data, const int width, const int precision)
|
|
||||||
{
|
|
||||||
stringstream oss;
|
|
||||||
string sep = "";
|
|
||||||
oss << title << "[";
|
|
||||||
for (const auto& item : data) {
|
|
||||||
oss << sep << fixed << setw(width) << setprecision(precision) << item.get<double>();
|
|
||||||
sep = ", ";
|
|
||||||
}
|
|
||||||
oss << "]";
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
void Report::show()
|
|
||||||
{
|
|
||||||
header();
|
|
||||||
body();
|
|
||||||
footer();
|
|
||||||
}
|
|
||||||
struct separated : numpunct<char> {
|
|
||||||
char do_decimal_point() const { return ','; }
|
|
||||||
char do_thousands_sep() const { return '.'; }
|
|
||||||
string do_grouping() const { return "\03"; }
|
|
||||||
};
|
|
||||||
void Report::header()
|
|
||||||
{
|
|
||||||
locale mylocale(cout.getloc(), new separated);
|
|
||||||
locale::global(mylocale);
|
|
||||||
cout.imbue(mylocale);
|
|
||||||
stringstream oss;
|
|
||||||
cout << Colors::MAGENTA() << string(MAXL, '*') << endl;
|
|
||||||
cout << headerLine("Report " + data["model"].get<string>() + " ver. " + data["version"].get<string>() + " with " + to_string(data["folds"].get<int>()) + " Folds cross validation and " + to_string(data["seeds"].size()) + " random seeds. " + data["date"].get<string>() + " " + data["time"].get<string>());
|
|
||||||
cout << headerLine(data["title"].get<string>());
|
|
||||||
cout << headerLine("Random seeds: " + fromVector("seeds") + " Stratified: " + (data["stratified"].get<bool>() ? "True" : "False"));
|
|
||||||
oss << "Execution took " << setprecision(2) << fixed << data["duration"].get<float>() << " seconds, " << data["duration"].get<float>() / 3600 << " hours, on " << data["platform"].get<string>();
|
|
||||||
cout << headerLine(oss.str());
|
|
||||||
cout << headerLine("Score is " + data["score_name"].get<string>());
|
|
||||||
cout << string(MAXL, '*') << endl;
|
|
||||||
cout << endl;
|
|
||||||
}
|
|
||||||
void Report::body()
|
|
||||||
{
|
|
||||||
cout << Colors::GREEN() << "Dataset Sampl. Feat. Cls Nodes Edges States Score Time Hyperparameters" << endl;
|
|
||||||
cout << "============================== ====== ===== === ========= ========= ========= =============== ================== ===============" << endl;
|
|
||||||
json lastResult;
|
|
||||||
totalScore = 0;
|
|
||||||
bool odd = true;
|
|
||||||
for (const auto& r : data["results"]) {
|
|
||||||
auto color = odd ? Colors::CYAN() : Colors::BLUE();
|
|
||||||
cout << color << setw(30) << left << r["dataset"].get<string>() << " ";
|
|
||||||
cout << setw(6) << right << r["samples"].get<int>() << " ";
|
|
||||||
cout << setw(5) << right << r["features"].get<int>() << " ";
|
|
||||||
cout << setw(3) << right << r["classes"].get<int>() << " ";
|
|
||||||
cout << setw(9) << setprecision(2) << fixed << r["nodes"].get<float>() << " ";
|
|
||||||
cout << setw(9) << setprecision(2) << fixed << r["leaves"].get<float>() << " ";
|
|
||||||
cout << setw(9) << setprecision(2) << fixed << r["depth"].get<float>() << " ";
|
|
||||||
cout << setw(8) << right << setprecision(6) << fixed << r["score"].get<double>() << "±" << setw(6) << setprecision(4) << fixed << r["score_std"].get<double>() << " ";
|
|
||||||
cout << setw(11) << right << setprecision(6) << fixed << r["time"].get<double>() << "±" << setw(6) << setprecision(4) << fixed << r["time_std"].get<double>() << " ";
|
|
||||||
try {
|
|
||||||
cout << r["hyperparameters"].get<string>();
|
|
||||||
}
|
|
||||||
catch (const exception& err) {
|
|
||||||
cout << r["hyperparameters"];
|
|
||||||
}
|
|
||||||
cout << endl;
|
|
||||||
lastResult = r;
|
|
||||||
totalScore += r["score"].get<double>();
|
|
||||||
odd = !odd;
|
|
||||||
}
|
|
||||||
if (data["results"].size() == 1) {
|
|
||||||
cout << string(MAXL, '*') << endl;
|
|
||||||
cout << headerLine(fVector("Train scores: ", lastResult["scores_train"], 14, 12));
|
|
||||||
cout << headerLine(fVector("Test scores: ", lastResult["scores_test"], 14, 12));
|
|
||||||
cout << headerLine(fVector("Train times: ", lastResult["times_train"], 10, 3));
|
|
||||||
cout << headerLine(fVector("Test times: ", lastResult["times_test"], 10, 3));
|
|
||||||
cout << string(MAXL, '*') << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Report::footer()
|
|
||||||
{
|
|
||||||
cout << Colors::MAGENTA() << string(MAXL, '*') << endl;
|
|
||||||
auto score = data["score_name"].get<string>();
|
|
||||||
if (score == BestResult::scoreName()) {
|
|
||||||
stringstream oss;
|
|
||||||
oss << score << " compared to " << BestResult::title() << " .: " << totalScore / BestResult::score();
|
|
||||||
cout << headerLine(oss.str());
|
|
||||||
}
|
|
||||||
cout << string(MAXL, '*') << endl << Colors::RESET();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
#ifndef REPORT_H
|
|
||||||
#define REPORT_H
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include "Colors.h"
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
const int MAXL = 128;
|
|
||||||
namespace platform {
|
|
||||||
using namespace std;
|
|
||||||
class Report {
|
|
||||||
public:
|
|
||||||
explicit Report(json data_) { data = data_; };
|
|
||||||
virtual ~Report() = default;
|
|
||||||
void show();
|
|
||||||
private:
|
|
||||||
void header();
|
|
||||||
void body();
|
|
||||||
void footer();
|
|
||||||
string fromVector(const string& key);
|
|
||||||
json data;
|
|
||||||
double totalScore; // Total score of all results in a report
|
|
||||||
};
|
|
||||||
};
|
|
||||||
#endif
|
|
@@ -1,239 +0,0 @@
|
|||||||
#include <filesystem>
|
|
||||||
#include "platformUtils.h"
|
|
||||||
#include "Results.h"
|
|
||||||
#include "Report.h"
|
|
||||||
#include "BestResult.h"
|
|
||||||
#include "Colors.h"
|
|
||||||
namespace platform {
|
|
||||||
Result::Result(const string& path, const string& filename)
|
|
||||||
: path(path)
|
|
||||||
, filename(filename)
|
|
||||||
{
|
|
||||||
auto data = load();
|
|
||||||
date = data["date"];
|
|
||||||
score = 0;
|
|
||||||
for (const auto& result : data["results"]) {
|
|
||||||
score += result["score"].get<double>();
|
|
||||||
}
|
|
||||||
scoreName = data["score_name"];
|
|
||||||
if (scoreName == BestResult::scoreName()) {
|
|
||||||
score /= BestResult::score();
|
|
||||||
}
|
|
||||||
title = data["title"];
|
|
||||||
duration = data["duration"];
|
|
||||||
model = data["model"];
|
|
||||||
}
|
|
||||||
json Result::load() const
|
|
||||||
{
|
|
||||||
ifstream resultData(path + "/" + filename);
|
|
||||||
if (resultData.is_open()) {
|
|
||||||
json data = json::parse(resultData);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
throw invalid_argument("Unable to open result file. [" + path + "/" + filename + "]");
|
|
||||||
}
|
|
||||||
void Results::load()
|
|
||||||
{
|
|
||||||
using std::filesystem::directory_iterator;
|
|
||||||
for (const auto& file : directory_iterator(path)) {
|
|
||||||
auto filename = file.path().filename().string();
|
|
||||||
if (filename.find(".json") != string::npos && filename.find("results_") == 0) {
|
|
||||||
auto result = Result(path, filename);
|
|
||||||
bool addResult = true;
|
|
||||||
if (model != "any" && result.getModel() != model || scoreName != "any" && scoreName != result.getScoreName())
|
|
||||||
addResult = false;
|
|
||||||
if (addResult)
|
|
||||||
files.push_back(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
string Result::to_string() const
|
|
||||||
{
|
|
||||||
stringstream oss;
|
|
||||||
oss << date << " ";
|
|
||||||
oss << setw(12) << left << model << " ";
|
|
||||||
oss << setw(11) << left << scoreName << " ";
|
|
||||||
oss << right << setw(11) << setprecision(7) << fixed << score << " ";
|
|
||||||
oss << setw(9) << setprecision(3) << fixed << duration << " ";
|
|
||||||
oss << setw(50) << left << title << " ";
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
void Results::show() const
|
|
||||||
{
|
|
||||||
cout << Colors::GREEN() << "Results found: " << files.size() << endl;
|
|
||||||
cout << "-------------------" << endl;
|
|
||||||
auto i = 0;
|
|
||||||
cout << " # Date Model Score Name Score Duration Title" << endl;
|
|
||||||
cout << "=== ========== ============ =========== =========== ========= =============================================================" << endl;
|
|
||||||
bool odd = true;
|
|
||||||
for (const auto& result : files) {
|
|
||||||
auto color = odd ? Colors::BLUE() : Colors::CYAN();
|
|
||||||
cout << color << setw(3) << fixed << right << i++ << " ";
|
|
||||||
cout << result.to_string() << endl;
|
|
||||||
if (i == max && max != 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
odd = !odd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int Results::getIndex(const string& intent) const
|
|
||||||
{
|
|
||||||
string color;
|
|
||||||
if (intent == "delete") {
|
|
||||||
color = Colors::RED();
|
|
||||||
} else {
|
|
||||||
color = Colors::YELLOW();
|
|
||||||
}
|
|
||||||
cout << color << "Choose result to " << intent << " (cancel=-1): ";
|
|
||||||
string line;
|
|
||||||
getline(cin, line);
|
|
||||||
int index = stoi(line);
|
|
||||||
if (index >= -1 && index < static_cast<int>(files.size())) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
cout << "Invalid index" << endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
void Results::report(const int index) const
|
|
||||||
{
|
|
||||||
cout << Colors::YELLOW() << "Reporting " << files.at(index).getFilename() << endl;
|
|
||||||
auto data = files.at(index).load();
|
|
||||||
Report report(data);
|
|
||||||
report.show();
|
|
||||||
}
|
|
||||||
void Results::menu()
|
|
||||||
{
|
|
||||||
char option;
|
|
||||||
int index;
|
|
||||||
bool finished = false;
|
|
||||||
string filename, line, options = "qldhsr";
|
|
||||||
while (!finished) {
|
|
||||||
cout << Colors::RESET() << "Choose option (quit='q', list='l', delete='d', hide='h', sort='s', report='r'): ";
|
|
||||||
getline(cin, line);
|
|
||||||
if (line.size() == 0)
|
|
||||||
continue;
|
|
||||||
if (options.find(line[0]) != string::npos) {
|
|
||||||
if (line.size() > 1) {
|
|
||||||
cout << "Invalid option" << endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
option = line[0];
|
|
||||||
} else {
|
|
||||||
index = stoi(line);
|
|
||||||
if (index >= 0 && index < files.size()) {
|
|
||||||
report(index);
|
|
||||||
} else {
|
|
||||||
cout << "Invalid option" << endl;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (option) {
|
|
||||||
case 'q':
|
|
||||||
finished = true;
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
show();
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
index = getIndex("delete");
|
|
||||||
if (index == -1)
|
|
||||||
break;
|
|
||||||
filename = files[index].getFilename();
|
|
||||||
cout << "Deleting " << filename << endl;
|
|
||||||
remove((path + "/" + filename).c_str());
|
|
||||||
files.erase(files.begin() + index);
|
|
||||||
cout << "File: " + filename + " deleted!" << endl;
|
|
||||||
show();
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
index = getIndex("hide");
|
|
||||||
if (index == -1)
|
|
||||||
break;
|
|
||||||
filename = files[index].getFilename();
|
|
||||||
cout << "Hiding " << filename << endl;
|
|
||||||
rename((path + "/" + filename).c_str(), (path + "/." + filename).c_str());
|
|
||||||
files.erase(files.begin() + index);
|
|
||||||
show();
|
|
||||||
menu();
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
sortList();
|
|
||||||
show();
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
index = getIndex("report");
|
|
||||||
if (index == -1)
|
|
||||||
break;
|
|
||||||
report(index);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
cout << "Invalid option" << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Results::sortList()
|
|
||||||
{
|
|
||||||
cout << Colors::YELLOW() << "Choose sorting field (date='d', score='s', duration='u', model='m'): ";
|
|
||||||
string line;
|
|
||||||
char option;
|
|
||||||
getline(cin, line);
|
|
||||||
if (line.size() == 0)
|
|
||||||
return;
|
|
||||||
if (line.size() > 1) {
|
|
||||||
cout << "Invalid option" << endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
option = line[0];
|
|
||||||
switch (option) {
|
|
||||||
case 'd':
|
|
||||||
sortDate();
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
sortScore();
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
sortDuration();
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
sortModel();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
cout << "Invalid option" << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Results::sortDate()
|
|
||||||
{
|
|
||||||
sort(files.begin(), files.end(), [](const Result& a, const Result& b) {
|
|
||||||
return a.getDate() > b.getDate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void Results::sortModel()
|
|
||||||
{
|
|
||||||
sort(files.begin(), files.end(), [](const Result& a, const Result& b) {
|
|
||||||
return a.getModel() > b.getModel();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void Results::sortDuration()
|
|
||||||
{
|
|
||||||
sort(files.begin(), files.end(), [](const Result& a, const Result& b) {
|
|
||||||
return a.getDuration() > b.getDuration();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void Results::sortScore()
|
|
||||||
{
|
|
||||||
sort(files.begin(), files.end(), [](const Result& a, const Result& b) {
|
|
||||||
return a.getScore() > b.getScore();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void Results::manage()
|
|
||||||
{
|
|
||||||
if (files.size() == 0) {
|
|
||||||
cout << "No results found!" << endl;
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
show();
|
|
||||||
menu();
|
|
||||||
cout << "Done!" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,56 +0,0 @@
|
|||||||
#ifndef RESULTS_H
|
|
||||||
#define RESULTS_H
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
namespace platform {
|
|
||||||
using namespace std;
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
class Result {
|
|
||||||
public:
|
|
||||||
Result(const string& path, const string& filename);
|
|
||||||
json load() const;
|
|
||||||
string to_string() const;
|
|
||||||
string getFilename() const { return filename; };
|
|
||||||
string getDate() const { return date; };
|
|
||||||
double getScore() const { return score; };
|
|
||||||
string getTitle() const { return title; };
|
|
||||||
double getDuration() const { return duration; };
|
|
||||||
string getModel() const { return model; };
|
|
||||||
string getScoreName() const { return scoreName; };
|
|
||||||
private:
|
|
||||||
string path;
|
|
||||||
string filename;
|
|
||||||
string date;
|
|
||||||
double score;
|
|
||||||
string title;
|
|
||||||
double duration;
|
|
||||||
string model;
|
|
||||||
string scoreName;
|
|
||||||
};
|
|
||||||
class Results {
|
|
||||||
public:
|
|
||||||
Results(const string& path, const int max, const string& model, const string& score) : path(path), max(max), model(model), scoreName(score) { load(); };
|
|
||||||
void manage();
|
|
||||||
private:
|
|
||||||
string path;
|
|
||||||
int max;
|
|
||||||
string model;
|
|
||||||
string scoreName;
|
|
||||||
vector<Result> files;
|
|
||||||
void load(); // Loads the list of results
|
|
||||||
void show() const;
|
|
||||||
void report(const int index) const;
|
|
||||||
int getIndex(const string& intent) const;
|
|
||||||
void menu();
|
|
||||||
void sortList();
|
|
||||||
void sortDate();
|
|
||||||
void sortScore();
|
|
||||||
void sortModel();
|
|
||||||
void sortDuration();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,57 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <locale>
|
|
||||||
#include "Paths.h"
|
|
||||||
#include "Colors.h"
|
|
||||||
#include "Datasets.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
const int BALANCE_LENGTH = 75;
|
|
||||||
|
|
||||||
struct separated : numpunct<char> {
|
|
||||||
char do_decimal_point() const { return ','; }
|
|
||||||
char do_thousands_sep() const { return '.'; }
|
|
||||||
string do_grouping() const { return "\03"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
void outputBalance(const string& balance)
|
|
||||||
{
|
|
||||||
auto temp = string(balance);
|
|
||||||
while (temp.size() > BALANCE_LENGTH - 1) {
|
|
||||||
auto part = temp.substr(0, BALANCE_LENGTH);
|
|
||||||
cout << part << endl;
|
|
||||||
cout << setw(48) << " ";
|
|
||||||
temp = temp.substr(BALANCE_LENGTH);
|
|
||||||
}
|
|
||||||
cout << temp << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
auto data = platform::Datasets(platform::Paths().datasets(), false);
|
|
||||||
locale mylocale(cout.getloc(), new separated);
|
|
||||||
locale::global(mylocale);
|
|
||||||
cout.imbue(mylocale);
|
|
||||||
cout << Colors::GREEN() << "Dataset Sampl. Feat. Cls. Balance" << endl;
|
|
||||||
string balanceBars = string(BALANCE_LENGTH, '=');
|
|
||||||
cout << "============================== ====== ===== === " << balanceBars << endl;
|
|
||||||
bool odd = true;
|
|
||||||
for (const auto& dataset : data.getNames()) {
|
|
||||||
auto color = odd ? Colors::CYAN() : Colors::BLUE();
|
|
||||||
cout << color << setw(30) << left << dataset << " ";
|
|
||||||
data.loadDataset(dataset);
|
|
||||||
auto nSamples = data.getNSamples(dataset);
|
|
||||||
cout << setw(6) << right << nSamples << " ";
|
|
||||||
cout << setw(5) << right << data.getFeatures(dataset).size() << " ";
|
|
||||||
cout << setw(3) << right << data.getNClasses(dataset) << " ";
|
|
||||||
stringstream oss;
|
|
||||||
string sep = "";
|
|
||||||
for (auto number : data.getClassesCounts(dataset)) {
|
|
||||||
oss << sep << setprecision(2) << fixed << (float)number / nSamples * 100.0 << "% (" << number << ")";
|
|
||||||
sep = " / ";
|
|
||||||
}
|
|
||||||
outputBalance(oss.str());
|
|
||||||
odd = !odd;
|
|
||||||
}
|
|
||||||
cout << Colors::RESET() << endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
@@ -1,122 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <argparse/argparse.hpp>
|
|
||||||
#include "platformUtils.h"
|
|
||||||
#include "Experiment.h"
|
|
||||||
#include "Datasets.h"
|
|
||||||
#include "DotEnv.h"
|
|
||||||
#include "Models.h"
|
|
||||||
#include "modelRegister.h"
|
|
||||||
#include "Paths.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
argparse::ArgumentParser manageArguments(int argc, char** argv)
|
|
||||||
{
|
|
||||||
auto env = platform::DotEnv();
|
|
||||||
argparse::ArgumentParser program("main");
|
|
||||||
program.add_argument("-d", "--dataset").default_value("").help("Dataset file name");
|
|
||||||
program.add_argument("-p", "--path")
|
|
||||||
.help("folder where the data files are located, default")
|
|
||||||
.default_value(string{ platform::Paths::datasets() });
|
|
||||||
program.add_argument("-m", "--model")
|
|
||||||
.help("Model to use " + platform::Models::instance()->toString())
|
|
||||||
.action([](const std::string& value) {
|
|
||||||
static const vector<string> choices = platform::Models::instance()->getNames();
|
|
||||||
if (find(choices.begin(), choices.end(), value) != choices.end()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
throw runtime_error("Model must be one of " + platform::Models::instance()->toString());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
program.add_argument("--title").default_value("").help("Experiment title");
|
|
||||||
program.add_argument("--discretize").help("Discretize input dataset").default_value((bool)stoi(env.get("discretize"))).implicit_value(true);
|
|
||||||
program.add_argument("--stratified").help("If Stratified KFold is to be done").default_value((bool)stoi(env.get("stratified"))).implicit_value(true);
|
|
||||||
program.add_argument("-f", "--folds").help("Number of folds").default_value(stoi(env.get("n_folds"))).scan<'i', int>().action([](const string& value) {
|
|
||||||
try {
|
|
||||||
auto k = stoi(value);
|
|
||||||
if (k < 2) {
|
|
||||||
throw runtime_error("Number of folds must be greater than 1");
|
|
||||||
}
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
catch (const runtime_error& err) {
|
|
||||||
throw runtime_error(err.what());
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
throw runtime_error("Number of folds must be an integer");
|
|
||||||
}});
|
|
||||||
auto seed_values = env.getSeeds();
|
|
||||||
program.add_argument("-s", "--seeds").nargs(1, 10).help("Random seeds. Set to -1 to have pseudo random").scan<'i', int>().default_value(seed_values);
|
|
||||||
try {
|
|
||||||
program.parse_args(argc, argv);
|
|
||||||
auto file_name = program.get<string>("dataset");
|
|
||||||
auto path = program.get<string>("path");
|
|
||||||
auto model_name = program.get<string>("model");
|
|
||||||
auto discretize_dataset = program.get<bool>("discretize");
|
|
||||||
auto stratified = program.get<bool>("stratified");
|
|
||||||
auto n_folds = program.get<int>("folds");
|
|
||||||
auto seeds = program.get<vector<int>>("seeds");
|
|
||||||
auto complete_file_name = path + file_name + ".arff";
|
|
||||||
auto title = program.get<string>("title");
|
|
||||||
if (title == "" && file_name == "") {
|
|
||||||
throw runtime_error("title is mandatory if dataset is not provided");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const exception& err) {
|
|
||||||
cerr << err.what() << endl;
|
|
||||||
cerr << program;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
auto program = manageArguments(argc, argv);
|
|
||||||
bool saveResults = false;
|
|
||||||
auto file_name = program.get<string>("dataset");
|
|
||||||
auto path = program.get<string>("path");
|
|
||||||
auto model_name = program.get<string>("model");
|
|
||||||
auto discretize_dataset = program.get<bool>("discretize");
|
|
||||||
auto stratified = program.get<bool>("stratified");
|
|
||||||
auto n_folds = program.get<int>("folds");
|
|
||||||
auto seeds = program.get<vector<int>>("seeds");
|
|
||||||
vector<string> filesToTest;
|
|
||||||
auto datasets = platform::Datasets(path, true, platform::ARFF);
|
|
||||||
auto title = program.get<string>("title");
|
|
||||||
if (file_name != "") {
|
|
||||||
if (!datasets.isDataset(file_name)) {
|
|
||||||
cerr << "Dataset " << file_name << " not found" << endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (title == "") {
|
|
||||||
title = "Test " + file_name + " " + model_name + " " + to_string(n_folds) + " folds";
|
|
||||||
}
|
|
||||||
filesToTest.push_back(file_name);
|
|
||||||
} else {
|
|
||||||
filesToTest = platform::Datasets(path, true, platform::ARFF).getNames();
|
|
||||||
saveResults = true;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Begin Processing
|
|
||||||
*/
|
|
||||||
auto env = platform::DotEnv();
|
|
||||||
auto experiment = platform::Experiment();
|
|
||||||
experiment.setTitle(title).setLanguage("cpp").setLanguageVersion("14.0.3");
|
|
||||||
experiment.setDiscretized(discretize_dataset).setModel(model_name).setPlatform(env.get("platform"));
|
|
||||||
experiment.setStratified(stratified).setNFolds(n_folds).setScoreName("accuracy");
|
|
||||||
for (auto seed : seeds) {
|
|
||||||
experiment.addRandomSeed(seed);
|
|
||||||
}
|
|
||||||
platform::Timer timer;
|
|
||||||
timer.start();
|
|
||||||
experiment.go(filesToTest, path);
|
|
||||||
experiment.setDuration(timer.getDuration());
|
|
||||||
if (saveResults)
|
|
||||||
experiment.save(platform::Paths::results());
|
|
||||||
else
|
|
||||||
experiment.report();
|
|
||||||
cout << "Done!" << endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <argparse/argparse.hpp>
|
|
||||||
#include "platformUtils.h"
|
|
||||||
#include "Paths.h"
|
|
||||||
#include "Results.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
argparse::ArgumentParser manageArguments(int argc, char** argv)
|
|
||||||
{
|
|
||||||
argparse::ArgumentParser program("manage");
|
|
||||||
program.add_argument("-n", "--number").default_value(0).help("Number of results to show (0 = all)").scan<'i', int>();
|
|
||||||
program.add_argument("-m", "--model").default_value("any").help("Filter results of the selected model)");
|
|
||||||
program.add_argument("-s", "--score").default_value("any").help("Filter results of the score name supplied");
|
|
||||||
try {
|
|
||||||
program.parse_args(argc, argv);
|
|
||||||
auto number = program.get<int>("number");
|
|
||||||
if (number < 0) {
|
|
||||||
throw runtime_error("Number of results must be greater than or equal to 0");
|
|
||||||
}
|
|
||||||
auto model = program.get<string>("model");
|
|
||||||
auto score = program.get<string>("score");
|
|
||||||
}
|
|
||||||
catch (const exception& err) {
|
|
||||||
cerr << err.what() << endl;
|
|
||||||
cerr << program;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
auto program = manageArguments(argc, argv);
|
|
||||||
auto number = program.get<int>("number");
|
|
||||||
auto model = program.get<string>("model");
|
|
||||||
auto score = program.get<string>("score");
|
|
||||||
auto results = platform::Results(platform::Paths::results(), number, model, score);
|
|
||||||
results.manage();
|
|
||||||
return 0;
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
#ifndef MODEL_REGISTER_H
|
|
||||||
#define MODEL_REGISTER_H
|
|
||||||
static platform::Registrar registrarT("TAN",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::TAN();});
|
|
||||||
static platform::Registrar registrarTLD("TANLd",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::TANLd();});
|
|
||||||
static platform::Registrar registrarS("SPODE",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::SPODE(2);});
|
|
||||||
static platform::Registrar registrarSLD("SPODELd",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::SPODELd(2);});
|
|
||||||
static platform::Registrar registrarK("KDB",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::KDB(2);});
|
|
||||||
static platform::Registrar registrarKLD("KDBLd",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::KDBLd(2);});
|
|
||||||
static platform::Registrar registrarA("AODE",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::AODE();});
|
|
||||||
static platform::Registrar registrarALD("AODELd",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::AODELd();});
|
|
||||||
static platform::Registrar registrarBA("BoostAODE",
|
|
||||||
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::BoostAODE();});
|
|
||||||
#endif
|
|
@@ -1,109 +0,0 @@
|
|||||||
#include "platformUtils.h"
|
|
||||||
#include "Paths.h"
|
|
||||||
|
|
||||||
using namespace torch;
|
|
||||||
|
|
||||||
vector<string> split(const string& text, char delimiter)
|
|
||||||
{
|
|
||||||
vector<string> result;
|
|
||||||
stringstream ss(text);
|
|
||||||
string token;
|
|
||||||
while (getline(ss, token, delimiter)) {
|
|
||||||
result.push_back(token);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<vector<mdlp::labels_t>, map<string, int>> discretize(vector<mdlp::samples_t>& X, mdlp::labels_t& y, vector<string> features)
|
|
||||||
{
|
|
||||||
vector<mdlp::labels_t> Xd;
|
|
||||||
map<string, int> maxes;
|
|
||||||
auto fimdlp = mdlp::CPPFImdlp();
|
|
||||||
for (int i = 0; i < X.size(); i++) {
|
|
||||||
fimdlp.fit(X[i], y);
|
|
||||||
mdlp::labels_t& xd = fimdlp.transform(X[i]);
|
|
||||||
maxes[features[i]] = *max_element(xd.begin(), xd.end()) + 1;
|
|
||||||
Xd.push_back(xd);
|
|
||||||
}
|
|
||||||
return { Xd, maxes };
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<mdlp::labels_t> discretizeDataset(vector<mdlp::samples_t>& X, mdlp::labels_t& y)
|
|
||||||
{
|
|
||||||
vector<mdlp::labels_t> Xd;
|
|
||||||
auto fimdlp = mdlp::CPPFImdlp();
|
|
||||||
for (int i = 0; i < X.size(); i++) {
|
|
||||||
fimdlp.fit(X[i], y);
|
|
||||||
mdlp::labels_t& xd = fimdlp.transform(X[i]);
|
|
||||||
Xd.push_back(xd);
|
|
||||||
}
|
|
||||||
return Xd;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool file_exists(const string& name)
|
|
||||||
{
|
|
||||||
if (FILE* file = fopen(name.c_str(), "r")) {
|
|
||||||
fclose(file);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tuple<Tensor, Tensor, vector<string>, string, map<string, vector<int>>> loadDataset(const string& path, const string& name, bool class_last, bool discretize_dataset)
|
|
||||||
{
|
|
||||||
auto handler = ArffFiles();
|
|
||||||
handler.load(path + static_cast<string>(name) + ".arff", class_last);
|
|
||||||
// Get Dataset X, y
|
|
||||||
vector<mdlp::samples_t>& X = handler.getX();
|
|
||||||
mdlp::labels_t& y = handler.getY();
|
|
||||||
// Get className & Features
|
|
||||||
auto className = handler.getClassName();
|
|
||||||
vector<string> features;
|
|
||||||
auto attributes = handler.getAttributes();
|
|
||||||
transform(attributes.begin(), attributes.end(), back_inserter(features), [](const auto& pair) { return pair.first; });
|
|
||||||
Tensor Xd;
|
|
||||||
auto states = map<string, vector<int>>();
|
|
||||||
if (discretize_dataset) {
|
|
||||||
auto Xr = discretizeDataset(X, y);
|
|
||||||
Xd = torch::zeros({ static_cast<int>(Xr[0].size()), static_cast<int>(Xr.size()) }, torch::kInt32);
|
|
||||||
for (int i = 0; i < features.size(); ++i) {
|
|
||||||
states[features[i]] = vector<int>(*max_element(Xr[i].begin(), Xr[i].end()) + 1);
|
|
||||||
iota(begin(states[features[i]]), end(states[features[i]]), 0);
|
|
||||||
Xd.index_put_({ "...", i }, torch::tensor(Xr[i], torch::kInt32));
|
|
||||||
}
|
|
||||||
states[className] = vector<int>(*max_element(y.begin(), y.end()) + 1);
|
|
||||||
iota(begin(states[className]), end(states[className]), 0);
|
|
||||||
} else {
|
|
||||||
Xd = torch::zeros({ static_cast<int>(X[0].size()), static_cast<int>(X.size()) }, torch::kFloat32);
|
|
||||||
for (int i = 0; i < features.size(); ++i) {
|
|
||||||
Xd.index_put_({ "...", i }, torch::tensor(X[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { Xd, torch::tensor(y, torch::kInt32), features, className, states };
|
|
||||||
}
|
|
||||||
|
|
||||||
tuple<vector<vector<int>>, vector<int>, vector<string>, string, map<string, vector<int>>> loadFile(const string& name)
|
|
||||||
{
|
|
||||||
auto handler = ArffFiles();
|
|
||||||
handler.load(platform::Paths::datasets() + static_cast<string>(name) + ".arff");
|
|
||||||
// Get Dataset X, y
|
|
||||||
vector<mdlp::samples_t>& X = handler.getX();
|
|
||||||
mdlp::labels_t& y = handler.getY();
|
|
||||||
// Get className & Features
|
|
||||||
auto className = handler.getClassName();
|
|
||||||
vector<string> features;
|
|
||||||
auto attributes = handler.getAttributes();
|
|
||||||
transform(attributes.begin(), attributes.end(), back_inserter(features), [](const auto& pair) { return pair.first; });
|
|
||||||
// Discretize Dataset
|
|
||||||
vector<mdlp::labels_t> Xd;
|
|
||||||
map<string, int> maxes;
|
|
||||||
tie(Xd, maxes) = discretize(X, y, features);
|
|
||||||
maxes[className] = *max_element(y.begin(), y.end()) + 1;
|
|
||||||
map<string, vector<int>> states;
|
|
||||||
for (auto feature : features) {
|
|
||||||
states[feature] = vector<int>(maxes[feature]);
|
|
||||||
}
|
|
||||||
states[className] = vector<int>(maxes[className]);
|
|
||||||
return { Xd, y, features, className, states };
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
#ifndef PLATFORM_UTILS_H
|
|
||||||
#define PLATFORM_UTILS_H
|
|
||||||
#include <torch/torch.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <tuple>
|
|
||||||
#include "ArffFiles.h"
|
|
||||||
#include "CPPFImdlp.h"
|
|
||||||
using namespace std;
|
|
||||||
const string PATH = "../../data/";
|
|
||||||
|
|
||||||
bool file_exists(const std::string& name);
|
|
||||||
vector<string> split(const string& text, char delimiter);
|
|
||||||
pair<vector<mdlp::labels_t>, map<string, int>> discretize(vector<mdlp::samples_t>& X, mdlp::labels_t& y, vector<string> features);
|
|
||||||
vector<mdlp::labels_t> discretizeDataset(vector<mdlp::samples_t>& X, mdlp::labels_t& y);
|
|
||||||
pair<torch::Tensor, map<string, vector<int>>> discretizeTorch(torch::Tensor& X, torch::Tensor& y, vector<string>& features, const string& className);
|
|
||||||
tuple<vector<vector<int>>, vector<int>, vector<string>, string, map<string, vector<int>>> loadFile(const string& name);
|
|
||||||
tuple<torch::Tensor, torch::Tensor, vector<string>, string, map<string, vector<int>>> loadDataset(const string& path, const string& name, bool class_last, bool discretize_dataset);
|
|
||||||
map<string, vector<int>> get_states(vector<string>& features, string className, map<string, int>& maxes);
|
|
||||||
#endif //PLATFORM_UTILS_H
|
|
@@ -3,18 +3,18 @@
|
|||||||
#include "Network.h"
|
#include "Network.h"
|
||||||
#include "bayesnetUtils.h"
|
#include "bayesnetUtils.h"
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
Network::Network() : features(vector<string>()), className(""), classNumStates(0), fitted(false) {}
|
Network::Network() : features(std::vector<std::string>()), className(""), classNumStates(0), fitted(false), laplaceSmoothing(0) {}
|
||||||
Network::Network(float maxT) : features(vector<string>()), className(""), classNumStates(0), maxThreads(maxT), fitted(false) {}
|
Network::Network(float maxT) : features(std::vector<std::string>()), className(""), classNumStates(0), maxThreads(maxT), fitted(false), laplaceSmoothing(0) {}
|
||||||
Network::Network(Network& other) : laplaceSmoothing(other.laplaceSmoothing), features(other.features), className(other.className), classNumStates(other.getClassNumStates()), maxThreads(other.
|
Network::Network(Network& other) : laplaceSmoothing(other.laplaceSmoothing), features(other.features), className(other.className), classNumStates(other.getClassNumStates()), maxThreads(other.
|
||||||
getmaxThreads()), fitted(other.fitted)
|
getmaxThreads()), fitted(other.fitted)
|
||||||
{
|
{
|
||||||
for (const auto& pair : other.nodes) {
|
for (const auto& node : other.nodes) {
|
||||||
nodes[pair.first] = std::make_unique<Node>(*pair.second);
|
nodes[node.first] = std::make_unique<Node>(*node.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Network::initialize()
|
void Network::initialize()
|
||||||
{
|
{
|
||||||
features = vector<string>();
|
features = std::vector<std::string>();
|
||||||
className = "";
|
className = "";
|
||||||
classNumStates = 0;
|
classNumStates = 0;
|
||||||
fitted = false;
|
fitted = false;
|
||||||
@@ -29,10 +29,10 @@ namespace bayesnet {
|
|||||||
{
|
{
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
void Network::addNode(const string& name)
|
void Network::addNode(const std::string& name)
|
||||||
{
|
{
|
||||||
if (name == "") {
|
if (name == "") {
|
||||||
throw invalid_argument("Node name cannot be empty");
|
throw std::invalid_argument("Node name cannot be empty");
|
||||||
}
|
}
|
||||||
if (nodes.find(name) != nodes.end()) {
|
if (nodes.find(name) != nodes.end()) {
|
||||||
return;
|
return;
|
||||||
@@ -42,7 +42,7 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
nodes[name] = std::make_unique<Node>(name);
|
nodes[name] = std::make_unique<Node>(name);
|
||||||
}
|
}
|
||||||
vector<string> Network::getFeatures() const
|
std::vector<std::string> Network::getFeatures() const
|
||||||
{
|
{
|
||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
@@ -58,11 +58,11 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
string Network::getClassName() const
|
std::string Network::getClassName() const
|
||||||
{
|
{
|
||||||
return className;
|
return className;
|
||||||
}
|
}
|
||||||
bool Network::isCyclic(const string& nodeId, unordered_set<string>& visited, unordered_set<string>& recStack)
|
bool Network::isCyclic(const std::string& nodeId, std::unordered_set<std::string>& visited, std::unordered_set<std::string>& recStack)
|
||||||
{
|
{
|
||||||
if (visited.find(nodeId) == visited.end()) // if node hasn't been visited yet
|
if (visited.find(nodeId) == visited.end()) // if node hasn't been visited yet
|
||||||
{
|
{
|
||||||
@@ -71,85 +71,85 @@ namespace bayesnet {
|
|||||||
for (Node* child : nodes[nodeId]->getChildren()) {
|
for (Node* child : nodes[nodeId]->getChildren()) {
|
||||||
if (visited.find(child->getName()) == visited.end() && isCyclic(child->getName(), visited, recStack))
|
if (visited.find(child->getName()) == visited.end() && isCyclic(child->getName(), visited, recStack))
|
||||||
return true;
|
return true;
|
||||||
else if (recStack.find(child->getName()) != recStack.end())
|
if (recStack.find(child->getName()) != recStack.end())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recStack.erase(nodeId); // remove node from recursion stack before function ends
|
recStack.erase(nodeId); // remove node from recursion stack before function ends
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void Network::addEdge(const string& parent, const string& child)
|
void Network::addEdge(const std::string& parent, const std::string& child)
|
||||||
{
|
{
|
||||||
if (nodes.find(parent) == nodes.end()) {
|
if (nodes.find(parent) == nodes.end()) {
|
||||||
throw invalid_argument("Parent node " + parent + " does not exist");
|
throw std::invalid_argument("Parent node " + parent + " does not exist");
|
||||||
}
|
}
|
||||||
if (nodes.find(child) == nodes.end()) {
|
if (nodes.find(child) == nodes.end()) {
|
||||||
throw invalid_argument("Child node " + child + " does not exist");
|
throw std::invalid_argument("Child node " + child + " does not exist");
|
||||||
}
|
}
|
||||||
// Temporarily add edge to check for cycles
|
// Temporarily add edge to check for cycles
|
||||||
nodes[parent]->addChild(nodes[child].get());
|
nodes[parent]->addChild(nodes[child].get());
|
||||||
nodes[child]->addParent(nodes[parent].get());
|
nodes[child]->addParent(nodes[parent].get());
|
||||||
unordered_set<string> visited;
|
std::unordered_set<std::string> visited;
|
||||||
unordered_set<string> recStack;
|
std::unordered_set<std::string> recStack;
|
||||||
if (isCyclic(nodes[child]->getName(), visited, recStack)) // if adding this edge forms a cycle
|
if (isCyclic(nodes[child]->getName(), visited, recStack)) // if adding this edge forms a cycle
|
||||||
{
|
{
|
||||||
// remove problematic edge
|
// remove problematic edge
|
||||||
nodes[parent]->removeChild(nodes[child].get());
|
nodes[parent]->removeChild(nodes[child].get());
|
||||||
nodes[child]->removeParent(nodes[parent].get());
|
nodes[child]->removeParent(nodes[parent].get());
|
||||||
throw invalid_argument("Adding this edge forms a cycle in the graph.");
|
throw std::invalid_argument("Adding this edge forms a cycle in the graph.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map<string, std::unique_ptr<Node>>& Network::getNodes()
|
std::map<std::string, std::unique_ptr<Node>>& Network::getNodes()
|
||||||
{
|
{
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
void Network::checkFitData(int n_samples, int n_features, int n_samples_y, const vector<string>& featureNames, const string& className, const map<string, vector<int>>& states, const torch::Tensor& weights)
|
void Network::checkFitData(int n_samples, int n_features, int n_samples_y, const std::vector<std::string>& featureNames, const std::string& className, const std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights)
|
||||||
{
|
{
|
||||||
if (weights.size(0) != n_samples) {
|
if (weights.size(0) != n_samples) {
|
||||||
throw invalid_argument("Weights (" + to_string(weights.size(0)) + ") must have the same number of elements as samples (" + to_string(n_samples) + ") in Network::fit");
|
throw std::invalid_argument("Weights (" + std::to_string(weights.size(0)) + ") must have the same number of elements as samples (" + std::to_string(n_samples) + ") in Network::fit");
|
||||||
}
|
}
|
||||||
if (n_samples != n_samples_y) {
|
if (n_samples != n_samples_y) {
|
||||||
throw invalid_argument("X and y must have the same number of samples in Network::fit (" + to_string(n_samples) + " != " + to_string(n_samples_y) + ")");
|
throw std::invalid_argument("X and y must have the same number of samples in Network::fit (" + std::to_string(n_samples) + " != " + std::to_string(n_samples_y) + ")");
|
||||||
}
|
}
|
||||||
if (n_features != featureNames.size()) {
|
if (n_features != featureNames.size()) {
|
||||||
throw invalid_argument("X and features must have the same number of features in Network::fit (" + to_string(n_features) + " != " + to_string(featureNames.size()) + ")");
|
throw std::invalid_argument("X and features must have the same number of features in Network::fit (" + std::to_string(n_features) + " != " + std::to_string(featureNames.size()) + ")");
|
||||||
}
|
}
|
||||||
if (n_features != features.size() - 1) {
|
if (n_features != features.size() - 1) {
|
||||||
throw invalid_argument("X and local features must have the same number of features in Network::fit (" + to_string(n_features) + " != " + to_string(features.size() - 1) + ")");
|
throw std::invalid_argument("X and local features must have the same number of features in Network::fit (" + std::to_string(n_features) + " != " + std::to_string(features.size() - 1) + ")");
|
||||||
}
|
}
|
||||||
if (find(features.begin(), features.end(), className) == features.end()) {
|
if (find(features.begin(), features.end(), className) == features.end()) {
|
||||||
throw invalid_argument("className not found in Network::features");
|
throw std::invalid_argument("className not found in Network::features");
|
||||||
}
|
}
|
||||||
for (auto& feature : featureNames) {
|
for (auto& feature : featureNames) {
|
||||||
if (find(features.begin(), features.end(), feature) == features.end()) {
|
if (find(features.begin(), features.end(), feature) == features.end()) {
|
||||||
throw invalid_argument("Feature " + feature + " not found in Network::features");
|
throw std::invalid_argument("Feature " + feature + " not found in Network::features");
|
||||||
}
|
}
|
||||||
if (states.find(feature) == states.end()) {
|
if (states.find(feature) == states.end()) {
|
||||||
throw invalid_argument("Feature " + feature + " not found in states");
|
throw std::invalid_argument("Feature " + feature + " not found in states");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Network::setStates(const map<string, vector<int>>& states)
|
void Network::setStates(const std::map<std::string, std::vector<int>>& states)
|
||||||
{
|
{
|
||||||
// Set states to every Node in the network
|
// Set states to every Node in the network
|
||||||
for (int i = 0; i < features.size(); ++i) {
|
for_each(features.begin(), features.end(), [this, &states](const std::string& feature) {
|
||||||
nodes[features[i]]->setNumStates(states.at(features[i]).size());
|
nodes.at(feature)->setNumStates(states.at(feature).size());
|
||||||
}
|
});
|
||||||
classNumStates = nodes[className]->getNumStates();
|
classNumStates = nodes.at(className)->getNumStates();
|
||||||
}
|
}
|
||||||
// X comes in nxm, where n is the number of features and m the number of samples
|
// X comes in nxm, where n is the number of features and m the number of samples
|
||||||
void Network::fit(const torch::Tensor& X, const torch::Tensor& y, const torch::Tensor& weights, const vector<string>& featureNames, const string& className, const map<string, vector<int>>& states)
|
void Network::fit(const torch::Tensor& X, const torch::Tensor& y, const torch::Tensor& weights, const std::vector<std::string>& featureNames, const std::string& className, const std::map<std::string, std::vector<int>>& states)
|
||||||
{
|
{
|
||||||
checkFitData(X.size(1), X.size(0), y.size(0), featureNames, className, states, weights);
|
checkFitData(X.size(1), X.size(0), y.size(0), featureNames, className, states, weights);
|
||||||
this->className = className;
|
this->className = className;
|
||||||
Tensor ytmp = torch::transpose(y.view({ y.size(0), 1 }), 0, 1);
|
torch::Tensor ytmp = torch::transpose(y.view({ y.size(0), 1 }), 0, 1);
|
||||||
samples = torch::cat({ X , ytmp }, 0);
|
samples = torch::cat({ X , ytmp }, 0);
|
||||||
for (int i = 0; i < featureNames.size(); ++i) {
|
for (int i = 0; i < featureNames.size(); ++i) {
|
||||||
auto row_feature = X.index({ i, "..." });
|
auto row_feature = X.index({ i, "..." });
|
||||||
}
|
}
|
||||||
completeFit(states, weights);
|
completeFit(states, weights);
|
||||||
}
|
}
|
||||||
void Network::fit(const torch::Tensor& samples, const torch::Tensor& weights, const vector<string>& featureNames, const string& className, const map<string, vector<int>>& states)
|
void Network::fit(const torch::Tensor& samples, const torch::Tensor& weights, const std::vector<std::string>& featureNames, const std::string& className, const std::map<std::string, std::vector<int>>& states)
|
||||||
{
|
{
|
||||||
checkFitData(samples.size(1), samples.size(0) - 1, samples.size(1), featureNames, className, states, weights);
|
checkFitData(samples.size(1), samples.size(0) - 1, samples.size(1), featureNames, className, states, weights);
|
||||||
this->className = className;
|
this->className = className;
|
||||||
@@ -157,7 +157,7 @@ namespace bayesnet {
|
|||||||
completeFit(states, weights);
|
completeFit(states, weights);
|
||||||
}
|
}
|
||||||
// input_data comes in nxm, where n is the number of features and m the number of samples
|
// input_data comes in nxm, where n is the number of features and m the number of samples
|
||||||
void Network::fit(const vector<vector<int>>& input_data, const vector<int>& labels, const vector<float>& weights_, const vector<string>& featureNames, const string& className, const map<string, vector<int>>& states)
|
void Network::fit(const std::vector<std::vector<int>>& input_data, const std::vector<int>& labels, const std::vector<double>& weights_, const std::vector<std::string>& featureNames, const std::string& className, const std::map<std::string, std::vector<int>>& states)
|
||||||
{
|
{
|
||||||
const torch::Tensor weights = torch::tensor(weights_, torch::kFloat64);
|
const torch::Tensor weights = torch::tensor(weights_, torch::kFloat64);
|
||||||
checkFitData(input_data[0].size(), input_data.size(), labels.size(), featureNames, className, states, weights);
|
checkFitData(input_data[0].size(), input_data.size(), labels.size(), featureNames, className, states, weights);
|
||||||
@@ -170,41 +170,15 @@ namespace bayesnet {
|
|||||||
samples.index_put_({ -1, "..." }, torch::tensor(labels, torch::kInt32));
|
samples.index_put_({ -1, "..." }, torch::tensor(labels, torch::kInt32));
|
||||||
completeFit(states, weights);
|
completeFit(states, weights);
|
||||||
}
|
}
|
||||||
void Network::completeFit(const map<string, vector<int>>& states, const torch::Tensor& weights)
|
void Network::completeFit(const std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights)
|
||||||
{
|
{
|
||||||
setStates(states);
|
setStates(states);
|
||||||
laplaceSmoothing = 1.0 / samples.size(1); // To use in CPT computation
|
laplaceSmoothing = 1.0 / samples.size(1); // To use in CPT computation
|
||||||
int maxThreadsRunning = static_cast<int>(std::thread::hardware_concurrency() * maxThreads);
|
std::vector<std::thread> threads;
|
||||||
if (maxThreadsRunning < 1) {
|
for (auto& node : nodes) {
|
||||||
maxThreadsRunning = 1;
|
threads.emplace_back([this, &node, &weights]() {
|
||||||
}
|
node.second->computeCPT(samples, features, laplaceSmoothing, weights);
|
||||||
vector<thread> threads;
|
|
||||||
mutex mtx;
|
|
||||||
condition_variable cv;
|
|
||||||
int activeThreads = 0;
|
|
||||||
int nextNodeIndex = 0;
|
|
||||||
while (nextNodeIndex < nodes.size()) {
|
|
||||||
unique_lock<mutex> lock(mtx);
|
|
||||||
cv.wait(lock, [&activeThreads, &maxThreadsRunning]() { return activeThreads < maxThreadsRunning; });
|
|
||||||
threads.emplace_back([this, &nextNodeIndex, &mtx, &cv, &activeThreads, &weights]() {
|
|
||||||
while (true) {
|
|
||||||
unique_lock<mutex> lock(mtx);
|
|
||||||
if (nextNodeIndex >= nodes.size()) {
|
|
||||||
break; // No more work remaining
|
|
||||||
}
|
|
||||||
auto& pair = *std::next(nodes.begin(), nextNodeIndex);
|
|
||||||
++nextNodeIndex;
|
|
||||||
lock.unlock();
|
|
||||||
pair.second->computeCPT(samples, features, laplaceSmoothing, weights);
|
|
||||||
lock.lock();
|
|
||||||
nodes[pair.first] = std::move(pair.second);
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
lock_guard<mutex> lock(mtx);
|
|
||||||
--activeThreads;
|
|
||||||
cv.notify_one();
|
|
||||||
});
|
});
|
||||||
++activeThreads;
|
|
||||||
}
|
}
|
||||||
for (auto& thread : threads) {
|
for (auto& thread : threads) {
|
||||||
thread.join();
|
thread.join();
|
||||||
@@ -214,12 +188,12 @@ namespace bayesnet {
|
|||||||
torch::Tensor Network::predict_tensor(const torch::Tensor& samples, const bool proba)
|
torch::Tensor Network::predict_tensor(const torch::Tensor& samples, const bool proba)
|
||||||
{
|
{
|
||||||
if (!fitted) {
|
if (!fitted) {
|
||||||
throw logic_error("You must call fit() before calling predict()");
|
throw std::logic_error("You must call fit() before calling predict()");
|
||||||
}
|
}
|
||||||
torch::Tensor result;
|
torch::Tensor result;
|
||||||
result = torch::zeros({ samples.size(1), classNumStates }, torch::kFloat64);
|
result = torch::zeros({ samples.size(1), classNumStates }, torch::kFloat64);
|
||||||
for (int i = 0; i < samples.size(1); ++i) {
|
for (int i = 0; i < samples.size(1); ++i) {
|
||||||
const Tensor sample = samples.index({ "...", i });
|
const torch::Tensor sample = samples.index({ "...", i });
|
||||||
auto psample = predict_sample(sample);
|
auto psample = predict_sample(sample);
|
||||||
auto temp = torch::tensor(psample, torch::kFloat64);
|
auto temp = torch::tensor(psample, torch::kFloat64);
|
||||||
// result.index_put_({ i, "..." }, torch::tensor(predict_sample(sample), torch::kFloat64));
|
// result.index_put_({ i, "..." }, torch::tensor(predict_sample(sample), torch::kFloat64));
|
||||||
@@ -227,36 +201,35 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
if (proba)
|
if (proba)
|
||||||
return result;
|
return result;
|
||||||
else
|
return result.argmax(1);
|
||||||
return result.argmax(1);
|
|
||||||
}
|
}
|
||||||
// Return mxn tensor of probabilities
|
// Return mxn tensor of probabilities
|
||||||
Tensor Network::predict_proba(const Tensor& samples)
|
torch::Tensor Network::predict_proba(const torch::Tensor& samples)
|
||||||
{
|
{
|
||||||
return predict_tensor(samples, true);
|
return predict_tensor(samples, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return mxn tensor of probabilities
|
// Return mxn tensor of probabilities
|
||||||
Tensor Network::predict(const Tensor& samples)
|
torch::Tensor Network::predict(const torch::Tensor& samples)
|
||||||
{
|
{
|
||||||
return predict_tensor(samples, false);
|
return predict_tensor(samples, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return mx1 vector of predictions
|
// Return mx1 std::vector of predictions
|
||||||
// tsamples is nxm vector of samples
|
// tsamples is nxm std::vector of samples
|
||||||
vector<int> Network::predict(const vector<vector<int>>& tsamples)
|
std::vector<int> Network::predict(const std::vector<std::vector<int>>& tsamples)
|
||||||
{
|
{
|
||||||
if (!fitted) {
|
if (!fitted) {
|
||||||
throw logic_error("You must call fit() before calling predict()");
|
throw std::logic_error("You must call fit() before calling predict()");
|
||||||
}
|
}
|
||||||
vector<int> predictions;
|
std::vector<int> predictions;
|
||||||
vector<int> sample;
|
std::vector<int> sample;
|
||||||
for (int row = 0; row < tsamples[0].size(); ++row) {
|
for (int row = 0; row < tsamples[0].size(); ++row) {
|
||||||
sample.clear();
|
sample.clear();
|
||||||
for (int col = 0; col < tsamples.size(); ++col) {
|
for (int col = 0; col < tsamples.size(); ++col) {
|
||||||
sample.push_back(tsamples[col][row]);
|
sample.push_back(tsamples[col][row]);
|
||||||
}
|
}
|
||||||
vector<double> classProbabilities = predict_sample(sample);
|
std::vector<double> classProbabilities = predict_sample(sample);
|
||||||
// Find the class with the maximum posterior probability
|
// Find the class with the maximum posterior probability
|
||||||
auto maxElem = max_element(classProbabilities.begin(), classProbabilities.end());
|
auto maxElem = max_element(classProbabilities.begin(), classProbabilities.end());
|
||||||
int predictedClass = distance(classProbabilities.begin(), maxElem);
|
int predictedClass = distance(classProbabilities.begin(), maxElem);
|
||||||
@@ -264,14 +237,15 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
return predictions;
|
return predictions;
|
||||||
}
|
}
|
||||||
// Return mxn vector of probabilities
|
// Return mxn std::vector of probabilities
|
||||||
vector<vector<double>> Network::predict_proba(const vector<vector<int>>& tsamples)
|
// tsamples is nxm std::vector of samples
|
||||||
|
std::vector<std::vector<double>> Network::predict_proba(const std::vector<std::vector<int>>& tsamples)
|
||||||
{
|
{
|
||||||
if (!fitted) {
|
if (!fitted) {
|
||||||
throw logic_error("You must call fit() before calling predict_proba()");
|
throw std::logic_error("You must call fit() before calling predict_proba()");
|
||||||
}
|
}
|
||||||
vector<vector<double>> predictions;
|
std::vector<std::vector<double>> predictions;
|
||||||
vector<int> sample;
|
std::vector<int> sample;
|
||||||
for (int row = 0; row < tsamples[0].size(); ++row) {
|
for (int row = 0; row < tsamples[0].size(); ++row) {
|
||||||
sample.clear();
|
sample.clear();
|
||||||
for (int col = 0; col < tsamples.size(); ++col) {
|
for (int col = 0; col < tsamples.size(); ++col) {
|
||||||
@@ -281,9 +255,9 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
return predictions;
|
return predictions;
|
||||||
}
|
}
|
||||||
double Network::score(const vector<vector<int>>& tsamples, const vector<int>& labels)
|
double Network::score(const std::vector<std::vector<int>>& tsamples, const std::vector<int>& labels)
|
||||||
{
|
{
|
||||||
vector<int> y_pred = predict(tsamples);
|
std::vector<int> y_pred = predict(tsamples);
|
||||||
int correct = 0;
|
int correct = 0;
|
||||||
for (int i = 0; i < y_pred.size(); ++i) {
|
for (int i = 0; i < y_pred.size(); ++i) {
|
||||||
if (y_pred[i] == labels[i]) {
|
if (y_pred[i] == labels[i]) {
|
||||||
@@ -292,35 +266,35 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
return (double)correct / y_pred.size();
|
return (double)correct / y_pred.size();
|
||||||
}
|
}
|
||||||
// Return 1xn vector of probabilities
|
// Return 1xn std::vector of probabilities
|
||||||
vector<double> Network::predict_sample(const vector<int>& sample)
|
std::vector<double> Network::predict_sample(const std::vector<int>& sample)
|
||||||
{
|
{
|
||||||
// Ensure the sample size is equal to the number of features
|
// Ensure the sample size is equal to the number of features
|
||||||
if (sample.size() != features.size() - 1) {
|
if (sample.size() != features.size() - 1) {
|
||||||
throw invalid_argument("Sample size (" + to_string(sample.size()) +
|
throw std::invalid_argument("Sample size (" + std::to_string(sample.size()) +
|
||||||
") does not match the number of features (" + to_string(features.size() - 1) + ")");
|
") does not match the number of features (" + std::to_string(features.size() - 1) + ")");
|
||||||
}
|
}
|
||||||
map<string, int> evidence;
|
std::map<std::string, int> evidence;
|
||||||
for (int i = 0; i < sample.size(); ++i) {
|
for (int i = 0; i < sample.size(); ++i) {
|
||||||
evidence[features[i]] = sample[i];
|
evidence[features[i]] = sample[i];
|
||||||
}
|
}
|
||||||
return exactInference(evidence);
|
return exactInference(evidence);
|
||||||
}
|
}
|
||||||
// Return 1xn vector of probabilities
|
// Return 1xn std::vector of probabilities
|
||||||
vector<double> Network::predict_sample(const Tensor& sample)
|
std::vector<double> Network::predict_sample(const torch::Tensor& sample)
|
||||||
{
|
{
|
||||||
// Ensure the sample size is equal to the number of features
|
// Ensure the sample size is equal to the number of features
|
||||||
if (sample.size(0) != features.size() - 1) {
|
if (sample.size(0) != features.size() - 1) {
|
||||||
throw invalid_argument("Sample size (" + to_string(sample.size(0)) +
|
throw std::invalid_argument("Sample size (" + std::to_string(sample.size(0)) +
|
||||||
") does not match the number of features (" + to_string(features.size() - 1) + ")");
|
") does not match the number of features (" + std::to_string(features.size() - 1) + ")");
|
||||||
}
|
}
|
||||||
map<string, int> evidence;
|
std::map<std::string, int> evidence;
|
||||||
for (int i = 0; i < sample.size(0); ++i) {
|
for (int i = 0; i < sample.size(0); ++i) {
|
||||||
evidence[features[i]] = sample[i].item<int>();
|
evidence[features[i]] = sample[i].item<int>();
|
||||||
}
|
}
|
||||||
return exactInference(evidence);
|
return exactInference(evidence);
|
||||||
}
|
}
|
||||||
double Network::computeFactor(map<string, int>& completeEvidence)
|
double Network::computeFactor(std::map<std::string, int>& completeEvidence)
|
||||||
{
|
{
|
||||||
double result = 1.0;
|
double result = 1.0;
|
||||||
for (auto& node : getNodes()) {
|
for (auto& node : getNodes()) {
|
||||||
@@ -328,17 +302,17 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
vector<double> Network::exactInference(map<string, int>& evidence)
|
std::vector<double> Network::exactInference(std::map<std::string, int>& evidence)
|
||||||
{
|
{
|
||||||
vector<double> result(classNumStates, 0.0);
|
std::vector<double> result(classNumStates, 0.0);
|
||||||
vector<thread> threads;
|
std::vector<std::thread> threads;
|
||||||
mutex mtx;
|
std::mutex mtx;
|
||||||
for (int i = 0; i < classNumStates; ++i) {
|
for (int i = 0; i < classNumStates; ++i) {
|
||||||
threads.emplace_back([this, &result, &evidence, i, &mtx]() {
|
threads.emplace_back([this, &result, &evidence, i, &mtx]() {
|
||||||
auto completeEvidence = map<string, int>(evidence);
|
auto completeEvidence = std::map<std::string, int>(evidence);
|
||||||
completeEvidence[getClassName()] = i;
|
completeEvidence[getClassName()] = i;
|
||||||
double factor = computeFactor(completeEvidence);
|
double factor = computeFactor(completeEvidence);
|
||||||
lock_guard<mutex> lock(mtx);
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
result[i] = factor;
|
result[i] = factor;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -350,12 +324,12 @@ namespace bayesnet {
|
|||||||
transform(result.begin(), result.end(), result.begin(), [sum](const double& value) { return value / sum; });
|
transform(result.begin(), result.end(), result.begin(), [sum](const double& value) { return value / sum; });
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
vector<string> Network::show() const
|
std::vector<std::string> Network::show() const
|
||||||
{
|
{
|
||||||
vector<string> result;
|
std::vector<std::string> result;
|
||||||
// Draw the network
|
// Draw the network
|
||||||
for (auto& node : nodes) {
|
for (auto& node : nodes) {
|
||||||
string line = node.first + " -> ";
|
std::string line = node.first + " -> ";
|
||||||
for (auto child : node.second->getChildren()) {
|
for (auto child : node.second->getChildren()) {
|
||||||
line += child->getName() + ", ";
|
line += child->getName() + ", ";
|
||||||
}
|
}
|
||||||
@@ -363,12 +337,12 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
vector<string> Network::graph(const string& title) const
|
std::vector<std::string> Network::graph(const std::string& title) const
|
||||||
{
|
{
|
||||||
auto output = vector<string>();
|
auto output = std::vector<std::string>();
|
||||||
auto prefix = "digraph BayesNet {\nlabel=<BayesNet ";
|
auto prefix = "digraph BayesNet {\nlabel=<BayesNet ";
|
||||||
auto suffix = ">\nfontsize=30\nfontcolor=blue\nlabelloc=t\nlayout=circo\n";
|
auto suffix = ">\nfontsize=30\nfontcolor=blue\nlabelloc=t\nlayout=circo\n";
|
||||||
string header = prefix + title + suffix;
|
std::string header = prefix + title + suffix;
|
||||||
output.push_back(header);
|
output.push_back(header);
|
||||||
for (auto& node : nodes) {
|
for (auto& node : nodes) {
|
||||||
auto result = node.second->graph(className);
|
auto result = node.second->graph(className);
|
||||||
@@ -377,9 +351,9 @@ namespace bayesnet {
|
|||||||
output.push_back("}\n");
|
output.push_back("}\n");
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
vector<pair<string, string>> Network::getEdges() const
|
std::vector<std::pair<std::string, std::string>> Network::getEdges() const
|
||||||
{
|
{
|
||||||
auto edges = vector<pair<string, string>>();
|
auto edges = std::vector<std::pair<std::string, std::string>>();
|
||||||
for (const auto& node : nodes) {
|
for (const auto& node : nodes) {
|
||||||
auto head = node.first;
|
auto head = node.first;
|
||||||
for (const auto& child : node.second->getChildren()) {
|
for (const auto& child : node.second->getChildren()) {
|
||||||
@@ -393,13 +367,12 @@ namespace bayesnet {
|
|||||||
{
|
{
|
||||||
return getEdges().size();
|
return getEdges().size();
|
||||||
}
|
}
|
||||||
vector<string> Network::topological_sort()
|
std::vector<std::string> Network::topological_sort()
|
||||||
{
|
{
|
||||||
/* Check if al the fathers of every node are before the node */
|
/* Check if al the fathers of every node are before the node */
|
||||||
auto result = features;
|
auto result = features;
|
||||||
result.erase(remove(result.begin(), result.end(), className), result.end());
|
result.erase(remove(result.begin(), result.end(), className), result.end());
|
||||||
bool ending{ false };
|
bool ending{ false };
|
||||||
int idx = 0;
|
|
||||||
while (!ending) {
|
while (!ending) {
|
||||||
ending = true;
|
ending = true;
|
||||||
for (auto feature : features) {
|
for (auto feature : features) {
|
||||||
@@ -421,10 +394,10 @@ namespace bayesnet {
|
|||||||
ending = false;
|
ending = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw logic_error("Error in topological sort because of node " + feature + " is not in result");
|
throw std::logic_error("Error in topological sort because of node " + feature + " is not in result");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw logic_error("Error in topological sort because of node father " + fatherName + " is not in result");
|
throw std::logic_error("Error in topological sort because of node father " + fatherName + " is not in result");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -434,8 +407,8 @@ namespace bayesnet {
|
|||||||
void Network::dump_cpt() const
|
void Network::dump_cpt() const
|
||||||
{
|
{
|
||||||
for (auto& node : nodes) {
|
for (auto& node : nodes) {
|
||||||
cout << "* " << node.first << ": (" << node.second->getNumStates() << ") : " << node.second->getCPT().sizes() << endl;
|
std::cout << "* " << node.first << ": (" << node.second->getNumStates() << ") : " << node.second->getCPT().sizes() << std::endl;
|
||||||
cout << node.second->getCPT() << endl;
|
std::cout << node.second->getCPT() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
63
src/bayesian_network/Network.h
Normal file
63
src/bayesian_network/Network.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#ifndef NETWORK_H
|
||||||
|
#define NETWORK_H
|
||||||
|
#include "Node.h"
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class Network {
|
||||||
|
public:
|
||||||
|
Network();
|
||||||
|
explicit Network(float);
|
||||||
|
explicit Network(Network&);
|
||||||
|
~Network() = default;
|
||||||
|
torch::Tensor& getSamples();
|
||||||
|
float getmaxThreads();
|
||||||
|
void addNode(const std::string&);
|
||||||
|
void addEdge(const std::string&, const std::string&);
|
||||||
|
std::map<std::string, std::unique_ptr<Node>>& getNodes();
|
||||||
|
std::vector<std::string> getFeatures() const;
|
||||||
|
int getStates() const;
|
||||||
|
std::vector<std::pair<std::string, std::string>> getEdges() const;
|
||||||
|
int getNumEdges() const;
|
||||||
|
int getClassNumStates() const;
|
||||||
|
std::string getClassName() const;
|
||||||
|
/*
|
||||||
|
Notice: Nodes have to be inserted in the same order as they are in the dataset, i.e., first node is first column and so on.
|
||||||
|
*/
|
||||||
|
void fit(const std::vector<std::vector<int>>& input_data, const std::vector<int>& labels, const std::vector<double>& weights, const std::vector<std::string>& featureNames, const std::string& className, const std::map<std::string, std::vector<int>>& states);
|
||||||
|
void fit(const torch::Tensor& X, const torch::Tensor& y, const torch::Tensor& weights, const std::vector<std::string>& featureNames, const std::string& className, const std::map<std::string, std::vector<int>>& states);
|
||||||
|
void fit(const torch::Tensor& samples, const torch::Tensor& weights, const std::vector<std::string>& featureNames, const std::string& className, const std::map<std::string, std::vector<int>>& states);
|
||||||
|
std::vector<int> predict(const std::vector<std::vector<int>>&); // Return mx1 std::vector of predictions
|
||||||
|
torch::Tensor predict(const torch::Tensor&); // Return mx1 tensor of predictions
|
||||||
|
torch::Tensor predict_tensor(const torch::Tensor& samples, const bool proba);
|
||||||
|
std::vector<std::vector<double>> predict_proba(const std::vector<std::vector<int>>&); // Return mxn std::vector of probabilities
|
||||||
|
torch::Tensor predict_proba(const torch::Tensor&); // Return mxn tensor of probabilities
|
||||||
|
double score(const std::vector<std::vector<int>>&, const std::vector<int>&);
|
||||||
|
std::vector<std::string> topological_sort();
|
||||||
|
std::vector<std::string> show() const;
|
||||||
|
std::vector<std::string> graph(const std::string& title) const; // Returns a std::vector of std::strings representing the graph in graphviz format
|
||||||
|
void initialize();
|
||||||
|
void dump_cpt() const;
|
||||||
|
inline std::string version() { return { project_version.begin(), project_version.end() }; }
|
||||||
|
private:
|
||||||
|
std::map<std::string, std::unique_ptr<Node>> nodes;
|
||||||
|
bool fitted;
|
||||||
|
float maxThreads = 0.95;
|
||||||
|
int classNumStates;
|
||||||
|
std::vector<std::string> features; // Including classname
|
||||||
|
std::string className;
|
||||||
|
double laplaceSmoothing;
|
||||||
|
torch::Tensor samples; // nxm tensor used to fit the model
|
||||||
|
bool isCyclic(const std::string&, std::unordered_set<std::string>&, std::unordered_set<std::string>&);
|
||||||
|
std::vector<double> predict_sample(const std::vector<int>&);
|
||||||
|
std::vector<double> predict_sample(const torch::Tensor&);
|
||||||
|
std::vector<double> exactInference(std::map<std::string, int>&);
|
||||||
|
double computeFactor(std::map<std::string, int>&);
|
||||||
|
void completeFit(const std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights);
|
||||||
|
void checkFitData(int n_features, int n_samples, int n_samples_y, const std::vector<std::string>& featureNames, const std::string& className, const std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights);
|
||||||
|
void setStates(const std::map<std::string, std::vector<int>>&);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
@@ -3,7 +3,7 @@
|
|||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
|
|
||||||
Node::Node(const std::string& name)
|
Node::Node(const std::string& name)
|
||||||
: name(name), numStates(0), cpTable(torch::Tensor()), parents(vector<Node*>()), children(vector<Node*>())
|
: name(name), numStates(0), cpTable(torch::Tensor()), parents(std::vector<Node*>()), children(std::vector<Node*>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void Node::clear()
|
void Node::clear()
|
||||||
@@ -14,7 +14,7 @@ namespace bayesnet {
|
|||||||
dimensions.clear();
|
dimensions.clear();
|
||||||
numStates = 0;
|
numStates = 0;
|
||||||
}
|
}
|
||||||
string Node::getName() const
|
std::string Node::getName() const
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@@ -34,11 +34,11 @@ namespace bayesnet {
|
|||||||
{
|
{
|
||||||
children.push_back(child);
|
children.push_back(child);
|
||||||
}
|
}
|
||||||
vector<Node*>& Node::getParents()
|
std::vector<Node*>& Node::getParents()
|
||||||
{
|
{
|
||||||
return parents;
|
return parents;
|
||||||
}
|
}
|
||||||
vector<Node*>& Node::getChildren()
|
std::vector<Node*>& Node::getChildren()
|
||||||
{
|
{
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
@@ -63,28 +63,28 @@ namespace bayesnet {
|
|||||||
*/
|
*/
|
||||||
unsigned Node::minFill()
|
unsigned Node::minFill()
|
||||||
{
|
{
|
||||||
unordered_set<string> neighbors;
|
std::unordered_set<std::string> neighbors;
|
||||||
for (auto child : children) {
|
for (auto child : children) {
|
||||||
neighbors.emplace(child->getName());
|
neighbors.emplace(child->getName());
|
||||||
}
|
}
|
||||||
for (auto parent : parents) {
|
for (auto parent : parents) {
|
||||||
neighbors.emplace(parent->getName());
|
neighbors.emplace(parent->getName());
|
||||||
}
|
}
|
||||||
auto source = vector<string>(neighbors.begin(), neighbors.end());
|
auto source = std::vector<std::string>(neighbors.begin(), neighbors.end());
|
||||||
return combinations(source).size();
|
return combinations(source).size();
|
||||||
}
|
}
|
||||||
vector<pair<string, string>> Node::combinations(const vector<string>& source)
|
std::vector<std::pair<std::string, std::string>> Node::combinations(const std::vector<std::string>& source)
|
||||||
{
|
{
|
||||||
vector<pair<string, string>> result;
|
std::vector<std::pair<std::string, std::string>> result;
|
||||||
for (int i = 0; i < source.size(); ++i) {
|
for (int i = 0; i < source.size(); ++i) {
|
||||||
string temp = source[i];
|
std::string temp = source[i];
|
||||||
for (int j = i + 1; j < source.size(); ++j) {
|
for (int j = i + 1; j < source.size(); ++j) {
|
||||||
result.push_back({ temp, source[j] });
|
result.push_back({ temp, source[j] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
void Node::computeCPT(const torch::Tensor& dataset, const vector<string>& features, const double laplaceSmoothing, const torch::Tensor& weights)
|
void Node::computeCPT(const torch::Tensor& dataset, const std::vector<std::string>& features, const double laplaceSmoothing, const torch::Tensor& weights)
|
||||||
{
|
{
|
||||||
dimensions.clear();
|
dimensions.clear();
|
||||||
// Get dimensions of the CPT
|
// Get dimensions of the CPT
|
||||||
@@ -96,16 +96,16 @@ namespace bayesnet {
|
|||||||
// Fill table with counts
|
// Fill table with counts
|
||||||
auto pos = find(features.begin(), features.end(), name);
|
auto pos = find(features.begin(), features.end(), name);
|
||||||
if (pos == features.end()) {
|
if (pos == features.end()) {
|
||||||
throw logic_error("Feature " + name + " not found in dataset");
|
throw std::logic_error("Feature " + name + " not found in dataset");
|
||||||
}
|
}
|
||||||
int name_index = pos - features.begin();
|
int name_index = pos - features.begin();
|
||||||
for (int n_sample = 0; n_sample < dataset.size(1); ++n_sample) {
|
for (int n_sample = 0; n_sample < dataset.size(1); ++n_sample) {
|
||||||
torch::List<c10::optional<torch::Tensor>> coordinates;
|
c10::List<c10::optional<at::Tensor>> coordinates;
|
||||||
coordinates.push_back(dataset.index({ name_index, n_sample }));
|
coordinates.push_back(dataset.index({ name_index, n_sample }));
|
||||||
for (auto parent : parents) {
|
for (auto parent : parents) {
|
||||||
pos = find(features.begin(), features.end(), parent->getName());
|
pos = find(features.begin(), features.end(), parent->getName());
|
||||||
if (pos == features.end()) {
|
if (pos == features.end()) {
|
||||||
throw logic_error("Feature parent " + parent->getName() + " not found in dataset");
|
throw std::logic_error("Feature parent " + parent->getName() + " not found in dataset");
|
||||||
}
|
}
|
||||||
int parent_index = pos - features.begin();
|
int parent_index = pos - features.begin();
|
||||||
coordinates.push_back(dataset.index({ parent_index, n_sample }));
|
coordinates.push_back(dataset.index({ parent_index, n_sample }));
|
||||||
@@ -116,17 +116,17 @@ namespace bayesnet {
|
|||||||
// Normalize the counts
|
// Normalize the counts
|
||||||
cpTable = cpTable / cpTable.sum(0);
|
cpTable = cpTable / cpTable.sum(0);
|
||||||
}
|
}
|
||||||
float Node::getFactorValue(map<string, int>& evidence)
|
float Node::getFactorValue(std::map<std::string, int>& evidence)
|
||||||
{
|
{
|
||||||
torch::List<c10::optional<torch::Tensor>> coordinates;
|
c10::List<c10::optional<at::Tensor>> coordinates;
|
||||||
// following predetermined order of indices in the cpTable (see Node.h)
|
// following predetermined order of indices in the cpTable (see Node.h)
|
||||||
coordinates.push_back(torch::tensor(evidence[name]));
|
coordinates.push_back(at::tensor(evidence[name]));
|
||||||
transform(parents.begin(), parents.end(), back_inserter(coordinates), [&evidence](const auto& parent) { return torch::tensor(evidence[parent->getName()]); });
|
transform(parents.begin(), parents.end(), std::back_inserter(coordinates), [&evidence](const auto& parent) { return at::tensor(evidence[parent->getName()]); });
|
||||||
return cpTable.index({ coordinates }).item<float>();
|
return cpTable.index({ coordinates }).item<float>();
|
||||||
}
|
}
|
||||||
vector<string> Node::graph(const string& className)
|
std::vector<std::string> Node::graph(const std::string& className)
|
||||||
{
|
{
|
||||||
auto output = vector<string>();
|
auto output = std::vector<std::string>();
|
||||||
auto suffix = name == className ? ", fontcolor=red, fillcolor=lightblue, style=filled " : "";
|
auto suffix = name == className ? ", fontcolor=red, fillcolor=lightblue, style=filled " : "";
|
||||||
output.push_back(name + " [shape=circle" + suffix + "] \n");
|
output.push_back(name + " [shape=circle" + suffix + "] \n");
|
||||||
transform(children.begin(), children.end(), back_inserter(output), [this](const auto& child) { return name + " -> " + child->getName(); });
|
transform(children.begin(), children.end(), back_inserter(output), [this](const auto& child) { return name + " -> " + child->getName(); });
|
36
src/bayesian_network/Node.h
Normal file
36
src/bayesian_network/Node.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef NODE_H
|
||||||
|
#define NODE_H
|
||||||
|
#include <torch/torch.h>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
namespace bayesnet {
|
||||||
|
class Node {
|
||||||
|
private:
|
||||||
|
std::string name;
|
||||||
|
std::vector<Node*> parents;
|
||||||
|
std::vector<Node*> children;
|
||||||
|
int numStates; // number of states of the variable
|
||||||
|
torch::Tensor cpTable; // Order of indices is 0-> node variable, 1-> 1st parent, 2-> 2nd parent, ...
|
||||||
|
std::vector<int64_t> dimensions; // dimensions of the cpTable
|
||||||
|
std::vector<std::pair<std::string, std::string>> combinations(const std::vector<std::string>&);
|
||||||
|
public:
|
||||||
|
explicit Node(const std::string&);
|
||||||
|
void clear();
|
||||||
|
void addParent(Node*);
|
||||||
|
void addChild(Node*);
|
||||||
|
void removeParent(Node*);
|
||||||
|
void removeChild(Node*);
|
||||||
|
std::string getName() const;
|
||||||
|
std::vector<Node*>& getParents();
|
||||||
|
std::vector<Node*>& getChildren();
|
||||||
|
torch::Tensor& getCPT();
|
||||||
|
void computeCPT(const torch::Tensor& dataset, const std::vector<std::string>& features, const double laplaceSmoothing, const torch::Tensor& weights);
|
||||||
|
int getNumStates() const;
|
||||||
|
void setNumStates(int);
|
||||||
|
unsigned minFill();
|
||||||
|
std::vector<std::string> graph(const std::string& clasName); // Returns a std::vector of std::strings representing the graph in graphviz format
|
||||||
|
float getFactorValue(std::map<std::string, int>&);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
@@ -2,10 +2,9 @@
|
|||||||
#include "bayesnetUtils.h"
|
#include "bayesnetUtils.h"
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace torch;
|
|
||||||
|
|
||||||
Classifier::Classifier(Network model) : model(model), m(0), n(0), metrics(Metrics()), fitted(false) {}
|
Classifier::Classifier(Network model) : model(model), m(0), n(0), metrics(Metrics()), fitted(false) {}
|
||||||
Classifier& Classifier::build(vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights)
|
const std::string CLASSIFIER_NOT_FITTED = "Classifier has not been fitted";
|
||||||
|
Classifier& Classifier::build(const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights)
|
||||||
{
|
{
|
||||||
this->features = features;
|
this->features = features;
|
||||||
this->className = className;
|
this->className = className;
|
||||||
@@ -13,7 +12,7 @@ namespace bayesnet {
|
|||||||
m = dataset.size(1);
|
m = dataset.size(1);
|
||||||
n = dataset.size(0) - 1;
|
n = dataset.size(0) - 1;
|
||||||
checkFitParameters();
|
checkFitParameters();
|
||||||
auto n_classes = states[className].size();
|
auto n_classes = states.at(className).size();
|
||||||
metrics = Metrics(dataset, features, className, n_classes);
|
metrics = Metrics(dataset, features, className, n_classes);
|
||||||
model.initialize();
|
model.initialize();
|
||||||
buildModel(weights);
|
buildModel(weights);
|
||||||
@@ -21,7 +20,7 @@ namespace bayesnet {
|
|||||||
fitted = true;
|
fitted = true;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
void Classifier::buildDataset(Tensor& ytmp)
|
void Classifier::buildDataset(torch::Tensor& ytmp)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
auto yresized = torch::transpose(ytmp.view({ ytmp.size(0), 1 }), 0, 1);
|
auto yresized = torch::transpose(ytmp.view({ ytmp.size(0), 1 }), 0, 1);
|
||||||
@@ -29,8 +28,8 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
std::cerr << e.what() << '\n';
|
std::cerr << e.what() << '\n';
|
||||||
cout << "X dimensions: " << dataset.sizes() << "\n";
|
std::cout << "X dimensions: " << dataset.sizes() << "\n";
|
||||||
cout << "y dimensions: " << ytmp.sizes() << "\n";
|
std::cout << "y dimensions: " << ytmp.sizes() << "\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,7 +38,7 @@ namespace bayesnet {
|
|||||||
model.fit(dataset, weights, features, className, states);
|
model.fit(dataset, weights, features, className, states);
|
||||||
}
|
}
|
||||||
// X is nxm where n is the number of features and m the number of samples
|
// X is nxm where n is the number of features and m the number of samples
|
||||||
Classifier& Classifier::fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states)
|
Classifier& Classifier::fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
|
||||||
{
|
{
|
||||||
dataset = X;
|
dataset = X;
|
||||||
buildDataset(y);
|
buildDataset(y);
|
||||||
@@ -47,79 +46,101 @@ namespace bayesnet {
|
|||||||
return build(features, className, states, weights);
|
return build(features, className, states, weights);
|
||||||
}
|
}
|
||||||
// X is nxm where n is the number of features and m the number of samples
|
// X is nxm where n is the number of features and m the number of samples
|
||||||
Classifier& Classifier::fit(vector<vector<int>>& X, vector<int>& y, vector<string>& features, string className, map<string, vector<int>>& states)
|
Classifier& Classifier::fit(std::vector<std::vector<int>>& X, std::vector<int>& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
|
||||||
{
|
{
|
||||||
dataset = torch::zeros({ static_cast<int>(X.size()), static_cast<int>(X[0].size()) }, kInt32);
|
dataset = torch::zeros({ static_cast<int>(X.size()), static_cast<int>(X[0].size()) }, torch::kInt32);
|
||||||
for (int i = 0; i < X.size(); ++i) {
|
for (int i = 0; i < X.size(); ++i) {
|
||||||
dataset.index_put_({ i, "..." }, torch::tensor(X[i], kInt32));
|
dataset.index_put_({ i, "..." }, torch::tensor(X[i], torch::kInt32));
|
||||||
}
|
}
|
||||||
auto ytmp = torch::tensor(y, kInt32);
|
auto ytmp = torch::tensor(y, torch::kInt32);
|
||||||
buildDataset(ytmp);
|
buildDataset(ytmp);
|
||||||
const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
|
const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
|
||||||
return build(features, className, states, weights);
|
return build(features, className, states, weights);
|
||||||
}
|
}
|
||||||
Classifier& Classifier::fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states)
|
Classifier& Classifier::fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
|
||||||
{
|
{
|
||||||
this->dataset = dataset;
|
this->dataset = dataset;
|
||||||
const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
|
const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
|
||||||
return build(features, className, states, weights);
|
return build(features, className, states, weights);
|
||||||
}
|
}
|
||||||
Classifier& Classifier::fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights)
|
Classifier& Classifier::fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights)
|
||||||
{
|
{
|
||||||
this->dataset = dataset;
|
this->dataset = dataset;
|
||||||
return build(features, className, states, weights);
|
return build(features, className, states, weights);
|
||||||
}
|
}
|
||||||
void Classifier::checkFitParameters()
|
void Classifier::checkFitParameters()
|
||||||
{
|
{
|
||||||
|
if (torch::is_floating_point(dataset)) {
|
||||||
|
throw std::invalid_argument("dataset (X, y) must be of type Integer");
|
||||||
|
}
|
||||||
if (n != features.size()) {
|
if (n != features.size()) {
|
||||||
throw invalid_argument("X " + to_string(n) + " and features " + to_string(features.size()) + " must have the same number of features");
|
throw std::invalid_argument("Classifier: X " + std::to_string(n) + " and features " + std::to_string(features.size()) + " must have the same number of features");
|
||||||
}
|
}
|
||||||
if (states.find(className) == states.end()) {
|
if (states.find(className) == states.end()) {
|
||||||
throw invalid_argument("className not found in states");
|
throw std::invalid_argument("className not found in states");
|
||||||
}
|
}
|
||||||
for (auto feature : features) {
|
for (auto feature : features) {
|
||||||
if (states.find(feature) == states.end()) {
|
if (states.find(feature) == states.end()) {
|
||||||
throw invalid_argument("feature [" + feature + "] not found in states");
|
throw std::invalid_argument("feature [" + feature + "] not found in states");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Tensor Classifier::predict(Tensor& X)
|
torch::Tensor Classifier::predict(torch::Tensor& X)
|
||||||
{
|
{
|
||||||
if (!fitted) {
|
if (!fitted) {
|
||||||
throw logic_error("Classifier has not been fitted");
|
throw std::logic_error(CLASSIFIER_NOT_FITTED);
|
||||||
}
|
}
|
||||||
return model.predict(X);
|
return model.predict(X);
|
||||||
}
|
}
|
||||||
vector<int> Classifier::predict(vector<vector<int>>& X)
|
std::vector<int> Classifier::predict(std::vector<std::vector<int>>& X)
|
||||||
{
|
{
|
||||||
if (!fitted) {
|
if (!fitted) {
|
||||||
throw logic_error("Classifier has not been fitted");
|
throw std::logic_error(CLASSIFIER_NOT_FITTED);
|
||||||
}
|
}
|
||||||
auto m_ = X[0].size();
|
auto m_ = X[0].size();
|
||||||
auto n_ = X.size();
|
auto n_ = X.size();
|
||||||
vector<vector<int>> Xd(n_, vector<int>(m_, 0));
|
std::vector<std::vector<int>> Xd(n_, std::vector<int>(m_, 0));
|
||||||
for (auto i = 0; i < n_; i++) {
|
for (auto i = 0; i < n_; i++) {
|
||||||
Xd[i] = vector<int>(X[i].begin(), X[i].end());
|
Xd[i] = std::vector<int>(X[i].begin(), X[i].end());
|
||||||
}
|
}
|
||||||
auto yp = model.predict(Xd);
|
auto yp = model.predict(Xd);
|
||||||
return yp;
|
return yp;
|
||||||
}
|
}
|
||||||
float Classifier::score(Tensor& X, Tensor& y)
|
torch::Tensor Classifier::predict_proba(torch::Tensor& X)
|
||||||
{
|
{
|
||||||
if (!fitted) {
|
if (!fitted) {
|
||||||
throw logic_error("Classifier has not been fitted");
|
throw std::logic_error(CLASSIFIER_NOT_FITTED);
|
||||||
}
|
}
|
||||||
Tensor y_pred = predict(X);
|
return model.predict_proba(X);
|
||||||
|
}
|
||||||
|
std::vector<std::vector<double>> Classifier::predict_proba(std::vector<std::vector<int>>& X)
|
||||||
|
{
|
||||||
|
if (!fitted) {
|
||||||
|
throw std::logic_error(CLASSIFIER_NOT_FITTED);
|
||||||
|
}
|
||||||
|
auto m_ = X[0].size();
|
||||||
|
auto n_ = X.size();
|
||||||
|
std::vector<std::vector<int>> Xd(n_, std::vector<int>(m_, 0));
|
||||||
|
// Convert to nxm vector
|
||||||
|
for (auto i = 0; i < n_; i++) {
|
||||||
|
Xd[i] = std::vector<int>(X[i].begin(), X[i].end());
|
||||||
|
}
|
||||||
|
auto yp = model.predict_proba(Xd);
|
||||||
|
return yp;
|
||||||
|
}
|
||||||
|
float Classifier::score(torch::Tensor& X, torch::Tensor& y)
|
||||||
|
{
|
||||||
|
torch::Tensor y_pred = predict(X);
|
||||||
return (y_pred == y).sum().item<float>() / y.size(0);
|
return (y_pred == y).sum().item<float>() / y.size(0);
|
||||||
}
|
}
|
||||||
float Classifier::score(vector<vector<int>>& X, vector<int>& y)
|
float Classifier::score(std::vector<std::vector<int>>& X, std::vector<int>& y)
|
||||||
{
|
{
|
||||||
if (!fitted) {
|
if (!fitted) {
|
||||||
throw logic_error("Classifier has not been fitted");
|
throw std::logic_error(CLASSIFIER_NOT_FITTED);
|
||||||
}
|
}
|
||||||
return model.score(X, y);
|
return model.score(X, y);
|
||||||
}
|
}
|
||||||
vector<string> Classifier::show() const
|
std::vector<std::string> Classifier::show() const
|
||||||
{
|
{
|
||||||
return model.show();
|
return model.show();
|
||||||
}
|
}
|
||||||
@@ -134,7 +155,7 @@ namespace bayesnet {
|
|||||||
int Classifier::getNumberOfNodes() const
|
int Classifier::getNumberOfNodes() const
|
||||||
{
|
{
|
||||||
// Features does not include class
|
// Features does not include class
|
||||||
return fitted ? model.getFeatures().size() + 1 : 0;
|
return fitted ? model.getFeatures().size() : 0;
|
||||||
}
|
}
|
||||||
int Classifier::getNumberOfEdges() const
|
int Classifier::getNumberOfEdges() const
|
||||||
{
|
{
|
||||||
@@ -144,7 +165,11 @@ namespace bayesnet {
|
|||||||
{
|
{
|
||||||
return fitted ? model.getStates() : 0;
|
return fitted ? model.getStates() : 0;
|
||||||
}
|
}
|
||||||
vector<string> Classifier::topological_order()
|
int Classifier::getClassNumStates() const
|
||||||
|
{
|
||||||
|
return fitted ? model.getClassNumStates() : 0;
|
||||||
|
}
|
||||||
|
std::vector<std::string> Classifier::topological_order()
|
||||||
{
|
{
|
||||||
return model.topological_sort();
|
return model.topological_sort();
|
||||||
}
|
}
|
||||||
@@ -152,4 +177,8 @@ namespace bayesnet {
|
|||||||
{
|
{
|
||||||
model.dump_cpt();
|
model.dump_cpt();
|
||||||
}
|
}
|
||||||
|
void Classifier::setHyperparameters(const nlohmann::json& hyperparameters)
|
||||||
|
{
|
||||||
|
//For classifiers that don't have hyperparameters
|
||||||
|
}
|
||||||
}
|
}
|
59
src/classifiers/Classifier.h
Normal file
59
src/classifiers/Classifier.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef CLASSIFIER_H
|
||||||
|
#define CLASSIFIER_H
|
||||||
|
#include <torch/torch.h>
|
||||||
|
#include "BaseClassifier.h"
|
||||||
|
#include "Network.h"
|
||||||
|
#include "BayesMetrics.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class Classifier : public BaseClassifier {
|
||||||
|
public:
|
||||||
|
Classifier(Network model);
|
||||||
|
virtual ~Classifier() = default;
|
||||||
|
Classifier& fit(std::vector<std::vector<int>>& X, std::vector<int>& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states) override;
|
||||||
|
Classifier& fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states) override;
|
||||||
|
Classifier& fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states) override;
|
||||||
|
Classifier& fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights) override;
|
||||||
|
void addNodes();
|
||||||
|
int getNumberOfNodes() const override;
|
||||||
|
int getNumberOfEdges() const override;
|
||||||
|
int getNumberOfStates() const override;
|
||||||
|
int getClassNumStates() const override;
|
||||||
|
torch::Tensor predict(torch::Tensor& X) override;
|
||||||
|
std::vector<int> predict(std::vector<std::vector<int>>& X) override;
|
||||||
|
torch::Tensor predict_proba(torch::Tensor& X) override;
|
||||||
|
std::vector<std::vector<double>> predict_proba(std::vector<std::vector<int>>& X) override;
|
||||||
|
status_t getStatus() const override { return status; }
|
||||||
|
std::string getVersion() override { return { project_version.begin(), project_version.end() }; };
|
||||||
|
float score(torch::Tensor& X, torch::Tensor& y) override;
|
||||||
|
float score(std::vector<std::vector<int>>& X, std::vector<int>& y) override;
|
||||||
|
std::vector<std::string> show() const override;
|
||||||
|
std::vector<std::string> topological_order() override;
|
||||||
|
std::vector<std::string> getNotes() const override { return notes; }
|
||||||
|
void dump_cpt() const override;
|
||||||
|
void setHyperparameters(const nlohmann::json& hyperparameters) override; //For classifiers that don't have hyperparameters
|
||||||
|
protected:
|
||||||
|
bool fitted;
|
||||||
|
unsigned int m, n; // m: number of samples, n: number of features
|
||||||
|
Network model;
|
||||||
|
Metrics metrics;
|
||||||
|
std::vector<std::string> features;
|
||||||
|
std::string className;
|
||||||
|
std::map<std::string, std::vector<int>> states;
|
||||||
|
torch::Tensor dataset; // (n+1)xm tensor
|
||||||
|
status_t status = NORMAL;
|
||||||
|
std::vector<std::string> notes; // Used to store messages occurred during the fit process
|
||||||
|
void checkFitParameters();
|
||||||
|
virtual void buildModel(const torch::Tensor& weights) = 0;
|
||||||
|
void trainModel(const torch::Tensor& weights) override;
|
||||||
|
void buildDataset(torch::Tensor& y);
|
||||||
|
private:
|
||||||
|
Classifier& build(const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,9 +1,20 @@
|
|||||||
#include "KDB.h"
|
#include "KDB.h"
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace torch;
|
KDB::KDB(int k, float theta) : Classifier(Network()), k(k), theta(theta)
|
||||||
|
{
|
||||||
|
validHyperparameters = { "k", "theta" };
|
||||||
|
|
||||||
KDB::KDB(int k, float theta) : Classifier(Network()), k(k), theta(theta) {}
|
}
|
||||||
|
void KDB::setHyperparameters(const nlohmann::json& hyperparameters)
|
||||||
|
{
|
||||||
|
if (hyperparameters.contains("k")) {
|
||||||
|
k = hyperparameters["k"];
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("theta")) {
|
||||||
|
theta = hyperparameters["theta"];
|
||||||
|
}
|
||||||
|
}
|
||||||
void KDB::buildModel(const torch::Tensor& weights)
|
void KDB::buildModel(const torch::Tensor& weights)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -28,16 +39,16 @@ namespace bayesnet {
|
|||||||
// 1. For each feature Xi, compute mutual information, I(X;C),
|
// 1. For each feature Xi, compute mutual information, I(X;C),
|
||||||
// where C is the class.
|
// where C is the class.
|
||||||
addNodes();
|
addNodes();
|
||||||
const Tensor& y = dataset.index({ -1, "..." });
|
const torch::Tensor& y = dataset.index({ -1, "..." });
|
||||||
vector<double> mi;
|
std::vector<double> mi;
|
||||||
for (auto i = 0; i < features.size(); i++) {
|
for (auto i = 0; i < features.size(); i++) {
|
||||||
Tensor firstFeature = dataset.index({ i, "..." });
|
torch::Tensor firstFeature = dataset.index({ i, "..." });
|
||||||
mi.push_back(metrics.mutualInformation(firstFeature, y, weights));
|
mi.push_back(metrics.mutualInformation(firstFeature, y, weights));
|
||||||
}
|
}
|
||||||
// 2. Compute class conditional mutual information I(Xi;XjIC), f or each
|
// 2. Compute class conditional mutual information I(Xi;XjIC), f or each
|
||||||
auto conditionalEdgeWeights = metrics.conditionalEdge(weights);
|
auto conditionalEdgeWeights = metrics.conditionalEdge(weights);
|
||||||
// 3. Let the used variable list, S, be empty.
|
// 3. Let the used variable list, S, be empty.
|
||||||
vector<int> S;
|
std::vector<int> S;
|
||||||
// 4. Let the DAG network being constructed, BN, begin with a single
|
// 4. Let the DAG network being constructed, BN, begin with a single
|
||||||
// class node, C.
|
// class node, C.
|
||||||
// 5. Repeat until S includes all domain features
|
// 5. Repeat until S includes all domain features
|
||||||
@@ -55,9 +66,9 @@ namespace bayesnet {
|
|||||||
S.push_back(idx);
|
S.push_back(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void KDB::add_m_edges(int idx, vector<int>& S, Tensor& weights)
|
void KDB::add_m_edges(int idx, std::vector<int>& S, torch::Tensor& weights)
|
||||||
{
|
{
|
||||||
auto n_edges = min(k, static_cast<int>(S.size()));
|
auto n_edges = std::min(k, static_cast<int>(S.size()));
|
||||||
auto cond_w = clone(weights);
|
auto cond_w = clone(weights);
|
||||||
bool exit_cond = k == 0;
|
bool exit_cond = k == 0;
|
||||||
int num = 0;
|
int num = 0;
|
||||||
@@ -69,7 +80,7 @@ namespace bayesnet {
|
|||||||
model.addEdge(features[max_minfo], features[idx]);
|
model.addEdge(features[max_minfo], features[idx]);
|
||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
catch (const invalid_argument& e) {
|
catch (const std::invalid_argument& e) {
|
||||||
// Loops are not allowed
|
// Loops are not allowed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,11 +90,11 @@ namespace bayesnet {
|
|||||||
exit_cond = num == n_edges || candidates.size(0) == 0;
|
exit_cond = num == n_edges || candidates.size(0) == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vector<string> KDB::graph(const string& title) const
|
std::vector<std::string> KDB::graph(const std::string& title) const
|
||||||
{
|
{
|
||||||
string header{ title };
|
std::string header{ title };
|
||||||
if (title == "KDB") {
|
if (title == "KDB") {
|
||||||
header += " (k=" + to_string(k) + ", theta=" + to_string(theta) + ")";
|
header += " (k=" + std::to_string(k) + ", theta=" + std::to_string(theta) + ")";
|
||||||
}
|
}
|
||||||
return model.graph(header);
|
return model.graph(header);
|
||||||
}
|
}
|
@@ -4,19 +4,18 @@
|
|||||||
#include "Classifier.h"
|
#include "Classifier.h"
|
||||||
#include "bayesnetUtils.h"
|
#include "bayesnetUtils.h"
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace std;
|
|
||||||
using namespace torch;
|
|
||||||
class KDB : public Classifier {
|
class KDB : public Classifier {
|
||||||
private:
|
private:
|
||||||
int k;
|
int k;
|
||||||
float theta;
|
float theta;
|
||||||
void add_m_edges(int idx, vector<int>& S, Tensor& weights);
|
void add_m_edges(int idx, std::vector<int>& S, torch::Tensor& weights);
|
||||||
protected:
|
protected:
|
||||||
void buildModel(const torch::Tensor& weights) override;
|
void buildModel(const torch::Tensor& weights) override;
|
||||||
public:
|
public:
|
||||||
explicit KDB(int k, float theta = 0.03);
|
explicit KDB(int k, float theta = 0.03);
|
||||||
virtual ~KDB() {};
|
virtual ~KDB() = default;
|
||||||
vector<string> graph(const string& name = "KDB") const override;
|
void setHyperparameters(const nlohmann::json& hyperparameters) override;
|
||||||
|
std::vector<std::string> graph(const std::string& name = "KDB") const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@@ -1,16 +1,15 @@
|
|||||||
#include "KDBLd.h"
|
#include "KDBLd.h"
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace std;
|
|
||||||
KDBLd::KDBLd(int k) : KDB(k), Proposal(dataset, features, className) {}
|
KDBLd::KDBLd(int k) : KDB(k), Proposal(dataset, features, className) {}
|
||||||
KDBLd& KDBLd::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
KDBLd& KDBLd::fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_)
|
||||||
{
|
{
|
||||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
checkInput(X_, y_);
|
||||||
features = features_;
|
features = features_;
|
||||||
className = className_;
|
className = className_;
|
||||||
Xf = X_;
|
Xf = X_;
|
||||||
y = y_;
|
y = y_;
|
||||||
// Fills vectors Xv & yv with the data from tensors X_ (discretized) & y
|
// Fills std::vectors Xv & yv with the data from tensors X_ (discretized) & y
|
||||||
states = fit_local_discretization(y);
|
states = fit_local_discretization(y);
|
||||||
// We have discretized the input data
|
// We have discretized the input data
|
||||||
// 1st we need to fit the model to build the normal KDB structure, KDB::fit initializes the base Bayesian network
|
// 1st we need to fit the model to build the normal KDB structure, KDB::fit initializes the base Bayesian network
|
||||||
@@ -18,12 +17,12 @@ namespace bayesnet {
|
|||||||
states = localDiscretizationProposal(states, model);
|
states = localDiscretizationProposal(states, model);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
Tensor KDBLd::predict(Tensor& X)
|
torch::Tensor KDBLd::predict(torch::Tensor& X)
|
||||||
{
|
{
|
||||||
auto Xt = prepareX(X);
|
auto Xt = prepareX(X);
|
||||||
return KDB::predict(Xt);
|
return KDB::predict(Xt);
|
||||||
}
|
}
|
||||||
vector<string> KDBLd::graph(const string& name) const
|
std::vector<std::string> KDBLd::graph(const std::string& name) const
|
||||||
{
|
{
|
||||||
return KDB::graph(name);
|
return KDB::graph(name);
|
||||||
}
|
}
|
18
src/classifiers/KDBLd.h
Normal file
18
src/classifiers/KDBLd.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef KDBLD_H
|
||||||
|
#define KDBLD_H
|
||||||
|
#include "KDB.h"
|
||||||
|
#include "Proposal.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class KDBLd : public KDB, public Proposal {
|
||||||
|
private:
|
||||||
|
public:
|
||||||
|
explicit KDBLd(int k);
|
||||||
|
virtual ~KDBLd() = default;
|
||||||
|
KDBLd& fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, map<std::string, std::vector<int>>& states) override;
|
||||||
|
std::vector<std::string> graph(const std::string& name = "KDB") const override;
|
||||||
|
torch::Tensor predict(torch::Tensor& X) override;
|
||||||
|
static inline std::string version() { return "0.0.1"; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // !KDBLD_H
|
@@ -2,21 +2,30 @@
|
|||||||
#include "ArffFiles.h"
|
#include "ArffFiles.h"
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
Proposal::Proposal(torch::Tensor& dataset_, vector<string>& features_, string& className_) : pDataset(dataset_), pFeatures(features_), pClassName(className_) {}
|
Proposal::Proposal(torch::Tensor& dataset_, std::vector<std::string>& features_, std::string& className_) : pDataset(dataset_), pFeatures(features_), pClassName(className_) {}
|
||||||
Proposal::~Proposal()
|
Proposal::~Proposal()
|
||||||
{
|
{
|
||||||
for (auto& [key, value] : discretizers) {
|
for (auto& [key, value] : discretizers) {
|
||||||
delete value;
|
delete value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map<string, vector<int>> Proposal::localDiscretizationProposal(const map<string, vector<int>>& oldStates, Network& model)
|
void Proposal::checkInput(const torch::Tensor& X, const torch::Tensor& y)
|
||||||
|
{
|
||||||
|
if (!torch::is_floating_point(X)) {
|
||||||
|
throw std::invalid_argument("X must be a floating point tensor");
|
||||||
|
}
|
||||||
|
if (torch::is_floating_point(y)) {
|
||||||
|
throw std::invalid_argument("y must be an integer tensor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map<std::string, std::vector<int>> Proposal::localDiscretizationProposal(const map<std::string, std::vector<int>>& oldStates, Network& model)
|
||||||
{
|
{
|
||||||
// order of local discretization is important. no good 0, 1, 2...
|
// order of local discretization is important. no good 0, 1, 2...
|
||||||
// although we rediscretize features after the local discretization of every feature
|
// although we rediscretize features after the local discretization of every feature
|
||||||
auto order = model.topological_sort();
|
auto order = model.topological_sort();
|
||||||
auto& nodes = model.getNodes();
|
auto& nodes = model.getNodes();
|
||||||
map<string, vector<int>> states = oldStates;
|
map<std::string, std::vector<int>> states = oldStates;
|
||||||
vector<int> indicesToReDiscretize;
|
std::vector<int> indicesToReDiscretize;
|
||||||
bool upgrade = false; // Flag to check if we need to upgrade the model
|
bool upgrade = false; // Flag to check if we need to upgrade the model
|
||||||
for (auto feature : order) {
|
for (auto feature : order) {
|
||||||
auto nodeParents = nodes[feature]->getParents();
|
auto nodeParents = nodes[feature]->getParents();
|
||||||
@@ -24,16 +33,16 @@ namespace bayesnet {
|
|||||||
upgrade = true;
|
upgrade = true;
|
||||||
int index = find(pFeatures.begin(), pFeatures.end(), feature) - pFeatures.begin();
|
int index = find(pFeatures.begin(), pFeatures.end(), feature) - pFeatures.begin();
|
||||||
indicesToReDiscretize.push_back(index); // We need to re-discretize this feature
|
indicesToReDiscretize.push_back(index); // We need to re-discretize this feature
|
||||||
vector<string> parents;
|
std::vector<std::string> parents;
|
||||||
transform(nodeParents.begin(), nodeParents.end(), back_inserter(parents), [](const auto& p) { return p->getName(); });
|
transform(nodeParents.begin(), nodeParents.end(), back_inserter(parents), [](const auto& p) { return p->getName(); });
|
||||||
// Remove class as parent as it will be added later
|
// Remove class as parent as it will be added later
|
||||||
parents.erase(remove(parents.begin(), parents.end(), pClassName), parents.end());
|
parents.erase(remove(parents.begin(), parents.end(), pClassName), parents.end());
|
||||||
// Get the indices of the parents
|
// Get the indices of the parents
|
||||||
vector<int> indices;
|
std::vector<int> indices;
|
||||||
indices.push_back(-1); // Add class index
|
indices.push_back(-1); // Add class index
|
||||||
transform(parents.begin(), parents.end(), back_inserter(indices), [&](const auto& p) {return find(pFeatures.begin(), pFeatures.end(), p) - pFeatures.begin(); });
|
transform(parents.begin(), parents.end(), back_inserter(indices), [&](const auto& p) {return find(pFeatures.begin(), pFeatures.end(), p) - pFeatures.begin(); });
|
||||||
// Now we fit the discretizer of the feature, conditioned on its parents and the class i.e. discretizer.fit(X[index], X[indices] + y)
|
// Now we fit the discretizer of the feature, conditioned on its parents and the class i.e. discretizer.fit(X[index], X[indices] + y)
|
||||||
vector<string> yJoinParents(Xf.size(1));
|
std::vector<std::string> yJoinParents(Xf.size(1));
|
||||||
for (auto idx : indices) {
|
for (auto idx : indices) {
|
||||||
for (int i = 0; i < Xf.size(1); ++i) {
|
for (int i = 0; i < Xf.size(1); ++i) {
|
||||||
yJoinParents[i] += to_string(pDataset.index({ idx, i }).item<int>());
|
yJoinParents[i] += to_string(pDataset.index({ idx, i }).item<int>());
|
||||||
@@ -42,25 +51,16 @@ namespace bayesnet {
|
|||||||
auto arff = ArffFiles();
|
auto arff = ArffFiles();
|
||||||
auto yxv = arff.factorize(yJoinParents);
|
auto yxv = arff.factorize(yJoinParents);
|
||||||
auto xvf_ptr = Xf.index({ index }).data_ptr<float>();
|
auto xvf_ptr = Xf.index({ index }).data_ptr<float>();
|
||||||
auto xvf = vector<mdlp::precision_t>(xvf_ptr, xvf_ptr + Xf.size(1));
|
auto xvf = std::vector<mdlp::precision_t>(xvf_ptr, xvf_ptr + Xf.size(1));
|
||||||
discretizers[feature]->fit(xvf, yxv);
|
discretizers[feature]->fit(xvf, yxv);
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// auto tmp = discretizers[feature]->transform(xvf);
|
|
||||||
// Xv[index] = tmp;
|
|
||||||
// auto xStates = vector<int>(discretizers[pFeatures[index]]->getCutPoints().size() + 1);
|
|
||||||
// iota(xStates.begin(), xStates.end(), 0);
|
|
||||||
// //Update new states of the feature/node
|
|
||||||
// states[feature] = xStates;
|
|
||||||
}
|
}
|
||||||
if (upgrade) {
|
if (upgrade) {
|
||||||
// Discretize again X (only the affected indices) with the new fitted discretizers
|
// Discretize again X (only the affected indices) with the new fitted discretizers
|
||||||
for (auto index : indicesToReDiscretize) {
|
for (auto index : indicesToReDiscretize) {
|
||||||
auto Xt_ptr = Xf.index({ index }).data_ptr<float>();
|
auto Xt_ptr = Xf.index({ index }).data_ptr<float>();
|
||||||
auto Xt = vector<float>(Xt_ptr, Xt_ptr + Xf.size(1));
|
auto Xt = std::vector<float>(Xt_ptr, Xt_ptr + Xf.size(1));
|
||||||
pDataset.index_put_({ index, "..." }, torch::tensor(discretizers[pFeatures[index]]->transform(Xt)));
|
pDataset.index_put_({ index, "..." }, torch::tensor(discretizers[pFeatures[index]]->transform(Xt)));
|
||||||
auto xStates = vector<int>(discretizers[pFeatures[index]]->getCutPoints().size() + 1);
|
auto xStates = std::vector<int>(discretizers[pFeatures[index]]->getCutPoints().size() + 1);
|
||||||
iota(xStates.begin(), xStates.end(), 0);
|
iota(xStates.begin(), xStates.end(), 0);
|
||||||
//Update new states of the feature/node
|
//Update new states of the feature/node
|
||||||
states[pFeatures[index]] = xStates;
|
states[pFeatures[index]] = xStates;
|
||||||
@@ -70,28 +70,28 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
map<string, vector<int>> Proposal::fit_local_discretization(const torch::Tensor& y)
|
map<std::string, std::vector<int>> Proposal::fit_local_discretization(const torch::Tensor& y)
|
||||||
{
|
{
|
||||||
// Discretize the continuous input data and build pDataset (Classifier::dataset)
|
// Discretize the continuous input data and build pDataset (Classifier::dataset)
|
||||||
int m = Xf.size(1);
|
int m = Xf.size(1);
|
||||||
int n = Xf.size(0);
|
int n = Xf.size(0);
|
||||||
map<string, vector<int>> states;
|
map<std::string, std::vector<int>> states;
|
||||||
pDataset = torch::zeros({ n + 1, m }, kInt32);
|
pDataset = torch::zeros({ n + 1, m }, torch::kInt32);
|
||||||
auto yv = vector<int>(y.data_ptr<int>(), y.data_ptr<int>() + y.size(0));
|
auto yv = std::vector<int>(y.data_ptr<int>(), y.data_ptr<int>() + y.size(0));
|
||||||
// discretize input data by feature(row)
|
// discretize input data by feature(row)
|
||||||
for (auto i = 0; i < pFeatures.size(); ++i) {
|
for (auto i = 0; i < pFeatures.size(); ++i) {
|
||||||
auto* discretizer = new mdlp::CPPFImdlp();
|
auto* discretizer = new mdlp::CPPFImdlp();
|
||||||
auto Xt_ptr = Xf.index({ i }).data_ptr<float>();
|
auto Xt_ptr = Xf.index({ i }).data_ptr<float>();
|
||||||
auto Xt = vector<float>(Xt_ptr, Xt_ptr + Xf.size(1));
|
auto Xt = std::vector<float>(Xt_ptr, Xt_ptr + Xf.size(1));
|
||||||
discretizer->fit(Xt, yv);
|
discretizer->fit(Xt, yv);
|
||||||
pDataset.index_put_({ i, "..." }, torch::tensor(discretizer->transform(Xt)));
|
pDataset.index_put_({ i, "..." }, torch::tensor(discretizer->transform(Xt)));
|
||||||
auto xStates = vector<int>(discretizer->getCutPoints().size() + 1);
|
auto xStates = std::vector<int>(discretizer->getCutPoints().size() + 1);
|
||||||
iota(xStates.begin(), xStates.end(), 0);
|
iota(xStates.begin(), xStates.end(), 0);
|
||||||
states[pFeatures[i]] = xStates;
|
states[pFeatures[i]] = xStates;
|
||||||
discretizers[pFeatures[i]] = discretizer;
|
discretizers[pFeatures[i]] = discretizer;
|
||||||
}
|
}
|
||||||
int n_classes = torch::max(y).item<int>() + 1;
|
int n_classes = torch::max(y).item<int>() + 1;
|
||||||
auto yStates = vector<int>(n_classes);
|
auto yStates = std::vector<int>(n_classes);
|
||||||
iota(yStates.begin(), yStates.end(), 0);
|
iota(yStates.begin(), yStates.end(), 0);
|
||||||
states[pClassName] = yStates;
|
states[pClassName] = yStates;
|
||||||
pDataset.index_put_({ n, "..." }, y);
|
pDataset.index_put_({ n, "..." }, y);
|
||||||
@@ -101,7 +101,7 @@ namespace bayesnet {
|
|||||||
{
|
{
|
||||||
auto Xtd = torch::zeros_like(X, torch::kInt32);
|
auto Xtd = torch::zeros_like(X, torch::kInt32);
|
||||||
for (int i = 0; i < X.size(0); ++i) {
|
for (int i = 0; i < X.size(0); ++i) {
|
||||||
auto Xt = vector<float>(X[i].data_ptr<float>(), X[i].data_ptr<float>() + X.size(1));
|
auto Xt = std::vector<float>(X[i].data_ptr<float>(), X[i].data_ptr<float>() + X.size(1));
|
||||||
auto Xd = discretizers[pFeatures[i]]->transform(Xt);
|
auto Xd = discretizers[pFeatures[i]]->transform(Xt);
|
||||||
Xtd.index_put_({ i }, torch::tensor(Xd, torch::kInt32));
|
Xtd.index_put_({ i }, torch::tensor(Xd, torch::kInt32));
|
||||||
}
|
}
|
30
src/classifiers/Proposal.h
Normal file
30
src/classifiers/Proposal.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef PROPOSAL_H
|
||||||
|
#define PROPOSAL_H
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <torch/torch.h>
|
||||||
|
#include "Network.h"
|
||||||
|
#include "CPPFImdlp.h"
|
||||||
|
#include "Classifier.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class Proposal {
|
||||||
|
public:
|
||||||
|
Proposal(torch::Tensor& pDataset, std::vector<std::string>& features_, std::string& className_);
|
||||||
|
virtual ~Proposal();
|
||||||
|
protected:
|
||||||
|
void checkInput(const torch::Tensor& X, const torch::Tensor& y);
|
||||||
|
torch::Tensor prepareX(torch::Tensor& X);
|
||||||
|
map<std::string, std::vector<int>> localDiscretizationProposal(const map<std::string, std::vector<int>>& states, Network& model);
|
||||||
|
map<std::string, std::vector<int>> fit_local_discretization(const torch::Tensor& y);
|
||||||
|
torch::Tensor Xf; // X continuous nxm tensor
|
||||||
|
torch::Tensor y; // y discrete nx1 tensor
|
||||||
|
map<std::string, mdlp::CPPFImdlp*> discretizers;
|
||||||
|
private:
|
||||||
|
torch::Tensor& pDataset; // (n+1)xm tensor
|
||||||
|
std::vector<std::string>& pFeatures;
|
||||||
|
std::string& pClassName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -17,7 +17,7 @@ namespace bayesnet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vector<string> SPODE::graph(const string& name) const
|
std::vector<std::string> SPODE::graph(const std::string& name) const
|
||||||
{
|
{
|
||||||
return model.graph(name);
|
return model.graph(name);
|
||||||
}
|
}
|
@@ -10,8 +10,8 @@ namespace bayesnet {
|
|||||||
void buildModel(const torch::Tensor& weights) override;
|
void buildModel(const torch::Tensor& weights) override;
|
||||||
public:
|
public:
|
||||||
explicit SPODE(int root);
|
explicit SPODE(int root);
|
||||||
virtual ~SPODE() {};
|
virtual ~SPODE() = default;
|
||||||
vector<string> graph(const string& name = "SPODE") const override;
|
std::vector<std::string> graph(const std::string& name = "SPODE") const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@@ -1,16 +1,15 @@
|
|||||||
#include "SPODELd.h"
|
#include "SPODELd.h"
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace std;
|
|
||||||
SPODELd::SPODELd(int root) : SPODE(root), Proposal(dataset, features, className) {}
|
SPODELd::SPODELd(int root) : SPODE(root), Proposal(dataset, features, className) {}
|
||||||
SPODELd& SPODELd::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
SPODELd& SPODELd::fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_)
|
||||||
{
|
{
|
||||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
checkInput(X_, y_);
|
||||||
features = features_;
|
features = features_;
|
||||||
className = className_;
|
className = className_;
|
||||||
Xf = X_;
|
Xf = X_;
|
||||||
y = y_;
|
y = y_;
|
||||||
// Fills vectors Xv & yv with the data from tensors X_ (discretized) & y
|
// Fills std::vectors Xv & yv with the data from tensors X_ (discretized) & y
|
||||||
states = fit_local_discretization(y);
|
states = fit_local_discretization(y);
|
||||||
// We have discretized the input data
|
// We have discretized the input data
|
||||||
// 1st we need to fit the model to build the normal SPODE structure, SPODE::fit initializes the base Bayesian network
|
// 1st we need to fit the model to build the normal SPODE structure, SPODE::fit initializes the base Bayesian network
|
||||||
@@ -18,14 +17,16 @@ namespace bayesnet {
|
|||||||
states = localDiscretizationProposal(states, model);
|
states = localDiscretizationProposal(states, model);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
SPODELd& SPODELd::fit(torch::Tensor& dataset, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
SPODELd& SPODELd::fit(torch::Tensor& dataset, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_)
|
||||||
{
|
{
|
||||||
|
if (!torch::is_floating_point(dataset)) {
|
||||||
|
throw std::runtime_error("Dataset must be a floating point tensor");
|
||||||
|
}
|
||||||
Xf = dataset.index({ torch::indexing::Slice(0, dataset.size(0) - 1), "..." }).clone();
|
Xf = dataset.index({ torch::indexing::Slice(0, dataset.size(0) - 1), "..." }).clone();
|
||||||
y = dataset.index({ -1, "..." }).clone();
|
y = dataset.index({ -1, "..." }).clone();
|
||||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
|
||||||
features = features_;
|
features = features_;
|
||||||
className = className_;
|
className = className_;
|
||||||
// Fills vectors Xv & yv with the data from tensors X_ (discretized) & y
|
// Fills std::vectors Xv & yv with the data from tensors X_ (discretized) & y
|
||||||
states = fit_local_discretization(y);
|
states = fit_local_discretization(y);
|
||||||
// We have discretized the input data
|
// We have discretized the input data
|
||||||
// 1st we need to fit the model to build the normal SPODE structure, SPODE::fit initializes the base Bayesian network
|
// 1st we need to fit the model to build the normal SPODE structure, SPODE::fit initializes the base Bayesian network
|
||||||
@@ -34,12 +35,12 @@ namespace bayesnet {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tensor SPODELd::predict(Tensor& X)
|
torch::Tensor SPODELd::predict(torch::Tensor& X)
|
||||||
{
|
{
|
||||||
auto Xt = prepareX(X);
|
auto Xt = prepareX(X);
|
||||||
return SPODE::predict(Xt);
|
return SPODE::predict(Xt);
|
||||||
}
|
}
|
||||||
vector<string> SPODELd::graph(const string& name) const
|
std::vector<std::string> SPODELd::graph(const std::string& name) const
|
||||||
{
|
{
|
||||||
return SPODE::graph(name);
|
return SPODE::graph(name);
|
||||||
}
|
}
|
18
src/classifiers/SPODELd.h
Normal file
18
src/classifiers/SPODELd.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef SPODELD_H
|
||||||
|
#define SPODELD_H
|
||||||
|
#include "SPODE.h"
|
||||||
|
#include "Proposal.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class SPODELd : public SPODE, public Proposal {
|
||||||
|
public:
|
||||||
|
explicit SPODELd(int root);
|
||||||
|
virtual ~SPODELd() = default;
|
||||||
|
SPODELd& fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, map<std::string, std::vector<int>>& states) override;
|
||||||
|
SPODELd& fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, map<std::string, std::vector<int>>& states) override;
|
||||||
|
std::vector<std::string> graph(const std::string& name = "SPODE") const override;
|
||||||
|
torch::Tensor predict(torch::Tensor& X) override;
|
||||||
|
static inline std::string version() { return "0.0.1"; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // !SPODELD_H
|
@@ -1,8 +1,6 @@
|
|||||||
#include "TAN.h"
|
#include "TAN.h"
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace torch;
|
|
||||||
|
|
||||||
TAN::TAN() : Classifier(Network()) {}
|
TAN::TAN() : Classifier(Network()) {}
|
||||||
|
|
||||||
void TAN::buildModel(const torch::Tensor& weights)
|
void TAN::buildModel(const torch::Tensor& weights)
|
||||||
@@ -11,10 +9,10 @@ namespace bayesnet {
|
|||||||
addNodes();
|
addNodes();
|
||||||
// 1. Compute mutual information between each feature and the class and set the root node
|
// 1. Compute mutual information between each feature and the class and set the root node
|
||||||
// as the highest mutual information with the class
|
// as the highest mutual information with the class
|
||||||
auto mi = vector <pair<int, float >>();
|
auto mi = std::vector <std::pair<int, float >>();
|
||||||
Tensor class_dataset = dataset.index({ -1, "..." });
|
torch::Tensor class_dataset = dataset.index({ -1, "..." });
|
||||||
for (int i = 0; i < static_cast<int>(features.size()); ++i) {
|
for (int i = 0; i < static_cast<int>(features.size()); ++i) {
|
||||||
Tensor feature_dataset = dataset.index({ i, "..." });
|
torch::Tensor feature_dataset = dataset.index({ i, "..." });
|
||||||
auto mi_value = metrics.mutualInformation(class_dataset, feature_dataset, weights);
|
auto mi_value = metrics.mutualInformation(class_dataset, feature_dataset, weights);
|
||||||
mi.push_back({ i, mi_value });
|
mi.push_back({ i, mi_value });
|
||||||
}
|
}
|
||||||
@@ -34,7 +32,7 @@ namespace bayesnet {
|
|||||||
model.addEdge(className, feature);
|
model.addEdge(className, feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vector<string> TAN::graph(const string& title) const
|
std::vector<std::string> TAN::graph(const std::string& title) const
|
||||||
{
|
{
|
||||||
return model.graph(title);
|
return model.graph(title);
|
||||||
}
|
}
|
@@ -2,16 +2,14 @@
|
|||||||
#define TAN_H
|
#define TAN_H
|
||||||
#include "Classifier.h"
|
#include "Classifier.h"
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace std;
|
|
||||||
using namespace torch;
|
|
||||||
class TAN : public Classifier {
|
class TAN : public Classifier {
|
||||||
private:
|
private:
|
||||||
protected:
|
protected:
|
||||||
void buildModel(const torch::Tensor& weights) override;
|
void buildModel(const torch::Tensor& weights) override;
|
||||||
public:
|
public:
|
||||||
TAN();
|
TAN();
|
||||||
virtual ~TAN() {};
|
virtual ~TAN() = default;
|
||||||
vector<string> graph(const string& name = "TAN") const override;
|
std::vector<std::string> graph(const std::string& name = "TAN") const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@@ -1,16 +1,15 @@
|
|||||||
#include "TANLd.h"
|
#include "TANLd.h"
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace std;
|
|
||||||
TANLd::TANLd() : TAN(), Proposal(dataset, features, className) {}
|
TANLd::TANLd() : TAN(), Proposal(dataset, features, className) {}
|
||||||
TANLd& TANLd::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
TANLd& TANLd::fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_)
|
||||||
{
|
{
|
||||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
checkInput(X_, y_);
|
||||||
features = features_;
|
features = features_;
|
||||||
className = className_;
|
className = className_;
|
||||||
Xf = X_;
|
Xf = X_;
|
||||||
y = y_;
|
y = y_;
|
||||||
// Fills vectors Xv & yv with the data from tensors X_ (discretized) & y
|
// Fills std::vectors Xv & yv with the data from tensors X_ (discretized) & y
|
||||||
states = fit_local_discretization(y);
|
states = fit_local_discretization(y);
|
||||||
// We have discretized the input data
|
// We have discretized the input data
|
||||||
// 1st we need to fit the model to build the normal TAN structure, TAN::fit initializes the base Bayesian network
|
// 1st we need to fit the model to build the normal TAN structure, TAN::fit initializes the base Bayesian network
|
||||||
@@ -19,12 +18,12 @@ namespace bayesnet {
|
|||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
}
|
}
|
||||||
Tensor TANLd::predict(Tensor& X)
|
torch::Tensor TANLd::predict(torch::Tensor& X)
|
||||||
{
|
{
|
||||||
auto Xt = prepareX(X);
|
auto Xt = prepareX(X);
|
||||||
return TAN::predict(Xt);
|
return TAN::predict(Xt);
|
||||||
}
|
}
|
||||||
vector<string> TANLd::graph(const string& name) const
|
std::vector<std::string> TANLd::graph(const std::string& name) const
|
||||||
{
|
{
|
||||||
return TAN::graph(name);
|
return TAN::graph(name);
|
||||||
}
|
}
|
18
src/classifiers/TANLd.h
Normal file
18
src/classifiers/TANLd.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef TANLD_H
|
||||||
|
#define TANLD_H
|
||||||
|
#include "TAN.h"
|
||||||
|
#include "Proposal.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class TANLd : public TAN, public Proposal {
|
||||||
|
private:
|
||||||
|
public:
|
||||||
|
TANLd();
|
||||||
|
virtual ~TANLd() = default;
|
||||||
|
TANLd& fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, map<std::string, std::vector<int>>& states) override;
|
||||||
|
std::vector<std::string> graph(const std::string& name = "TAN") const override;
|
||||||
|
torch::Tensor predict(torch::Tensor& X) override;
|
||||||
|
static inline std::string version() { return "0.0.1"; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // !TANLD_H
|
34
src/ensembles/AODE.cc
Normal file
34
src/ensembles/AODE.cc
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include "AODE.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
AODE::AODE(bool predict_voting) : Ensemble(predict_voting)
|
||||||
|
{
|
||||||
|
validHyperparameters = { "predict_voting" };
|
||||||
|
|
||||||
|
}
|
||||||
|
void AODE::setHyperparameters(const nlohmann::json& hyperparameters_)
|
||||||
|
{
|
||||||
|
auto hyperparameters = hyperparameters_;
|
||||||
|
if (hyperparameters.contains("predict_voting")) {
|
||||||
|
predict_voting = hyperparameters["predict_voting"];
|
||||||
|
hyperparameters.erase("predict_voting");
|
||||||
|
}
|
||||||
|
if (!hyperparameters.empty()) {
|
||||||
|
throw std::invalid_argument("Invalid hyperparameters" + hyperparameters.dump());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AODE::buildModel(const torch::Tensor& weights)
|
||||||
|
{
|
||||||
|
models.clear();
|
||||||
|
significanceModels.clear();
|
||||||
|
for (int i = 0; i < features.size(); ++i) {
|
||||||
|
models.push_back(std::make_unique<SPODE>(i));
|
||||||
|
}
|
||||||
|
n_models = models.size();
|
||||||
|
significanceModels = std::vector<double>(n_models, 1.0);
|
||||||
|
}
|
||||||
|
std::vector<std::string> AODE::graph(const std::string& title) const
|
||||||
|
{
|
||||||
|
return Ensemble::graph(title);
|
||||||
|
}
|
||||||
|
}
|
@@ -4,12 +4,13 @@
|
|||||||
#include "SPODE.h"
|
#include "SPODE.h"
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
class AODE : public Ensemble {
|
class AODE : public Ensemble {
|
||||||
|
public:
|
||||||
|
AODE(bool predict_voting = true);
|
||||||
|
virtual ~AODE() {};
|
||||||
|
void setHyperparameters(const nlohmann::json& hyperparameters) override;
|
||||||
|
std::vector<std::string> graph(const std::string& title = "AODE") const override;
|
||||||
protected:
|
protected:
|
||||||
void buildModel(const torch::Tensor& weights) override;
|
void buildModel(const torch::Tensor& weights) override;
|
||||||
public:
|
|
||||||
AODE();
|
|
||||||
virtual ~AODE() {};
|
|
||||||
vector<string> graph(const string& title = "AODE") const override;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
54
src/ensembles/AODELd.cc
Normal file
54
src/ensembles/AODELd.cc
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "AODELd.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
AODELd::AODELd(bool predict_voting) : Ensemble(predict_voting), Proposal(dataset, features, className)
|
||||||
|
{
|
||||||
|
validHyperparameters = { "predict_voting" };
|
||||||
|
|
||||||
|
}
|
||||||
|
void AODELd::setHyperparameters(const nlohmann::json& hyperparameters_)
|
||||||
|
{
|
||||||
|
auto hyperparameters = hyperparameters_;
|
||||||
|
if (hyperparameters.contains("predict_voting")) {
|
||||||
|
predict_voting = hyperparameters["predict_voting"];
|
||||||
|
hyperparameters.erase("predict_voting");
|
||||||
|
}
|
||||||
|
if (!hyperparameters.empty()) {
|
||||||
|
throw std::invalid_argument("Invalid hyperparameters" + hyperparameters.dump());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AODELd& AODELd::fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_)
|
||||||
|
{
|
||||||
|
checkInput(X_, y_);
|
||||||
|
features = features_;
|
||||||
|
className = className_;
|
||||||
|
Xf = X_;
|
||||||
|
y = y_;
|
||||||
|
// Fills std::vectors Xv & yv with the data from tensors X_ (discretized) & y
|
||||||
|
states = fit_local_discretization(y);
|
||||||
|
// We have discretized the input data
|
||||||
|
// 1st we need to fit the model to build the normal TAN structure, TAN::fit initializes the base Bayesian network
|
||||||
|
Ensemble::fit(dataset, features, className, states);
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
}
|
||||||
|
void AODELd::buildModel(const torch::Tensor& weights)
|
||||||
|
{
|
||||||
|
models.clear();
|
||||||
|
for (int i = 0; i < features.size(); ++i) {
|
||||||
|
models.push_back(std::make_unique<SPODELd>(i));
|
||||||
|
}
|
||||||
|
n_models = models.size();
|
||||||
|
significanceModels = std::vector<double>(n_models, 1.0);
|
||||||
|
}
|
||||||
|
void AODELd::trainModel(const torch::Tensor& weights)
|
||||||
|
{
|
||||||
|
for (const auto& model : models) {
|
||||||
|
model->fit(Xf, y, features, className, states);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<std::string> AODELd::graph(const std::string& name) const
|
||||||
|
{
|
||||||
|
return Ensemble::graph(name);
|
||||||
|
}
|
||||||
|
}
|
20
src/ensembles/AODELd.h
Normal file
20
src/ensembles/AODELd.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef AODELD_H
|
||||||
|
#define AODELD_H
|
||||||
|
#include "Ensemble.h"
|
||||||
|
#include "Proposal.h"
|
||||||
|
#include "SPODELd.h"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
class AODELd : public Ensemble, public Proposal {
|
||||||
|
public:
|
||||||
|
AODELd(bool predict_voting = true);
|
||||||
|
virtual ~AODELd() = default;
|
||||||
|
AODELd& fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_) override;
|
||||||
|
void setHyperparameters(const nlohmann::json& hyperparameters) override;
|
||||||
|
std::vector<std::string> graph(const std::string& name = "AODELd") const override;
|
||||||
|
protected:
|
||||||
|
void trainModel(const torch::Tensor& weights) override;
|
||||||
|
void buildModel(const torch::Tensor& weights) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // !AODELD_H
|
296
src/ensembles/BoostAODE.cc
Normal file
296
src/ensembles/BoostAODE.cc
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
#include <set>
|
||||||
|
#include <functional>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <tuple>
|
||||||
|
#include "BoostAODE.h"
|
||||||
|
#include "CFS.h"
|
||||||
|
#include "FCBF.h"
|
||||||
|
#include "IWSS.h"
|
||||||
|
#include "folding.hpp"
|
||||||
|
|
||||||
|
namespace bayesnet {
|
||||||
|
struct {
|
||||||
|
std::string CFS = "CFS";
|
||||||
|
std::string FCBF = "FCBF";
|
||||||
|
std::string IWSS = "IWSS";
|
||||||
|
}SelectFeatures;
|
||||||
|
struct {
|
||||||
|
std::string ASC = "asc";
|
||||||
|
std::string DESC = "desc";
|
||||||
|
std::string RAND = "rand";
|
||||||
|
}Orders;
|
||||||
|
BoostAODE::BoostAODE(bool predict_voting) : Ensemble(predict_voting)
|
||||||
|
{
|
||||||
|
validHyperparameters = {
|
||||||
|
"repeatSparent", "maxModels", "order", "convergence", "threshold",
|
||||||
|
"select_features", "tolerance", "predict_voting", "predict_single"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
void BoostAODE::buildModel(const torch::Tensor& weights)
|
||||||
|
{
|
||||||
|
// Models shall be built in trainModel
|
||||||
|
models.clear();
|
||||||
|
significanceModels.clear();
|
||||||
|
n_models = 0;
|
||||||
|
// Prepare the validation dataset
|
||||||
|
auto y_ = dataset.index({ -1, "..." });
|
||||||
|
if (convergence) {
|
||||||
|
// Prepare train & validation sets from train data
|
||||||
|
auto fold = folding::StratifiedKFold(5, y_, 271);
|
||||||
|
dataset_ = torch::clone(dataset);
|
||||||
|
// save input dataset
|
||||||
|
auto [train, test] = fold.getFold(0);
|
||||||
|
auto train_t = torch::tensor(train);
|
||||||
|
auto test_t = torch::tensor(test);
|
||||||
|
// Get train and validation sets
|
||||||
|
X_train = dataset.index({ torch::indexing::Slice(0, dataset.size(0) - 1), train_t });
|
||||||
|
y_train = dataset.index({ -1, train_t });
|
||||||
|
X_test = dataset.index({ torch::indexing::Slice(0, dataset.size(0) - 1), test_t });
|
||||||
|
y_test = dataset.index({ -1, test_t });
|
||||||
|
dataset = X_train;
|
||||||
|
m = X_train.size(1);
|
||||||
|
auto n_classes = states.at(className).size();
|
||||||
|
metrics = Metrics(dataset, features, className, n_classes);
|
||||||
|
// Build dataset with train data
|
||||||
|
buildDataset(y_train);
|
||||||
|
} else {
|
||||||
|
// Use all data to train
|
||||||
|
X_train = dataset.index({ torch::indexing::Slice(0, dataset.size(0) - 1), "..." });
|
||||||
|
y_train = y_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void BoostAODE::setHyperparameters(const nlohmann::json& hyperparameters_)
|
||||||
|
{
|
||||||
|
auto hyperparameters = hyperparameters_;
|
||||||
|
if (hyperparameters.contains("repeatSparent")) {
|
||||||
|
repeatSparent = hyperparameters["repeatSparent"];
|
||||||
|
hyperparameters.erase("repeatSparent");
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("maxModels")) {
|
||||||
|
maxModels = hyperparameters["maxModels"];
|
||||||
|
hyperparameters.erase("maxModels");
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("order")) {
|
||||||
|
std::vector<std::string> algos = { Orders.ASC, Orders.DESC, Orders.RAND };
|
||||||
|
order_algorithm = hyperparameters["order"];
|
||||||
|
if (std::find(algos.begin(), algos.end(), order_algorithm) == algos.end()) {
|
||||||
|
throw std::invalid_argument("Invalid order algorithm, valid values [" + Orders.ASC + ", " + Orders.DESC + ", " + Orders.RAND + "]");
|
||||||
|
}
|
||||||
|
hyperparameters.erase("order");
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("convergence")) {
|
||||||
|
convergence = hyperparameters["convergence"];
|
||||||
|
hyperparameters.erase("convergence");
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("predict_single")) {
|
||||||
|
predict_single = hyperparameters["predict_single"];
|
||||||
|
hyperparameters.erase("predict_single");
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("threshold")) {
|
||||||
|
threshold = hyperparameters["threshold"];
|
||||||
|
hyperparameters.erase("threshold");
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("tolerance")) {
|
||||||
|
tolerance = hyperparameters["tolerance"];
|
||||||
|
hyperparameters.erase("tolerance");
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("predict_voting")) {
|
||||||
|
predict_voting = hyperparameters["predict_voting"];
|
||||||
|
hyperparameters.erase("predict_voting");
|
||||||
|
}
|
||||||
|
if (hyperparameters.contains("select_features")) {
|
||||||
|
auto selectedAlgorithm = hyperparameters["select_features"];
|
||||||
|
std::vector<std::string> algos = { SelectFeatures.IWSS, SelectFeatures.CFS, SelectFeatures.CFS };
|
||||||
|
selectFeatures = true;
|
||||||
|
select_features_algorithm = selectedAlgorithm;
|
||||||
|
if (std::find(algos.begin(), algos.end(), selectedAlgorithm) == algos.end()) {
|
||||||
|
throw std::invalid_argument("Invalid selectFeatures value, valid values [" + SelectFeatures.IWSS + ", " + SelectFeatures.CFS + ", " + SelectFeatures.FCBF + "]");
|
||||||
|
}
|
||||||
|
hyperparameters.erase("select_features");
|
||||||
|
}
|
||||||
|
if (!hyperparameters.empty()) {
|
||||||
|
throw std::invalid_argument("Invalid hyperparameters" + hyperparameters.dump());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::tuple<torch::Tensor&, double, bool> update_weights(torch::Tensor& ytrain, torch::Tensor& ypred, torch::Tensor& weights)
|
||||||
|
{
|
||||||
|
bool terminate = false;
|
||||||
|
double alpha_t = 0;
|
||||||
|
auto mask_wrong = ypred != ytrain;
|
||||||
|
auto mask_right = ypred == ytrain;
|
||||||
|
auto masked_weights = weights * mask_wrong.to(weights.dtype());
|
||||||
|
double epsilon_t = masked_weights.sum().item<double>();
|
||||||
|
if (epsilon_t > 0.5) {
|
||||||
|
// Inverse the weights policy (plot ln(wt))
|
||||||
|
// "In each round of AdaBoost, there is a sanity check to ensure that the current base
|
||||||
|
// learner is better than random guess" (Zhi-Hua Zhou, 2012)
|
||||||
|
terminate = true;
|
||||||
|
} else {
|
||||||
|
double wt = (1 - epsilon_t) / epsilon_t;
|
||||||
|
alpha_t = epsilon_t == 0 ? 1 : 0.5 * log(wt);
|
||||||
|
// Step 3.2: Update weights for next classifier
|
||||||
|
// Step 3.2.1: Update weights of wrong samples
|
||||||
|
weights += mask_wrong.to(weights.dtype()) * exp(alpha_t) * weights;
|
||||||
|
// Step 3.2.2: Update weights of right samples
|
||||||
|
weights += mask_right.to(weights.dtype()) * exp(-alpha_t) * weights;
|
||||||
|
// Step 3.3: Normalise the weights
|
||||||
|
double totalWeights = torch::sum(weights).item<double>();
|
||||||
|
weights = weights / totalWeights;
|
||||||
|
}
|
||||||
|
return { weights, alpha_t, terminate };
|
||||||
|
}
|
||||||
|
std::unordered_set<int> BoostAODE::initializeModels()
|
||||||
|
{
|
||||||
|
std::unordered_set<int> featuresUsed;
|
||||||
|
torch::Tensor weights_ = torch::full({ m }, 1.0 / m, torch::kFloat64);
|
||||||
|
int maxFeatures = 0;
|
||||||
|
if (select_features_algorithm == SelectFeatures.CFS) {
|
||||||
|
featureSelector = new CFS(dataset, features, className, maxFeatures, states.at(className).size(), weights_);
|
||||||
|
} else if (select_features_algorithm == SelectFeatures.IWSS) {
|
||||||
|
if (threshold < 0 || threshold >0.5) {
|
||||||
|
throw std::invalid_argument("Invalid threshold value for " + SelectFeatures.IWSS + " [0, 0.5]");
|
||||||
|
}
|
||||||
|
featureSelector = new IWSS(dataset, features, className, maxFeatures, states.at(className).size(), weights_, threshold);
|
||||||
|
} else if (select_features_algorithm == SelectFeatures.FCBF) {
|
||||||
|
if (threshold < 1e-7 || threshold > 1) {
|
||||||
|
throw std::invalid_argument("Invalid threshold value for " + SelectFeatures.FCBF + " [1e-7, 1]");
|
||||||
|
}
|
||||||
|
featureSelector = new FCBF(dataset, features, className, maxFeatures, states.at(className).size(), weights_, threshold);
|
||||||
|
}
|
||||||
|
featureSelector->fit();
|
||||||
|
auto cfsFeatures = featureSelector->getFeatures();
|
||||||
|
for (const int& feature : cfsFeatures) {
|
||||||
|
featuresUsed.insert(feature);
|
||||||
|
std::unique_ptr<Classifier> model = std::make_unique<SPODE>(feature);
|
||||||
|
model->fit(dataset, features, className, states, weights_);
|
||||||
|
models.push_back(std::move(model));
|
||||||
|
significanceModels.push_back(1.0);
|
||||||
|
n_models++;
|
||||||
|
}
|
||||||
|
notes.push_back("Used features in initialization: " + std::to_string(featuresUsed.size()) + " of " + std::to_string(features.size()) + " with " + select_features_algorithm);
|
||||||
|
delete featureSelector;
|
||||||
|
return featuresUsed;
|
||||||
|
}
|
||||||
|
torch::Tensor BoostAODE::ensemble_predict(torch::Tensor& X, SPODE* model)
|
||||||
|
{
|
||||||
|
if (initialize_prob_table) {
|
||||||
|
initialize_prob_table = false;
|
||||||
|
prob_table = model->predict_proba(X) * 1.0;
|
||||||
|
} else {
|
||||||
|
prob_table += model->predict_proba(X) * 1.0;
|
||||||
|
}
|
||||||
|
// prob_table doesn't store probabilities but the sum of them
|
||||||
|
// to have them we need to divide by the sum of the "weights" used to
|
||||||
|
// consider the results obtanined in the model's predict_proba.
|
||||||
|
return prob_table.argmax(1);
|
||||||
|
}
|
||||||
|
void BoostAODE::trainModel(const torch::Tensor& weights)
|
||||||
|
{
|
||||||
|
// Algorithm based on the adaboost algorithm for classification
|
||||||
|
// as explained in Ensemble methods (Zhi-Hua Zhou, 2012)
|
||||||
|
initialize_prob_table = true;
|
||||||
|
fitted = true;
|
||||||
|
double alpha_t = 0;
|
||||||
|
torch::Tensor weights_ = torch::full({ m }, 1.0 / m, torch::kFloat64);
|
||||||
|
bool exitCondition = false;
|
||||||
|
std::unordered_set<int> featuresUsed;
|
||||||
|
if (selectFeatures) {
|
||||||
|
featuresUsed = initializeModels();
|
||||||
|
auto ypred = predict(X_train);
|
||||||
|
std::tie(weights_, alpha_t, exitCondition) = update_weights(y_train, ypred, weights_);
|
||||||
|
// Update significance of the models
|
||||||
|
for (int i = 0; i < n_models; ++i) {
|
||||||
|
significanceModels[i] = alpha_t;
|
||||||
|
}
|
||||||
|
if (exitCondition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool resetMaxModels = false;
|
||||||
|
if (maxModels == 0) {
|
||||||
|
maxModels = .1 * n > 10 ? .1 * n : n;
|
||||||
|
resetMaxModels = true; // Flag to unset maxModels
|
||||||
|
}
|
||||||
|
// Variables to control the accuracy finish condition
|
||||||
|
double priorAccuracy = 0.0;
|
||||||
|
double delta = 1.0;
|
||||||
|
double convergence_threshold = 1e-4;
|
||||||
|
int count = 0; // number of times the accuracy is lower than the convergence_threshold
|
||||||
|
// Step 0: Set the finish condition
|
||||||
|
// if not repeatSparent a finish condition is run out of features
|
||||||
|
// n_models == maxModels
|
||||||
|
// epsilon sub t > 0.5 => inverse the weights policy
|
||||||
|
// validation error is not decreasing
|
||||||
|
bool ascending = order_algorithm == Orders.ASC;
|
||||||
|
std::mt19937 g{ 173 };
|
||||||
|
while (!exitCondition) {
|
||||||
|
// Step 1: Build ranking with mutual information
|
||||||
|
auto featureSelection = metrics.SelectKBestWeighted(weights_, ascending, n); // Get all the features sorted
|
||||||
|
if (order_algorithm == Orders.RAND) {
|
||||||
|
std::shuffle(featureSelection.begin(), featureSelection.end(), g);
|
||||||
|
}
|
||||||
|
auto feature = featureSelection[0];
|
||||||
|
if (!repeatSparent || featuresUsed.size() < featureSelection.size()) {
|
||||||
|
bool used = true;
|
||||||
|
for (const auto& feat : featureSelection) {
|
||||||
|
if (std::find(featuresUsed.begin(), featuresUsed.end(), feat) != featuresUsed.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
used = false;
|
||||||
|
feature = feat;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (used) {
|
||||||
|
exitCondition = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::unique_ptr<Classifier> model;
|
||||||
|
model = std::make_unique<SPODE>(feature);
|
||||||
|
model->fit(dataset, features, className, states, weights_);
|
||||||
|
torch::Tensor ypred;
|
||||||
|
if (predict_single) {
|
||||||
|
ypred = model->predict(X_train);
|
||||||
|
} else {
|
||||||
|
ypred = ensemble_predict(X_train, dynamic_cast<SPODE*>(model.get()));
|
||||||
|
}
|
||||||
|
// Step 3.1: Compute the classifier amout of say
|
||||||
|
std::tie(weights_, alpha_t, exitCondition) = update_weights(y_train, ypred, weights_);
|
||||||
|
if (exitCondition) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Step 3.4: Store classifier and its accuracy to weigh its future vote
|
||||||
|
featuresUsed.insert(feature);
|
||||||
|
models.push_back(std::move(model));
|
||||||
|
significanceModels.push_back(alpha_t);
|
||||||
|
n_models++;
|
||||||
|
if (convergence) {
|
||||||
|
auto y_val_predict = predict(X_test);
|
||||||
|
double accuracy = (y_val_predict == y_test).sum().item<double>() / (double)y_test.size(0);
|
||||||
|
if (priorAccuracy == 0) {
|
||||||
|
priorAccuracy = accuracy;
|
||||||
|
} else {
|
||||||
|
delta = accuracy - priorAccuracy;
|
||||||
|
}
|
||||||
|
if (delta < convergence_threshold) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
priorAccuracy = accuracy;
|
||||||
|
}
|
||||||
|
exitCondition = n_models >= maxModels && repeatSparent || count > tolerance;
|
||||||
|
}
|
||||||
|
if (featuresUsed.size() != features.size()) {
|
||||||
|
notes.push_back("Used features in train: " + std::to_string(featuresUsed.size()) + " of " + std::to_string(features.size()));
|
||||||
|
status = WARNING;
|
||||||
|
}
|
||||||
|
notes.push_back("Number of models: " + std::to_string(n_models));
|
||||||
|
if (resetMaxModels) {
|
||||||
|
maxModels = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<std::string> BoostAODE::graph(const std::string& title) const
|
||||||
|
{
|
||||||
|
return Ensemble::graph(title);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user