Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +22 -10
- .idea/SaProt.iml +7 -0
- LICENSE +21 -0
- README.md +605 -0
- conf/ClinVar/esm2.yaml +36 -0
- conf/ClinVar/saprot.yaml +37 -0
- conf/Contact/esm2.yaml +67 -0
- conf/Contact/saprot.yaml +66 -0
- conf/DeepLoc/cls10/esm2.yaml +67 -0
- conf/DeepLoc/cls10/saprot.yaml +68 -0
- conf/DeepLoc/cls2/esm2.yaml +67 -0
- conf/DeepLoc/cls2/saprot.yaml +68 -0
- conf/EC/esm2.yaml +67 -0
- conf/EC/saprot.yaml +68 -0
- conf/GO/BP/esm2.yaml +67 -0
- conf/GO/BP/saprot.yaml +68 -0
- conf/GO/CC/esm2.yaml +67 -0
- conf/GO/CC/saprot.yaml +68 -0
- conf/GO/MF/esm2.yaml +67 -0
- conf/GO/MF/saprot.yaml +68 -0
- conf/HumanPPI/esm2.yaml +66 -0
- conf/HumanPPI/saprot.yaml +67 -0
- conf/MetalIonBinding/esm2.yaml +67 -0
- conf/MetalIonBinding/saprot.yaml +68 -0
- conf/ProteinGym/esm2.yaml +33 -0
- conf/ProteinGym/saprot.yaml +36 -0
- conf/Thermostability/esm2.yaml +69 -0
- conf/Thermostability/saprot.yaml +68 -0
- conf/pretrain/saprot.yaml +81 -0
- conf/scnet/ClinVar_saprot.yaml +37 -0
- conf/scnet/Thermostability_saprot_1gpu.yaml +68 -0
- config.json +102 -0
- model/README.md +9 -0
- model/abstract_model.py +282 -0
- model/model_interface.py +103 -0
- model/saprot/base.py +210 -0
- model/saprot/esm_mutation_model.py +288 -0
- model/saprot/saprot_annotation_model.py +89 -0
- model/saprot/saprot_classification_model.py +74 -0
- model/saprot/saprot_contact_model.py +145 -0
- model/saprot/saprot_foldseek_mutation_model.py +383 -0
- model/saprot/saprot_if_model.py +130 -0
- model/saprot/saprot_lm_model.py +74 -0
- model/saprot/saprot_ppi_model.py +78 -0
- model/saprot/saprot_regression_model.py +95 -0
- requirements.txt +16 -0
- scripts/LMDB/README.md +1 -0
- scripts/__init__.py +1 -0
- scripts/bin/README.md +1 -0
- scripts/compute_clinvar_auc.py +57 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,47 @@
|
|
| 1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 4 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bin.* filter=lfs diff=lfs merge=lfs -text
|
| 5 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 11 |
*.model filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
| 13 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 14 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 15 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
| 17 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 18 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 19 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 20 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 21 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 22 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 24 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 25 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
*.zstandard filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.db* filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.ark* filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
**/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
**/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
**/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*.gguf* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.ggml filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
*.llamafile* filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.pt2 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 46 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
.idea/SaProt.iml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<module version="4">
|
| 3 |
+
<component name="PyDocumentationSettings">
|
| 4 |
+
<option name="format" value="GOOGLE" />
|
| 5 |
+
<option name="myDocStringFormat" value="Google" />
|
| 6 |
+
</component>
|
| 7 |
+
</module>
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2023 westlake-repl
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
ADDED
|
@@ -0,0 +1,605 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
- zh
|
| 6 |
+
tags:
|
| 7 |
+
- OneScience
|
| 8 |
+
- life-science
|
| 9 |
+
- protein
|
| 10 |
+
- protein-language-model
|
| 11 |
+
- structure-aware
|
| 12 |
+
- mutation-effect
|
| 13 |
+
- embedding
|
| 14 |
+
- inverse-folding
|
| 15 |
+
- SaProt
|
| 16 |
+
frameworks: PyTorch
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
<p align="center">
|
| 20 |
+
<strong>
|
| 21 |
+
<span style="font-size: 30px;">SaProt</span>
|
| 22 |
+
</strong>
|
| 23 |
+
</p>
|
| 24 |
+
|
| 25 |
+
# Model Introduction
|
| 26 |
+
|
| 27 |
+
SaProt (Protein Language Modeling with Structure-aware Vocabulary) is a protein language model that jointly models protein amino acid sequences and structural information. Its central idea is to combine amino acids (AA) with the 3Di structural alphabet generated by Foldseek into structure-aware tokens, allowing the model to learn representations from both protein sequences and structural context.
|
| 28 |
+
|
| 29 |
+
SaProt can be used for protein representation extraction, zero-shot mutation effect prediction, protein inverse folding, and downstream task fine-tuning.
|
| 30 |
+
|
| 31 |
+
Paper:
|
| 32 |
+
|
| 33 |
+
> **SaProt: Protein Language Modeling with Structure-aware Vocabulary**
|
| 34 |
+
> ICLR 2024 Spotlight
|
| 35 |
+
> Follow-up work was published in Nature Biotechnology (2025)
|
| 36 |
+
|
| 37 |
+
# Model Description
|
| 38 |
+
|
| 39 |
+
SaProt models proteins using a structure-aware vocabulary formed by combining amino acids (AA) with the Foldseek 3Di structural alphabet.
|
| 40 |
+
|
| 41 |
+
For example, a structure-aware sequence can be represented as:
|
| 42 |
+
|
| 43 |
+
```text
|
| 44 |
+
M#EvVpQpL#VyQdYaKv
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
Every two characters form a structure-aware token: the first character represents the amino acid, and the second represents the corresponding 3Di structural state. `#` can be used to mask low-confidence structural regions.
|
| 48 |
+
|
| 49 |
+
The official release provides pretrained models at multiple scales:
|
| 50 |
+
|
| 51 |
+
| Model | Parameter scale | Training data |
|
| 52 |
+
| --- | ---: | --- |
|
| 53 |
+
| `SaProt_35M_AF2` | 35M | 40M AF2 structures |
|
| 54 |
+
| `SaProt_650M_PDB` | 650M | 40M AF2 structures + 60K PDB structures |
|
| 55 |
+
| `SaProt_650M_AF2` | 650M | 40M AF2 structures |
|
| 56 |
+
| `SaProt_1.3B_AF2` | 1.3B | 40M AF2 structures |
|
| 57 |
+
| `SaProt_1.3B_AFDB_OMG_NCBI` | 1.3B | AFDB + OMG_prot50 + NCBI |
|
| 58 |
+
|
| 59 |
+
For the 35M and 650M SaProt models, the official recommendation is to use SA-token inputs containing structural information for the best results. The 1.3B version can handle both structure-aware sequences and amino-acid-only sequences relatively well.
|
| 60 |
+
|
| 61 |
+
# Use Cases
|
| 62 |
+
|
| 63 |
+
| Use case | Description |
|
| 64 |
+
| --- | --- |
|
| 65 |
+
| Protein representation extraction | Extract residue-level or protein-level embeddings |
|
| 66 |
+
| Zero-shot mutation effect prediction | Evaluate single or multiple mutations directly without task-specific fine-tuning |
|
| 67 |
+
| Structure-aware protein modeling | Jointly use amino acid and 3Di structural tokens |
|
| 68 |
+
| Protein inverse folding | Design sequences from structural information |
|
| 69 |
+
| Downstream task fine-tuning | Apply to tasks such as EC, GO, stability, PPI, Contact, and DeepLoc |
|
| 70 |
+
|
| 71 |
+
# Usage
|
| 72 |
+
|
| 73 |
+
## 1. Using OneCode
|
| 74 |
+
|
| 75 |
+
Experience intelligent one-click AI4S programming in the OneCode online environment:
|
| 76 |
+
|
| 77 |
+
[Try intelligent one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
|
| 78 |
+
|
| 79 |
+
## 2. Manual Installation and Usage
|
| 80 |
+
|
| 81 |
+
**Hardware Requirements**
|
| 82 |
+
|
| 83 |
+
- SaProt supports inference on CPUs and GPUs/DCUs.
|
| 84 |
+
- The 35M model can be used for lightweight testing; GPUs/DCUs are recommended for the 650M and 1.3B models.
|
| 85 |
+
- Batch embedding, mutation scanning, pretraining, and fine-tuning substantially increase GPU memory and host memory requirements.
|
| 86 |
+
|
| 87 |
+
### Set Up the Runtime Environment
|
| 88 |
+
|
| 89 |
+
#### DCU Environment
|
| 90 |
+
|
| 91 |
+
```bash
|
| 92 |
+
# Activate DTK and CONDA first
|
| 93 |
+
conda create -n onescience311 python=3.11 -y
|
| 94 |
+
conda activate onescience311
|
| 95 |
+
|
| 96 |
+
pip install onescience[bio] \
|
| 97 |
+
-i http://mirrors.onescience.ai:3141/pypi/simple/ \
|
| 98 |
+
--trusted-host mirrors.onescience.ai
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
#### Environment Notes
|
| 102 |
+
|
| 103 |
+
- If you encounter missing dependencies or version incompatibilities during execution, refer to the dependency versions specified in `requirements.txt` and install or adjust the relevant dependencies as needed.
|
| 104 |
+
|
| 105 |
+
### Prepare Models and Data
|
| 106 |
+
|
| 107 |
+
#### 1) SaProt Model Weights
|
| 108 |
+
|
| 109 |
+
The official models are primarily released on Hugging Face:
|
| 110 |
+
|
| 111 |
+
```text
|
| 112 |
+
SaProt_35M_AF2
|
| 113 |
+
https://huggingface.co/westlake-repl/SaProt_35M_AF2
|
| 114 |
+
|
| 115 |
+
SaProt_650M_PDB
|
| 116 |
+
https://huggingface.co/westlake-repl/SaProt_650M_PDB
|
| 117 |
+
|
| 118 |
+
SaProt_650M_AF2
|
| 119 |
+
https://huggingface.co/westlake-repl/SaProt_650M_AF2
|
| 120 |
+
|
| 121 |
+
SaProt_1.3B_AF2
|
| 122 |
+
https://huggingface.co/westlake-repl/SaProt_1.3B_AF2
|
| 123 |
+
|
| 124 |
+
SaProt_1.3B_AFDB_OMG_NCBI
|
| 125 |
+
https://huggingface.co/westlake-repl/SaProt_1.3B_AFDB_OMG_NCBI
|
| 126 |
+
```
|
| 127 |
+
|
| 128 |
+
For example, download `SaProt_650M_AF2` in advance for offline use:
|
| 129 |
+
|
| 130 |
+
```bash
|
| 131 |
+
huggingface-cli download \
|
| 132 |
+
westlake-repl/SaProt_650M_AF2 \
|
| 133 |
+
--local-dir ./weight/PLMs/SaProt_650M_AF2
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
It is recommended to download all model weights under `weight/PLMs/`. The current SaProt configuration reads from:
|
| 137 |
+
|
| 138 |
+
```text
|
| 139 |
+
weight/PLMs/SaProt_650M_AF2
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
To run the ESM2 comparison experiment, also prepare:
|
| 143 |
+
|
| 144 |
+
```bash
|
| 145 |
+
huggingface-cli download \
|
| 146 |
+
facebook/esm2_t33_650M_UR50D \
|
| 147 |
+
--local-dir ./weight/PLMs/esm2_t33_650M_UR50D
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
The corresponding configuration reads from:
|
| 151 |
+
|
| 152 |
+
```text
|
| 153 |
+
weight/PLMs/esm2_t33_650M_UR50D
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
#### 2) Foldseek
|
| 157 |
+
|
| 158 |
+
SaProt structure-aware inputs require PDB/CIF structures to be encoded as Foldseek 3Di sequences first. The official README provides the following download link:
|
| 159 |
+
|
| 160 |
+
```text
|
| 161 |
+
https://drive.google.com/file/d/1B_9t3n_nlj8Y3Kpc_mMjtMdY0OPYa7Re/view
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
You can also use the official Foldseek Linux prebuilt package or a Foldseek installation already available on the system or platform.
|
| 165 |
+
|
| 166 |
+
For this adapted version, place Foldseek at:
|
| 167 |
+
|
| 168 |
+
```text
|
| 169 |
+
SaProt/
|
| 170 |
+
└── scripts/
|
| 171 |
+
└── bin/
|
| 172 |
+
└── foldseek
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
Then grant it execute permission:
|
| 176 |
+
|
| 177 |
+
```bash
|
| 178 |
+
chmod +x scripts/bin/foldseek
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
The Foldseek path used by the current configuration is:
|
| 182 |
+
|
| 183 |
+
```text
|
| 184 |
+
scripts/bin/foldseek
|
| 185 |
+
```
|
| 186 |
+
|
| 187 |
+
#### 3) Downstream Task Datasets
|
| 188 |
+
|
| 189 |
+
The official downstream task datasets are available at:
|
| 190 |
+
|
| 191 |
+
```text
|
| 192 |
+
https://drive.google.com/drive/folders/11dNGqPYfLE3M-Mbh4U7IQpuHxJpuRr4g?usp=sharing
|
| 193 |
+
```
|
| 194 |
+
|
| 195 |
+
For this adapted version, extract the downstream task data to:
|
| 196 |
+
|
| 197 |
+
```text
|
| 198 |
+
scripts/LMDB/
|
| 199 |
+
```
|
| 200 |
+
|
| 201 |
+
Typical paths used by the configuration include:
|
| 202 |
+
|
| 203 |
+
```text
|
| 204 |
+
scripts/LMDB/Thermostability/foldseek/train
|
| 205 |
+
scripts/LMDB/Thermostability/foldseek/valid
|
| 206 |
+
scripts/LMDB/Thermostability/foldseek/test
|
| 207 |
+
scripts/LMDB/ProteinGym/substitutions
|
| 208 |
+
scripts/LMDB/ClinVar
|
| 209 |
+
```
|
| 210 |
+
|
| 211 |
+
#### 4) Pretraining Dataset
|
| 212 |
+
|
| 213 |
+
To pretrain SaProt from scratch or continue pretraining, prepare the official pretraining data:
|
| 214 |
+
|
| 215 |
+
```text
|
| 216 |
+
westlake-repl/AF2_UniRef50
|
| 217 |
+
https://huggingface.co/datasets/westlake-repl/AF2_UniRef50
|
| 218 |
+
```
|
| 219 |
+
|
| 220 |
+
The official pretraining configuration uses LMDB data directories such as:
|
| 221 |
+
|
| 222 |
+
```text
|
| 223 |
+
scripts/LMDB/AF2_Uniref50/foldseek/train
|
| 224 |
+
scripts/LMDB/AF2_Uniref50/foldseek/valid
|
| 225 |
+
```
|
| 226 |
+
|
| 227 |
+
The pretraining dataset is large and is only needed when pretraining from scratch or continuing pretraining.
|
| 228 |
+
|
| 229 |
+
## 3. Quick Start
|
| 230 |
+
|
| 231 |
+
### Download the Model Package
|
| 232 |
+
|
| 233 |
+
```bash
|
| 234 |
+
hf download OneScience-Group/SaProt \
|
| 235 |
+
--local-dir ./SaProt
|
| 236 |
+
|
| 237 |
+
cd SaProt
|
| 238 |
+
```
|
| 239 |
+
|
| 240 |
+
### Quick Verification
|
| 241 |
+
|
| 242 |
+
Check the dependencies:
|
| 243 |
+
|
| 244 |
+
```bash
|
| 245 |
+
python - <<'PY'
|
| 246 |
+
import torch
|
| 247 |
+
import transformers
|
| 248 |
+
import esm
|
| 249 |
+
import pytorch_lightning as pl
|
| 250 |
+
|
| 251 |
+
print("torch:", torch.__version__)
|
| 252 |
+
print("transformers:", transformers.__version__)
|
| 253 |
+
print("pytorch_lightning:", pl.__version__)
|
| 254 |
+
print("SaProt dependencies OK")
|
| 255 |
+
PY
|
| 256 |
+
```
|
| 257 |
+
|
| 258 |
+
Check Foldseek:
|
| 259 |
+
|
| 260 |
+
```bash
|
| 261 |
+
./scripts/bin/foldseek version
|
| 262 |
+
```
|
| 263 |
+
|
| 264 |
+
Test model loading:
|
| 265 |
+
|
| 266 |
+
```bash
|
| 267 |
+
python - <<'PY'
|
| 268 |
+
from transformers import EsmTokenizer, EsmForMaskedLM
|
| 269 |
+
|
| 270 |
+
model_path = "./weight/PLMs/SaProt_650M_AF2"
|
| 271 |
+
|
| 272 |
+
tokenizer = EsmTokenizer.from_pretrained(model_path)
|
| 273 |
+
model = EsmForMaskedLM.from_pretrained(model_path)
|
| 274 |
+
|
| 275 |
+
print("SaProt load OK")
|
| 276 |
+
PY
|
| 277 |
+
```
|
| 278 |
+
|
| 279 |
+
# Example Data
|
| 280 |
+
|
| 281 |
+
The official repository provides:
|
| 282 |
+
|
| 283 |
+
```text
|
| 284 |
+
scripts/example/8ac8.cif
|
| 285 |
+
```
|
| 286 |
+
|
| 287 |
+
This can be used to demonstrate conversion from a protein structure to a structure-aware sequence.
|
| 288 |
+
|
| 289 |
+
Your own structure input can be:
|
| 290 |
+
|
| 291 |
+
```text
|
| 292 |
+
*.pdb
|
| 293 |
+
*.cif
|
| 294 |
+
```
|
| 295 |
+
|
| 296 |
+
If you already have a Foldseek-encoded structure-aware sequence, you can pass it directly to SaProt without processing the structure file again.
|
| 297 |
+
|
| 298 |
+
# Inference Examples
|
| 299 |
+
|
| 300 |
+
## Load SaProt for Forward Inference
|
| 301 |
+
|
| 302 |
+
```bash
|
| 303 |
+
python - <<'PY'
|
| 304 |
+
import torch
|
| 305 |
+
from transformers import EsmTokenizer, EsmForMaskedLM
|
| 306 |
+
|
| 307 |
+
model_path = "weight/PLMs/SaProt_650M_AF2"
|
| 308 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 309 |
+
|
| 310 |
+
tokenizer = EsmTokenizer.from_pretrained(model_path)
|
| 311 |
+
model = EsmForMaskedLM.from_pretrained(model_path)
|
| 312 |
+
model.to(device)
|
| 313 |
+
model.eval()
|
| 314 |
+
|
| 315 |
+
seq = "M#EvVpQpL#VyQdYaKv"
|
| 316 |
+
tokens = tokenizer.tokenize(seq)
|
| 317 |
+
print(tokens)
|
| 318 |
+
|
| 319 |
+
inputs = tokenizer(seq, return_tensors="pt")
|
| 320 |
+
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 321 |
+
|
| 322 |
+
with torch.no_grad():
|
| 323 |
+
outputs = model(**inputs)
|
| 324 |
+
|
| 325 |
+
print(outputs.logits.shape)
|
| 326 |
+
PY
|
| 327 |
+
```
|
| 328 |
+
|
| 329 |
+
## Load SaProt with the ESM Interface
|
| 330 |
+
|
| 331 |
+
If the model directory contains `SaProt_650M_AF2.pt`, you can use the ESM loading function provided by the project:
|
| 332 |
+
|
| 333 |
+
```bash
|
| 334 |
+
python - <<'PY'
|
| 335 |
+
from scripts.utils.esm_loader import load_esm_saprot
|
| 336 |
+
|
| 337 |
+
model_path = "weight/PLMs/SaProt_650M_AF2/SaProt_650M_AF2.pt"
|
| 338 |
+
model, alphabet = load_esm_saprot(model_path)
|
| 339 |
+
|
| 340 |
+
print("ESM SaProt load OK")
|
| 341 |
+
PY
|
| 342 |
+
```
|
| 343 |
+
|
| 344 |
+
## Convert a Structure File to a Structure-Aware Sequence
|
| 345 |
+
|
| 346 |
+
```bash
|
| 347 |
+
python - <<'PY'
|
| 348 |
+
from scripts.utils.foldseek_util import get_struc_seq
|
| 349 |
+
|
| 350 |
+
pdb_path = "scripts/example/8ac8.cif"
|
| 351 |
+
|
| 352 |
+
parsed_seqs = get_struc_seq("scripts/bin/foldseek", pdb_path, ["A"], plddt_mask=False)["A"]
|
| 353 |
+
seq, foldseek_seq, combined_seq = parsed_seqs
|
| 354 |
+
|
| 355 |
+
print(f"seq: {seq}")
|
| 356 |
+
print(f"foldseek_seq: {foldseek_seq}")
|
| 357 |
+
print(f"combined_seq: {combined_seq}")
|
| 358 |
+
PY
|
| 359 |
+
```
|
| 360 |
+
|
| 361 |
+
The `A` chain selection extracts only chain A from the structure file. The `combined_seq` in the returned result is a structure-aware sequence that can be used directly by SaProt.
|
| 362 |
+
|
| 363 |
+
## Mutation Effect Prediction
|
| 364 |
+
|
| 365 |
+
```bash
|
| 366 |
+
python - <<'PY'
|
| 367 |
+
import torch
|
| 368 |
+
from model.saprot.saprot_foldseek_mutation_model import SaprotFoldseekMutationModel
|
| 369 |
+
|
| 370 |
+
config = {
|
| 371 |
+
"foldseek_path": None,
|
| 372 |
+
"config_path": "weight/PLMs/SaProt_650M_AF2",
|
| 373 |
+
"load_pretrained": True,
|
| 374 |
+
}
|
| 375 |
+
model = SaprotFoldseekMutationModel(**config)
|
| 376 |
+
tokenizer = model.tokenizer
|
| 377 |
+
|
| 378 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 379 |
+
model.eval()
|
| 380 |
+
model.to(device)
|
| 381 |
+
|
| 382 |
+
seq = "M#EvVpQpL#VyQdYaKv"
|
| 383 |
+
|
| 384 |
+
mut_info = "V3A"
|
| 385 |
+
mut_value = model.predict_mut(seq, mut_info)
|
| 386 |
+
print(mut_value)
|
| 387 |
+
|
| 388 |
+
mut_info = "V3A:Q4M"
|
| 389 |
+
mut_value = model.predict_mut(seq, mut_info)
|
| 390 |
+
print(mut_value)
|
| 391 |
+
|
| 392 |
+
mut_pos = 3
|
| 393 |
+
mut_dict = model.predict_pos_mut(seq, mut_pos)
|
| 394 |
+
print(mut_dict)
|
| 395 |
+
|
| 396 |
+
mut_pos = 3
|
| 397 |
+
mut_dict = model.predict_pos_prob(seq, mut_pos)
|
| 398 |
+
print(mut_dict)
|
| 399 |
+
PY
|
| 400 |
+
```
|
| 401 |
+
|
| 402 |
+
## Extract Protein Embeddings
|
| 403 |
+
|
| 404 |
+
```bash
|
| 405 |
+
python - <<'PY'
|
| 406 |
+
import torch
|
| 407 |
+
from model.saprot.base import SaprotBaseModel
|
| 408 |
+
from transformers import EsmTokenizer
|
| 409 |
+
|
| 410 |
+
config = {
|
| 411 |
+
"task": "base",
|
| 412 |
+
"config_path": "weight/PLMs/SaProt_650M_AF2",
|
| 413 |
+
"load_pretrained": True,
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
model = SaprotBaseModel(**config)
|
| 417 |
+
tokenizer = EsmTokenizer.from_pretrained(config["config_path"])
|
| 418 |
+
|
| 419 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 420 |
+
model.to(device)
|
| 421 |
+
model.eval()
|
| 422 |
+
|
| 423 |
+
seq = "M#EvVpQpL#VyQdYaKv"
|
| 424 |
+
tokens = tokenizer.tokenize(seq)
|
| 425 |
+
print(tokens)
|
| 426 |
+
|
| 427 |
+
inputs = tokenizer(seq, return_tensors="pt")
|
| 428 |
+
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 429 |
+
|
| 430 |
+
with torch.no_grad():
|
| 431 |
+
embeddings = model.get_hidden_states(inputs, reduction="mean")
|
| 432 |
+
|
| 433 |
+
print(embeddings[0].shape)
|
| 434 |
+
PY
|
| 435 |
+
```
|
| 436 |
+
|
| 437 |
+
## Protein Inverse Folding
|
| 438 |
+
|
| 439 |
+
Inverse folding requires additional inverse folding model weights:
|
| 440 |
+
|
| 441 |
+
```text
|
| 442 |
+
https://huggingface.co/westlake-repl/SaProt_650M_AF2_inverse_folding
|
| 443 |
+
```
|
| 444 |
+
|
| 445 |
+
After downloading, place them at:
|
| 446 |
+
|
| 447 |
+
```text
|
| 448 |
+
weight/PLMs/SaProt_650M_AF2_inverse_folding
|
| 449 |
+
```
|
| 450 |
+
|
| 451 |
+
Example:
|
| 452 |
+
|
| 453 |
+
```bash
|
| 454 |
+
python - <<'PY'
|
| 455 |
+
import torch
|
| 456 |
+
from model.saprot.saprot_if_model import SaProtIFModel
|
| 457 |
+
|
| 458 |
+
config = {
|
| 459 |
+
"config_path": "weight/PLMs/SaProt_650M_AF2_inverse_folding",
|
| 460 |
+
"load_pretrained": True,
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 464 |
+
model = SaProtIFModel(**config)
|
| 465 |
+
model = model.to(device)
|
| 466 |
+
|
| 467 |
+
aa_seq = "##########"
|
| 468 |
+
struc_seq = "dddddddddd"
|
| 469 |
+
|
| 470 |
+
pred_aa_seq = model.predict(aa_seq, struc_seq)
|
| 471 |
+
print(pred_aa_seq)
|
| 472 |
+
PY
|
| 473 |
+
```
|
| 474 |
+
|
| 475 |
+
# Training
|
| 476 |
+
|
| 477 |
+
This repository uses a unified training entry point:
|
| 478 |
+
|
| 479 |
+
```bash
|
| 480 |
+
python scripts/training.py -c <config_path>
|
| 481 |
+
```
|
| 482 |
+
|
| 483 |
+
Configuration files are located in `conf/`, model code is located in `model/`, and data processing and utility code is located in `scripts/`. The current configuration uses the pretrained model weights in `weight/PLMs/SaProt_650M_AF2` by default.
|
| 484 |
+
|
| 485 |
+
## Pretraining
|
| 486 |
+
|
| 487 |
+
To pretrain SaProt from scratch or continue pretraining, first prepare the pretraining LMDB dataset, then run:
|
| 488 |
+
|
| 489 |
+
```bash
|
| 490 |
+
python scripts/training.py -c conf/pretrain/saprot.yaml
|
| 491 |
+
```
|
| 492 |
+
|
| 493 |
+
This configuration reads from the following paths by default:
|
| 494 |
+
|
| 495 |
+
```text
|
| 496 |
+
scripts/LMDB/AF2_Uniref50/foldseek/train
|
| 497 |
+
scripts/LMDB/AF2_Uniref50/foldseek/valid
|
| 498 |
+
```
|
| 499 |
+
|
| 500 |
+
## Downstream Fine-Tuning
|
| 501 |
+
|
| 502 |
+
Use the following commands to fine-tune SaProt on downstream tasks:
|
| 503 |
+
|
| 504 |
+
```bash
|
| 505 |
+
# Thermostability
|
| 506 |
+
python scripts/training.py -c conf/Thermostability/saprot.yaml
|
| 507 |
+
|
| 508 |
+
# EC
|
| 509 |
+
python scripts/training.py -c conf/EC/saprot.yaml
|
| 510 |
+
|
| 511 |
+
# GO
|
| 512 |
+
python scripts/training.py -c conf/GO/MF/saprot.yaml
|
| 513 |
+
python scripts/training.py -c conf/GO/BP/saprot.yaml
|
| 514 |
+
python scripts/training.py -c conf/GO/CC/saprot.yaml
|
| 515 |
+
|
| 516 |
+
# Metal ion binding
|
| 517 |
+
python scripts/training.py -c conf/MetalIonBinding/saprot.yaml
|
| 518 |
+
|
| 519 |
+
# Human PPI
|
| 520 |
+
python scripts/training.py -c conf/HumanPPI/saprot.yaml
|
| 521 |
+
|
| 522 |
+
# Contact prediction
|
| 523 |
+
python scripts/training.py -c conf/Contact/saprot.yaml
|
| 524 |
+
|
| 525 |
+
# DeepLoc
|
| 526 |
+
python scripts/training.py -c conf/DeepLoc/cls2/saprot.yaml
|
| 527 |
+
python scripts/training.py -c conf/DeepLoc/cls10/saprot.yaml
|
| 528 |
+
```
|
| 529 |
+
|
| 530 |
+
For single-GPU or limited-memory environments, use `conf/scnet/Thermostability_saprot_1gpu.yaml` as a starting point:
|
| 531 |
+
|
| 532 |
+
```bash
|
| 533 |
+
python scripts/training.py -c conf/scnet/Thermostability_saprot_1gpu.yaml
|
| 534 |
+
```
|
| 535 |
+
|
| 536 |
+
## Zero-Shot Mutation Effect Evaluation
|
| 537 |
+
|
| 538 |
+
ProteinGym evaluation:
|
| 539 |
+
|
| 540 |
+
```bash
|
| 541 |
+
python scripts/mutation_zeroshot.py -c conf/ProteinGym/saprot.yaml
|
| 542 |
+
```
|
| 543 |
+
|
| 544 |
+
The output file is saved by default to:
|
| 545 |
+
|
| 546 |
+
```text
|
| 547 |
+
output/ProteinGym/SaProt_650M_AF2.tsv
|
| 548 |
+
```
|
| 549 |
+
|
| 550 |
+
ClinVar evaluation:
|
| 551 |
+
|
| 552 |
+
```bash
|
| 553 |
+
python scripts/mutation_zeroshot.py -c conf/ClinVar/saprot.yaml
|
| 554 |
+
python scripts/compute_clinvar_auc.py -c conf/ClinVar/saprot.yaml
|
| 555 |
+
```
|
| 556 |
+
|
| 557 |
+
ClinVar prediction results are saved by default to:
|
| 558 |
+
|
| 559 |
+
```text
|
| 560 |
+
output/ClinVar/SaProt_650M_AF2
|
| 561 |
+
```
|
| 562 |
+
|
| 563 |
+
Single-GPU environments can also use the adapted configuration:
|
| 564 |
+
|
| 565 |
+
```bash
|
| 566 |
+
python scripts/mutation_zeroshot.py -c conf/scnet/ClinVar_saprot.yaml
|
| 567 |
+
python scripts/compute_clinvar_auc.py -c conf/scnet/ClinVar_saprot.yaml
|
| 568 |
+
```
|
| 569 |
+
|
| 570 |
+
## ESM2 Comparison Experiment
|
| 571 |
+
|
| 572 |
+
To run the ESM2 baseline, additionally prepare the weights in `weight/PLMs/esm2_t33_650M_UR50D` and the corresponding normal LMDB data. Example commands:
|
| 573 |
+
|
| 574 |
+
```bash
|
| 575 |
+
python scripts/training.py -c conf/Thermostability/esm2.yaml
|
| 576 |
+
python scripts/mutation_zeroshot.py -c conf/ProteinGym/esm2.yaml
|
| 577 |
+
```
|
| 578 |
+
|
| 579 |
+
# Output Description
|
| 580 |
+
|
| 581 |
+
| Task | Main output |
|
| 582 |
+
| --- | --- |
|
| 583 |
+
| Structure encoding | AA sequence, 3Di sequence, and structure-aware sequence |
|
| 584 |
+
| Model forward pass | Token-level logits |
|
| 585 |
+
| Protein representation | Residue-level/protein-level embeddings |
|
| 586 |
+
| Mutation effect prediction | Mutation score |
|
| 587 |
+
| Zero-shot evaluation | ProteinGym Spearman results or ClinVar AUC results |
|
| 588 |
+
| Inverse folding | Protein sequences generated or evaluated under structural conditions |
|
| 589 |
+
| Downstream fine-tuning | Prediction results and model checkpoint for the corresponding task |
|
| 590 |
+
|
| 591 |
+
# Official OneScience Information
|
| 592 |
+
|
| 593 |
+
| Platform | Main OneScience repository | Skills repository |
|
| 594 |
+
| --- | --- | --- |
|
| 595 |
+
| Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
|
| 596 |
+
| GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
|
| 597 |
+
|
| 598 |
+
# Citation and License
|
| 599 |
+
|
| 600 |
+
- The official SaProt source repository is released under the **MIT License**, which permits use, modification, distribution, sublicensing, and commercial use. When copying or distributing it, retain the original copyright notice and the MIT License text.
|
| 601 |
+
|
| 602 |
+
- SaProt model weights are released independently through Hugging Face. For commercial use, redistribution, or other purposes, check and comply with the license on each corresponding model page. The relevant pretraining and downstream datasets are also subject to the licenses and terms of use on their respective dataset pages.
|
| 603 |
+
|
| 604 |
+
- This repository is a **DCU-adapted version** of SaProt. Use of the repository code, model weights, and related data remains subject to the licenses and terms of use of their respective original projects.
|
| 605 |
+
|
conf/ClinVar/esm2.yaml
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
CUDA_VISIBLE_DEVICES: 1,2,3,4,5,6,7
|
| 5 |
+
MASTER_ADDR: 127.0.0.1
|
| 6 |
+
MASTER_PORT: 12316
|
| 7 |
+
WORLD_SIZE: 1
|
| 8 |
+
NODE_RANK: 0
|
| 9 |
+
|
| 10 |
+
out_path: ~
|
| 11 |
+
dataset_dir: scripts/LMDB/ClinVar
|
| 12 |
+
|
| 13 |
+
model:
|
| 14 |
+
model_py_path: saprot/saprot_mutation_model
|
| 15 |
+
load_prev_scheduler: True
|
| 16 |
+
kwargs:
|
| 17 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 18 |
+
load_pretrained: True
|
| 19 |
+
log_clinvar: True
|
| 20 |
+
log_dir: output/ClinVar/esm2_t33_650M_UR50D
|
| 21 |
+
|
| 22 |
+
dataset:
|
| 23 |
+
dataset_py_path: mutation_zeroshot_dataset
|
| 24 |
+
dataloader_kwargs:
|
| 25 |
+
batch_size: 16
|
| 26 |
+
num_workers: 8
|
| 27 |
+
kwargs: {}
|
| 28 |
+
|
| 29 |
+
Trainer:
|
| 30 |
+
log_every_n_steps: 1
|
| 31 |
+
devices: 1
|
| 32 |
+
accelerator: gpu
|
| 33 |
+
num_nodes: 1
|
| 34 |
+
strategy:
|
| 35 |
+
find_unused_parameters: False
|
| 36 |
+
logger: False
|
conf/ClinVar/saprot.yaml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
CUDA_VISIBLE_DEVICES: 5
|
| 5 |
+
MASTER_ADDR: 127.0.0.1
|
| 6 |
+
MASTER_PORT: 12315
|
| 7 |
+
WORLD_SIZE: 1
|
| 8 |
+
NODE_RANK: 0
|
| 9 |
+
|
| 10 |
+
out_path: ~
|
| 11 |
+
dataset_dir: scripts/LMDB/ClinVar
|
| 12 |
+
|
| 13 |
+
model:
|
| 14 |
+
model_py_path: saprot/saprot_foldseek_mutation_model
|
| 15 |
+
kwargs:
|
| 16 |
+
foldseek_path: scripts/bin/foldseek
|
| 17 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 18 |
+
load_pretrained: True
|
| 19 |
+
log_clinvar: True
|
| 20 |
+
log_dir: output/ClinVar/SaProt_650M_AF2
|
| 21 |
+
plddt_threshold: 70.
|
| 22 |
+
|
| 23 |
+
dataset:
|
| 24 |
+
dataset_py_path: mutation_zeroshot_dataset
|
| 25 |
+
dataloader_kwargs:
|
| 26 |
+
batch_size: 16
|
| 27 |
+
num_workers: 8
|
| 28 |
+
kwargs: {}
|
| 29 |
+
|
| 30 |
+
Trainer:
|
| 31 |
+
log_every_n_steps: 1
|
| 32 |
+
devices: 1
|
| 33 |
+
accelerator: gpu
|
| 34 |
+
num_nodes: 1
|
| 35 |
+
strategy:
|
| 36 |
+
find_unused_parameters: False
|
| 37 |
+
logger: False
|
conf/Contact/esm2.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
use_wandb: true
|
| 4 |
+
os_environ:
|
| 5 |
+
WANDB_API_KEY: ~
|
| 6 |
+
WANDB_RUN_ID: ~
|
| 7 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 8 |
+
MASTER_ADDR: 127.0.0.1
|
| 9 |
+
MASTER_PORT: 12316
|
| 10 |
+
WORLD_SIZE: 1
|
| 11 |
+
NODE_RANK: 0
|
| 12 |
+
wandb_config:
|
| 13 |
+
project: TAPE_Contact
|
| 14 |
+
name: esm2_t33_650M_UR50D
|
| 15 |
+
|
| 16 |
+
model:
|
| 17 |
+
# Which model to use
|
| 18 |
+
model_py_path: saprot/saprot_contact_model
|
| 19 |
+
kwargs:
|
| 20 |
+
# Arguments to initialize the specific class
|
| 21 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 22 |
+
load_pretrained: True
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 1.0e-2
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/Contact/esm2_t33_650M_UR50D.pt
|
| 36 |
+
load_prev_scheduler: false
|
| 37 |
+
save_weights_only: true
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
dataset:
|
| 41 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 42 |
+
dataset_py_path: saprot/saprot_contact_dataset
|
| 43 |
+
dataloader_kwargs:
|
| 44 |
+
batch_size: 2
|
| 45 |
+
num_workers: 8
|
| 46 |
+
train_lmdb: scripts/LMDB/Contact/normal/train
|
| 47 |
+
valid_lmdb: scripts/LMDB/Contact/normal/valid
|
| 48 |
+
test_lmdb: scripts/LMDB/Contact/normal/test
|
| 49 |
+
# Arguments to initialize the specific class
|
| 50 |
+
kwargs:
|
| 51 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 30
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: False
|
| 59 |
+
logger: False
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.1
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 1
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 1
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/Contact/saprot.yaml
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: 127.0.0.1
|
| 8 |
+
MASTER_PORT: 12316
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: TAPE_Contact
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_contact_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
|
| 23 |
+
# Arguments to initialize the basic class AbstractModel
|
| 24 |
+
lr_scheduler_kwargs:
|
| 25 |
+
last_epoch: -1
|
| 26 |
+
init_lr: 1.0e-2
|
| 27 |
+
# Weather to use this scheduler or not
|
| 28 |
+
on_use: false
|
| 29 |
+
|
| 30 |
+
optimizer_kwargs:
|
| 31 |
+
betas: [0.9, 0.98]
|
| 32 |
+
weight_decay: 0.01
|
| 33 |
+
|
| 34 |
+
save_path: weight/Contact/SaProt_650M_AF2.pt
|
| 35 |
+
load_prev_scheduler: false
|
| 36 |
+
save_weights_only: true
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
dataset:
|
| 40 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 41 |
+
dataset_py_path: saprot/saprot_contact_dataset
|
| 42 |
+
dataloader_kwargs:
|
| 43 |
+
batch_size: 2
|
| 44 |
+
num_workers: 8
|
| 45 |
+
train_lmdb: scripts/LMDB/Contact/foldseek/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/Contact/foldseek/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/Contact/foldseek/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 51 |
+
|
| 52 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 53 |
+
Trainer:
|
| 54 |
+
max_epochs: 30
|
| 55 |
+
log_every_n_steps: 1
|
| 56 |
+
strategy:
|
| 57 |
+
find_unused_parameters: False
|
| 58 |
+
logger: False
|
| 59 |
+
enable_checkpointing: false
|
| 60 |
+
val_check_interval: 0.1
|
| 61 |
+
accelerator: gpu
|
| 62 |
+
devices: 1
|
| 63 |
+
num_nodes: 1
|
| 64 |
+
accumulate_grad_batches: 1
|
| 65 |
+
precision: 16
|
| 66 |
+
num_sanity_val_steps: 0
|
conf/DeepLoc/cls10/esm2.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: DeepLoc_cls10
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_classification_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
num_labels: 10
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/DeepLoc/cls10/esm2_t33_650M_UR50D.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_classification_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 8
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/DeepLoc/cls10/normal/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/DeepLoc/cls10/normal/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/DeepLoc/cls10/normal/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 100
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.5
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 8
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 1
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/DeepLoc/cls10/saprot.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: DeepLoc_cls10
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_classification_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
num_labels: 10
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/DeepLoc/cls10/SaProt_650M_AF2.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_classification_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 8
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/DeepLoc/cls10/foldseek/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/DeepLoc/cls10/foldseek/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/DeepLoc/cls10/foldseek/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 51 |
+
plddt_threshold: 70.
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 100
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: True
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.5
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 8
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 1
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
conf/DeepLoc/cls2/esm2.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: DeepLoc_cls2
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_classification_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
num_labels: 2
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/DeepLoc/cls2/esm2_t33_650M_UR50D.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_classification_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 8
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/DeepLoc/cls2/normal/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/DeepLoc/cls2/normal/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/DeepLoc/cls2/normal/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 100
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.5
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 8
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 1
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/DeepLoc/cls2/saprot.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: DeepLoc_cls2
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_classification_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
num_labels: 2
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/DeepLoc/cls2/SaProt_650M_AF2.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_classification_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 8
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/DeepLoc/cls2/foldseek/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/DeepLoc/cls2/foldseek/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/DeepLoc/cls2/foldseek/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 51 |
+
plddt_threshold: 70
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 100
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: True
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.5
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 8
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 1
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
conf/EC/esm2.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: EC
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_annotation_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
anno_type: EC
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/EC/esm2_t33_650M_UR50D.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_annotation_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 8
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/EC/AF2/normal/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/EC/AF2/normal/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/EC/AF2/normal/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 100
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.1
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 8
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 1
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/EC/saprot.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: EC
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_annotation_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
anno_type: EC
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/EC/SaProt_650M_AF2.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_annotation_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 8
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/EC/AF2/foldseek/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/EC/AF2/foldseek/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/EC/AF2/foldseek/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 51 |
+
plddt_threshold: 70
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 100
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: True
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.1
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 8
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 1
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
conf/GO/BP/esm2.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: GO_BP
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_annotation_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
anno_type: GO_BP
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/GO/BP/esm2_t33_650M_UR50D.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_annotation_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 4
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/GO/AF2/BP/normal/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/GO/AF2/BP/normal/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/GO/AF2/BP/normal/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 100
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.5
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 8
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 2
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/GO/BP/saprot.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: GO_BP
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_annotation_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
anno_type: GO_BP
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/GO/BP/SaProt_650M_AF2.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_annotation_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 4
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/GO/AF2/BP/foldseek/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/GO/AF2/BP/foldseek/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/GO/AF2/BP/foldseek/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 51 |
+
plddt_threshold: 70
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 100
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: True
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.5
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 8
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 2
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
conf/GO/CC/esm2.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: GO_CC
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_annotation_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
anno_type: GO_CC
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/GO/CC/esm2_t33_650M_UR50D.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_annotation_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 4
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/GO/AF2/CC/normal/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/GO/AF2/CC/normal/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/GO/AF2/CC/normal/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 100
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.5
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 8
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 2
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/GO/CC/saprot.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: GO_CC
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_annotation_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
anno_type: GO_CC
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/GO/CC/SaProt_650M_AF2.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_annotation_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 4
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/GO/AF2/CC/foldseek/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/GO/AF2/CC/foldseek/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/GO/AF2/CC/foldseek/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 51 |
+
plddt_threshold: 70
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 100
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: True
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.5
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 8
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 2
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
conf/GO/MF/esm2.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: GO_MF
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_annotation_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
anno_type: GO_MF
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/GO/MF/esm2_t33_650M_UR50D.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_annotation_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 4
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/GO/AF2/MF/normal/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/GO/AF2/MF/normal/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/GO/AF2/MF/normal/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 100
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.5
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 8
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 2
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/GO/MF/saprot.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: GO_MF
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_annotation_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
anno_type: GO_MF
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/GO/MF/SaProt_650M_AF2.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_annotation_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 4
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/GO/AF2/MF/foldseek/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/GO/AF2/MF/foldseek/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/GO/AF2/MF/foldseek/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 51 |
+
plddt_threshold: 70
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 100
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: True
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.5
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 8
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 2
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
conf/HumanPPI/esm2.yaml
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: HumanPPI
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_ppi_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
|
| 23 |
+
# Arguments to initialize the basic class AbstractModel
|
| 24 |
+
lr_scheduler_kwargs:
|
| 25 |
+
last_epoch: -1
|
| 26 |
+
init_lr: 2.0e-5
|
| 27 |
+
# Weather to use this scheduler or not
|
| 28 |
+
on_use: false
|
| 29 |
+
|
| 30 |
+
optimizer_kwargs:
|
| 31 |
+
betas: [0.9, 0.98]
|
| 32 |
+
weight_decay: 0.01
|
| 33 |
+
|
| 34 |
+
save_path: weight/HumanPPI/esm2_t33_650M_UR50D.pt
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
dataset:
|
| 38 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 39 |
+
dataset_py_path: saprot/saprot_ppi_dataset
|
| 40 |
+
dataloader_kwargs:
|
| 41 |
+
batch_size: 4
|
| 42 |
+
num_workers: 8
|
| 43 |
+
|
| 44 |
+
train_lmdb: scripts/LMDB/HumanPPI/normal/train
|
| 45 |
+
valid_lmdb: scripts/LMDB/HumanPPI/normal/valid
|
| 46 |
+
test_lmdb: scripts/LMDB/HumanPPI/normal/test
|
| 47 |
+
# Arguments to initialize the specific class
|
| 48 |
+
kwargs:
|
| 49 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 53 |
+
Trainer:
|
| 54 |
+
max_epochs: 20
|
| 55 |
+
log_every_n_steps: 1
|
| 56 |
+
strategy:
|
| 57 |
+
find_unused_parameters: True
|
| 58 |
+
logger: True
|
| 59 |
+
enable_checkpointing: false
|
| 60 |
+
val_check_interval: 0.1
|
| 61 |
+
accelerator: gpu
|
| 62 |
+
devices: 8
|
| 63 |
+
num_nodes: 1
|
| 64 |
+
accumulate_grad_batches: 2
|
| 65 |
+
precision: 16
|
| 66 |
+
num_sanity_val_steps: 0
|
conf/HumanPPI/saprot.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: HumanPPI
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_ppi_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
|
| 23 |
+
# Arguments to initialize the basic class AbstractModel
|
| 24 |
+
lr_scheduler_kwargs:
|
| 25 |
+
last_epoch: -1
|
| 26 |
+
init_lr: 2.0e-5
|
| 27 |
+
# Weather to use this scheduler or not
|
| 28 |
+
on_use: false
|
| 29 |
+
|
| 30 |
+
optimizer_kwargs:
|
| 31 |
+
betas: [0.9, 0.98]
|
| 32 |
+
weight_decay: 0.01
|
| 33 |
+
|
| 34 |
+
save_path: weight/HumanPPI/SaProt_650M_AF2.pt
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
dataset:
|
| 38 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 39 |
+
dataset_py_path: saprot/saprot_ppi_dataset
|
| 40 |
+
dataloader_kwargs:
|
| 41 |
+
batch_size: 4
|
| 42 |
+
num_workers: 8
|
| 43 |
+
|
| 44 |
+
train_lmdb: scripts/LMDB/HumanPPI/foldseek/train
|
| 45 |
+
valid_lmdb: scripts/LMDB/HumanPPI/foldseek/valid
|
| 46 |
+
test_lmdb: scripts/LMDB/HumanPPI/foldseek/test
|
| 47 |
+
# Arguments to initialize the specific class
|
| 48 |
+
kwargs:
|
| 49 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 50 |
+
plddt_threshold: 70
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 20
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.1
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 8
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 2
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/MetalIonBinding/esm2.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: MetalIonBinding
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_classification_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
num_labels: 2
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/MetalIonBinding/esm2_t33_650M_UR50D.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_classification_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 8
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/MetalIonBinding/AF2/normal/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/MetalIonBinding/AF2/normal/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/MetalIonBinding/AF2/normal/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 200
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.5
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 4
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 2
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
conf/MetalIonBinding/saprot.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: MetalIonBinding
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_classification_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
num_labels: 2
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 2.0e-5
|
| 28 |
+
# Weather to use this scheduler or not
|
| 29 |
+
on_use: false
|
| 30 |
+
|
| 31 |
+
optimizer_kwargs:
|
| 32 |
+
betas: [0.9, 0.98]
|
| 33 |
+
weight_decay: 0.01
|
| 34 |
+
|
| 35 |
+
save_path: weight/MetalIonBinding/SaProt_650M_AF2.pt
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
dataset:
|
| 39 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 40 |
+
dataset_py_path: saprot/saprot_classification_dataset
|
| 41 |
+
dataloader_kwargs:
|
| 42 |
+
batch_size: 8
|
| 43 |
+
num_workers: 8
|
| 44 |
+
|
| 45 |
+
train_lmdb: scripts/LMDB/MetalIonBinding/AF2/foldseek/train
|
| 46 |
+
valid_lmdb: scripts/LMDB/MetalIonBinding/AF2/foldseek/valid
|
| 47 |
+
test_lmdb: scripts/LMDB/MetalIonBinding/AF2/foldseek/test
|
| 48 |
+
# Arguments to initialize the specific class
|
| 49 |
+
kwargs:
|
| 50 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 51 |
+
plddt_threshold: 70
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 200
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: True
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.5
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 4
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 2
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
conf/ProteinGym/esm2.yaml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 5 |
+
MASTER_ADDR: 127.0.0.1
|
| 6 |
+
MASTER_PORT: 12315
|
| 7 |
+
WORLD_SIZE: 1
|
| 8 |
+
NODE_RANK: 0
|
| 9 |
+
|
| 10 |
+
out_path: output/ProteinGym/esm2_t33_650M_UR50D.tsv
|
| 11 |
+
dataset_dir: scripts/LMDB/ProteinGym/substitutions
|
| 12 |
+
|
| 13 |
+
model:
|
| 14 |
+
model_py_path: saprot/esm_mutation_model
|
| 15 |
+
kwargs:
|
| 16 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 17 |
+
load_pretrained: True
|
| 18 |
+
|
| 19 |
+
dataset:
|
| 20 |
+
dataset_py_path: mutation_zeroshot_dataset
|
| 21 |
+
dataloader_kwargs:
|
| 22 |
+
batch_size: 32
|
| 23 |
+
num_workers: 8
|
| 24 |
+
kwargs: {}
|
| 25 |
+
|
| 26 |
+
Trainer:
|
| 27 |
+
log_every_n_steps: 1
|
| 28 |
+
devices: 1
|
| 29 |
+
accelerator: gpu
|
| 30 |
+
num_nodes: 1
|
| 31 |
+
strategy:
|
| 32 |
+
find_unused_parameters: False
|
| 33 |
+
logger: False
|
conf/ProteinGym/saprot.yaml
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
#CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 5 |
+
CUDA_VISIBLE_DEVICES: 6
|
| 6 |
+
MASTER_ADDR: 127.0.0.1
|
| 7 |
+
MASTER_PORT: 12315
|
| 8 |
+
WORLD_SIZE: 1
|
| 9 |
+
NODE_RANK: 0
|
| 10 |
+
|
| 11 |
+
out_path: output/ProteinGym/SaProt_650M_AF2.tsv
|
| 12 |
+
dataset_dir: scripts/LMDB/ProteinGym/substitutions
|
| 13 |
+
|
| 14 |
+
model:
|
| 15 |
+
model_py_path: saprot/saprot_foldseek_mutation_model
|
| 16 |
+
kwargs:
|
| 17 |
+
foldseek_path: scripts/bin/foldseek
|
| 18 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 19 |
+
load_pretrained: True
|
| 20 |
+
plddt_threshold: 70
|
| 21 |
+
|
| 22 |
+
dataset:
|
| 23 |
+
dataset_py_path: mutation_zeroshot_dataset
|
| 24 |
+
dataloader_kwargs:
|
| 25 |
+
batch_size: 32
|
| 26 |
+
num_workers: 8
|
| 27 |
+
kwargs: {}
|
| 28 |
+
|
| 29 |
+
Trainer:
|
| 30 |
+
log_every_n_steps: 1
|
| 31 |
+
devices: 1
|
| 32 |
+
accelerator: gpu
|
| 33 |
+
num_nodes: 1
|
| 34 |
+
strategy:
|
| 35 |
+
find_unused_parameters: False
|
| 36 |
+
logger: False
|
conf/Thermostability/esm2.yaml
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: Thermostability
|
| 13 |
+
name: esm2_t33_650M_UR50D
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_regression_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/esm2_t33_650M_UR50D
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
|
| 23 |
+
# Arguments to initialize the basic class AbstractModel
|
| 24 |
+
lr_scheduler_kwargs:
|
| 25 |
+
last_epoch: -1
|
| 26 |
+
init_lr: 2.0e-5
|
| 27 |
+
# Weather to use this scheduler or not
|
| 28 |
+
on_use: false
|
| 29 |
+
|
| 30 |
+
optimizer_kwargs:
|
| 31 |
+
betas: [0.9, 0.98]
|
| 32 |
+
weight_decay: 0.01
|
| 33 |
+
|
| 34 |
+
save_path: weight/Thermostability/esm2_t33_650M_UR50D.pt
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
dataset:
|
| 38 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 39 |
+
dataset_py_path: saprot/saprot_regression_dataset
|
| 40 |
+
dataloader_kwargs:
|
| 41 |
+
batch_size: 8
|
| 42 |
+
num_workers: 8
|
| 43 |
+
|
| 44 |
+
train_lmdb: scripts/LMDB/Thermostability/normal/train
|
| 45 |
+
valid_lmdb: scripts/LMDB/Thermostability/normal/valid
|
| 46 |
+
test_lmdb: scripts/LMDB/Thermostability/normal/test
|
| 47 |
+
# Arguments to initialize the specific class
|
| 48 |
+
kwargs:
|
| 49 |
+
tokenizer: weight/PLMs/esm2_t33_650M_UR50D
|
| 50 |
+
mix_max_norm: [40, 67]
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 54 |
+
Trainer:
|
| 55 |
+
max_epochs: 200
|
| 56 |
+
log_every_n_steps: 1
|
| 57 |
+
strategy:
|
| 58 |
+
find_unused_parameters: True
|
| 59 |
+
logger: True
|
| 60 |
+
enable_checkpointing: false
|
| 61 |
+
val_check_interval: 0.5
|
| 62 |
+
accelerator: gpu
|
| 63 |
+
devices: 8
|
| 64 |
+
num_nodes: 1
|
| 65 |
+
accumulate_grad_batches: 1
|
| 66 |
+
precision: 16
|
| 67 |
+
num_sanity_val_steps: 0
|
| 68 |
+
|
| 69 |
+
|
conf/Thermostability/saprot.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: Thermostability
|
| 13 |
+
name: SaProt_650M_AF2
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_regression_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
|
| 23 |
+
# Arguments to initialize the basic class AbstractModel
|
| 24 |
+
lr_scheduler_kwargs:
|
| 25 |
+
last_epoch: -1
|
| 26 |
+
init_lr: 2.0e-5
|
| 27 |
+
# Weather to use this scheduler or not
|
| 28 |
+
on_use: false
|
| 29 |
+
|
| 30 |
+
optimizer_kwargs:
|
| 31 |
+
betas: [0.9, 0.98]
|
| 32 |
+
weight_decay: 0.01
|
| 33 |
+
|
| 34 |
+
save_path: weight/Thermostability/SaProt_650M_AF2.pt
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
dataset:
|
| 38 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 39 |
+
dataset_py_path: saprot/saprot_regression_dataset
|
| 40 |
+
dataloader_kwargs:
|
| 41 |
+
batch_size: 8
|
| 42 |
+
num_workers: 8
|
| 43 |
+
|
| 44 |
+
train_lmdb: scripts/LMDB/Thermostability/foldseek/train
|
| 45 |
+
valid_lmdb: scripts/LMDB/Thermostability/foldseek/valid
|
| 46 |
+
test_lmdb: scripts/LMDB/Thermostability/foldseek/test
|
| 47 |
+
# Arguments to initialize the specific class
|
| 48 |
+
kwargs:
|
| 49 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 50 |
+
mix_max_norm: [40, 67]
|
| 51 |
+
plddt_threshold: 70
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 200
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: True
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.5
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 4
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 2
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
conf/pretrain/saprot.yaml
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 0,1,2,3,4,5,6,7
|
| 7 |
+
MASTER_ADDR: 127.0.0.1
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
# WANDB_MODE: offline
|
| 12 |
+
wandb_config:
|
| 13 |
+
project: pretraining
|
| 14 |
+
name: SaProt_650M_AF2
|
| 15 |
+
|
| 16 |
+
model:
|
| 17 |
+
# Which model to use
|
| 18 |
+
model_py_path: saprot/saprot_lm_model
|
| 19 |
+
kwargs:
|
| 20 |
+
# Arguments to initialize the specific class
|
| 21 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 22 |
+
load_pretrained: True
|
| 23 |
+
|
| 24 |
+
# Arguments to initialize the basic class AbstractModel
|
| 25 |
+
lr_scheduler_kwargs:
|
| 26 |
+
last_epoch: -1
|
| 27 |
+
init_lr: 0
|
| 28 |
+
max_lr: 4.0e-4
|
| 29 |
+
final_lr: 4.0e-5
|
| 30 |
+
warmup_steps: 2000
|
| 31 |
+
start_decay_after_n_steps: 150000
|
| 32 |
+
end_decay_after_n_steps: 1500000
|
| 33 |
+
# Weather to use this scheduler or not
|
| 34 |
+
on_use: True
|
| 35 |
+
|
| 36 |
+
optimizer_kwargs:
|
| 37 |
+
betas: [0.9, 0.98]
|
| 38 |
+
weight_decay: 0.01
|
| 39 |
+
|
| 40 |
+
save_path: weight/Pretrain/SaProt_650M_AF2.pt
|
| 41 |
+
load_prev_scheduler: false
|
| 42 |
+
save_weights_only: false
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
dataset:
|
| 46 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 47 |
+
dataset_py_path: saprot/saprot_foldseek_dataset
|
| 48 |
+
dataloader_kwargs:
|
| 49 |
+
batch_size: 8
|
| 50 |
+
num_workers: 8
|
| 51 |
+
|
| 52 |
+
train_lmdb: scripts/LMDB/AF2_Uniref50/foldseek/train
|
| 53 |
+
valid_lmdb: scripts/LMDB/AF2_Uniref50/foldseek/valid
|
| 54 |
+
test_lmdb: scripts/LMDB/AF2_Uniref50/foldseek/valid
|
| 55 |
+
# Arguments to initialize the specific class
|
| 56 |
+
kwargs:
|
| 57 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 58 |
+
max_length: 1024
|
| 59 |
+
mask_ratio: 0.15
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 63 |
+
Trainer:
|
| 64 |
+
# max_epochs: 100
|
| 65 |
+
max_steps: 1000000
|
| 66 |
+
min_steps: 1000000
|
| 67 |
+
log_every_n_steps: 1
|
| 68 |
+
accelerator: gpu
|
| 69 |
+
devices: 1
|
| 70 |
+
accumulate_grad_batches: 1
|
| 71 |
+
num_nodes: 1
|
| 72 |
+
strategy:
|
| 73 |
+
find_unused_parameters: True
|
| 74 |
+
logger: False
|
| 75 |
+
enable_checkpointing: false
|
| 76 |
+
num_sanity_val_steps: 0
|
| 77 |
+
val_check_interval: 100
|
| 78 |
+
precision: 16
|
| 79 |
+
limit_val_batches: 1.0
|
| 80 |
+
limit_test_batches: 1.0
|
| 81 |
+
|
conf/scnet/ClinVar_saprot.yaml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
CUDA_VISIBLE_DEVICES: 0
|
| 5 |
+
MASTER_ADDR: 127.0.0.1
|
| 6 |
+
MASTER_PORT: 12315
|
| 7 |
+
WORLD_SIZE: 1
|
| 8 |
+
NODE_RANK: 0
|
| 9 |
+
|
| 10 |
+
out_path: ~
|
| 11 |
+
dataset_dir: scripts/LMDB/ClinVar
|
| 12 |
+
|
| 13 |
+
model:
|
| 14 |
+
model_py_path: saprot/saprot_foldseek_mutation_model
|
| 15 |
+
kwargs:
|
| 16 |
+
foldseek_path: scripts/bin/foldseek
|
| 17 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 18 |
+
load_pretrained: True
|
| 19 |
+
log_clinvar: True
|
| 20 |
+
log_dir: output/ClinVar/SaProt_650M_AF2_scnet
|
| 21 |
+
plddt_threshold: 70.
|
| 22 |
+
|
| 23 |
+
dataset:
|
| 24 |
+
dataset_py_path: mutation_zeroshot_dataset
|
| 25 |
+
dataloader_kwargs:
|
| 26 |
+
batch_size: 8
|
| 27 |
+
num_workers: 4
|
| 28 |
+
kwargs: {}
|
| 29 |
+
|
| 30 |
+
Trainer:
|
| 31 |
+
log_every_n_steps: 1
|
| 32 |
+
devices: 1
|
| 33 |
+
accelerator: gpu
|
| 34 |
+
num_nodes: 1
|
| 35 |
+
strategy:
|
| 36 |
+
find_unused_parameters: False
|
| 37 |
+
logger: False
|
conf/scnet/Thermostability_saprot_1gpu.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setting:
|
| 2 |
+
seed: 20000812
|
| 3 |
+
os_environ:
|
| 4 |
+
WANDB_API_KEY: ~
|
| 5 |
+
WANDB_RUN_ID: ~
|
| 6 |
+
CUDA_VISIBLE_DEVICES: 3
|
| 7 |
+
MASTER_ADDR: localhost
|
| 8 |
+
MASTER_PORT: 12315
|
| 9 |
+
WORLD_SIZE: 1
|
| 10 |
+
NODE_RANK: 0
|
| 11 |
+
wandb_config:
|
| 12 |
+
project: Thermostability
|
| 13 |
+
name: SaProt_650M_AF2_scnet_1gpu
|
| 14 |
+
|
| 15 |
+
model:
|
| 16 |
+
# Which model to use
|
| 17 |
+
model_py_path: saprot/saprot_regression_model
|
| 18 |
+
kwargs:
|
| 19 |
+
# Arguments to initialize the specific class
|
| 20 |
+
config_path: weight/PLMs/SaProt_650M_AF2
|
| 21 |
+
load_pretrained: True
|
| 22 |
+
|
| 23 |
+
# Arguments to initialize the basic class AbstractModel
|
| 24 |
+
lr_scheduler_kwargs:
|
| 25 |
+
last_epoch: -1
|
| 26 |
+
init_lr: 2.0e-5
|
| 27 |
+
# Weather to use this scheduler or not
|
| 28 |
+
on_use: false
|
| 29 |
+
|
| 30 |
+
optimizer_kwargs:
|
| 31 |
+
betas: [0.9, 0.98]
|
| 32 |
+
weight_decay: 0.01
|
| 33 |
+
|
| 34 |
+
save_path: weight/Thermostability/SaProt_650M_AF2_scnet_1gpu.pt
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
dataset:
|
| 38 |
+
# Arguments to initialize the basic class LMDBDataset
|
| 39 |
+
dataset_py_path: saprot/saprot_regression_dataset
|
| 40 |
+
dataloader_kwargs:
|
| 41 |
+
batch_size: 1
|
| 42 |
+
num_workers: 4
|
| 43 |
+
|
| 44 |
+
train_lmdb: scripts/LMDB/Thermostability/foldseek/train
|
| 45 |
+
valid_lmdb: scripts/LMDB/Thermostability/foldseek/valid
|
| 46 |
+
test_lmdb: scripts/LMDB/Thermostability/foldseek/test
|
| 47 |
+
# Arguments to initialize the specific class
|
| 48 |
+
kwargs:
|
| 49 |
+
tokenizer: weight/PLMs/SaProt_650M_AF2
|
| 50 |
+
mix_max_norm: [40, 67]
|
| 51 |
+
plddt_threshold: 70
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# Arguments to initialize Pytorch Lightning Trainer
|
| 55 |
+
Trainer:
|
| 56 |
+
max_epochs: 200
|
| 57 |
+
log_every_n_steps: 1
|
| 58 |
+
strategy:
|
| 59 |
+
find_unused_parameters: True
|
| 60 |
+
logger: False
|
| 61 |
+
enable_checkpointing: false
|
| 62 |
+
val_check_interval: 0.5
|
| 63 |
+
accelerator: gpu
|
| 64 |
+
devices: 1
|
| 65 |
+
num_nodes: 1
|
| 66 |
+
accumulate_grad_batches: 8
|
| 67 |
+
precision: 16
|
| 68 |
+
num_sanity_val_steps: 0
|
config.json
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"framework": "PyTorch",
|
| 3 |
+
"task": "protein-language-modeling",
|
| 4 |
+
"model": "SaProt",
|
| 5 |
+
"description": "Structure-aware protein language modeling with amino-acid and Foldseek 3Di tokens for representation extraction, mutation-effect prediction, inverse folding, pretraining, downstream fine-tuning, and benchmark evaluation.",
|
| 6 |
+
"subtasks": [
|
| 7 |
+
"structure-aware-sequence-encoding",
|
| 8 |
+
"masked-language-model-inference",
|
| 9 |
+
"protein-embedding-extraction",
|
| 10 |
+
"zero-shot-mutation-effect-prediction",
|
| 11 |
+
"protein-inverse-folding",
|
| 12 |
+
"saprot-pretraining",
|
| 13 |
+
"downstream-finetuning",
|
| 14 |
+
"benchmark-evaluation",
|
| 15 |
+
"esm2-baseline-evaluation"
|
| 16 |
+
],
|
| 17 |
+
"entrypoints": {
|
| 18 |
+
"train": "scripts/training.py",
|
| 19 |
+
"pretrain_config": "conf/pretrain/saprot.yaml",
|
| 20 |
+
"thermos_finetune_config": "conf/Thermostability/saprot.yaml",
|
| 21 |
+
"scnet_thermos_finetune_config": "conf/scnet/Thermostability_saprot_1gpu.yaml",
|
| 22 |
+
"mutation_zeroshot": "scripts/mutation_zeroshot.py",
|
| 23 |
+
"proteingym_config": "conf/ProteinGym/saprot.yaml",
|
| 24 |
+
"clinvar_config": "conf/ClinVar/saprot.yaml",
|
| 25 |
+
"scnet_clinvar_config": "conf/scnet/ClinVar_saprot.yaml",
|
| 26 |
+
"clinvar_auc": "scripts/compute_clinvar_auc.py",
|
| 27 |
+
"environment_setup": "scripts/environment.sh"
|
| 28 |
+
},
|
| 29 |
+
"packages": [
|
| 30 |
+
"model",
|
| 31 |
+
"scripts.dataset",
|
| 32 |
+
"scripts.utils"
|
| 33 |
+
],
|
| 34 |
+
"configs": {
|
| 35 |
+
"root": "conf",
|
| 36 |
+
"saprot_tasks": [
|
| 37 |
+
"conf/Thermostability/saprot.yaml",
|
| 38 |
+
"conf/EC/saprot.yaml",
|
| 39 |
+
"conf/GO/MF/saprot.yaml",
|
| 40 |
+
"conf/GO/BP/saprot.yaml",
|
| 41 |
+
"conf/GO/CC/saprot.yaml",
|
| 42 |
+
"conf/MetalIonBinding/saprot.yaml",
|
| 43 |
+
"conf/HumanPPI/saprot.yaml",
|
| 44 |
+
"conf/Contact/saprot.yaml",
|
| 45 |
+
"conf/DeepLoc/cls2/saprot.yaml",
|
| 46 |
+
"conf/DeepLoc/cls10/saprot.yaml",
|
| 47 |
+
"conf/ProteinGym/saprot.yaml",
|
| 48 |
+
"conf/ClinVar/saprot.yaml"
|
| 49 |
+
],
|
| 50 |
+
"esm2_baselines": [
|
| 51 |
+
"conf/Thermostability/esm2.yaml",
|
| 52 |
+
"conf/EC/esm2.yaml",
|
| 53 |
+
"conf/GO/MF/esm2.yaml",
|
| 54 |
+
"conf/GO/BP/esm2.yaml",
|
| 55 |
+
"conf/GO/CC/esm2.yaml",
|
| 56 |
+
"conf/MetalIonBinding/esm2.yaml",
|
| 57 |
+
"conf/HumanPPI/esm2.yaml",
|
| 58 |
+
"conf/Contact/esm2.yaml",
|
| 59 |
+
"conf/DeepLoc/cls2/esm2.yaml",
|
| 60 |
+
"conf/DeepLoc/cls10/esm2.yaml",
|
| 61 |
+
"conf/ProteinGym/esm2.yaml",
|
| 62 |
+
"conf/ClinVar/esm2.yaml"
|
| 63 |
+
],
|
| 64 |
+
"scnet": [
|
| 65 |
+
"conf/scnet/Thermostability_saprot_1gpu.yaml",
|
| 66 |
+
"conf/scnet/ClinVar_saprot.yaml"
|
| 67 |
+
]
|
| 68 |
+
},
|
| 69 |
+
"example_data": [
|
| 70 |
+
"scripts/example/8ac8.cif"
|
| 71 |
+
],
|
| 72 |
+
"data_roots": {
|
| 73 |
+
"downstream_lmdb": "scripts/LMDB",
|
| 74 |
+
"pretrain_lmdb": "scripts/LMDB/AF2_Uniref50/foldseek",
|
| 75 |
+
"proteingym": "scripts/LMDB/ProteinGym/substitutions",
|
| 76 |
+
"clinvar": "scripts/LMDB/ClinVar",
|
| 77 |
+
"thermos": "scripts/LMDB/Thermostability/foldseek"
|
| 78 |
+
},
|
| 79 |
+
"model_cache": "weight/PLMs",
|
| 80 |
+
"default_model": "weight/PLMs/SaProt_650M_AF2",
|
| 81 |
+
"optional_models": {
|
| 82 |
+
"esm2_650m": "weight/PLMs/esm2_t33_650M_UR50D",
|
| 83 |
+
"saprot_inverse_folding": "weight/PLMs/SaProt_650M_AF2_inverse_folding"
|
| 84 |
+
},
|
| 85 |
+
"external_tools": {
|
| 86 |
+
"foldseek": "scripts/bin/foldseek"
|
| 87 |
+
},
|
| 88 |
+
"outputs": {
|
| 89 |
+
"finetuned_checkpoints": "weight/{task}/*.pt",
|
| 90 |
+
"proteingym_scores": "output/ProteinGym/SaProt_650M_AF2.tsv",
|
| 91 |
+
"clinvar_predictions": "output/ClinVar/SaProt_650M_AF2",
|
| 92 |
+
"scnet_clinvar_predictions": "output/ClinVar/SaProt_650M_AF2_scnet"
|
| 93 |
+
},
|
| 94 |
+
"quick_commands": {
|
| 95 |
+
"setup": "bash scripts/environment.sh",
|
| 96 |
+
"train_thermostability": "python scripts/training.py -c conf/Thermostability/saprot.yaml",
|
| 97 |
+
"train_thermostability_scnet_1gpu": "python scripts/training.py -c conf/scnet/Thermostability_saprot_1gpu.yaml",
|
| 98 |
+
"zeroshot_proteingym": "python scripts/mutation_zeroshot.py -c conf/ProteinGym/saprot.yaml",
|
| 99 |
+
"zeroshot_clinvar": "python scripts/mutation_zeroshot.py -c conf/ClinVar/saprot.yaml",
|
| 100 |
+
"compute_clinvar_auc": "python scripts/compute_clinvar_auc.py -c conf/ClinVar/saprot.yaml"
|
| 101 |
+
}
|
| 102 |
+
}
|
model/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Introduction
|
| 2 |
+
## abstract_model.py
|
| 3 |
+
The `abstract_model.py` file contains the abstract class for all models.
|
| 4 |
+
|
| 5 |
+
## model_interface.py
|
| 6 |
+
The `model_interface.py` file is used to dynamically load a model.
|
| 7 |
+
## saprot
|
| 8 |
+
The `saprot` folder contains various implementations for using SaProt model on downstream tasks. Note that
|
| 9 |
+
SaProt shares the same architecture with ESM models, so we can use the same code to fine-tune both SaProt and ESM-2.
|
model/abstract_model.py
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import abc
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
import pytorch_lightning as pl
|
| 6 |
+
from scripts.utils.lr_scheduler import Esm2LRScheduler
|
| 7 |
+
from torch import distributed as dist
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class AbstractModel(pl.LightningModule):
|
| 11 |
+
def __init__(self,
|
| 12 |
+
lr_scheduler_kwargs: dict = None,
|
| 13 |
+
optimizer_kwargs: dict = None,
|
| 14 |
+
save_path: str = None,
|
| 15 |
+
from_checkpoint: str = None,
|
| 16 |
+
load_prev_scheduler: bool = False,
|
| 17 |
+
save_weights_only: bool = True,):
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
Args:
|
| 21 |
+
lr_scheduler: Kwargs for lr_scheduler
|
| 22 |
+
optimizer_kwargs: Kwargs for optimizer_kwargs
|
| 23 |
+
save_path: Save trained model
|
| 24 |
+
from_checkpoint: Load model from checkpoint
|
| 25 |
+
load_prev_scheduler: Whether load previous scheduler from save_path
|
| 26 |
+
load_strict: Whether load model strictly
|
| 27 |
+
save_weights_only: Whether save only weights or also optimizer and lr_scheduler
|
| 28 |
+
|
| 29 |
+
"""
|
| 30 |
+
super().__init__()
|
| 31 |
+
self.initialize_model()
|
| 32 |
+
|
| 33 |
+
self.metrics = {}
|
| 34 |
+
for stage in ["train", "valid", "test"]:
|
| 35 |
+
stage_metrics = self.initialize_metrics(stage)
|
| 36 |
+
# Rigister metrics as attributes
|
| 37 |
+
for metric_name, metric in stage_metrics.items():
|
| 38 |
+
setattr(self, metric_name, metric)
|
| 39 |
+
|
| 40 |
+
self.metrics[stage] = stage_metrics
|
| 41 |
+
|
| 42 |
+
self.lr_scheduler_kwargs = {"init_lr": 0} if lr_scheduler_kwargs is None else lr_scheduler_kwargs
|
| 43 |
+
self.optimizer_kwargs = {} if optimizer_kwargs is None else optimizer_kwargs
|
| 44 |
+
self.init_optimizers()
|
| 45 |
+
|
| 46 |
+
self.save_path = save_path
|
| 47 |
+
self.save_weights_only = save_weights_only
|
| 48 |
+
|
| 49 |
+
self.step = 0
|
| 50 |
+
self.epoch = 0
|
| 51 |
+
|
| 52 |
+
self.load_prev_scheduler = load_prev_scheduler
|
| 53 |
+
if from_checkpoint:
|
| 54 |
+
self.load_checkpoint(from_checkpoint, load_prev_scheduler)
|
| 55 |
+
|
| 56 |
+
@abc.abstractmethod
|
| 57 |
+
def initialize_model(self) -> None:
|
| 58 |
+
"""
|
| 59 |
+
All model initialization should be done here
|
| 60 |
+
Note that the whole model must be named as "self.model" for model saving and loading
|
| 61 |
+
"""
|
| 62 |
+
raise NotImplementedError
|
| 63 |
+
|
| 64 |
+
@abc.abstractmethod
|
| 65 |
+
def forward(self, *args, **kwargs):
|
| 66 |
+
"""
|
| 67 |
+
Forward propagation
|
| 68 |
+
"""
|
| 69 |
+
raise NotImplementedError
|
| 70 |
+
|
| 71 |
+
@abc.abstractmethod
|
| 72 |
+
def initialize_metrics(self, stage: str) -> dict:
|
| 73 |
+
"""
|
| 74 |
+
Initialize metrics for each stage
|
| 75 |
+
Args:
|
| 76 |
+
stage: "train", "valid" or "test"
|
| 77 |
+
|
| 78 |
+
Returns:
|
| 79 |
+
A dictionary of metrics for the stage. Keys are metric names and values are metric objects
|
| 80 |
+
"""
|
| 81 |
+
raise NotImplementedError
|
| 82 |
+
|
| 83 |
+
@abc.abstractmethod
|
| 84 |
+
def loss_func(self, stage: str, outputs, labels) -> torch.Tensor:
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
Args:
|
| 88 |
+
stage: "train", "valid" or "test"
|
| 89 |
+
outputs: model outputs for calculating loss
|
| 90 |
+
labels: labels for calculating loss
|
| 91 |
+
|
| 92 |
+
Returns:
|
| 93 |
+
loss
|
| 94 |
+
|
| 95 |
+
"""
|
| 96 |
+
raise NotImplementedError
|
| 97 |
+
|
| 98 |
+
@staticmethod
|
| 99 |
+
def load_weights(model, weights):
|
| 100 |
+
model_dict = model.state_dict()
|
| 101 |
+
|
| 102 |
+
unused_params = []
|
| 103 |
+
missed_params = list(model_dict.keys())
|
| 104 |
+
|
| 105 |
+
for k, v in weights.items():
|
| 106 |
+
if k in model_dict.keys():
|
| 107 |
+
model_dict[k] = v
|
| 108 |
+
missed_params.remove(k)
|
| 109 |
+
|
| 110 |
+
else:
|
| 111 |
+
unused_params.append(k)
|
| 112 |
+
|
| 113 |
+
if len(missed_params) > 0:
|
| 114 |
+
print(f"\033[31mSome weights of {type(model).__name__} were not "
|
| 115 |
+
f"initialized from the model checkpoint: {missed_params}\033[0m")
|
| 116 |
+
|
| 117 |
+
if len(unused_params) > 0:
|
| 118 |
+
print(f"\033[31mSome weights of the model checkpoint were not used: {unused_params}\033[0m")
|
| 119 |
+
|
| 120 |
+
model.load_state_dict(model_dict)
|
| 121 |
+
|
| 122 |
+
# Add 1 to step after each optimizer step
|
| 123 |
+
def optimizer_step(
|
| 124 |
+
self,
|
| 125 |
+
epoch: int,
|
| 126 |
+
batch_idx: int,
|
| 127 |
+
optimizer,
|
| 128 |
+
optimizer_idx: int = 0,
|
| 129 |
+
optimizer_closure=None,
|
| 130 |
+
on_tpu: bool = False,
|
| 131 |
+
using_native_amp: bool = False,
|
| 132 |
+
using_lbfgs: bool = False,
|
| 133 |
+
) -> None:
|
| 134 |
+
super().optimizer_step(
|
| 135 |
+
epoch, batch_idx, optimizer, optimizer_idx, optimizer_closure, on_tpu, using_native_amp, using_lbfgs
|
| 136 |
+
)
|
| 137 |
+
self.step += 1
|
| 138 |
+
|
| 139 |
+
def on_train_epoch_end(self):
|
| 140 |
+
self.epoch += 1
|
| 141 |
+
|
| 142 |
+
def training_step(self, batch, batch_idx):
|
| 143 |
+
inputs, labels = batch
|
| 144 |
+
outputs = self(**inputs)
|
| 145 |
+
loss = self.loss_func('train', outputs, labels)
|
| 146 |
+
return loss
|
| 147 |
+
|
| 148 |
+
def validation_step(self, batch, batch_idx):
|
| 149 |
+
inputs, labels = batch
|
| 150 |
+
outputs = self(**inputs)
|
| 151 |
+
return self.loss_func('valid', outputs, labels)
|
| 152 |
+
|
| 153 |
+
def test_step(self, batch, batch_idx):
|
| 154 |
+
inputs, labels = batch
|
| 155 |
+
outputs = self(**inputs)
|
| 156 |
+
return self.loss_func('test', outputs, labels)
|
| 157 |
+
|
| 158 |
+
def load_checkpoint(self, from_checkpoint, load_prev_scheduler):
|
| 159 |
+
state_dict = torch.load(from_checkpoint, map_location=self.device)
|
| 160 |
+
self.load_weights(self.model, state_dict["model"])
|
| 161 |
+
|
| 162 |
+
if load_prev_scheduler:
|
| 163 |
+
try:
|
| 164 |
+
self.step = state_dict["global_step"]
|
| 165 |
+
self.epoch = state_dict["epoch"]
|
| 166 |
+
self.best_value = state_dict["best_value"]
|
| 167 |
+
self.optimizer.load_state_dict(state_dict["optimizer"])
|
| 168 |
+
self.lr_scheduler.load_state_dict(state_dict["lr_scheduler"])
|
| 169 |
+
print(f"Previous training global step: {self.step}")
|
| 170 |
+
print(f"Previous training epoch: {self.epoch}")
|
| 171 |
+
print(f"Previous best value: {self.best_value}")
|
| 172 |
+
print(f"Previous lr_scheduler: {state_dict['lr_scheduler']}")
|
| 173 |
+
|
| 174 |
+
except Exception as e:
|
| 175 |
+
print(e)
|
| 176 |
+
raise KeyError("Wrong in loading previous scheduler, please set load_prev_scheduler=False")
|
| 177 |
+
|
| 178 |
+
def save_checkpoint(self, save_info: dict = None) -> None:
|
| 179 |
+
"""
|
| 180 |
+
Save model to save_path
|
| 181 |
+
Args:
|
| 182 |
+
save_info: Other info to save
|
| 183 |
+
"""
|
| 184 |
+
state_dict = {} if save_info is None else save_info
|
| 185 |
+
state_dict["model"] = self.model.state_dict()
|
| 186 |
+
|
| 187 |
+
if not self.save_weights_only:
|
| 188 |
+
state_dict["global_step"] = self.step
|
| 189 |
+
state_dict["epoch"] = self.epoch
|
| 190 |
+
state_dict["best_value"] = getattr(self, f"best_value", None)
|
| 191 |
+
state_dict["optimizer"] = self.optimizers().optimizer.state_dict()
|
| 192 |
+
state_dict["lr_scheduler"] = self.lr_schedulers().state_dict()
|
| 193 |
+
|
| 194 |
+
torch.save(state_dict, self.save_path)
|
| 195 |
+
|
| 196 |
+
def check_save_condition(self, now_value: float, mode: str, save_info: dict = None) -> None:
|
| 197 |
+
"""
|
| 198 |
+
Check whether to save model. If save_path is not None and now_value is the best, save model.
|
| 199 |
+
Args:
|
| 200 |
+
now_value: Current metric value
|
| 201 |
+
mode: "min" or "max", meaning whether the lower the better or the higher the better
|
| 202 |
+
save_info: Other info to save
|
| 203 |
+
"""
|
| 204 |
+
|
| 205 |
+
assert mode in ["min", "max"], "mode should be 'min' or 'max'"
|
| 206 |
+
|
| 207 |
+
if self.save_path is not None:
|
| 208 |
+
dir = os.path.dirname(self.save_path)
|
| 209 |
+
os.makedirs(dir, exist_ok=True)
|
| 210 |
+
|
| 211 |
+
if dist.get_rank() == 0:
|
| 212 |
+
# save the best checkpoint
|
| 213 |
+
best_value = getattr(self, f"best_value", None)
|
| 214 |
+
if best_value:
|
| 215 |
+
if mode == "min" and now_value < best_value or mode == "max" and now_value > best_value:
|
| 216 |
+
setattr(self, "best_value", now_value)
|
| 217 |
+
self.save_checkpoint(save_info)
|
| 218 |
+
|
| 219 |
+
else:
|
| 220 |
+
setattr(self, "best_value", now_value)
|
| 221 |
+
self.save_checkpoint(save_info)
|
| 222 |
+
|
| 223 |
+
def reset_metrics(self, stage) -> None:
|
| 224 |
+
"""
|
| 225 |
+
Reset metrics for given stage
|
| 226 |
+
Args:
|
| 227 |
+
stage: "train", "valid" or "test"
|
| 228 |
+
"""
|
| 229 |
+
for metric in self.metrics[stage].values():
|
| 230 |
+
metric.reset()
|
| 231 |
+
|
| 232 |
+
def get_log_dict(self, stage: str) -> dict:
|
| 233 |
+
"""
|
| 234 |
+
Get log dict for the stage
|
| 235 |
+
Args:
|
| 236 |
+
stage: "train", "valid" or "test"
|
| 237 |
+
|
| 238 |
+
Returns:
|
| 239 |
+
A dictionary of metrics for the stage. Keys are metric names and values are metric values
|
| 240 |
+
|
| 241 |
+
"""
|
| 242 |
+
return {name: metric.compute() for name, metric in self.metrics[stage].items()}
|
| 243 |
+
|
| 244 |
+
def log_info(self, info: dict) -> None:
|
| 245 |
+
"""
|
| 246 |
+
Record metrics during training and testing
|
| 247 |
+
Args:
|
| 248 |
+
info: dict of metrics
|
| 249 |
+
"""
|
| 250 |
+
if getattr(self, "logger", None) is not None:
|
| 251 |
+
info["learning_rate"] = self.lr_scheduler.get_last_lr()[0]
|
| 252 |
+
info["epoch"] = self.epoch
|
| 253 |
+
self.logger.log_metrics(info, step=self.step)
|
| 254 |
+
|
| 255 |
+
def init_optimizers(self):
|
| 256 |
+
# No decay for layer norm and bias
|
| 257 |
+
no_decay = ['LayerNorm.weight', 'bias']
|
| 258 |
+
|
| 259 |
+
if "weight_decay" in self.optimizer_kwargs:
|
| 260 |
+
weight_decay = self.optimizer_kwargs.pop("weight_decay")
|
| 261 |
+
else:
|
| 262 |
+
weight_decay = 0.01
|
| 263 |
+
|
| 264 |
+
optimizer_grouped_parameters = [
|
| 265 |
+
{'params': [p for n, p in self.model.named_parameters() if not any(nd in n for nd in no_decay)],
|
| 266 |
+
'weight_decay': weight_decay},
|
| 267 |
+
{'params': [p for n, p in self.model.named_parameters() if any(nd in n for nd in no_decay)],
|
| 268 |
+
'weight_decay': 0.0}
|
| 269 |
+
]
|
| 270 |
+
|
| 271 |
+
self.optimizer = torch.optim.AdamW(optimizer_grouped_parameters,
|
| 272 |
+
lr=self.lr_scheduler_kwargs['init_lr'],
|
| 273 |
+
**self.optimizer_kwargs)
|
| 274 |
+
|
| 275 |
+
self.lr_scheduler = Esm2LRScheduler(self.optimizer, **self.lr_scheduler_kwargs)
|
| 276 |
+
|
| 277 |
+
def configure_optimizers(self):
|
| 278 |
+
return {"optimizer": self.optimizer,
|
| 279 |
+
"lr_scheduler": {"scheduler": self.lr_scheduler,
|
| 280 |
+
"interval": "step",
|
| 281 |
+
"frequency": 1}
|
| 282 |
+
}
|
model/model_interface.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import yaml
|
| 2 |
+
import glob
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
# register all available models through *_model.py files
|
| 6 |
+
# def construct_model():
|
| 7 |
+
# model_dir = os.path.dirname(__file__)
|
| 8 |
+
#
|
| 9 |
+
# # lists all model files
|
| 10 |
+
# model_list = []
|
| 11 |
+
# for root, _, names in os.walk(model_dir):
|
| 12 |
+
# for name in names:
|
| 13 |
+
# if name.endswith('_model.py'):
|
| 14 |
+
# sub_dirs = root.replace(model_dir, '').split(os.sep)
|
| 15 |
+
# model_list.append((sub_dirs, name[:-3]))
|
| 16 |
+
#
|
| 17 |
+
# # load model_config.yaml, controlling which models to be loaded
|
| 18 |
+
# model_config = yaml.safe_load(open(f"{model_dir}/model_config.yaml", "r"))
|
| 19 |
+
#
|
| 20 |
+
# if model_config["verbose"]:
|
| 21 |
+
# print("*" * 30 + f" Loading model " + "*" * 30)
|
| 22 |
+
#
|
| 23 |
+
# # register models
|
| 24 |
+
# for sub_dirs, name in model_list:
|
| 25 |
+
# if name in model_config["models"]:
|
| 26 |
+
# if len(sub_dirs) > 1:
|
| 27 |
+
# cmd = f"from {'.'.join(sub_dirs)} import {name}"
|
| 28 |
+
# else:
|
| 29 |
+
# cmd = f"from . import {name}"
|
| 30 |
+
#
|
| 31 |
+
# exec(cmd)
|
| 32 |
+
#
|
| 33 |
+
# if model_config["verbose"]:
|
| 34 |
+
# info = f"Loaded model: {name}"
|
| 35 |
+
# print(f"\033[32m{info}\033[0m")
|
| 36 |
+
# else:
|
| 37 |
+
# if model_config["verbose"]:
|
| 38 |
+
# info = f"Skipped model: {name}"
|
| 39 |
+
# print(f"\033[31m{info}\033[0m")
|
| 40 |
+
#
|
| 41 |
+
# if model_config["verbose"]:
|
| 42 |
+
# print("*" * 75)
|
| 43 |
+
#
|
| 44 |
+
#
|
| 45 |
+
# # register function as a wrapper for all models
|
| 46 |
+
# def register_model(cls):
|
| 47 |
+
# model_dict[cls.__name__] = cls
|
| 48 |
+
# return cls
|
| 49 |
+
#
|
| 50 |
+
#
|
| 51 |
+
# model_dict = {}
|
| 52 |
+
# construct_model()
|
| 53 |
+
#
|
| 54 |
+
#
|
| 55 |
+
# class ModelInterface:
|
| 56 |
+
# @classmethod
|
| 57 |
+
# def get_available_models(cls):
|
| 58 |
+
# return model_dict.keys()
|
| 59 |
+
#
|
| 60 |
+
# @classmethod
|
| 61 |
+
# def init_model(cls, model: str, **kwargs):
|
| 62 |
+
# """
|
| 63 |
+
#
|
| 64 |
+
# Args:
|
| 65 |
+
# model : Class name of model you want to use. Must be in model_dict.keys()
|
| 66 |
+
# **kwargs: Kwargs for model initialization
|
| 67 |
+
#
|
| 68 |
+
# Returns: Corresponding model
|
| 69 |
+
#
|
| 70 |
+
# """
|
| 71 |
+
# assert model in model_dict.keys(), f"class {model} doesn't exist!"
|
| 72 |
+
# return model_dict[model](**kwargs)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
########################################################################
|
| 76 |
+
# Version 2 #
|
| 77 |
+
########################################################################
|
| 78 |
+
# register function as a wrapper for all models
|
| 79 |
+
def register_model(cls):
|
| 80 |
+
global now_cls
|
| 81 |
+
now_cls = cls
|
| 82 |
+
return cls
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
now_cls = None
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class ModelInterface:
|
| 89 |
+
@classmethod
|
| 90 |
+
def init_model(cls, model_py_path: str, **kwargs):
|
| 91 |
+
"""
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
model_py_path: Py file Path of model you want to use.
|
| 95 |
+
**kwargs: Kwargs for model initialization
|
| 96 |
+
|
| 97 |
+
Returns: Corresponding model
|
| 98 |
+
"""
|
| 99 |
+
sub_dirs = model_py_path.replace("\\", "/").split("/")
|
| 100 |
+
cmd = f"from {'.' + '.'.join(sub_dirs[:-1])} import {sub_dirs[-1]}"
|
| 101 |
+
exec(cmd)
|
| 102 |
+
|
| 103 |
+
return now_cls(**kwargs)
|
model/saprot/base.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
from typing import List, Dict
|
| 4 |
+
# from data.pdb2feature import batch_coords2feature
|
| 5 |
+
from transformers import EsmConfig, EsmTokenizer, EsmForMaskedLM, EsmForSequenceClassification
|
| 6 |
+
# from module.esm.structure_module import (
|
| 7 |
+
# EsmForMaskedLMWithStructure as EsmForMaskedLM,
|
| 8 |
+
# EsmForSequenceClassificationWithStructure as EsmForSequenceClassification,
|
| 9 |
+
# )
|
| 10 |
+
from ..abstract_model import AbstractModel
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class SaprotBaseModel(AbstractModel):
|
| 14 |
+
"""
|
| 15 |
+
ESM base model. It cannot be used directly but provides model initialization for downstream tasks.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
def __init__(self,
|
| 19 |
+
task: str,
|
| 20 |
+
config_path: str,
|
| 21 |
+
extra_config: dict = None,
|
| 22 |
+
load_pretrained: bool = False,
|
| 23 |
+
freeze_backbone: bool = False,
|
| 24 |
+
use_lora: bool = False,
|
| 25 |
+
lora_config_path: str = None,
|
| 26 |
+
**kwargs):
|
| 27 |
+
"""
|
| 28 |
+
Args:
|
| 29 |
+
task: Task name. Must be one of ['classification', 'regression', 'lm', 'base']
|
| 30 |
+
|
| 31 |
+
config_path: Path to the config file of huggingface esm model
|
| 32 |
+
|
| 33 |
+
extra_config: Extra config for the model
|
| 34 |
+
|
| 35 |
+
load_pretrained: Whether to load pretrained weights of base model
|
| 36 |
+
|
| 37 |
+
freeze_backbone: Whether to freeze the backbone of the model
|
| 38 |
+
|
| 39 |
+
use_lora: Whether to use LoRA on downstream tasks
|
| 40 |
+
|
| 41 |
+
lora_config_path: Path to the config file of LoRA. If not None, LoRA model is for inference only.
|
| 42 |
+
Otherwise, LoRA model is for training.
|
| 43 |
+
|
| 44 |
+
**kwargs: Other arguments for AbstractModel
|
| 45 |
+
"""
|
| 46 |
+
assert task in ['classification', 'regression', 'lm', 'base']
|
| 47 |
+
self.task = task
|
| 48 |
+
self.config_path = config_path
|
| 49 |
+
self.extra_config = extra_config
|
| 50 |
+
self.load_pretrained = load_pretrained
|
| 51 |
+
self.freeze_backbone = freeze_backbone
|
| 52 |
+
super().__init__(**kwargs)
|
| 53 |
+
|
| 54 |
+
# After all initialization done, lora technique is applied if needed
|
| 55 |
+
self.use_lora = use_lora
|
| 56 |
+
if use_lora:
|
| 57 |
+
self._init_lora(lora_config_path)
|
| 58 |
+
|
| 59 |
+
def _init_lora(self, lora_config_path):
|
| 60 |
+
from peft import (
|
| 61 |
+
PeftModelForSequenceClassification,
|
| 62 |
+
get_peft_model,
|
| 63 |
+
LoraConfig,
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
if lora_config_path:
|
| 67 |
+
# Note that the model is for inference only
|
| 68 |
+
self.model = PeftModelForSequenceClassification.from_pretrained(self.model, lora_config_path)
|
| 69 |
+
self.model.merge_and_unload()
|
| 70 |
+
print("LoRA model is initialized for inference.")
|
| 71 |
+
|
| 72 |
+
else:
|
| 73 |
+
lora_config = {
|
| 74 |
+
"task_type": "SEQ_CLS",
|
| 75 |
+
"target_modules": ["query", "key", "value", "intermediate.dense", "output.dense"],
|
| 76 |
+
"modules_to_save": ["classifier"],
|
| 77 |
+
"inference_mode": False,
|
| 78 |
+
"lora_dropout": 0.1,
|
| 79 |
+
"lora_alpha": 8,
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
peft_config = LoraConfig(**lora_config)
|
| 83 |
+
self.model = get_peft_model(self.model, peft_config)
|
| 84 |
+
# original_module is not needed for training
|
| 85 |
+
self.model.classifier.original_module = None
|
| 86 |
+
|
| 87 |
+
print("LoRA model is initialized for training.")
|
| 88 |
+
self.model.print_trainable_parameters()
|
| 89 |
+
|
| 90 |
+
# After LoRA model is initialized, add trainable parameters to optimizer
|
| 91 |
+
self.init_optimizers()
|
| 92 |
+
|
| 93 |
+
def initialize_model(self):
|
| 94 |
+
# Initialize tokenizer
|
| 95 |
+
self.tokenizer = EsmTokenizer.from_pretrained(self.config_path)
|
| 96 |
+
|
| 97 |
+
# Initialize different models according to task
|
| 98 |
+
config = EsmConfig.from_pretrained(self.config_path)
|
| 99 |
+
|
| 100 |
+
# Add extra config if needed
|
| 101 |
+
if self.extra_config is None:
|
| 102 |
+
self.extra_config = {}
|
| 103 |
+
|
| 104 |
+
for k, v in self.extra_config.items():
|
| 105 |
+
setattr(config, k, v)
|
| 106 |
+
|
| 107 |
+
if self.task == 'classification':
|
| 108 |
+
# Note that self.num_labels should be set in child classes
|
| 109 |
+
if self.load_pretrained:
|
| 110 |
+
self.model = EsmForSequenceClassification.from_pretrained(
|
| 111 |
+
self.config_path, num_labels=self.num_labels, **self.extra_config)
|
| 112 |
+
|
| 113 |
+
else:
|
| 114 |
+
config.num_labels = self.num_labels
|
| 115 |
+
self.model = EsmForSequenceClassification(config)
|
| 116 |
+
|
| 117 |
+
elif self.task == 'regression':
|
| 118 |
+
if self.load_pretrained:
|
| 119 |
+
self.model = EsmForSequenceClassification.from_pretrained(
|
| 120 |
+
self.config_path, num_labels=1, **self.extra_config)
|
| 121 |
+
|
| 122 |
+
else:
|
| 123 |
+
config.num_labels = 1
|
| 124 |
+
self.model = EsmForSequenceClassification(config)
|
| 125 |
+
|
| 126 |
+
elif self.task == 'lm':
|
| 127 |
+
if self.load_pretrained:
|
| 128 |
+
self.model = EsmForMaskedLM.from_pretrained(self.config_path, **self.extra_config)
|
| 129 |
+
|
| 130 |
+
else:
|
| 131 |
+
self.model = EsmForMaskedLM(config)
|
| 132 |
+
|
| 133 |
+
elif self.task == 'base':
|
| 134 |
+
if self.load_pretrained:
|
| 135 |
+
self.model = EsmForMaskedLM.from_pretrained(self.config_path, **self.extra_config)
|
| 136 |
+
|
| 137 |
+
else:
|
| 138 |
+
self.model = EsmForMaskedLM(config)
|
| 139 |
+
|
| 140 |
+
# Remove lm_head as it is not needed for PPI task
|
| 141 |
+
self.model.lm_head = None
|
| 142 |
+
|
| 143 |
+
# Freeze the backbone of the model
|
| 144 |
+
if self.freeze_backbone:
|
| 145 |
+
for param in self.model.esm.parameters():
|
| 146 |
+
param.requires_grad = False
|
| 147 |
+
|
| 148 |
+
def initialize_metrics(self, stage: str) -> dict:
|
| 149 |
+
return {}
|
| 150 |
+
|
| 151 |
+
def get_hidden_states(self, inputs, reduction: str = None) -> list:
|
| 152 |
+
"""
|
| 153 |
+
Get hidden representations of the model.
|
| 154 |
+
|
| 155 |
+
Args:
|
| 156 |
+
inputs: A dictionary of inputs. It should contain keys ["input_ids", "attention_mask", "token_type_ids"].
|
| 157 |
+
reduction: Whether to reduce the hidden states. If None, the hidden states are not reduced. If "mean",
|
| 158 |
+
the hidden states are averaged over the sequence length.
|
| 159 |
+
|
| 160 |
+
Returns:
|
| 161 |
+
hidden_states: A list of tensors. Each tensor is of shape [L, D], where L is the sequence length and D is
|
| 162 |
+
the hidden dimension.
|
| 163 |
+
"""
|
| 164 |
+
inputs["output_hidden_states"] = True
|
| 165 |
+
outputs = self.model.esm(**inputs)
|
| 166 |
+
|
| 167 |
+
# Get the index of the first <eos> token
|
| 168 |
+
input_ids = inputs["input_ids"]
|
| 169 |
+
eos_id = self.tokenizer.eos_token_id
|
| 170 |
+
ends = (input_ids == eos_id).int()
|
| 171 |
+
indices = ends.argmax(dim=-1)
|
| 172 |
+
|
| 173 |
+
repr_list = []
|
| 174 |
+
hidden_states = outputs["hidden_states"][-1]
|
| 175 |
+
for i, idx in enumerate(indices):
|
| 176 |
+
if reduction == "mean":
|
| 177 |
+
repr = hidden_states[i][1:idx].mean(dim=0)
|
| 178 |
+
else:
|
| 179 |
+
repr = hidden_states[i][1:idx]
|
| 180 |
+
|
| 181 |
+
repr_list.append(repr)
|
| 182 |
+
|
| 183 |
+
return repr_list
|
| 184 |
+
|
| 185 |
+
# def add_bias_feature(self, inputs, coords: List[Dict]) -> torch.Tensor:
|
| 186 |
+
# """
|
| 187 |
+
# Add structure information as biases to attention map. This function is used to add structure information
|
| 188 |
+
# to the model as Evoformer does.
|
| 189 |
+
#
|
| 190 |
+
# Args:
|
| 191 |
+
# inputs: A dictionary of inputs. It should contain keys ["input_ids", "attention_mask", "token_type_ids"].
|
| 192 |
+
# coords: Coordinates of backbone atoms. Each element is a dictionary with keys ["N", "CA", "C", "O"].
|
| 193 |
+
#
|
| 194 |
+
# Returns
|
| 195 |
+
# pair_feature: A tensor of shape [B, L, L, 407]. Here 407 is the RBF of distance(400) + angle(7).
|
| 196 |
+
# """
|
| 197 |
+
# inputs["pair_feature"] = batch_coords2feature(coords, self.model.device)
|
| 198 |
+
# return inputs
|
| 199 |
+
|
| 200 |
+
def save_checkpoint(self, save_info: dict = None) -> None:
|
| 201 |
+
"""
|
| 202 |
+
Rewrite this function for saving LoRA parameters
|
| 203 |
+
"""
|
| 204 |
+
if not self.use_lora:
|
| 205 |
+
return super().save_checkpoint(save_info)
|
| 206 |
+
|
| 207 |
+
else:
|
| 208 |
+
self.model.save_pretrained(self.save_path)
|
| 209 |
+
|
| 210 |
+
|
model/saprot/esm_mutation_model.py
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import torch
|
| 3 |
+
import copy
|
| 4 |
+
import json
|
| 5 |
+
import torchmetrics
|
| 6 |
+
import torch.distributed as dist
|
| 7 |
+
|
| 8 |
+
from scripts.utils.constants import aa_set, aa_list
|
| 9 |
+
from ..model_interface import register_model
|
| 10 |
+
from .base import SaprotBaseModel
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@register_model
|
| 14 |
+
class EsmMutationModel(SaprotBaseModel):
|
| 15 |
+
def __init__(self,
|
| 16 |
+
use_bias_feature: bool = False,
|
| 17 |
+
MSA_log_path: str = None,
|
| 18 |
+
log_clinvar: bool = False,
|
| 19 |
+
log_dir: str = None,
|
| 20 |
+
**kwargs):
|
| 21 |
+
"""
|
| 22 |
+
Args:
|
| 23 |
+
use_bias_feature: Whether to use structure information as bias feature
|
| 24 |
+
|
| 25 |
+
MSA_log_path: If not None, the model will load MSA log from this path (following Tranception paper)
|
| 26 |
+
|
| 27 |
+
log_clinvar: If True, the model will log the predicted evolutionary indices for ClinVar variants
|
| 28 |
+
|
| 29 |
+
log_dir: If log_clinvar is True, the model will save the predicted evolutionary indices for ClinVar variants
|
| 30 |
+
|
| 31 |
+
**kwargs: other arguments for SaprotBaseModel
|
| 32 |
+
"""
|
| 33 |
+
self.use_bias_feature = use_bias_feature
|
| 34 |
+
self.MSA_log_path = MSA_log_path
|
| 35 |
+
self.MSA_info_dict = {}
|
| 36 |
+
if MSA_log_path:
|
| 37 |
+
with open(MSA_log_path, "r") as r:
|
| 38 |
+
for line in r:
|
| 39 |
+
data = json.loads(line)
|
| 40 |
+
data["MSA_log_prior"] = torch.tensor(data["MSA_log_prior"])
|
| 41 |
+
self.MSA_info_dict[data["DMS_id"]] = data
|
| 42 |
+
|
| 43 |
+
self.log_clinvar = log_clinvar
|
| 44 |
+
self.log_dir = log_dir
|
| 45 |
+
if log_clinvar:
|
| 46 |
+
self.mut_info_list = []
|
| 47 |
+
|
| 48 |
+
super().__init__(task="lm", **kwargs)
|
| 49 |
+
|
| 50 |
+
def initialize_metrics(self, stage):
|
| 51 |
+
return {f"{stage}_spearman": torchmetrics.SpearmanCorrCoef()}
|
| 52 |
+
|
| 53 |
+
def forward(self, wild_type, seqs, mut_info, structure_content, structure_type, plddt, struc_seq):
|
| 54 |
+
if self.use_bias_feature and getattr(self, "coords", None) is None:
|
| 55 |
+
structure_type = "cif" if structure_type == "mmcif" else structure_type
|
| 56 |
+
tmp_path = f"EsmMutationModel_{self.global_rank}.{structure_type}"
|
| 57 |
+
with open(tmp_path, "w") as f:
|
| 58 |
+
f.write(structure_content)
|
| 59 |
+
|
| 60 |
+
self.coords = parse_structure(tmp_path, ["A"])["A"]["coords"]
|
| 61 |
+
os.remove(tmp_path)
|
| 62 |
+
|
| 63 |
+
ins_seqs = []
|
| 64 |
+
ori_seqs = []
|
| 65 |
+
mut_data = []
|
| 66 |
+
|
| 67 |
+
# The running bottleneck is two forward passes of the model to deal with insertion
|
| 68 |
+
# Therefore we only forward pass the model twice for sequences with insertion
|
| 69 |
+
ins_dict = {}
|
| 70 |
+
|
| 71 |
+
for i, (seq, info) in enumerate(zip(seqs, mut_info)):
|
| 72 |
+
# We adopt the same strategy for esm2 model as in esm2 inverse folding paper
|
| 73 |
+
ori_seq = [aa for aa in wild_type]
|
| 74 |
+
ins_seq = copy.deepcopy(ori_seq)
|
| 75 |
+
tmp_data = []
|
| 76 |
+
ins_num = 0
|
| 77 |
+
|
| 78 |
+
# To indicate whether there is insertion in the sequence
|
| 79 |
+
flag = False
|
| 80 |
+
|
| 81 |
+
for single in info.split(":"):
|
| 82 |
+
# Mask the amino acid where the mutation happens
|
| 83 |
+
# -1 is added because the index starts from 1 and we need to convert it to 0
|
| 84 |
+
if single[0] in aa_set:
|
| 85 |
+
ori_aa, pos, mut_aa = single[0], int(single[1:-1]), single[-1]
|
| 86 |
+
ori_seq[pos - ins_num - 1] = self.tokenizer.mask_token
|
| 87 |
+
ins_seq[pos - 1] = self.tokenizer.mask_token
|
| 88 |
+
|
| 89 |
+
tmp_data.append((ori_aa, pos - ins_num, mut_aa, pos))
|
| 90 |
+
|
| 91 |
+
# For insertion
|
| 92 |
+
else:
|
| 93 |
+
ins_dict[i] = len(ins_dict)
|
| 94 |
+
flag = True
|
| 95 |
+
|
| 96 |
+
ins_num += 1
|
| 97 |
+
ins_pos = int(single[:-1])
|
| 98 |
+
ins_seq = ins_seq[:ins_pos - 1] + [self.tokenizer.mask_token] + ins_seq[ins_pos - 1:]
|
| 99 |
+
|
| 100 |
+
if flag:
|
| 101 |
+
ins_seqs.append(" ".join(ins_seq))
|
| 102 |
+
|
| 103 |
+
ori_seqs.append(" ".join(ori_seq))
|
| 104 |
+
mut_data.append(tmp_data)
|
| 105 |
+
|
| 106 |
+
device = self.device
|
| 107 |
+
|
| 108 |
+
if len(ins_seqs) > 0:
|
| 109 |
+
ins_inputs = self.tokenizer.batch_encode_plus(ins_seqs, return_tensors="pt", padding=True)
|
| 110 |
+
ins_inputs = {k: v.to(device) for k, v in ins_inputs.items()}
|
| 111 |
+
if self.use_bias_feature:
|
| 112 |
+
coords = [copy.deepcopy(self.coords) for _ in range(len(seqs))]
|
| 113 |
+
self.add_bias_feature(ins_inputs, coords)
|
| 114 |
+
|
| 115 |
+
ins_outputs = self.model(**ins_inputs)
|
| 116 |
+
ins_probs = ins_outputs['logits'].softmax(dim=-1)
|
| 117 |
+
|
| 118 |
+
ori_inputs = self.tokenizer.batch_encode_plus(ori_seqs, return_tensors="pt", padding=True)
|
| 119 |
+
ori_inputs = {k: v.to(device) for k, v in ori_inputs.items()}
|
| 120 |
+
if self.use_bias_feature:
|
| 121 |
+
coords = [copy.deepcopy(self.coords) for _ in range(len(seqs))]
|
| 122 |
+
self.add_bias_feature(ori_inputs, coords)
|
| 123 |
+
|
| 124 |
+
ori_outputs = self.model(**ori_inputs)
|
| 125 |
+
ori_probs = ori_outputs['logits'].softmax(dim=-1)
|
| 126 |
+
|
| 127 |
+
if self.MSA_log_path is not None:
|
| 128 |
+
aa2id = {"A": 5, "C": 6, "D": 7, "E": 8, "F": 9, "G": 10, "H": 11, "I": 12, "K": 13, "L": 14, "M": 15,
|
| 129 |
+
"N": 16, "P": 17, "Q": 18, "R": 19, "S": 20, "T": 21, "V": 22, "W": 23, "Y": 24}
|
| 130 |
+
DMS_id = os.path.basename(self.trainer.datamodule.test_lmdb)
|
| 131 |
+
MSA_info = self.MSA_info_dict[DMS_id]
|
| 132 |
+
MSA_log_prior = MSA_info["MSA_log_prior"].to(device)
|
| 133 |
+
st, ed = MSA_info["MSA_start"], MSA_info["MSA_end"]
|
| 134 |
+
|
| 135 |
+
preds = []
|
| 136 |
+
for i, data_list in enumerate(mut_data):
|
| 137 |
+
pred = 0
|
| 138 |
+
for data in data_list:
|
| 139 |
+
ori_aa, ori_pos, mut_aa, ins_pos = data
|
| 140 |
+
|
| 141 |
+
ori_prob = ori_probs[i, ori_pos, self.tokenizer.convert_tokens_to_ids(ori_aa)]
|
| 142 |
+
|
| 143 |
+
if i in ins_dict:
|
| 144 |
+
mut_prob = ins_probs[ins_dict[i], ins_pos, self.tokenizer.convert_tokens_to_ids(mut_aa)]
|
| 145 |
+
else:
|
| 146 |
+
mut_prob = ori_probs[i, ins_pos, self.tokenizer.convert_tokens_to_ids(mut_aa)]
|
| 147 |
+
|
| 148 |
+
# Add MSA info if available
|
| 149 |
+
if self.MSA_log_path is not None and st <= ori_pos -1 < ed:
|
| 150 |
+
ori_msa_prob = MSA_log_prior[ori_pos - 1 - st, aa2id[ori_aa]]
|
| 151 |
+
mut_msa_prob = MSA_log_prior[ori_pos - 1 - st, aa2id[mut_aa]]
|
| 152 |
+
pred += 0.4 * torch.log(mut_prob / ori_prob) + 0.6 * (mut_msa_prob - ori_msa_prob)
|
| 153 |
+
|
| 154 |
+
else:
|
| 155 |
+
# compute zero-shot score
|
| 156 |
+
pred += torch.log(mut_prob / ori_prob)
|
| 157 |
+
|
| 158 |
+
preds.append(pred)
|
| 159 |
+
|
| 160 |
+
if self.log_clinvar:
|
| 161 |
+
self.mut_info_list.append((mut_info, -torch.tensor(preds)))
|
| 162 |
+
|
| 163 |
+
return torch.tensor(preds).to(ori_probs)
|
| 164 |
+
|
| 165 |
+
def loss_func(self, stage, outputs, labels):
|
| 166 |
+
fitness = labels['labels']
|
| 167 |
+
self.test_spearman(outputs, fitness)
|
| 168 |
+
|
| 169 |
+
def on_test_epoch_end(self):
|
| 170 |
+
spearman = self.test_spearman.compute()
|
| 171 |
+
self.reset_metrics("test")
|
| 172 |
+
self.log("spearman", spearman)
|
| 173 |
+
if self.use_bias_feature:
|
| 174 |
+
self.coords = None
|
| 175 |
+
|
| 176 |
+
if self.log_clinvar:
|
| 177 |
+
# Get dataset name
|
| 178 |
+
name = os.path.basename(self.trainer.datamodule.test_lmdb)
|
| 179 |
+
device_rank = dist.get_rank()
|
| 180 |
+
log_path = f"{self.log_dir}/{name}_{device_rank}.csv"
|
| 181 |
+
with open(log_path, "w") as w:
|
| 182 |
+
w.write("protein_name,mutations,evol_indices\n")
|
| 183 |
+
|
| 184 |
+
for mut_info, preds in self.mut_info_list:
|
| 185 |
+
for mut, pred in zip(mut_info, preds):
|
| 186 |
+
w.write(f"{name},{mut},{pred}\n")
|
| 187 |
+
|
| 188 |
+
self.mut_info_list = []
|
| 189 |
+
|
| 190 |
+
def predict_mut(self, seq: str, mut_info: str) -> float:
|
| 191 |
+
"""
|
| 192 |
+
Predict the mutational effect of a given mutation
|
| 193 |
+
Args:
|
| 194 |
+
seq: The wild type sequence
|
| 195 |
+
|
| 196 |
+
mut_info: The mutation information in the format of "A123B", where A is the original amino acid, 123 is the
|
| 197 |
+
position and B is the mutated amino acid. If multiple mutations are provided, they should be
|
| 198 |
+
separated by colon, e.g. "A123B:C124D".
|
| 199 |
+
|
| 200 |
+
Returns:
|
| 201 |
+
The predicted mutational effect
|
| 202 |
+
"""
|
| 203 |
+
tokens = self.tokenizer.tokenize(seq)
|
| 204 |
+
for single in mut_info.split(":"):
|
| 205 |
+
pos = int(single[1:-1])
|
| 206 |
+
tokens[pos - 1] = self.tokenizer.mask_token
|
| 207 |
+
|
| 208 |
+
mask_seq = " ".join(tokens)
|
| 209 |
+
inputs = self.tokenizer(mask_seq, return_tensors="pt")
|
| 210 |
+
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 211 |
+
|
| 212 |
+
with torch.no_grad():
|
| 213 |
+
outputs = self.model(**inputs)
|
| 214 |
+
logits = outputs.logits
|
| 215 |
+
probs = logits.softmax(dim=-1)
|
| 216 |
+
|
| 217 |
+
score = 0
|
| 218 |
+
for single in mut_info.split(":"):
|
| 219 |
+
ori_aa, pos, mut_aa = single[0], int(single[1:-1]), single[-1]
|
| 220 |
+
ori_prob = probs[0, pos, self.tokenizer.convert_tokens_to_ids(ori_aa)]
|
| 221 |
+
mut_prob = probs[0, pos, self.tokenizer.convert_tokens_to_ids(mut_aa)]
|
| 222 |
+
|
| 223 |
+
score += torch.log(mut_prob / ori_prob)
|
| 224 |
+
|
| 225 |
+
return score
|
| 226 |
+
|
| 227 |
+
def predict_pos_mut(self, seq: str, pos: int) -> dict:
|
| 228 |
+
"""
|
| 229 |
+
Predict the mutational effect of mutations at a given position
|
| 230 |
+
Args:
|
| 231 |
+
seq: The wild type sequence
|
| 232 |
+
|
| 233 |
+
pos: The position of the mutation
|
| 234 |
+
|
| 235 |
+
Returns:
|
| 236 |
+
The predicted mutational effect
|
| 237 |
+
"""
|
| 238 |
+
tokens = self.tokenizer.tokenize(seq)
|
| 239 |
+
ori_aa = tokens[pos - 1][0]
|
| 240 |
+
tokens[pos - 1] = self.tokenizer.mask_token
|
| 241 |
+
|
| 242 |
+
mask_seq = " ".join(tokens)
|
| 243 |
+
inputs = self.tokenizer(mask_seq, return_tensors="pt")
|
| 244 |
+
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 245 |
+
|
| 246 |
+
with torch.no_grad():
|
| 247 |
+
outputs = self.model(**inputs)
|
| 248 |
+
logits = outputs.logits
|
| 249 |
+
probs = logits.softmax(dim=-1)[0, pos]
|
| 250 |
+
|
| 251 |
+
scores = {}
|
| 252 |
+
ori_prob = probs[self.tokenizer.convert_tokens_to_ids(ori_aa)]
|
| 253 |
+
for mut_aa in aa_list:
|
| 254 |
+
mut_prob = probs[self.tokenizer.convert_tokens_to_ids(mut_aa)]
|
| 255 |
+
score = torch.log(mut_prob / ori_prob).item()
|
| 256 |
+
scores[f"{ori_aa}{pos}{mut_aa}"] = score
|
| 257 |
+
|
| 258 |
+
return scores
|
| 259 |
+
|
| 260 |
+
def predict_pos_prob(self, seq: str, pos: int) -> dict:
|
| 261 |
+
"""
|
| 262 |
+
Predict the probability of all amino acids at a given position
|
| 263 |
+
Args:
|
| 264 |
+
seq: The wild type sequence
|
| 265 |
+
|
| 266 |
+
pos: The position of the mutation
|
| 267 |
+
|
| 268 |
+
Returns:
|
| 269 |
+
The predicted probability of all amino acids
|
| 270 |
+
"""
|
| 271 |
+
tokens = self.tokenizer.tokenize(seq)
|
| 272 |
+
tokens[pos - 1] = self.tokenizer.mask_token
|
| 273 |
+
|
| 274 |
+
mask_seq = " ".join(tokens)
|
| 275 |
+
inputs = self.tokenizer(mask_seq, return_tensors="pt")
|
| 276 |
+
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 277 |
+
|
| 278 |
+
with torch.no_grad():
|
| 279 |
+
outputs = self.model(**inputs)
|
| 280 |
+
logits = outputs.logits
|
| 281 |
+
probs = logits.softmax(dim=-1)[0, pos]
|
| 282 |
+
|
| 283 |
+
scores = {}
|
| 284 |
+
for aa in aa_list:
|
| 285 |
+
prob = probs[self.tokenizer.convert_tokens_to_ids(aa)]
|
| 286 |
+
scores[aa] = prob.item()
|
| 287 |
+
|
| 288 |
+
return scores
|
model/saprot/saprot_annotation_model.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torchmetrics
|
| 2 |
+
import torch
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
from torch.nn.functional import binary_cross_entropy_with_logits
|
| 6 |
+
from scripts.utils.metrics import count_f1_max
|
| 7 |
+
from ..model_interface import register_model
|
| 8 |
+
from .base import SaprotBaseModel
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@register_model
|
| 12 |
+
class SaprotAnnotationModel(SaprotBaseModel):
|
| 13 |
+
def __init__(self, anno_type: str, **kwargs):
|
| 14 |
+
"""
|
| 15 |
+
Args:
|
| 16 |
+
anno_type: one of EC, GO, GO_MF, GO_CC
|
| 17 |
+
**kwargs: other parameters for SaprotBaseModel
|
| 18 |
+
"""
|
| 19 |
+
label2num = {"EC": 585, "GO_BP": 1943, "GO_MF": 489, "GO_CC": 320}
|
| 20 |
+
self.num_labels = label2num[anno_type]
|
| 21 |
+
super().__init__(task="classification", **kwargs)
|
| 22 |
+
|
| 23 |
+
def initialize_metrics(self, stage):
|
| 24 |
+
return {f"{stage}_aupr": torchmetrics.AveragePrecision(pos_label=1, average='micro')}
|
| 25 |
+
|
| 26 |
+
def forward(self, inputs, coords=None):
|
| 27 |
+
if coords is not None:
|
| 28 |
+
inputs = self.add_bias_feature(inputs, coords)
|
| 29 |
+
|
| 30 |
+
# If backbone is frozen, the embedding will be the average of all residues
|
| 31 |
+
if self.freeze_backbone:
|
| 32 |
+
repr = torch.stack(self.get_hidden_states(inputs, reduction="mean"))
|
| 33 |
+
x = self.model.classifier.dropout(repr)
|
| 34 |
+
x = self.model.classifier.dense(x)
|
| 35 |
+
x = torch.tanh(x)
|
| 36 |
+
x = self.model.classifier.dropout(x)
|
| 37 |
+
logits = self.model.classifier.out_proj(x)
|
| 38 |
+
|
| 39 |
+
else:
|
| 40 |
+
logits = self.model(**inputs).logits
|
| 41 |
+
|
| 42 |
+
return logits
|
| 43 |
+
|
| 44 |
+
def loss_func(self, stage, logits, labels):
|
| 45 |
+
label = labels['labels'].to(logits)
|
| 46 |
+
# add weight to balance positive and negative samples
|
| 47 |
+
# num_pos = label.sum()
|
| 48 |
+
# pos_weight = (label.numel() - num_pos) / num_pos
|
| 49 |
+
|
| 50 |
+
loss = binary_cross_entropy_with_logits(logits, label.float())
|
| 51 |
+
aupr = getattr(self, f"{stage}_aupr")(logits.sigmoid().detach(), label)
|
| 52 |
+
|
| 53 |
+
if stage == "train":
|
| 54 |
+
log_dict = {"train_loss": loss,
|
| 55 |
+
# "train_aupr": aupr
|
| 56 |
+
}
|
| 57 |
+
self.log_info(log_dict)
|
| 58 |
+
self.reset_metrics("train")
|
| 59 |
+
|
| 60 |
+
return loss
|
| 61 |
+
|
| 62 |
+
def test_epoch_end(self, outputs):
|
| 63 |
+
preds = self.all_gather(torch.cat(self.test_aupr.preds, dim=-1)).view(-1, self.num_labels)
|
| 64 |
+
target = self.all_gather(torch.cat(self.test_aupr.target, dim=-1)).long().view(-1, self.num_labels)
|
| 65 |
+
fmax = count_f1_max(preds, target)
|
| 66 |
+
|
| 67 |
+
log_dict = {"test_f1_max": fmax,
|
| 68 |
+
"test_loss": torch.cat(self.all_gather(outputs), dim=-1).mean(),
|
| 69 |
+
# "test_aupr": self.test_aupr.compute()
|
| 70 |
+
}
|
| 71 |
+
self.log_info(log_dict)
|
| 72 |
+
print(log_dict)
|
| 73 |
+
self.reset_metrics("test")
|
| 74 |
+
|
| 75 |
+
def validation_epoch_end(self, outputs):
|
| 76 |
+
aupr = self.valid_aupr.compute()
|
| 77 |
+
|
| 78 |
+
preds = self.all_gather(torch.cat(self.valid_aupr.preds, dim=-1)).view(-1, self.num_labels)
|
| 79 |
+
target = self.all_gather(torch.cat(self.valid_aupr.target, dim=-1)).long().view(-1, self.num_labels)
|
| 80 |
+
f1_max = count_f1_max(preds, target)
|
| 81 |
+
|
| 82 |
+
log_dict = {"valid_f1_max": f1_max,
|
| 83 |
+
"valid_loss": torch.cat(self.all_gather(outputs), dim=-1).mean(),
|
| 84 |
+
# "valid_aupr": aupr
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
self.log_info(log_dict)
|
| 88 |
+
self.reset_metrics("valid")
|
| 89 |
+
self.check_save_condition(log_dict["valid_f1_max"], mode="max")
|
model/saprot/saprot_classification_model.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torchmetrics
|
| 2 |
+
import torch
|
| 3 |
+
|
| 4 |
+
from torch.nn.functional import cross_entropy
|
| 5 |
+
from ..model_interface import register_model
|
| 6 |
+
from .base import SaprotBaseModel
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@register_model
|
| 10 |
+
class SaprotClassificationModel(SaprotBaseModel):
|
| 11 |
+
def __init__(self, num_labels: int, **kwargs):
|
| 12 |
+
"""
|
| 13 |
+
Args:
|
| 14 |
+
num_labels: number of labels
|
| 15 |
+
**kwargs: other arguments for SaprotBaseModel
|
| 16 |
+
"""
|
| 17 |
+
self.num_labels = num_labels
|
| 18 |
+
super().__init__(task="classification", **kwargs)
|
| 19 |
+
|
| 20 |
+
def initialize_metrics(self, stage):
|
| 21 |
+
return {f"{stage}_acc": torchmetrics.Accuracy()}
|
| 22 |
+
|
| 23 |
+
def forward(self, inputs, coords=None):
|
| 24 |
+
if coords is not None:
|
| 25 |
+
inputs = self.add_bias_feature(inputs, coords)
|
| 26 |
+
|
| 27 |
+
# If backbone is frozen, the embedding will be the average of all residues
|
| 28 |
+
if self.freeze_backbone:
|
| 29 |
+
repr = torch.stack(self.get_hidden_states(inputs, reduction="mean"))
|
| 30 |
+
x = self.model.classifier.dropout(repr)
|
| 31 |
+
x = self.model.classifier.dense(x)
|
| 32 |
+
x = torch.tanh(x)
|
| 33 |
+
x = self.model.classifier.dropout(x)
|
| 34 |
+
logits = self.model.classifier.out_proj(x)
|
| 35 |
+
|
| 36 |
+
else:
|
| 37 |
+
logits = self.model(**inputs).logits
|
| 38 |
+
|
| 39 |
+
return logits
|
| 40 |
+
|
| 41 |
+
def loss_func(self, stage, logits, labels):
|
| 42 |
+
label = labels['labels']
|
| 43 |
+
loss = cross_entropy(logits, label)
|
| 44 |
+
|
| 45 |
+
# Update metrics
|
| 46 |
+
for metric in self.metrics[stage].values():
|
| 47 |
+
metric.update(logits.detach(), label)
|
| 48 |
+
|
| 49 |
+
if stage == "train":
|
| 50 |
+
log_dict = self.get_log_dict("train")
|
| 51 |
+
log_dict["train_loss"] = loss
|
| 52 |
+
self.log_info(log_dict)
|
| 53 |
+
|
| 54 |
+
# Reset train metrics
|
| 55 |
+
self.reset_metrics("train")
|
| 56 |
+
|
| 57 |
+
return loss
|
| 58 |
+
|
| 59 |
+
def test_epoch_end(self, outputs):
|
| 60 |
+
log_dict = self.get_log_dict("test")
|
| 61 |
+
log_dict["test_loss"] = torch.cat(self.all_gather(outputs), dim=-1).mean()
|
| 62 |
+
|
| 63 |
+
print(log_dict)
|
| 64 |
+
self.log_info(log_dict)
|
| 65 |
+
|
| 66 |
+
self.reset_metrics("test")
|
| 67 |
+
|
| 68 |
+
def validation_epoch_end(self, outputs):
|
| 69 |
+
log_dict = self.get_log_dict("valid")
|
| 70 |
+
log_dict["valid_loss"] = torch.cat(self.all_gather(outputs), dim=-1).mean()
|
| 71 |
+
|
| 72 |
+
self.log_info(log_dict)
|
| 73 |
+
self.reset_metrics("valid")
|
| 74 |
+
self.check_save_condition(log_dict["valid_acc"], mode="max")
|
model/saprot/saprot_contact_model.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torchmetrics
|
| 2 |
+
import torch
|
| 3 |
+
import numpy as np
|
| 4 |
+
import math
|
| 5 |
+
|
| 6 |
+
from torch.nn import Linear, ReLU
|
| 7 |
+
from torch.nn.functional import cross_entropy
|
| 8 |
+
from ..model_interface import register_model
|
| 9 |
+
from .base import SaprotBaseModel
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@register_model
|
| 13 |
+
class SaprotContactModel(SaprotBaseModel):
|
| 14 |
+
def __init__(self, **kwargs):
|
| 15 |
+
"""
|
| 16 |
+
Args:
|
| 17 |
+
num_labels: number of labels
|
| 18 |
+
**kwargs: other arguments for SaprotBaseModel
|
| 19 |
+
"""
|
| 20 |
+
super().__init__(task="base", **kwargs)
|
| 21 |
+
|
| 22 |
+
def initialize_model(self):
|
| 23 |
+
super().initialize_model()
|
| 24 |
+
|
| 25 |
+
# hidden_size = self.model.config.hidden_size * 2
|
| 26 |
+
hidden_size = self.model.config.num_attention_heads
|
| 27 |
+
# hidden_size = self.model.config.num_hidden_layers * self.model.config.num_attention_heads
|
| 28 |
+
|
| 29 |
+
classifier = torch.nn.Sequential(
|
| 30 |
+
# Linear(hidden_size, hidden_size),
|
| 31 |
+
# ReLU(),
|
| 32 |
+
Linear(hidden_size, 2)
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
# Freeze all parameters except classifier
|
| 36 |
+
for param in self.model.parameters():
|
| 37 |
+
param.requires_grad = False
|
| 38 |
+
|
| 39 |
+
setattr(self.model, "classifier", classifier)
|
| 40 |
+
|
| 41 |
+
def initialize_metrics(self, stage):
|
| 42 |
+
metric_dict = {}
|
| 43 |
+
for length in ["P@L", "P@L/2", "P@L/5"]:
|
| 44 |
+
for range in ["short_range", "medium_range", "long_range"]:
|
| 45 |
+
metric_dict[f"{stage}_{range}_{length}"] = torchmetrics.Accuracy(ignore_index=-1)
|
| 46 |
+
|
| 47 |
+
return metric_dict
|
| 48 |
+
|
| 49 |
+
def forward(self, inputs):
|
| 50 |
+
# inputs["output_hidden_states"] = True
|
| 51 |
+
# outputs = self.model.esm(**inputs)
|
| 52 |
+
#
|
| 53 |
+
# hidden_states = outputs["hidden_states"][-1]
|
| 54 |
+
# prod = hidden_states[:, :, None, :] * hidden_states[:, None, :, :]
|
| 55 |
+
# diff = hidden_states[:, :, None, :] - hidden_states[:, None, :, :]
|
| 56 |
+
# pairwise_features = torch.cat((prod, diff), -1)
|
| 57 |
+
# pairwise_features = (pairwise_features + pairwise_features.transpose(1, 2)) / 2
|
| 58 |
+
#
|
| 59 |
+
# logits = self.model.classifier(pairwise_features)
|
| 60 |
+
# logits = logits[:, 1: -1, 1: -1].contiguous()
|
| 61 |
+
#
|
| 62 |
+
# return logits
|
| 63 |
+
|
| 64 |
+
inputs["output_attentions"] = True
|
| 65 |
+
outputs = self.model.esm(**inputs)
|
| 66 |
+
|
| 67 |
+
attention_maps = torch.cat(outputs["attentions"][-1:], 1).permute(0, 2, 3, 1)
|
| 68 |
+
# attention_maps = torch.cat(outputs["attentions"], 1).permute(0, 2, 3, 1)
|
| 69 |
+
attention_maps = (attention_maps + attention_maps.transpose(1, 2)) / 2
|
| 70 |
+
logits = self.model.classifier(attention_maps)
|
| 71 |
+
logits = logits[:, 1: -1, 1: -1].contiguous()
|
| 72 |
+
|
| 73 |
+
return logits
|
| 74 |
+
|
| 75 |
+
def loss_func(self, stage, logits, labels):
|
| 76 |
+
lengths = labels["lengths"]
|
| 77 |
+
targets = labels["targets"].to(logits.device)
|
| 78 |
+
loss = cross_entropy(logits.view(-1, logits.size(-1)), targets.flatten(), ignore_index=-1)
|
| 79 |
+
|
| 80 |
+
# Iterate through all proteins and count accuracy
|
| 81 |
+
length_dict = {"P@L": 1, "P@L/2": 2, "P@L/5": 5}
|
| 82 |
+
range_dict = ["short_range", "medium_range", "long_range"]
|
| 83 |
+
for pred_map, label_map, L in zip(logits.detach(), targets, lengths):
|
| 84 |
+
x_inds, y_inds = np.indices(label_map.shape)
|
| 85 |
+
for r in range_dict:
|
| 86 |
+
if r == "short_range":
|
| 87 |
+
mask = (np.abs(y_inds - x_inds) < 6) | (np.abs(y_inds - x_inds) > 11)
|
| 88 |
+
|
| 89 |
+
elif r == "medium_range":
|
| 90 |
+
mask = (np.abs(y_inds - x_inds) < 12) | (np.abs(y_inds - x_inds) > 23)
|
| 91 |
+
|
| 92 |
+
else:
|
| 93 |
+
mask = np.abs(y_inds - x_inds) < 24
|
| 94 |
+
|
| 95 |
+
mask = torch.from_numpy(mask)
|
| 96 |
+
copy_label_map = label_map.clone()
|
| 97 |
+
copy_label_map[mask] = -1
|
| 98 |
+
|
| 99 |
+
# Mask the lower triangle
|
| 100 |
+
mask = torch.triu(torch.ones_like(copy_label_map), diagonal=1)
|
| 101 |
+
copy_label_map[mask == 0] = -1
|
| 102 |
+
|
| 103 |
+
selector = copy_label_map != -1
|
| 104 |
+
preds = pred_map[selector].float()
|
| 105 |
+
labels = copy_label_map[selector]
|
| 106 |
+
|
| 107 |
+
probs = preds.softmax(dim=-1)[:, 1]
|
| 108 |
+
for k, v in length_dict.items():
|
| 109 |
+
l = min(math.ceil(L / v), (labels == 1).sum().item())
|
| 110 |
+
|
| 111 |
+
top_inds = torch.argsort(probs, descending=True)[:l]
|
| 112 |
+
top_labels = labels[top_inds]
|
| 113 |
+
|
| 114 |
+
if top_labels.numel() == 0:
|
| 115 |
+
continue
|
| 116 |
+
|
| 117 |
+
metric = f"{stage}_{r}_{k}"
|
| 118 |
+
self.metrics[stage][metric].update(top_labels, torch.ones_like(top_labels))
|
| 119 |
+
|
| 120 |
+
if stage == "train":
|
| 121 |
+
# log_dict = self.get_log_dict("train")
|
| 122 |
+
# log_dict["train_loss"] = loss
|
| 123 |
+
# self.log_info(log_dict)
|
| 124 |
+
|
| 125 |
+
# Reset train metrics
|
| 126 |
+
self.reset_metrics("train")
|
| 127 |
+
|
| 128 |
+
return loss
|
| 129 |
+
|
| 130 |
+
def test_epoch_end(self, outputs):
|
| 131 |
+
log_dict = self.get_log_dict("test")
|
| 132 |
+
log_dict["test_loss"] = torch.cat(self.all_gather(outputs), dim=-1).mean()
|
| 133 |
+
|
| 134 |
+
print(log_dict)
|
| 135 |
+
self.log_info(log_dict)
|
| 136 |
+
|
| 137 |
+
self.reset_metrics("test")
|
| 138 |
+
|
| 139 |
+
def validation_epoch_end(self, outputs):
|
| 140 |
+
log_dict = self.get_log_dict("valid")
|
| 141 |
+
log_dict["valid_loss"] = torch.cat(self.all_gather(outputs), dim=-1).mean()
|
| 142 |
+
|
| 143 |
+
self.log_info(log_dict)
|
| 144 |
+
self.reset_metrics("valid")
|
| 145 |
+
self.check_save_condition(log_dict["valid_medium_range_P@L/5"], mode="max")
|
model/saprot/saprot_foldseek_mutation_model.py
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import copy
|
| 2 |
+
import os
|
| 3 |
+
import torch
|
| 4 |
+
import json
|
| 5 |
+
import torchmetrics
|
| 6 |
+
import random
|
| 7 |
+
import numpy as np
|
| 8 |
+
|
| 9 |
+
from scripts.utils.constants import aa_set, foldseek_struc_vocab, aa_list
|
| 10 |
+
from ..model_interface import register_model
|
| 11 |
+
from .base import SaprotBaseModel
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@register_model
|
| 15 |
+
class SaprotFoldseekMutationModel(SaprotBaseModel):
|
| 16 |
+
def __init__(self,
|
| 17 |
+
foldseek_path: str,
|
| 18 |
+
plddt_threshold: float = 0.,
|
| 19 |
+
mask_rate: float = None,
|
| 20 |
+
substitute_rate: float = None,
|
| 21 |
+
MSA_log_path: str = None,
|
| 22 |
+
log_clinvar: bool = False,
|
| 23 |
+
log_dir: str = None,
|
| 24 |
+
**kwargs):
|
| 25 |
+
"""
|
| 26 |
+
Args:
|
| 27 |
+
foldseek_path: Foldseek binary file path
|
| 28 |
+
|
| 29 |
+
plddt_threshold: The threshold for plddt to determine whether a structure token should be masked
|
| 30 |
+
|
| 31 |
+
mask_rate: If not None, the model will randomly mask structure tokens with this rate
|
| 32 |
+
|
| 33 |
+
substitute_rate: If not None, the model will randomly substitute structure tokens with this rate
|
| 34 |
+
|
| 35 |
+
MSA_log_path: If not None, the model will load MSA log from this path (follow Tranception paper)
|
| 36 |
+
|
| 37 |
+
log_clinvar: If True, the model will log the predicted evolutionary indices for ClinVar variants
|
| 38 |
+
|
| 39 |
+
log_dir: If log_clinvar is True, the model will save the predicted evolutionary indices for ClinVar variants
|
| 40 |
+
|
| 41 |
+
**kwargs: Other arguments for SaprotBaseModel
|
| 42 |
+
"""
|
| 43 |
+
self.foldseek_path = foldseek_path
|
| 44 |
+
self.plddt_threshold = plddt_threshold
|
| 45 |
+
self.mask_rate = mask_rate
|
| 46 |
+
self.substitute_rate = substitute_rate
|
| 47 |
+
|
| 48 |
+
self.MSA_log_path = MSA_log_path
|
| 49 |
+
self.MSA_info_dict = {}
|
| 50 |
+
if MSA_log_path:
|
| 51 |
+
with open(MSA_log_path, "r") as r:
|
| 52 |
+
for line in r:
|
| 53 |
+
data = json.loads(line)
|
| 54 |
+
data["MSA_log_prior"] = torch.tensor(data["MSA_log_prior"])
|
| 55 |
+
self.MSA_info_dict[data["DMS_id"]] = data
|
| 56 |
+
|
| 57 |
+
self.log_clinvar = log_clinvar
|
| 58 |
+
self.log_dir = log_dir
|
| 59 |
+
if log_clinvar:
|
| 60 |
+
self.mut_info_list = []
|
| 61 |
+
|
| 62 |
+
super().__init__(task="lm", **kwargs)
|
| 63 |
+
|
| 64 |
+
def initialize_metrics(self, stage):
|
| 65 |
+
return {f"{stage}_spearman": torchmetrics.SpearmanCorrCoef()}
|
| 66 |
+
|
| 67 |
+
def get_struc_seq(self, structure_content, structure_type, plddt):
|
| 68 |
+
structure_type = "cif" if structure_type == "mmcif" else structure_type
|
| 69 |
+
|
| 70 |
+
# Sample a random rank to avoid file conflict
|
| 71 |
+
rank = random.randint(0, 1000000)
|
| 72 |
+
|
| 73 |
+
tmp_pdb_path = f"EsmFoldseekMutationModel_{self.global_rank}_{rank}.{structure_type}"
|
| 74 |
+
tmp_save_path = f"EsmFoldseekMutationModel_{self.global_rank}_{rank}.tsv"
|
| 75 |
+
|
| 76 |
+
# Save structure content to temporary file
|
| 77 |
+
with open(tmp_pdb_path, "w") as w:
|
| 78 |
+
w.write(structure_content)
|
| 79 |
+
|
| 80 |
+
# Get foldseek structural sequecne
|
| 81 |
+
cmd = f"{self.foldseek_path} structureto3didescriptor -v 0 --threads 1 {tmp_pdb_path} {tmp_save_path}"
|
| 82 |
+
os.system(cmd)
|
| 83 |
+
|
| 84 |
+
with open(tmp_save_path, "r") as r:
|
| 85 |
+
line = r.readline()
|
| 86 |
+
struc_seq = line.split("\t")[2]
|
| 87 |
+
|
| 88 |
+
if plddt is not None:
|
| 89 |
+
plddts = np.array(plddt)
|
| 90 |
+
|
| 91 |
+
# Mask regions with plddt < threshold
|
| 92 |
+
indices = np.where(plddts < self.plddt_threshold)[0]
|
| 93 |
+
np_seq = np.array(list(struc_seq))
|
| 94 |
+
np_seq[indices] = "#"
|
| 95 |
+
struc_seq = "".join(np_seq)
|
| 96 |
+
|
| 97 |
+
if self.mask_rate is not None:
|
| 98 |
+
# Mask random structure tokens
|
| 99 |
+
indices = np.random.choice(len(struc_seq), int(len(struc_seq) * self.mask_rate), replace=False)
|
| 100 |
+
np_seq = np.array(list(struc_seq))
|
| 101 |
+
np_seq[indices] = "#"
|
| 102 |
+
struc_seq = "".join(np_seq)
|
| 103 |
+
|
| 104 |
+
if self.substitute_rate is not None:
|
| 105 |
+
# Substitute random structure tokens
|
| 106 |
+
indices = np.random.choice(len(struc_seq), int(len(struc_seq) * self.substitute_rate), replace=False)
|
| 107 |
+
np_seq = np.array(list(struc_seq))
|
| 108 |
+
np_seq[indices] = np.random.choice(list(foldseek_struc_vocab), len(indices))
|
| 109 |
+
struc_seq = "".join(np_seq)
|
| 110 |
+
|
| 111 |
+
os.remove(tmp_pdb_path)
|
| 112 |
+
os.remove(tmp_save_path)
|
| 113 |
+
os.remove(tmp_save_path + ".dbtype")
|
| 114 |
+
return struc_seq
|
| 115 |
+
|
| 116 |
+
def forward(self, wild_type, seqs, mut_info, structure_content, structure_type, plddt):
|
| 117 |
+
device = self.device
|
| 118 |
+
|
| 119 |
+
if getattr(self, "struc_seq", None) is None:
|
| 120 |
+
self.struc_seq = self.get_struc_seq(structure_content, structure_type, plddt)
|
| 121 |
+
|
| 122 |
+
ins_seqs = []
|
| 123 |
+
ori_seqs = []
|
| 124 |
+
mut_data = []
|
| 125 |
+
|
| 126 |
+
# The running bottleneck is two forward passes of the model to deal with insertion
|
| 127 |
+
# Therefore we only forward pass the model twice for sequences with insertion
|
| 128 |
+
ins_dict = {}
|
| 129 |
+
|
| 130 |
+
for i, (seq, info) in enumerate(zip(seqs, mut_info)):
|
| 131 |
+
# We adopt the same strategy for esm2 model as in esm2 inverse folding paper
|
| 132 |
+
ori_seq = [a+b.lower() for a, b in zip(wild_type, self.struc_seq)]
|
| 133 |
+
ins_seq = copy.deepcopy(ori_seq)
|
| 134 |
+
tmp_data = []
|
| 135 |
+
ins_num = 0
|
| 136 |
+
|
| 137 |
+
# To indicate whether there is insertion in the sequence
|
| 138 |
+
flag = False
|
| 139 |
+
|
| 140 |
+
for single in info.split(":"):
|
| 141 |
+
# Mask the amino acid where the mutation happens
|
| 142 |
+
# -1 is added because the index starts from 1 and we need to convert it to 0
|
| 143 |
+
if single[0] in aa_set:
|
| 144 |
+
ori_aa, pos, mut_aa = single[0], int(single[1:-1]), single[-1]
|
| 145 |
+
|
| 146 |
+
ori_seq[pos-ins_num-1] = "#" + ori_seq[pos-ins_num-1][-1]
|
| 147 |
+
ins_seq[pos-1] = "#" + ins_seq[pos-1][-1]
|
| 148 |
+
# ori_seq[pos - ins_num - 1] = self.tokenizer.mask_token
|
| 149 |
+
# ins_seq[pos - 1] = self.tokenizer.mask_token
|
| 150 |
+
|
| 151 |
+
tmp_data.append((ori_aa, pos-ins_num, mut_aa, pos))
|
| 152 |
+
|
| 153 |
+
# For insertion
|
| 154 |
+
else:
|
| 155 |
+
ins_dict[i] = len(ins_dict)
|
| 156 |
+
flag = True
|
| 157 |
+
|
| 158 |
+
ins_num += 1
|
| 159 |
+
ins_pos = int(single[:-1])
|
| 160 |
+
ins_seq = ins_seq[:ins_pos-1] + ["##"] + ins_seq[ins_pos-1:]
|
| 161 |
+
# ins_seq = ins_seq[:ins_pos-1] + [self.tokenizer.mask_token] + ins_seq[ins_pos-1:]
|
| 162 |
+
|
| 163 |
+
if flag:
|
| 164 |
+
ins_seqs.append(" ".join(ins_seq))
|
| 165 |
+
|
| 166 |
+
ori_seqs.append(" ".join(ori_seq))
|
| 167 |
+
mut_data.append(tmp_data)
|
| 168 |
+
|
| 169 |
+
if len(ins_seqs) > 0:
|
| 170 |
+
ins_inputs = self.tokenizer.batch_encode_plus(ins_seqs, return_tensors="pt", padding=True)
|
| 171 |
+
ins_inputs = {k: v.to(device) for k, v in ins_inputs.items()}
|
| 172 |
+
ins_outputs = self.model(**ins_inputs)
|
| 173 |
+
ins_probs = ins_outputs['logits'].softmax(dim=-1)
|
| 174 |
+
|
| 175 |
+
ori_inputs = self.tokenizer.batch_encode_plus(ori_seqs, return_tensors="pt", padding=True)
|
| 176 |
+
ori_inputs = {k: v.to(device) for k, v in ori_inputs.items()}
|
| 177 |
+
ori_outputs = self.model(**ori_inputs)
|
| 178 |
+
ori_probs = ori_outputs['logits'].softmax(dim=-1)
|
| 179 |
+
|
| 180 |
+
if self.MSA_log_path is not None:
|
| 181 |
+
aa2id = {"A": 5, "C": 6, "D": 7, "E": 8, "F": 9, "G": 10, "H": 11, "I": 12, "K": 13, "L": 14, "M": 15,
|
| 182 |
+
"N": 16, "P": 17, "Q": 18, "R": 19, "S": 20, "T": 21, "V": 22, "W": 23, "Y": 24}
|
| 183 |
+
DMS_id = os.path.basename(self.trainer.datamodule.test_lmdb)
|
| 184 |
+
MSA_info = self.MSA_info_dict[DMS_id]
|
| 185 |
+
MSA_log_prior = MSA_info["MSA_log_prior"].to(device)
|
| 186 |
+
st, ed = MSA_info["MSA_start"], MSA_info["MSA_end"]
|
| 187 |
+
|
| 188 |
+
preds = []
|
| 189 |
+
for i, data_list in enumerate(mut_data):
|
| 190 |
+
pred = 0
|
| 191 |
+
for data in data_list:
|
| 192 |
+
ori_aa, ori_pos, mut_aa, ins_pos = data
|
| 193 |
+
|
| 194 |
+
ori_st = self.tokenizer.get_vocab()[ori_aa + foldseek_struc_vocab[0]]
|
| 195 |
+
mut_st = self.tokenizer.get_vocab()[mut_aa + foldseek_struc_vocab[0]]
|
| 196 |
+
|
| 197 |
+
ori_prob = ori_probs[i, ori_pos, ori_st: ori_st + len(foldseek_struc_vocab)].sum()
|
| 198 |
+
# ori_sturc_aa = ori_aa + self.struc_seq[ori_pos-1].lower()
|
| 199 |
+
# ori_struc_id = self.tokenizer.get_vocab()[ori_sturc_aa]
|
| 200 |
+
# ori_prob = ori_probs[i, ori_pos, ori_struc_id]
|
| 201 |
+
|
| 202 |
+
if i in ins_dict:
|
| 203 |
+
mut_prob = ins_probs[ins_dict[i], ins_pos, mut_st: mut_st + len(foldseek_struc_vocab)].sum()
|
| 204 |
+
# mut_prob = ins_probs[ins_dict[i], ins_pos, mut_st: mut_st + len(foldseek_struc_vocab)].max()
|
| 205 |
+
else:
|
| 206 |
+
mut_prob = ori_probs[i, ori_pos, mut_st: mut_st + len(foldseek_struc_vocab)].sum()
|
| 207 |
+
# mut_prob = ori_probs[i, ins_pos, mut_st: mut_st + len(foldseek_struc_vocab)].max()
|
| 208 |
+
# mut_struc_aa = mut_aa + self.struc_seq[ori_pos-1].lower()
|
| 209 |
+
# mut_struc_id = self.tokenizer.get_vocab()[mut_struc_aa]
|
| 210 |
+
# mut_prob = ori_probs[i, ori_pos, mut_struc_id]
|
| 211 |
+
|
| 212 |
+
# print(ori_prob, mut_prob)
|
| 213 |
+
# struc_logits = []
|
| 214 |
+
# ori_idx, mut_idx = None, None
|
| 215 |
+
# for aa in aa_list:
|
| 216 |
+
# struct_aa = aa + self.struc_seq[ori_pos - 1].lower()
|
| 217 |
+
# aa_logits = ori_outputs['logits'][i, ori_pos, self.tokenizer.get_vocab()[struct_aa]]
|
| 218 |
+
# struc_logits.append(aa_logits)
|
| 219 |
+
#
|
| 220 |
+
# if aa == ori_aa:
|
| 221 |
+
# ori_idx = len(struc_logits) - 1
|
| 222 |
+
#
|
| 223 |
+
# if aa == mut_aa:
|
| 224 |
+
# mut_idx = len(struc_logits) - 1
|
| 225 |
+
#
|
| 226 |
+
# struc_probs = torch.softmax(torch.tensor(struc_logits), dim=-1)
|
| 227 |
+
# ori_prob = struc_probs[ori_idx]
|
| 228 |
+
# mut_prob = struc_probs[mut_idx]
|
| 229 |
+
|
| 230 |
+
# Add MSA info if available
|
| 231 |
+
if self.MSA_log_path is not None and st <= ori_pos -1 < ed:
|
| 232 |
+
ori_msa_prob = MSA_log_prior[ori_pos - 1 - st, aa2id[ori_aa]]
|
| 233 |
+
mut_msa_prob = MSA_log_prior[ori_pos - 1 - st, aa2id[mut_aa]]
|
| 234 |
+
pred += 0.4 * torch.log(mut_prob / ori_prob) + 0.6 * (mut_msa_prob - ori_msa_prob)
|
| 235 |
+
|
| 236 |
+
# compute zero-shot score
|
| 237 |
+
else:
|
| 238 |
+
pred += torch.log(mut_prob / ori_prob)
|
| 239 |
+
# ori_prob = ori_probs[i, ori_pos, ori_st: ori_st + len(foldseek_struc_vocab)]
|
| 240 |
+
# mut_prob = ori_probs[i, ori_pos, mut_st: mut_st + len(foldseek_struc_vocab)]
|
| 241 |
+
# pred += torch.log(mut_prob / ori_prob).mean()
|
| 242 |
+
|
| 243 |
+
preds.append(pred)
|
| 244 |
+
|
| 245 |
+
if self.log_clinvar:
|
| 246 |
+
self.mut_info_list.append((mut_info, -torch.tensor(preds)))
|
| 247 |
+
|
| 248 |
+
return torch.tensor(preds).to(ori_probs)
|
| 249 |
+
|
| 250 |
+
def loss_func(self, stage, outputs, labels):
|
| 251 |
+
fitness = labels['labels']
|
| 252 |
+
|
| 253 |
+
# Update metrics
|
| 254 |
+
for metric in self.metrics[stage].values():
|
| 255 |
+
metric.update(outputs.detach().float(), fitness.float())
|
| 256 |
+
|
| 257 |
+
def test_epoch_end(self, outputs):
|
| 258 |
+
spearman = self.test_spearman.compute()
|
| 259 |
+
self.struc_seq = None
|
| 260 |
+
self.reset_metrics("test")
|
| 261 |
+
self.log("spearman", spearman)
|
| 262 |
+
|
| 263 |
+
if self.log_clinvar:
|
| 264 |
+
# Get dataset name
|
| 265 |
+
name = os.path.basename(self.trainer.datamodule.test_lmdb)
|
| 266 |
+
log_path = f"{self.log_dir}/{name}.csv"
|
| 267 |
+
with open(log_path, "w") as w:
|
| 268 |
+
w.write("protein_name,mutations,evol_indices\n")
|
| 269 |
+
|
| 270 |
+
for mut_info, preds in self.mut_info_list:
|
| 271 |
+
for mut, pred in zip(mut_info, preds):
|
| 272 |
+
w.write(f"{name},{mut},{pred}\n")
|
| 273 |
+
|
| 274 |
+
self.mut_info_list = []
|
| 275 |
+
|
| 276 |
+
def predict_mut(self, seq: str, mut_info: str) -> float:
|
| 277 |
+
"""
|
| 278 |
+
Predict the mutational effect of a given mutation
|
| 279 |
+
Args:
|
| 280 |
+
seq: The wild type sequence
|
| 281 |
+
|
| 282 |
+
mut_info: The mutation information in the format of "A123B", where A is the original amino acid, 123 is the
|
| 283 |
+
position and B is the mutated amino acid. If multiple mutations are provided, they should be
|
| 284 |
+
separated by colon, e.g. "A123B:C124D".
|
| 285 |
+
|
| 286 |
+
Returns:
|
| 287 |
+
The predicted mutational effect
|
| 288 |
+
"""
|
| 289 |
+
tokens = self.tokenizer.tokenize(seq)
|
| 290 |
+
for single in mut_info.split(":"):
|
| 291 |
+
pos = int(single[1:-1])
|
| 292 |
+
tokens[pos - 1] = "#" + tokens[pos - 1][-1]
|
| 293 |
+
|
| 294 |
+
mask_seq = " ".join(tokens)
|
| 295 |
+
inputs = self.tokenizer(mask_seq, return_tensors="pt")
|
| 296 |
+
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 297 |
+
|
| 298 |
+
with torch.no_grad():
|
| 299 |
+
outputs = self.model(**inputs)
|
| 300 |
+
logits = outputs.logits
|
| 301 |
+
probs = logits.softmax(dim=-1)
|
| 302 |
+
|
| 303 |
+
score = 0
|
| 304 |
+
for single in mut_info.split(":"):
|
| 305 |
+
ori_aa, pos, mut_aa = single[0], int(single[1:-1]), single[-1]
|
| 306 |
+
ori_st = self.tokenizer.get_vocab()[ori_aa + foldseek_struc_vocab[0]]
|
| 307 |
+
mut_st = self.tokenizer.get_vocab()[mut_aa + foldseek_struc_vocab[0]]
|
| 308 |
+
|
| 309 |
+
ori_prob = probs[0, pos, ori_st: ori_st + len(foldseek_struc_vocab)].sum()
|
| 310 |
+
mut_prob = probs[0, pos, mut_st: mut_st + len(foldseek_struc_vocab)].sum()
|
| 311 |
+
|
| 312 |
+
score += torch.log(mut_prob / ori_prob)
|
| 313 |
+
|
| 314 |
+
return score.item()
|
| 315 |
+
|
| 316 |
+
def predict_pos_mut(self, seq: str, pos: int) -> dict:
|
| 317 |
+
"""
|
| 318 |
+
Predict the mutational effect of mutations at a given position
|
| 319 |
+
Args:
|
| 320 |
+
seq: The wild type sequence
|
| 321 |
+
|
| 322 |
+
pos: The position of the mutation
|
| 323 |
+
|
| 324 |
+
Returns:
|
| 325 |
+
The predicted mutational effect
|
| 326 |
+
"""
|
| 327 |
+
tokens = self.tokenizer.tokenize(seq)
|
| 328 |
+
ori_aa = tokens[pos - 1][0]
|
| 329 |
+
tokens[pos - 1] = "#" + tokens[pos - 1][-1]
|
| 330 |
+
|
| 331 |
+
mask_seq = " ".join(tokens)
|
| 332 |
+
inputs = self.tokenizer(mask_seq, return_tensors="pt")
|
| 333 |
+
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 334 |
+
|
| 335 |
+
with torch.no_grad():
|
| 336 |
+
outputs = self.model(**inputs)
|
| 337 |
+
logits = outputs.logits
|
| 338 |
+
probs = logits.softmax(dim=-1)[0, pos]
|
| 339 |
+
|
| 340 |
+
scores = {}
|
| 341 |
+
ori_st = self.tokenizer.get_vocab()[ori_aa + foldseek_struc_vocab[0]]
|
| 342 |
+
for mut_aa in aa_list:
|
| 343 |
+
mut_st = self.tokenizer.get_vocab()[mut_aa + foldseek_struc_vocab[0]]
|
| 344 |
+
|
| 345 |
+
ori_prob = probs[ori_st: ori_st + len(foldseek_struc_vocab)].sum()
|
| 346 |
+
mut_prob = probs[mut_st: mut_st + len(foldseek_struc_vocab)].sum()
|
| 347 |
+
|
| 348 |
+
score = torch.log(mut_prob / ori_prob)
|
| 349 |
+
scores[f"{ori_aa}{pos}{mut_aa}"] = score.item()
|
| 350 |
+
|
| 351 |
+
return scores
|
| 352 |
+
|
| 353 |
+
def predict_pos_prob(self, seq: str, pos: int) -> dict:
|
| 354 |
+
"""
|
| 355 |
+
Predict the probability of all amino acids at a given position
|
| 356 |
+
Args:
|
| 357 |
+
seq: The wild type sequence
|
| 358 |
+
|
| 359 |
+
pos: The position of the mutation
|
| 360 |
+
|
| 361 |
+
Returns:
|
| 362 |
+
The predicted probability of all amino acids
|
| 363 |
+
"""
|
| 364 |
+
tokens = self.tokenizer.tokenize(seq)
|
| 365 |
+
tokens[pos - 1] = "#" + tokens[pos - 1][-1]
|
| 366 |
+
|
| 367 |
+
mask_seq = " ".join(tokens)
|
| 368 |
+
inputs = self.tokenizer(mask_seq, return_tensors="pt")
|
| 369 |
+
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 370 |
+
|
| 371 |
+
with torch.no_grad():
|
| 372 |
+
outputs = self.model(**inputs)
|
| 373 |
+
logits = outputs.logits
|
| 374 |
+
probs = logits.softmax(dim=-1)[0, pos]
|
| 375 |
+
|
| 376 |
+
scores = {}
|
| 377 |
+
for aa in aa_list:
|
| 378 |
+
st = self.tokenizer.get_vocab()[aa + foldseek_struc_vocab[0]]
|
| 379 |
+
prob = probs[st: st + len(foldseek_struc_vocab)].sum()
|
| 380 |
+
|
| 381 |
+
scores[aa] = prob.item()
|
| 382 |
+
|
| 383 |
+
return scores
|
model/saprot/saprot_if_model.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.distributed as dist
|
| 3 |
+
import torchmetrics
|
| 4 |
+
|
| 5 |
+
from torch.nn.functional import cross_entropy
|
| 6 |
+
|
| 7 |
+
from ..model_interface import register_model
|
| 8 |
+
from .base import SaprotBaseModel
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@register_model
|
| 12 |
+
class SaProtIFModel(SaprotBaseModel):
|
| 13 |
+
"""
|
| 14 |
+
SaProt inverse folding model.
|
| 15 |
+
"""
|
| 16 |
+
def __init__(self, **kwargs):
|
| 17 |
+
super().__init__(task='lm', **kwargs)
|
| 18 |
+
|
| 19 |
+
def initialize_metrics(self, stage):
|
| 20 |
+
return {f"{stage}_acc": torchmetrics.Accuracy(ignore_index=-1)}
|
| 21 |
+
|
| 22 |
+
def forward(self, inputs, coords=None):
|
| 23 |
+
if coords is not None:
|
| 24 |
+
inputs = self.add_bias_feature(inputs, coords)
|
| 25 |
+
|
| 26 |
+
outputs = self.model(**inputs)
|
| 27 |
+
return outputs
|
| 28 |
+
|
| 29 |
+
def loss_func(self, stage, outputs, labels):
|
| 30 |
+
logits = outputs['logits']
|
| 31 |
+
# merge the first and second dimension of logits
|
| 32 |
+
logits = logits.view(-1, logits.size(-1))
|
| 33 |
+
|
| 34 |
+
# flatten labels
|
| 35 |
+
labels = labels['labels'].flatten().to(logits.device)
|
| 36 |
+
|
| 37 |
+
loss = cross_entropy(logits, labels, ignore_index=-1)
|
| 38 |
+
getattr(self, f"{stage}_acc").update(logits.detach(), labels)
|
| 39 |
+
|
| 40 |
+
if stage == 'train':
|
| 41 |
+
log_dict = self.get_log_dict("train")
|
| 42 |
+
log_dict["train_loss"] = loss
|
| 43 |
+
self.log_info(log_dict)
|
| 44 |
+
self.reset_metrics("train")
|
| 45 |
+
|
| 46 |
+
return loss
|
| 47 |
+
|
| 48 |
+
def on_train_epoch_end(self):
|
| 49 |
+
super().on_train_epoch_end()
|
| 50 |
+
|
| 51 |
+
# Re-sample the subset of the training data
|
| 52 |
+
if getattr(self.trainer.datamodule.train_dataset, "num_per_cluster", None) is not None:
|
| 53 |
+
self.trainer.datamodule.train_dataset.sample_subset()
|
| 54 |
+
|
| 55 |
+
def on_test_epoch_end(self):
|
| 56 |
+
log_dict = self.get_log_dict("test")
|
| 57 |
+
log_dict["test_loss"] = torch.cat(self.all_gather(self.test_outputs), dim=-1).mean()
|
| 58 |
+
|
| 59 |
+
if dist.get_rank() == 0:
|
| 60 |
+
print(log_dict)
|
| 61 |
+
self.log_info(log_dict)
|
| 62 |
+
|
| 63 |
+
self.reset_metrics("test")
|
| 64 |
+
|
| 65 |
+
def on_validation_epoch_end(self):
|
| 66 |
+
log_dict = self.get_log_dict("valid")
|
| 67 |
+
log_dict["valid_loss"] = torch.cat(self.all_gather(self.valid_outputs), dim=-1).mean()
|
| 68 |
+
|
| 69 |
+
if dist.get_rank() == 0:
|
| 70 |
+
print(log_dict)
|
| 71 |
+
self.log_info(log_dict)
|
| 72 |
+
self.reset_metrics("valid")
|
| 73 |
+
|
| 74 |
+
valid_acc = log_dict["valid_acc"]
|
| 75 |
+
self.check_save_condition(valid_acc, mode="max")
|
| 76 |
+
|
| 77 |
+
def predict(self, aa_seq: str, struc_seq: str, method: str = "argmax", num_samples: int = 1) -> str:
|
| 78 |
+
"""
|
| 79 |
+
Predict all masked amino acids in the sequence.
|
| 80 |
+
Args:
|
| 81 |
+
aa_seq: Amino acid sequence (could be all masked or partially masked).
|
| 82 |
+
|
| 83 |
+
struc_seq: Foldseek sequence.
|
| 84 |
+
|
| 85 |
+
method: Prediction method. It could be either "argmax" or "multinomial". If "argmax", the most probable
|
| 86 |
+
amino acid will be selected. If "multinomial", the amino acid will be sampled from the multinomial
|
| 87 |
+
distribution.
|
| 88 |
+
|
| 89 |
+
num_samples: Number of predicted sequences. Only works when method is "multinomial".
|
| 90 |
+
|
| 91 |
+
Returns:
|
| 92 |
+
Predicted residue sequence.
|
| 93 |
+
"""
|
| 94 |
+
assert len(aa_seq) == len(struc_seq), "The length of the amino acid sequence and the foldseek sequence must be the same."
|
| 95 |
+
assert method in ["argmax", "multinomial"], "The prediction method must be either 'argmax' or 'multinomial'."
|
| 96 |
+
if method == "argmax":
|
| 97 |
+
assert num_samples == 1, "The sample number must be 1 when the prediction method is 'argmax'."
|
| 98 |
+
|
| 99 |
+
sa_seq = "".join(f"{aa}{struc}" for aa, struc in zip(aa_seq, struc_seq))
|
| 100 |
+
|
| 101 |
+
# Record the index of masked amino acids
|
| 102 |
+
mask_indices = [i for i, aa in enumerate(aa_seq) if aa == '#']
|
| 103 |
+
|
| 104 |
+
with torch.no_grad():
|
| 105 |
+
inputs = self.tokenizer(sa_seq, return_tensors='pt')
|
| 106 |
+
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 107 |
+
|
| 108 |
+
outputs = self.model(**inputs)
|
| 109 |
+
logits = outputs.logits[0, 1: -1]
|
| 110 |
+
probs = torch.softmax(logits, dim=-1)
|
| 111 |
+
|
| 112 |
+
# Predict amino acids
|
| 113 |
+
if method == "argmax":
|
| 114 |
+
batch_preds = probs.argmax(dim=-1).unsqueeze(0)
|
| 115 |
+
else:
|
| 116 |
+
batch_preds = torch.multinomial(probs, num_samples, replacement=True).permute(1, 0)
|
| 117 |
+
|
| 118 |
+
pred_aa_seqs = []
|
| 119 |
+
for preds in batch_preds:
|
| 120 |
+
masked_preds = preds[mask_indices]
|
| 121 |
+
pred_tokens = self.tokenizer.convert_ids_to_tokens(masked_preds)
|
| 122 |
+
|
| 123 |
+
tokens = list(aa_seq)
|
| 124 |
+
for i, pred_token in zip(mask_indices, pred_tokens):
|
| 125 |
+
tokens[i] = pred_token[0]
|
| 126 |
+
|
| 127 |
+
pred_aa_seq = "".join(tokens)
|
| 128 |
+
pred_aa_seqs.append(pred_aa_seq)
|
| 129 |
+
|
| 130 |
+
return pred_aa_seqs
|
model/saprot/saprot_lm_model.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import os
|
| 3 |
+
import torchmetrics
|
| 4 |
+
|
| 5 |
+
from torch.nn.functional import cross_entropy
|
| 6 |
+
from ..model_interface import register_model
|
| 7 |
+
from .base import SaprotBaseModel
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@register_model
|
| 11 |
+
class SaprotLMModel(SaprotBaseModel):
|
| 12 |
+
def __init__(self, **kwargs):
|
| 13 |
+
super().__init__(task='lm', **kwargs)
|
| 14 |
+
|
| 15 |
+
def initialize_metrics(self, stage):
|
| 16 |
+
return {f"{stage}_acc": torchmetrics.Accuracy(ignore_index=-1)}
|
| 17 |
+
|
| 18 |
+
def forward(self, inputs, coords=None):
|
| 19 |
+
if coords is not None:
|
| 20 |
+
inputs = self.add_bias_feature(inputs, coords)
|
| 21 |
+
|
| 22 |
+
outputs = self.model(**inputs)
|
| 23 |
+
|
| 24 |
+
# Get hidden representations
|
| 25 |
+
if "output_hidden_states" in inputs and inputs["output_hidden_states"]:
|
| 26 |
+
input_ids = inputs["input_ids"]
|
| 27 |
+
ends = (input_ids == 2).int()
|
| 28 |
+
indices = ends.argmax(dim=-1)
|
| 29 |
+
repr_list = []
|
| 30 |
+
hidden_states = outputs["hidden_states"][-1]
|
| 31 |
+
for i, idx in enumerate(indices):
|
| 32 |
+
repr = hidden_states[i][1:idx].mean(dim=0)
|
| 33 |
+
repr_list.append(repr)
|
| 34 |
+
|
| 35 |
+
reprs = torch.stack(repr_list, dim=0)
|
| 36 |
+
outputs["hidden_states"] = reprs
|
| 37 |
+
|
| 38 |
+
return outputs
|
| 39 |
+
|
| 40 |
+
def loss_func(self, stage, outputs, labels):
|
| 41 |
+
logits = outputs['logits']
|
| 42 |
+
# merge the first and second dimension of logits
|
| 43 |
+
logits = logits.view(-1, logits.size(-1))
|
| 44 |
+
|
| 45 |
+
# flatten labels
|
| 46 |
+
labels = labels['labels'].flatten().to(logits.device)
|
| 47 |
+
|
| 48 |
+
loss = cross_entropy(logits, labels, ignore_index=-1)
|
| 49 |
+
getattr(self, f"{stage}_acc").update(logits.detach(), labels)
|
| 50 |
+
|
| 51 |
+
if stage == 'train':
|
| 52 |
+
log_dict = self.get_log_dict("train")
|
| 53 |
+
log_dict["train_loss"] = loss
|
| 54 |
+
self.log_info(log_dict)
|
| 55 |
+
self.reset_metrics("train")
|
| 56 |
+
|
| 57 |
+
return loss
|
| 58 |
+
|
| 59 |
+
def test_epoch_end(self, outputs):
|
| 60 |
+
log_dict = self.get_log_dict("test")
|
| 61 |
+
log_dict["test_loss"] = torch.cat(self.all_gather(outputs), dim=-1).mean()
|
| 62 |
+
|
| 63 |
+
print(log_dict)
|
| 64 |
+
self.log_info(log_dict)
|
| 65 |
+
|
| 66 |
+
self.reset_metrics("test")
|
| 67 |
+
|
| 68 |
+
def validation_epoch_end(self, outputs):
|
| 69 |
+
log_dict = self.get_log_dict("valid")
|
| 70 |
+
log_dict["valid_loss"] = torch.cat(self.all_gather(outputs), dim=-1).mean()
|
| 71 |
+
|
| 72 |
+
self.log_info(log_dict)
|
| 73 |
+
self.reset_metrics("valid")
|
| 74 |
+
self.check_save_condition(log_dict["valid_loss"], mode="min")
|
model/saprot/saprot_ppi_model.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torchmetrics
|
| 2 |
+
import torch
|
| 3 |
+
|
| 4 |
+
from torch.nn import Linear, ReLU
|
| 5 |
+
from torch.nn.functional import cross_entropy
|
| 6 |
+
from ..model_interface import register_model
|
| 7 |
+
from .base import SaprotBaseModel
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@register_model
|
| 11 |
+
class SaprotPPIModel(SaprotBaseModel):
|
| 12 |
+
def __init__(self, **kwargs):
|
| 13 |
+
"""
|
| 14 |
+
Args:
|
| 15 |
+
**kwargs: other arguments for SaprotBaseModel
|
| 16 |
+
"""
|
| 17 |
+
super().__init__(task="base", **kwargs)
|
| 18 |
+
|
| 19 |
+
def initialize_model(self):
|
| 20 |
+
super().initialize_model()
|
| 21 |
+
|
| 22 |
+
hidden_size = self.model.config.hidden_size * 2
|
| 23 |
+
classifier = torch.nn.Sequential(
|
| 24 |
+
Linear(hidden_size, hidden_size),
|
| 25 |
+
ReLU(),
|
| 26 |
+
Linear(hidden_size, 2)
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
setattr(self.model, "classifier", classifier)
|
| 30 |
+
|
| 31 |
+
def initialize_metrics(self, stage):
|
| 32 |
+
return {f"{stage}_acc": torchmetrics.Accuracy()}
|
| 33 |
+
|
| 34 |
+
def forward(self, inputs_1, inputs_2):
|
| 35 |
+
if self.freeze_backbone:
|
| 36 |
+
hidden_1 = torch.stack(self.get_hidden_states(inputs_1, reduction="mean"))
|
| 37 |
+
hidden_2 = torch.stack(self.get_hidden_states(inputs_2, reduction="mean"))
|
| 38 |
+
else:
|
| 39 |
+
hidden_1 = self.model.esm(**inputs_1)[0][:, 0, :]
|
| 40 |
+
hidden_2 = self.model.esm(**inputs_2)[0][:, 0, :]
|
| 41 |
+
|
| 42 |
+
hidden_concat = torch.cat([hidden_1, hidden_2], dim=-1)
|
| 43 |
+
return self.model.classifier(hidden_concat)
|
| 44 |
+
|
| 45 |
+
def loss_func(self, stage, logits, labels):
|
| 46 |
+
label = labels['labels']
|
| 47 |
+
loss = cross_entropy(logits, label)
|
| 48 |
+
|
| 49 |
+
# Update metrics
|
| 50 |
+
for metric in self.metrics[stage].values():
|
| 51 |
+
metric.update(logits.detach(), label)
|
| 52 |
+
|
| 53 |
+
if stage == "train":
|
| 54 |
+
log_dict = self.get_log_dict("train")
|
| 55 |
+
log_dict["train_loss"] = loss
|
| 56 |
+
self.log_info(log_dict)
|
| 57 |
+
|
| 58 |
+
# Reset train metrics
|
| 59 |
+
self.reset_metrics("train")
|
| 60 |
+
|
| 61 |
+
return loss
|
| 62 |
+
|
| 63 |
+
def test_epoch_end(self, outputs):
|
| 64 |
+
log_dict = self.get_log_dict("test")
|
| 65 |
+
log_dict["test_loss"] = torch.cat(self.all_gather(outputs), dim=-1).mean()
|
| 66 |
+
|
| 67 |
+
print(log_dict)
|
| 68 |
+
self.log_info(log_dict)
|
| 69 |
+
|
| 70 |
+
self.reset_metrics("test")
|
| 71 |
+
|
| 72 |
+
def validation_epoch_end(self, outputs):
|
| 73 |
+
log_dict = self.get_log_dict("valid")
|
| 74 |
+
log_dict["valid_loss"] = torch.cat(self.all_gather(outputs), dim=-1).mean()
|
| 75 |
+
|
| 76 |
+
self.log_info(log_dict)
|
| 77 |
+
self.reset_metrics("valid")
|
| 78 |
+
self.check_save_condition(log_dict["valid_acc"], mode="max")
|
model/saprot/saprot_regression_model.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch.distributed as dist
|
| 2 |
+
import torchmetrics
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from ..model_interface import register_model
|
| 6 |
+
from .base import SaprotBaseModel
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@register_model
|
| 10 |
+
class SaprotRegressionModel(SaprotBaseModel):
|
| 11 |
+
def __init__(self, test_result_path: str = None, **kwargs):
|
| 12 |
+
"""
|
| 13 |
+
Args:
|
| 14 |
+
test_result_path: path to save test result
|
| 15 |
+
**kwargs: other arguments for SaprotBaseModel
|
| 16 |
+
"""
|
| 17 |
+
self.test_result_path = test_result_path
|
| 18 |
+
super().__init__(task="regression", **kwargs)
|
| 19 |
+
|
| 20 |
+
def initialize_metrics(self, stage):
|
| 21 |
+
return {f"{stage}_loss": torchmetrics.MeanSquaredError(),
|
| 22 |
+
f"{stage}_spearman": torchmetrics.SpearmanCorrCoef(),
|
| 23 |
+
f"{stage}_R2": torchmetrics.R2Score(),
|
| 24 |
+
f"{stage}_pearson": torchmetrics.PearsonCorrCoef()}
|
| 25 |
+
|
| 26 |
+
def forward(self, inputs, structure_info=None):
|
| 27 |
+
if structure_info:
|
| 28 |
+
# To be implemented
|
| 29 |
+
raise NotImplementedError
|
| 30 |
+
|
| 31 |
+
# If backbone is frozen, the embedding will be the average of all residues
|
| 32 |
+
if self.freeze_backbone:
|
| 33 |
+
repr = torch.stack(self.get_hidden_states(inputs, reduction="mean"))
|
| 34 |
+
x = self.model.classifier.dropout(repr)
|
| 35 |
+
x = self.model.classifier.dense(x)
|
| 36 |
+
x = torch.tanh(x)
|
| 37 |
+
x = self.model.classifier.dropout(x)
|
| 38 |
+
logits = self.model.classifier.out_proj(x).squeeze(dim=-1)
|
| 39 |
+
|
| 40 |
+
else:
|
| 41 |
+
logits = self.model(**inputs).logits.squeeze(dim=-1)
|
| 42 |
+
|
| 43 |
+
return logits
|
| 44 |
+
|
| 45 |
+
def loss_func(self, stage, outputs, labels):
|
| 46 |
+
fitness = labels['labels'].to(outputs)
|
| 47 |
+
loss = torch.nn.functional.mse_loss(outputs, fitness)
|
| 48 |
+
|
| 49 |
+
# Update metrics
|
| 50 |
+
for metric in self.metrics[stage].values():
|
| 51 |
+
# Training is on half precision, but metrics expect float to compute correctly.
|
| 52 |
+
metric.update(outputs.detach().float(), fitness.float())
|
| 53 |
+
|
| 54 |
+
if stage == "train":
|
| 55 |
+
# Skip calculating metrics if the batch size is 1
|
| 56 |
+
if fitness.shape[0] > 1:
|
| 57 |
+
log_dict = self.get_log_dict("train")
|
| 58 |
+
self.log_info(log_dict)
|
| 59 |
+
|
| 60 |
+
# Reset train metrics
|
| 61 |
+
self.reset_metrics("train")
|
| 62 |
+
|
| 63 |
+
return loss
|
| 64 |
+
|
| 65 |
+
def test_epoch_end(self, outputs):
|
| 66 |
+
if self.test_result_path is not None:
|
| 67 |
+
from torchmetrics.utilities.distributed import gather_all_tensors
|
| 68 |
+
|
| 69 |
+
preds = self.test_spearman.preds
|
| 70 |
+
preds[-1] = preds[-1].unsqueeze(dim=0) if preds[-1].shape == () else preds[-1]
|
| 71 |
+
preds = torch.cat(gather_all_tensors(torch.cat(preds, dim=0)))
|
| 72 |
+
|
| 73 |
+
targets = self.test_spearman.target
|
| 74 |
+
targets[-1] = targets[-1].unsqueeze(dim=0) if targets[-1].shape == () else targets[-1]
|
| 75 |
+
targets = torch.cat(gather_all_tensors(torch.cat(targets, dim=0)))
|
| 76 |
+
|
| 77 |
+
if dist.get_rank() == 0:
|
| 78 |
+
with open(self.test_result_path, 'w') as w:
|
| 79 |
+
w.write("pred\ttarget\n")
|
| 80 |
+
for pred, target in zip(preds, targets):
|
| 81 |
+
w.write(f"{pred.item()}\t{target.item()}\n")
|
| 82 |
+
|
| 83 |
+
log_dict = self.get_log_dict("test")
|
| 84 |
+
|
| 85 |
+
print(log_dict)
|
| 86 |
+
self.log_info(log_dict)
|
| 87 |
+
self.reset_metrics("test")
|
| 88 |
+
|
| 89 |
+
def validation_epoch_end(self, outputs):
|
| 90 |
+
log_dict = self.get_log_dict("valid")
|
| 91 |
+
|
| 92 |
+
self.log_info(log_dict)
|
| 93 |
+
self.reset_metrics("valid")
|
| 94 |
+
self.check_save_condition(log_dict["valid_loss"], mode="min")
|
| 95 |
+
|
requirements.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch==1.13.1
|
| 2 |
+
torchvision==0.14.1
|
| 3 |
+
torchaudio==0.13.1
|
| 4 |
+
pytorch-lightning==1.8.3
|
| 5 |
+
wandb==0.12.10
|
| 6 |
+
transformers==4.28.0
|
| 7 |
+
easydict==1.10
|
| 8 |
+
biopython==1.81
|
| 9 |
+
scipy==1.14.1
|
| 10 |
+
peft==0.10.0
|
| 11 |
+
lmdb==1.4.1
|
| 12 |
+
torchmetrics==0.9.3
|
| 13 |
+
pandas==2.1.1
|
| 14 |
+
fair-esm==2.0.0
|
| 15 |
+
numpy==1.25.2
|
| 16 |
+
protobuf<4,>=3.20.0
|
scripts/LMDB/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Place the datasets here
|
scripts/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
scripts/bin/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Place the foldseek binary file here
|
scripts/compute_clinvar_auc.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import argparse
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import yaml
|
| 5 |
+
|
| 6 |
+
from glob import glob
|
| 7 |
+
from sklearn import metrics
|
| 8 |
+
from easydict import EasyDict
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def get_args():
|
| 12 |
+
parser = argparse.ArgumentParser()
|
| 13 |
+
parser.add_argument('-c', '--config', help="running configurations", type=str, required=True)
|
| 14 |
+
return parser.parse_args()
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def main(args):
|
| 18 |
+
with open(args.config, 'r', encoding='utf-8') as r:
|
| 19 |
+
config = EasyDict(yaml.safe_load(r))
|
| 20 |
+
|
| 21 |
+
output_dir = "output/ClinVar"
|
| 22 |
+
# file_name = "esm2_t33_650M_UR50D_foldseek_plddt70_iter2900448_mask70"
|
| 23 |
+
|
| 24 |
+
label_name = "ClinVar_labels.csv"
|
| 25 |
+
label_path = os.path.join(output_dir, label_name)
|
| 26 |
+
|
| 27 |
+
list_variables_to_keep = ["protein_name", "mutations", "evol_indices"]
|
| 28 |
+
all_evol_indices = pd.concat(
|
| 29 |
+
[
|
| 30 |
+
pd.read_csv(path, low_memory=False)[list_variables_to_keep]
|
| 31 |
+
for path in glob(f"{config.model.kwargs.log_dir}/*.csv")
|
| 32 |
+
],
|
| 33 |
+
ignore_index=True,
|
| 34 |
+
)
|
| 35 |
+
all_evol_indices = all_evol_indices.drop_duplicates()
|
| 36 |
+
|
| 37 |
+
labels_dataset = pd.read_csv(label_path, low_memory=False)
|
| 38 |
+
all_evol_indices_with_labels = pd.merge(
|
| 39 |
+
all_evol_indices,
|
| 40 |
+
labels_dataset[["protein_name", "mutations", "ClinVar_labels"]],
|
| 41 |
+
how="right",
|
| 42 |
+
on=["protein_name", "mutations"],
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
all_evol_indices_with_labels = all_evol_indices_with_labels[
|
| 46 |
+
all_evol_indices_with_labels.ClinVar_labels != 0.5
|
| 47 |
+
]
|
| 48 |
+
|
| 49 |
+
fpr, tpr, threshold = metrics.roc_curve(
|
| 50 |
+
all_evol_indices_with_labels["ClinVar_labels"], all_evol_indices_with_labels["evol_indices"]
|
| 51 |
+
)
|
| 52 |
+
roc_auc = metrics.auc(fpr, tpr)
|
| 53 |
+
print(roc_auc)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
if __name__ == '__main__':
|
| 57 |
+
main(get_args())
|