Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial implementation of cvi Art Modules. #70

Merged
merged 1 commit into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions artlib/cvi/CVIFuzzyArt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import numpy as np
import sklearn.metrics as metrics
from artlib.elementary.FuzzyART import FuzzyART


class CVIFuzzyART(FuzzyART):
"""CVI Fuzzy Art Classification

Expanded version of Fuzzy Art that uses Cluster Validity Indicies to help with cluster selection.
PBM is not implemented, can be seen here.
https://git.mst.edu/acil-group/CVI-Fuzzy-ART/-/blob/master/PBM_index.m?ref_type=heads

Note, the default step_fit function in base ART evaluates the matching function even if
the other criteria has failed. This means it could run slower then it would otherwise.


Parameters:
rho: float [0,1] for the vigilance parameter.
alpha: float choice parameter. 1e-7 recommended value.
beta: float [0,1] learning parameters. beta = 1 is fast learning recommended value.
validity: int the cluster validity index being used.
W: list of weights, top down.
labels: class labels for data set.
"""
CALINSKIHARABASZ = 1
DAVIESBOULDIN = 2
SILHOUETTE = 3
# PBM = 4

def __init__(self, rho: float, alpha: float, beta: float, validity: int):
super().__init__(rho, alpha, beta)
# Because Fuzzy art doesn't accept validity, and makes the params the way it does, validations have to be done after init.
self.params['validity'] = validity
assert 'validity' in self.params
assert isinstance(self.params['validity'], int)

def CVIMatch(self, x, w, c_, params, cache):
if len(self.W) < 2:
return True

match params['validity']:
case self.CALINSKIHARABASZ:
validFunc = metrics.calinski_harabasz_score
case self.DAVIESBOULDIN:
validFunc = metrics.davies_bouldin_score
case self.SILHOUETTE:
validFunc = metrics.silhouette_score
case _:
validFunc = metrics.calinski_harabasz_score

oldVI = validFunc(self.data, self.labels_)
newLabels = np.copy(self.labels_)
newLabels[self.index] = c_
new_VI = validFunc(self.data, newLabels)
if params['validity'] != self.DAVIESBOULDIN:
return new_VI > oldVI
else:
return new_VI < oldVI

def fit(self, X: np.ndarray, max_iter: int = 1):
self.data = X
self.validate_data(X)
self.check_dimensions(X)
self.is_fitted_ = True

self.W: list[np.ndarray] = []
self.labels_ = np.zeros((X.shape[0], ), dtype=int)
for _ in range(max_iter):
print(_)
for i, x in enumerate(X):
self.pre_step_fit(X)
self.index = i
c = self.step_fit(x, match_reset_func=self.CVIMatch)
self.labels_[i] = c
self.post_step_fit(X)
71 changes: 71 additions & 0 deletions artlib/cvi/iCVIFuzzyArt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""
Add Reference in correct format.
The original matlab code can be found at https://github.com/ACIL-Group/iCVI-toolbox/tree/master
The formulation is available at
https://scholarsmine.mst.edu/cgi/viewcontent.cgi?article=3833&context=doctoral_dissertations Pages 314-316 and 319-320
Extended icvi offline mode can be found at
https://ieeexplore.ieee.org/document/9745260
"""
import numpy as np
from artlib.elementary.FuzzyART import FuzzyART
from artlib.cvi.iCVIs.CalinkskiHarabasz import iCVI_CH


class iCVIFuzzyART(FuzzyART):
"""iCVI Fuzzy Art Classification

Parameters:
rho: float [0,1] for the vigilance parameter.
alpha: float choice parameter. 1e-7 recommended value.
beta: float [0,1] learning parameters. beta = 1 is fast learning recommended value.
validity: int the cluster validity index being used.
W: list of weights, top down.
labels: class labels for data set.
"""
CALINSKIHARABASZ = 1

def __init__(self, rho: float, alpha: float, beta: float, validity: int, offline: bool = True):
super().__init__(rho, alpha, beta)
self.params['validity'] = validity # Currently not used. Waiting for more algorithms.
self.offline = offline
assert 'validity' in self.params # Because Fuzzy art doesn't accept validity, and makes the params the way it does, validations have to be done after init.
assert isinstance(self.params['validity'], int)

def iCVIMatch(self, x, w, c_, params, cache):
if self.offline:
new = self.iCVI.switchLabel(x, self.labels_[self.index], c_)
else:
new = self.iCVI.addSample(x, c_)
# Eventually this should be an icvi function that you pass the params, and it handles if this is true or false.
return new['criterionValue'] > self.iCVI.criterionValue
# return self.iCVI.evalLabel(x, c_) This except pass params instead.

# Could add max epochs back in, but only if offline is true, or do something special...
def fit(self, X: np.ndarray):
self.validate_data(X)
self.check_dimensions(X)
self.is_fitted_ = True

self.W: list[np.ndarray] = []
self.labels_ = np.zeros((X.shape[0], ), dtype=int)

self.iCVI = iCVI_CH(X[0])

if self.offline:
for x in X:
params = self.iCVI.addSample(x, 0)
self.iCVI.update(params)

for i, x in enumerate(X):
self.pre_step_fit(X)
self.index = i
c = self.step_fit(x, match_reset_func=self.iCVIMatch)

if self.offline:
params = self.iCVI.switchLabel(x, self.labels_[i], c)
else:
params = self.iCVI.addSample(x, c)
self.iCVI.update(params)

self.labels_[i] = c
self.post_step_fit(X)
Loading