From In-Silico to Wet-Lab: Evaluating AI Protein Design Performance

2026年8月27日 15:36
站內 AI 整理稿

In this tutorial, we use Anthropic’s claude-protein-binder-design dataset, which contains 1,440 AI-designed miniprotein binders tested against 16 targets.

Because the release includes both computational predictions and real wet-lab results from two independent labs, we can go beyond simply studying the designs.

We evaluate how well structure predictors identify successful binders, whether combining predictions improves performance, how rankings translate into practical testing budgets, and how much disagreement comes from the assays themselves.

Also, we train a target-aware classifier to test whether these signals can reliably predict experimental success.Copy CodeCopiedUse a different Browserimport subprocess, sys, warnings, itertools, math warnings.filterwarnings("ignore") import importlib.

util needed = {"huggingfacehub": "huggingfacehub>=0.24", "pyarrow": "pyarrow", "pandas": "pandas", "sklearn": "scikit-learn", "matplotlib": "matplotlib", "scipy": "scipy"} missing = [pkg for mod, pkg in needed.items() if importlib.util.findspec(mod) is None] if missing: print("installing:", ", ".

join(missing)) subprocess.run([sys.executable, "-m", "pip", "install", "-q", missing], check=False) import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy import stats from huggingfacehub import HfApi, hfhubdownload from sklearn.

metrics import rocaucscore, cohenkappascore, averageprecisionscore from sklearn.modelselection import GroupKFold, StratifiedKFold from sklearn.ensemble import HistGradientBoostingClassifier from sklearn.inspection import permutationimportance SEED = 0 rngglobal = np.random.defaultrng(SEED) pd.

setoption("display.width", 200) pd.setoption("display.maxcolumns", 100) plt.rcParams.update({"figure.dpi": 110, "font.size": 9, "axes.grid": True, "grid.alpha": 0.25, "axes.spines.top": False, "axes.spines.

right": False}) REPO = "Anthropic/claude-protein-binder-design" BAR = "=" 78 def head(n, title): prefix = f"{n}." if str(n) else "" print(f"\n{BAR}\n {prefix}{title}\n{BAR}") head(1, "TABLE DISCOVERY") api = HfApi() repofiles = api.

listrepofiles(REPO, repotype="dataset") TABLES = {} for f in repofiles: if f.startswith("data/tables/") and f.endswith(".parquet"): key = f[len("data/tables/"): -len(".parquet")].

replace("/", "") TABLES[key] = f print(f"Found {len(TABLES)} Parquet tables:") for k in sorted(TABLES): print(f" - {k:38s} {TABLES[k]}") def loadtable(name: str) -> pd.DataFrame: """Load a subset by its viewer name, with a datasets-library fallback.""" if name in TABLES: return pd.

readparquet(hfhubdownload(REPO, TABLES[name], repotype="dataset")) from datasets import loaddataset return loaddataset(REPO, name, split="full").topandas() ds = loadtable("designsummary") print(f"\ndesignsummary: {ds.shape[0]:,} rows x {ds.

shape[1]} columns") We start by installing only what the runtime is actually missing, then enumerate the repository once and build a {subset to path} map instead of hard-coding file locations.

This matters because the naming is not uniform; the subset wetlabsummary lives at data/tables/wetlab/summary.parquet, and a guessed path would fail silently.With the map in place we pull designsummary, one row per design, 1,440 rows wide enough to carry every join we need downstream.

Copy CodeCopiedUse a different Browserhead(2, "SCHEMA + EVALUABLE SET") CALLS = {"binder", "nonbinder"} tested = ds["adaptyvbinding"].isin(CALLS) | ds["twistbinding"].isin(CALLS) ev = ds[tested].copy() ev["y"] = ev["binderfinal"].

astype(int) print(f"All designs : {len(ds):,}") print(f"Evaluable (>=1 vendor call): {len(ev):,}") print(f"Confirmed binders : {int(ev['y'].sum()):,} " f"({100 ev['y'].mean():.

1f}% base rate)") print(f"Never measured : {len(ds) - len(ev):,}") print("\nCategorical levels:") for c in ["designmodel", "campaign", "generator", "sequencedesignmethod", "vendoragreement"]: vals = ds[c].astype(str).valuecounts() print(f" {c:24s} ({len(vals)}): {', '.join(vals.index[:6])}" + (" ...

" if len(vals) > 6 else "")) print(f"\nTargets ({ds['target'].nunique()}): {', '.join(sorted(ds['target'].unique()))}") print(f"Binder length: {ds.binderlength.min()}-{ds.binderlength.max()} aa " f"(median {ds.binderlength.median():.0f})") head(3, "HIT-RATE LANDSCAPE") def wilson(k, n, z=1.

96): if n == 0: return (np.nan, np.nan, np.nan) p = k / n d = 1 + z2 / n c = (p + z2 / (2 n)) / d h = z math.sqrt(p (1 - p) / n + z2 / (4 * n2)) / d return p, max(0.0, c - h), min(1.0, c + h) def ratetable(df, by): rows = [] for key, g in df.groupby(by, dropna=False): p, lo, hi = wilson(int(g.y.

sum()), len(g)) rows.append({by: key, "n": len(g), "hits": int(g.y.sum()), "rate": p, "lo": lo, "hi": hi}) return pd.DataFrame(rows).sortvalues("rate", ascending=False).

