IQA-Interpretation / analysis /features /feature_stats.py
dvarfe's picture
move to plotly backend
5109422
Raw
History Blame Contribute Delete
4.04 kB
"""
Статистики по 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()