|
| 1 | +import numpy as np |
| 2 | + |
| 3 | +class ART1: |
| 4 | + """ |
| 5 | + Adaptive Resonance Theory 1 (ART1) model for binary data clustering. |
| 6 | +
|
| 7 | + The ART1 algorithm is a type of neural network used for unsupervised learning and clustering of binary input data. |
| 8 | + It continuously learns to categorize inputs based on similarity while preserving previously learned categories. |
| 9 | + The vigilance parameter controls the degree of similarity required to assign an input to an existing category, |
| 10 | + allowing for flexible and adaptive clustering. |
| 11 | +
|
| 12 | + Attributes: |
| 13 | + num_features (int): Number of features in the input data. |
| 14 | + vigilance (float): Threshold for similarity that determines whether an input matches an existing cluster. |
| 15 | + weights (list): List of cluster weights representing the learned categories. |
| 16 | + """ |
| 17 | + |
| 18 | + def __init__(self, num_features: int, vigilance: float = 0.7) -> None: |
| 19 | + """ |
| 20 | + Initialize the ART1 model with the given number of features and vigilance parameter. |
| 21 | +
|
| 22 | + Args: |
| 23 | + num_features (int): Number of features in the input data. |
| 24 | + vigilance (float): Threshold for similarity (default is 0.7). |
| 25 | + |
| 26 | + Examples: |
| 27 | + >>> model = ART1(num_features=4, vigilance=0.5) |
| 28 | + >>> model.num_features |
| 29 | + 4 |
| 30 | + >>> model.vigilance |
| 31 | + 0.5 |
| 32 | + """ |
| 33 | + self.vigilance = vigilance # Controls cluster strictness |
| 34 | + self.num_features = num_features |
| 35 | + self.weights = [] # List of cluster weights |
| 36 | + |
| 37 | + def train(self, data: np.ndarray) -> None: |
| 38 | + """ |
| 39 | + Train the ART1 model on the provided data. |
| 40 | +
|
| 41 | + Args: |
| 42 | + data (np.ndarray): A 2D array of binary input data (num_samples x num_features). |
| 43 | +
|
| 44 | + Examples: |
| 45 | + >>> model = ART1(num_features=4, vigilance=0.5) |
| 46 | + >>> data = np.array([[1, 1, 0, 0], [1, 1, 1, 0]]) |
| 47 | + >>> model.train(data) |
| 48 | + >>> len(model.weights) |
| 49 | + 2 |
| 50 | + """ |
| 51 | + for x in data: |
| 52 | + match = False |
| 53 | + for i, w in enumerate(self.weights): |
| 54 | + if self._similarity(w, x) >= self.vigilance: |
| 55 | + self.weights[i] = self._learn(w, x) |
| 56 | + match = True |
| 57 | + break |
| 58 | + if not match: |
| 59 | + self.weights.append(x.copy()) # Add a new cluster |
| 60 | + |
| 61 | + def _similarity(self, w: np.ndarray, x: np.ndarray) -> float: |
| 62 | + """ |
| 63 | + Calculate similarity between weight and input. |
| 64 | +
|
| 65 | + Args: |
| 66 | + w (np.ndarray): Weight vector representing a cluster. |
| 67 | + x (np.ndarray): Input vector. |
| 68 | +
|
| 69 | + Returns: |
| 70 | + float: The similarity score between the weight and the input. |
| 71 | +
|
| 72 | + Examples: |
| 73 | + >>> model = ART1(num_features=4) |
| 74 | + >>> w = np.array([1, 1, 0, 0]) |
| 75 | + >>> x = np.array([1, 0, 0, 0]) |
| 76 | + >>> model._similarity(w, x) |
| 77 | + 0.25 |
| 78 | + """ |
| 79 | + return np.dot(w, x) / (self.num_features) |
| 80 | + |
| 81 | + def _learn(self, w: np.ndarray, x: np.ndarray, learning_rate: float = 0.5) -> np.ndarray: |
| 82 | + """ |
| 83 | + Update cluster weights using the learning rate. |
| 84 | +
|
| 85 | + Args: |
| 86 | + w (np.ndarray): Current weight vector for the cluster. |
| 87 | + x (np.ndarray): Input vector. |
| 88 | + learning_rate (float): Learning rate for weight update (default is 0.5). |
| 89 | +
|
| 90 | + Returns: |
| 91 | + np.ndarray: Updated weight vector. |
| 92 | +
|
| 93 | + Examples: |
| 94 | + >>> model = ART1(num_features=4) |
| 95 | + >>> w = np.array([1, 1, 0, 0]) |
| 96 | + >>> x = np.array([0, 1, 1, 0]) |
| 97 | + >>> model._learn(w, x) |
| 98 | + array([0.5, 1. , 0.5, 0. ]) |
| 99 | + """ |
| 100 | + return learning_rate * x + (1 - learning_rate) * w |
| 101 | + |
| 102 | + def predict(self, x: np.ndarray) -> int: |
| 103 | + """ |
| 104 | + Assign data to the closest cluster. |
| 105 | +
|
| 106 | + Args: |
| 107 | + x (np.ndarray): Input vector. |
| 108 | +
|
| 109 | + Returns: |
| 110 | + int: Index of the assigned cluster, or -1 if no match. |
| 111 | +
|
| 112 | + Examples: |
| 113 | + >>> model = ART1(num_features=4) |
| 114 | + >>> model.weights = [np.array([1, 1, 0, 0])] |
| 115 | + >>> model.predict(np.array([1, 1, 0, 0])) |
| 116 | + 0 |
| 117 | + >>> model.predict(np.array([0, 0, 0, 0])) |
| 118 | + -1 |
| 119 | + """ |
| 120 | + similarities = [self._similarity(w, x) for w in self.weights] |
| 121 | + return np.argmax(similarities) if max(similarities) >= self.vigilance else -1 # -1 if no match |
| 122 | + |
| 123 | + |
| 124 | +# Example usage for ART1 |
| 125 | +def art1_example() -> None: |
| 126 | + """ |
| 127 | + Example function demonstrating the usage of the ART1 model. |
| 128 | +
|
| 129 | + This function creates a dataset, trains the ART1 model, and prints the assigned clusters for each data point. |
| 130 | +
|
| 131 | + Examples: |
| 132 | + >>> art1_example() |
| 133 | + Data point 0 assigned to cluster: 0 |
| 134 | + Data point 1 assigned to cluster: 0 |
| 135 | + Data point 2 assigned to cluster: 1 |
| 136 | + Data point 3 assigned to cluster: 1 |
| 137 | + """ |
| 138 | + data = np.array([[1, 1, 0, 0], [1, 1, 1, 0], [0, 0, 1, 1], [0, 1, 0, 1]]) |
| 139 | + model = ART1(num_features=4, vigilance=0.5) |
| 140 | + model.train(data) |
| 141 | + |
| 142 | + for i, x in enumerate(data): |
| 143 | + cluster = model.predict(x) |
| 144 | + print(f"Data point {i} assigned to cluster: {cluster}") |
| 145 | + |
| 146 | +if __name__ == "__main__": |
| 147 | + art1_example() |
0 commit comments