resetindex(drop=True) for dim in ["designmodel", "campaign", "generator", "sequencedesignmethod"]: t = ratetable(ev, dim) print(f"\n--- hit rate by {dim} ---") print(t.tostring(index=False, formatters={"rate": "{:.3f}".format, "lo": "{:.3f}".format, "hi": "{:.3f}".

format})) tt = ratetable(ev, "target") fig, ax = plt.subplots(figsize=(9, 4.2)) ax.bar(tt.target, tt.rate, color="#4C72B0") ax.errorbar(tt.target, tt.rate, yerr=[(tt.rate - tt.lo).clip(lower=0), (tt.hi - tt.rate).clip(lower=0)], fmt="none", ecolor="0.25", capsize=3, lw=1) ax.axhline(ev.y.

mean(), ls="--", c="crimson", lw=1, label=f"pooled {ev.y.mean():.2f}") ax.setylabel("experimental hit rate"); ax.settitle("Hit rate by target (Wilson 95% CI)") ax.tickparams(axis="x", rotation=55); ax.legend(); plt.tightlayout(); plt.

show() print("\nRead this plot as the dominant effect size in the dataset: target choice " "swamps generator choice.Any model comparison that does not stratify by " "target is mostly measuring which targets that model was pointed at.

") We define the evaluable set by filtering on actual vendor calls rather than on binderfinal, because that column is a bool and so records the 120 never-measured designs as False rather than missing.

From there we compute hit rates by model, campaign, generator, and target, wrapping each in a Wilson interval since several subgroups sit in the small-n regime where the normal approximation misbehaves.

The target plot is the one to read first: it shows antigen choice swamping every other factor we compare.Copy CodeCopiedUse a different Browserhead(4, "PER-PREDICTOR DISCRIMINATIVE POWER") PREDICTORS = sorted({c[len("ipsaemin"):] for c in ds.columns if c.

startswith("ipsaemin")}) print(f"Predictors ({len(PREDICTORS)}): {', '.join(PREDICTORS)}") def aucci(y, s, nboot=300, seed=SEED): s = np.asarray(s, dtype=float); y = np.asarray(y, dtype=int) m = ~np.isnan(s) y, s = y[m], s[m] if len(y) < 30 or len(np.unique(y)) < 2: return dict(auc=np.nan, lo=np.

nan, hi=np.nan, n=len(y), ap=np.nan) base = rocaucscore(y, s) ap = averageprecisionscore(y, s) rng = np.random.defaultrng(seed) idx, boots = np.arange(len(y)), [] for in range(nboot): b = rng.choice(idx, len(idx), replace=True) if len(np.unique(y[b])) > 1: boots.

append(rocaucscore(y[b], s[b])) lo, hi = (np.percentile(boots, [2.5, 97.5]) if boots else (np.nan, np.nan)) return dict(auc=base, lo=lo, hi=hi, n=len(y), ap=ap) rows = [] for p in PREDICTORS: for metric in ["ipsaemin", "scdockq"]: col = f"{metric}{p}" if col in ev.columns: r = aucci(ev.

y, ev[col]) rows.append({"predictor": p, "metric": metric, **r}) perf = pd.DataFrame(rows) piv = perf.pivot(index="predictor", columns="metric", values="auc").sortvalues("ipsaemin", ascending=False) print("\nAUC vs experimental binderfinal:") print(perf.sortvalues("auc", ascending=False).

tostring( index=False, formatters={c: "{:.3f}".format for c in ["auc", "lo", "hi", "ap"]})) fig, ax = plt.subplots(figsize=(9, 4.2)) x = np.arange(len(piv)); w = 0.38 for i, (metric, colr) in enumerate([("ipsaemin", "#4C72B0"), ("scdockq", "#DD8452")]): sub = perf[perf.metric == metric].

setindex("predictor").reindex(piv.index) loe

Related

相關文章

智東西生成式AI

華為的數據供應商,拿下90%的具身大腦企業|對話景聯文CEO

機器人前瞻(公眾號:robot_pro) 作者 | 周加琦 編輯 | 漠影 機器人前瞻8月267日報道,近日,杭州AI數據運營商景聯文科技發佈了一批用宇樹機器人採集的真機數據集,包含近15000小時的真機數據,用於模型訓練。 對於當前的具身智能行業而言,15000小時真機數據已經是一筆不小的數據量。但對整個行業來說,遠遠不夠。 相比大模型時代已經積累了億級的互聯網數據,具身智能沒有現成的大規模數據可用於訓練。 對於長期處在數據產業的企業而言,這種差異感更明顯。

剛剛
IT之家生成式AI

谷歌推出全球首個雙盲 AI 評估技術,基於加密環境保障基準測試公正性

作者:小泵 責編:小泵 評論: 8 月 27 日消息,谷歌宣佈推出全球首個針對前沿專有 AI 模型的雙盲評估,將外部評估限制在加密“黑箱”環境中,避免模型提前獲取測試信息來優化性能。就像學生考前不能提前看到試題才能真實反映水平一樣,當前 AI 模型評估也面臨同樣問題:如果模型提前接觸測試題目(注:也就是所謂的“基準汙染”),評估結果的可信度就會大打折扣。

剛剛

上線一月,Seedance 2.5能否抗住“平替”圍攻?

1小時前HappyHorse與Wan3.0賽馬,誰是阿里版Seedance?昨天衝擊250億,拓竹復刻大疆第一步:隔壁開店圈地2026-08-21閱讀更多內容,狠戳這裡查看AI測評豆包MiniMax即夢AI選靠譜AI,看真實評測查看AI測評官方交流社區加入諮詢項目審核和入駐聯繫項目推薦訂閱號關注下一篇Figure用一款APP建立全球「數據採集經濟體」,中國具身企業誰會搶先跟進?

剛剛

ChatGPT報警後:誰有權審判你的對話框?

ChatGPT等生成式AI若在對話中偵測到潛在威脅,是否應主動通報執法單位,引發各界討論。支持者認為AI應承擔社會責任即時阻止憾事,反對者則擔憂語言模型易誤判玩笑與反諷,恐侵犯隱私和言論自由。目前缺乏明確法律框架界定AI舉報義務與責任歸屬,未來或需建立分級制度,由人類專業人員最終判斷。

剛剛
鈦媒體生成式AI

HBM正在被重新定義

半導體產業縱橫2026.08.27 18:25 · 來自北京全文3679字00:00 / 11:37新一代HBM4將成為技術轉折點。文 | 半導體產業縱橫隨著AI大模型訓練與推理需求持續爆發,算力芯片的性能瓶頸已從計算單元轉向存儲帶寬,HBM高帶寬內存成為制約高端AI硬件迭代的核心關鍵。在HotChips 2026國際高端芯片技術大會上,全球兩大存儲龍頭三星、SK海力士集中披露了HBM4下一代技術演進路線,打破了行業沿用多年的HBM迭代邏輯。

剛剛