增加特征重要性计算

This commit is contained in:
RYDE-WORK 2026-03-03 13:45:46 +08:00
parent 4b75b7406d
commit 3b38727053
14 changed files with 123 additions and 55 deletions

View File

@ -126,10 +126,11 @@ train: requirements
# INTERPRETABILITY #
#################################################################################
# 参数:
# TASK 目标任务 (delivery, size, pdi, ee, biodist, toxic; 默认: delivery)
# METHOD 方法 (ig, ablation, attention, all; 默认: all)
# DATA 数据路径 (默认: data/processed/train.parquet)
# MODEL 模型路径 (默认: models/model.pt)
# TASK 目标任务 (delivery, size, pdi, ee, biodist, toxic, all; 默认: delivery)
# 如果指定 'all',将依次计算所有 6 个任务
# METHOD 方法 (ig, ablation, attention, all; 默认: ig)
# DATA 数据路径 (默认: data/interim/internal.csv即最终模型的全量训练数据)
# MODEL 模型路径 (默认: models/final/model.pt)
TASK_FLAG = $(if $(TASK),--task $(TASK),)
METHOD_FLAG = $(if $(METHOD),--method $(METHOD),)

View File

@ -29,8 +29,8 @@ from torch.utils.data import DataLoader
from loguru import logger
from tqdm import tqdm
from lnp_ml.config import MODELS_DIR, PROCESSED_DATA_DIR, REPORTS_DIR
from lnp_ml.dataset import LNPDataset, collate_fn
from lnp_ml.config import MODELS_DIR, INTERIM_DATA_DIR, REPORTS_DIR
from lnp_ml.dataset import LNPDataset, collate_fn, process_dataframe
from lnp_ml.modeling.predict import load_model
from lnp_ml.modeling.models import LNPModel, LNPModelWithoutMPNN
@ -332,12 +332,12 @@ def save_csv(
def main() -> None:
parser = argparse.ArgumentParser(description="Token-level Feature Importance")
parser.add_argument("--model-path", type=str, default=str(MODELS_DIR / "model.pt"),
parser.add_argument("--model-path", type=str, default=str(MODELS_DIR / "final" / "model.pt"),
help="Path to trained model checkpoint")
parser.add_argument("--data-path", type=str, default=str(PROCESSED_DATA_DIR / "train.parquet"),
help="Path to data (parquet) for computing importance")
parser.add_argument("--task", type=str, default="delivery", choices=TASKS,
help="Target task for importance computation")
parser.add_argument("--data-path", type=str, default=str(INTERIM_DATA_DIR / "internal.csv"),
help="Path to data (.csv or .parquet) for computing importance")
parser.add_argument("--task", type=str, default="all", choices=TASKS + ["all"],
help="Target task for importance computation ('all' to run on all tasks)")
parser.add_argument("--method", type=str, default="ig",
choices=["ig", "ablation", "attention", "all"],
help="Which method(s) to run")
@ -363,7 +363,12 @@ def main() -> None:
logger.info(f"Tokens ({len(token_names)}): {token_names}")
# ── Load data ──
df = pd.read_parquet(args.data_path)
data_path = Path(args.data_path)
if data_path.suffix == ".csv":
df = pd.read_csv(data_path)
df = process_dataframe(df)
else:
df = pd.read_parquet(data_path)
dataset = LNPDataset(df)
loader = DataLoader(dataset, batch_size=args.batch_size, shuffle=False, collate_fn=collate_fn)
logger.info(f"Samples: {len(dataset)}")
@ -382,54 +387,62 @@ def main() -> None:
else [args.method]
)
results: Dict[str, np.ndarray] = {}
# Determine tasks to process
tasks_to_run = TASKS if args.task == "all" else [args.task]
for method in methods:
for task in tasks_to_run:
logger.info(f"\n{'#'*60}")
logger.info(f"# Processing task: {task}")
logger.info(f"{'#'*60}")
results: Dict[str, np.ndarray] = {}
for method in methods:
logger.info(f"\n{'='*60}")
logger.info(f"Computing: {method} (task={task})")
logger.info(f"{'='*60}")
if method == "ig":
imp = integrated_gradients_importance(
model, all_tokens, device,
task=task, batch_size=args.batch_size, n_steps=args.n_steps,
)
results["Integrated Gradients"] = imp
elif method == "ablation":
imp = token_ablation_importance(
model, all_tokens, device,
task=task, batch_size=args.batch_size,
)
results["Token Ablation"] = imp
elif method == "attention":
imp = fusion_attention_importance(
model, all_tokens, device,
batch_size=args.batch_size,
)
if imp is not None:
results["Fusion Attention"] = imp
# ── Print summary ──
logger.info(f"\n{'='*60}")
logger.info(f"Computing: {method}")
logger.info(f"Token Importance Summary (task={task})")
logger.info(f"{'='*60}")
for method_name, importance in results.items():
normed = normalize(importance)
order = np.argsort(-normed)
logger.info(f"\n {method_name}:")
for rank, idx in enumerate(order, 1):
logger.info(f" {rank:>2d}. {token_names[idx]:<10s} {normed[idx]:.4f}")
if method == "ig":
imp = integrated_gradients_importance(
model, all_tokens, device,
task=args.task, batch_size=args.batch_size, n_steps=args.n_steps,
)
results["Integrated Gradients"] = imp
logger.info(f"\n Gate values (sigmoid):")
for name, val in sorted(gv.items(), key=lambda x: -x[1]):
logger.info(f" {name:<10s} {val:.4f}")
elif method == "ablation":
imp = token_ablation_importance(
model, all_tokens, device,
task=args.task, batch_size=args.batch_size,
)
results["Token Ablation"] = imp
elif method == "attention":
imp = fusion_attention_importance(
model, all_tokens, device,
batch_size=args.batch_size,
)
if imp is not None:
results["Fusion Attention"] = imp
# ── Print summary ──
logger.info(f"\n{'='*60}")
logger.info(f"Token Importance Summary (task={args.task})")
logger.info(f"{'='*60}")
for method_name, importance in results.items():
normed = normalize(importance)
order = np.argsort(-normed)
logger.info(f"\n {method_name}:")
for rank, idx in enumerate(order, 1):
logger.info(f" {rank:>2d}. {token_names[idx]:<10s} {normed[idx]:.4f}")
logger.info(f"\n Gate values (sigmoid):")
for name, val in sorted(gv.items(), key=lambda x: -x[1]):
logger.info(f" {name:<10s} {val:.4f}")
# ── Save results ──
if results:
plot_token_importance(results, token_names, args.task, out_dir)
save_csv(results, token_names, args.task, out_dir, gate_vals=gv)
# ── Save results ──
if results:
plot_token_importance(results, token_names, task, out_dir)
save_csv(results, token_names, task, out_dir, gate_vals=gv)
logger.info("\nDone!")

View File

@ -0,0 +1,9 @@
token,Integrated Gradients_raw,Integrated Gradients_normalized,gate_sigmoid
desc,3.2013970842762367e-09,0.17906809140868585,0.5030443072319031
mpnn,3.109777150387161e-09,0.17394338920379962,0.5024935007095337
maccs,3.0657202874248063e-09,0.1714790968475434,0.5030479431152344
morgan,3.020539287877718e-09,0.16895192663283606,0.5045571327209473
help,1.937320535640997e-09,0.10836278088337024,0.49689680337905884
comp,1.876732953403087e-09,0.10497385335304418,0.5007365345954895
exp,1.6503372406931126e-09,0.09231055445232395,0.5002157688140869
phys,1.6274562664095572e-11,0.0009103072183966515,0.49989768862724304
Internal Server Error - Gitea4PDT
500 Internal Server Error

Gitea Version: 1.23.8