Commit c5c42ada authored by Delvallez Delvallez's avatar Delvallez Delvallez

outils de visulistaion des résultats

parent 9d73694e
#!/usr/bin/env python
import json
import numpy as np
import sys
import seaborn as sns
import matplotlib.pyplot as plt
import argparse
from pathlib import Path
from itertools import combinations
def compter_noeuds_sensibles(json_path, pert_type):
"""
Compte le nombre de perturbations pour lesquelles chaque noeud
est considéré comme sensible.
Parameters
----------
json_path : str
Chemin du fichier JSON.
est_sensible : callable
Fonction prenant (mean, std) et renvoyant True ou False.
Returns
-------
np.ndarray
Matrice des comptes.
"""
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
premiere_perturbation = next(iter(data.values()))
shape = np.array(premiere_perturbation["patching_mean"]).shape
compteurs = np.zeros(shape, dtype=int)
compteur_pert = {}
for perturbation, valeurs in data.items():
if pert_type == "all" or pert_type == perturbation_type(perturbation):
mean = np.array(valeurs["patching_mean"])
std = np.array(valeurs["patching_std"])
resc_mean = (np.sign(mean) / np.absolute(mean).max()) * np.absolute(mean)
resc_std = (np.sign(std) / np.absolute(std).max()) * np.absolute(std)
compteur_pert[perturbation] ={}
compteur_pert[perturbation]["carte"] = ((np.abs(mean) > 0.1) & (np.abs(resc_mean) > 0.5)) | (resc_std > 0.5)
compteur_pert[perturbation]["noeuds_sensibles"] = []
for i in range(mean.shape[0]):
for j in range(mean.shape[1]):
if compteur_pert[perturbation]["carte"][i,j]:
compteur_pert[perturbation]["noeuds_sensibles"].append((i,j,mean[i,j], std[i,j], resc_mean[i,j], resc_std[i,j]))
compteur_pert[perturbation]["total"] = np.sum(compteur_pert[perturbation]["carte"])
compteurs += compteur_pert[perturbation]["carte"]
return compteurs, compteur_pert
def plot_noeuds_sensibles(noeuds_sensibles, title, save_path):
ax = sns.heatmap(
noeuds_sensibles,
cmap="Reds",
vmin=0,
vmax=noeuds_sensibles.max(),
xticklabels=True,
yticklabels=True,
annot=True,
annot_kws={"size": 8},
)
plt.title(title)
plt.xlabel("Head")
plt.ylabel("Layer")
if save_path:
plt.savefig(save_path)
plt.close()
else:
plt.show()
def perturbation_type(pert):
if "->" in pert:
return "replace"
if pert[0] == "+":
return "append"
if pert[-1] =="+":
return "prepend"
raise RuntimeWarning("Unreachable")
def stats(noeuds_perts, path):
s = "Nombre de noeuds relevés par perturbation"
for pert, values in noeuds_perts.items():
s+=f"\n{pert} : {values["total"]}"
for pert, values in noeuds_perts.items():
s+=f"\n{pert} : "
for i,j,mean,std,rmean,rstd in values["noeuds_sensibles"]:
s += f"\n\t({i}, {j}) {mean:.2f}, {std:.2f} | {rmean:.2f}, {rstd:.2f}"
print(s)
if path:
with open(path,'w') as f:
f.write(s)
def cli():
parser = argparse.ArgumentParser(
description=(
"Génère une matrice récapitulative des noeuds "
"sensibles pour le lot de matrices de sensibilité à des"
" perturbations fourni."
)
)
parser.add_argument(
"input_json",
help="Fichier JSON contennant les matrices de sensibilité aux perturbation.",
)
parser.add_argument(
"-t",
"--title",
default="Noeuds sensibles",
help=(
"Titre du graphique généré "
"(défaut : pNoeuds sensibles)."
),
)
parser.add_argument(
"-s",
"--save",
default=False,
type=Path,
help=(
"Chemin de répertoire pour la sauvegarde des informations"
"(défaut : False (affiche mais ne sauvegarde pas))."
),
)
parser.add_argument(
"-p",
"--perturbationtype",
default="all",
choices=['all', 'replace', 'append', 'prepend'],
help=(
"Chemin de sauvegarde du graphique"
"(défaut : False (affiche mais ne sauvegarde pas))."
),
)
return parser.parse_args()
if __name__ == "__main__":
args = cli()
noeuds_sensibles, noeuds_perts = compter_noeuds_sensibles(args.input_json, pert_type = args.perturbationtype)
save_path = False if not args.save else Path(args.save)
save_stats_path = False if not args.save else save_path/f"StatsSensibiliteNoeuds-{args.perturbationtype}.txt"
save_plot_path = False if not args.save else save_path/f"SensibiliteNoeuds-{args.perturbationtype}.png"
stats(noeuds_perts, save_stats_path)
plot_noeuds_sensibles(noeuds_sensibles,
title=args.title,
save_path=save_plot_path)
#!/usr/bin/env python
import subprocess
from pathlib import Path
import argparse
def image_ou_blanche(chemin, taille):
"""
Retourne les arguments ImageMagick correspondant soit à une image
existante, soit à une image blanche de substitution.
Parameters
----------
chemin : Path
Chemin de l'image recherchée.
taille : str
Taille de l'image blanche (ex. "1000x600").
Returns
-------
list[str]
Arguments à insérer dans la commande ImageMagick.
"""
if chemin.exists():
return [str(chemin)]
else:
print(f"Image absente : {chemin.name}")
return ["-size", taille, "xc:white"]
def perturbation_type(pert):
if "->" in pert:
return "replace"
if pert[0] == "+":
return "append"
if pert[-1] =="+":
return "prepend"
raise RuntimeWarning("Unreachable")
def generer_planche(
dossier,
sortie="planche_globale.png",
nb_colonnes=3,
keep_temp=False,
pert_type = "all"
):
"""
Génère une grande planche récapitulative.
Chaque perturbation donne lieu à une tuile :
- score en haut ;
- mean et std côte à côte ;
- meanV2 et stdV2 si disponibles.
Parameters
----------
dossier : str
Dossier contenant les images.
sortie : str
Nom de l'image finale.
nb_colonnes : int
Nombre de tuiles par ligne dans la planche finale.
"""
dossier = Path(dossier)
prefix = "PerturbationScore_"
perturbations = sorted(
f.stem[len(prefix):]
for f in dossier.glob(f"{prefix}*.png")
)
tuiles = []
for pert in perturbations:
if pert_type == "all" or perturbation_type(pert) == pert_type:
score = dossier / f"PerturbationScore_{pert}.png"
mean = dossier / f"ComponentPatching-mean_{pert}.png"
std = dossier / f"ComponentPatching-std_{pert}.png"
mean_v2 = dossier / f"ComponentPatching-mean_{pert}V2.png"
std_v2 = dossier / f"ComponentPatching-std_{pert}V2.png"
tuile = dossier / f"__tuile_{pert}.png"
# Ligne mean/std
cmd = [
"magick",
"(",
*image_ou_blanche(mean, "1000x600"),
"-resize", "x600",
")",
"(",
*image_ou_blanche(std, "1000x600"),
"-resize", "x600",
")",
"+append",
]
if mean_v2.exists() or std_v2.exists():
ligne_v2 = dossier / f"__ligneV2_{pert}.png"
subprocess.run(
[
"magick",
"(",
*image_ou_blanche(mean_v2, "1000x600"),
"-resize", "x600",
")",
"(",
*image_ou_blanche(std_v2, "1000x600"),
"-resize", "x600",
")",
"+append",
str(ligne_v2),
],
check=True,
)
ligne_bas = dossier / f"__bas_{pert}.png"
subprocess.run(
cmd + [str(ligne_bas)],
check=True,
)
subprocess.run(
[
"magick",
str(score),
"-resize", "1500x400",
str(ligne_bas),
str(ligne_v2),
"-append",
str(tuile),
],
check=True,
)
ligne_v2.unlink()
ligne_bas.unlink()
else:
ligne_bas = dossier / f"__bas_{pert}.png"
subprocess.run(
cmd + [str(ligne_bas)],
check=True,
)
subprocess.run(
[
"magick",
str(score),
"-resize", "1500x400",
str(ligne_bas),
"-append",
str(tuile),
],
check=True,
)
ligne_bas.unlink()
tuiles.append(tuile)
if not tuiles:
raise RuntimeError("Aucune perturbation trouvée.")
# Construction de la planche globale
lignes = []
for i in range(0, len(tuiles), nb_colonnes):
ligne = dossier / f"__planche_ligne_{i}.png"
subprocess.run(
["magick"] +
[str(t) for t in tuiles[i:i+nb_colonnes]] +
["+append", str(ligne)],
check=True,
)
lignes.append(ligne)
subprocess.run(
["magick"] +
[str(l) for l in lignes] +
["-append", sortie],
check=True,
)
print(f"Planche enregistrée dans : {sortie}")
# nettoyage
if not keep_temp:
for f in tuiles + lignes:
f.unlink()
def main():
parser = argparse.ArgumentParser(
description=(
"Génère une planche récapitulative à partir des "
"graphiques produits lors des expériences de perturbation."
)
)
parser.add_argument(
"input_dir",
help="Dossier contenant les images à regrouper.",
)
parser.add_argument(
"-o",
"--output",
default="planche_globale.png",
help=(
"Nom du fichier image généré "
"(défaut : planche_globale.png)."
),
)
parser.add_argument(
"-c",
"--cols",
type=int,
default=4,
help=(
"Nombre de triplets affichés par ligne "
"dans la planche finale (défaut : 4)."
),
)
parser.add_argument(
"--keep-temp",
action="store_true",
help=(
"Conserve les images intermédiaires "
"créées pendant l'assemblage."
),
)
parser.add_argument(
"-p",
"--pert_type",
choices=["all", "replace", "append", "prepend"],
default="all",
help=(
"Restreindre la place à un certain type de perturbation."
),
)
args = parser.parse_args()
generer_planche(
dossier=args.input_dir,
sortie=args.output,
nb_colonnes=args.cols,
keep_temp=args.keep_temp,
pert_type= args.pert_type
)
if __name__ == "__main__":
main()
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment