# ESMFold 蛋白质结构

**Meta AI 的超高速蛋白质结构预测** — 无需多序列比对，从氨基酸序列在几秒内预测 3D 蛋白质结构。

> 🧬 开发者 **Meta AI 研究** | MIT 许可证 | 比 AlphaFold2 快 10x–60x

***

## 什么是 ESMFold？

ESMFold 是 Meta AI 的蛋白质结构预测系统，利用 **Evolutionary Scale Modeling (ESM-2)** — 世界上最大的蛋白质语言模型（150 亿参数）— 直接从氨基酸序列预测 3D 蛋白质结构。

### 相较于 AlphaFold2 的主要优势

| 功能            | ESMFold   | AlphaFold2  |
| ------------- | --------- | ----------- |
| 是否需要 MSA      | ❌ 否       | ✅ 是         |
| 速度（典型蛋白质）     | **约 2 秒** | 约 10 分钟–数小时 |
| 准确性（TM-score） | \~0.87    | \~0.92      |
| GPU 显存（650aa） | ≈8GB      | ≈8GB        |
| 单序列输入         | ✅ 是       | 有限          |
| 孤立蛋白（无同源序列）   | ✅ 优秀      | 困难          |

### 为什么不使用 MSA？

AlphaFold2 需要 **多序列比对（MSA）** — 收集并比对查询蛋白的进化同源序列。这个过程计算代价高，对于没有进化同源的全新或工程化蛋白是不可行的。

ESMFold 将进化信息存储在其语言模型权重中 **（训练于 2.5 亿条蛋白质序列），** 从而完全消除 MSA。这样它变得：

* **更快：** 无需 MSA 搜索（每次预测节省数分钟）
* **更具可扩展性：** 高效处理整个蛋白组
* **对新型蛋白更友好：** 工程化序列没有进化同源

***

## 在 Clore.ai 上快速开始

### 步骤 1：选择服务器

