lnp_ml/docs/colab_molt5_quickstart.md

305 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LNP-ML + MolT5 快速上手指南
适用于:拿到代码包、从未运行过任何代码的新组员。
论文Edwards et al., "Translation between Molecules and Natural Language", EMNLP 2022
模型laituan245/molt5-base109M 参数,无需手动下载,训练时自动从 HuggingFace 拉取)
---
## 前提条件(开始前确认)
- Google Drive 的 `MyDrive/lnp_project_molt5/` 下已有以下内容:
- `lnp_ml/`:项目代码(从 BioT5 版本复制来的,已修改)
- MolT5 权重无需提前下载,训练时自动拉取
如果以上文件夹不存在,先看文末的【从零开始】章节。
---
## 每次重新打开 Colab 都需要运行
### Cell 1挂载 Drive
```python
from google.colab import drive
drive.mount('/content/drive')
```
> 弹出授权页面时,确认选择有 `lnp_project_molt5` 的 Google 账号。
---
### Cell 2安装依赖
```python
!pip install rdkit loguru transformers sentencepiece optuna -q
```
---
### Cell 3配置路径
```python
import os, sys
LNP_PATH = "/content/drive/MyDrive/lnp_project_molt5/lnp_ml"
sys.path.insert(0, LNP_PATH)
os.chdir(LNP_PATH)
print("路径配置完成 ✓")
```
---
### Cell 4确认文件完整
```python
checks = [
f"{LNP_PATH}/lnp_ml/modeling/models.py",
f"{LNP_PATH}/lnp_ml/modeling/encoders/molt5_encoder.py",
f"{LNP_PATH}/lnp_ml/modeling/encoders/__init__.py",
f"{LNP_PATH}/data/processed/train.parquet",
f"{LNP_PATH}/data/processed/val.parquet",
f"{LNP_PATH}/data/processed/test.parquet",
f"{LNP_PATH}/data/processed/train_pretrain.parquet",
f"{LNP_PATH}/data/processed/val_pretrain.parquet",
]
for f in checks:
print(f"{'✓' if os.path.exists(f) else '✗ 缺失'} {f}")
```
**所有文件都显示 ✓ 再继续。**
---
## 正式流程
### Cell 5验证 MolT5 模块
```python
!PYTHONPATH={LNP_PATH} \
python {LNP_PATH}/verify_llm_encoder.py \
--model_path "laituan245/molt5-base" \
--lnp_repo_path "{LNP_PATH}"
```
**正常输出:**
```
TEST 1 PASSED ✓
TEST 2 PASSED ✓
TEST 3 PASSED ✓
TEST 4 PASSED ✓
ALL TESTS PASSED ✓
```
---
### Cell 6预训练约 10 分钟)
```python
!PYTHONPATH={LNP_PATH} \
python -m lnp_ml.modeling.pretrain main \
--train-path data/processed/train_pretrain.parquet \
--val-path data/processed/val_pretrain.parquet \
--epochs 50 \
--lr 1e-4 \
--device cuda
```
完成后确认权重存在:
```python
print("✓ 预训练权重存在" if os.path.exists(f"{LNP_PATH}/models/pretrain_delivery.pt")
else "✗ 预训练权重未生成")
```
参考结果:`Best val_loss: 0.5867`
---
### Cell 7正式训练约 20 分钟)
```python
!PYTHONPATH={LNP_PATH} \
python lnp_ml/modeling/final_train_optuna_cv.py \
--init-from-pretrain models/pretrain_delivery.pt \
--use-llm \
--llm-model-path "laituan245/molt5-base" \
--llm-device cuda \
--n-trials 20 \
--epochs-per-trial 30 \
--seed 42 \
--device cuda \
--output-dir models/final
```
完成后确认:
```python
print("✓ 模型权重存在" if os.path.exists(f"{LNP_PATH}/models/final/model.pt")
else "✗ 模型未生成")
```
参考结果:`Best val_loss: 2.3131`
---
### Cell 8测试评估
```python
!PYTHONPATH={LNP_PATH} \
python lnp_ml/modeling/predict.py test \
--test-path data/processed/test.parquet \
--model-path models/final/model.pt \
--output-path models/final/test_results.json
import json
with open(f'{LNP_PATH}/models/final/test_results.json') as f:
results = json.load(f)
print("=== 分类任务 ===")
for task in ['pdi', 'ee', 'toxic']:
m = results['detailed_metrics'][task]
print(f" {task}: acc={m['accuracy']:.4f}, f1={m['f1']:.4f}")
print("\n=== 回归任务 ===")
for task in ['size', 'delivery']:
m = results['detailed_metrics'][task]
print(f" {task}: R²={m['r2']:.4f}, RMSE={m['rmse']:.4f}")
print("\n=== 分布任务 ===")
m = results['detailed_metrics']['biodist']
print(f" biodist: KL={m['kl_divergence']:.4f}, JS={m['js_divergence']:.4f}")
```
**参考指标:**
| 任务 | 指标 | 参考值 |
|---|---|---|
| delivery | R² | ~0.60 |
| size | R² | ~0.44 |
| pdi | acc | ~0.75 |
| ee | acc | ~0.70 |
| toxic | acc | ~0.98 |
| biodist | KL | ~0.73 |
---
### Cell 9备份模型
```python
!cp -r {LNP_PATH}/models/final \
{LNP_PATH}/models/final_backup
print("备份完成 ✓")
```
---
## 如果已有训练好的模型,直接从 Cell 8 开始
只需运行 Cell 1 → Cell 2 → Cell 3 → Cell 8跳过 Cell 4-7。
---
## 常见问题
| 报错 | 原因 | 解决方法 |
|---|---|---|
| `No module named 'rdkit'` | 依赖未安装 | 重新运行 Cell 2 |
| `No module named 'lnp_ml'` | 路径未配置 | 重新运行 Cell 3 |
| `'NoneType' has no attribute 'Study'` | optuna 未安装 | 重新运行 Cell 2 |
| `Drive not mounted` | Drive 未挂载 | 重新运行 Cell 1 |
| `Mountpoint must not already contain files` | Drive 已挂载但缓存混乱 | 菜单栏 → 运行时 → 重新启动运行时,再从 Cell 1 开始 |
| Colab 断开后重连 | 环境变量丢失 | 从 Cell 1 重跑Cell 6/7 已有权重可跳过 |
---
## 【从零开始】如果 lnp_project_molt5 不存在
### Step A从 BioT5 版本复制代码
```python
import os
os.makedirs("/content/drive/MyDrive/lnp_project_molt5", exist_ok=True)
!cp -r "/content/drive/MyDrive/lnp_project_biot5-plus/lnp_ml" \
"/content/drive/MyDrive/lnp_project_molt5/lnp_ml"
print("代码复制完成 ✓")
```
### Step B上传 molt5_encoder.py
```python
from google.colab import files
import shutil
uploaded = files.upload() # 选择 molt5_encoder.py
shutil.copy(
'molt5_encoder.py',
'/content/drive/MyDrive/lnp_project_molt5/lnp_ml/lnp_ml/modeling/encoders/molt5_encoder.py'
)
print("上传完成 ✓")
```
### Step C修改代码文件
```python
# Step C-1更新 __init__.py
path = "/content/drive/MyDrive/lnp_project_molt5/lnp_ml/lnp_ml/modeling/encoders/__init__.py"
new_content = """from lnp_ml.modeling.encoders.rdkit_encoder import CachedRDKitEncoder
from lnp_ml.modeling.encoders.mpnn_encoder import CachedMPNNEncoder
from .llm_encoder import LLMEncoder
from .molt5_encoder import MolT5Encoder
__all__ = ["CachedRDKitEncoder", "CachedMPNNEncoder", "LLMEncoder", "MolT5Encoder"]
"""
with open(path, 'w') as f:
f.write(new_content)
print("__init__.py ✓")
```
```python
# Step C-2更新 models.py
path = "/content/drive/MyDrive/lnp_project_molt5/lnp_ml/lnp_ml/modeling/models.py"
with open(path, 'r') as f:
content = f.read()
content = content.replace(
"from lnp_ml.modeling.encoders import CachedRDKitEncoder, CachedMPNNEncoder, LLMEncoder",
"from lnp_ml.modeling.encoders import CachedRDKitEncoder, CachedMPNNEncoder, LLMEncoder, MolT5Encoder"
)
content = content.replace(
"self.llm_encoder = LLMEncoder(",
"self.llm_encoder = MolT5Encoder("
)
with open(path, 'w') as f:
f.write(content)
print("models.py ✓")
```
```python
# Step C-3修复 verify 脚本
path = "/content/drive/MyDrive/lnp_project_molt5/lnp_ml/verify_llm_encoder.py"
with open(path, 'r') as f:
content = f.read()
content = content.replace(
"from lnp_ml.modeling.encoders.llm_encoder import LLMEncoder",
"from lnp_ml.modeling.encoders.molt5_encoder import MolT5Encoder as LLMEncoder"
)
content = content.replace(
"encoder._llm.named_parameters()",
"encoder._encoder.named_parameters()"
)
content = content.replace(
"model.llm_encoder._llm.parameters()",
"model.llm_encoder._encoder.parameters()"
)
with open(path, 'w') as f:
f.write(content)
print("verify_llm_encoder.py ✓")
```
完成后从 Cell 5 开始正常运行。