cv-tda 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cv_tda-0.0.1/PKG-INFO +41 -0
- cv_tda-0.0.1/README.md +3 -0
- cv_tda-0.0.1/pyproject.toml +62 -0
- cv_tda-0.0.1/setup.cfg +4 -0
- cv_tda-0.0.1/src/cv_tda.egg-info/PKG-INFO +41 -0
- cv_tda-0.0.1/src/cv_tda.egg-info/SOURCES.txt +52 -0
- cv_tda-0.0.1/src/cv_tda.egg-info/dependency_links.txt +1 -0
- cv_tda-0.0.1/src/cv_tda.egg-info/requires.txt +22 -0
- cv_tda-0.0.1/src/cv_tda.egg-info/top_level.txt +1 -0
- cv_tda-0.0.1/src/cvtda/classification/NNClassifier.py +173 -0
- cv_tda-0.0.1/src/cvtda/classification/__init__.py +6 -0
- cv_tda-0.0.1/src/cvtda/classification/classify.py +209 -0
- cv_tda-0.0.1/src/cvtda/classification/estimate_quality.py +47 -0
- cv_tda-0.0.1/src/cvtda/classification/information_value.py +98 -0
- cv_tda-0.0.1/src/cvtda/dumping/__init__.py +9 -0
- cv_tda-0.0.1/src/cvtda/dumping/base.py +34 -0
- cv_tda-0.0.1/src/cvtda/dumping/devnull.py +18 -0
- cv_tda-0.0.1/src/cvtda/dumping/dump_name_concat.py +4 -0
- cv_tda-0.0.1/src/cvtda/dumping/numpy.py +36 -0
- cv_tda-0.0.1/src/cvtda/face_recognition/BaseLearner.py +67 -0
- cv_tda-0.0.1/src/cvtda/face_recognition/DiagramsLearner.py +33 -0
- cv_tda-0.0.1/src/cvtda/face_recognition/NNLearner.py +193 -0
- cv_tda-0.0.1/src/cvtda/face_recognition/SimpleTopologicalLearner.py +14 -0
- cv_tda-0.0.1/src/cvtda/face_recognition/__init__.py +4 -0
- cv_tda-0.0.1/src/cvtda/face_recognition/learn.py +99 -0
- cv_tda-0.0.1/src/cvtda/logging/__init__.py +8 -0
- cv_tda-0.0.1/src/cvtda/logging/base.py +39 -0
- cv_tda-0.0.1/src/cvtda/logging/cli.py +33 -0
- cv_tda-0.0.1/src/cvtda/logging/devnull.py +27 -0
- cv_tda-0.0.1/src/cvtda/neural_network/Dataset.py +101 -0
- cv_tda-0.0.1/src/cvtda/neural_network/NNBase.py +54 -0
- cv_tda-0.0.1/src/cvtda/neural_network/__init__.py +2 -0
- cv_tda-0.0.1/src/cvtda/topology/DiagramVectorizer.py +180 -0
- cv_tda-0.0.1/src/cvtda/topology/FeatureExtractor.py +89 -0
- cv_tda-0.0.1/src/cvtda/topology/FiltrationsExtractor.py +182 -0
- cv_tda-0.0.1/src/cvtda/topology/GeometryExtractor.py +163 -0
- cv_tda-0.0.1/src/cvtda/topology/GreyscaleExtractor.py +37 -0
- cv_tda-0.0.1/src/cvtda/topology/PointCloudsExtractor.py +37 -0
- cv_tda-0.0.1/src/cvtda/topology/__init__.py +7 -0
- cv_tda-0.0.1/src/cvtda/topology/interface/Extractor.py +123 -0
- cv_tda-0.0.1/src/cvtda/topology/interface/TopologicalExtractor.py +80 -0
- cv_tda-0.0.1/src/cvtda/topology/interface/__init__.py +2 -0
- cv_tda-0.0.1/src/cvtda/topology/utils/__init__.py +3 -0
- cv_tda-0.0.1/src/cvtda/topology/utils/hstack.py +30 -0
- cv_tda-0.0.1/src/cvtda/topology/utils/process_iter.py +7 -0
- cv_tda-0.0.1/src/cvtda/topology/utils/process_iter_dump.py +22 -0
- cv_tda-0.0.1/src/cvtda/utils/__init__.py +7 -0
- cv_tda-0.0.1/src/cvtda/utils/duplicates.py +105 -0
- cv_tda-0.0.1/src/cvtda/utils/image2pointcloud.py +20 -0
- cv_tda-0.0.1/src/cvtda/utils/rgb2gray.py +15 -0
- cv_tda-0.0.1/src/cvtda/utils/rgb2hsv.py +15 -0
- cv_tda-0.0.1/src/cvtda/utils/sequence2features.py +47 -0
- cv_tda-0.0.1/src/cvtda/utils/set_random_seed.py +13 -0
- cv_tda-0.0.1/src/cvtda/utils/spread_points.py +12 -0
cv_tda-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cv-tda
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A Library to Solve Various Computer Vision Problems Using Topological Data Analysis
|
|
5
|
+
Author-email: Aleksandr Abramov <ggttpo100ajiex@yandex.ru>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/TTPO100AJIEX/CVTDA
|
|
8
|
+
Keywords: topological data analysis,computer vision,algebraic topology,image analysis,classification,face recognition,topological features
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
16
|
+
Requires-Python: ==3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: giotto-tda==0.6.2
|
|
19
|
+
Requires-Dist: gudhi>=3.10.1
|
|
20
|
+
Requires-Dist: joblib>=1.4.2
|
|
21
|
+
Requires-Dist: scikit-learn==1.6.1
|
|
22
|
+
Requires-Dist: scikit-image>=0.25.0
|
|
23
|
+
Requires-Dist: tqdm>=4.67.1
|
|
24
|
+
Provides-Extra: classification
|
|
25
|
+
Requires-Dist: catboost>=1.2.7; extra == "classification"
|
|
26
|
+
Requires-Dist: xgboost>=3.0.0; extra == "classification"
|
|
27
|
+
Requires-Dist: matplotlib>=3.10.1; extra == "classification"
|
|
28
|
+
Requires-Dist: pandas>=2.2.3; extra == "classification"
|
|
29
|
+
Requires-Dist: torch>=2.5.1; extra == "classification"
|
|
30
|
+
Requires-Dist: torchvision>=0.20.1; extra == "classification"
|
|
31
|
+
Requires-Dist: torchph==0.0.0; extra == "classification"
|
|
32
|
+
Provides-Extra: face-recognition
|
|
33
|
+
Requires-Dist: pytorch-metric-learning==2.8.1; extra == "face-recognition"
|
|
34
|
+
Requires-Dist: matplotlib>=3.10.1; extra == "face-recognition"
|
|
35
|
+
Requires-Dist: torch>=2.5.1; extra == "face-recognition"
|
|
36
|
+
Requires-Dist: torchvision>=0.20.1; extra == "face-recognition"
|
|
37
|
+
Requires-Dist: torchph==0.0.0; extra == "face-recognition"
|
|
38
|
+
|
|
39
|
+
# Computer Vision With Topological Data Analysis
|
|
40
|
+
|
|
41
|
+
TO BE DONE
|
cv_tda-0.0.1/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cv-tda"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Aleksandr Abramov", email = "ggttpo100ajiex@yandex.ru" },
|
|
10
|
+
]
|
|
11
|
+
description = "A Library to Solve Various Computer Vision Problems Using Topological Data Analysis"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = "==3.10"
|
|
14
|
+
dependencies = [
|
|
15
|
+
"giotto-tda==0.6.2",
|
|
16
|
+
"gudhi>=3.10.1",
|
|
17
|
+
"joblib>=1.4.2",
|
|
18
|
+
"scikit-learn==1.6.1",
|
|
19
|
+
"scikit-image>=0.25.0",
|
|
20
|
+
"tqdm>=4.67.1",
|
|
21
|
+
]
|
|
22
|
+
keywords = [
|
|
23
|
+
"topological data analysis",
|
|
24
|
+
"computer vision",
|
|
25
|
+
"algebraic topology",
|
|
26
|
+
"image analysis",
|
|
27
|
+
"classification",
|
|
28
|
+
"face recognition",
|
|
29
|
+
"topological features",
|
|
30
|
+
]
|
|
31
|
+
classifiers = [
|
|
32
|
+
"Development Status :: 4 - Beta",
|
|
33
|
+
"Intended Audience :: Science/Research",
|
|
34
|
+
"Intended Audience :: Developers",
|
|
35
|
+
"Operating System :: OS Independent",
|
|
36
|
+
"Programming Language :: Python :: 3.10",
|
|
37
|
+
"Topic :: Scientific/Engineering",
|
|
38
|
+
"Topic :: Scientific/Engineering :: Image Processing",
|
|
39
|
+
]
|
|
40
|
+
license = "MIT"
|
|
41
|
+
license-files = ["LICENCE*"]
|
|
42
|
+
|
|
43
|
+
[project.optional-dependencies]
|
|
44
|
+
classification = [
|
|
45
|
+
"catboost>=1.2.7",
|
|
46
|
+
"xgboost>=3.0.0",
|
|
47
|
+
"matplotlib>=3.10.1",
|
|
48
|
+
"pandas>=2.2.3",
|
|
49
|
+
"torch>=2.5.1",
|
|
50
|
+
"torchvision>=0.20.1",
|
|
51
|
+
"torchph==0.0.0"
|
|
52
|
+
]
|
|
53
|
+
face_recognition = [
|
|
54
|
+
"pytorch-metric-learning==2.8.1",
|
|
55
|
+
"matplotlib>=3.10.1",
|
|
56
|
+
"torch>=2.5.1",
|
|
57
|
+
"torchvision>=0.20.1",
|
|
58
|
+
"torchph==0.0.0"
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[project.urls]
|
|
62
|
+
Repository = "https://github.com/TTPO100AJIEX/CVTDA"
|
cv_tda-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cv-tda
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A Library to Solve Various Computer Vision Problems Using Topological Data Analysis
|
|
5
|
+
Author-email: Aleksandr Abramov <ggttpo100ajiex@yandex.ru>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/TTPO100AJIEX/CVTDA
|
|
8
|
+
Keywords: topological data analysis,computer vision,algebraic topology,image analysis,classification,face recognition,topological features
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
16
|
+
Requires-Python: ==3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: giotto-tda==0.6.2
|
|
19
|
+
Requires-Dist: gudhi>=3.10.1
|
|
20
|
+
Requires-Dist: joblib>=1.4.2
|
|
21
|
+
Requires-Dist: scikit-learn==1.6.1
|
|
22
|
+
Requires-Dist: scikit-image>=0.25.0
|
|
23
|
+
Requires-Dist: tqdm>=4.67.1
|
|
24
|
+
Provides-Extra: classification
|
|
25
|
+
Requires-Dist: catboost>=1.2.7; extra == "classification"
|
|
26
|
+
Requires-Dist: xgboost>=3.0.0; extra == "classification"
|
|
27
|
+
Requires-Dist: matplotlib>=3.10.1; extra == "classification"
|
|
28
|
+
Requires-Dist: pandas>=2.2.3; extra == "classification"
|
|
29
|
+
Requires-Dist: torch>=2.5.1; extra == "classification"
|
|
30
|
+
Requires-Dist: torchvision>=0.20.1; extra == "classification"
|
|
31
|
+
Requires-Dist: torchph==0.0.0; extra == "classification"
|
|
32
|
+
Provides-Extra: face-recognition
|
|
33
|
+
Requires-Dist: pytorch-metric-learning==2.8.1; extra == "face-recognition"
|
|
34
|
+
Requires-Dist: matplotlib>=3.10.1; extra == "face-recognition"
|
|
35
|
+
Requires-Dist: torch>=2.5.1; extra == "face-recognition"
|
|
36
|
+
Requires-Dist: torchvision>=0.20.1; extra == "face-recognition"
|
|
37
|
+
Requires-Dist: torchph==0.0.0; extra == "face-recognition"
|
|
38
|
+
|
|
39
|
+
# Computer Vision With Topological Data Analysis
|
|
40
|
+
|
|
41
|
+
TO BE DONE
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/cv_tda.egg-info/PKG-INFO
|
|
4
|
+
src/cv_tda.egg-info/SOURCES.txt
|
|
5
|
+
src/cv_tda.egg-info/dependency_links.txt
|
|
6
|
+
src/cv_tda.egg-info/requires.txt
|
|
7
|
+
src/cv_tda.egg-info/top_level.txt
|
|
8
|
+
src/cvtda/classification/NNClassifier.py
|
|
9
|
+
src/cvtda/classification/__init__.py
|
|
10
|
+
src/cvtda/classification/classify.py
|
|
11
|
+
src/cvtda/classification/estimate_quality.py
|
|
12
|
+
src/cvtda/classification/information_value.py
|
|
13
|
+
src/cvtda/dumping/__init__.py
|
|
14
|
+
src/cvtda/dumping/base.py
|
|
15
|
+
src/cvtda/dumping/devnull.py
|
|
16
|
+
src/cvtda/dumping/dump_name_concat.py
|
|
17
|
+
src/cvtda/dumping/numpy.py
|
|
18
|
+
src/cvtda/face_recognition/BaseLearner.py
|
|
19
|
+
src/cvtda/face_recognition/DiagramsLearner.py
|
|
20
|
+
src/cvtda/face_recognition/NNLearner.py
|
|
21
|
+
src/cvtda/face_recognition/SimpleTopologicalLearner.py
|
|
22
|
+
src/cvtda/face_recognition/__init__.py
|
|
23
|
+
src/cvtda/face_recognition/learn.py
|
|
24
|
+
src/cvtda/logging/__init__.py
|
|
25
|
+
src/cvtda/logging/base.py
|
|
26
|
+
src/cvtda/logging/cli.py
|
|
27
|
+
src/cvtda/logging/devnull.py
|
|
28
|
+
src/cvtda/neural_network/Dataset.py
|
|
29
|
+
src/cvtda/neural_network/NNBase.py
|
|
30
|
+
src/cvtda/neural_network/__init__.py
|
|
31
|
+
src/cvtda/topology/DiagramVectorizer.py
|
|
32
|
+
src/cvtda/topology/FeatureExtractor.py
|
|
33
|
+
src/cvtda/topology/FiltrationsExtractor.py
|
|
34
|
+
src/cvtda/topology/GeometryExtractor.py
|
|
35
|
+
src/cvtda/topology/GreyscaleExtractor.py
|
|
36
|
+
src/cvtda/topology/PointCloudsExtractor.py
|
|
37
|
+
src/cvtda/topology/__init__.py
|
|
38
|
+
src/cvtda/topology/interface/Extractor.py
|
|
39
|
+
src/cvtda/topology/interface/TopologicalExtractor.py
|
|
40
|
+
src/cvtda/topology/interface/__init__.py
|
|
41
|
+
src/cvtda/topology/utils/__init__.py
|
|
42
|
+
src/cvtda/topology/utils/hstack.py
|
|
43
|
+
src/cvtda/topology/utils/process_iter.py
|
|
44
|
+
src/cvtda/topology/utils/process_iter_dump.py
|
|
45
|
+
src/cvtda/utils/__init__.py
|
|
46
|
+
src/cvtda/utils/duplicates.py
|
|
47
|
+
src/cvtda/utils/image2pointcloud.py
|
|
48
|
+
src/cvtda/utils/rgb2gray.py
|
|
49
|
+
src/cvtda/utils/rgb2hsv.py
|
|
50
|
+
src/cvtda/utils/sequence2features.py
|
|
51
|
+
src/cvtda/utils/set_random_seed.py
|
|
52
|
+
src/cvtda/utils/spread_points.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
giotto-tda==0.6.2
|
|
2
|
+
gudhi>=3.10.1
|
|
3
|
+
joblib>=1.4.2
|
|
4
|
+
scikit-learn==1.6.1
|
|
5
|
+
scikit-image>=0.25.0
|
|
6
|
+
tqdm>=4.67.1
|
|
7
|
+
|
|
8
|
+
[classification]
|
|
9
|
+
catboost>=1.2.7
|
|
10
|
+
xgboost>=3.0.0
|
|
11
|
+
matplotlib>=3.10.1
|
|
12
|
+
pandas>=2.2.3
|
|
13
|
+
torch>=2.5.1
|
|
14
|
+
torchvision>=0.20.1
|
|
15
|
+
torchph==0.0.0
|
|
16
|
+
|
|
17
|
+
[face_recognition]
|
|
18
|
+
pytorch-metric-learning==2.8.1
|
|
19
|
+
matplotlib>=3.10.1
|
|
20
|
+
torch>=2.5.1
|
|
21
|
+
torchvision>=0.20.1
|
|
22
|
+
torchph==0.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cvtda
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
import numpy
|
|
4
|
+
import torch
|
|
5
|
+
import sklearn.base
|
|
6
|
+
import sklearn.metrics
|
|
7
|
+
import torch.utils.data
|
|
8
|
+
|
|
9
|
+
import cvtda.utils
|
|
10
|
+
import cvtda.logging
|
|
11
|
+
import cvtda.neural_network
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NNClassifier(sklearn.base.ClassifierMixin):
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
|
|
18
|
+
n_jobs: int = -1,
|
|
19
|
+
random_state: int = 42,
|
|
20
|
+
|
|
21
|
+
device: torch.device = torch.device("cuda"),
|
|
22
|
+
batch_size: int = 256,
|
|
23
|
+
learning_rate: float = 1e-3,
|
|
24
|
+
n_epochs: int = 100,
|
|
25
|
+
|
|
26
|
+
skip_diagrams: bool = False,
|
|
27
|
+
skip_images: bool = False,
|
|
28
|
+
skip_features: bool = False
|
|
29
|
+
):
|
|
30
|
+
self.n_jobs_ = n_jobs
|
|
31
|
+
self.random_state_ = random_state
|
|
32
|
+
|
|
33
|
+
self.device_ = device
|
|
34
|
+
self.batch_size_ = batch_size
|
|
35
|
+
self.learning_rate_ = learning_rate
|
|
36
|
+
self.n_epochs_ = n_epochs
|
|
37
|
+
|
|
38
|
+
self.skip_diagrams_ = skip_diagrams
|
|
39
|
+
self.skip_images_ = skip_images
|
|
40
|
+
self.skip_features_ = skip_features
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def fit(self, train: cvtda.neural_network.Dataset, val: typing.Optional[cvtda.neural_network.Dataset]):
|
|
44
|
+
cvtda.utils.set_random_seed(self.random_state_)
|
|
45
|
+
train_dl = torch.utils.data.DataLoader(
|
|
46
|
+
torch.utils.data.TensorDataset(train.images, train.features, train.labels, torch.arange(len(train))),
|
|
47
|
+
batch_size = self.batch_size_,
|
|
48
|
+
shuffle = True
|
|
49
|
+
)
|
|
50
|
+
self.init_(len(torch.unique(train.labels)), *next(iter(train_dl)), train)
|
|
51
|
+
|
|
52
|
+
for epoch in range(self.n_epochs_):
|
|
53
|
+
sum_loss = 0
|
|
54
|
+
|
|
55
|
+
self.model_list_.train()
|
|
56
|
+
for images, features, labels, diagram_idxs in cvtda.logging.logger().pbar(train_dl, desc = f"Epoch {epoch}"):
|
|
57
|
+
self.optimizer_.zero_grad()
|
|
58
|
+
pred = self.forward_(images, features, diagram_idxs, train)
|
|
59
|
+
loss = torch.nn.functional.cross_entropy(pred, labels.to(self.device_), reduction = 'mean')
|
|
60
|
+
loss.backward()
|
|
61
|
+
self.optimizer_.step()
|
|
62
|
+
sum_loss += loss.item()
|
|
63
|
+
postfix = { 'loss': sum_loss, 'lr': self.optimizer_.param_groups[0]['lr'] }
|
|
64
|
+
self.scheduler_.step()
|
|
65
|
+
|
|
66
|
+
if val is not None:
|
|
67
|
+
val_proba = self.predict_proba_(val)
|
|
68
|
+
val_pred = numpy.argmax(val_proba, axis = 1)
|
|
69
|
+
postfix['val_acc'] = sklearn.metrics.accuracy_score(val.labels, val_pred)
|
|
70
|
+
|
|
71
|
+
print(f"Epoch {epoch}:", postfix)
|
|
72
|
+
|
|
73
|
+
self.fitted_ = True
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
def predict_proba(self, dataset: cvtda.neural_network.Dataset) -> numpy.ndarray:
|
|
77
|
+
assert self.fitted_ is True, 'fit() must be called before predict_proba()'
|
|
78
|
+
cvtda.utils.set_random_seed(self.random_state_)
|
|
79
|
+
return self.predict_proba_(dataset)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def init_(
|
|
83
|
+
self,
|
|
84
|
+
num_classes: int,
|
|
85
|
+
images: torch.Tensor,
|
|
86
|
+
features: torch.Tensor,
|
|
87
|
+
labels: torch.Tensor,
|
|
88
|
+
diagram_idxs: torch.Tensor,
|
|
89
|
+
dataset: cvtda.neural_network.Dataset
|
|
90
|
+
):
|
|
91
|
+
diagrams = [] if self.skip_diagrams_ else dataset.get_diagrams(diagram_idxs)
|
|
92
|
+
|
|
93
|
+
is_no_topology = (self.skip_diagrams_ and self.skip_features_ and not self.skip_images_)
|
|
94
|
+
print('Topology: ', not is_no_topology)
|
|
95
|
+
print('Images: ', images.shape)
|
|
96
|
+
print('Features: ', features.shape)
|
|
97
|
+
|
|
98
|
+
if is_no_topology:
|
|
99
|
+
images_output = num_classes
|
|
100
|
+
else:
|
|
101
|
+
images_output = 1024
|
|
102
|
+
|
|
103
|
+
self.nn_base_ = cvtda.neural_network.NNBase(
|
|
104
|
+
num_diagrams = len(diagrams) // 2,
|
|
105
|
+
skip_diagrams = self.skip_diagrams_,
|
|
106
|
+
skip_images = self.skip_images_,
|
|
107
|
+
skip_features = self.skip_features_,
|
|
108
|
+
images_n_channels = images.shape[1],
|
|
109
|
+
images_output = images_output
|
|
110
|
+
).to(self.device_).train()
|
|
111
|
+
|
|
112
|
+
self.model_ = torch.nn.Sequential(
|
|
113
|
+
torch.nn.Dropout(0.4), torch.nn.LazyLinear(256), torch.nn.BatchNorm1d(256), torch.nn.GELU(),
|
|
114
|
+
torch.nn.Dropout(0.3), torch.nn.Linear(256, 128), torch.nn.BatchNorm1d(128), torch.nn.GELU(),
|
|
115
|
+
torch.nn.Dropout(0.2), torch.nn.Linear(128, 64), torch.nn.BatchNorm1d(64), torch.nn.GELU(),
|
|
116
|
+
torch.nn.Dropout(0.1), torch.nn.Linear(64, 32), torch.nn.BatchNorm1d(32), torch.nn.GELU(),
|
|
117
|
+
torch.nn.Linear(32, num_classes)
|
|
118
|
+
).to(self.device_).train()
|
|
119
|
+
|
|
120
|
+
if is_no_topology:
|
|
121
|
+
self.model_ = torch.nn.Identity()
|
|
122
|
+
|
|
123
|
+
self.model_list_ = torch.nn.ModuleList([ self.nn_base_, self.model_ ])
|
|
124
|
+
|
|
125
|
+
self.optimizer_ = torch.optim.AdamW(
|
|
126
|
+
params = self.model_list_.parameters(),
|
|
127
|
+
lr = self.learning_rate_ * 100
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def lr_scheduler_lambda(epoch):
|
|
131
|
+
if epoch < self.n_epochs_ // 10:
|
|
132
|
+
return 1
|
|
133
|
+
if epoch < self.n_epochs_ // 4:
|
|
134
|
+
return 0.1
|
|
135
|
+
if epoch < self.n_epochs_ // 2:
|
|
136
|
+
return 0.01
|
|
137
|
+
if epoch < 3 * self.n_epochs_ // 4:
|
|
138
|
+
return 0.001
|
|
139
|
+
return 0.0001
|
|
140
|
+
self.scheduler_ = torch.optim.lr_scheduler.LambdaLR(self.optimizer_, lr_scheduler_lambda)
|
|
141
|
+
|
|
142
|
+
self.forward_(images, features, diagram_idxs, dataset)
|
|
143
|
+
if not is_no_topology:
|
|
144
|
+
cvtda.logging.logger().print(f'Input to LazyLinear: {self.model_[1].in_features}')
|
|
145
|
+
cvtda.logging.logger().print(f'Parameters: {sum(p.numel() for p in self.model_list_.parameters())}')
|
|
146
|
+
|
|
147
|
+
def forward_(
|
|
148
|
+
self,
|
|
149
|
+
images: torch.Tensor,
|
|
150
|
+
features: torch.Tensor,
|
|
151
|
+
diagram_idxs: torch.Tensor,
|
|
152
|
+
dataset: cvtda.neural_network.Dataset
|
|
153
|
+
) -> torch.Tensor:
|
|
154
|
+
images = images.to(self.device_)
|
|
155
|
+
features = features.to(self.device_)
|
|
156
|
+
if self.skip_diagrams_:
|
|
157
|
+
return self.model_(self.nn_base_(images, features))
|
|
158
|
+
return self.model_(self.nn_base_(images, features, *dataset.get_diagrams(diagram_idxs)))
|
|
159
|
+
|
|
160
|
+
def predict_proba_(self, dataset: cvtda.neural_network.Dataset) -> numpy.ndarray:
|
|
161
|
+
dl = torch.utils.data.DataLoader(
|
|
162
|
+
torch.utils.data.TensorDataset(dataset.images, dataset.features, torch.arange(len(dataset))),
|
|
163
|
+
batch_size = self.batch_size_,
|
|
164
|
+
shuffle = False
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
y_pred_proba = [ ]
|
|
168
|
+
self.model_list_.eval()
|
|
169
|
+
with torch.no_grad():
|
|
170
|
+
for images, features, diagram_idxs in dl:
|
|
171
|
+
y_pred_proba.append(self.forward_(images, features, diagram_idxs, dataset))
|
|
172
|
+
y_pred_proba = torch.vstack(y_pred_proba)
|
|
173
|
+
return torch.nn.functional.softmax(y_pred_proba, dim = 1).cpu().numpy()
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from .classify import classify
|
|
2
|
+
from .estimate_quality import estimate_quality
|
|
3
|
+
from .information_value import calculate_binary_information_value
|
|
4
|
+
from .information_value import calculate_information_value
|
|
5
|
+
from .information_value import InformationValueFeatureSelector
|
|
6
|
+
from .NNClassifier import NNClassifier
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import numpy
|
|
5
|
+
import torch
|
|
6
|
+
import pandas
|
|
7
|
+
import xgboost
|
|
8
|
+
import catboost
|
|
9
|
+
import sklearn.base
|
|
10
|
+
import sklearn.ensemble
|
|
11
|
+
import sklearn.neighbors
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
|
|
14
|
+
import cvtda.dumping
|
|
15
|
+
import cvtda.neural_network
|
|
16
|
+
from .NNClassifier import NNClassifier
|
|
17
|
+
from .estimate_quality import estimate_quality
|
|
18
|
+
|
|
19
|
+
def classify(
|
|
20
|
+
train_images: numpy.ndarray,
|
|
21
|
+
train_features: numpy.ndarray,
|
|
22
|
+
train_labels: numpy.ndarray,
|
|
23
|
+
train_diagrams: typing.Optional[typing.List[numpy.ndarray]],
|
|
24
|
+
|
|
25
|
+
test_images: numpy.ndarray,
|
|
26
|
+
test_features: numpy.ndarray,
|
|
27
|
+
test_labels: numpy.ndarray,
|
|
28
|
+
test_diagrams: typing.Optional[typing.List[numpy.ndarray]],
|
|
29
|
+
|
|
30
|
+
label_names: typing.Optional[typing.List[str]] = None,
|
|
31
|
+
confusion_matrix_include_values: bool = True,
|
|
32
|
+
|
|
33
|
+
n_jobs: int = -1,
|
|
34
|
+
random_state: int = 42,
|
|
35
|
+
dump_name: typing.Optional[str] = None,
|
|
36
|
+
only_get_from_dump: bool = False,
|
|
37
|
+
|
|
38
|
+
knn_neighbours: int = 50,
|
|
39
|
+
|
|
40
|
+
random_forest_estimators: int = 100,
|
|
41
|
+
|
|
42
|
+
nn_device: torch.device = torch.device('cuda'),
|
|
43
|
+
nn_batch_size: int = 256,
|
|
44
|
+
nn_learning_rate: float = 1e-3,
|
|
45
|
+
nn_epochs: int = 100,
|
|
46
|
+
|
|
47
|
+
grad_boost_max_iter: int = 20,
|
|
48
|
+
grad_boost_max_depth: int = 4,
|
|
49
|
+
grad_boost_max_features: float = 0.1,
|
|
50
|
+
|
|
51
|
+
xgboost_n_classifiers: int = 25,
|
|
52
|
+
xgboost_max_depth: int = 4,
|
|
53
|
+
xgboost_device: str = 'gpu',
|
|
54
|
+
|
|
55
|
+
catboost_iterations: int = 600,
|
|
56
|
+
catboost_depth: int = 4,
|
|
57
|
+
catboost_device: str = 'GPU',
|
|
58
|
+
|
|
59
|
+
without_diagrams: bool = False
|
|
60
|
+
):
|
|
61
|
+
if (train_images is not None) and (not only_get_from_dump):
|
|
62
|
+
nn_train = cvtda.neural_network.Dataset(
|
|
63
|
+
train_images, train_diagrams, train_features, train_labels, n_jobs = n_jobs, device = nn_device
|
|
64
|
+
)
|
|
65
|
+
nn_test = cvtda.neural_network.Dataset(
|
|
66
|
+
test_images, test_diagrams, test_features, test_labels, n_jobs = n_jobs, device = nn_device
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def classify_one(classifier: sklearn.base.ClassifierMixin, name: str, display_name: str, ax: plt.Axes):
|
|
70
|
+
if without_diagrams and name == 'NNClassifier_diagrams':
|
|
71
|
+
print(f'Skipping {name} - {classifier}')
|
|
72
|
+
return {}
|
|
73
|
+
|
|
74
|
+
print(f'Trying {name} - {classifier}')
|
|
75
|
+
|
|
76
|
+
dumper = cvtda.dumping.dumper()
|
|
77
|
+
model_dump_name = cvtda.dumping.dump_name_concat(dump_name, name)
|
|
78
|
+
if only_get_from_dump or dumper.has_dump(model_dump_name):
|
|
79
|
+
y_pred_proba = dumper.get_dump(model_dump_name)
|
|
80
|
+
else:
|
|
81
|
+
if type(classifier) == NNClassifier:
|
|
82
|
+
classifier.fit(nn_train, nn_test)
|
|
83
|
+
y_pred_proba = classifier.predict_proba(nn_test)
|
|
84
|
+
else:
|
|
85
|
+
classifier.fit(train_features, train_labels)
|
|
86
|
+
y_pred_proba = classifier.predict_proba(test_features)
|
|
87
|
+
|
|
88
|
+
if model_dump_name is not None:
|
|
89
|
+
dumper.save_dump(y_pred_proba, model_dump_name)
|
|
90
|
+
|
|
91
|
+
ax.set_title(display_name)
|
|
92
|
+
result = {
|
|
93
|
+
'classifier': display_name,
|
|
94
|
+
**estimate_quality(
|
|
95
|
+
y_pred_proba, test_labels, ax,
|
|
96
|
+
label_names = label_names, confusion_matrix_include_values = confusion_matrix_include_values
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
print(result)
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
classifiers = [
|
|
103
|
+
sklearn.neighbors.KNeighborsClassifier(
|
|
104
|
+
n_jobs = n_jobs,
|
|
105
|
+
n_neighbors = knn_neighbours
|
|
106
|
+
),
|
|
107
|
+
sklearn.ensemble.RandomForestClassifier(
|
|
108
|
+
n_estimators = random_forest_estimators,
|
|
109
|
+
random_state = random_state,
|
|
110
|
+
n_jobs = n_jobs
|
|
111
|
+
),
|
|
112
|
+
sklearn.ensemble.HistGradientBoostingClassifier(
|
|
113
|
+
random_state = random_state,
|
|
114
|
+
max_iter = grad_boost_max_iter,
|
|
115
|
+
max_depth = grad_boost_max_depth,
|
|
116
|
+
max_features = grad_boost_max_features,
|
|
117
|
+
verbose = 2
|
|
118
|
+
),
|
|
119
|
+
catboost.CatBoostClassifier(
|
|
120
|
+
iterations = catboost_iterations,
|
|
121
|
+
depth = catboost_depth,
|
|
122
|
+
random_seed = random_state,
|
|
123
|
+
loss_function = 'MultiClass',
|
|
124
|
+
devices = '0-3',
|
|
125
|
+
task_type = catboost_device,
|
|
126
|
+
verbose = True
|
|
127
|
+
),
|
|
128
|
+
xgboost.XGBClassifier(
|
|
129
|
+
n_jobs = n_jobs,
|
|
130
|
+
n_estimators = xgboost_n_classifiers,
|
|
131
|
+
max_depth = xgboost_max_depth,
|
|
132
|
+
device = xgboost_device
|
|
133
|
+
),
|
|
134
|
+
NNClassifier(
|
|
135
|
+
random_state = random_state,
|
|
136
|
+
device = nn_device,
|
|
137
|
+
batch_size = nn_batch_size,
|
|
138
|
+
learning_rate = nn_learning_rate,
|
|
139
|
+
n_epochs = nn_epochs * 2,
|
|
140
|
+
skip_diagrams = True,
|
|
141
|
+
skip_images = True,
|
|
142
|
+
skip_features = False,
|
|
143
|
+
),
|
|
144
|
+
NNClassifier(
|
|
145
|
+
random_state = random_state,
|
|
146
|
+
device = nn_device,
|
|
147
|
+
batch_size = nn_batch_size,
|
|
148
|
+
learning_rate = nn_learning_rate,
|
|
149
|
+
n_epochs = nn_epochs // 5,
|
|
150
|
+
skip_diagrams = False,
|
|
151
|
+
skip_images = True,
|
|
152
|
+
skip_features = True,
|
|
153
|
+
),
|
|
154
|
+
NNClassifier(
|
|
155
|
+
random_state = random_state,
|
|
156
|
+
device = nn_device,
|
|
157
|
+
batch_size = nn_batch_size,
|
|
158
|
+
learning_rate = nn_learning_rate,
|
|
159
|
+
n_epochs = nn_epochs,
|
|
160
|
+
skip_diagrams = True,
|
|
161
|
+
skip_images = False,
|
|
162
|
+
skip_features = True,
|
|
163
|
+
),
|
|
164
|
+
NNClassifier(
|
|
165
|
+
random_state = random_state,
|
|
166
|
+
device = nn_device,
|
|
167
|
+
batch_size = nn_batch_size,
|
|
168
|
+
learning_rate = nn_learning_rate,
|
|
169
|
+
n_epochs = nn_epochs,
|
|
170
|
+
skip_diagrams = True,
|
|
171
|
+
skip_images = False,
|
|
172
|
+
skip_features = False,
|
|
173
|
+
)
|
|
174
|
+
]
|
|
175
|
+
names = [
|
|
176
|
+
'KNeighborsClassifier',
|
|
177
|
+
'RandomForestClassifier',
|
|
178
|
+
'HistGradientBoostingClassifier',
|
|
179
|
+
'CatBoostClassifier',
|
|
180
|
+
'XGBClassifier',
|
|
181
|
+
'NNClassifier_features',
|
|
182
|
+
'NNClassifier_diagrams',
|
|
183
|
+
'NNClassifier_images',
|
|
184
|
+
'NNClassifier_features_images'
|
|
185
|
+
]
|
|
186
|
+
display_names = [
|
|
187
|
+
'Метод k ближайших соседей',
|
|
188
|
+
'Случайный лес',
|
|
189
|
+
'Град. бустинг на основе гистограмм',
|
|
190
|
+
'CatBoost',
|
|
191
|
+
'XGBoost',
|
|
192
|
+
'Нейронная сеть для тополог. признаков',
|
|
193
|
+
'Обучаемая векторизация диаграмм',
|
|
194
|
+
'ResNet50 – базовая модель',
|
|
195
|
+
'Комбинированная нейронная сеть'
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
figure, axes = plt.subplots(3, 3, figsize = (15, 15))
|
|
199
|
+
df = pandas.DataFrame([ classify_one(*args) for args in zip(classifiers, names, display_names, axes.flat) ])
|
|
200
|
+
figure.tight_layout()
|
|
201
|
+
|
|
202
|
+
dumper = cvtda.dumping.dumper()
|
|
203
|
+
if (dump_name is not None) and isinstance(dumper, cvtda.dumping.NumpyDumper):
|
|
204
|
+
file = dumper.get_file_name_(cvtda.dumping.dump_name_concat(dump_name, "confusion_matrixes"))
|
|
205
|
+
os.makedirs(os.path.dirname(file), exist_ok = True)
|
|
206
|
+
figure.savefig(file[:-4] + ".svg")
|
|
207
|
+
figure.savefig(file[:-4] + ".png")
|
|
208
|
+
df.to_csv(dumper.get_file_name_(cvtda.dumping.dump_name_concat(dump_name, "quality_metrics.csv"))[:-4])
|
|
209
|
+
return df
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
import numpy
|
|
4
|
+
import sklearn.metrics
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
|
|
7
|
+
def estimate_quality(
|
|
8
|
+
y_pred_proba: numpy.ndarray,
|
|
9
|
+
y_true: numpy.ndarray,
|
|
10
|
+
ax: typing.Optional[plt.Axes] = None,
|
|
11
|
+
label_names: typing.Optional[typing.List[str]] = None,
|
|
12
|
+
confusion_matrix_include_values: bool = True
|
|
13
|
+
) -> dict:
|
|
14
|
+
if label_names is None:
|
|
15
|
+
label_names = list(range(y_pred_proba.shape[1]))
|
|
16
|
+
|
|
17
|
+
y_pred = numpy.argmax(y_pred_proba, axis = 1)
|
|
18
|
+
if ax:
|
|
19
|
+
sklearn.metrics.ConfusionMatrixDisplay.from_predictions(
|
|
20
|
+
y_true, y_pred, ax = ax, colorbar = False,
|
|
21
|
+
display_labels = label_names, include_values = confusion_matrix_include_values
|
|
22
|
+
)
|
|
23
|
+
ax.set_xticks(ax.get_xticks(), labels = ax.get_xticklabels(), rotation = 45, ha = "right", rotation_mode = "anchor")
|
|
24
|
+
ax.set_xlabel(None)
|
|
25
|
+
ax.set_ylabel(None)
|
|
26
|
+
|
|
27
|
+
numeric_labels = list(range(y_pred_proba.shape[1]))
|
|
28
|
+
auc_roc_input = y_pred_proba
|
|
29
|
+
multi_class = 'ovo'
|
|
30
|
+
average = 'macro'
|
|
31
|
+
|
|
32
|
+
if y_pred_proba.shape[1] == 2:
|
|
33
|
+
auc_roc_input = y_pred_proba[:, 1]
|
|
34
|
+
multi_class = 'raise'
|
|
35
|
+
average = 'binary'
|
|
36
|
+
|
|
37
|
+
metrics = {
|
|
38
|
+
'Accuracy': sklearn.metrics.accuracy_score (y_true, y_pred),
|
|
39
|
+
'AUC-ROC': sklearn.metrics.roc_auc_score (y_true, auc_roc_input, multi_class = multi_class, labels = numeric_labels),
|
|
40
|
+
'Precision': sklearn.metrics.precision_score (y_true, y_pred, average = average, zero_division = 0),
|
|
41
|
+
'Recall': sklearn.metrics.recall_score (y_true, y_pred, average = average),
|
|
42
|
+
'F1-score': sklearn.metrics.f1_score (y_true, y_pred, average = average)
|
|
43
|
+
}
|
|
44
|
+
for k in [ 2, 3, 5, 7, 9, 15, 30 ]:
|
|
45
|
+
if y_pred_proba.shape[1] > k:
|
|
46
|
+
metrics[f'TOP-{k} Accuracy'] = sklearn.metrics.top_k_accuracy_score(y_true, y_pred_proba, k = k, labels = numeric_labels)
|
|
47
|
+
return metrics
|