在 [clore.ai](https://clore.ai) 市场：

* **最低要求：** NVIDIA GPU，带有 **16GB 显存** （ESM-2 语言模型较大）
* **推荐：** A100 40GB、RTX 3090、RTX 4090 可用于完整模型
* **较小的选项：** 使用 `esm2_t33_650M_UR50D` 适用于 8GB 显存

GPU 显存指南：

| 蛋白质长度        | 模型变体        | 所需显存   |
| ------------ | ----------- | ------ |
| 最多 300 个氨基酸  | ESMFold（3B） | 约 16GB |
| 最多 500 个氨基酸  | ESMFold（3B） | 约 20GB |
| 最多 1000 个氨基酸 | ESMFold（3B） | 约 40GB |
| 最多 600 个氨基酸  | ESMFold（分块） | ≈8GB   |

### 步骤 2：构建自定义 Docker 镜像

```dockerfile
FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-devel

# 系统依赖
RUN apt-get update && apt-get install -y \
    git \
    wget \
    curl \
    openssh-server \
    libhdf5-dev \
    pkg-config \
    && rm -rf /var/lib/apt/lists/*

# 配置 SSH
RUN mkdir /var/run/sshd && \
    echo 'root:esmfold' | chpasswd && \
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# 安装 ESMFold 及依赖
RUN pip install --no-cache-dir \
    fair-esm[esmfold] \
    torch \
    biopython \
    biotite \
    fastapi \
    uvicorn \
    pydantic \
    openmm==8.0.0 \
    pdbfixer

# 安装 OpenFold（ESMFold 所需）
RUN pip install "git+https://github.com/aqlaboratory/openfold.git@4b41059694619831a7db195b7e0988fc4ff3a307"

EXPOSE 22

CMD ["/usr/sbin/sshd", "-D"]
```

### 步骤 3：在 Clore.ai 上部署

* **Docker 镜像：** `yourname/esmfold:latest`
* **端口：** `22` （SSH）
* **环境：** `NVIDIA_VISIBLE_DEVICES=all`

***

## 安装与设置

### 方法 1：pip 安装

```bash
# 安装 ESMFold
pip install fair-esm[esmfold]

# 安装 OpenFold（必需依赖）
pip install "git+https://github.com/aqlaboratory/openfold.git@4b41059694619831a7db195b7e0988fc4ff3a307"

# 可选，但推荐
pip install biotite biopython
```

### 方法 2：从源码安装

```bash
git clone https://github.com/facebookresearch/esm.git
cd esm
pip install -e ".[esmfold]"
```

### 验证安装

```python
import esm
print("ESM 版本：", esm.__version__)

# 快速模型加载测试
model = esm.pretrained.esmfold_v1()
print("ESMFold 加载成功！")
```

***

## 基本用法

### 预测单个蛋白质结构

```python
import torch
import esm

# 加载 ESMFold 模型
model = esm.pretrained.esmfold_v1()
model = model.eval().cuda()

# 可选：启用分块大小以节省显存
# 增加计算时间但减少显存使用
model.set_chunk_size(64)  # 减少以降低显存需求

# 蛋白质序列（示例：溶菌酶 C）
sequence = "KVFGRCELAAAMKRHGLDNYRGYSLGNWVCAAKFESNFNTQATNRNTDGSTDYGILQINSRWWCNDGRTPGSRNLCNIPCSALLSSDITASVNCAKKIVSDGNGMNAWVAWRNRCKGTDVQAWIRGCRL"

# 预测结构
with torch.no_grad():
    output = model.infer_pdb(sequence)

# 保存 PDB 文件
with open("lysozyme.pdb", "w") as f:
    f.write(output)

print(f"结构已预测！已保存到 lysozyme.pdb")
print(f"序列长度：{len(sequence)} 个氨基酸")
```

### 批量预测多个序列

```python
import torch
import esm

model = esm.pretrained.esmfold_v1()
model = model.eval().cuda()

sequences = {
    "protein_A": "MKTAYIAKQRQISFVKSHFSRQ...",
    "protein_B": "MGDVEKGKKIFVQKCAQCHTVEK...",
    "ubiquitin": "MQIFVKTLTGKTITLEVEPSDTIENVKAKIQDKEGIPPDQQRLIFAGKQLEDGRTLSDYNIQKESTLHLVLRLRGG",
}

for name, seq in sequences.items():
    with torch.no_grad():
        output = model.infer_pdb(seq)
    
    with open(f"{name}.pdb", "w") as f:
        f.write(output)
    
    print(f"已预测 {name}：{len(seq)} aa")

print("所有预测完成！")
```

### 获取每残基置信度（pLDDT）

```python
import torch
import esm
import numpy as np

model = esm.pretrained.esmfold_v1()
model = model.eval().cuda()

sequence = "MQIFVKTLTGKTITLEVEPSDTIENVKAKIQDKEGIPPDQQRLIFAGKQLEDGRTLSDYNIQKESTLHLVLRLRGG"

with torch.no_grad():
    output = model.infer(sequence)

# 提取 pLDDT 分数（每残基置信度）
plddt = output["plddt"].cpu().numpy()  # 形状: [1, seq_len]
plddt_per_residue = plddt[0]

print(f"平均 pLDDT：{plddt_per_residue.mean():.2f}")
print(f"高置信残基（>90）：{(plddt_per_residue > 90).sum()}")
print(f"低置信残基（<50）：{(plddt_per_residue < 50).sum()}")

# 将置信区间分类
for i, score in enumerate(plddt_per_residue):
    if score >= 90:
        confidence = "非常高（蓝色）"
    elif score >= 70:
        confidence = "可信（浅蓝）"
    elif score >= 50:
        confidence = "低（黄色）"
    else:
        confidence = "非常低（橙色）"
    # print(f"残基 {i+1}：{score:.1f} - {confidence}")  # 取消注释以输出完整信息
```

***

## REST API 服务器

为 ESMFold 构建生产级 API：

```python
# api_server.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import torch
import esm
import base64
from typing import Optional

app = FastAPI(
    title="ESMFold 蛋白质结构预测 API",
    description="从氨基酸序列预测蛋白质 3D 结构",
    version="1.0.0"
)

# 在启动时加载模型
print("正在加载 ESMFold 模型（约需 30 秒）...")
model = esm.pretrained.esmfold_v1()
model = model.eval().cuda()
model.set_chunk_size(64)  # 内存优化
print("ESMFold 已就绪！")

class PredictionRequest(BaseModel):
    sequence: str
    name: Optional[str] = "protein"

class PredictionResponse(BaseModel):
    name: str
    sequence_length: int
    pdb_content: str
    mean_plddt: float
    inference_time_seconds: float

@app.post("/predict", response_model=PredictionResponse)
async def predict_structure(request: PredictionRequest):
    """从氨基酸序列预测蛋白质 3D 结构。"""
    
    # 验证序列
    valid_aa = set("ACDEFGHIKLMNPQRSTVWY")
    sequence = request.sequence.upper().strip()
    
    invalid = set(sequence) - valid_aa
    if invalid:
        raise HTTPException(
            status_code=400,
            detail=f"序列中包含无效氨基酸：{invalid}。请使用标准的 20 种氨基酸。"
        )
    
    if len(sequence) > 2000:
        raise HTTPException(
            status_code=400,
            detail="序列过长（最多 2000 个氨基酸）。对于更长序列，请使用分块预测。"
        )
    
    start_time = time.time()
    
    try:
        with torch.no_grad():
            output = model.infer(sequence)
            pdb_content = model.output_to_pdb(output)[0]
            
        plddt = output["plddt"].cpu().numpy()[0]
        mean_plddt = float(plddt.mean())
        
    except torch.cuda.OutOfMemoryError:
        torch.cuda.empty_cache()
        raise HTTPException(
            status_code=507,
            detail="GPU 内存不足。尝试更短的序列或减小分块大小。"
        )
    
    inference_time = time.time() - start_time
    
    return PredictionResponse(
        name=request.name,
        sequence_length=len(sequence),
        pdb_content=pdb_content,
        mean_plddt=mean_plddt,
        inference_time_seconds=round(inference_time, 2)
    )

@app.get("/health")
def health():
    gpu_mem = torch.cuda.memory_allocated() / 1024**3 if torch.cuda.is_available() else 0
    return {
        "status": "ok",
        "model": "ESMFold v1",
        "device": str(next(model.parameters()).device),
        "gpu_memory_gb": round(gpu_mem, 2)
    }

@app.get("/")
def root():
    return {"message": "ESMFold API — /predict 用于预测结构，/docs 为 Swagger UI"}
```

```bash
# 运行 API
pip install fastapi uvicorn
uvicorn api_server:app --host 0.0.0.0 --port 8080 --workers 1
```

***

## API 使用示例

```bash
# 通过 API 预测结构
curl -X POST http://localhost:8080/predict \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ubiquitin",
    "sequence": "MQIFVKTLTGKTITLEVEPSDTIENVKAKIQDKEGIPPDQQRLIFAGKQLEDGRTLSDYNIQKESTLHLVLRLRGG"
  }' | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f\"Name: {data['name']}\")
print(f\"Length: {data['sequence_length']} aa\")
print(f\"Mean pLDDT: {data['mean_plddt']:.1f}\")
print(f\"Time: {data['inference_time_seconds']}s\")
# 保存 PDB
open('ubiquitin.pdb', 'w').write(data['pdb_content'])
print('PDB 已保存！')
"
```

***

## 批处理脚本

```python
# batch_predict.py
import torch
import esm
import os
from pathlib import Path
from Bio import SeqIO  # pip install biopython

def predict_fasta(fasta_file: str, output_dir: str, chunk_size: int = 64):
    """为 FASTA 文件中的所有序列预测结构。"""
    
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    
    # 加载模型
    model = esm.pretrained.esmfold_v1()
    model = model.eval().cuda()
    model.set_chunk_size(chunk_size)
    
    # 读取 FASTA
    sequences = list(SeqIO.parse(fasta_file, "fasta"))
    print(f"正在为 {len(sequences)} 个蛋白质预测结构...")
    
    results = []
    for i, record in enumerate(sequences):
        seq = str(record.seq).upper()
        name = record.id
        
        print(f"[{i+1}/{len(sequences)}] 正在预测 {name}（{len(seq)} aa）...")
        
        try:
            with torch.no_grad():
                output = model.infer(seq)
                pdb = model.output_to_pdb(output)[0]
            
            plddt = output["plddt"].cpu().numpy()[0].mean()
            
            # 保存 PDB
            output_path = os.path.join(output_dir, f"{name}.pdb")
            with open(output_path, "w") as f:
                f.write(pdb)
            
            results.append({
                "name": name,
                "length": len(seq),
                "mean_plddt": round(float(plddt), 2),
                "output": output_path,
                "status": "success"
            })
            
        except Exception as e:
            print(f"  错误：{e}")
            results.append({"name": name, "status": f"error: {e}"})
    
    # 写入汇总
    import csv
    with open(os.path.join(output_dir, "summary.csv"), "w") as f:
        writer = csv.DictWriter(f, fieldnames=["name", "length", "mean_plddt", "output", "status"])
        writer.writeheader()
        writer.writerows(results)
    
    success = sum(1 for r in results if r.get("status") == "success")
    print(f"\n完成！{success}/{len(sequences)} 个结构预测成功")
    print(f"结果已保存到 {output_dir}/")

if __name__ == "__main__":
    predict_fasta(
        fasta_file="./proteins.fasta",
        output_dir="./predicted_structures",
        chunk_size=64
    )
```

***

## 结构可视化

### 使用 Py3Dmol（Jupyter / Python）

```python
import py3Dmol  # pip install py3Dmol

with open("protein.pdb") as f:
    pdb_data = f.read()

view = py3Dmol.view(width=800, height=600)
view.addModel(pdb_data, "pdb")
view.setStyle({"cartoon": {"colorscheme": "ssJmol"}})
view.zoomTo()
view.show()
```

### 使用 PyMOL

```bash
# 安装 PyMOL
apt-get install pymol

# 打开结构
pymol lysozyme.pdb
```

### 使用 Biotite 的编程化可视化

```python
import biotite.structure.io.pdb as pdb
import biotite.structure as struc
import numpy as np

# 加载预测结构
pdb_file = pdb.PDBFile.read("lysozyme.pdb")
structure = pdb.get_structure(pdb_file, model=1)

# 分析二级结构
sse = struc.annotate_sse(structure)

helix_frac = (sse == 'a').mean() * 100
sheet_frac = (sse == 'b').mean() * 100
coil_frac = (sse == 'c').mean() * 100

print(f"二级结构组成：")
print(f"  α 螺旋：  {helix_frac:.1f}%")
print(f"  β 折叠：   {sheet_frac:.1f}%")
print(f"  Coil/其他：   {coil_frac:.1f}%")
```

***

## 内存优化

### 分块大小指南

```python
# 更低的 chunk_size = 更少显存，预测更慢
# 更高的 chunk_size = 更多显存，预测更快

# 对于 8GB 显存（允许最多 ~400 aa）
model.set_chunk_size(32)

# 对于 16GB 显存（可达 ~700 aa）
model.set_chunk_size(64)

# 对于 40GB 显存（可达 ~2000 aa，无需分块）
model.set_chunk_size(None)  # 禁用分块
```

### 超长序列的 CPU 卸载

```python
# 在 CPU 上加载模型，每次推理时移动到 GPU
model = esm.pretrained.esmfold_v1()
model = model.eval()

# 在推理时移动到 GPU，之后移回 CPU
model = model.cuda()
with torch.no_grad():
    output = model.infer(sequence)
model = model.cpu()  # 释放 GPU 内存
torch.cuda.empty_cache()
```

***

## 故障排除

### CUDA 内存不足（Out of Memory）

```bash
# 减小分块大小
model.set_chunk_size(32)  # 或甚至 16

# 检查可用显存
nvidia-smi --query-gpu=memory.free --format=csv,noheader

# 对于超长蛋白，按结构域拆分
# 通常将 >1000 aa 的蛋白拆分为 300-500 aa 的结构域是安全的
```

### openfold 导入错误

```bash
# 使用特定提交重新安装
pip install "git+https://github.com/aqlaboratory/openfold.git@4b41059694619831a7db195b7e0988fc4ff3a307"

# 检查安装
python -c "import openfold; print('OpenFold OK')"
```

### 模型加载慢

```bash
# 第一次加载会下载 2.7GB 的模型权重 — 这是正常的
# 随后加载使用缓存的权重（约 30 秒加载时间）

# 检查缓存位置
python -c "import torch; print(torch.hub.get_dir())"
ls ~/.cache/torch/hub/
```

{% hint style="warning" %}
**内存说明：** ESMFold 的语言模型（ESM-2 15B 参数）需要大量显存。对于显存少于 16GB 的 GPU 服务器，请使用 `esm2_t33_650M_UR50D` 骨干变体或启用激进的分块。
{% endhint %}

{% hint style="info" %}
**pLDDT 解释：**

* **>90** = 非常高的置信度（AlphaFold 着色中的蓝色）
* **70–90** = 可信（青/浅蓝）
* **50–70** = 低置信度（黄色）— 请谨慎对待
* **<50** = 非常低置信度（橙/红）— 可能为无序区
  {% endhint %}

***

## Clore.ai 的 GPU 建议

ESMFold 的显存需求主要由 ESM-2 15B 参数语言模型决定。序列长度会增加额外的内存开销。

| GPU               | 显存（VRAM） | Clore.ai 价格 | 最大序列长度          | 预测时间（300 aa） |
| ----------------- | -------- | ----------- | --------------- | ------------ |
| RTX 3090          | 24 GB    | \~$0.12/小时  | \~400 aa（使用分块）  | 约 8 秒        |
| RTX 4090          | 24 GB    | \~$0.70/小时  | \~400 aa（使用分块）  | 约 5 秒        |
| A100 40GB         | 40 GB    | \~$1.20/小时  | \~800 aa 可舒适处理  | 约 3 秒        |
| 💡 本指南中的所有示例均可部署在 | 80 GB    | \~$2.00/小时  | \~1500+ aa，大型蛋白 | 约 4 秒        |

{% hint style="warning" %}
**最低显存：16GB。** 使用完整 ESM-2 骨干时，ESMFold 无法在 8GB GPU 上运行。RTX 3090/4090（24GB）可在不分块的情况下处理最多 \~400 个氨基酸的蛋白 —— 在 API 中启用 `chunk_size=64` 以处理更长序列。
{% endhint %}

**研究性价比最高：** RTX 3090 每小时约 $0.12，可处理绝大多数蛋白质结构预测任务（人类平均蛋白：约 300–400 aa）。在每次预测约 8 秒的情况下，你每小时可处理约 450 个结构，成本约 $0.12 —— 与需要进行每个结构数分钟 MSA 计算的 AlphaFold2 相比具有优势。

**高通量蛋白组学：** 对于筛选数千个序列，A100 40GB（约 $1.20/小时）结合批量推理每小时可处理 \~1200+ 次预测 —— 适用于蛋白组规模的研究。

***

## 资源

* 🐙 **GitHub：** [github.com/facebookresearch/esm](https://github.com/facebookresearch/esm)
* 🤗 **模型：** [huggingface.co/facebook/esmfold\_v1](https://huggingface.co/facebook/esmfold_v1)
* 📄 **论文：** [使用语言模型进行原子级蛋白质结构的进化尺度预测（Science, 2023）](https://www.science.org/doi/10.1126/science.ade2574)
* 🌐 **ESM 元基因组图谱：** [esmatlas.com](https://esmatlas.com) — 使用 ESMFold 预测了 7.72 亿 个结构
* 💻 **Meta AI 博客：** [ai.meta.com/blog/protein-folding-esmfold-metagenomics](https://ai.meta.com/blog/protein-folding-esmfold-metagenomics/)
* 🔬 **ESM 更新日志：** [github.com/facebookresearch/esm/blob/main/CHANGELOG.md](https://github.com/facebookresearch/esm/blob/main/CHANGELOG.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-zh/ke-xue-yu-yan-jiu/esmfold.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
