Hugging Face 上托管了大量生物与化学领域的模型。问题不是找不到模型,而是模型太多、质量参差——需要一套筛选方法。
值得关注的模型类别
| 类别 | 代表 | 用途 |
|---|---|---|
| 蛋白语言模型 | ESM-2、ProtT5、ProtBERT | 蛋白表示与性质预测(见 143《ESM-2 论文精读》、144《ProtT5 论文精读》) |
| 分子语言模型 | ChemBERTa、MolFormer | 分子表示(见 145《ChemBERTa 论文精读》、147《MolFormer 论文精读》) |
| 结构预测 | ESMFold | 见 116《ESMFold 论文精读》 |
| 分子生成 | 各类 SMILES 生成模型 | 见 158《AI 分子生成模型》 |
| 反应预测 | Molecular Transformer 类 | 逆合成 |
| 单细胞 | scGPT、Geneformer | 组学分析 |
| 文本挖掘 | BioBERT、PubMedBERT、SciBERT | 文献信息抽取(见 163《大语言模型与药物发现》) |
筛选模型的方法
# 【看模型页面的哪些信息】
#
# 1) 【下载量与点赞数】
# 高下载量说明有人在用
# → 但也可能只是名字响亮
#
# 2) 【是否有对应的论文】
# 有论文 → 至少经过某种形式的审阅
# 没有 → 需要更谨慎
#
# 3) 【模型卡的完整度】
# □ 训练数据说明清楚吗?
# □ 【训练数据的时间范围?】
# → 判断是否与你的测试集重叠
# □ 评测结果与划分方式?
# □ 已知的局限性?
# □ 【许可是什么?】
#
# 【模型卡写得越详细,通常质量越高】
#
# 4) 【最近更新时间】
# 长期未更新的模型可能与新版库不兼容
#
# 5) 【社区讨论区】
# □ 有人报告问题吗?
# □ 作者响应吗?
# → 【这里常常比模型卡更能说明真实情况】
#
# 6) 【是否有官方组织维护】
# facebook/esm2_* Meta 官方
# Rostlab/prot_* ProtTrans 团队
# DeepChem/ChemBERTa-* DeepChem 团队
# ibm/MoLFormer-* IBM
# → 【官方组织的模型通常更可靠】
# 【最重要的一条】:
# 【在自己的数据上验证】
# → 排行榜与下载量都不能替代这一步
使用的通用模板
from transformers import AutoTokenizer, AutoModel
import torch
import numpy as np
def load_and_embed(model_name, sequences, batch_size=16,
max_length=512, trust_remote_code=False):
"""通用的表示提取模板"""
tokenizer = AutoTokenizer.from_pretrained(
model_name, trust_remote_code=trust_remote_code)
model = AutoModel.from_pretrained(
model_name, trust_remote_code=trust_remote_code).eval()
if torch.cuda.is_available():
model = model.cuda()
out = []
for i in range(0, len(sequences), batch_size):
batch = sequences[i:i + batch_size]
enc = tokenizer(batch, padding=True, truncation=True,
max_length=max_length, return_tensors="pt")
if torch.cuda.is_available():
enc = {k: v.cuda() for k, v in enc.items()}
with torch.no_grad():
h = model(**enc).last_hidden_state
# 【正确的平均池化:用 attention_mask 排除 padding】
mask = enc["attention_mask"].unsqueeze(-1).float()
pooled = (h * mask).sum(1) / mask.sum(1)
out.append(pooled.cpu().numpy())
return np.vstack(out)
# 【常见错误】:
# 1) 平均池化时把 padding 位置也算进去
# → 表示被稀释,长度不同的序列不可比
# 2) 【蛋白模型的输入格式】
# ProtT5 要求氨基酸间加空格(见 144)
# ESM 不需要
# → 【格式错了结果完全无意义,但不会报错】
# 3) 忘记 model.eval()
# → dropout 仍然激活,结果有随机性
# 4) trust_remote_code=True 的安全风险
# → 会执行仓库中的代码,只对可信来源使用
评估一个模型是否值得用
# 【标准流程】
#
# 第 1 步:【建立基线】
# 蛋白任务:氨基酸组成 + 理化性质
# 分子任务:ECFP + 描述符
# 模型:LightGBM
# → 【几分钟就能跑完】
#
# 第 2 步:【提取预训练表示】
# 冻结模型,只提取表示
# → 喂给同样的 LightGBM
#
# 第 3 步:【同条件比较】
# 同样的数据划分(骨架/时间划分)
# 同样的下游模型
# 多个随机种子
#
# 第 4 步:【判断】
# 提升 > 2 倍标准差 → 有价值
# 否则 → 【用基线,省下部署成本】
#
# 第 5 步:【考虑拼接】
# 把预训练表示与手工特征拼接
# → 常常比单独用任一种都好
#
# 【一个实用的观察】:
# 多数情况下,预训练表示的价值
# 在【数据量少】时最明显
# → 数据量 < 500 时值得试
# → 数据量 > 5000 时收益递减
import numpy as np
from sklearn.model_selection import cross_val_score
import lightgbm as lgb
def compare_representations(reps_dict, y, cv=5):
"""比较多种表示的效果"""
for name, X in reps_dict.items():
scores = []
for seed in range(3):
m = lgb.LGBMRegressor(n_estimators=500, random_state=seed,
verbose=-1)
s = cross_val_score(m, X, y, cv=cv,
scoring="neg_root_mean_squared_error")
scores.append(-s.mean())
print(f"{name:25s} RMSE = {np.mean(scores):.4f} ± {np.std(scores):.4f}")
# 用法:
# compare_representations({
# "ECFP+描述符(基线)": X_baseline,
# "ChemBERTa": X_chemberta,
# "MolFormer": X_molformer,
# "拼接": np.hstack([X_baseline, X_chemberta]),
# }, y)
使用中的注意事项
- 数据泄漏的风险:预训练模型可能见过你的测试集分子——尤其是从公开数据库来的分子。评估时应检查;
- 许可差异很大:从完全开放到禁止商用都有,企业使用前必须确认;
- 模型体积与推理成本:大模型需要 GPU,部署复杂度显著增加;
- 版本固定:模型可能被更新,用
revision参数固定到具体 commit 保证可复现; - 安全性:
trust_remote_code=True会执行仓库中的代码——只对可信来源使用; - 不同层的表示差异大:应该在验证集上比较不同层(见 144《ProtT5 论文精读》)。
关键要点
- 优先选官方组织维护、有论文、模型卡完整的模型;讨论区常比模型卡更说明问题;
- 平均池化必须用 attention_mask 排除 padding;ProtT5 要求氨基酸间加空格;
- 预训练表示的价值在数据量少时最明显,数据多时收益递减;
- 用
revision固定模型版本;trust_remote_code=True只对可信来源使用。
延伸资源
- ESM-2:143《ESM-2 论文精读》;ProtT5:144《ProtT5 论文精读》;ChemBERTa:145《ChemBERTa 论文精读》;MolFormer:147《MolFormer 论文精读》;
- 大语言模型:163《大语言模型与药物发现》;论文阅读框架:170《AI 制药论文阅读框架》。