Spaces:
Running
Running
| """ | |
| Статистики по SAE-признакам и их визуализация (bar charts). | |
| """ | |
| from typing import List, Optional, Tuple | |
| import numpy as np | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| from analysis.features.feature_indexing import FeatureMatrix | |
| def compute_feature_stats(features: FeatureMatrix) -> pd.DataFrame: | |
| """ | |
| Вычисляет статистики для каждого признака SAE. | |
| Параметры | |
| ---------- | |
| features : CSR activations; ``column_feature_ids[j]`` = global SAE id | |
| Возвращает | |
| ---------- | |
| DataFrame с колонками: feature_id (global SAE id), mean, frequency, max, mean_acts | |
| mean — средняя активация по всем патчам | |
| frequency — доля патчей с ненулевой активацией | |
| max — максимальная активация среди всех патчей | |
| mean_acts — средняя активация по ненулевым патчам | |
| """ | |
| codes = features.codes | |
| mat = codes if codes.dtype == np.float32 else codes.astype(np.float32) | |
| n_rows = mat.shape[0] | |
| means = np.asarray(mat.mean(axis=0)).ravel() | |
| freqs = np.asarray(mat.getnnz(axis=0), dtype=np.float32).ravel() / float(n_rows) | |
| maxes = np.asarray(mat.tocsc().max(axis=0).toarray()).ravel() | |
| mean_acts = np.where(freqs > 0, means / freqs, 0.0) | |
| col_ids = [int(fid) for fid in features.column_feature_ids] | |
| return pd.DataFrame({ | |
| 'feature_id': col_ids, | |
| 'mean': means, | |
| 'frequency': freqs, | |
| 'max': maxes, | |
| 'mean_acts': mean_acts, | |
| }) | |
| def get_top_features( | |
| stats: pd.DataFrame, | |
| top_k: int = 20, | |
| criterion: str = 'mean', | |
| min_mean_acts: Optional[float] = None, | |
| ) -> List[int]: | |
| """ | |
| Возвращает индексы top-K признаков по выбранному критерию. | |
| Параметры | |
| ---------- | |
| stats : DataFrame из compute_feature_stats | |
| top_k : число топ-признаков | |
| criterion : 'mean' | 'frequency' | 'max' | 'mean_acts' | |
| min_mean_acts : предварительный фильтр по mean_acts | |
| Возвращает | |
| ---------- | |
| List[int] — feature_id в порядке убывания критерия | |
| """ | |
| assert criterion in ('mean', 'frequency', 'max', 'mean_acts'), \ | |
| f"criterion must be one of 'mean', 'frequency', 'max', 'mean_acts', got {criterion!r}" | |
| filtered = stats | |
| if min_mean_acts is not None: | |
| filtered = stats[stats['mean_acts'] >= min_mean_acts] | |
| return filtered.nlargest(top_k, criterion)['feature_id'].tolist() | |
| def plot_top_features( | |
| stats: pd.DataFrame, | |
| top_k: int = 20, | |
| criterion: str = 'mean', | |
| figsize: Tuple[int, int] = (14, 4), | |
| min_mean_acts: Optional[float] = None, | |
| ) -> None: | |
| """ | |
| Bar-chart топ-K признаков по выбранному критерию. | |
| Над каждым баром подписывается frequency (доля ненулевых патчей). | |
| """ | |
| top_ids = get_top_features(stats, top_k=top_k, criterion=criterion, | |
| min_mean_acts=min_mean_acts) | |
| top_stats = stats.set_index('feature_id').loc[top_ids] | |
| fig, ax = plt.subplots(figsize=figsize) | |
| bars = ax.bar(range(len(top_ids)), top_stats[criterion].values, color='steelblue') | |
| for bar, freq_val in zip(bars, top_stats['frequency'].values): | |
| ax.text( | |
| bar.get_x() + bar.get_width() / 2, | |
| bar.get_height(), | |
| f'{freq_val:.3f}', | |
| ha='center', va='bottom', fontsize=6, color='black', rotation=90, | |
| ) | |
| ax.set_xticks(range(len(top_ids))) | |
| ax.set_xticklabels([str(i) for i in top_ids], rotation=90, fontsize=8) | |
| ax.set_xlabel('Feature ID') | |
| ax.set_ylabel(criterion) | |
| ax.set_title(f'Top-{top_k} features by {criterion}') | |
| plt.tight_layout() | |
| plt.show() | |