| |
| """ |
| Probe 8: Disease/Tissue Embedding Analysis - GlycanBERT |
| ======================================================== |
| Uses glycowork metadata (disease_association, tissue_sample, glycan_type) |
| to probe what biological knowledge GlycanBERT embeddings encode. |
| |
| Sub-probes: |
| 8a. Glycan Type Classification (N / O / free / repeat / lipid) [16,556 samples] |
| 8b. Tissue Source Classification (top-10 tissues) [~2,000 samples] |
| 8c. Disease Association (cancer vs non-cancer vs healthy) [~200 samples] |
| 8d. t-SNE Visualization colored by glycan_type + tissue [all 39,873] |
| |
| Data: bert_v6_contrastive/analysis/probe8_disease_tissue_data.csv |
| |
| Usage: |
| python probe_8_disease_tissue.py --model v5 --device cuda |
| python probe_8_disease_tissue.py --model v6 --device cuda |
| """ |
|
|
| import sys |
| import os |
| import json |
| import csv |
| import argparse |
| import ast |
| import numpy as np |
| from pathlib import Path |
| from collections import Counter |
|
|
| PROJECT_ROOT = Path(__file__).resolve().parents[2] |
| VOCAB_PATH = PROJECT_ROOT / 'bert_training_v4' / 'data' / 'vocabulary.json' |
| DATA_PATH = PROJECT_ROOT / 'bert_v6_contrastive' / 'analysis' / 'probe8_disease_tissue_data.csv' |
|
|
| CHECKPOINTS = { |
| 'v5': PROJECT_ROOT / 'checkpoints_v5_bpe_topo' / 'best_v5_bpe_topo_model.pt', |
| 'v6': PROJECT_ROOT / 'bert_v5.1_contrastive' / 'checkpoints' / 'best_v51_contrastive_model.pt', |
| } |
|
|
| GTYPE_COLORS = { |
| 'N': '#0072B2', 'O': '#E69F00', 'free': '#009E73', |
| 'repeat': '#D55E00', 'lipid': '#CC79A7', |
| } |
| TISSUE_COLORS = [ |
| '#0072B2', '#E69F00', '#009E73', '#D55E00', '#CC79A7', |
| '#56B4E9', '#F0E442', '#999999', '#882255', '#44AA99', |
| ] |
|
|
| sys.path.insert(0, str(PROJECT_ROOT)) |
| sys.path.insert(0, str(PROJECT_ROOT / 'bert_training_v4')) |
| from model.multimodal_glycan_bert_v3 import MultimodalGlycanBERT, MultimodalGlycanBERTConfig |
| from downstream_tasks.utils.tokenizer import WURCSTokenizer |
|
|
|
|
| def load_model(ckpt_path, device='cuda'): |
| import torch |
| print(f"Loading model from {ckpt_path}...") |
| state = torch.load(ckpt_path, map_location='cpu', weights_only=False) |
| sd = state.get('model_state_dict', state) |
| if 'proj_head_state_dict' in state: |
| sd = {k: v for k, v in sd.items() if not k.startswith('proj_head')} |
| emb_weight = sd.get('seq_embeddings.token_embeddings.weight', |
| sd.get('token_embeddings.weight')) |
| vocab_size = emb_weight.shape[0] if emb_weight is not None else 2200 |
| hidden = emb_weight.shape[1] if emb_weight is not None else 768 |
| config = MultimodalGlycanBERTConfig( |
| seq_vocab_size=vocab_size, seq_hidden_size=hidden, |
| seq_num_layers=12, seq_num_heads=12, seq_max_length=256, |
| use_cnn_frontend=True, cnn_kernel_size=3, |
| ) |
| model = MultimodalGlycanBERT(config) |
| model.load_state_dict(sd, strict=False) |
| model = model.to(device).eval() |
| print(f" Loaded: {sum(p.numel() for p in model.parameters()):,} params, vocab={vocab_size}, hidden={hidden}") |
| return model |
|
|
|
|
| def batch_cls_embeddings(model, wurcs_list, tokenizer, device='cuda', |
| batch_size=128, max_len=256): |
| import torch |
| all_embs = [] |
| errors = 0 |
| for i in range(0, len(wurcs_list), batch_size): |
| batch = wurcs_list[i:i+batch_size] |
| token_ids_list, bd_list, lt_list = [], [], [] |
| for w in batch: |
| try: |
| tok_out = tokenizer.tokenize(w) |
| ids = tok_out['token_ids'][:max_len] |
| bd = tok_out['branch_depths'][:max_len] |
| lt = tok_out['linkage_types'][:max_len] |
| token_ids_list.append(ids) |
| bd_list.append(bd) |
| lt_list.append(lt) |
| except Exception: |
| errors += 1 |
| continue |
| if not token_ids_list: |
| continue |
| max_l = max(len(x) for x in token_ids_list) |
| padded_ids = torch.zeros(len(token_ids_list), max_l, dtype=torch.long) |
| padded_bd = torch.zeros_like(padded_ids) |
| padded_lt = torch.zeros_like(padded_ids) |
| for j, (ids, bd, lt) in enumerate(zip(token_ids_list, bd_list, lt_list)): |
| padded_ids[j, :len(ids)] = torch.tensor(ids, dtype=torch.long) |
| padded_bd[j, :len(bd)] = torch.tensor(bd, dtype=torch.long) |
| padded_lt[j, :len(lt)] = torch.tensor(lt, dtype=torch.long) |
| padded_ids = padded_ids.to(device) |
| padded_bd = padded_bd.to(device) |
| padded_lt = padded_lt.to(device) |
| with torch.no_grad(): |
| seq_out = model.seq_embeddings(padded_ids, branch_depths=padded_bd, |
| linkage_types=padded_lt) |
| cls_emb = seq_out[:, 0, :].cpu().numpy() |
| all_embs.append(cls_emb) |
| if (i // batch_size) % 20 == 0: |
| print(f" Embedded {i+len(batch)}/{len(wurcs_list)} ({errors} errors)") |
| print(f" Total embedded: {sum(e.shape[0] for e in all_embs):,} ({errors} errors)") |
| return np.vstack(all_embs) if all_embs else np.zeros((0, 768)) |
|
|
|
|
| def setup_nature_style(): |
| import matplotlib |
| matplotlib.use('Agg') |
| import matplotlib.pyplot as plt |
| plt.rcParams.update({ |
| 'font.family': 'sans-serif', |
| 'font.sans-serif': ['Arial', 'Helvetica', 'DejaVu Sans'], |
| 'font.size': 10, 'axes.titlesize': 12, 'axes.labelsize': 11, |
| 'xtick.labelsize': 9, 'ytick.labelsize': 9, 'legend.fontsize': 9, |
| 'figure.dpi': 300, 'savefig.dpi': 300, 'savefig.bbox': 'tight', |
| 'axes.linewidth': 0.8, 'axes.spines.top': False, 'axes.spines.right': False, |
| }) |
| return plt |
|
|
|
|
| def probe_8a_glycan_type(embs, labels, output_dir, model_name): |
| from sklearn.linear_model import LogisticRegression |
| from sklearn.model_selection import StratifiedKFold |
| from sklearn.metrics import accuracy_score, f1_score |
| from sklearn.preprocessing import LabelEncoder |
| print(f"\n{'='*60}") |
| print(f"PROBE 8a: Glycan Type Classification ({model_name})") |
| print(f"{'='*60}") |
| le = LabelEncoder() |
| y = le.fit_transform(labels) |
| classes = le.classes_ |
| print(f" Classes: {list(classes)}") |
| print(f" Distribution: {dict(zip(classes, np.bincount(y)))}") |
| skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) |
| accs, f1s = [], [] |
| for fold, (train_idx, test_idx) in enumerate(skf.split(embs, y)): |
| clf = LogisticRegression(max_iter=1000, C=1.0, solver='lbfgs', |
| multi_class='multinomial') |
| clf.fit(embs[train_idx], y[train_idx]) |
| preds = clf.predict(embs[test_idx]) |
| acc = accuracy_score(y[test_idx], preds) |
| f1 = f1_score(y[test_idx], preds, average='macro') |
| accs.append(acc) |
| f1s.append(f1) |
| print(f" Fold {fold+1}: acc={acc:.4f}, F1={f1:.4f}") |
| mean_acc, std_acc = np.mean(accs), np.std(accs) |
| mean_f1, std_f1 = np.mean(f1s), np.std(f1s) |
| print(f"\n RESULT: Accuracy = {mean_acc:.4f} +/- {std_acc:.4f}") |
| print(f" F1 Macro = {mean_f1:.4f} +/- {std_f1:.4f}") |
| results = { |
| 'task': 'glycan_type_classification', 'model': model_name, |
| 'n_samples': len(labels), 'n_classes': len(classes), |
| 'classes': list(classes), |
| 'distribution': {str(c): int(n) for c, n in zip(classes, np.bincount(y))}, |
| 'accuracy_mean': float(mean_acc), 'accuracy_std': float(std_acc), |
| 'f1_macro_mean': float(mean_f1), 'f1_macro_std': float(std_f1), |
| 'per_fold_accuracy': [float(a) for a in accs], |
| } |
| with open(os.path.join(output_dir, f'probe8a_glycan_type_{model_name}.json'), 'w') as f: |
| json.dump(results, f, indent=2) |
| return results |
|
|
|
|
| def probe_8b_tissue(embs, labels, output_dir, model_name): |
| from sklearn.linear_model import LogisticRegression |
| from sklearn.model_selection import StratifiedKFold |
| from sklearn.metrics import accuracy_score, f1_score |
| from sklearn.preprocessing import LabelEncoder |
| print(f"\n{'='*60}") |
| print(f"PROBE 8b: Tissue Classification ({model_name})") |
| print(f"{'='*60}") |
| counts = Counter(labels) |
| top_tissues = [t for t, c in counts.most_common(10) if c >= 30] |
| mask = np.array([l in top_tissues for l in labels]) |
| embs_f = embs[mask] |
| labels_f = np.array(labels)[mask] |
| if len(labels_f) < 50: |
| print(" SKIP: Too few samples for tissue classification") |
| return {'task': 'tissue_classification', 'model': model_name, 'skipped': True} |
| le = LabelEncoder() |
| y = le.fit_transform(labels_f) |
| classes = le.classes_ |
| print(f" Classes ({len(classes)}): {list(classes)}") |
| print(f" Samples: {len(labels_f)}") |
| n_splits = min(5, min(Counter(y).values())) |
| if n_splits < 2: |
| print(" SKIP: Not enough samples per class for stratified CV") |
| return {'task': 'tissue_classification', 'model': model_name, 'skipped': True} |
| skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=42) |
| accs, f1s = [], [] |
| for fold, (train_idx, test_idx) in enumerate(skf.split(embs_f, y)): |
| clf = LogisticRegression(max_iter=1000, C=1.0, solver='lbfgs', |
| multi_class='multinomial') |
| clf.fit(embs_f[train_idx], y[train_idx]) |
| preds = clf.predict(embs_f[test_idx]) |
| acc = accuracy_score(y[test_idx], preds) |
| f1 = f1_score(y[test_idx], preds, average='macro') |
| accs.append(acc) |
| f1s.append(f1) |
| print(f" Fold {fold+1}: acc={acc:.4f}, F1={f1:.4f}") |
| mean_acc, std_acc = np.mean(accs), np.std(accs) |
| mean_f1, std_f1 = np.mean(f1s), np.std(f1s) |
| print(f"\n RESULT: Accuracy = {mean_acc:.4f} +/- {std_acc:.4f}") |
| print(f" F1 Macro = {mean_f1:.4f} +/- {std_f1:.4f}") |
| results = { |
| 'task': 'tissue_classification', 'model': model_name, |
| 'n_samples': int(len(labels_f)), 'n_classes': int(len(classes)), |
| 'classes': list(classes), |
| 'accuracy_mean': float(mean_acc), 'accuracy_std': float(std_acc), |
| 'f1_macro_mean': float(mean_f1), 'f1_macro_std': float(std_f1), |
| 'per_fold_accuracy': [float(a) for a in accs], |
| } |
| with open(os.path.join(output_dir, f'probe8b_tissue_{model_name}.json'), 'w') as f: |
| json.dump(results, f, indent=2) |
| return results |
|
|
|
|
| def probe_8c_disease(embs_all, disease_labels, wurcs_all, output_dir, model_name): |
| print(f"\n{'='*60}") |
| print(f"PROBE 8c: Disease Association ({model_name})") |
| print(f"{'='*60}") |
| cancer_keywords = ['cancer', 'carcinoma', 'tumor', 'glioblastoma', 'melanoma', |
| 'leukemia', 'lymphoma', 'sarcoma', 'adenoma', 'myeloma', |
| 'neuroblastoma', 'hepatoblastoma'] |
| labels = [] |
| for d in disease_labels: |
| if not d: |
| labels.append('healthy') |
| elif any(kw in d.lower() for kw in cancer_keywords): |
| labels.append('cancer') |
| else: |
| labels.append('non_cancer_disease') |
| labels = np.array(labels) |
| counts = Counter(labels) |
| print(f" Distribution: {dict(counts)}") |
| has_disease = labels != 'healthy' |
| n_disease = has_disease.sum() |
| print(f" Disease glycans: {n_disease}") |
| if n_disease < 50: |
| print(" SKIP: Too few disease glycans for classification") |
| results = { |
| 'task': 'disease_association', 'model': model_name, |
| 'n_total': int(len(labels)), 'distribution': {str(k): int(v) for k, v in counts.items()}, |
| 'skipped': True, 'reason': f'Only {n_disease} disease glycans' |
| } |
| with open(os.path.join(output_dir, f'probe8c_disease_{model_name}.json'), 'w') as f: |
| json.dump(results, f, indent=2) |
| return results |
| from sklearn.linear_model import LogisticRegression |
| from sklearn.model_selection import StratifiedKFold |
| from sklearn.metrics import accuracy_score, f1_score, roc_auc_score |
| disease_idx = np.where(has_disease)[0] |
| healthy_idx = np.where(~has_disease)[0] |
| np.random.seed(42) |
| healthy_sub = np.random.choice(healthy_idx, size=min(len(healthy_idx), len(disease_idx)*3), replace=False) |
| idx = np.concatenate([disease_idx, healthy_sub]) |
| np.random.shuffle(idx) |
| X = embs_all[idx] |
| y = (labels[idx] != 'healthy').astype(int) |
| print(f" Binary probe: disease={y.sum()}, healthy={len(y)-y.sum()}") |
| n_splits = min(5, min(Counter(y).values())) |
| if n_splits < 2: |
| results = {'task': 'disease_association', 'model': model_name, 'skipped': True} |
| with open(os.path.join(output_dir, f'probe8c_disease_{model_name}.json'), 'w') as f: |
| json.dump(results, f, indent=2) |
| return results |
| skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=42) |
| accs, f1s, aucs = [], [], [] |
| for fold, (tr, te) in enumerate(skf.split(X, y)): |
| clf = LogisticRegression(max_iter=1000, C=1.0) |
| clf.fit(X[tr], y[tr]) |
| preds = clf.predict(X[te]) |
| proba = clf.predict_proba(X[te])[:, 1] |
| acc = accuracy_score(y[te], preds) |
| f1 = f1_score(y[te], preds, average='binary') |
| try: |
| auc = roc_auc_score(y[te], proba) |
| except: |
| auc = 0.5 |
| accs.append(acc); f1s.append(f1); aucs.append(auc) |
| print(f" Fold {fold+1}: acc={acc:.4f}, F1={f1:.4f}, AUC={auc:.4f}") |
| print(f"\n RESULT: Acc={np.mean(accs):.4f}+/-{np.std(accs):.4f}, AUC={np.mean(aucs):.4f}+/-{np.std(aucs):.4f}") |
| results = { |
| 'task': 'disease_association', 'model': model_name, |
| 'n_disease': int(n_disease), 'n_healthy_subsample': int(len(healthy_sub)), |
| 'distribution': {str(k): int(v) for k, v in counts.items()}, |
| 'accuracy_mean': float(np.mean(accs)), 'accuracy_std': float(np.std(accs)), |
| 'f1_mean': float(np.mean(f1s)), 'auc_mean': float(np.mean(aucs)), |
| } |
| with open(os.path.join(output_dir, f'probe8c_disease_{model_name}.json'), 'w') as f: |
| json.dump(results, f, indent=2) |
| return results |
|
|
|
|
| def probe_8d_visualization(embs, gtype_labels, tissue_labels, disease_labels, |
| output_dir, model_name): |
| plt = setup_nature_style() |
| from sklearn.manifold import TSNE |
| print(f"\n{'='*60}") |
| print(f"PROBE 8d: t-SNE Visualization ({model_name})") |
| print(f"{'='*60}") |
| n = len(embs) |
| if n > 10000: |
| np.random.seed(42) |
| idx = np.random.choice(n, 10000, replace=False) |
| embs_sub = embs[idx] |
| gtype_sub = np.array(gtype_labels)[idx] |
| tissue_sub = np.array(tissue_labels)[idx] |
| disease_sub = np.array(disease_labels)[idx] |
| else: |
| embs_sub = embs |
| gtype_sub = np.array(gtype_labels) |
| tissue_sub = np.array(tissue_labels) |
| disease_sub = np.array(disease_labels) |
| print(f" Running t-SNE on {len(embs_sub)} samples (perplexity=50)...") |
| tsne = TSNE(n_components=2, perplexity=50, random_state=42, max_iter=1000, |
| learning_rate='auto', init='pca') |
| coords = tsne.fit_transform(embs_sub) |
| fig, axes = plt.subplots(1, 3, figsize=(21, 6)) |
| ax = axes[0] |
| for gtype in ['N', 'O', 'free', 'repeat', 'lipid']: |
| mask = gtype_sub == gtype |
| if mask.sum() > 0: |
| ax.scatter(coords[mask, 0], coords[mask, 1], |
| c=GTYPE_COLORS.get(gtype, 'gray'), s=8, alpha=0.5, |
| label=f'{gtype} ({mask.sum()})', edgecolors='none', rasterized=True) |
| no_type = gtype_sub == '' |
| if no_type.sum() > 0: |
| ax.scatter(coords[no_type, 0], coords[no_type, 1], |
| c='#DDDDDD', s=4, alpha=0.2, label=f'unlabeled ({no_type.sum()})', |
| edgecolors='none', rasterized=True) |
| ax.set_xlabel('t-SNE 1'); ax.set_ylabel('t-SNE 2') |
| ax.set_title(f'Glycan Type - {model_name}') |
| ax.legend(frameon=False, markerscale=2, fontsize=8) |
| ax = axes[1] |
| tissue_counts = Counter([t for t in tissue_sub if t]) |
| top_tissues = [t for t, _ in tissue_counts.most_common(10)] |
| for i, tissue in enumerate(top_tissues): |
| mask = tissue_sub == tissue |
| if mask.sum() > 0: |
| ax.scatter(coords[mask, 0], coords[mask, 1], |
| c=TISSUE_COLORS[i % len(TISSUE_COLORS)], s=12, alpha=0.6, |
| label=f'{tissue} ({mask.sum()})', edgecolors='none', rasterized=True) |
| other = np.array([t not in top_tissues for t in tissue_sub]) |
| if other.sum() > 0: |
| ax.scatter(coords[other, 0], coords[other, 1], |
| c='#EEEEEE', s=3, alpha=0.15, edgecolors='none', rasterized=True) |
| ax.set_xlabel('t-SNE 1'); ax.set_ylabel('t-SNE 2') |
| ax.set_title(f'Tissue Source (top 10) - {model_name}') |
| ax.legend(frameon=False, markerscale=2, fontsize=7, loc='center left', |
| bbox_to_anchor=(1.0, 0.5)) |
| ax = axes[2] |
| cancer_kw = ['cancer', 'carcinoma', 'tumor', 'glioblastoma', 'melanoma', |
| 'leukemia', 'lymphoma'] |
| disease_cats = [] |
| for d in disease_sub: |
| if not d: |
| disease_cats.append('healthy') |
| elif any(kw in d.lower() for kw in cancer_kw): |
| disease_cats.append('cancer') |
| else: |
| disease_cats.append('other_disease') |
| disease_cats = np.array(disease_cats) |
| for cat, color, s, alpha in [('healthy', '#DDDDDD', 3, 0.15), |
| ('other_disease', '#E69F00', 25, 0.8), |
| ('cancer', '#D55E00', 30, 0.9)]: |
| mask = disease_cats == cat |
| if mask.sum() > 0: |
| ax.scatter(coords[mask, 0], coords[mask, 1], |
| c=color, s=s, alpha=alpha, |
| label=f'{cat} ({mask.sum()})', edgecolors='none', rasterized=True) |
| ax.set_xlabel('t-SNE 1'); ax.set_ylabel('t-SNE 2') |
| ax.set_title(f'Disease Association - {model_name}') |
| ax.legend(frameon=False, markerscale=2, fontsize=8) |
| plt.suptitle(f'Probe 8: Glycowork Metadata Embedding Analysis - GlycanBERT {model_name}', |
| fontsize=13, fontweight='bold') |
| plt.tight_layout() |
| fig_path = os.path.join(output_dir, f'probe8_tsne_{model_name.lower()}.png') |
| plt.savefig(fig_path, dpi=300, bbox_inches='tight', facecolor='white') |
| plt.savefig(fig_path.replace('.png', '.pdf'), bbox_inches='tight', facecolor='white') |
| plt.close() |
| print(f" Saved: {fig_path}") |
| np.savez(os.path.join(output_dir, f'probe8_tsne_coords_{model_name.lower()}.npz'), |
| coords=coords, gtype=gtype_sub, tissue=tissue_sub, disease=disease_sub) |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--model', choices=['v5', 'v6'], required=True) |
| parser.add_argument('--device', default='cuda') |
| parser.add_argument('--output_dir', default=None) |
| parser.add_argument('--max_samples', type=int, default=None) |
| args = parser.parse_args() |
| model_name = {'v5': 'V5-A', 'v6': 'V6'}[args.model] |
| if args.output_dir is None: |
| args.output_dir = str(PROJECT_ROOT / 'bert_v6_contrastive' / 'analysis' / |
| f'probe8_disease_tissue_{args.model}') |
| os.makedirs(args.output_dir, exist_ok=True) |
| print(f"\nLoading probe data from {DATA_PATH}...") |
| wurcs_list, iupac_list = [], [] |
| gtype_labels, tissue_labels, disease_labels = [], [], [] |
| with open(DATA_PATH) as f: |
| for row in csv.DictReader(f): |
| wurcs_list.append(row['wurcs']) |
| iupac_list.append(row.get('iupac', '')) |
| gtype_labels.append(row.get('glycan_type', '')) |
| tissue_labels.append(row.get('tissue', '').split(';')[0] if row.get('tissue') else '') |
| disease_labels.append(row.get('disease', '')) |
| total = len(wurcs_list) |
| if args.max_samples and args.max_samples < total: |
| np.random.seed(42) |
| idx = np.random.choice(total, args.max_samples, replace=False) |
| wurcs_list = [wurcs_list[i] for i in idx] |
| gtype_labels = [gtype_labels[i] for i in idx] |
| tissue_labels = [tissue_labels[i] for i in idx] |
| disease_labels = [disease_labels[i] for i in idx] |
| total = args.max_samples |
| print(f" Total: {total:,}") |
| print(f" glycan_type: {sum(1 for g in gtype_labels if g):,}") |
| print(f" tissue: {sum(1 for t in tissue_labels if t):,}") |
| print(f" disease: {sum(1 for d in disease_labels if d):,}") |
| print(f"\nLoading tokenizer from {VOCAB_PATH}...") |
| tokenizer = WURCSTokenizer(str(VOCAB_PATH)) |
| print(f" Vocab size: {tokenizer.vocab_size}") |
| ckpt = CHECKPOINTS[args.model] |
| if not ckpt.exists(): |
| print(f"ERROR: Checkpoint not found: {ckpt}") |
| sys.exit(1) |
| model = load_model(str(ckpt), device=args.device) |
| print(f"\nEmbedding {total:,} glycans...") |
| embs = batch_cls_embeddings(model, wurcs_list, tokenizer, device=args.device) |
| import gc, torch |
| del model |
| torch.cuda.empty_cache() |
| gc.collect() |
| print(f"\nEmbeddings shape: {embs.shape}") |
| all_results = {} |
| gtype_mask = np.array([bool(g) for g in gtype_labels]) |
| if gtype_mask.sum() > 100: |
| all_results['8a_glycan_type'] = probe_8a_glycan_type( |
| embs[gtype_mask], np.array(gtype_labels)[gtype_mask], |
| args.output_dir, model_name) |
| tissue_mask = np.array([bool(t) for t in tissue_labels]) |
| if tissue_mask.sum() > 50: |
| all_results['8b_tissue'] = probe_8b_tissue( |
| embs[tissue_mask], np.array(tissue_labels)[tissue_mask], |
| args.output_dir, model_name) |
| all_results['8c_disease'] = probe_8c_disease( |
| embs, disease_labels, wurcs_list, args.output_dir, model_name) |
| probe_8d_visualization(embs, gtype_labels, tissue_labels, disease_labels, |
| args.output_dir, model_name) |
| with open(os.path.join(args.output_dir, f'probe8_all_results_{model_name.lower()}.json'), 'w') as f: |
| json.dump(all_results, f, indent=2, default=str) |
| print(f"\n{'='*60}") |
| print(f"PROBE 8 COMPLETE - {model_name}") |
| print(f"Results: {args.output_dir}") |
| print(f"{'='*60}") |
|
|
|
|
| if __name__ == '__main__': |
| main() |
|
|