Project Overview
DeepFRI is a protein function prediction framework based on a GCN + LSTM language model, with the original implementation based on TensorFlow/Keras. This Skill documents the entire process of migrating it completely to PyTorch + Ascend NPU.
Model architecture:
- DeepCNN: 16-way parallel Conv1D → BatchNorm → ReLU → GlobalMaxPool → FuncPredictor
- DeepFRI GCN:LSTM LM → Embedding → 3×MultiGraphConv → SumPool → Dense → FuncPredictor
- LSTM language model: 2-layer CuDNNLSTM (hidden=512), outputs concatenated into 1024-dimensional features
Supported Ontologies: MF (Molecular Function), BP (Biological Process), CC (Cellular Component), EC (Enzyme Classification)
Prerequisites
| Item | Requirement |
|---|---|
| Hardware | Ascend910 series (at least 1 card) |
| CANN | ≥ 8.2 (8.3.RC1 recommended) |
| Python | 3.10 |
| PyTorch | 2.5.1 |
| torch_npu | 2.5.1 |
Migration Steps
Step 1: Clone repository & download model
cd /home/panjingxu
git clone https://github.com/flatironinstitute/DeepFRI.git
cd DeepFRI
# 下载 GPU 版预训练模型(README 精度参考值基于此版本)
wget https://users.flatironinstitute.org/~renfrew/DeepFRI_data/trained_models.tar.gz
tar xzf trained_models.tar.gz
Note: The official website provides two versions:
trained_models.tar.gz(GPU version): CNN + MultiGraphConv + CuDNNLSTM, the README accuracy reference values are based on thisnewest_trained_models.tar.gz(CPU version): CNN + GraphConv + standard LSTM, different structure, different number of goterms
Step 2: Create Conda environment
export PIP_INDEX_URL=https://repo.huaweicloud.com/repository/pypi/simple/
conda create -n deepfri_npu python=3.10 -y
conda activate deepfri_npu
# TF(仅用于权重提取,可选)
pip install tensorflow numpy==1.26.4 biopython scikit-learn
# PyTorch + NPU
pip install torch==2.5.1 torch_npu==2.5.1
pip install pyyaml decorator attrs psutil cloudpickle tornado
Step 3: Verify NPU availability
source /home/Ascend/ascend-toolkit/set_env.sh
python3 -c "import torch; import torch_npu; a = torch.randn(3,4).npu(); print(a+a)"
Step 4: Analyze TF model weight structure
Since newer versions of TF 2.x cannot load CuDNNLSTM, use h5py directly to read the HDF5 weights:
import h5py
f = h5py.File('trained_models/DeepFRI-MERGED_xxx.hdf5', 'r')
mw = f['model_weights']
for layer_name in mw.keys():
grp = mw[layer_name]
def print_ds(name, obj):
if isinstance(obj, h5py.Dataset):
print(f" {name}: {obj.shape}")
grp.visititems(print_ds)
f.close()
Step 5: Deploy PyTorch model files & convert weights
Copy the files under this Skill's scripts/ directory to the corresponding locations in the DeepFRI repository:
SKILL_DIR=/path/to/awesome-ascend-skills/skills/ai-for-science/models/deepfri/scripts
REPO_DIR=/path/to/DeepFRI # 替换为实际路径
# 复制 PyTorch 模型文件到 deepfrier 包内
cp $SKILL_DIR/torch_layers.py $REPO_DIR/deepfrier/
cp $SKILL_DIR/torch_model.py $REPO_DIR/deepfrier/
cp $SKILL_DIR/torch_predictor.py $REPO_DIR/deepfrier/
# 复制脚本到仓库根目录
cp $SKILL_DIR/convert_weights.py $REPO_DIR/
cp $SKILL_DIR/predict_npu.py $REPO_DIR/
cp $SKILL_DIR/verify_accuracy.py $REPO_DIR/
Then convert the weights:
cd $REPO_DIR
python convert_weights.py
# 生成 trained_models/pytorch/*.pt
scripts directory file list:
| File | Target location |
|---|---|
scripts/torch_layers.py |
deepfrier/torch_layers.py |
scripts/torch_model.py |
deepfrier/torch_model.py |
scripts/torch_predictor.py |
deepfrier/torch_predictor.py |
scripts/convert_weights.py |
convert_weights.py |
scripts/predict_npu.py |
predict_npu.py |
scripts/verify_accuracy.py |
verify_accuracy.py |
Step 6: NPU inference
source /home/Ascend/ascend-toolkit/set_env.sh
# CNN 序列输入(对应 README Option 2)
python predict_npu.py \
--seq 'SMTDLLSAEDIKKAIGAFTAADSFDHKKFFQMVGLKKKSADDVKKVFHILDKDKDGFIDEDELGSILKGFSSDARDLSAKETKTLMAAGDKDGDGKIGVEEFSTLVAES' \
-ont mf --verbose --device npu:0
# CNN fasta 输入(对应 README Option 3)
python predict_npu.py \
--fasta_fn examples/pdb_chains.fasta \
-ont mf --verbose --device npu:0
Step 7: Accuracy verification
python verify_accuracy.py
Key conversion rules
Dense / Linear
| TF | PyTorch | Convert |
|---|---|---|
kernel: (in, out) |
weight: (out, in) |
Transpose .T |
bias: (out,) |
bias: (out,) |
Copy directly |
Conv1D
| TF | PyTorch | Convert |
|---|---|---|
Input (batch, length, channels) |
Input (batch, channels, length) |
transpose before and after |
kernel: (K, Cin, Cout) |
weight: (Cout, Cin, K) |
np.transpose(w, (2,1,0)) |
bias: (Cout,) |
bias: (Cout,) |
Copy directly |
BatchNorm (⚠️ the most critical pitfall in this migration)
| TF | PyTorch | Convert |
|---|---|---|
gamma |
weight |
Copy directly |
beta |
bias |
Copy directly |
moving_mean |
running_mean |
Copy directly |
moving_variance |
running_var |
Copy directly |
| Default eps = 1e-3 | Default eps = 1e-5 | Must set eps=1e-3 |
⚠️ Fatal Bug: TF BatchNorm default
eps=1e-3, PyTorch defaulteps=1e-5, 100 times difference. Whenmoving_variancecontains values close to 0, the wrong eps causes division-by-zero explosion, softmax saturates to 0/1,model output is all 0. This is the most critical discovery in this CNN migration.
CuDNNLSTM → nn.LSTM
| TF CuDNNLSTM | PyTorch nn.LSTM | Convert |
|---|---|---|
kernel: (input, 4*H) |
weight_ih: (4*H, input) |
Transpose .T |
recurrent_kernel: (H, 4*H) |
weight_hh: (4*H, H) |
Transpose .T |
bias: (8*H,) |
bias_ih: (4*H,) + bias_hh: (4*H,) |
Split into first half + second half |
pt_weight_ih = tf_kernel.T
pt_weight_hh = tf_recurrent_kernel.T
pt_bias_ih = tf_bias[:4 * H] # 前半:input-hidden bias
pt_bias_hh = tf_bias[4 * H:] # 后半:hidden-hidden bias
Accuracy comparison results
CNN model vs official website README (NPU Ascend910)
| Test case | GO term | Official reference value | NPU output | diff | Status |
|---|---|---|---|---|---|
| Option 2: seq→mf (1S3P-A) | GO:0005509 calcium ion binding | 0.99769 | 0.99769 | 5e-6 | ✅ PASS |
| Option 3: fasta→mf (1S3P-A) | GO:0005509 calcium ion binding | 0.99769 | 0.99769 | 5e-6 | ✅ PASS |
| Option 3: fasta→mf (2J9H-A) | GO:0004364 glutathione transferase | 0.46937 | 0.46936 | 9e-6 | ✅ PASS |
| Option 3: fasta→mf (2J9H-A) | GO:0016765 transferase activity | 0.19910 | 0.19913 | 2.6e-5 | ✅ PASS |
CNN full Ontology validation
| Ontology | Status | Top prediction |
|---|---|---|
| MF | ✅ OK | GO:0005509 score=0.99769 (calcium ion binding) |
| BP | ✅ OK | GO:0051179 score=0.14491 (localization) |
| CC | ✅ OK | GO:0005829 score=0.23145 (cytosol) |
| EC | ✅ OK | No prediction exceeds the threshold (this protein is not an enzyme, as expected) |
GCN model description
The GPU version of the GCN model embeds a CuDNNLSTM language model. CuDNNLSTM uses GPU-specific weights.
When run on a standard LSTM (PyTorch nn.LSTM), the step-by-step floating-point differences are exponentially amplified through the 3-layer MultiGraphConv
graph convolution, causing the final output to saturate. If GCN functionality is needed, it is recommended to download the official
newest_trained_models.tar.gz(CPU version) and use the standard LSTM + GraphConv.
List of newly added files
| File | Lines | Description |
|---|---|---|
deepfrier/torch_layers.py |
199 | PyTorch version of graph convolution layers (MultiGraphConv, GraphConv, SAGEConv, NoGraphConv, ChebConv, SumPooling, FuncPredictor) |
deepfrier/torch_model.py |
124 | PyTorch version of models (LSTMLanguageModel, DeepFRIGCN, DeepCNN) |
deepfrier/torch_predictor.py |
178 | PyTorch version of the inference predictor (PredictorPyTorch, compatible with the original TF version interface) |
convert_weights.py |
147 | TF HDF5 → PyTorch state_dict weight conversion (LSTM/GCN/CNN) |
predict_npu.py |
46 | NPU inference entry script (including transfer_to_npu automatic migration injection) |
verify_accuracy.py |
72 | Accuracy comparison validation script (compares against the official README reference values) |
predict_npu.py (NPU entry script)
Inject at the top of the entry script transfer_to_npu, automatically mapping torch.cuda.* to torch.npu.*:
import torch_npu
from torch_npu.contrib import transfer_to_npu
from deepfrier.torch_predictor import PredictorPyTorch
convert_weights.py core logic
- Use
h5pyto read all Datasets in themodel_weightsgroup of the TF HDF5 model - CuDNNLSTM: transpose kernel/recurrent_kernel, split bias
(8*H,)into ih + hh - MultiGraphConv: directly copy kernel (both TF and PyTorch use
(in, out)matrix multiplication) - Dense: transpose kernel
.T - Conv1D:kernel
np.transpose(w, (2,1,0)) - BatchNorm: directly copy gamma/beta/moving_mean/moving_var
Key implementation of torch_layers.py
The _normalize method of MultiGraphConv corresponds to the three-way normalization in the TF version:
def _normalize(self, A, eps=1e-6):
# 去除自环
A = A.clone()
diag = torch.diagonal(A, dim1=-2, dim2=-1)
A = A - torch.diag_embed(diag)
A_hat = A + eye
deg = A_hat.sum(dim=2)
D_asymm = torch.diag_embed(1.0 / (eps + deg))
D_symm = torch.diag_embed(1.0 / (eps + deg.sqrt()))
# 返回三路:[原始A, 非对称归一化, 对称归一化]
return [A, D_asymm @ A_hat, D_symm @ A_hat @ D_symm]
The three results are each multiplied by the feature matrix, then concatenated, and then passed through a linear transformation and ELU activation.
Key adaptations of DeepCNN in torch_model.py
# BatchNorm eps 必须匹配 TF 默认值
self.bn = nn.BatchNorm1d(total_filters, eps=1e-3)
# Conv1D 输入需要 transpose
def forward(self, seq):
x = seq.transpose(1, 2) # (batch, length, ch) → (batch, ch, length)
conv_outs = [conv(x) for conv in self.conv_layers]
x = torch.cat(conv_outs, dim=1)
x = self.bn(x)
x = F.relu(x)
x = x.max(dim=2)[0] # GlobalMaxPool
return self.func_predictor(x)
Migration lessons learned
Pitfalls encountered (sorted by severity)
-
BatchNorm eps mismatch(most critical): TF default
1e-3, PyTorch default1e-5, causing CNN output to be all 0. Fix:nn.BatchNorm1d(dim, eps=1e-3) -
CuDNNLSTM platform limitation: the GPU version model embeds CuDNNLSTM, which cannot be loaded and validated with TF on aarch64 + NPU. Solution: use h5py to read weights directly + numpy to manually validate single-step
-
TF 2.x is incompatible with CuDNNLSTM: TF 2.21's
load_modeldoes not recognizeCuDNNLSTM. Solution: bypass model loading and extract weights directly from HDF5 -
Conv1D dimension order:TF
(batch, length, channels)vs PyTorch(batch, channels, length), transpose is required before and after
General lessons applicable to other projects
- When a TF model cannot be loaded, prefer to use
h5pyto directly read the HDF5 weight structure - All normalization layers (LayerNorm, BatchNorm) must have their default eps values checked
- CuDNNLSTM bias
(8*H,)Splitting rule: the first half = input bias, the second half = recurrent bias - After conversion, first compare the intermediate layer outputs (mean/std/min/max) to locate the first diverging layer
Known limitations
- The GCN model (cmap input) uses a CuDNNLSTM language model; these weights are GPU-specific, and a standard LSTM will exhibit numerical amplification in deep MultiGraphConv. If GCN functionality is needed, it is recommended to use the official CPU version of the model
- CNN
padding='same'PyTorch will issue a warning for even kernel sizes; this does not affect accuracy - Ascend910 does not support fp64; torch_npu automatically downgrades to fp32, which does not affect this model
References
- DeepFRI weight conversion and accuracy alignment checklist:
references/weight-conversion-checklist.md