Assign labels to the neurons based on highest average spiking activity. :param spikes: Binary tensor of shape ``(n_samples, time, n_neurons)`` of a single layer's spiking activity. :param labels: Vector of shape ``(n_samples,)`` with data labels corresponding to spiking
(
spikes: torch.Tensor,
labels: torch.Tensor,
n_labels: int,
rates: Optional[torch.Tensor] = None,
alpha: float = 1.0,
)
| 6 | |
| 7 | |
| 8 | def assign_labels( |
| 9 | spikes: torch.Tensor, |
| 10 | labels: torch.Tensor, |
| 11 | n_labels: int, |
| 12 | rates: Optional[torch.Tensor] = None, |
| 13 | alpha: float = 1.0, |
| 14 | ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| 15 | # language=rst |
| 16 | """ |
| 17 | Assign labels to the neurons based on highest average spiking activity. |
| 18 | |
| 19 | :param spikes: Binary tensor of shape ``(n_samples, time, n_neurons)`` of a single |
| 20 | layer's spiking activity. |
| 21 | :param labels: Vector of shape ``(n_samples,)`` with data labels corresponding to |
| 22 | spiking activity. |
| 23 | :param n_labels: The number of target labels in the data. |
| 24 | :param rates: If passed, these represent spike rates from a previous |
| 25 | ``assign_labels()`` call. |
| 26 | :param alpha: Rate of decay of label assignments. |
| 27 | :return: Tuple of class assignments, per-class spike proportions, and per-class |
| 28 | firing rates. Neurons that never fired are assigned ``-1`` (unassigned) so |
| 29 | they do not bias predictions toward class ``0``. |
| 30 | """ |
| 31 | |
| 32 | n_neurons = spikes.size(2) |
| 33 | |
| 34 | if rates is None: |
| 35 | rates = torch.zeros((n_neurons, n_labels), device=spikes.device) |
| 36 | |
| 37 | # Sum over time dimension (spike ordering doesn't matter). |
| 38 | spikes = spikes.sum(1) |
| 39 | |
| 40 | for i in range(n_labels): |
| 41 | # Create mask. |
| 42 | mask = labels == i |
| 43 | # Count the number of samples with this label. |
| 44 | n_labeled = mask.sum().float() |
| 45 | |
| 46 | if n_labeled > 0: |
| 47 | # Get indices of samples with this label. |
| 48 | label_sum = spikes[mask].sum(0) |
| 49 | # Update rates. |
| 50 | rates[:, i] = alpha * rates[:, i] + (label_sum / n_labeled) |
| 51 | |
| 52 | # Compute proportions of spike activity per class. |
| 53 | total_activity = rates.sum(1, keepdim=True) |
| 54 | proportions = torch.where( |
| 55 | total_activity > 0, rates / total_activity, torch.zeros_like(rates) |
| 56 | ) |
| 57 | |
| 58 | # Noise for random tie breaking. |
| 59 | eps = 1e-6 # Small enough not to distort real decisions |
| 60 | noise = eps * torch.randn_like(proportions) |
| 61 | |
| 62 | # Neuron assignments are the labels they fire most for. |
| 63 | assignments = torch.argmax(proportions + noise, dim=1) |
| 64 | |
| 65 | # Neurons that never fired have no class preference; mark them with -1 so |
no outgoing calls
no test coverage detected