mirror of
https://github.com/RYDE-WORK/lnp_ml.git
synced 2026-07-22 05:49:55 +08:00
206 lines
8.0 KiB
Markdown
206 lines
8.0 KiB
Markdown
# LLM 分子编码器分支(LLM Encoder Branch)
|
||
|
||
本模块在 LNP 多任务预测模型上新增了一个**可选的 LLM 分子编码器分支**。它把可电离脂质的 SMILES 送入一个冻结的预训练分子模型,提取语义向量,作为一个额外的 token(V5)追加到模型的 fusion 序列中参与多任务预测。
|
||
|
||
支持四种分子编码器,可通过参数自由切换,用于横向对比:
|
||
|
||
| llm_type | 模型 | 年份 / 发表 | 参数量 | hidden | 输入 |
|
||
|---|---|---|---|---|---|
|
||
| `biot5` | BioT5-plus | 2024, arXiv | ~250M | 768 | SMILES |
|
||
| `molt5` | MolT5-base | 2022, EMNLP | 109M | 768 | SMILES |
|
||
| `moleculestm` | MoleculeSTM GNN | 2023, Nature MI | ~2M (GNN) | 300 | 分子图 |
|
||
| `selfiested` | SELFIES-TED | 2025, IBM | 358M | 1024 | SELFIES |
|
||
|
||
---
|
||
|
||
## 1. 设计说明
|
||
|
||
- **非侵入式**:默认 `use_llm=False`,此时模型行为与原版完全一致,不影响既有功能。只有显式开启才会启用 LLM 分支。
|
||
- **统一接口**:四个 encoder 的 `forward(List[str]) -> [B, d_model]` 接口完全一致,通过 `models.build_llm_encoder()` 工厂函数按 `llm_type` 实例化。
|
||
- **冻结主体**:LLM / GNN 主体全程冻结,只有内部一层 `projection`(LayerNorm + Linear + ReLU + Dropout)参与训练,参数量增加极小。
|
||
- **惰性导入**:`encoders/__init__.py` 对四个 LLM encoder 采用惰性导入,未安装 `torch_geometric` / `selfies` 等可选依赖时,只要不使用对应 encoder,导入不会报错。
|
||
|
||
---
|
||
|
||
## 2. 文件清单
|
||
|
||
新增文件(均在 `lnp_ml/modeling/encoders/` 下):
|
||
|
||
```
|
||
encoders/
|
||
├── llm_encoder.py # BioT5-plus (类名 LLMEncoder)
|
||
├── molt5_encoder.py # MolT5 (类名 MolT5Encoder)
|
||
├── moleculestm_encoder.py # MoleculeSTM (类名 MoleculeSTMEncoder)
|
||
├── molecule_gnn_model.py # MoleculeSTM 的 GNN 网络定义(被上一个文件 import)
|
||
└── selfiested_encoder.py # SELFIES-TED (类名 SELFIESTEDEncoder)
|
||
```
|
||
|
||
修改文件:
|
||
|
||
```
|
||
lnp_ml/modeling/models.py # 新增 use_llm / llm_type 等参数 + build_llm_encoder 工厂 + V5 追加逻辑;desc 维度 210 -> 217
|
||
lnp_ml/modeling/encoders/__init__.py # 惰性导出四个 LLM encoder
|
||
verify_llm_encoder.py # LLM 模块验证脚本(四项测试)
|
||
```
|
||
|
||
> 注:`models.py` 中 `DEFAULT_INPUT_DIMS["desc"]` 已从 210 更新为 **217**,以匹配新版 RDKit 的描述符维度。
|
||
|
||
---
|
||
|
||
## 3. 依赖安装
|
||
|
||
**基础依赖(所有模型都需要):**
|
||
```bash
|
||
pip install rdkit loguru transformers sentencepiece optuna
|
||
```
|
||
|
||
**按模型追加:**
|
||
|
||
| 模型 | 额外依赖 |
|
||
|---|---|
|
||
| biot5 | 无(基础依赖即可) |
|
||
| molt5 | 无 |
|
||
| moleculestm | `pip install torch_geometric torch_scatter torch_sparse ogb` |
|
||
| selfiested | `pip install selfies` |
|
||
|
||
---
|
||
|
||
## 4. 预训练权重
|
||
|
||
| 模型 | 是否需手动下载 | 说明 |
|
||
|---|---|---|
|
||
| **molt5** | 否 | 训练时自动从 HuggingFace 拉取 `laituan245/molt5-base` |
|
||
| **selfiested** | 否 | 训练时自动从 HuggingFace 拉取 `ibm/materials.selfies-ted` |
|
||
| **biot5** | 是 | 需下载 BioT5-plus 权重文件夹,见下方 |
|
||
| **moleculestm** | 是 | 需下载 MoleculeSTM GNN 权重,见下方 |
|
||
|
||
**BioT5-plus 权重下载:**
|
||
从 HuggingFace `QizhiPei/biot5-plus-base` 下载整个权重文件夹(含 `config.json`、`tokenizer_config.json` 等),放到本地任意路径,训练时用 `--llm-model-path <该路径>` 指定。也可直接传 HuggingFace ID `QizhiPei/biot5-plus-base` 在线加载。
|
||
|
||
**MoleculeSTM GNN 权重下载:**
|
||
```python
|
||
from huggingface_hub import snapshot_download
|
||
snapshot_download(
|
||
repo_id="chao1224/MoleculeSTM",
|
||
repo_type="model",
|
||
local_dir="<本地目录>/moleculestm_weights",
|
||
allow_patterns="*Graph*",
|
||
)
|
||
```
|
||
下载后使用的 GNN 权重路径为:
|
||
```
|
||
<本地目录>/moleculestm_weights/pretrained_MoleculeSTM/SciBERT-Graph-3e-5-1-1e-4-1-InfoNCE-0.1-32-32/molecule_model.pth
|
||
```
|
||
训练时用 `--llm-model-path <该 .pth 路径>` 指定。
|
||
|
||
---
|
||
|
||
## 5. 用法
|
||
|
||
### 5.1 代码中直接构造模型
|
||
|
||
```python
|
||
from lnp_ml.modeling.models import LNPModelWithoutMPNN
|
||
|
||
# 不启用 LLM(与原版行为一致)
|
||
model = LNPModelWithoutMPNN(d_model=256)
|
||
|
||
# 启用 LLM 分支(以 MolT5 为例)
|
||
model = LNPModelWithoutMPNN(
|
||
d_model=256,
|
||
use_llm=True,
|
||
llm_type="molt5", # biot5 / molt5 / moleculestm / selfiested
|
||
llm_model_path="laituan245/molt5-base", # 见上表,不同模型路径不同
|
||
llm_device="cuda",
|
||
)
|
||
```
|
||
|
||
各模型对应的 `llm_type` 和 `llm_model_path`:
|
||
|
||
| llm_type | llm_model_path 示例 |
|
||
|---|---|
|
||
| `biot5` | `QizhiPei/biot5-plus-base` 或本地 BioT5 权重文件夹路径 |
|
||
| `molt5` | `laituan245/molt5-base` |
|
||
| `moleculestm` | `.../moleculestm_weights/.../molecule_model.pth` |
|
||
| `selfiested` | `ibm/materials.selfies-ted` |
|
||
|
||
### 5.2 验证 LLM 模块(训练前建议先跑)
|
||
|
||
```bash
|
||
python verify_llm_encoder.py \
|
||
--model_path <llm_model_path> \
|
||
--lnp_repo_path <仓库根目录>
|
||
```
|
||
四项测试(输出维度、维度对齐、可复现性、过拟合 sanity)全部 PASS 再训练。
|
||
|
||
### 5.3 训练
|
||
|
||
> 训练脚本(`final_train_optuna_cv.py` / `pretrain.py`)需要支持 `--use-llm` / `--llm-type` / `--llm-model-path` / `--llm-device` 参数。各模型的完整 Colab 操作步骤见 `docs/` 下四份 quickstart 指南。
|
||
|
||
---
|
||
|
||
## 6. 实验结果
|
||
|
||
四个模型在 LNP 测试集上的对比(核心指标 delivery R²,baseline 为不含 LLM 的版本):
|
||
|
||
| 任务 | Baseline | MolT5 | MoleculeSTM | BioT5-plus | SELFIES-TED |
|
||
|---|---|---|---|---|---|
|
||
| delivery R² | 0.373 | 0.602 | 0.552 | **0.633** | 0.618 |
|
||
| size R² | 0.215 | 0.440 | 0.408 | 0.423 | 0.381 |
|
||
| pdi acc | 0.786 | 0.748 | 0.710 | 0.695 | 0.664 |
|
||
| ee acc | 0.679 | 0.702 | 0.679 | 0.679 | 0.679 |
|
||
| toxic acc | 0.980 | 0.980 | 0.951 | 0.960 | 0.941 |
|
||
| biodist KL | 0.293 | 0.727 | 0.646 | 0.703 | 0.643 |
|
||
|
||
**主要结论:**
|
||
1. 四个 LLM 编码器都显著提升了核心回归任务,delivery R² 从 baseline 的 0.37 最高提升到 0.63。
|
||
2. 领域专用预训练比堆参数更重要:BioT5-plus(针对生物分子预训练)效果最好,而参数最大、最新的 SELFIES-TED 仅排第二。
|
||
3. 序列模型整体优于图模型:三个基于 SMILES/SELFIES 的模型均优于基于图的 MoleculeSTM。
|
||
4. MolT5 性价比最高:仅 109M 参数,delivery R² 排第三且与第一仅差 0.03,分类任务表现最均衡。
|
||
5. 共同局限:biodist(分布预测)四个 LLM 均略低于 baseline,属多任务学习中的 trade-off。
|
||
|
||
> 各模型每个任务的详细指标见 `results/<模型名>/` 目录。
|
||
|
||
---
|
||
|
||
## 附:训练脚本改动
|
||
|
||
本 PR 已经更新了两个训练脚本以支持 LLM 分支(MoE 相关代码未引入):
|
||
|
||
- `lnp_ml/modeling/final_train_optuna_cv.py`
|
||
- `lnp_ml/modeling/pretrain.py`
|
||
|
||
两者都新增了四个命令行参数,并透传给模型:
|
||
|
||
```
|
||
--use-llm # 是否启用 LLM 分支
|
||
--llm-type {biot5,molt5,moleculestm,selfiested} # 选择哪个 encoder
|
||
--llm-model-path <路径或HF_ID> # 见第 4/5 节
|
||
--llm-device cuda # LLM 推理设备
|
||
```
|
||
|
||
预训练保存的 checkpoint 的 config 中也会记录 LLM 设置,恢复模型时自动读取。
|
||
|
||
### 训练示例(以 MolT5 为例)
|
||
|
||
```bash
|
||
# 预训练
|
||
python -m lnp_ml.modeling.pretrain main \
|
||
--train-path data/processed/train_pretrain.parquet \
|
||
--val-path data/processed/val_pretrain.parquet \
|
||
--use-llm --llm-type molt5 \
|
||
--llm-model-path laituan245/molt5-base \
|
||
--llm-device cuda --epochs 50 --lr 1e-4 --device cuda
|
||
|
||
# 正式训练
|
||
python lnp_ml/modeling/final_train_optuna_cv.py \
|
||
--init-from-pretrain models/pretrain_delivery.pt \
|
||
--use-llm --llm-type molt5 \
|
||
--llm-model-path laituan245/molt5-base \
|
||
--llm-device cuda \
|
||
--n-trials 20 --epochs-per-trial 30 --seed 42 \
|
||
--device cuda --output-dir models/final
|
||
```
|
||
|
||
切换其他模型只需改 `--llm-type` 和 `--llm-model-path`(见第 5.1 节对照表)。
|