Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +23 -10
- .streamlit/config.toml +6 -0
- LICENSE +201 -0
- MANIFEST.in +1 -0
- README.md +500 -0
- config.json +25 -0
- data/README.md +13 -0
- data/benchmark/README.md +10 -0
- data/benchmark/dataset_summary.csv +13 -0
- data/example_multichain_protein/combo_muts.csv +10 -0
- data/example_multichain_protein/example_dataset.csv +254 -0
- data/example_multichain_protein/multichain_protein.cif +0 -0
- data/example_multichain_protein/vh_chain1.fasta +2 -0
- data/example_multichain_protein/vl_chain2.fasta +2 -0
- data/example_protein/APEX_33overhang.fasta +2 -0
- data/example_protein/MULTI-assembly_input.csv +15 -0
- data/example_protein/apex.cif +0 -0
- data/example_protein/apex.fasta +2 -0
- data/example_protein/combo_muts.csv +15 -0
- data/example_protein/example_dataset.csv +185 -0
- data/example_protein/run.sh +43 -0
- env.yml +26 -0
- env_mac.yml +27 -0
- hub/checkpoints/esm2_t36_3B_UR50D-contact-regression.pt +3 -0
- model/__init__.py +6 -0
- model/featurizers/__init__.py +7 -0
- model/featurizers/ankh_featurizers.py +82 -0
- model/featurizers/base_featurizers.py +266 -0
- model/featurizers/combinatorial_featurizers.py +425 -0
- model/featurizers/esm_featurizers.py +620 -0
- model/featurizers/model_choices.py +70 -0
- model/featurizers/model_locations.py +38 -0
- model/featurizers/msa_featurizers.py +381 -0
- model/featurizers/prott5_featurizers.py +65 -0
- model/featurizers/unirep_featurizers.py +42 -0
- model/featurizers/zeroshot_featurizers.py +632 -0
- model/multievolve_workflow.png +3 -0
- model/predictors/__init__.py +3 -0
- model/predictors/base_regressors.py +550 -0
- model/predictors/gaussian_process_regressors.py +503 -0
- model/predictors/neural_net_regressors.py +746 -0
- model/predictors/sweep_configs/cnn_custom_grid_sweep.yaml +18 -0
- model/predictors/sweep_configs/cnn_standard_bayes_sweep.yaml +18 -0
- model/predictors/sweep_configs/cnn_standard_grid_sweep.yaml +18 -0
- model/predictors/sweep_configs/cnn_test_sweep.yaml +18 -0
- model/predictors/sweep_configs/fcn_custom_grid_sweep.yaml +18 -0
- model/predictors/sweep_configs/fcn_standard_bayes_sweep.yaml +18 -0
- model/predictors/sweep_configs/fcn_standard_grid_sweep.yaml +18 -0
- model/predictors/sweep_configs/fcn_test_sweep.yaml +18 -0
- model/proposers/__init__.py +1 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,48 @@
|
|
| 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 -textmodel/multievolve_workflow.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
model/streamlit_1.png filter=lfs diff=lfs merge=lfs -text
|
.streamlit/config.toml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[theme]
|
| 2 |
+
primaryColor = "#1E88E5"
|
| 3 |
+
backgroundColor = "#FFFFFF"
|
| 4 |
+
secondaryBackgroundColor = "#E3F2FD"
|
| 5 |
+
textColor = "#262730"
|
| 6 |
+
font = "sans serif"
|
LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
MANIFEST.in
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
include model/predictors/sweep_configs/*.yaml
|
README.md
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
- zh
|
| 6 |
+
tags:
|
| 7 |
+
- OneScience
|
| 8 |
+
- life-science
|
| 9 |
+
- protein
|
| 10 |
+
- directed-evolution
|
| 11 |
+
- multi-mutant
|
| 12 |
+
- protein-language-model
|
| 13 |
+
- MULTI-evolve
|
| 14 |
+
frameworks: PyTorch
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
<p align="center">
|
| 18 |
+
<strong>
|
| 19 |
+
<span style="font-size: 30px;">MULTI-evolve</span>
|
| 20 |
+
</strong>
|
| 21 |
+
</p>
|
| 22 |
+
|
| 23 |
+
# Model Introduction
|
| 24 |
+
|
| 25 |
+
MULTI-evolve (model-guided, universal, targeted installation of multi-mutants) is an end-to-end framework for protein directed evolution. It trains sequence-to-fitness prediction models, proposes combinatorial multi-mutants, generates MULTI-assembly site-directed mutagenesis oligonucleotides, and supports screening single-mutant candidates through a protein language model zero-shot ensemble method.
|
| 26 |
+
|
| 27 |
+
Paper:
|
| 28 |
+
|
| 29 |
+
> **Rapid directed evolution guided by protein language models and epistatic interactions**
|
| 30 |
+
> Science, 2026
|
| 31 |
+
> https://doi.org/10.1126/science.aea1820
|
| 32 |
+
|
| 33 |
+
# Model Description
|
| 34 |
+
|
| 35 |
+
The core workflow of MULTI-evolve includes:
|
| 36 |
+
|
| 37 |
+
1. Train fully connected neural networks using experimental sequence-to-fitness data.
|
| 38 |
+
2. Compare different data splits, sequence representations, and machine learning models.
|
| 39 |
+
3. Select the best-performing prediction model to score combinatorial mutants and propose candidates.
|
| 40 |
+
4. Generate MULTI-assembly site-directed mutagenesis oligonucleotides from the selected multi-mutants.
|
| 41 |
+
5. In selected iterations, use a protein language model zero-shot ensemble method to screen single-mutant candidates.
|
| 42 |
+
|
| 43 |
+
# Use Cases
|
| 44 |
+
|
| 45 |
+
| Use case | Description |
|
| 46 |
+
| --- | --- |
|
| 47 |
+
| Protein directed evolution | Train fitness prediction models from experimental data and screen candidate mutations |
|
| 48 |
+
| Multi-mutant design | Predict combinatorial mutations and screen multi-mutants with high predicted fitness |
|
| 49 |
+
| Protein complex optimization | Support mutation formats and inputs for multichain proteins |
|
| 50 |
+
| Zero-shot mutation screening | Use a protein language model ensemble method to screen candidate single mutations |
|
| 51 |
+
|
| 52 |
+
# Usage
|
| 53 |
+
|
| 54 |
+
## 1. Using OneCode
|
| 55 |
+
|
| 56 |
+
Experience intelligent one-click AI4S programming in the OneCode online environment:
|
| 57 |
+
|
| 58 |
+
[Try intelligent one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
|
| 59 |
+
|
| 60 |
+
## 2. Manual Installation and Usage
|
| 61 |
+
|
| 62 |
+
**Hardware Requirements**
|
| 63 |
+
|
| 64 |
+
- Supervised model training and standard combinatorial mutation prediction in MULTI-evolve can run on a CPU or GPU/DCU.
|
| 65 |
+
- Protein language model zero-shot prediction uses models such as ESM and ESM-IF; a GPU/DCU is recommended.
|
| 66 |
+
|
| 67 |
+
### Set Up the Runtime Environment
|
| 68 |
+
|
| 69 |
+
#### DCU Environment
|
| 70 |
+
|
| 71 |
+
```bash
|
| 72 |
+
# Activate DTK and CONDA first
|
| 73 |
+
conda create -n onescience311 python=3.11 -y
|
| 74 |
+
conda activate onescience311
|
| 75 |
+
|
| 76 |
+
pip install onescience[bio] \
|
| 77 |
+
-i http://mirrors.onescience.ai:3141/pypi/simple/ \
|
| 78 |
+
--trusted-host mirrors.onescience.ai
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
#### Environment Notes
|
| 82 |
+
|
| 83 |
+
- If you encounter missing dependencies or version incompatibilities during execution, refer to the dependency versions specified in `env.yml` and install or adjust the relevant dependencies as needed.
|
| 84 |
+
|
| 85 |
+
### Prepare Models and Data
|
| 86 |
+
|
| 87 |
+
The different MULTI-evolve functions have different model and data requirements. Prepare the resources for standard supervised training, combinatorial multi-mutant recommendation, MULTI-assembly design, protein language model zero-shot screening, and benchmark reproduction separately.
|
| 88 |
+
|
| 89 |
+
#### 1) Supervised Learning Input Data
|
| 90 |
+
|
| 91 |
+
To train your own protein fitness prediction model with MULTI-evolve, prepare:
|
| 92 |
+
|
| 93 |
+
```text
|
| 94 |
+
Wild-type protein FASTA
|
| 95 |
+
+
|
| 96 |
+
Experimental training data CSV
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
The training data CSV must contain at least:
|
| 100 |
+
|
| 101 |
+
```text
|
| 102 |
+
mutation
|
| 103 |
+
property_value
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
For example, the mutation format for a single-chain protein is:
|
| 107 |
+
|
| 108 |
+
```text
|
| 109 |
+
A40P/E61Y
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
For multichain proteins, use `:` to separate different chains:
|
| 113 |
+
|
| 114 |
+
```text
|
| 115 |
+
A40P/E61Y:WT
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
Where:
|
| 119 |
+
|
| 120 |
+
```text
|
| 121 |
+
/ Separates multiple mutations on the same chain
|
| 122 |
+
: Separates different protein chains
|
| 123 |
+
WT Indicates that the corresponding chain remains wild type
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
The official repository provides example data:
|
| 127 |
+
|
| 128 |
+
```text
|
| 129 |
+
data/
|
| 130 |
+
├── example_protein/
|
| 131 |
+
└── example_multichain_protein/
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
Therefore, no additional training data download is required to run the official basic examples.
|
| 135 |
+
|
| 136 |
+
#### 2) Combinatorial Mutation Candidate Pool
|
| 137 |
+
|
| 138 |
+
When running combinatorial multi-mutant recommendation, in addition to the wild-type FASTA and training data, provide a mutation pool: a list of candidate single mutations eligible for combinatorial design.
|
| 139 |
+
|
| 140 |
+
Example:
|
| 141 |
+
|
| 142 |
+
```text
|
| 143 |
+
data/example_protein/combo_muts.csv
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
Pass this file as the:
|
| 147 |
+
|
| 148 |
+
```text
|
| 149 |
+
--mutation-pool
|
| 150 |
+
```
|
| 151 |
+
|
| 152 |
+
parameter, for example:
|
| 153 |
+
|
| 154 |
+
```bash
|
| 155 |
+
p2_propose.py \
|
| 156 |
+
--experiment-name multievolve_example \
|
| 157 |
+
--protein-name example_protein \
|
| 158 |
+
--wt-files apex.fasta \
|
| 159 |
+
--training-dataset example_dataset.csv \
|
| 160 |
+
--mutation-pool combo_muts.csv \
|
| 161 |
+
--top-muts-per-load 3 \
|
| 162 |
+
--export-name multievolve_proposals
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
#### 3) Protein Language Model Zero-Shot Mode
|
| 166 |
+
|
| 167 |
+
The MULTI-evolve protein language model zero-shot ensemble workflow requires:
|
| 168 |
+
|
| 169 |
+
```text
|
| 170 |
+
Wild-type FASTA
|
| 171 |
+
+
|
| 172 |
+
PDB/CIF protein structure
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
The current official code uses the following models:
|
| 176 |
+
|
| 177 |
+
```text
|
| 178 |
+
ESM-1v:
|
| 179 |
+
esm1v_t33_650M_UR90S_1
|
| 180 |
+
esm1v_t33_650M_UR90S_2
|
| 181 |
+
esm1v_t33_650M_UR90S_3
|
| 182 |
+
esm1v_t33_650M_UR90S_4
|
| 183 |
+
esm1v_t33_650M_UR90S_5
|
| 184 |
+
|
| 185 |
+
ESM-2:
|
| 186 |
+
esm2_t36_3B_UR50D
|
| 187 |
+
|
| 188 |
+
ESM-IF1:
|
| 189 |
+
esm_if1_gvp4_t16_142M_UR50
|
| 190 |
+
```
|
| 191 |
+
|
| 192 |
+
MULTI-evolve calls these models through `fair-esm`. On the first run, if the corresponding weights are not available locally, `fair-esm` automatically downloads the models and caches them in the PyTorch Hub checkpoint directory.
|
| 193 |
+
|
| 194 |
+
The default cache location is:
|
| 195 |
+
|
| 196 |
+
```text
|
| 197 |
+
~/.cache/torch/hub/checkpoints/
|
| 198 |
+
```
|
| 199 |
+
|
| 200 |
+
ESM-2 also uses the corresponding contact regression weights:
|
| 201 |
+
|
| 202 |
+
```text
|
| 203 |
+
esm2_t36_3B_UR50D-contact-regression.pt
|
| 204 |
+
```
|
| 205 |
+
- The current repository already includes `esm2_t36_3B_UR50D-contact-regression.pt` under `hub/checkpoints/`.
|
| 206 |
+
|
| 207 |
+
For network-restricted or offline environments, download the weights in advance:
|
| 208 |
+
|
| 209 |
+
```bash
|
| 210 |
+
mkdir -p ~/.cache/torch/hub/checkpoints
|
| 211 |
+
cd ~/.cache/torch/hub/checkpoints
|
| 212 |
+
|
| 213 |
+
wget https://dl.fbaipublicfiles.com/fair-esm/models/esm1v_t33_650M_UR90S_1.pt
|
| 214 |
+
wget https://dl.fbaipublicfiles.com/fair-esm/models/esm1v_t33_650M_UR90S_2.pt
|
| 215 |
+
wget https://dl.fbaipublicfiles.com/fair-esm/models/esm1v_t33_650M_UR90S_3.pt
|
| 216 |
+
wget https://dl.fbaipublicfiles.com/fair-esm/models/esm1v_t33_650M_UR90S_4.pt
|
| 217 |
+
wget https://dl.fbaipublicfiles.com/fair-esm/models/esm1v_t33_650M_UR90S_5.pt
|
| 218 |
+
|
| 219 |
+
wget https://dl.fbaipublicfiles.com/fair-esm/models/esm2_t36_3B_UR50D.pt
|
| 220 |
+
wget https://dl.fbaipublicfiles.com/fair-esm/regression/esm2_t36_3B_UR50D-contact-regression.pt
|
| 221 |
+
|
| 222 |
+
wget https://dl.fbaipublicfiles.com/fair-esm/models/esm_if1_gvp4_t16_142M_UR50.pt
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
To save the models in the current project or another location, set the PyTorch Hub cache root with `TORCH_HOME`. For example, to use a directory in the current project:
|
| 226 |
+
|
| 227 |
+
```bash
|
| 228 |
+
cd /path/to/MULTI-evolve
|
| 229 |
+
export TORCH_HOME=$PWD
|
| 230 |
+
mkdir -p ${TORCH_HOME}/hub/checkpoints
|
| 231 |
+
```
|
| 232 |
+
|
| 233 |
+
Then save or symlink the weights above to:
|
| 234 |
+
|
| 235 |
+
```text
|
| 236 |
+
/path/to/MULTI-evolve/hub/checkpoints/
|
| 237 |
+
```
|
| 238 |
+
|
| 239 |
+
This avoids modifying the MULTI-evolve source code.
|
| 240 |
+
|
| 241 |
+
#### 4) Benchmark DMS Data
|
| 242 |
+
|
| 243 |
+
To run the official benchmark and reproduce the performance comparison across different:
|
| 244 |
+
|
| 245 |
+
```text
|
| 246 |
+
data splitting methods
|
| 247 |
+
sequence representation methods
|
| 248 |
+
machine learning models
|
| 249 |
+
```
|
| 250 |
+
|
| 251 |
+
you must additionally prepare the official benchmark DMS data by downloading it separately from Zenodo.
|
| 252 |
+
|
| 253 |
+
Download:
|
| 254 |
+
|
| 255 |
+
```text
|
| 256 |
+
DOI: 10.5281/zenodo.17620759
|
| 257 |
+
https://zenodo.org/records/17620759
|
| 258 |
+
```
|
| 259 |
+
|
| 260 |
+
After downloading, place the DMS CSV files directly in the following directory. If the directory does not exist, create it manually first:
|
| 261 |
+
|
| 262 |
+
```text
|
| 263 |
+
data/benchmark/datasets/
|
| 264 |
+
```
|
| 265 |
+
|
| 266 |
+
The benchmark script entry point in the current repository is:
|
| 267 |
+
|
| 268 |
+
```text
|
| 269 |
+
scripts/notebooks/benchmark/multievolve_hyperparameter_tuning.py
|
| 270 |
+
```
|
| 271 |
+
|
| 272 |
+
## 3. Quick Start
|
| 273 |
+
|
| 274 |
+
### Download the Model Package
|
| 275 |
+
|
| 276 |
+
```bash
|
| 277 |
+
hf download OneScience-Group/MULTI-evolve \
|
| 278 |
+
--local-dir ./MULTI-evolve
|
| 279 |
+
|
| 280 |
+
cd MULTI-evolve
|
| 281 |
+
```
|
| 282 |
+
|
| 283 |
+
- Standard supervised training and combinatorial mutation recommendation in MULTI-evolve do not require additional large fixed datasets; you can use the example data in the repository or your own experimental data.
|
| 284 |
+
|
| 285 |
+
- The protein language model zero-shot mode may require additional ESM/ESM-IF model caches; prepare them in advance in offline environments.
|
| 286 |
+
|
| 287 |
+
### Quick Verification
|
| 288 |
+
|
| 289 |
+
Install the current repository:
|
| 290 |
+
|
| 291 |
+
```bash
|
| 292 |
+
python -m pip install -e . --no-deps
|
| 293 |
+
```
|
| 294 |
+
|
| 295 |
+
Check the commands:
|
| 296 |
+
|
| 297 |
+
```bash
|
| 298 |
+
p1_train.py --help
|
| 299 |
+
p2_propose.py --help
|
| 300 |
+
p3_assembly_design.py --help
|
| 301 |
+
plm_zeroshot_ensemble.py --help
|
| 302 |
+
```
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
# Example Data
|
| 306 |
+
|
| 307 |
+
The official repository provides:
|
| 308 |
+
|
| 309 |
+
```text
|
| 310 |
+
data/
|
| 311 |
+
├── example_protein/
|
| 312 |
+
├── example_multichain_protein/
|
| 313 |
+
└── benchmark/
|
| 314 |
+
```
|
| 315 |
+
|
| 316 |
+
The official command-line examples primarily use:
|
| 317 |
+
|
| 318 |
+
```bash
|
| 319 |
+
cd data/example_protein
|
| 320 |
+
```
|
| 321 |
+
|
| 322 |
+
Typical inputs include:
|
| 323 |
+
|
| 324 |
+
```text
|
| 325 |
+
apex.fasta
|
| 326 |
+
example_dataset.csv
|
| 327 |
+
combo_muts.csv
|
| 328 |
+
APEX_33overhang.fasta
|
| 329 |
+
apex.cif
|
| 330 |
+
```
|
| 331 |
+
|
| 332 |
+
These files are used for:
|
| 333 |
+
|
| 334 |
+
| File | Purpose |
|
| 335 |
+
| --- | --- |
|
| 336 |
+
| `apex.fasta` | Wild-type protein amino acid sequence |
|
| 337 |
+
| `example_dataset.csv` | Training data |
|
| 338 |
+
| `combo_muts.csv` | Combinatorial mutation candidate pool |
|
| 339 |
+
| `APEX_33overhang.fasta` | DNA input required for MULTI-assembly oligonucleotide design |
|
| 340 |
+
| `apex.cif` | Structure-conditioned scoring for protein language models |
|
| 341 |
+
|
| 342 |
+
# Inference and Training Examples
|
| 343 |
+
|
| 344 |
+
## Step 1: Train the Neural Network Model
|
| 345 |
+
|
| 346 |
+
```bash
|
| 347 |
+
# If the runtime environment is not active, activate the conda environment in use, such as onescience311
|
| 348 |
+
conda activate onescience311
|
| 349 |
+
cd data/example_protein
|
| 350 |
+
|
| 351 |
+
p1_train.py \
|
| 352 |
+
--experiment-name multievolve_example \
|
| 353 |
+
--protein-name example_protein \
|
| 354 |
+
--wt-files apex.fasta \
|
| 355 |
+
--training-dataset-fname example_dataset.csv \
|
| 356 |
+
--wandb-key dummy \
|
| 357 |
+
--mode test
|
| 358 |
+
```
|
| 359 |
+
|
| 360 |
+
Key parameters:
|
| 361 |
+
|
| 362 |
+
| Parameter | Description |
|
| 363 |
+
| --- | --- |
|
| 364 |
+
| `--experiment-name` | Current experiment name; keep it consistent in subsequent steps |
|
| 365 |
+
| `--protein-name` | Protein name |
|
| 366 |
+
| `--wt-files` | Wild-type FASTA; use commas to separate multiple FASTA files for multichain proteins |
|
| 367 |
+
| `--training-dataset-fname` | Training data CSV |
|
| 368 |
+
| `--mode` | `test` or `standard` |
|
| 369 |
+
|
| 370 |
+
## Step 2: Propose Combinatorial Multi-Mutants
|
| 371 |
+
|
| 372 |
+
```bash
|
| 373 |
+
p2_propose.py \
|
| 374 |
+
--experiment-name multievolve_example \
|
| 375 |
+
--protein-name example_protein \
|
| 376 |
+
--wt-files apex.fasta \
|
| 377 |
+
--training-dataset example_dataset.csv \
|
| 378 |
+
--mutation-pool combo_muts.csv \
|
| 379 |
+
--top-muts-per-load 3 \
|
| 380 |
+
--export-name multievolve_proposals
|
| 381 |
+
```
|
| 382 |
+
|
| 383 |
+
The script loads the trained model saved to the local cache in Step 1 and scores the combinatorial mutation candidates.
|
| 384 |
+
|
| 385 |
+
Typical output:
|
| 386 |
+
|
| 387 |
+
```text
|
| 388 |
+
multievolve_proposals.csv
|
| 389 |
+
```
|
| 390 |
+
|
| 391 |
+
For protein complexes, candidate files are also generated separately for each chain.
|
| 392 |
+
|
| 393 |
+
## Step 3: Design MULTI-assembly Oligonucleotides
|
| 394 |
+
|
| 395 |
+
```bash
|
| 396 |
+
p3_assembly_design.py \
|
| 397 |
+
--mutations-file multievolve_proposals.csv \
|
| 398 |
+
--wt-fasta APEX_33overhang.fasta \
|
| 399 |
+
--overhang 33 \
|
| 400 |
+
--species human \
|
| 401 |
+
--oligo-direction top \
|
| 402 |
+
--tm 80 \
|
| 403 |
+
--output design
|
| 404 |
+
```
|
| 405 |
+
|
| 406 |
+
Where:
|
| 407 |
+
|
| 408 |
+
| Parameter | Description |
|
| 409 |
+
| --- | --- |
|
| 410 |
+
| `--mutations-file` | Candidate mutation CSV generated in Step 2 |
|
| 411 |
+
| `--wt-fasta` | Wild-type DNA FASTA containing overhangs at both ends |
|
| 412 |
+
| `--overhang` | Overhang length |
|
| 413 |
+
| `--species` | `human`, `ecoli`, or `yeast` |
|
| 414 |
+
| `--oligo-direction` | `top` or `bottom` |
|
| 415 |
+
| `--tm` | Target oligonucleotide melting temperature; the official recommendation is 80 °C |
|
| 416 |
+
| `--output` | `design` or `update` |
|
| 417 |
+
|
| 418 |
+
Outputs:
|
| 419 |
+
|
| 420 |
+
```text
|
| 421 |
+
cloning_sheet.csv
|
| 422 |
+
oligos.csv
|
| 423 |
+
```
|
| 424 |
+
|
| 425 |
+
## Protein Language Model Zero-Shot Ensemble
|
| 426 |
+
|
| 427 |
+
```bash
|
| 428 |
+
plm_zeroshot_ensemble.py \
|
| 429 |
+
--wt-file apex.fasta \
|
| 430 |
+
--pdb-files apex.cif \
|
| 431 |
+
--chain-id A \
|
| 432 |
+
--variants 24 \
|
| 433 |
+
--excluded-positions 1,14,41,112 \
|
| 434 |
+
--normalizing-method aa_substitution_type
|
| 435 |
+
```
|
| 436 |
+
|
| 437 |
+
Where:
|
| 438 |
+
|
| 439 |
+
| Parameter | Description |
|
| 440 |
+
| --- | --- |
|
| 441 |
+
| `--wt-file` | Wild-type protein FASTA |
|
| 442 |
+
| `--pdb-files` | PDB/CIF structure files; use commas to separate multiple structures |
|
| 443 |
+
| `--chain-id` | Chain ID of the target protein in the structure file |
|
| 444 |
+
| `--variants` | Number of mutations nominated by each method |
|
| 445 |
+
| `--excluded-positions` | Positions excluded from mutation |
|
| 446 |
+
| `--normalizing-method` | `aa_substitution_type` or `aa_mutation` |
|
| 447 |
+
|
| 448 |
+
This workflow ensembles four methods and produces:
|
| 449 |
+
|
| 450 |
+
```text
|
| 451 |
+
plm_zeroshot_ensemble_nominated_mutations.csv
|
| 452 |
+
```
|
| 453 |
+
|
| 454 |
+
# Output Description
|
| 455 |
+
|
| 456 |
+
MULTI-evolve generates model caches, evaluation results, and candidate sequences at different stages.
|
| 457 |
+
|
| 458 |
+
The official repository automatically creates the following after execution:
|
| 459 |
+
|
| 460 |
+
```text
|
| 461 |
+
proteins/
|
| 462 |
+
└── <protein_name>/
|
| 463 |
+
├── feature_cache/
|
| 464 |
+
├── model_cache/
|
| 465 |
+
│ └── <dataset>/
|
| 466 |
+
│ ├── objects/
|
| 467 |
+
│ └── results/
|
| 468 |
+
├── proposers/
|
| 469 |
+
│ └── results/
|
| 470 |
+
└── split_cache/
|
| 471 |
+
└── <dataset>/
|
| 472 |
+
```
|
| 473 |
+
|
| 474 |
+
The main outputs include:
|
| 475 |
+
|
| 476 |
+
| Output | Description |
|
| 477 |
+
| --- | --- |
|
| 478 |
+
| `model_cache/` | Trained models and comparison results |
|
| 479 |
+
| `feature_cache/` | Cached sequence representations |
|
| 480 |
+
| `multievolve_proposals.csv` | Recommended multi-mutant candidates |
|
| 481 |
+
| `cloning_sheet.csv` | MULTI-assembly cloning design sheet |
|
| 482 |
+
| `oligos.csv` | Site-directed mutagenesis oligonucleotide sequences |
|
| 483 |
+
| `plm_zeroshot_ensemble_nominated_mutations.csv` | Protein language model zero-shot ensemble recommendations |
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
# Official OneScience Information
|
| 487 |
+
|
| 488 |
+
| Platform | Main OneScience repository | Skills repository |
|
| 489 |
+
| --- | --- | --- |
|
| 490 |
+
| Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
|
| 491 |
+
| GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
# Citation and License
|
| 495 |
+
|
| 496 |
+
- Original MULTI-evolve paper: [Rapid directed evolution guided by protein language models and epistatic interactions](https://doi.org/10.1126/science.aea1820).
|
| 497 |
+
- The `LICENSE` in the repository root is currently **Apache License 2.0**. This license permits use, modification, distribution, and commercial use, but redistribution requires retaining the license, copyright, and attribution notices, and clearly indicating modified files.
|
| 498 |
+
- Apache-2.0 also includes a patent license and explicitly does not grant rights to use the project's trademarks.
|
| 499 |
+
- `setup.py` still contains an `MIT License` classifier, which is inconsistent with the actual `LICENSE` file in the repository root. For SCNet/ModelScope redistribution, use the Apache-2.0 `LICENSE` in the repository root as the authoritative license, and retain the original license file.
|
| 500 |
+
- This repository is a DCU-adapted version of MULTI-evolve, with some environment configurations, dependencies, and execution procedures adjusted. Use of the repository code, model weights, and related data remains subject to the licenses and terms of use of their respective original projects.
|
config.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"framework": "Pytorch",
|
| 3 |
+
"task": "protein-engineering",
|
| 4 |
+
"subtasks": [
|
| 5 |
+
"supervised-protein-fitness-training",
|
| 6 |
+
"combinatorial-mutant-proposal",
|
| 7 |
+
"multi-assembly-oligo-design",
|
| 8 |
+
"protein-language-model-zeroshot-screening",
|
| 9 |
+
"benchmark-evaluation"
|
| 10 |
+
],
|
| 11 |
+
"entrypoints": {
|
| 12 |
+
"train": "scripts/p1_train.py",
|
| 13 |
+
"propose": "scripts/p2_propose.py",
|
| 14 |
+
"assembly_design": "scripts/p3_assembly_design.py",
|
| 15 |
+
"zeroshot": "scripts/plm_zeroshot_ensemble.py",
|
| 16 |
+
"streamlit_app": "scripts/app.py",
|
| 17 |
+
"benchmark": "scripts/notebooks/benchmark/multievolve_hyperparameter_tuning.py"
|
| 18 |
+
},
|
| 19 |
+
"package": "model",
|
| 20 |
+
"example_data": [
|
| 21 |
+
"data/example_protein",
|
| 22 |
+
"data/example_multichain_protein"
|
| 23 |
+
],
|
| 24 |
+
"model_cache": "hub/checkpoints"
|
| 25 |
+
}
|
data/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Example data
|
| 2 |
+
|
| 3 |
+
## Training dataset
|
| 4 |
+
|
| 5 |
+
CSV file with columns ```mutation``` and ```property_value```. Variants should be formatted as ```A40P/E61Y```, or for protein complexes as ```A40P/E61Y:WT```, where ```:``` separates the individual chains (e.g. chain 1 mutations:chain 2 mutations), ```/``` separates the individual mutations, and ```WT``` indicates the wildtype sequence.
|
| 6 |
+
|
| 7 |
+
## Multi-chain protein
|
| 8 |
+
|
| 9 |
+
The variable heavy domain (vh) and variable light domain (vl) chains of HuABC2, anti-CD122 antibody.
|
| 10 |
+
|
| 11 |
+
## Single chain protein
|
| 12 |
+
|
| 13 |
+
Engineered ascorbate peroxidase (APEX)
|
data/benchmark/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Benchmarking
|
| 2 |
+
|
| 3 |
+
The multievolve_hyperparameter_tuning.py script in notebooks/benchmark can be used to train the models for benchmarking MULTI-evolve.
|
| 4 |
+
|
| 5 |
+
"dataset_summary.csv" provides details on the individual datasets used for benchmarking. The table was derived and modified from [ProteinGym](https://proteingym.org/).
|
| 6 |
+
|
| 7 |
+
# Data
|
| 8 |
+
|
| 9 |
+
Large datasets are not included in this repository due to size constraints. Please download DMS dataset files from Zenodo (10.5281/zenodo.17620759) and place in:
|
| 10 |
+
- `data/benchmark/datasets/`
|
data/benchmark/dataset_summary.csv
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
DMS_id,DMS_filename,UniProt_ID,taxon,source_organism,target_seq,seq_len,includes_multiple_mutants,DMS_total_number_mutants,DMS_number_multiple_mutants,DMS_binarization_cutoff,DMS_binarization_method,first_author,title,year,jo,molecule_name,selection_assay,selection_type,ProteinGym_version,coarse_selection_type
|
| 2 |
+
CAPSD_AAV2S_Sinai_2021,CAPSD_AAV2S_Sinai_2021.csv,CAPSD_AAV2S,Virus,Adeno-associated virus 2,MAADGYLPDWLEDTLSEGIRQWWKLKPGPPPPKPAERHKDDSRGLVLPGYKYLGPFNGLDKGEPVNEADAAALEHDKAYDRQLDSGDNPYLKYNHADAEFQERLKEDTSFGGNLGRAVFQAKKRVLEPLGLVEEPVKTAPGKKRPVEHSPVEPDSSSGTGKAGQQPARKRLNFGQTGDADSVPDPQPLGQPPAAPSGLGTNTMATGSGAPMADNNEGADGVGNSSGNWHCDSTWMGDRVITTSTRTWALPTYNNHLYKQISSQSGASNDNHYFGYSTPWGYFDFNRFHCHFSPRDWQRLINNNWGFRPKRLNFKLFNIQVKEVTQNDGTTTIANNLTSTVQVFTDSEYQLPYVLGSAHQGCLPPFPADVFMVPQYGYLTLNNGSQAVGRSSFYCLEYFPSQMLRTGNNFTFSYTFEDVPFHSSYAHSQSLDRLMNPLIDQYLYYLSRTNTPSGTTTQSRLQFSQAGASDIRDQSRNWLPGPCYRQQRVSKTSADNNNSEYSWTGATKYHLNGRDSLVNPGPAMASHKDDEEKFFPQSGVLIFGKQGSEKTNVDIEKVMITDEEEIRTTNPVATEQYGSVSTNLQRGNRQAATADVNTQGVLPGMVWQDRDVYLQGPIWAKIPHTDGHFHPSPLMGGFGLKHPPPQILIKNTPVPANPSTTFSAAKFASFITQYSTGQVSVEIEWELQKENSKRWNPEIQYTSNYNKSVNVDFTVDTNGVYSEPRPIGTRYLTRNL,735,TRUE,42328,41796,-1.2,manual,Sinai,Generative AAV capsid diversification by latent interpolation,2021,10.1101/2021.04.16.440236,AAV,viability for AAV capsid production,,0.1,OrganismalFitness
|
| 3 |
+
D7PM05_CLYGR_Somermeyer_2022,D7PM05_CLYGR_Somermeyer_2022.csv,D7PM05_CLYGR,Eukaryote,Clytia gregaria,MTALTEGAKLFEKEIPYITELEGDVEGMKFIIKGEGTGDATTGTIKAKYICTTGDLPVPWATILSSLSYGVFCFAKYPRHIADFFKSTQPDGYSQDRIISFDNDGQYDVKAKVTYENGTLYNRVTVKGTGFKSNGNILGMRVLYHSPPHAVYILPDRKNGGMKIEYNKAFDVMGGGHQMARHAQFNKPLGAWEEDYPLYHHLTVWTSFGKDPDDDETDHLTIVEVIKAVDLETYR,235,TRUE,24515,23346,12500,manual,Somermeyer,Heterogeneity of the GFP fitness landscape and data-driven protein design,2022,10.7554/eLife.75842,Green fluorescent protein cgreGFP,Fluorescence,FACS,1,Activity
|
| 4 |
+
F7YBW8_MESOW_Aakre_2015,F7YBW8_MESOW_Aakre_2015.csv,F7YBW8_MESOW,Prokaryote,Mesorhizobium opportunistum (strain LMG 24607 / HAMBI 3007 / WSM2075),MANVEKMSVAVTPQQAAVMREAVEAGEYATASEIVREAVRDWLAKRELRHDDIRRLRQLWDEGKASGRPEPVDFDALRKEARQKLTEVPPNGR,93,TRUE,9192,9155,-0.001724,median,Aakre,Evolving New Protein-Protein Interaction Specificity through Promiscuous Intermediates,2015,10.1016/j.cell.2015.09.055,Antitoxin ParD3,fitness,Growth (antitoxin neutralization of ParE3),0.1,OrganismalFitness
|
| 5 |
+
GFP_AEQVI_Sarkisyan_2016,GFP_AEQVI_Sarkisyan_2016.csv,GFP_AEQVI,Eukaryote,Aequorea victoria,MSKGEELFTGVVPILVELDGDVNGHKFSVSGEGEGDATYGKLTLKFICTTGKLPVPWPTLVTTLSYGVQCFSRYPDHMKQHDFFKSAMPEGYVQERTIFFKDDGNYKTRAEVKFEGDTLVNRIELKGIDFKEDGNILGHKLEYNYNSHNVYIMADKQKNGIKVNFKIRHNIEDGSVQLADHYQQNTPIGDGPVLLPDNHYLSTQSALSKDPNEKRDHMVLLEFVTAAGITHGMDELYK,238,TRUE,51714,50630,2.5,manual,Sarkisyan,Local fitness landscape of the green fluorescent protein,2016,10.1038/nature17995,GFP,Fluorescence,FACS,0.1,Activity
|
| 6 |
+
HIS7_YEAST_Pokusaeva_2019,HIS7_YEAST_Pokusaeva_2019.csv,HIS7_YEAST,Eukaryote,Saccharomyces cerevisiae,MTEQKALVKRITNETKIQIAISLKGGPLAIEHSIFPEKEAEAVAEQATQSQVINVHTGIGFLDHMIHALAKHSGWSLIVECIGDLHIDDHHTTEDCGIALGQAFKEALGAVRGVKRFGSGFAPLDEALSRAVVDLSNRPYAVVELGLQREKVGDLSCEMIPHFLESFAEASRITLHVDCLRGKNDHHRSESAFKALAVAIREATSPNGTNDVPSTKGVLM,220,TRUE,496137,495969,0.3,manual,Pokusaeva,An experimental assay of the interactions of amino acids from orthologous sequences shaping a complex fitness landscape,2019,10.1371/journal.pgen.1008079,IGP dehydratase (HIS3),Growth,Growth,0.1,OrganismalFitness
|
| 7 |
+
PHOT_CHLRE_Chen_2023,PHOT_CHLRE_Chen_2023.csv,PHOT_CHLRE,Eukaryote,Chlamydomonas reinhardtii,AGLRHTFVVADATLPDCPLVYASEGFYAMTGYGPDEVLGHNARFLQGEGTDPKEVQKIRDAIKKGEACSVRLLNYRKDGTPFWNLLTVTPIKTPDGRVSKFVGVQVDVTSKTEGKALA,118,TRUE,167529,165407,0.631701888,median,Chen,Deep Mutational Scanning of an Oxygen-Independent Fluorescent Protein CreiLOV for Comprehensive Profiling of Mutational and Epistatic Effects,2023,10.1021/acssynbio.2c00662,Phototropin,Fluorescence,FACS,1,Activity
|
| 8 |
+
Q6WV13_9MAXI_Somermeyer_2022,Q6WV13_9MAXI_Somermeyer_2022.csv,Q6WV12_9MAXI,Eukaryote,Pontellina plumata,MPAMKIECRITGTLNGVEFELVGGGEGTPEQGRMTNKMKSTKGALTFSPYLLSHVMGYGFYHFGTYPSGYENPFLHAINNGGYTNTRIEKYEDGGVLHVSFSYRYEAGRVIGDFKVVGTGFPEDSVIFTDKIIRSNATVEHLHPMGDNVLVGSFARTFSLRDGGYYSFVVDSHMHFKSAIHPSILQNGGPMFAFRRVEELHSNTELGIVEYQHAFKTPIAFA,222,TRUE,31401,30260,15721.24977,median,Somermeyer,Heterogeneity of the GFP fitness landscape and data-driven protein design,2022,10.7554/eLife.75842,Green fluorescent protein ppluGFP2,Fluorescence,FACS,1,Activity
|
| 9 |
+
Q8WTC7_9CNID_Somermeyer_2022,Q8WTC7_9CNID_Somermeyer_2022.csv,Q8WTC7_9CNID,Eukaryote,Aequorea macrodactyla,MSKGEELFTGIVPVLIELDGDVHGHKFSVRGEGEGDADYGKLEIKFICTTGKLPVPWPTLVTTLSYGILCFARYPEHMKMNDFFKSAMPEGYIQERTIFFQDDGKYKTRGEVKFEGDTLVNRIELKGMDFKEDGNILGHKLEYNFNSHNVYIMPDKANNGLKVNFKIRHNIEGGGVQLADHYQTNVPLGDGPVLIPINHYLSCQTAISKDRNETRDHMVFLEFFSACGHTHGMDELYK,238,TRUE,33510,32309,5000,manual,Somermeyer,Heterogeneity of the GFP fitness landscape and data-driven protein design,2022,10.7554/eLife.75842,Green fluorescent protein amacGFP,Fluorescence,FACS,1,Activity
|
| 10 |
+
SPG1_STRSG_Wu_2016,SPG1_STRSG_Wu_2016.csv,SPG1_STRSG,Prokaryote,Streptococcus sp. group G,MEKEKKVKYFLRKSAFGLASVSAAFLVGSTVFAVDSPIEDTPIIRNGGELTNLLGNSETTLALRNEESATADLTAAAVADTVAAAAAENAGAAAWEAAAAADALAKAKADALKEFNKYGVSDYYKNLINNAKTVEGIKDLQAQVVESAKKARISEATDGLSDFLKSQTPAEDTVKSIELAEAKVLANRELDKYGVSDYHKNLINNAKTVEGVKELIDEILAALPKTDQYKLILNGKTLKGETTTEAVDAATAEKVFKQYANDNGVDGEWTYDDATKTFTVTEKPEVIDASELTPAVTTYKLVINGKTLKGETTTKAVDAETAEKAFKQYANDNGVDGVWTYDDATKTFTVTEMVTEVPGDAPTEPEKPEASIPLVPLTPATPIAKDDAKKDDTKKEDAKKPEAKKDDAKKAETLPTTGEGSNPFFTAAALAVMAGAGALAVASKRKED,448,TRUE,149360,149284,0.122438875,median,Wu,Adaptation in protein fitness landscapes is facilitated by indirect paths,2016,10.7554/eLife.16965,GB1,Binding (IgG),binding,1,Binding
|
| 11 |
+
Q65J43_BACLD_g4_Thomas_2025,Q65J43_BACLD_g4_Thomas_2025.csv,Q65J43_BACLD,Prokaryote,Bacillus licheniformis,MIKKWAVHLLFSALVLLGLSGGAAYSPQHAEGAARYDDVLYFPASRYPETGAHISDAIKAGHADVCTIERSGADKRRQESLKGIPTKPGFDRDEWPMAMCEEGGKGASVRYVSSSDNRGAGSWVGNRLNGYADGTRILFIVQ,142,TRUE,15404,15183,1.33474,WT,Thomas,Engineering highly active nuclease enzymes with machine learning and high-throughput screening,2025,10.1016/j.cels.2025.101236,NucB,Fluorescence,FACS,,Activity
|
| 12 |
+
TRPB1_THEMA_Tm9D8_Johnston_2024,TRPB1_THEMA_Tm9D8_Johnston_2024.csv,TRPB1_THEMA,Prokaryote,Escherichia coli,MKGYFGPYGGQYVPEILMGALEELEAAYEGIMKDESFWKEFNDLLRDYAGRPTPLYFARRLSEKYGARVYLKREDLLHTGAHKINNAIGQVLLAKLMGKTRIIAETGAGQHGVATATAAALFGMECVIYMGEEDTIRQKLNVERMKLLGAKVVPVKSGSRTLKDAIDEALRDWITNLQTTYYVFGSVVGPHPYPIIVRNFQKVIGEETKKQIPEKEGRLPDYIVACVSGGSNAAGIFYPFIDSGVKLIGVEAGGEGLETGKHAASLLKGKIGYLHGSKTFVLQDDWGQVQVSHSVSAGLDYSGVGPEHAYWRETGKVLYDAVTDEEALDAFIELSRLEGIIPALESSHALAYLKKINIKGKVVVVNLSGRGDKDLESVLNHPYVRERIR,389,TRUE,159129,159052,0.40807,WT,Johnston,A combinatorially complete epistatic fitness landscape in an enzyme active site,2024,10.1073/pnas.2400439121,TrpB,Growth,Growth,,Stability
|
| 13 |
+
VH14_Li_2023,VH14_Li_2023.csv,,Human,Homo sapiens,EVQLVETGGGLVQPGGSLRLSCAASGFTLNSYGISWVRQAPGKGPEWVSVIYSDGRRTFYGDSVKGRFTISRDTSTNTVYLQMNSLRVEDTAVYYCAKGRAAGTFDSWGQGTLVTVSSGGGGSGGGGSGGGGSDVVMTQSPESLAVSLGERATISCKSSQSVLYESRNKNSVAWYQQKAGQPPKLLIYWASTRESGVPDRFSGSGSGTDFTLTISSLQAEDAAVYYCQQYHRLPLSFGGGTKVEIK,246,TRUE,35889,35452,-0.787659364,WT,Li,Machine learning optimization of candidate antibody yields highly diverse sub-nanomolar affinity antibody libraries,2023,10.1038/s41467-023-39022-2,scFv,Binding,,,Binding
|
data/example_multichain_protein/combo_muts.csv
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
A40P
|
| 2 |
+
S51I
|
| 3 |
+
T116L
|
| 4 |
+
L11Q
|
| 5 |
+
Q13P
|
| 6 |
+
S63A
|
| 7 |
+
S65H
|
| 8 |
+
F203G
|
| 9 |
+
I226L
|
| 10 |
+
I226P
|
data/example_multichain_protein/example_dataset.csv
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
mutation,property_value
|
| 2 |
+
WT:WT,1
|
| 3 |
+
A40P:WT,1.006
|
| 4 |
+
A50R:WT,1.034
|
| 5 |
+
D106W:WT,0.923
|
| 6 |
+
D76N:WT,0.977
|
| 7 |
+
E35H:WT,0.867
|
| 8 |
+
E35N:WT,0.93
|
| 9 |
+
E61Y:WT,0.748
|
| 10 |
+
E91S:WT,0.871
|
| 11 |
+
F27W:WT,0.651
|
| 12 |
+
F32Y:WT,0.802
|
| 13 |
+
G9V:WT,0.238
|
| 14 |
+
I48V:WT,0.339
|
| 15 |
+
I71T:WT,0.783
|
| 16 |
+
M108F:WT,0.853
|
| 17 |
+
M34H:WT,0.498
|
| 18 |
+
Q113D:WT,0.818
|
| 19 |
+
Q113P:WT,0.858
|
| 20 |
+
Q84D:WT,0.897
|
| 21 |
+
R100I:WT,1.015
|
| 22 |
+
R100L:WT,0.86
|
| 23 |
+
R104G:WT,0.924
|
| 24 |
+
R19T:WT,1.297
|
| 25 |
+
S121P:WT,1.312
|
| 26 |
+
S51I:WT,1.077
|
| 27 |
+
S80T:WT,0.99
|
| 28 |
+
T116L:WT,1.369
|
| 29 |
+
T28D:WT,1.171
|
| 30 |
+
T28P:WT,0.901
|
| 31 |
+
T90P:WT,1.18
|
| 32 |
+
V72I:WT,1.179
|
| 33 |
+
Y103N:WT,1.179
|
| 34 |
+
Y105G:WT,1.098
|
| 35 |
+
Y110V:WT,1.437
|
| 36 |
+
Y33W:WT,1.409
|
| 37 |
+
A55P:WT,1.219
|
| 38 |
+
A64D:WT,1.077
|
| 39 |
+
D106Y:WT,1.036
|
| 40 |
+
D57G:WT,0.763
|
| 41 |
+
E6A:WT,0.929
|
| 42 |
+
E6G:WT,0.435
|
| 43 |
+
E91R:WT,0.659
|
| 44 |
+
F70W:WT,0.487
|
| 45 |
+
G107Y:WT,0.601
|
| 46 |
+
G44T:WT,0.904
|
| 47 |
+
K43Q:WT,1.123
|
| 48 |
+
L11E:WT,0.886
|
| 49 |
+
L11Q:WT,1.108
|
| 50 |
+
M108Y:WT,0.788
|
| 51 |
+
N53P:WT,1.009
|
| 52 |
+
N56E:WT,0.945
|
| 53 |
+
Q113A:WT,0.814
|
| 54 |
+
Q13P:WT,1.219
|
| 55 |
+
Q3K:WT,1.023
|
| 56 |
+
Q84V:WT,0.973
|
| 57 |
+
R104Y:WT,0.919
|
| 58 |
+
R19W:WT,0.75
|
| 59 |
+
R52A:WT,0.951
|
| 60 |
+
S101E:WT,1.014
|
| 61 |
+
S25G:WT,0.804
|
| 62 |
+
S63A:WT,0.993
|
| 63 |
+
S65H:WT,0.891
|
| 64 |
+
T115K:WT,0.354
|
| 65 |
+
V2K:WT,1.02
|
| 66 |
+
Y102P:WT,0.945
|
| 67 |
+
Y33D:WT,1.274
|
| 68 |
+
Y82R:WT,0.66
|
| 69 |
+
WT:D49Y,0.148
|
| 70 |
+
WT:E78A,0.604
|
| 71 |
+
WT:E80S,0.397
|
| 72 |
+
WT:F61Y,0.354
|
| 73 |
+
WT:F82G,1.289
|
| 74 |
+
WT:I105L,1.533
|
| 75 |
+
WT:I105P,1.176
|
| 76 |
+
WT:I26N,1.777
|
| 77 |
+
WT:I26S,1.889
|
| 78 |
+
WT:L21I,1.728
|
| 79 |
+
WT:L45K,0.204
|
| 80 |
+
WT:M32L,0.475
|
| 81 |
+
WT:N52T,0.97
|
| 82 |
+
WT:N52V,1.242
|
| 83 |
+
WT:N91E,1.629
|
| 84 |
+
WT:N91Y,0.534
|
| 85 |
+
WT:Q36K,0.455
|
| 86 |
+
WT:Q88M,1.562
|
| 87 |
+
WT:R18T,1.523
|
| 88 |
+
WT:S24R,1.358
|
| 89 |
+
WT:S24T,1.231
|
| 90 |
+
WT:S27Q,1.09
|
| 91 |
+
WT:S28P,1.769
|
| 92 |
+
WT:T50A,0.146
|
| 93 |
+
WT:T71R,0.731
|
| 94 |
+
WT:V29I,0.617
|
| 95 |
+
WT:V54Q,0.327
|
| 96 |
+
WT:V84T,1.762
|
| 97 |
+
WT:W90Y,0.528
|
| 98 |
+
WT:Y31W,0.608
|
| 99 |
+
WT:Y33A,0.006
|
| 100 |
+
WT:Y33C,0.462
|
| 101 |
+
WT:Y70F,1.467
|
| 102 |
+
WT:Y93P,1.463
|
| 103 |
+
WT:Y93W,1.303
|
| 104 |
+
WT:A42H,0.445
|
| 105 |
+
WT:A59M,0.56
|
| 106 |
+
WT:A59P,0.994
|
| 107 |
+
WT:C23W,0.001
|
| 108 |
+
WT:E104V,0.46
|
| 109 |
+
WT:E78L,0.518
|
| 110 |
+
WT:E80G,0.332
|
| 111 |
+
WT:F82Y,1.167
|
| 112 |
+
WT:F97Y,0.285
|
| 113 |
+
WT:G100V,0.06
|
| 114 |
+
WT:G99N,0.857
|
| 115 |
+
WT:G99P,0.735
|
| 116 |
+
WT:I105N,0.59
|
| 117 |
+
WT:K38L,0.45
|
| 118 |
+
WT:L46T,0.099
|
| 119 |
+
WT:L4M,0.311
|
| 120 |
+
WT:L53R,1.764
|
| 121 |
+
WT:P43T,0.11
|
| 122 |
+
WT:R44K,0.885
|
| 123 |
+
WT:S12H,0.74
|
| 124 |
+
WT:S27E,1.6
|
| 125 |
+
WT:S30P,1.732
|
| 126 |
+
WT:S30Y,0.356
|
| 127 |
+
WT:S7Q,0.836
|
| 128 |
+
WT:T20V,1.589
|
| 129 |
+
WT:T92Y,0.965
|
| 130 |
+
WT:T96D,0.538
|
| 131 |
+
WT:W90L,1.342
|
| 132 |
+
WT:Y33W,0.352
|
| 133 |
+
WT:Y48R,0.62
|
| 134 |
+
WT:Y93L,0.505
|
| 135 |
+
A40P:F82G,1.338
|
| 136 |
+
A40P:I105L,1.428
|
| 137 |
+
A40P:I105P,1.422
|
| 138 |
+
A40P:L21I,1.561
|
| 139 |
+
A40P:N52T,1.092
|
| 140 |
+
A40P:N52V,1.168
|
| 141 |
+
A40P:R18T,1.551
|
| 142 |
+
A40P:S24R,1.232
|
| 143 |
+
A40P:S28P,1.785
|
| 144 |
+
A40P:V84T,1.878
|
| 145 |
+
A40P:Y70F,1.359
|
| 146 |
+
A40P:Y93P,1.7
|
| 147 |
+
A40P:Y93W,1.186
|
| 148 |
+
A40P:F82Y,1.5
|
| 149 |
+
A40P:L53R,2.078
|
| 150 |
+
A40P:S30P,1.839
|
| 151 |
+
A40P:T20V,1.388
|
| 152 |
+
S51I:F82G,1.19
|
| 153 |
+
S51I:I105L,1.048
|
| 154 |
+
S51I:I105P,0.875
|
| 155 |
+
S51I:L21I,1.43
|
| 156 |
+
S51I:N52T,0.836
|
| 157 |
+
S51I:N52V,0.8
|
| 158 |
+
S51I:R18T,0.998
|
| 159 |
+
S51I:S24R,1.076
|
| 160 |
+
S51I:S28P,1.219
|
| 161 |
+
S51I:V84T,1.711
|
| 162 |
+
S51I:Y70F,1.28
|
| 163 |
+
S51I:Y93P,1.482
|
| 164 |
+
S51I:Y93W,1.313
|
| 165 |
+
S51I:F82Y,0.847
|
| 166 |
+
S51I:L53R,1.661
|
| 167 |
+
S51I:S30P,1.569
|
| 168 |
+
S51I:T20V,0.86
|
| 169 |
+
T116L:F82G,1.54
|
| 170 |
+
T116L:I105L,1.445
|
| 171 |
+
T116L:I105P,1.209
|
| 172 |
+
T116L:L21I,1.92
|
| 173 |
+
T116L:N52T,0.984
|
| 174 |
+
T116L:N52V,1.172
|
| 175 |
+
T116L:R18T,1.704
|
| 176 |
+
T116L:S24R,1.577
|
| 177 |
+
T116L:S28P,2.098
|
| 178 |
+
T116L:V84T,2.126
|
| 179 |
+
T116L:Y70F,1.7
|
| 180 |
+
T116L:Y93P,1.62
|
| 181 |
+
T116L:Y93W,1.477
|
| 182 |
+
T116L:F82Y,1.255
|
| 183 |
+
T116L:L53R,2.41
|
| 184 |
+
T116L:S30P,1.929
|
| 185 |
+
T116L:T20V,1.606
|
| 186 |
+
L11Q:F82G,1.913
|
| 187 |
+
L11Q:I105L,1.707
|
| 188 |
+
L11Q:I105P,1.222
|
| 189 |
+
L11Q:L21I,1.98
|
| 190 |
+
L11Q:N52T,1.143
|
| 191 |
+
L11Q:N52V,1.491
|
| 192 |
+
L11Q:R18T,1.548
|
| 193 |
+
L11Q:S24R,1.525
|
| 194 |
+
L11Q:S28P,2.08
|
| 195 |
+
L11Q:V84T,1.98
|
| 196 |
+
L11Q:Y70F,1.441
|
| 197 |
+
L11Q:Y93P,1.14
|
| 198 |
+
L11Q:Y93W,0.733
|
| 199 |
+
L11Q:F82Y,1.139
|
| 200 |
+
L11Q:L53R,1.958
|
| 201 |
+
L11Q:S30P,1.666
|
| 202 |
+
L11Q:T20V,1.258
|
| 203 |
+
Q13P:F82G,1.502
|
| 204 |
+
Q13P:I105L,1.548
|
| 205 |
+
Q13P:I105P,1.272
|
| 206 |
+
Q13P:L21I,1.73
|
| 207 |
+
Q13P:N52T,0.917
|
| 208 |
+
Q13P:N52V,1.245
|
| 209 |
+
Q13P:R18T,1.216
|
| 210 |
+
Q13P:S24R,1.462
|
| 211 |
+
Q13P:S28P,1.842
|
| 212 |
+
Q13P:V84T,2.395
|
| 213 |
+
Q13P:Y70F,1.99
|
| 214 |
+
Q13P:Y93P,1.895
|
| 215 |
+
Q13P:Y93W,1.31
|
| 216 |
+
Q13P:F82Y,1.325
|
| 217 |
+
Q13P:L53R,2.547
|
| 218 |
+
Q13P:S30P,1.959
|
| 219 |
+
Q13P:T20V,1.659
|
| 220 |
+
S63A:F82G,1.357
|
| 221 |
+
S63A:I105L,1.728
|
| 222 |
+
S63A:I105P,1.141
|
| 223 |
+
S63A:L21I,1.63
|
| 224 |
+
S63A:N52T,0.935
|
| 225 |
+
S63A:N52V,1.105
|
| 226 |
+
S63A:R18T,1.625
|
| 227 |
+
S63A:S24R,1.435
|
| 228 |
+
S63A:S28P,2.055
|
| 229 |
+
S63A:V84T,2.517
|
| 230 |
+
S63A:Y70F,1.623
|
| 231 |
+
S63A:Y93P,1.938
|
| 232 |
+
S63A:Y93W,1.44
|
| 233 |
+
S63A:F82Y,1.129
|
| 234 |
+
S63A:L53R,2.276
|
| 235 |
+
S63A:S30P,1.869
|
| 236 |
+
S63A:T20V,1.719
|
| 237 |
+
S65H:F82G,1.34
|
| 238 |
+
S65H:I105L,1.308
|
| 239 |
+
S65H:I105P,0.763
|
| 240 |
+
S65H:L21I,1.619
|
| 241 |
+
S65H:N52T,0.722
|
| 242 |
+
S65H:N52V,0.84
|
| 243 |
+
S65H:R18T,1.346
|
| 244 |
+
S65H:S24R,1.285
|
| 245 |
+
S65H:S28P,2.09
|
| 246 |
+
S65H:V84T,1.78
|
| 247 |
+
S65H:Y70F,1.235
|
| 248 |
+
S65H:Y93P,0.732
|
| 249 |
+
S65H:Y93W,0.685
|
| 250 |
+
S65H:F82Y,0.853
|
| 251 |
+
S65H:L53R,1.669
|
| 252 |
+
S65H:S30P,1.45
|
| 253 |
+
S65H:T20V,1.203
|
| 254 |
+
Q13P:L21I/S28P/L53R/V84T,2.018
|
data/example_multichain_protein/multichain_protein.cif
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/example_multichain_protein/vh_chain1.fasta
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
>vh_anticd122
|
| 2 |
+
EVQLVESGGGLVQPGGSLRLSCAASGFTFSDFYMEWVRQAPGKGLEWIAASRNKANDYTTEYSASVKGRFIVSRDDSKNSLYLQMNSLKTEDTAVYYCARSYYRYDGMDYWGQGTTVTVSS
|
data/example_multichain_protein/vl_chain2.fasta
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
>vl_anticd122
|
| 2 |
+
EIVLTQSPATLSLSPGERATLSCSAISSVSYMYWYQQKPGQAPRLLIYDTSNLVSGVPARFSGSGSGTDYTLTISSLEPEDFAVYYCQQWNTYPYTFGGGTKVEIK
|
data/example_protein/APEX_33overhang.fasta
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
>APEX_33overhang
|
| 2 |
+
TCTCGCACCATGGACTACAAGGATGACGACGATAAGGGAAAGTCTTACCCAACTGTGAGTGCTGATTACCAGGACGCCGTTGAGAAGGCGAAGAAGAAGCTCAGAGGCTTCATCGCTGAGAAGAGATGCGCTCCTCTAATGCTCCGTTTGGCATTCCACTCTGCTGGAACCTTTGACAAGGGCACGAAGACCGGTGGACCCTTCGGAACCATCAAGCACCCTGCCGAACTGGCTCACAGCGCTAACAACGGTCTTGACATCGCTGTTAGGCTTTTGGAGCCACTCAAGGCGGAGTTCCCTATTTTGAGCTACGCCGATTTCTACCAGTTGGCTGGCGTTGTTGCCGTTGAGGTCACGGGTGGACCTAAGGTTCCATTCCACCCTGGAAGAGAGGACAAGCCTGAGCCACCACCAGAGGGTCGCTTGCCCGATGCCACTAAGGGTTCTGACCATTTGAGAGATGTGTTTGGCAAAGCTATGGGGCTTACTGACCAAGATATCGTTGCTCTATCTGGGGGTCACACTATTGGAGCTGCACACAAGGAGCGTTCTGGATTTGAGGGTCCCTGGACCTCTAATCCTCTTATTTTCGACAACTCATACTTCACGGAGTTGTTGAGTGGTGAGAAGGAAGGTCTCCTTCAGCTACCTTCTGACAAGGCTCTTTTGTCTGACCCTGTATTCCGCCCTCTCGTTGACAAATATGCAGCGGACGAAGATGCCTTCTTTGCTGATTACGCTGAGGCTCACCAAAAGCTTTCCGAGCTTGGGTTTGCTGATGCCCTGCAGCTGCCTCCCCTGGAGCGCCTGACCCTGGACTAGTAAGCGAGACGAAGACGAATTCTCTAGATATAG
|
data/example_protein/MULTI-assembly_input.csv
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
N72A/A167R/T192K
|
| 2 |
+
A167R/T192K/D222E
|
| 3 |
+
S138A/A167R/T192K
|
| 4 |
+
N72A/A167R/T192K/D222E
|
| 5 |
+
N72A/S138A/A167R/T192K
|
| 6 |
+
N72A/A167R/T192K/D229A
|
| 7 |
+
N72A/S138A/A167R/T192K/D222E
|
| 8 |
+
N72A/A167R/T192K/D222E/D229A
|
| 9 |
+
K61R/N72A/A167R/T192K/D222E
|
| 10 |
+
N72A/S138A/A167R/T192K/D222E/D229A
|
| 11 |
+
K61R/N72A/S138A/A167R/T192K/D222E
|
| 12 |
+
K61R/N72A/A167R/T192K/D222E/D229A
|
| 13 |
+
K61R/N72A/S138A/A167R/T192K/D222E/D229A
|
| 14 |
+
N72A/S138A/A148Q/A167R/T192K/D222E/D229A
|
| 15 |
+
N72A/S138A/A167R/I185V/T192K/D222E/D229A
|
data/example_protein/apex.cif
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/example_protein/apex.fasta
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
>Lam2015_APEX_translation Expresses APEX2 in the cytosol of mammalian cells
|
| 2 |
+
MGKSYPTVSADYQDAVEKAKKKLRGFIAEKRCAPLMLRLAFHSAGTFDKGTKTGGPFGTIKHPAELAHSANNGLDIAVRLLEPLKAEFPILSYADFYQLAGVVAVEVTGGPKVPFHPGREDKPEPPPEGRLPDATKGSDHLRDVFGKAMGLTDQDIVALSGGHTIGAAHKERSGFEGPWTSNPLIFDNSYFTELLSGEKEGLLQLPSDKALLSDPVFRPLVDKYAADEDAFFADYAEAHQKLSELGFADA
|
data/example_protein/combo_muts.csv
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
T192V
|
| 2 |
+
T192K
|
| 3 |
+
A167R
|
| 4 |
+
N72A
|
| 5 |
+
D222E
|
| 6 |
+
A148Q
|
| 7 |
+
D229A
|
| 8 |
+
S138A
|
| 9 |
+
K61R
|
| 10 |
+
S196A
|
| 11 |
+
I185V
|
| 12 |
+
L84V
|
| 13 |
+
E87Q
|
| 14 |
+
G50R
|
| 15 |
+
L80M
|
data/example_protein/example_dataset.csv
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
mutation,property_value,round
|
| 2 |
+
WT,1,0
|
| 3 |
+
A167R/T192V,5.003,1
|
| 4 |
+
T192V,2.327,0
|
| 5 |
+
T192K,2.637,0
|
| 6 |
+
A167R,2.277,0
|
| 7 |
+
N72A,2.157,0
|
| 8 |
+
D222E,1.843,0
|
| 9 |
+
A148Q,1.733,0
|
| 10 |
+
D229A,1.73,0
|
| 11 |
+
S138A,1.697,0
|
| 12 |
+
K61R,1.66,0
|
| 13 |
+
S196A,1.527,0
|
| 14 |
+
I185V,1.443,0
|
| 15 |
+
L84V,1.527,0
|
| 16 |
+
E87Q,1.49,0
|
| 17 |
+
G50R,1.877,0
|
| 18 |
+
L80M,1.447,0
|
| 19 |
+
K21R,1.31,0
|
| 20 |
+
A148R,1.307,0
|
| 21 |
+
S69G,1.3,0
|
| 22 |
+
G50N,1.29,0
|
| 23 |
+
Q240L,1.277,0
|
| 24 |
+
P123A,1.267,0
|
| 25 |
+
E237K,1.213,0
|
| 26 |
+
K20R,1.19,0
|
| 27 |
+
L84I,1.19,0
|
| 28 |
+
S213T,1.19,0
|
| 29 |
+
D143E,1.153,0
|
| 30 |
+
A250D,1.14,0
|
| 31 |
+
Q154K,1.1,0
|
| 32 |
+
K21A,1.07,0
|
| 33 |
+
G2A,0.997,0
|
| 34 |
+
F47Y,0.993,0
|
| 35 |
+
K22D,0.937,0
|
| 36 |
+
E171D,0.897,0
|
| 37 |
+
R31N,0.86,0
|
| 38 |
+
S43E,0.83,0
|
| 39 |
+
F26L,0.823,0
|
| 40 |
+
I165L,0.803,0
|
| 41 |
+
E128A,0.717,0
|
| 42 |
+
G162A,0.603,0
|
| 43 |
+
Y93W,0.557,0
|
| 44 |
+
K49A,0.55,0
|
| 45 |
+
N182T,0.49,0
|
| 46 |
+
H116R,0.483,0
|
| 47 |
+
Q240E,0.36,0
|
| 48 |
+
H68Y,0.253,0
|
| 49 |
+
K3H,0.24,0
|
| 50 |
+
Q204L,0.223,0
|
| 51 |
+
K170G,0.197,0
|
| 52 |
+
F247C,0.15,0
|
| 53 |
+
H239W,0.137,0
|
| 54 |
+
H239L,0.033,0
|
| 55 |
+
L220W,0.753,0
|
| 56 |
+
A168C,0.297,0
|
| 57 |
+
G50E,1.423,0
|
| 58 |
+
V216G,0.903,0
|
| 59 |
+
A148H,1.297,0
|
| 60 |
+
G146I,1.387,0
|
| 61 |
+
K49V,0.663,0
|
| 62 |
+
R31P,0.237,0
|
| 63 |
+
G2P,0.863,0
|
| 64 |
+
P123L,0.987,0
|
| 65 |
+
E120I,1.033,0
|
| 66 |
+
L81C,0.417,0
|
| 67 |
+
L66Q,0.34,0
|
| 68 |
+
F57N,0.277,0
|
| 69 |
+
L35I,1.247,0
|
| 70 |
+
V102C,0.513,0
|
| 71 |
+
A10P,1.233,0
|
| 72 |
+
P123K,1.123,0
|
| 73 |
+
T7P,1.133,0
|
| 74 |
+
V216D,0.833,0
|
| 75 |
+
D143Y,0.74,0
|
| 76 |
+
E128P,1.067,0
|
| 77 |
+
E65M,0.5,0
|
| 78 |
+
R172Y,0.787,0
|
| 79 |
+
G25E,1.24,0
|
| 80 |
+
L37V,0.23,0
|
| 81 |
+
K85M,0.49,0
|
| 82 |
+
L141A,0.287,0
|
| 83 |
+
A148M,0.923,0
|
| 84 |
+
L205F,0.353,0
|
| 85 |
+
A167R/T192K,7.267,1
|
| 86 |
+
N72A/T192V,4.3,1
|
| 87 |
+
N72A/T192K,6.273,1
|
| 88 |
+
N72A/A167R,4.723,1
|
| 89 |
+
T192V/D222E,3.81,1
|
| 90 |
+
T192K/D222E,5.47,1
|
| 91 |
+
A167R/D222E,5.91,1
|
| 92 |
+
N72A/D222E,5.7,1
|
| 93 |
+
A148Q/T192V,2.147,1
|
| 94 |
+
A148Q/T192K,3.333,1
|
| 95 |
+
A148Q/A167R,3.46,1
|
| 96 |
+
N72A/A148Q,3.08,1
|
| 97 |
+
A148Q/D222E,3.193,1
|
| 98 |
+
T192V/D229A,3.17,1
|
| 99 |
+
T192K/D229A,4.523,1
|
| 100 |
+
A167R/D229A,4.557,1
|
| 101 |
+
N72A/D229A,3.973,1
|
| 102 |
+
D222E/D229A,3.023,1
|
| 103 |
+
A148Q/D229A,2.32,1
|
| 104 |
+
S138A/T192V,3.007,1
|
| 105 |
+
S138A/T192K,5.277,1
|
| 106 |
+
S138A/A167R,4.847,1
|
| 107 |
+
N72A/S138A,4.453,1
|
| 108 |
+
S138A/D222E,4.233,1
|
| 109 |
+
S138A/D229A,3.477,1
|
| 110 |
+
K61R/T192V,2.41,1
|
| 111 |
+
K61R/T192K,4.253,1
|
| 112 |
+
K61R/A167R,4.153,1
|
| 113 |
+
K61R/N72A,3.037,1
|
| 114 |
+
K61R/D222E,2.923,1
|
| 115 |
+
K61R/A148Q,2.417,1
|
| 116 |
+
K61R/D229A,2.873,1
|
| 117 |
+
K61R/S138A,3.103,1
|
| 118 |
+
T192V/S196A,3.627,1
|
| 119 |
+
A167R/S196A,3.24,1
|
| 120 |
+
N72A/S196A,2.697,1
|
| 121 |
+
S196A/D222E,2.26,1
|
| 122 |
+
A148Q/S196A,1.953,1
|
| 123 |
+
S196A/D229A,1.85,1
|
| 124 |
+
S138A/S196A,2.167,1
|
| 125 |
+
K61R/S196A,2.533,1
|
| 126 |
+
I185V/T192V,3.023,1
|
| 127 |
+
I185V/T192K,4.017,1
|
| 128 |
+
A167R/I185V,3.277,1
|
| 129 |
+
N72A/I185V,2.313,1
|
| 130 |
+
I185V/D222E,2.373,1
|
| 131 |
+
A148Q/I185V,2.013,1
|
| 132 |
+
I185V/D229A,1.977,1
|
| 133 |
+
S138A/I185V,2.453,1
|
| 134 |
+
K61R/I185V,2.293,1
|
| 135 |
+
I185V/S196A,2.06,1
|
| 136 |
+
L84V/T192V,1.963,1
|
| 137 |
+
L84V/T192K,3.53,1
|
| 138 |
+
L84V/A167R,3.033,1
|
| 139 |
+
N72A/L84V,2.573,1
|
| 140 |
+
L84V/D222E,2.43,1
|
| 141 |
+
L84V/A148Q,1.967,1
|
| 142 |
+
L84V/D229A,2.103,1
|
| 143 |
+
L84V/S138A,2.417,1
|
| 144 |
+
K61R/L84V,2.4,1
|
| 145 |
+
L84V/S196A,1.327,1
|
| 146 |
+
L84V/I185V,1.487,1
|
| 147 |
+
E87Q/T192V,1.8,1
|
| 148 |
+
E87Q/T192K,2.397,1
|
| 149 |
+
E87Q/A167R,3.04,1
|
| 150 |
+
N72A/E87Q,2.43,1
|
| 151 |
+
E87Q/D222E,2.597,1
|
| 152 |
+
E87Q/A148Q,1.99,1
|
| 153 |
+
E87Q/D229A,1.943,1
|
| 154 |
+
E87Q/S138A,1.297,1
|
| 155 |
+
K61R/E87Q,1.307,1
|
| 156 |
+
E87Q/S196A,1.32,1
|
| 157 |
+
E87Q/I185V,1.05,1
|
| 158 |
+
L84V/E87Q,1.113,1
|
| 159 |
+
G50R/T192V,1.913,1
|
| 160 |
+
G50R/T192K,2.997,1
|
| 161 |
+
G50R/A167R,3.003,1
|
| 162 |
+
G50R/N72A,2.48,1
|
| 163 |
+
G50R/D222E,1.83,1
|
| 164 |
+
G50R/A148Q,1.31,1
|
| 165 |
+
G50R/D229A,1.283,1
|
| 166 |
+
G50R/S138A,1.483,1
|
| 167 |
+
G50R/K61R,1.36,1
|
| 168 |
+
G50R/S196A,1.507,1
|
| 169 |
+
G50R/I185V,1.483,1
|
| 170 |
+
G50R/L84V,1.42,1
|
| 171 |
+
G50R/E87Q,1.577,1
|
| 172 |
+
L80M/T192V,1.84,1
|
| 173 |
+
L80M/T192K,2.83,1
|
| 174 |
+
L80M/A167R,2.517,1
|
| 175 |
+
N72A/L80M,2.14,1
|
| 176 |
+
L80M/D222E,2.127,1
|
| 177 |
+
L80M/A148Q,1.797,1
|
| 178 |
+
L80M/D229A,1.757,1
|
| 179 |
+
L80M/S138A,1.897,1
|
| 180 |
+
K61R/L80M,1.237,1
|
| 181 |
+
L80M/S196A,1.25,1
|
| 182 |
+
L80M/I185V,1.187,1
|
| 183 |
+
L80M/L84V,1.237,1
|
| 184 |
+
L80M/E87Q,0.827,1
|
| 185 |
+
G50R/L80M,1.353,1
|
data/example_protein/run.sh
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
module purge
|
| 3 |
+
module load sghpc-mpi-gcc/26.3
|
| 4 |
+
#module load compiler/dtk/25.04
|
| 5 |
+
source /public/software/apps/anaconda3/2023.09/etc/profile.d/conda.sh
|
| 6 |
+
conda activate model_bio
|
| 7 |
+
export WANDB_MODE=disabled
|
| 8 |
+
export WANDB_SILENT=true
|
| 9 |
+
export WANDB_DISABLED=true
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
export HIP_VISIBLE_DEVICES=1
|
| 13 |
+
|
| 14 |
+
#python -u ~/.conda/envs/model_bio/bin/p1_train.py \
|
| 15 |
+
# --experiment-name multievolve_example \
|
| 16 |
+
# --protein-name example_protein \
|
| 17 |
+
# --wt-files apex.fasta \
|
| 18 |
+
# --training-dataset-fname example_dataset.csv \
|
| 19 |
+
# --wandb-key dummy_key \
|
| 20 |
+
# --mode test
|
| 21 |
+
|
| 22 |
+
#检查模型文件
|
| 23 |
+
#find /public/home/scnb9biwet/jiangqq/MULTI-evolve-main/proteins/example_protein/model_cache -name "*.pth" -o -name "*.pt"
|
| 24 |
+
|
| 25 |
+
## Step 2:Propose MULTI-evolve Variants,利用训练好的模型来推荐突变
|
| 26 |
+
#python -u ~/.conda/envs/model_bio/bin/p2_propose.py \
|
| 27 |
+
#--experiment-name multievolve_example \
|
| 28 |
+
#--protein-name example_protein \
|
| 29 |
+
#--wt-files apex.fasta \
|
| 30 |
+
#--training-dataset example_dataset.csv \
|
| 31 |
+
#--mutation-pool combo_muts.csv \
|
| 32 |
+
#--top-muts-per-load 3 \
|
| 33 |
+
#--export-name multievolve_proposals
|
| 34 |
+
|
| 35 |
+
## step 3: 生成 MULTI-assembly 寡核苷酸
|
| 36 |
+
python -u ~/.conda/envs/model_bio/bin/p3_assembly_design.py \
|
| 37 |
+
--mutations-file ../../proteins/example_protein/multievolve_proposals.csv \
|
| 38 |
+
--wt-fasta APEX_33overhang.fasta \
|
| 39 |
+
--overhang 33 \
|
| 40 |
+
--species human \
|
| 41 |
+
--oligo-direction top \
|
| 42 |
+
--tm 80 \
|
| 43 |
+
--output design
|
env.yml
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# updated 5/21/2025
|
| 2 |
+
name: multievolve
|
| 3 |
+
channels:
|
| 4 |
+
- conda-forge
|
| 5 |
+
dependencies:
|
| 6 |
+
- python=3.11
|
| 7 |
+
- pip
|
| 8 |
+
- tqdm
|
| 9 |
+
- ipykernel
|
| 10 |
+
- numpy=1.26
|
| 11 |
+
- pandas=2.2
|
| 12 |
+
- matplotlib
|
| 13 |
+
- seaborn=0.13
|
| 14 |
+
- scipy=1.15
|
| 15 |
+
- biopython=1.85
|
| 16 |
+
- scikit-learn=1.6
|
| 17 |
+
- scikit-optimize=0.10
|
| 18 |
+
- pip:
|
| 19 |
+
- torch==2.6.0
|
| 20 |
+
- wandb==0.19
|
| 21 |
+
- Levenshtein
|
| 22 |
+
- streamlit==1.45
|
| 23 |
+
- fair-esm
|
| 24 |
+
- biotite==0.41.2
|
| 25 |
+
- ankh
|
| 26 |
+
- protobuf
|
env_mac.yml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# updated 5/21/2025
|
| 2 |
+
name: multievolve
|
| 3 |
+
channels:
|
| 4 |
+
- conda-forge
|
| 5 |
+
dependencies:
|
| 6 |
+
- python=3.11
|
| 7 |
+
- pip
|
| 8 |
+
- tqdm
|
| 9 |
+
- ipykernel
|
| 10 |
+
- numpy=1.26
|
| 11 |
+
- pandas=2.2
|
| 12 |
+
- matplotlib
|
| 13 |
+
- seaborn=0.13
|
| 14 |
+
- scipy=1.15
|
| 15 |
+
- biopython=1.85
|
| 16 |
+
- scikit-learn=1.6
|
| 17 |
+
- scikit-optimize=0.10
|
| 18 |
+
- pip:
|
| 19 |
+
- torch==2.2.2
|
| 20 |
+
- transformers==4.42.4
|
| 21 |
+
- wandb==0.19
|
| 22 |
+
- Levenshtein
|
| 23 |
+
- streamlit==1.45
|
| 24 |
+
- fair-esm
|
| 25 |
+
- biotite==0.41.2
|
| 26 |
+
- ankh
|
| 27 |
+
- protobuf
|
hub/checkpoints/esm2_t36_3B_UR50D-contact-regression.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4da500eab246481dc9c8c95bc7b1d02f2803d761c380b0e95186d4a07d0fc84e
|
| 3 |
+
size 6759
|
model/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Import main components
|
| 2 |
+
from model.splitters import *
|
| 3 |
+
from model.predictors import *
|
| 4 |
+
from model.proposers import *
|
| 5 |
+
from model.utils import *
|
| 6 |
+
from model.featurizers import *
|
model/featurizers/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from model.featurizers.base_featurizers import *
|
| 2 |
+
from model.featurizers.esm_featurizers import *
|
| 3 |
+
from model.featurizers.msa_featurizers import *
|
| 4 |
+
from model.featurizers.zeroshot_featurizers import *
|
| 5 |
+
from model.featurizers.combinatorial_featurizers import *
|
| 6 |
+
from model.featurizers.ankh_featurizers import *
|
| 7 |
+
from model.featurizers.prott5_featurizers import *
|
model/featurizers/ankh_featurizers.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ankh
|
| 2 |
+
import numpy as np
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from model.featurizers.base_featurizers import BaseFeaturizer
|
| 6 |
+
|
| 7 |
+
# alternate name: AnkhBaseFeaturizer
|
| 8 |
+
class AnkhFeaturizer(BaseFeaturizer):
|
| 9 |
+
def __init__(self,
|
| 10 |
+
protein=None,
|
| 11 |
+
use_cache=False,
|
| 12 |
+
model_version=None,
|
| 13 |
+
batch_size=968,
|
| 14 |
+
model_type="ankh",
|
| 15 |
+
**kwargs):
|
| 16 |
+
|
| 17 |
+
super().__init__(model_type,protein, use_cache, **kwargs)
|
| 18 |
+
|
| 19 |
+
self.batch_size = batch_size
|
| 20 |
+
self.model_version = model_version
|
| 21 |
+
|
| 22 |
+
def featurize_ankh(self, seqs):
|
| 23 |
+
|
| 24 |
+
if self.model_version == 'large':
|
| 25 |
+
self.model, self.tokenizer = ankh.load_large_model()
|
| 26 |
+
elif self.model_version == 'base':
|
| 27 |
+
self.model, self.tokenizer = ankh.load_base_model()
|
| 28 |
+
else:
|
| 29 |
+
raise ValueError(f"Invalid model version: {self.model_version}")
|
| 30 |
+
self.model.eval()
|
| 31 |
+
self.model.to(self.device)
|
| 32 |
+
|
| 33 |
+
input_seqs = [list(seq) for seq in seqs]
|
| 34 |
+
|
| 35 |
+
seq_batch = []
|
| 36 |
+
|
| 37 |
+
for i in range(0, len(input_seqs), self.batch_size):
|
| 38 |
+
batch = input_seqs[i:i + self.batch_size]
|
| 39 |
+
outputs = self.tokenizer(
|
| 40 |
+
batch,
|
| 41 |
+
add_special_tokens=True,
|
| 42 |
+
padding=True,
|
| 43 |
+
is_split_into_words=True,
|
| 44 |
+
return_tensors="pt",
|
| 45 |
+
)
|
| 46 |
+
outputs = {key: val.to(self.device) for key, val in outputs.items()}
|
| 47 |
+
with torch.no_grad():
|
| 48 |
+
embeddings = self.model(input_ids=outputs['input_ids'], attention_mask=outputs['attention_mask'])
|
| 49 |
+
seq_batch.append(embeddings['last_hidden_state'].mean(axis=1).cpu().numpy())
|
| 50 |
+
|
| 51 |
+
return np.concatenate(seq_batch)
|
| 52 |
+
|
| 53 |
+
# alternate name: AnkhBaseEmbedFeaturizer
|
| 54 |
+
class AnkhBaseFeaturizer(AnkhFeaturizer):
|
| 55 |
+
def __init__(self,
|
| 56 |
+
protein=None,
|
| 57 |
+
use_cache=False,
|
| 58 |
+
model_version="base",
|
| 59 |
+
batch_size=968,
|
| 60 |
+
model_type="ankh_base",
|
| 61 |
+
**kwargs):
|
| 62 |
+
super().__init__(protein, use_cache, model_version, batch_size, model_type, **kwargs)
|
| 63 |
+
|
| 64 |
+
def custom_featurizer(self, seqs):
|
| 65 |
+
|
| 66 |
+
X = self.featurize_ankh(seqs)
|
| 67 |
+
return X
|
| 68 |
+
|
| 69 |
+
# alternate name: AnkhLargeEmbedFeaturizer
|
| 70 |
+
class AnkhLargeFeaturizer(AnkhFeaturizer):
|
| 71 |
+
def __init__(self,
|
| 72 |
+
protein=None,
|
| 73 |
+
use_cache=False,
|
| 74 |
+
model_version="large",
|
| 75 |
+
batch_size=968,
|
| 76 |
+
model_type="ankh_large",
|
| 77 |
+
**kwargs):
|
| 78 |
+
super().__init__(protein, use_cache, model_version, batch_size, model_type, **kwargs)
|
| 79 |
+
|
| 80 |
+
def custom_featurizer(self, seqs):
|
| 81 |
+
X = self.featurize_ankh(seqs)
|
| 82 |
+
return X
|
model/featurizers/base_featurizers.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
import numpy as np
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
# root_folder = os.path.dirname(os.path.dirname(__file__))
|
| 6 |
+
# sys.path.append(root_folder)
|
| 7 |
+
|
| 8 |
+
from model.utils.other_utils import AAs
|
| 9 |
+
from model.utils.featurizer_utils import seqs_to_georgiev, featurize_aa_idx
|
| 10 |
+
from model.utils.cache_utils import load_cache, update_cache
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class BaseFeaturizer(ABC):
|
| 14 |
+
"""Abstract base class for featurizers.
|
| 15 |
+
|
| 16 |
+
Attributes:
|
| 17 |
+
model_type (str): Type of featurization model to use.
|
| 18 |
+
name (str): Name of the featurizer.
|
| 19 |
+
protein (str): Name of protein being featurized.
|
| 20 |
+
use_cache (bool): Whether to cache featurization results.
|
| 21 |
+
flatten_features (bool): Whether to flatten output features.
|
| 22 |
+
device (torch.device): Device to use for computation.
|
| 23 |
+
|
| 24 |
+
Example Usage:
|
| 25 |
+
|
| 26 |
+
featurizer = BaseFeaturizer(
|
| 27 |
+
model_type='onehot', # Type of featurization used
|
| 28 |
+
protein='protein1', # Name of protein for caching
|
| 29 |
+
use_cache=True, # Whether to cache results
|
| 30 |
+
flatten_features=False # Whether to flatten output features
|
| 31 |
+
)
|
| 32 |
+
features = featurizer.featurize(sequences)
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
def __init__(self, model_type=None, protein=None, use_cache=False, flatten_features=False, **kwargs):
|
| 36 |
+
"""
|
| 37 |
+
Args:
|
| 38 |
+
model_type (str): Type of featurization model.
|
| 39 |
+
protein (str): Name of protein being featurized.
|
| 40 |
+
use_cache (bool): Whether to cache results.
|
| 41 |
+
flatten_features (bool): Whether to flatten output features.
|
| 42 |
+
**kwargs: Additional keyword arguments.
|
| 43 |
+
"""
|
| 44 |
+
self.model_type = model_type
|
| 45 |
+
self.name = str(model_type)
|
| 46 |
+
self.protein = protein
|
| 47 |
+
self.use_cache = use_cache
|
| 48 |
+
self.flatten_features = flatten_features
|
| 49 |
+
self.set_parameters(**kwargs)
|
| 50 |
+
|
| 51 |
+
if torch.backends.mps.is_available():
|
| 52 |
+
self.device = torch.device("mps")
|
| 53 |
+
elif torch.cuda.is_available():
|
| 54 |
+
self.device = torch.device("cuda:0")
|
| 55 |
+
else:
|
| 56 |
+
self.device = torch.device("cpu")
|
| 57 |
+
|
| 58 |
+
def set_parameters(self, **kwargs):
|
| 59 |
+
"""Sets additional parameters from kwargs."""
|
| 60 |
+
for key, value in kwargs.items():
|
| 61 |
+
setattr(self, key, value)
|
| 62 |
+
|
| 63 |
+
def load_features(self, seqs):
|
| 64 |
+
"""
|
| 65 |
+
Loads cached features if they exist.
|
| 66 |
+
|
| 67 |
+
Args:
|
| 68 |
+
seqs (list): List of sequences to featurize.
|
| 69 |
+
|
| 70 |
+
Returns:
|
| 71 |
+
tuple: (seq_to_feature dict, original sequences, unique sorted sequences)
|
| 72 |
+
"""
|
| 73 |
+
print("Loading features...")
|
| 74 |
+
|
| 75 |
+
assert (
|
| 76 |
+
self.protein is not None
|
| 77 |
+
), "No protein specified to cache. Either specify a protein or set use_cache to False."
|
| 78 |
+
original_seqs = seqs
|
| 79 |
+
cache = load_cache(self.model_type, self.protein)
|
| 80 |
+
seq_to_feature = {seq: cache[seq] for seq in seqs if seq in cache}
|
| 81 |
+
seqs = [seq for seq in seqs if seq not in cache]
|
| 82 |
+
print(f"Seqs in cache: {len(seq_to_feature)} | Seqs not in cache: {len(seqs)}")
|
| 83 |
+
del cache # free up memory
|
| 84 |
+
|
| 85 |
+
unique_seqs = {}
|
| 86 |
+
for seq in seqs:
|
| 87 |
+
if seq not in unique_seqs:
|
| 88 |
+
unique_seqs[seq] = len(unique_seqs)
|
| 89 |
+
|
| 90 |
+
unique_seqs_sorted = sorted(unique_seqs.keys(), key=lambda k: unique_seqs[k])
|
| 91 |
+
|
| 92 |
+
return seq_to_feature, original_seqs, unique_seqs_sorted
|
| 93 |
+
|
| 94 |
+
@abstractmethod
|
| 95 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 96 |
+
"""
|
| 97 |
+
Custom featurizer to be implemented in subclasses.
|
| 98 |
+
|
| 99 |
+
Args:
|
| 100 |
+
seqs (list): List of sequences to featurize.
|
| 101 |
+
**kwargs: Additional keyword arguments.
|
| 102 |
+
"""
|
| 103 |
+
pass
|
| 104 |
+
|
| 105 |
+
def featurize(self, seqs, **kwargs):
|
| 106 |
+
"""
|
| 107 |
+
Featurizes input sequences.
|
| 108 |
+
|
| 109 |
+
Args:
|
| 110 |
+
seqs (list): List of sequences to featurize.
|
| 111 |
+
**kwargs: Additional keyword arguments.
|
| 112 |
+
|
| 113 |
+
Returns:
|
| 114 |
+
np.ndarray: Array of featurized sequences.
|
| 115 |
+
"""
|
| 116 |
+
if self.use_cache:
|
| 117 |
+
seqs_to_feature, original_seqs, unique_seqs_sorted = self.load_features(
|
| 118 |
+
seqs
|
| 119 |
+
)
|
| 120 |
+
else:
|
| 121 |
+
seqs_to_feature = {}
|
| 122 |
+
original_seqs, unique_seqs_sorted = seqs, seqs
|
| 123 |
+
|
| 124 |
+
if len(unique_seqs_sorted) > 0:
|
| 125 |
+
X_unique = self.custom_featurizer(unique_seqs_sorted, **kwargs)
|
| 126 |
+
|
| 127 |
+
for idx, seq in enumerate(unique_seqs_sorted):
|
| 128 |
+
seqs_to_feature[seq] = X_unique[idx]
|
| 129 |
+
|
| 130 |
+
if self.use_cache:
|
| 131 |
+
update_cache(self.model_type, self.protein, seqs_to_feature)
|
| 132 |
+
|
| 133 |
+
X = np.array([seqs_to_feature[seq] for seq in original_seqs])
|
| 134 |
+
|
| 135 |
+
if self.flatten_features == True:
|
| 136 |
+
X = X.reshape(len(X), -1)
|
| 137 |
+
|
| 138 |
+
return X
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
class OneHotFeaturizer(BaseFeaturizer):
|
| 142 |
+
"""Class for one-hot encoding of sequences.
|
| 143 |
+
|
| 144 |
+
Attributes:
|
| 145 |
+
model_type (str): Type of featurization model to use.
|
| 146 |
+
name (str): Name of the featurizer.
|
| 147 |
+
protein (str): Name of protein being featurized.
|
| 148 |
+
use_cache (bool): Whether to cache featurization results.
|
| 149 |
+
flatten_features (bool): Whether to flatten output features.
|
| 150 |
+
device (torch.device): Device to use for computation.
|
| 151 |
+
|
| 152 |
+
Example Usage:
|
| 153 |
+
|
| 154 |
+
featurizer = OneHotFeaturizer(
|
| 155 |
+
protein='protein1', # Name of protein for caching
|
| 156 |
+
use_cache=True, # Whether to cache results
|
| 157 |
+
flatten_features=False # Whether to flatten output features
|
| 158 |
+
)
|
| 159 |
+
features = featurizer.featurize(sequences)
|
| 160 |
+
"""
|
| 161 |
+
|
| 162 |
+
def __init__(self, **kwargs) -> None:
|
| 163 |
+
super().__init__(model_type="onehot", **kwargs)
|
| 164 |
+
|
| 165 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 166 |
+
"""
|
| 167 |
+
One-hot encodes sequences.
|
| 168 |
+
|
| 169 |
+
Args:
|
| 170 |
+
seqs (list): List of sequences to encode.
|
| 171 |
+
**kwargs: Additional keyword arguments.
|
| 172 |
+
|
| 173 |
+
Returns:
|
| 174 |
+
np.ndarray: One-hot encoded sequences.
|
| 175 |
+
"""
|
| 176 |
+
data = [[char for char in seq] for seq in seqs]
|
| 177 |
+
|
| 178 |
+
from sklearn.preprocessing import OneHotEncoder
|
| 179 |
+
|
| 180 |
+
enc = OneHotEncoder(
|
| 181 |
+
categories=([AAs + ["X"]] * len(data[0])),
|
| 182 |
+
sparse_output=False,
|
| 183 |
+
)
|
| 184 |
+
X = enc.fit_transform(data).reshape(len(data), len(data[0]), len(AAs + ["X"]))
|
| 185 |
+
|
| 186 |
+
return X
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
class GeorgievFeaturizer(BaseFeaturizer):
|
| 190 |
+
"""Class for Georgiev encoding of sequences.
|
| 191 |
+
|
| 192 |
+
Attributes:
|
| 193 |
+
model_type (str): Type of featurization model to use.
|
| 194 |
+
name (str): Name of the featurizer.
|
| 195 |
+
protein (str): Name of protein being featurized.
|
| 196 |
+
use_cache (bool): Whether to cache featurization results.
|
| 197 |
+
flatten_features (bool): Whether to flatten output features.
|
| 198 |
+
device (torch.device): Device to use for computation.
|
| 199 |
+
|
| 200 |
+
Example Usage:
|
| 201 |
+
|
| 202 |
+
featurizer = GeorgievFeaturizer(
|
| 203 |
+
protein='protein1', # Name of protein for caching
|
| 204 |
+
use_cache=True, # Whether to cache results
|
| 205 |
+
flatten_features=False # Whether to flatten output features
|
| 206 |
+
)
|
| 207 |
+
features = featurizer.featurize(sequences)
|
| 208 |
+
"""
|
| 209 |
+
|
| 210 |
+
def __init__(self, **kwargs) -> None:
|
| 211 |
+
super().__init__(model_type="georgiev", **kwargs)
|
| 212 |
+
|
| 213 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 214 |
+
"""
|
| 215 |
+
Applies Georgiev encoding to sequences.
|
| 216 |
+
|
| 217 |
+
Args:
|
| 218 |
+
seqs (list): List of sequences to encode.
|
| 219 |
+
**kwargs: Additional keyword arguments.
|
| 220 |
+
|
| 221 |
+
Returns:
|
| 222 |
+
np.ndarray: Georgiev encoded sequences.
|
| 223 |
+
"""
|
| 224 |
+
X = seqs_to_georgiev(seqs)
|
| 225 |
+
|
| 226 |
+
return X
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
class AAIdxFeaturizer(BaseFeaturizer):
|
| 230 |
+
"""Class for amino acid index encoding of sequences.
|
| 231 |
+
|
| 232 |
+
Attributes:
|
| 233 |
+
model_type (str): Type of featurization model to use.
|
| 234 |
+
name (str): Name of the featurizer.
|
| 235 |
+
protein (str): Name of protein being featurized.
|
| 236 |
+
use_cache (bool): Whether to cache featurization results.
|
| 237 |
+
flatten_features (bool): Whether to flatten output features.
|
| 238 |
+
device (torch.device): Device to use for computation.
|
| 239 |
+
|
| 240 |
+
Example Usage:
|
| 241 |
+
|
| 242 |
+
featurizer = AAIdxFeaturizer(
|
| 243 |
+
protein='protein1', # Name of protein for caching
|
| 244 |
+
use_cache=True, # Whether to cache results
|
| 245 |
+
flatten_features=False # Whether to flatten output features
|
| 246 |
+
)
|
| 247 |
+
features = featurizer.featurize(sequences)
|
| 248 |
+
"""
|
| 249 |
+
|
| 250 |
+
def __init__(self, **kwargs) -> None:
|
| 251 |
+
super().__init__(model_type="aa_idx", **kwargs)
|
| 252 |
+
|
| 253 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 254 |
+
"""
|
| 255 |
+
Applies amino acid index encoding to sequences.
|
| 256 |
+
|
| 257 |
+
Args:
|
| 258 |
+
seqs (list): List of sequences to encode.
|
| 259 |
+
**kwargs: Additional keyword arguments.
|
| 260 |
+
|
| 261 |
+
Returns:
|
| 262 |
+
np.ndarray: Amino acid index encoded sequences.
|
| 263 |
+
"""
|
| 264 |
+
X = featurize_aa_idx(seqs)
|
| 265 |
+
|
| 266 |
+
return X
|
model/featurizers/combinatorial_featurizers.py
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from model.featurizers.base_featurizers import *
|
| 2 |
+
from model.featurizers.esm_featurizers import *
|
| 3 |
+
from model.featurizers.msa_featurizers import *
|
| 4 |
+
from model.featurizers.zeroshot_featurizers import *
|
| 5 |
+
|
| 6 |
+
from model.featurizers.model_choices import FEATURIZE_CHOICES
|
| 7 |
+
|
| 8 |
+
FEATURIZE_CLASSES = {
|
| 9 |
+
# Dictionary of model names to model classes.
|
| 10 |
+
# Base Featurizers
|
| 11 |
+
"onehot": OneHotFeaturizer,
|
| 12 |
+
"georgiev": GeorgievFeaturizer,
|
| 13 |
+
"aa_idx": AAIdxFeaturizer,
|
| 14 |
+
# MSA Featurizers
|
| 15 |
+
"msa_embed": MSAEmbedFeaturizer,
|
| 16 |
+
"msa_sequence_embed": MSASequenceEmbedFeaturizer,
|
| 17 |
+
"msa_logits": MSALogitsFeaturizer,
|
| 18 |
+
# ESM Featurizers
|
| 19 |
+
"esm_logits": ESMLogitsFeaturizer,
|
| 20 |
+
"esm_embed_1v": ESM1vEmbedFeaturizer,
|
| 21 |
+
"esm_embed_2_3b": ESM2EmbedFeaturizer,
|
| 22 |
+
"esm_embed_2_15b": ESM2_15b_EmbedFeaturizer,
|
| 23 |
+
# Zeroshot Featurizers
|
| 24 |
+
"zeroshot_msa": ZeroshotMSAFeaturizer,
|
| 25 |
+
"zeroshot_esm": ZeroshotESMFeaturizer,
|
| 26 |
+
"zeroshot_prose": ZeroshotProseFeaturizer,
|
| 27 |
+
"zeroshot_cscs": ZeroshotCSCSFeaturizer,
|
| 28 |
+
"zeroshot_cscs_gram": ZeroshotCSCSGramFeaturizer,
|
| 29 |
+
"zeroshot_cscs_sem": ZeroshotCSCSSemFeaturizer,
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class CombinatorialFeaturizer():
|
| 34 |
+
"""Base class for combining multiple featurizers.
|
| 35 |
+
|
| 36 |
+
Attributes:
|
| 37 |
+
name (str): Name of the combined featurizer.
|
| 38 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 39 |
+
|
| 40 |
+
Example Usage:
|
| 41 |
+
|
| 42 |
+
featurizer = CombinatorialFeaturizer(
|
| 43 |
+
featurize_methods=['onehot', 'georgiev'], # List of featurizers to combine
|
| 44 |
+
protein='protein1', # Name of protein for caching
|
| 45 |
+
use_cache=True # Whether to cache results
|
| 46 |
+
)
|
| 47 |
+
features = featurizer.featurize(sequences)
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
def __init__(self, featurize_methods, **kwargs):
|
| 51 |
+
"""
|
| 52 |
+
Args:
|
| 53 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 54 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 55 |
+
"""
|
| 56 |
+
for featurize_method in featurize_methods:
|
| 57 |
+
assert (
|
| 58 |
+
featurize_method in FEATURIZE_CHOICES
|
| 59 |
+
), f"{featurize_method} not in {FEATURIZE_CHOICES}"
|
| 60 |
+
|
| 61 |
+
model_type = "-".join(featurize_methods)
|
| 62 |
+
self.name = str(model_type)
|
| 63 |
+
|
| 64 |
+
self.featurizers = {
|
| 65 |
+
featurize_method: FEATURIZE_CLASSES[featurize_method](**kwargs)
|
| 66 |
+
for featurize_method in featurize_methods
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
def featurize(self, seqs, **kwargs):
|
| 70 |
+
"""
|
| 71 |
+
Featurizes sequences using all component featurizers.
|
| 72 |
+
|
| 73 |
+
Args:
|
| 74 |
+
seqs (list): List of sequences to featurize.
|
| 75 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 76 |
+
|
| 77 |
+
Returns:
|
| 78 |
+
np.ndarray: Combined features from all featurizers.
|
| 79 |
+
"""
|
| 80 |
+
X = []
|
| 81 |
+
for featurizer in self.featurizers.values():
|
| 82 |
+
X.append(featurizer.featurize(seqs, **kwargs))
|
| 83 |
+
|
| 84 |
+
X = np.concatenate(X, axis=-1)
|
| 85 |
+
|
| 86 |
+
return X
|
| 87 |
+
|
| 88 |
+
class ESMAugmentedFeaturizer(CombinatorialFeaturizer):
|
| 89 |
+
"""Class for combining ESM features with one-hot encoding.
|
| 90 |
+
|
| 91 |
+
Attributes:
|
| 92 |
+
name (str): Name of the combined featurizer.
|
| 93 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 94 |
+
|
| 95 |
+
Example Usage:
|
| 96 |
+
|
| 97 |
+
featurizer = ESMAugmentedFeaturizer(
|
| 98 |
+
protein='protein1', # Name of protein for caching
|
| 99 |
+
use_cache=True # Whether to cache results
|
| 100 |
+
)
|
| 101 |
+
features = featurizer.featurize(sequences)
|
| 102 |
+
"""
|
| 103 |
+
|
| 104 |
+
def __init__(self, featurize_methods=["zeroshot_esm", "onehot"], **kwargs):
|
| 105 |
+
"""
|
| 106 |
+
Args:
|
| 107 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 108 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 109 |
+
"""
|
| 110 |
+
super().__init__(featurize_methods, **kwargs)
|
| 111 |
+
|
| 112 |
+
def featurize(self, seqs, **kwargs):
|
| 113 |
+
"""
|
| 114 |
+
Featurizes sequences using ESM and one-hot encoding.
|
| 115 |
+
|
| 116 |
+
Args:
|
| 117 |
+
seqs (list): List of sequences to featurize.
|
| 118 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
np.ndarray: Combined ESM and one-hot features.
|
| 122 |
+
"""
|
| 123 |
+
X = []
|
| 124 |
+
|
| 125 |
+
featurizer_0 = list(self.featurizers.values())[0]
|
| 126 |
+
X.append(featurizer_0.featurize(seqs, **kwargs))
|
| 127 |
+
|
| 128 |
+
featurizer_1 = list(self.featurizers.values())[1]
|
| 129 |
+
onehot = featurizer_1.featurize(seqs, **kwargs)
|
| 130 |
+
X.append(onehot.reshape(onehot.shape[0], -1))
|
| 131 |
+
|
| 132 |
+
X = np.concatenate(X, axis=1)
|
| 133 |
+
|
| 134 |
+
return X
|
| 135 |
+
|
| 136 |
+
class MSAAugmentedFeaturizer(CombinatorialFeaturizer):
|
| 137 |
+
"""Class for combining MSA features with one-hot encoding.
|
| 138 |
+
|
| 139 |
+
Attributes:
|
| 140 |
+
name (str): Name of the combined featurizer.
|
| 141 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 142 |
+
|
| 143 |
+
Example Usage:
|
| 144 |
+
|
| 145 |
+
featurizer = MSAAugmentedFeaturizer(
|
| 146 |
+
protein='protein1', # Name of protein for caching
|
| 147 |
+
use_cache=True # Whether to cache results
|
| 148 |
+
)
|
| 149 |
+
features = featurizer.featurize(sequences)
|
| 150 |
+
"""
|
| 151 |
+
|
| 152 |
+
def __init__(self, featurize_methods=["zeroshot_msa", "onehot"], **kwargs):
|
| 153 |
+
"""
|
| 154 |
+
Args:
|
| 155 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 156 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 157 |
+
"""
|
| 158 |
+
super().__init__(featurize_methods, **kwargs)
|
| 159 |
+
|
| 160 |
+
def featurize(self, seqs, **kwargs):
|
| 161 |
+
"""
|
| 162 |
+
Featurizes sequences using MSA and one-hot encoding.
|
| 163 |
+
|
| 164 |
+
Args:
|
| 165 |
+
seqs (list): List of sequences to featurize.
|
| 166 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 167 |
+
|
| 168 |
+
Returns:
|
| 169 |
+
np.ndarray: Combined MSA and one-hot features.
|
| 170 |
+
"""
|
| 171 |
+
X = []
|
| 172 |
+
|
| 173 |
+
featurizer_0 = list(self.featurizers.values())[0]
|
| 174 |
+
X.append(featurizer_0.featurize(seqs, **kwargs))
|
| 175 |
+
|
| 176 |
+
featurizer_1 = list(self.featurizers.values())[1]
|
| 177 |
+
onehot = featurizer_1.featurize(seqs, **kwargs)
|
| 178 |
+
X.append(onehot.reshape(onehot.shape[0], -1))
|
| 179 |
+
|
| 180 |
+
X = np.concatenate(X, axis=1)
|
| 181 |
+
|
| 182 |
+
return X
|
| 183 |
+
|
| 184 |
+
class OnehotAndGeorgievFeaturizer(CombinatorialFeaturizer):
|
| 185 |
+
"""Class for combining one-hot and Georgiev encodings.
|
| 186 |
+
|
| 187 |
+
Attributes:
|
| 188 |
+
name (str): Name of the combined featurizer.
|
| 189 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 190 |
+
|
| 191 |
+
Example Usage:
|
| 192 |
+
|
| 193 |
+
featurizer = OnehotAndGeorgievFeaturizer(
|
| 194 |
+
protein='protein1', # Name of protein for caching
|
| 195 |
+
use_cache=True # Whether to cache results
|
| 196 |
+
)
|
| 197 |
+
features = featurizer.featurize(sequences)
|
| 198 |
+
"""
|
| 199 |
+
|
| 200 |
+
def __init__(self, featurize_methods=["onehot", "georgiev"], **kwargs):
|
| 201 |
+
"""
|
| 202 |
+
Args:
|
| 203 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 204 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 205 |
+
"""
|
| 206 |
+
super().__init__(featurize_methods, **kwargs)
|
| 207 |
+
|
| 208 |
+
class OnehotAndAAIdxFeaturizer(CombinatorialFeaturizer):
|
| 209 |
+
"""Class for combining one-hot and amino acid index encodings.
|
| 210 |
+
|
| 211 |
+
Attributes:
|
| 212 |
+
name (str): Name of the combined featurizer.
|
| 213 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 214 |
+
|
| 215 |
+
Example Usage:
|
| 216 |
+
|
| 217 |
+
featurizer = OnehotAndAAIdxFeaturizer(
|
| 218 |
+
protein='protein1', # Name of protein for caching
|
| 219 |
+
use_cache=True # Whether to cache results
|
| 220 |
+
)
|
| 221 |
+
features = featurizer.featurize(sequences)
|
| 222 |
+
"""
|
| 223 |
+
|
| 224 |
+
def __init__(self, featurize_methods=["onehot", "aa_idx"], **kwargs):
|
| 225 |
+
"""
|
| 226 |
+
Args:
|
| 227 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 228 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 229 |
+
"""
|
| 230 |
+
super().__init__(featurize_methods, **kwargs)
|
| 231 |
+
|
| 232 |
+
class OnehotAndESMLogitsFeaturizer(CombinatorialFeaturizer):
|
| 233 |
+
"""Class for combining one-hot encoding with ESM logits.
|
| 234 |
+
|
| 235 |
+
Attributes:
|
| 236 |
+
name (str): Name of the combined featurizer.
|
| 237 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 238 |
+
|
| 239 |
+
Example Usage:
|
| 240 |
+
|
| 241 |
+
featurizer = OnehotAndESMLogitsFeaturizer(
|
| 242 |
+
protein='protein1', # Name of protein for caching
|
| 243 |
+
use_cache=True # Whether to cache results
|
| 244 |
+
)
|
| 245 |
+
features = featurizer.featurize(sequences)
|
| 246 |
+
"""
|
| 247 |
+
|
| 248 |
+
def __init__(self, featurize_methods=["onehot", "esm_logits"], **kwargs):
|
| 249 |
+
"""
|
| 250 |
+
Args:
|
| 251 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 252 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 253 |
+
"""
|
| 254 |
+
super().__init__(featurize_methods, **kwargs)
|
| 255 |
+
|
| 256 |
+
def featurize(self, seqs, **kwargs):
|
| 257 |
+
"""
|
| 258 |
+
Featurizes sequences using one-hot encoding and ESM logits.
|
| 259 |
+
|
| 260 |
+
Args:
|
| 261 |
+
seqs (list): List of sequences to featurize.
|
| 262 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 263 |
+
|
| 264 |
+
Returns:
|
| 265 |
+
np.ndarray: Combined one-hot and ESM logits features.
|
| 266 |
+
"""
|
| 267 |
+
X = []
|
| 268 |
+
|
| 269 |
+
featurizer_0 = list(self.featurizers.values())[0]
|
| 270 |
+
x = featurizer_0.featurize(seqs, **kwargs)
|
| 271 |
+
zero_vectors = np.zeros((x.shape[0], 1, x.shape[2]))
|
| 272 |
+
X.append(np.concatenate((zero_vectors, x, zero_vectors), axis=1))
|
| 273 |
+
|
| 274 |
+
featurizer_1 = list(self.featurizers.values())[1]
|
| 275 |
+
X.append(featurizer_1.featurize(seqs, **kwargs))
|
| 276 |
+
|
| 277 |
+
X = np.concatenate(X, axis=-1)
|
| 278 |
+
|
| 279 |
+
return X
|
| 280 |
+
|
| 281 |
+
class OnehotAndESMMSALogitsFeaturizer(CombinatorialFeaturizer):
|
| 282 |
+
"""Class for combining one-hot encoding with ESM-MSA logits.
|
| 283 |
+
|
| 284 |
+
Attributes:
|
| 285 |
+
name (str): Name of the combined featurizer.
|
| 286 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 287 |
+
|
| 288 |
+
Example Usage:
|
| 289 |
+
|
| 290 |
+
featurizer = OnehotAndESMMSALogitsFeaturizer(
|
| 291 |
+
protein='protein1', # Name of protein for caching
|
| 292 |
+
use_cache=True # Whether to cache results
|
| 293 |
+
)
|
| 294 |
+
features = featurizer.featurize(sequences)
|
| 295 |
+
"""
|
| 296 |
+
|
| 297 |
+
def __init__(self, featurize_methods=["onehot", "msa_logits"], **kwargs):
|
| 298 |
+
"""
|
| 299 |
+
Args:
|
| 300 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 301 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 302 |
+
"""
|
| 303 |
+
super().__init__(featurize_methods, **kwargs)
|
| 304 |
+
|
| 305 |
+
def featurize(self, seqs, **kwargs):
|
| 306 |
+
"""
|
| 307 |
+
Featurizes sequences using one-hot encoding and ESM-MSA logits.
|
| 308 |
+
|
| 309 |
+
Args:
|
| 310 |
+
seqs (list): List of sequences to featurize.
|
| 311 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 312 |
+
|
| 313 |
+
Returns:
|
| 314 |
+
np.ndarray: Combined one-hot and ESM-MSA logits features.
|
| 315 |
+
"""
|
| 316 |
+
X = []
|
| 317 |
+
|
| 318 |
+
featurizer_0 = list(self.featurizers.values())[0]
|
| 319 |
+
x = featurizer_0.featurize(seqs, **kwargs)
|
| 320 |
+
zero_vectors = np.zeros((x.shape[0], 1, x.shape[2]))
|
| 321 |
+
X.append(np.concatenate((zero_vectors, x), axis=1))
|
| 322 |
+
|
| 323 |
+
featurizer_1 = list(self.featurizers.values())[1]
|
| 324 |
+
X.append(featurizer_1.featurize(seqs, **kwargs))
|
| 325 |
+
|
| 326 |
+
X = np.concatenate(X, axis=-1)
|
| 327 |
+
|
| 328 |
+
return X
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
class OnehotAndESM2EmbedFeaturizer(CombinatorialFeaturizer):
|
| 332 |
+
"""Class for combining one-hot encoding with ESM2 embeddings.
|
| 333 |
+
|
| 334 |
+
Attributes:
|
| 335 |
+
name (str): Name of the combined featurizer.
|
| 336 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 337 |
+
|
| 338 |
+
Example Usage:
|
| 339 |
+
|
| 340 |
+
featurizer = OnehotAndESM2EmbedFeaturizer(
|
| 341 |
+
protein='protein1', # Name of protein for caching
|
| 342 |
+
use_cache=True # Whether to cache results
|
| 343 |
+
)
|
| 344 |
+
features = featurizer.featurize(sequences)
|
| 345 |
+
"""
|
| 346 |
+
|
| 347 |
+
def __init__(self, featurize_methods=["onehot", "esm_embed_2_3b"], **kwargs):
|
| 348 |
+
"""
|
| 349 |
+
Args:
|
| 350 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 351 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 352 |
+
"""
|
| 353 |
+
super().__init__(featurize_methods, **kwargs)
|
| 354 |
+
|
| 355 |
+
def featurize(self, seqs, **kwargs):
|
| 356 |
+
"""
|
| 357 |
+
Featurizes sequences using one-hot encoding and ESM2 embeddings.
|
| 358 |
+
|
| 359 |
+
Args:
|
| 360 |
+
seqs (list): List of sequences to featurize.
|
| 361 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 362 |
+
|
| 363 |
+
Returns:
|
| 364 |
+
np.ndarray: Combined one-hot and ESM2 embedding features.
|
| 365 |
+
"""
|
| 366 |
+
X = []
|
| 367 |
+
|
| 368 |
+
featurizer_0 = list(self.featurizers.values())[0]
|
| 369 |
+
onehot = featurizer_0.featurize(seqs, **kwargs)
|
| 370 |
+
X.append(onehot.reshape(onehot.shape[0], -1))
|
| 371 |
+
|
| 372 |
+
featurizer_1 = list(self.featurizers.values())[1]
|
| 373 |
+
X.append(featurizer_1.featurize(seqs, **kwargs))
|
| 374 |
+
|
| 375 |
+
X = np.concatenate(X, axis=1)
|
| 376 |
+
|
| 377 |
+
return X
|
| 378 |
+
|
| 379 |
+
class OnehotAndESM2_15bEmbedFeaturizer(CombinatorialFeaturizer):
|
| 380 |
+
"""Class for combining one-hot encoding with ESM2 embeddings.
|
| 381 |
+
|
| 382 |
+
Attributes:
|
| 383 |
+
name (str): Name of the combined featurizer.
|
| 384 |
+
featurizers (dict): Dictionary mapping featurizer names to instances.
|
| 385 |
+
|
| 386 |
+
Example Usage:
|
| 387 |
+
|
| 388 |
+
featurizer = OnehotAndESM2EmbedFeaturizer(
|
| 389 |
+
protein='protein1', # Name of protein for caching
|
| 390 |
+
use_cache=True # Whether to cache results
|
| 391 |
+
)
|
| 392 |
+
features = featurizer.featurize(sequences)
|
| 393 |
+
"""
|
| 394 |
+
|
| 395 |
+
def __init__(self, featurize_methods=["onehot", "esm_embed_2_15b"], **kwargs):
|
| 396 |
+
"""
|
| 397 |
+
Args:
|
| 398 |
+
featurize_methods (list): List of featurizer names to combine.
|
| 399 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 400 |
+
"""
|
| 401 |
+
super().__init__(featurize_methods, **kwargs)
|
| 402 |
+
|
| 403 |
+
def featurize(self, seqs, **kwargs):
|
| 404 |
+
"""
|
| 405 |
+
Featurizes sequences using one-hot encoding and ESM2 embeddings.
|
| 406 |
+
|
| 407 |
+
Args:
|
| 408 |
+
seqs (list): List of sequences to featurize.
|
| 409 |
+
**kwargs: Additional arguments passed to each featurizer.
|
| 410 |
+
|
| 411 |
+
Returns:
|
| 412 |
+
np.ndarray: Combined one-hot and ESM2 embedding features.
|
| 413 |
+
"""
|
| 414 |
+
X = []
|
| 415 |
+
|
| 416 |
+
featurizer_0 = list(self.featurizers.values())[0]
|
| 417 |
+
onehot = featurizer_0.featurize(seqs, **kwargs)
|
| 418 |
+
X.append(onehot.reshape(onehot.shape[0], -1))
|
| 419 |
+
|
| 420 |
+
featurizer_1 = list(self.featurizers.values())[1]
|
| 421 |
+
X.append(featurizer_1.featurize(seqs, **kwargs))
|
| 422 |
+
|
| 423 |
+
X = np.concatenate(X, axis=1)
|
| 424 |
+
|
| 425 |
+
return X
|
model/featurizers/esm_featurizers.py
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import torch
|
| 3 |
+
|
| 4 |
+
from model.featurizers.model_choices import FEATURE_MODELS
|
| 5 |
+
from model.featurizers.base_featurizers import BaseFeaturizer
|
| 6 |
+
from concurrent.futures import ProcessPoolExecutor
|
| 7 |
+
import concurrent.futures
|
| 8 |
+
|
| 9 |
+
class ForgeESMFeaturizer(BaseFeaturizer):
|
| 10 |
+
"""Class for generating ESM Forge-based protein embeddings or log probabilities.
|
| 11 |
+
|
| 12 |
+
Attributes:
|
| 13 |
+
model_type (str): Type of featurization model to use.
|
| 14 |
+
name (str): Name of the featurizer.
|
| 15 |
+
protein (str): Name of protein being featurized.
|
| 16 |
+
use_cache (bool): Whether to cache featurization results.
|
| 17 |
+
flatten_features (bool): Whether to flatten output features.
|
| 18 |
+
device (torch.device): Device to use for computation.
|
| 19 |
+
model (str): ESM model name.
|
| 20 |
+
url (str): Forge API URL.
|
| 21 |
+
token (str): API token.
|
| 22 |
+
output_type (str): Output format type.
|
| 23 |
+
|
| 24 |
+
Example Usage:
|
| 25 |
+
|
| 26 |
+
featurizer = ForgeESMFeaturizer(
|
| 27 |
+
protein='protein1', # Name of protein for caching
|
| 28 |
+
use_cache=True, # Whether to cache results
|
| 29 |
+
flatten_features=False, # Whether to flatten output features
|
| 30 |
+
model='esm2_t33_650M', # ESM model to use
|
| 31 |
+
token='api_token', # Forge API token
|
| 32 |
+
output_type='sequence_representations' # Output type
|
| 33 |
+
)
|
| 34 |
+
features = featurizer.featurize(sequences)
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
def __init__(
|
| 38 |
+
self,
|
| 39 |
+
protein=None,
|
| 40 |
+
use_cache=False,
|
| 41 |
+
flatten_features=False,
|
| 42 |
+
model=None,
|
| 43 |
+
url="https://forge.evolutionaryscale.ai",
|
| 44 |
+
token=None,
|
| 45 |
+
output_type=None,
|
| 46 |
+
model_type="esm",
|
| 47 |
+
**kwargs,
|
| 48 |
+
):
|
| 49 |
+
"""
|
| 50 |
+
Args:
|
| 51 |
+
protein (str): Name of protein being featurized.
|
| 52 |
+
use_cache (bool): Whether to cache results.
|
| 53 |
+
flatten_features (bool): Whether to flatten output features.
|
| 54 |
+
model (str): ESM model name.
|
| 55 |
+
url (str): Forge API URL.
|
| 56 |
+
token (str): API token.
|
| 57 |
+
output_type (str): Output format type.
|
| 58 |
+
model_type (str): Type of featurization model.
|
| 59 |
+
**kwargs: Additional keyword arguments.
|
| 60 |
+
"""
|
| 61 |
+
super().__init__(model_type=model_type, protein=protein, use_cache=use_cache, flatten_features=flatten_features, **kwargs)
|
| 62 |
+
self.model = model
|
| 63 |
+
self.url = url
|
| 64 |
+
if token is None:
|
| 65 |
+
raise ValueError("ESM Forge API token must be provided")
|
| 66 |
+
self.token = token
|
| 67 |
+
self.output_type = output_type
|
| 68 |
+
|
| 69 |
+
def process_single_protein(self, sequence, model, url, token, output_type):
|
| 70 |
+
"""Process a single protein sequence using ESM Forge.
|
| 71 |
+
|
| 72 |
+
Args:
|
| 73 |
+
sequence (str): Protein sequence to process.
|
| 74 |
+
model (str): ESM model name.
|
| 75 |
+
url (str): Forge API URL.
|
| 76 |
+
token (str): Forge API access token.
|
| 77 |
+
output_type (str): Either "log_probabilities" or "sequence_representations".
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
numpy.ndarray: Protein embeddings or log probabilities.
|
| 81 |
+
|
| 82 |
+
Raises:
|
| 83 |
+
ValueError: If output_type is invalid.
|
| 84 |
+
RuntimeError: If API call fails.
|
| 85 |
+
"""
|
| 86 |
+
if output_type not in ["log_probabilities", "sequence_representations"]:
|
| 87 |
+
raise ValueError("output_type must be 'log_probabilities' or 'sequence_representations'")
|
| 88 |
+
|
| 89 |
+
try:
|
| 90 |
+
|
| 91 |
+
from esm.sdk.forge import ESM3ForgeInferenceClient
|
| 92 |
+
from esm.sdk.api import ESMProtein, LogitsConfig
|
| 93 |
+
|
| 94 |
+
forge_client = ESM3ForgeInferenceClient(model=model, url=url, token=token)
|
| 95 |
+
protein = ESMProtein(sequence=sequence)
|
| 96 |
+
protein_tensor = forge_client.encode(protein)
|
| 97 |
+
logits_output = forge_client.logits(
|
| 98 |
+
protein_tensor, LogitsConfig(sequence=True, return_embeddings=True)
|
| 99 |
+
)
|
| 100 |
+
except Exception as e:
|
| 101 |
+
raise RuntimeError(f"ESM Forge API call failed: {str(e)}")
|
| 102 |
+
|
| 103 |
+
if output_type == "log_probabilities":
|
| 104 |
+
return np.array(logits_output.logits.sequence.numpy())
|
| 105 |
+
elif output_type == "sequence_representations":
|
| 106 |
+
embeddings_float32 = logits_output.embeddings.squeeze().float()
|
| 107 |
+
return (np.array(embeddings_float32.numpy()).mean(axis=0))
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def process_proteins_parallel(self, seqs, model, url, token, output_type):
|
| 111 |
+
"""Process a list of protein sequences in parallel using ESM Forge.
|
| 112 |
+
|
| 113 |
+
Args:
|
| 114 |
+
seqs (list): List of protein sequences to process.
|
| 115 |
+
model (str): ESM model name to use.
|
| 116 |
+
url (str): Forge API URL.
|
| 117 |
+
token (str): Forge API access token.
|
| 118 |
+
output_type (str): Type of output features.
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
list: List of protein embeddings as numpy arrays.
|
| 122 |
+
"""
|
| 123 |
+
with ProcessPoolExecutor(max_workers=16) as executor:
|
| 124 |
+
future_to_index = {
|
| 125 |
+
executor.submit(self.process_single_protein, seq, model, url, token, output_type): i
|
| 126 |
+
for i, seq in enumerate(seqs)
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
results = [None] * len(seqs)
|
| 130 |
+
|
| 131 |
+
for future in concurrent.futures.as_completed(future_to_index):
|
| 132 |
+
index = future_to_index[future]
|
| 133 |
+
results[index] = future.result()
|
| 134 |
+
|
| 135 |
+
return results
|
| 136 |
+
|
| 137 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 138 |
+
"""
|
| 139 |
+
Featurizes sequences using ESM Forge.
|
| 140 |
+
|
| 141 |
+
Args:
|
| 142 |
+
seqs (list): List of sequences to featurize.
|
| 143 |
+
**kwargs: Additional keyword arguments.
|
| 144 |
+
|
| 145 |
+
Returns:
|
| 146 |
+
np.ndarray: Array of featurized sequences.
|
| 147 |
+
"""
|
| 148 |
+
X = self.process_proteins_parallel(seqs, model=self.model, url=self.url, token=self.token, output_type=self.output_type)
|
| 149 |
+
return X
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
class Forge_ESMC_6B_EmbedFeaturizer(ForgeESMFeaturizer):
|
| 153 |
+
"""Class for generating ESM-C 6B embeddings using Forge.
|
| 154 |
+
|
| 155 |
+
Attributes:
|
| 156 |
+
model_type (str): Type of featurization model to use.
|
| 157 |
+
name (str): Name of the featurizer.
|
| 158 |
+
protein (str): Name of protein being featurized.
|
| 159 |
+
use_cache (bool): Whether to cache featurization results.
|
| 160 |
+
flatten_features (bool): Whether to flatten output features.
|
| 161 |
+
device (torch.device): Device to use for computation.
|
| 162 |
+
model (str): ESM model name.
|
| 163 |
+
url (str): Forge API URL.
|
| 164 |
+
token (str): API token.
|
| 165 |
+
output_type (str): Output format type.
|
| 166 |
+
|
| 167 |
+
Example Usage:
|
| 168 |
+
|
| 169 |
+
featurizer = Forge_ESMC_6B_EmbedFeaturizer(
|
| 170 |
+
protein='protein1', # Name of protein for caching
|
| 171 |
+
use_cache=True, # Whether to cache results
|
| 172 |
+
flatten_features=False, # Whether to flatten output features
|
| 173 |
+
token='api_token' # Forge API token
|
| 174 |
+
)
|
| 175 |
+
features = featurizer.featurize(sequences)
|
| 176 |
+
"""
|
| 177 |
+
|
| 178 |
+
def __init__(
|
| 179 |
+
self,
|
| 180 |
+
protein=None,
|
| 181 |
+
use_cache=False,
|
| 182 |
+
flatten_features=False,
|
| 183 |
+
model="esmc-6b-2024-12",
|
| 184 |
+
url="https://forge.evolutionaryscale.ai",
|
| 185 |
+
token=None,
|
| 186 |
+
output_type="sequence_representations",
|
| 187 |
+
**kwargs,
|
| 188 |
+
):
|
| 189 |
+
"""
|
| 190 |
+
Args:
|
| 191 |
+
protein (str): Name of protein being featurized.
|
| 192 |
+
use_cache (bool): Whether to cache results.
|
| 193 |
+
flatten_features (bool): Whether to flatten output features.
|
| 194 |
+
model (str): ESM model name.
|
| 195 |
+
url (str): Forge API URL.
|
| 196 |
+
token (str): API token.
|
| 197 |
+
output_type (str): Output format type.
|
| 198 |
+
**kwargs: Additional keyword arguments.
|
| 199 |
+
"""
|
| 200 |
+
super().__init__(model_type="esmc_6b", protein=protein, use_cache=use_cache, flatten_features=flatten_features, output_type=output_type, model=model, url=url, token=token, **kwargs)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
class ESMBaseFeaturizer(BaseFeaturizer):
|
| 204 |
+
"""Base class for ESM model featurizers.
|
| 205 |
+
|
| 206 |
+
Attributes:
|
| 207 |
+
model_type (str): Type of featurization model to use.
|
| 208 |
+
name (str): Name of the featurizer.
|
| 209 |
+
protein (str): Name of protein being featurized.
|
| 210 |
+
use_cache (bool): Whether to cache featurization results.
|
| 211 |
+
flatten_features (bool): Whether to flatten output features.
|
| 212 |
+
device (torch.device): Device to use for computation.
|
| 213 |
+
batch_size (int): Batch size for processing.
|
| 214 |
+
model_locations (list): Paths to ESM model files.
|
| 215 |
+
|
| 216 |
+
Example Usage:
|
| 217 |
+
|
| 218 |
+
featurizer = ESMBaseFeaturizer(
|
| 219 |
+
protein='protein1', # Name of protein for caching
|
| 220 |
+
use_cache=True, # Whether to cache results
|
| 221 |
+
batch_size=968, # Processing batch size
|
| 222 |
+
model_locations=['path/to/model'] # Model file paths
|
| 223 |
+
)
|
| 224 |
+
features = featurizer.featurize(sequences)
|
| 225 |
+
"""
|
| 226 |
+
|
| 227 |
+
def __init__(
|
| 228 |
+
self,
|
| 229 |
+
protein=None,
|
| 230 |
+
use_cache=False,
|
| 231 |
+
model_locations=None,
|
| 232 |
+
batch_size=968,
|
| 233 |
+
model_type="esm",
|
| 234 |
+
**kwargs,
|
| 235 |
+
):
|
| 236 |
+
"""
|
| 237 |
+
Args:
|
| 238 |
+
protein (str): Name of protein being featurized.
|
| 239 |
+
use_cache (bool): Whether to cache results.
|
| 240 |
+
model_locations (list): Paths to ESM model files.
|
| 241 |
+
batch_size (int): Batch size for processing.
|
| 242 |
+
model_type (str): Type of featurization model.
|
| 243 |
+
**kwargs: Additional keyword arguments.
|
| 244 |
+
"""
|
| 245 |
+
super().__init__(model_type, protein, use_cache, **kwargs)
|
| 246 |
+
self.batch_size = batch_size
|
| 247 |
+
self.model_locations = model_locations
|
| 248 |
+
|
| 249 |
+
def eval_esm(self, model, batch_tokens, sequence_data, output_type):
|
| 250 |
+
"""
|
| 251 |
+
Evaluates sequences using ESM model.
|
| 252 |
+
|
| 253 |
+
Args:
|
| 254 |
+
model (torch.nn.Module): ESM model.
|
| 255 |
+
batch_tokens (torch.Tensor): Tokenized sequences.
|
| 256 |
+
sequence_data (list): Original sequence data.
|
| 257 |
+
output_type (str): Type of output features.
|
| 258 |
+
|
| 259 |
+
Returns:
|
| 260 |
+
np.ndarray: Model outputs.
|
| 261 |
+
"""
|
| 262 |
+
if output_type == "log_probabilities":
|
| 263 |
+
# Featurize as sequence log probabilities.
|
| 264 |
+
with torch.no_grad():
|
| 265 |
+
token_probs = torch.log_softmax(
|
| 266 |
+
model(batch_tokens.to(self.device))["logits"], dim=-1
|
| 267 |
+
)
|
| 268 |
+
output = token_probs.cpu().numpy()
|
| 269 |
+
|
| 270 |
+
elif output_type == "sequence_representations":
|
| 271 |
+
# Featurize as sequence embeddings (last hidden layer).
|
| 272 |
+
last_layer = len(model.layers)
|
| 273 |
+
with torch.no_grad():
|
| 274 |
+
results = model(
|
| 275 |
+
batch_tokens.to(self.device), repr_layers=[last_layer]
|
| 276 |
+
)
|
| 277 |
+
token_representations = results["representations"][last_layer]
|
| 278 |
+
|
| 279 |
+
sequence_representations = []
|
| 280 |
+
for i, (_, seq) in enumerate(sequence_data):
|
| 281 |
+
seq_embed = token_representations[i, 1 : len(seq) + 1].mean(0)
|
| 282 |
+
sequence_representations.append(seq_embed.cpu().numpy().ravel())
|
| 283 |
+
|
| 284 |
+
output = np.array(sequence_representations)
|
| 285 |
+
|
| 286 |
+
return output
|
| 287 |
+
|
| 288 |
+
def featurize_esm(self, seqs, output_type):
|
| 289 |
+
"""
|
| 290 |
+
Featurizes sequences using ESM model.
|
| 291 |
+
|
| 292 |
+
Args:
|
| 293 |
+
seqs (list): List of sequences to featurize.
|
| 294 |
+
output_type (str): Type of output features.
|
| 295 |
+
|
| 296 |
+
Returns:
|
| 297 |
+
np.ndarray: Array of featurized sequences.
|
| 298 |
+
"""
|
| 299 |
+
from esm import pretrained
|
| 300 |
+
model_loc_to_model = {}
|
| 301 |
+
for model_location in self.model_locations:
|
| 302 |
+
model, alphabet = pretrained.load_model_and_alphabet(model_location)
|
| 303 |
+
model.eval()
|
| 304 |
+
if torch.backends.mps.is_available() or torch.cuda.is_available():
|
| 305 |
+
model = model.to(self.device)
|
| 306 |
+
else:
|
| 307 |
+
print("GPU device not available")
|
| 308 |
+
return
|
| 309 |
+
model_loc_to_model[model_location] = model
|
| 310 |
+
|
| 311 |
+
output = []
|
| 312 |
+
|
| 313 |
+
batch_size = self.batch_size
|
| 314 |
+
n_batches = ((len(seqs[0]) - 1) // batch_size) + 1
|
| 315 |
+
for batchi in range(n_batches):
|
| 316 |
+
start = batchi * batch_size
|
| 317 |
+
end = (batchi + 1) * batch_size
|
| 318 |
+
|
| 319 |
+
model_features = []
|
| 320 |
+
for model_location in self.model_locations:
|
| 321 |
+
model = model_loc_to_model[model_location]
|
| 322 |
+
|
| 323 |
+
seq_batch = []
|
| 324 |
+
sbatch_size = 3
|
| 325 |
+
n_sbatches = ((len(seqs) - 1) // sbatch_size) + 1
|
| 326 |
+
for batchj in range(n_sbatches):
|
| 327 |
+
sb_start = batchj * sbatch_size
|
| 328 |
+
sb_end = (batchj + 1) * sbatch_size
|
| 329 |
+
|
| 330 |
+
sequence_data = [
|
| 331 |
+
(f"protein{sbidx}", seq[start:end])
|
| 332 |
+
if output_type == "log_probabilities"
|
| 333 |
+
else (f"protein{sbidx}", seq[start:end].replace("X", ""))
|
| 334 |
+
for sbidx, seq in enumerate(seqs[sb_start:sb_end])
|
| 335 |
+
]
|
| 336 |
+
|
| 337 |
+
batch_converter = alphabet.get_batch_converter()
|
| 338 |
+
batch_labels, batch_strs, batch_tokens = batch_converter(
|
| 339 |
+
sequence_data
|
| 340 |
+
)
|
| 341 |
+
|
| 342 |
+
if n_batches > 1:
|
| 343 |
+
if batchi == 0:
|
| 344 |
+
batch_tokens = batch_tokens[:, :-1]
|
| 345 |
+
elif batchi == n_batches - 1:
|
| 346 |
+
batch_tokens = batch_tokens[:, 1:]
|
| 347 |
+
else:
|
| 348 |
+
batch_tokens = batch_tokens[:, 1:-1]
|
| 349 |
+
|
| 350 |
+
seq_batch.append(
|
| 351 |
+
self.eval_esm(model, batch_tokens, sequence_data, output_type)
|
| 352 |
+
)
|
| 353 |
+
|
| 354 |
+
model_features.append(np.concatenate(seq_batch))
|
| 355 |
+
|
| 356 |
+
output.append(np.mean(model_features, axis=0))
|
| 357 |
+
|
| 358 |
+
X = np.hstack(output)
|
| 359 |
+
|
| 360 |
+
return X
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
class ESMLogitsFeaturizer(ESMBaseFeaturizer):
|
| 364 |
+
"""Class for generating ESM model log probabilities.
|
| 365 |
+
|
| 366 |
+
Attributes:
|
| 367 |
+
model_type (str): Type of featurization model to use.
|
| 368 |
+
name (str): Name of the featurizer.
|
| 369 |
+
protein (str): Name of protein being featurized.
|
| 370 |
+
use_cache (bool): Whether to cache featurization results.
|
| 371 |
+
flatten_features (bool): Whether to flatten output features.
|
| 372 |
+
device (torch.device): Device to use for computation.
|
| 373 |
+
batch_size (int): Batch size for processing.
|
| 374 |
+
model_locations (list): Paths to ESM model files.
|
| 375 |
+
|
| 376 |
+
Example Usage:
|
| 377 |
+
|
| 378 |
+
featurizer = ESMLogitsFeaturizer(
|
| 379 |
+
protein='protein1', # Name of protein for caching
|
| 380 |
+
use_cache=True, # Whether to cache results
|
| 381 |
+
batch_size=968 # Processing batch size
|
| 382 |
+
)
|
| 383 |
+
features = featurizer.featurize(sequences)
|
| 384 |
+
"""
|
| 385 |
+
|
| 386 |
+
def __init__(
|
| 387 |
+
self,
|
| 388 |
+
protein=None,
|
| 389 |
+
use_cache=False,
|
| 390 |
+
model_locations=FEATURE_MODELS["esm_logits"],
|
| 391 |
+
batch_size=968,
|
| 392 |
+
model_type="esm_logits",
|
| 393 |
+
**kwargs,
|
| 394 |
+
):
|
| 395 |
+
"""
|
| 396 |
+
Args:
|
| 397 |
+
protein (str): Name of protein being featurized.
|
| 398 |
+
use_cache (bool): Whether to cache results.
|
| 399 |
+
model_locations (list): Paths to ESM model files.
|
| 400 |
+
batch_size (int): Batch size for processing.
|
| 401 |
+
model_type (str): Type of featurization model.
|
| 402 |
+
**kwargs: Additional keyword arguments.
|
| 403 |
+
"""
|
| 404 |
+
super().__init__(
|
| 405 |
+
protein,
|
| 406 |
+
use_cache,
|
| 407 |
+
model_locations,
|
| 408 |
+
batch_size,
|
| 409 |
+
model_type,
|
| 410 |
+
**kwargs,
|
| 411 |
+
)
|
| 412 |
+
|
| 413 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 414 |
+
"""
|
| 415 |
+
Featurizes sequences using ESM model log probabilities.
|
| 416 |
+
|
| 417 |
+
Args:
|
| 418 |
+
seqs (list): List of sequences to featurize.
|
| 419 |
+
**kwargs: Additional keyword arguments.
|
| 420 |
+
|
| 421 |
+
Returns:
|
| 422 |
+
np.ndarray: Array of log probabilities.
|
| 423 |
+
"""
|
| 424 |
+
X = self.featurize_esm(seqs, output_type="log_probabilities")
|
| 425 |
+
return X
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
class ESM1vEmbedFeaturizer(ESMBaseFeaturizer):
|
| 429 |
+
"""Class for generating ESM-1v model embeddings.
|
| 430 |
+
|
| 431 |
+
Attributes:
|
| 432 |
+
model_type (str): Type of featurization model to use.
|
| 433 |
+
name (str): Name of the featurizer.
|
| 434 |
+
protein (str): Name of protein being featurized.
|
| 435 |
+
use_cache (bool): Whether to cache featurization results.
|
| 436 |
+
flatten_features (bool): Whether to flatten output features.
|
| 437 |
+
device (torch.device): Device to use for computation.
|
| 438 |
+
batch_size (int): Batch size for processing.
|
| 439 |
+
model_locations (list): Paths to ESM model files.
|
| 440 |
+
|
| 441 |
+
Example Usage:
|
| 442 |
+
|
| 443 |
+
featurizer = ESM1vEmbedFeaturizer(
|
| 444 |
+
protein='protein1', # Name of protein for caching
|
| 445 |
+
use_cache=True, # Whether to cache results
|
| 446 |
+
batch_size=968 # Processing batch size
|
| 447 |
+
)
|
| 448 |
+
features = featurizer.featurize(sequences)
|
| 449 |
+
"""
|
| 450 |
+
|
| 451 |
+
def __init__(
|
| 452 |
+
self,
|
| 453 |
+
protein=None,
|
| 454 |
+
use_cache=False,
|
| 455 |
+
model_locations=FEATURE_MODELS["esm_embed_1v"],
|
| 456 |
+
batch_size=968,
|
| 457 |
+
model_type="esm_embed_1v",
|
| 458 |
+
**kwargs,
|
| 459 |
+
):
|
| 460 |
+
"""
|
| 461 |
+
Args:
|
| 462 |
+
protein (str): Name of protein being featurized.
|
| 463 |
+
use_cache (bool): Whether to cache results.
|
| 464 |
+
model_locations (list): Paths to ESM model files.
|
| 465 |
+
batch_size (int): Batch size for processing.
|
| 466 |
+
model_type (str): Type of featurization model.
|
| 467 |
+
**kwargs: Additional keyword arguments.
|
| 468 |
+
"""
|
| 469 |
+
super().__init__(
|
| 470 |
+
protein,
|
| 471 |
+
use_cache,
|
| 472 |
+
model_locations,
|
| 473 |
+
batch_size,
|
| 474 |
+
model_type,
|
| 475 |
+
**kwargs,
|
| 476 |
+
)
|
| 477 |
+
|
| 478 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 479 |
+
"""
|
| 480 |
+
Featurizes sequences using ESM-1v model embeddings.
|
| 481 |
+
|
| 482 |
+
Args:
|
| 483 |
+
seqs (list): List of sequences to featurize.
|
| 484 |
+
**kwargs: Additional keyword arguments.
|
| 485 |
+
|
| 486 |
+
Returns:
|
| 487 |
+
np.ndarray: Array of sequence embeddings.
|
| 488 |
+
"""
|
| 489 |
+
X = self.featurize_esm(seqs, output_type="sequence_representations")
|
| 490 |
+
return X
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
class ESM2EmbedFeaturizer(ESMBaseFeaturizer):
|
| 494 |
+
"""Class for generating ESM-2 model embeddings.
|
| 495 |
+
|
| 496 |
+
Attributes:
|
| 497 |
+
model_type (str): Type of featurization model to use.
|
| 498 |
+
name (str): Name of the featurizer.
|
| 499 |
+
protein (str): Name of protein being featurized.
|
| 500 |
+
use_cache (bool): Whether to cache featurization results.
|
| 501 |
+
flatten_features (bool): Whether to flatten output features.
|
| 502 |
+
device (torch.device): Device to use for computation.
|
| 503 |
+
batch_size (int): Batch size for processing.
|
| 504 |
+
model_locations (list): Paths to ESM model files.
|
| 505 |
+
|
| 506 |
+
Example Usage:
|
| 507 |
+
|
| 508 |
+
featurizer = ESM2EmbedFeaturizer(
|
| 509 |
+
protein='protein1', # Name of protein for caching
|
| 510 |
+
use_cache=True, # Whether to cache results
|
| 511 |
+
batch_size=968 # Processing batch size
|
| 512 |
+
)
|
| 513 |
+
features = featurizer.featurize(sequences)
|
| 514 |
+
"""
|
| 515 |
+
|
| 516 |
+
def __init__(
|
| 517 |
+
self,
|
| 518 |
+
protein=None,
|
| 519 |
+
use_cache=False,
|
| 520 |
+
model_locations=FEATURE_MODELS["esm_embed_2_3b"],
|
| 521 |
+
batch_size=968,
|
| 522 |
+
model_type="esm_embed_2_3b",
|
| 523 |
+
**kwargs,
|
| 524 |
+
):
|
| 525 |
+
"""
|
| 526 |
+
Args:
|
| 527 |
+
protein (str): Name of protein being featurized.
|
| 528 |
+
use_cache (bool): Whether to cache results.
|
| 529 |
+
model_locations (list): Paths to ESM model files.
|
| 530 |
+
batch_size (int): Batch size for processing.
|
| 531 |
+
model_type (str): Type of featurization model.
|
| 532 |
+
**kwargs: Additional keyword arguments.
|
| 533 |
+
"""
|
| 534 |
+
super().__init__(
|
| 535 |
+
protein,
|
| 536 |
+
use_cache,
|
| 537 |
+
model_locations,
|
| 538 |
+
batch_size,
|
| 539 |
+
model_type,
|
| 540 |
+
**kwargs,
|
| 541 |
+
)
|
| 542 |
+
|
| 543 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 544 |
+
"""
|
| 545 |
+
Featurizes sequences using ESM-2 model embeddings.
|
| 546 |
+
|
| 547 |
+
Args:
|
| 548 |
+
seqs (list): List of sequences to featurize.
|
| 549 |
+
**kwargs: Additional keyword arguments.
|
| 550 |
+
|
| 551 |
+
Returns:
|
| 552 |
+
np.ndarray: Array of sequence embeddings.
|
| 553 |
+
"""
|
| 554 |
+
X = self.featurize_esm(seqs, output_type="sequence_representations")
|
| 555 |
+
return X
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
class ESM2_15b_EmbedFeaturizer(ESMBaseFeaturizer):
|
| 559 |
+
"""Class for generating ESM-2 15B model embeddings.
|
| 560 |
+
|
| 561 |
+
Attributes:
|
| 562 |
+
model_type (str): Type of featurization model to use.
|
| 563 |
+
name (str): Name of the featurizer.
|
| 564 |
+
protein (str): Name of protein being featurized.
|
| 565 |
+
use_cache (bool): Whether to cache featurization results.
|
| 566 |
+
flatten_features (bool): Whether to flatten output features.
|
| 567 |
+
device (torch.device): Device to use for computation.
|
| 568 |
+
batch_size (int): Batch size for processing.
|
| 569 |
+
model_locations (list): Paths to ESM model files.
|
| 570 |
+
|
| 571 |
+
Example Usage:
|
| 572 |
+
|
| 573 |
+
featurizer = ESM2_15b_EmbedFeaturizer(
|
| 574 |
+
protein='protein1', # Name of protein for caching
|
| 575 |
+
use_cache=True, # Whether to cache results
|
| 576 |
+
batch_size=968 # Processing batch size
|
| 577 |
+
)
|
| 578 |
+
features = featurizer.featurize(sequences)
|
| 579 |
+
"""
|
| 580 |
+
|
| 581 |
+
def __init__(
|
| 582 |
+
self,
|
| 583 |
+
protein=None,
|
| 584 |
+
use_cache=False,
|
| 585 |
+
model_locations=FEATURE_MODELS["esm_embed_2_15b"],
|
| 586 |
+
batch_size=968,
|
| 587 |
+
model_type="esm_embed_2_15b",
|
| 588 |
+
**kwargs,
|
| 589 |
+
):
|
| 590 |
+
"""
|
| 591 |
+
Args:
|
| 592 |
+
protein (str): Name of protein being featurized.
|
| 593 |
+
use_cache (bool): Whether to cache results.
|
| 594 |
+
model_locations (list): Paths to ESM model files.
|
| 595 |
+
batch_size (int): Batch size for processing.
|
| 596 |
+
model_type (str): Type of featurization model.
|
| 597 |
+
**kwargs: Additional keyword arguments.
|
| 598 |
+
"""
|
| 599 |
+
super().__init__(
|
| 600 |
+
protein,
|
| 601 |
+
use_cache,
|
| 602 |
+
model_locations,
|
| 603 |
+
batch_size,
|
| 604 |
+
model_type,
|
| 605 |
+
**kwargs,
|
| 606 |
+
)
|
| 607 |
+
|
| 608 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 609 |
+
"""
|
| 610 |
+
Featurizes sequences using ESM-2 15B model embeddings.
|
| 611 |
+
|
| 612 |
+
Args:
|
| 613 |
+
seqs (list): List of sequences to featurize.
|
| 614 |
+
**kwargs: Additional keyword arguments.
|
| 615 |
+
|
| 616 |
+
Returns:
|
| 617 |
+
np.ndarray: Array of sequence embeddings.
|
| 618 |
+
"""
|
| 619 |
+
X = self.featurize_esm(seqs, output_type="sequence_representations")
|
| 620 |
+
return X
|
model/featurizers/model_choices.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from model.featurizers.model_locations import (
|
| 2 |
+
msa_models,
|
| 3 |
+
esm_models,
|
| 4 |
+
prose_models,
|
| 5 |
+
prose_models_cas13,
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
FEATURIZE_CHOICES = [
|
| 9 |
+
# All the implemented featurizers
|
| 10 |
+
# Base Featurizers
|
| 11 |
+
'onehot',
|
| 12 |
+
'georgiev',
|
| 13 |
+
'aa_idx',
|
| 14 |
+
|
| 15 |
+
# MSA Featurizers
|
| 16 |
+
'msa_embed',
|
| 17 |
+
'msa_sequence_embed',
|
| 18 |
+
'msa_logits',
|
| 19 |
+
'msa_augmented',
|
| 20 |
+
|
| 21 |
+
# ESM Featurizers
|
| 22 |
+
'esm_logits',
|
| 23 |
+
'esm_embed_1v',
|
| 24 |
+
'esm_embed_2_3b',
|
| 25 |
+
'esm_embed_2_15b',
|
| 26 |
+
'esm_augmented',
|
| 27 |
+
|
| 28 |
+
# Zeroshot Featurizers
|
| 29 |
+
'zeroshot_msa',
|
| 30 |
+
'zeroshot_esm',
|
| 31 |
+
'zeroshot_cscs',
|
| 32 |
+
'zeroshot_cscs_gram',
|
| 33 |
+
'zeroshot_cscs_sem',
|
| 34 |
+
'zeroshot_prose',
|
| 35 |
+
'zeroshot_esmif',
|
| 36 |
+
]
|
| 37 |
+
|
| 38 |
+
FEATURE_MODELS = {
|
| 39 |
+
# Dictionary of model names to model locations.
|
| 40 |
+
# Base Featurizers
|
| 41 |
+
'onehot': [ None ],
|
| 42 |
+
'georgiev': [ None ],
|
| 43 |
+
'aa_idx': [ None ],
|
| 44 |
+
|
| 45 |
+
# MSA Featurizers
|
| 46 |
+
'msa_embed': msa_models,
|
| 47 |
+
'msa_sequence_embed': msa_models,
|
| 48 |
+
'msa_logits': msa_models,
|
| 49 |
+
'msa_augmented': msa_models,
|
| 50 |
+
|
| 51 |
+
# ESM Featurizers
|
| 52 |
+
'esm_logits': esm_models[:1],
|
| 53 |
+
'esm_embed_1v': esm_models[:1],
|
| 54 |
+
'esm_embed_2_3b': esm_models[5:6],
|
| 55 |
+
'esm_embed_2_15b': esm_models[7:8],
|
| 56 |
+
'esm_augmented': esm_models[:5],
|
| 57 |
+
|
| 58 |
+
# Zeroshot Featurizers
|
| 59 |
+
'zeroshot_msa': msa_models,
|
| 60 |
+
'zeroshot_esm': esm_models[:6],
|
| 61 |
+
'zeroshot_cscs': [ None ],
|
| 62 |
+
'zeroshot_cscs_gram': [ None ],
|
| 63 |
+
'zeroshot_cscs_sem': [ None ],
|
| 64 |
+
'zeroshot_prose': prose_models_cas13,
|
| 65 |
+
'zeroshot_esmif': esm_models[6:7],
|
| 66 |
+
|
| 67 |
+
# Prose Featurizers
|
| 68 |
+
'prose_embedmean': prose_models,
|
| 69 |
+
'prose_augmented': prose_models_cas13,
|
| 70 |
+
}
|
model/featurizers/model_locations.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
esm_models = [
|
| 2 |
+
'esm1v_t33_650M_UR90S_1',
|
| 3 |
+
'esm1v_t33_650M_UR90S_2',
|
| 4 |
+
'esm1v_t33_650M_UR90S_3',
|
| 5 |
+
'esm1v_t33_650M_UR90S_4',
|
| 6 |
+
'esm1v_t33_650M_UR90S_5',
|
| 7 |
+
'esm2_t36_3B_UR50D',
|
| 8 |
+
'esm_if1_gvp4_t16_142M_UR50',
|
| 9 |
+
'esm2_t48_15B_UR50D',
|
| 10 |
+
]
|
| 11 |
+
|
| 12 |
+
msa_models = [
|
| 13 |
+
'esm_msa1b_t12_100M_UR50S',
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
prose_models = [
|
| 17 |
+
'data/prose_pretrained_models/prose_dlm_3x1024.sav',
|
| 18 |
+
]
|
| 19 |
+
|
| 20 |
+
prose_models_cas13 = [
|
| 21 |
+
'target/cas13/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_0/_iter0500000.sav',
|
| 22 |
+
'target/cas13/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_1/_iter0400000.sav',
|
| 23 |
+
'target/cas13/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_3/_iter0600000.sav',
|
| 24 |
+
'target/cas13/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_4/_iter0600000.sav',
|
| 25 |
+
'target/cas13/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_5/_iter0600000.sav',
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
prose_models_cas13_old1 = [
|
| 29 |
+
'target/cas13/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_0/_iter0500000.sav',
|
| 30 |
+
'target/cas13/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_1/_iter0400000.sav',
|
| 31 |
+
'target/cas13/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_2/_iter0300000.sav',
|
| 32 |
+
]
|
| 33 |
+
|
| 34 |
+
prose_models_cas13_old = [
|
| 35 |
+
'target/cas13_old/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_1/_iter0300000.sav',
|
| 36 |
+
'target/cas13_old/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_2/_iter0200000.sav',
|
| 37 |
+
'target/cas13_old/prose_rd512_nl3_dr0_ns2000000_si100000_le1280_mr0.1_bs100_wd0_lr0.0001_cs0.98_3/_iter0300000.sav',
|
| 38 |
+
]
|
model/featurizers/msa_featurizers.py
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from Bio import SeqIO
|
| 2 |
+
import numpy as np
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from model.featurizers.base_featurizers import BaseFeaturizer
|
| 6 |
+
from model.featurizers.model_choices import FEATURE_MODELS
|
| 7 |
+
from model.utils.other_utils import read_msa, greedy_select, msa_splicer
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class MSABaseFeaturizer(BaseFeaturizer):
|
| 11 |
+
"""Base class for MSA-based featurizers.
|
| 12 |
+
|
| 13 |
+
Attributes:
|
| 14 |
+
model_type (str): Type of featurization model to use.
|
| 15 |
+
name (str): Name of the featurizer.
|
| 16 |
+
protein (str): Name of protein being featurized.
|
| 17 |
+
use_cache (bool): Whether to cache featurization results.
|
| 18 |
+
flatten_features (bool): Whether to flatten output features.
|
| 19 |
+
device (torch.device): Device to use for computation.
|
| 20 |
+
batch_size (int): Batch size for processing.
|
| 21 |
+
model_locations (list): Paths to model files.
|
| 22 |
+
msa_file (str): Path to MSA file.
|
| 23 |
+
|
| 24 |
+
Example Usage:
|
| 25 |
+
|
| 26 |
+
featurizer = MSABaseFeaturizer(
|
| 27 |
+
protein='protein1', # Name of protein for caching
|
| 28 |
+
use_cache=True, # Whether to cache results
|
| 29 |
+
msa_file='msa.fasta', # Path to MSA file
|
| 30 |
+
batch_size=968 # Processing batch size
|
| 31 |
+
)
|
| 32 |
+
features = featurizer.featurize(sequences)
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
def __init__(
|
| 36 |
+
self,
|
| 37 |
+
protein=None,
|
| 38 |
+
use_cache=False,
|
| 39 |
+
model_locations=None,
|
| 40 |
+
msa_file=None,
|
| 41 |
+
batch_size=968,
|
| 42 |
+
model_type="msa",
|
| 43 |
+
**kwargs
|
| 44 |
+
):
|
| 45 |
+
"""
|
| 46 |
+
Args:
|
| 47 |
+
protein (str): Name of protein being featurized.
|
| 48 |
+
use_cache (bool): Whether to cache results.
|
| 49 |
+
model_locations (list): Paths to model files.
|
| 50 |
+
msa_file (str): Path to MSA file.
|
| 51 |
+
batch_size (int): Batch size for processing.
|
| 52 |
+
model_type (str): Type of featurization model.
|
| 53 |
+
**kwargs: Additional keyword arguments.
|
| 54 |
+
"""
|
| 55 |
+
super().__init__(model_type, protein, use_cache, **kwargs)
|
| 56 |
+
self.batch_size = batch_size
|
| 57 |
+
self.model_locations = model_locations
|
| 58 |
+
self.msa_file = msa_file
|
| 59 |
+
self.device = torch.device("cpu") # MSAs might be too big for GPU
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def featurize_msa(self, seqs, msa_file, output_type, **kwargs):
|
| 64 |
+
"""
|
| 65 |
+
Featurizes sequences using MSA Transformer model.
|
| 66 |
+
|
| 67 |
+
Args:
|
| 68 |
+
seqs (list): List of sequences to featurize.
|
| 69 |
+
msa_file (str): Path to MSA file.
|
| 70 |
+
output_type (str): Type of output features to extract.
|
| 71 |
+
**kwargs: Additional keyword arguments.
|
| 72 |
+
|
| 73 |
+
Returns:
|
| 74 |
+
np.ndarray: Array of sequence features.
|
| 75 |
+
"""
|
| 76 |
+
from esm import pretrained
|
| 77 |
+
|
| 78 |
+
torch.set_grad_enabled(False)
|
| 79 |
+
# Check to see if there is an MSA file in **kwargs.
|
| 80 |
+
if msa_file is None:
|
| 81 |
+
assert self.msa_file is not None, "No MSA file provided."
|
| 82 |
+
msa_file = self.msa_file
|
| 83 |
+
assert output_type in [
|
| 84 |
+
"sequence_representations",
|
| 85 |
+
"msa_representations",
|
| 86 |
+
"log_probabilities",
|
| 87 |
+
]
|
| 88 |
+
msa = read_msa(msa_file)
|
| 89 |
+
|
| 90 |
+
# Instantiate the model
|
| 91 |
+
(
|
| 92 |
+
msa_transformer,
|
| 93 |
+
msa_transformer_alphabet,
|
| 94 |
+
) = pretrained.esm_msa1b_t12_100M_UR50S()
|
| 95 |
+
msa_transformer = msa_transformer.eval().to(self.device)
|
| 96 |
+
msa_transformer_batch_converter = msa_transformer_alphabet.get_batch_converter()
|
| 97 |
+
|
| 98 |
+
# Prep the MSA, making the appropriate mutations
|
| 99 |
+
inputs = greedy_select(
|
| 100 |
+
msa, num_seqs=128
|
| 101 |
+
) # can change this to pass more/fewer sequences
|
| 102 |
+
# This splices the MSA to exclude gaps in the first sequence, due to MSATransformer context window
|
| 103 |
+
# size limit of 1024. If your MSA width is less than 1024, then you don't need to do this
|
| 104 |
+
inputs = msa_splicer(inputs)
|
| 105 |
+
name, wt_seq = inputs[0][0], inputs[0][1]
|
| 106 |
+
reps = []
|
| 107 |
+
|
| 108 |
+
# Batch processing
|
| 109 |
+
batch_size = self.batch_size
|
| 110 |
+
num_batches = len(seqs) // batch_size + (len(seqs) % batch_size != 0)
|
| 111 |
+
for batch_i in range(num_batches): # TODO: change this to num_batches
|
| 112 |
+
start_idx = batch_i * batch_size
|
| 113 |
+
end_idx = start_idx + batch_size
|
| 114 |
+
batch_seqs = seqs[start_idx:end_idx]
|
| 115 |
+
|
| 116 |
+
input_msas = []
|
| 117 |
+
for seq in batch_seqs:
|
| 118 |
+
# Replace the first sequence in the MSA with the mutant sequence
|
| 119 |
+
assert len(wt_seq) == len(seq)
|
| 120 |
+
inputs[0] = (name, seq)
|
| 121 |
+
input_msas.append(inputs)
|
| 122 |
+
# print(len(input_msas), len(input_msas[0]), len(input_msas[0][0][1]), len(input_msas[0][1][1]))
|
| 123 |
+
# Run the MSA Transformer
|
| 124 |
+
(
|
| 125 |
+
msa_transformer_batch_labels,
|
| 126 |
+
msa_transformer_batch_strs,
|
| 127 |
+
msa_transformer_batch_tokens,
|
| 128 |
+
) = msa_transformer_batch_converter(input_msas)
|
| 129 |
+
num_msas = len(msa_transformer_batch_tokens)
|
| 130 |
+
msa_transformer_batch_tokens = msa_transformer_batch_tokens.to(
|
| 131 |
+
next(msa_transformer.parameters()).device
|
| 132 |
+
)
|
| 133 |
+
msa_transformer_predictions = msa_transformer.forward(
|
| 134 |
+
msa_transformer_batch_tokens, repr_layers=[12]
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
# Extract features
|
| 138 |
+
msa_reps = (
|
| 139 |
+
msa_transformer_predictions["representations"][12]
|
| 140 |
+
.detach()
|
| 141 |
+
.cpu()
|
| 142 |
+
.numpy()
|
| 143 |
+
)
|
| 144 |
+
for i in range(num_msas):
|
| 145 |
+
logits = (
|
| 146 |
+
msa_transformer_predictions["logits"][i][0].detach().cpu().numpy()
|
| 147 |
+
)
|
| 148 |
+
avg_msa_rep = msa_reps[i].mean((0, 1))
|
| 149 |
+
sequence_rep = msa_reps[i][0].mean(0)
|
| 150 |
+
|
| 151 |
+
if output_type == "msa_representations":
|
| 152 |
+
reps.append(avg_msa_rep)
|
| 153 |
+
|
| 154 |
+
elif output_type == "sequence_representations":
|
| 155 |
+
reps.append(sequence_rep)
|
| 156 |
+
|
| 157 |
+
elif output_type == "log_probabilities":
|
| 158 |
+
reps.append(logits)
|
| 159 |
+
|
| 160 |
+
X = np.array(reps)
|
| 161 |
+
|
| 162 |
+
return X
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class MSAEmbedFeaturizer(MSABaseFeaturizer):
|
| 166 |
+
"""Class for generating MSA embedding features.
|
| 167 |
+
|
| 168 |
+
Attributes:
|
| 169 |
+
model_type (str): Type of featurization model to use.
|
| 170 |
+
name (str): Name of the featurizer.
|
| 171 |
+
protein (str): Name of protein being featurized.
|
| 172 |
+
use_cache (bool): Whether to cache featurization results.
|
| 173 |
+
flatten_features (bool): Whether to flatten output features.
|
| 174 |
+
device (torch.device): Device to use for computation.
|
| 175 |
+
batch_size (int): Batch size for processing.
|
| 176 |
+
model_locations (list): Paths to model files.
|
| 177 |
+
msa_file (str): Path to MSA file.
|
| 178 |
+
|
| 179 |
+
Example Usage:
|
| 180 |
+
|
| 181 |
+
featurizer = MSAEmbedFeaturizer(
|
| 182 |
+
protein='protein1', # Name of protein for caching
|
| 183 |
+
use_cache=True, # Whether to cache results
|
| 184 |
+
msa_file='msa.fasta', # Path to MSA file
|
| 185 |
+
batch_size=968 # Processing batch size
|
| 186 |
+
)
|
| 187 |
+
features = featurizer.featurize(sequences)
|
| 188 |
+
"""
|
| 189 |
+
|
| 190 |
+
def __init__(
|
| 191 |
+
self,
|
| 192 |
+
protein=None,
|
| 193 |
+
use_cache=False,
|
| 194 |
+
msa_file=None,
|
| 195 |
+
model_locations=FEATURE_MODELS["msa_embed"],
|
| 196 |
+
batch_size=968,
|
| 197 |
+
model_type="msa_embed",
|
| 198 |
+
**kwargs
|
| 199 |
+
):
|
| 200 |
+
"""
|
| 201 |
+
Args:
|
| 202 |
+
protein (str): Name of protein being featurized.
|
| 203 |
+
use_cache (bool): Whether to cache results.
|
| 204 |
+
msa_file (str): Path to MSA file.
|
| 205 |
+
model_locations (list): Paths to model files.
|
| 206 |
+
batch_size (int): Batch size for processing.
|
| 207 |
+
model_type (str): Type of featurization model.
|
| 208 |
+
**kwargs: Additional keyword arguments.
|
| 209 |
+
"""
|
| 210 |
+
super().__init__(
|
| 211 |
+
protein,
|
| 212 |
+
use_cache,
|
| 213 |
+
model_locations,
|
| 214 |
+
msa_file,
|
| 215 |
+
batch_size,
|
| 216 |
+
model_type,
|
| 217 |
+
**kwargs
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
def custom_featurizer(self, seqs, msa_file=None, **kwargs):
|
| 221 |
+
"""
|
| 222 |
+
Featurizes sequences using MSA embeddings.
|
| 223 |
+
|
| 224 |
+
Args:
|
| 225 |
+
seqs (list): List of sequences to featurize.
|
| 226 |
+
msa_file (str): Path to MSA file.
|
| 227 |
+
**kwargs: Additional keyword arguments.
|
| 228 |
+
|
| 229 |
+
Returns:
|
| 230 |
+
np.ndarray: Array of MSA embedding features.
|
| 231 |
+
"""
|
| 232 |
+
X = self.featurize_msa(
|
| 233 |
+
seqs, msa_file, output_type="msa_representations", **kwargs
|
| 234 |
+
)
|
| 235 |
+
return X
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
class MSASequenceEmbedFeaturizer(MSABaseFeaturizer):
|
| 239 |
+
"""Class for generating MSA sequence embedding features.
|
| 240 |
+
|
| 241 |
+
Attributes:
|
| 242 |
+
model_type (str): Type of featurization model to use.
|
| 243 |
+
name (str): Name of the featurizer.
|
| 244 |
+
protein (str): Name of protein being featurized.
|
| 245 |
+
use_cache (bool): Whether to cache featurization results.
|
| 246 |
+
flatten_features (bool): Whether to flatten output features.
|
| 247 |
+
device (torch.device): Device to use for computation.
|
| 248 |
+
batch_size (int): Batch size for processing.
|
| 249 |
+
model_locations (list): Paths to model files.
|
| 250 |
+
msa_file (str): Path to MSA file.
|
| 251 |
+
|
| 252 |
+
Example Usage:
|
| 253 |
+
|
| 254 |
+
featurizer = MSASequenceEmbedFeaturizer(
|
| 255 |
+
protein='protein1', # Name of protein for caching
|
| 256 |
+
use_cache=True, # Whether to cache results
|
| 257 |
+
msa_file='msa.fasta', # Path to MSA file
|
| 258 |
+
batch_size=968 # Processing batch size
|
| 259 |
+
)
|
| 260 |
+
features = featurizer.featurize(sequences)
|
| 261 |
+
"""
|
| 262 |
+
|
| 263 |
+
def __init__(
|
| 264 |
+
self,
|
| 265 |
+
protein=None,
|
| 266 |
+
use_cache=False,
|
| 267 |
+
msa_file=None,
|
| 268 |
+
model_locations=FEATURE_MODELS["msa_sequence_embed"],
|
| 269 |
+
batch_size=968,
|
| 270 |
+
model_type="msa_sequence_embed",
|
| 271 |
+
**kwargs
|
| 272 |
+
):
|
| 273 |
+
"""
|
| 274 |
+
Args:
|
| 275 |
+
protein (str): Name of protein being featurized.
|
| 276 |
+
use_cache (bool): Whether to cache results.
|
| 277 |
+
msa_file (str): Path to MSA file.
|
| 278 |
+
model_locations (list): Paths to model files.
|
| 279 |
+
batch_size (int): Batch size for processing.
|
| 280 |
+
model_type (str): Type of featurization model.
|
| 281 |
+
**kwargs: Additional keyword arguments.
|
| 282 |
+
"""
|
| 283 |
+
super().__init__(
|
| 284 |
+
protein,
|
| 285 |
+
use_cache,
|
| 286 |
+
model_locations,
|
| 287 |
+
msa_file,
|
| 288 |
+
batch_size,
|
| 289 |
+
model_type,
|
| 290 |
+
**kwargs
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
def custom_featurizer(self, seqs, msa_file=None, **kwargs):
|
| 294 |
+
"""
|
| 295 |
+
Featurizes sequences using MSA sequence embeddings.
|
| 296 |
+
|
| 297 |
+
Args:
|
| 298 |
+
seqs (list): List of sequences to featurize.
|
| 299 |
+
msa_file (str): Path to MSA file.
|
| 300 |
+
**kwargs: Additional keyword arguments.
|
| 301 |
+
|
| 302 |
+
Returns:
|
| 303 |
+
np.ndarray: Array of MSA sequence embedding features.
|
| 304 |
+
"""
|
| 305 |
+
X = self.featurize_msa(
|
| 306 |
+
seqs, msa_file, output_type="sequence_representations", **kwargs
|
| 307 |
+
)
|
| 308 |
+
return X
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
class MSALogitsFeaturizer(MSABaseFeaturizer):
|
| 312 |
+
"""Class for generating MSA logits features.
|
| 313 |
+
|
| 314 |
+
Attributes:
|
| 315 |
+
model_type (str): Type of featurization model to use.
|
| 316 |
+
name (str): Name of the featurizer.
|
| 317 |
+
protein (str): Name of protein being featurized.
|
| 318 |
+
use_cache (bool): Whether to cache featurization results.
|
| 319 |
+
flatten_features (bool): Whether to flatten output features.
|
| 320 |
+
device (torch.device): Device to use for computation.
|
| 321 |
+
batch_size (int): Batch size for processing.
|
| 322 |
+
model_locations (list): Paths to model files.
|
| 323 |
+
msa_file (str): Path to MSA file.
|
| 324 |
+
|
| 325 |
+
Example Usage:
|
| 326 |
+
|
| 327 |
+
featurizer = MSALogitsFeaturizer(
|
| 328 |
+
protein='protein1', # Name of protein for caching
|
| 329 |
+
use_cache=True, # Whether to cache results
|
| 330 |
+
msa_file='msa.fasta', # Path to MSA file
|
| 331 |
+
batch_size=968 # Processing batch size
|
| 332 |
+
)
|
| 333 |
+
features = featurizer.featurize(sequences)
|
| 334 |
+
"""
|
| 335 |
+
|
| 336 |
+
def __init__(
|
| 337 |
+
self,
|
| 338 |
+
protein=None,
|
| 339 |
+
use_cache=False,
|
| 340 |
+
msa_file=None,
|
| 341 |
+
model_locations=FEATURE_MODELS["msa_logits"],
|
| 342 |
+
batch_size=968,
|
| 343 |
+
model_type="msa_logits",
|
| 344 |
+
**kwargs
|
| 345 |
+
):
|
| 346 |
+
"""
|
| 347 |
+
Args:
|
| 348 |
+
protein (str): Name of protein being featurized.
|
| 349 |
+
use_cache (bool): Whether to cache results.
|
| 350 |
+
msa_file (str): Path to MSA file.
|
| 351 |
+
model_locations (list): Paths to model files.
|
| 352 |
+
batch_size (int): Batch size for processing.
|
| 353 |
+
model_type (str): Type of featurization model.
|
| 354 |
+
**kwargs: Additional keyword arguments.
|
| 355 |
+
"""
|
| 356 |
+
super().__init__(
|
| 357 |
+
protein,
|
| 358 |
+
use_cache,
|
| 359 |
+
model_locations,
|
| 360 |
+
msa_file,
|
| 361 |
+
batch_size,
|
| 362 |
+
model_type,
|
| 363 |
+
**kwargs
|
| 364 |
+
)
|
| 365 |
+
|
| 366 |
+
def custom_featurizer(self, seqs, msa_file=None, **kwargs):
|
| 367 |
+
"""
|
| 368 |
+
Featurizes sequences using MSA logits.
|
| 369 |
+
|
| 370 |
+
Args:
|
| 371 |
+
seqs (list): List of sequences to featurize.
|
| 372 |
+
msa_file (str): Path to MSA file.
|
| 373 |
+
**kwargs: Additional keyword arguments.
|
| 374 |
+
|
| 375 |
+
Returns:
|
| 376 |
+
np.ndarray: Array of MSA logits features.
|
| 377 |
+
"""
|
| 378 |
+
X = self.featurize_msa(
|
| 379 |
+
seqs, msa_file, output_type="log_probabilities", **kwargs
|
| 380 |
+
)
|
| 381 |
+
return X
|
model/featurizers/prott5_featurizers.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ankh
|
| 2 |
+
import numpy as np
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from model.featurizers.base_featurizers import BaseFeaturizer
|
| 6 |
+
from transformers import T5Tokenizer, T5EncoderModel
|
| 7 |
+
import re
|
| 8 |
+
|
| 9 |
+
class ProtT5BaseFeaturizer(BaseFeaturizer):
|
| 10 |
+
def __init__(self,
|
| 11 |
+
protein=None,
|
| 12 |
+
use_cache=False,
|
| 13 |
+
model_version=None,
|
| 14 |
+
batch_size=968,
|
| 15 |
+
model_type="ProtT5",
|
| 16 |
+
**kwargs):
|
| 17 |
+
|
| 18 |
+
super().__init__(model_type,protein, use_cache, **kwargs)
|
| 19 |
+
|
| 20 |
+
self.batch_size = batch_size
|
| 21 |
+
self.model_version = model_version
|
| 22 |
+
|
| 23 |
+
def featurize_prott5(self, seqs):
|
| 24 |
+
|
| 25 |
+
if self.model_version == 'prot_t5_xl_u50':
|
| 26 |
+
self.tokenizer = T5Tokenizer.from_pretrained('Rostlab/prot_t5_xl_half_uniref50-enc', do_lower_case=False)
|
| 27 |
+
self.model = T5EncoderModel.from_pretrained("Rostlab/prot_t5_xl_half_uniref50-enc").to(self.device)
|
| 28 |
+
else:
|
| 29 |
+
raise ValueError(f"Invalid model version: {self.model_version}")
|
| 30 |
+
|
| 31 |
+
input_seqs = [" ".join(list(re.sub(r"[UZOB]", "X", sequence))) for sequence in seqs]
|
| 32 |
+
|
| 33 |
+
seq_batch = []
|
| 34 |
+
|
| 35 |
+
for i in range(0, len(input_seqs), self.batch_size):
|
| 36 |
+
batch = input_seqs[i:i + self.batch_size]
|
| 37 |
+
# tokenize sequences and pad up to the longest sequence in the batch
|
| 38 |
+
ids = self.tokenizer(batch, add_special_tokens=True, padding="longest")
|
| 39 |
+
|
| 40 |
+
input_ids = torch.tensor(ids['input_ids']).to(self.device)
|
| 41 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(self.device)
|
| 42 |
+
|
| 43 |
+
# generate embeddings
|
| 44 |
+
with torch.no_grad():
|
| 45 |
+
embeddings = self.model(input_ids=input_ids, attention_mask=attention_mask)
|
| 46 |
+
|
| 47 |
+
seq_batch.append(embeddings['last_hidden_state'].mean(axis=1).cpu().numpy())
|
| 48 |
+
|
| 49 |
+
return np.concatenate(seq_batch)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class ProtT5_XL_U50_EmbedFeaturizer(ProtT5BaseFeaturizer):
|
| 53 |
+
def __init__(self,
|
| 54 |
+
protein=None,
|
| 55 |
+
use_cache=False,
|
| 56 |
+
model_version="prot_t5_xl_u50",
|
| 57 |
+
batch_size=968,
|
| 58 |
+
model_type="ProtT5_XL_U50_Embed",
|
| 59 |
+
**kwargs):
|
| 60 |
+
super().__init__(protein, use_cache, model_version, batch_size, model_type, **kwargs)
|
| 61 |
+
|
| 62 |
+
def custom_featurizer(self, seqs):
|
| 63 |
+
|
| 64 |
+
X = self.featurize_prott5(seqs)
|
| 65 |
+
return X
|
model/featurizers/unirep_featurizers.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from jax_unirep.featurize import get_reps
|
| 2 |
+
from jax_unirep.utils import load_params
|
| 3 |
+
|
| 4 |
+
from model.featurizers.base_featurizers import BaseFeaturizer
|
| 5 |
+
|
| 6 |
+
UNIREP_MODEL_SIZES = [1900, 256, 64]
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class UnirepBaseFeaturizer(BaseFeaturizer):
|
| 10 |
+
def __init__(
|
| 11 |
+
self,
|
| 12 |
+
protein=None,
|
| 13 |
+
use_cache=False,
|
| 14 |
+
model_locations=None,
|
| 15 |
+
model_type="unirep",
|
| 16 |
+
model_size=1900,
|
| 17 |
+
**kwargs,
|
| 18 |
+
):
|
| 19 |
+
super().__init__(model_type, protein, use_cache, **kwargs)
|
| 20 |
+
self.model_locations = model_locations
|
| 21 |
+
self.update_model_name(model_size)
|
| 22 |
+
self.load_params()
|
| 23 |
+
|
| 24 |
+
def update_model_name(self, model_size):
|
| 25 |
+
# Validate model size
|
| 26 |
+
assert model_size in UNIREP_MODEL_SIZES, "Model size must be 1900, 256, or 64."
|
| 27 |
+
self.model_size = model_size
|
| 28 |
+
self.model_type = self.model_type + str(model_size)
|
| 29 |
+
|
| 30 |
+
def load_params(self):
|
| 31 |
+
self.params = load_params(self.model_locations, self.model_size)[1]
|
| 32 |
+
|
| 33 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 34 |
+
h_avg, h_final, c_final = get_reps(
|
| 35 |
+
seqs=seqs, params=self.params, mlstm_size=self.model_size
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
return h_avg
|
| 39 |
+
|
| 40 |
+
class EvotunedUnirepFeaturizer(UnirepBaseFeaturizer):
|
| 41 |
+
def __init__(self, model_type="evotuned_unirep", **kwargs):
|
| 42 |
+
super().__init__(model_type=model_type, **kwargs)
|
model/featurizers/zeroshot_featurizers.py
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from Bio import SeqIO
|
| 2 |
+
|
| 3 |
+
from model.featurizers.base_featurizers import BaseFeaturizer
|
| 4 |
+
from model.featurizers.model_choices import FEATURE_MODELS
|
| 5 |
+
from model.utils.data_utils import find_mutations_multithreaded
|
| 6 |
+
|
| 7 |
+
class ZeroshotBaseFeaturizer(BaseFeaturizer):
|
| 8 |
+
"""Base class for zero-shot featurizers.
|
| 9 |
+
|
| 10 |
+
Attributes:
|
| 11 |
+
model_type (str): Type of featurization model to use.
|
| 12 |
+
name (str): Name of the featurizer.
|
| 13 |
+
protein (str): Name of protein being featurized.
|
| 14 |
+
use_cache (bool): Whether to cache featurization results.
|
| 15 |
+
flatten_features (bool): Whether to flatten output features.
|
| 16 |
+
device (torch.device): Device to use for computation.
|
| 17 |
+
model_locations (list): Paths to model files.
|
| 18 |
+
wt_file (str): Path to wild-type sequence file.
|
| 19 |
+
wt_seq (str): Wild-type protein sequence.
|
| 20 |
+
|
| 21 |
+
Example Usage:
|
| 22 |
+
|
| 23 |
+
featurizer = ZeroshotBaseFeaturizer(
|
| 24 |
+
protein='protein1', # Name of protein for caching
|
| 25 |
+
use_cache=True, # Whether to cache results
|
| 26 |
+
wt_file='wt.fasta', # Path to wild-type sequence
|
| 27 |
+
model_locations=[] # Paths to model files
|
| 28 |
+
)
|
| 29 |
+
features = featurizer.featurize(sequences)
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
def __init__(
|
| 33 |
+
self,
|
| 34 |
+
protein=None,
|
| 35 |
+
use_cache=False,
|
| 36 |
+
model_locations=None,
|
| 37 |
+
wt_file=None,
|
| 38 |
+
model_type="zeroshot",
|
| 39 |
+
**kwargs
|
| 40 |
+
):
|
| 41 |
+
"""
|
| 42 |
+
Args:
|
| 43 |
+
protein (str): Name of protein being featurized.
|
| 44 |
+
use_cache (bool): Whether to cache results.
|
| 45 |
+
model_locations (list): Paths to model files.
|
| 46 |
+
wt_file (str): Path to wild-type sequence file.
|
| 47 |
+
model_type (str): Type of featurization model.
|
| 48 |
+
**kwargs: Additional keyword arguments.
|
| 49 |
+
"""
|
| 50 |
+
super().__init__(model_type, protein, use_cache, **kwargs)
|
| 51 |
+
self.model_locations = model_locations
|
| 52 |
+
self.wt_file = wt_file
|
| 53 |
+
self.wt_seq = str(SeqIO.read(self.wt_file, "fasta").seq)
|
| 54 |
+
|
| 55 |
+
def featurize_zeroshot(
|
| 56 |
+
self, seqs, model_locations, wt_file, zeroshot_model, **kwargs
|
| 57 |
+
):
|
| 58 |
+
"""
|
| 59 |
+
Featurizes sequences using zero-shot prediction.
|
| 60 |
+
|
| 61 |
+
Args:
|
| 62 |
+
seqs (list): List of sequences to featurize.
|
| 63 |
+
model_locations (list): Paths to model files.
|
| 64 |
+
wt_file (str): Path to wild-type sequence file.
|
| 65 |
+
zeroshot_model (callable): Zero-shot prediction function.
|
| 66 |
+
**kwargs: Additional keyword arguments.
|
| 67 |
+
|
| 68 |
+
Returns:
|
| 69 |
+
np.ndarray: Zero-shot prediction scores.
|
| 70 |
+
"""
|
| 71 |
+
assert (self.wt_file is not None) or (
|
| 72 |
+
wt_file is not None
|
| 73 |
+
), "No wt sequence provided."
|
| 74 |
+
assert (self.model_locations is not None) or (
|
| 75 |
+
model_locations is not None
|
| 76 |
+
), "No model locations provided."
|
| 77 |
+
|
| 78 |
+
wt_file = wt_file or self.wt_file
|
| 79 |
+
model_locations = model_locations or self.model_locations
|
| 80 |
+
|
| 81 |
+
wt_seq = str(SeqIO.read(self.wt_file, "fasta").seq)
|
| 82 |
+
model_locations = self.model_locations
|
| 83 |
+
|
| 84 |
+
mutations = find_mutations_multithreaded(wt_seq, seqs)
|
| 85 |
+
|
| 86 |
+
# make sure to remove model_locations and sequence from kwargs
|
| 87 |
+
kwargs.pop("model_locations", None)
|
| 88 |
+
kwargs.pop("sequence", None)
|
| 89 |
+
kwargs['device'] = self.device
|
| 90 |
+
X = zeroshot_model(
|
| 91 |
+
mutations, model_locations=model_locations, sequence=wt_seq, **kwargs
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
X = X.reshape(-1, 1)
|
| 95 |
+
|
| 96 |
+
return X
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class ZeroshotESMFeaturizer(ZeroshotBaseFeaturizer):
|
| 100 |
+
"""Class for ESM zero-shot featurization.
|
| 101 |
+
|
| 102 |
+
Attributes:
|
| 103 |
+
model_type (str): Type of featurization model to use.
|
| 104 |
+
name (str): Name of the featurizer.
|
| 105 |
+
protein (str): Name of protein being featurized.
|
| 106 |
+
use_cache (bool): Whether to cache featurization results.
|
| 107 |
+
flatten_features (bool): Whether to flatten output features.
|
| 108 |
+
device (torch.device): Device to use for computation.
|
| 109 |
+
model_locations (list): Paths to model files.
|
| 110 |
+
wt_file (str): Path to wild-type sequence file.
|
| 111 |
+
wt_seq (str): Wild-type protein sequence.
|
| 112 |
+
|
| 113 |
+
Example Usage:
|
| 114 |
+
|
| 115 |
+
featurizer = ZeroshotESMFeaturizer(
|
| 116 |
+
protein='protein1', # Name of protein for caching
|
| 117 |
+
use_cache=True, # Whether to cache results
|
| 118 |
+
wt_file='wt.fasta' # Path to wild-type sequence
|
| 119 |
+
)
|
| 120 |
+
features = featurizer.featurize(sequences)
|
| 121 |
+
"""
|
| 122 |
+
|
| 123 |
+
def __init__(
|
| 124 |
+
self,
|
| 125 |
+
protein=None,
|
| 126 |
+
use_cache=False,
|
| 127 |
+
model_locations=FEATURE_MODELS["zeroshot_esm"],
|
| 128 |
+
wt_file=None,
|
| 129 |
+
model_type="zeroshot_esm",
|
| 130 |
+
**kwargs
|
| 131 |
+
):
|
| 132 |
+
"""
|
| 133 |
+
Args:
|
| 134 |
+
protein (str): Name of protein being featurized.
|
| 135 |
+
use_cache (bool): Whether to cache results.
|
| 136 |
+
model_locations (list): Paths to model files.
|
| 137 |
+
wt_file (str): Path to wild-type sequence file.
|
| 138 |
+
model_type (str): Type of featurization model.
|
| 139 |
+
**kwargs: Additional keyword arguments.
|
| 140 |
+
"""
|
| 141 |
+
super().__init__(
|
| 142 |
+
protein=protein,
|
| 143 |
+
use_cache=use_cache,
|
| 144 |
+
model_locations=model_locations,
|
| 145 |
+
wt_file=wt_file,
|
| 146 |
+
model_type=model_type,
|
| 147 |
+
**kwargs
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 151 |
+
"""
|
| 152 |
+
Featurizes sequences using ESM zero-shot prediction.
|
| 153 |
+
|
| 154 |
+
Args:
|
| 155 |
+
seqs (list): List of sequences to featurize.
|
| 156 |
+
**kwargs: Additional keyword arguments.
|
| 157 |
+
|
| 158 |
+
Returns:
|
| 159 |
+
np.ndarray: ESM zero-shot prediction scores.
|
| 160 |
+
"""
|
| 161 |
+
from model.utils.zeroshot_utils import zero_shot_esm as zero_shot
|
| 162 |
+
|
| 163 |
+
X = self.featurize_zeroshot(
|
| 164 |
+
seqs,
|
| 165 |
+
model_locations=self.model_locations,
|
| 166 |
+
wt_file=self.wt_file,
|
| 167 |
+
zeroshot_model=zero_shot,
|
| 168 |
+
**kwargs
|
| 169 |
+
)
|
| 170 |
+
return X
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
class ZeroshotMSAFeaturizer(ZeroshotBaseFeaturizer):
|
| 174 |
+
"""Class for MSA zero-shot featurization.
|
| 175 |
+
|
| 176 |
+
Attributes:
|
| 177 |
+
model_type (str): Type of featurization model to use.
|
| 178 |
+
name (str): Name of the featurizer.
|
| 179 |
+
protein (str): Name of protein being featurized.
|
| 180 |
+
use_cache (bool): Whether to cache featurization results.
|
| 181 |
+
flatten_features (bool): Whether to flatten output features.
|
| 182 |
+
device (torch.device): Device to use for computation.
|
| 183 |
+
model_locations (list): Paths to model files.
|
| 184 |
+
wt_file (str): Path to wild-type sequence file.
|
| 185 |
+
wt_seq (str): Wild-type protein sequence.
|
| 186 |
+
msa_file (str): Path to MSA file.
|
| 187 |
+
|
| 188 |
+
Example Usage:
|
| 189 |
+
|
| 190 |
+
featurizer = ZeroshotMSAFeaturizer(
|
| 191 |
+
protein='protein1', # Name of protein for caching
|
| 192 |
+
use_cache=True, # Whether to cache results
|
| 193 |
+
wt_file='wt.fasta', # Path to wild-type sequence
|
| 194 |
+
msa_file='msa.fasta' # Path to MSA file
|
| 195 |
+
)
|
| 196 |
+
features = featurizer.featurize(sequences)
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
def __init__(
|
| 200 |
+
self,
|
| 201 |
+
protein=None,
|
| 202 |
+
use_cache=False,
|
| 203 |
+
model_locations=FEATURE_MODELS["zeroshot_msa"],
|
| 204 |
+
wt_file=None,
|
| 205 |
+
msa_file=None,
|
| 206 |
+
model_type="zeroshot_msa",
|
| 207 |
+
**kwargs
|
| 208 |
+
):
|
| 209 |
+
"""
|
| 210 |
+
Args:
|
| 211 |
+
protein (str): Name of protein being featurized.
|
| 212 |
+
use_cache (bool): Whether to cache results.
|
| 213 |
+
model_locations (list): Paths to model files.
|
| 214 |
+
wt_file (str): Path to wild-type sequence file.
|
| 215 |
+
msa_file (str): Path to MSA file.
|
| 216 |
+
model_type (str): Type of featurization model.
|
| 217 |
+
**kwargs: Additional keyword arguments.
|
| 218 |
+
"""
|
| 219 |
+
super().__init__(
|
| 220 |
+
protein=protein,
|
| 221 |
+
use_cache=use_cache,
|
| 222 |
+
model_locations=model_locations,
|
| 223 |
+
wt_file=wt_file,
|
| 224 |
+
model_type=model_type,
|
| 225 |
+
**kwargs
|
| 226 |
+
)
|
| 227 |
+
self.msa_file = msa_file
|
| 228 |
+
|
| 229 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 230 |
+
"""
|
| 231 |
+
Featurizes sequences using MSA zero-shot prediction.
|
| 232 |
+
|
| 233 |
+
Args:
|
| 234 |
+
seqs (list): List of sequences to featurize.
|
| 235 |
+
**kwargs: Additional keyword arguments.
|
| 236 |
+
|
| 237 |
+
Returns:
|
| 238 |
+
np.ndarray: MSA zero-shot prediction scores.
|
| 239 |
+
"""
|
| 240 |
+
from model.utils.zeroshot_utils import zero_shot_msa as zero_shot
|
| 241 |
+
|
| 242 |
+
X = self.featurize_zeroshot(
|
| 243 |
+
seqs,
|
| 244 |
+
model_locations=self.model_locations,
|
| 245 |
+
wt_file=self.wt_file,
|
| 246 |
+
zeroshot_model=zero_shot,
|
| 247 |
+
msa_file=self.msa_file,
|
| 248 |
+
**kwargs
|
| 249 |
+
)
|
| 250 |
+
return X
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
class ZeroshotCSCSFeaturizer(ZeroshotBaseFeaturizer):
|
| 254 |
+
"""Class for CSCS zero-shot featurization.
|
| 255 |
+
|
| 256 |
+
Attributes:
|
| 257 |
+
model_type (str): Type of featurization model to use.
|
| 258 |
+
name (str): Name of the featurizer.
|
| 259 |
+
protein (str): Name of protein being featurized.
|
| 260 |
+
use_cache (bool): Whether to cache featurization results.
|
| 261 |
+
flatten_features (bool): Whether to flatten output features.
|
| 262 |
+
device (torch.device): Device to use for computation.
|
| 263 |
+
model_locations (list): Paths to model files.
|
| 264 |
+
wt_file (str): Path to wild-type sequence file.
|
| 265 |
+
wt_seq (str): Wild-type protein sequence.
|
| 266 |
+
|
| 267 |
+
Example Usage:
|
| 268 |
+
|
| 269 |
+
featurizer = ZeroshotCSCSFeaturizer(
|
| 270 |
+
protein='protein1', # Name of protein for caching
|
| 271 |
+
use_cache=True, # Whether to cache results
|
| 272 |
+
wt_file='wt.fasta' # Path to wild-type sequence
|
| 273 |
+
)
|
| 274 |
+
features = featurizer.featurize(sequences)
|
| 275 |
+
"""
|
| 276 |
+
|
| 277 |
+
def __init__(
|
| 278 |
+
self,
|
| 279 |
+
protein=None,
|
| 280 |
+
use_cache=False,
|
| 281 |
+
model_locations=None,
|
| 282 |
+
wt_file=None,
|
| 283 |
+
model_type="zeroshot_cscs",
|
| 284 |
+
**kwargs
|
| 285 |
+
):
|
| 286 |
+
"""
|
| 287 |
+
Args:
|
| 288 |
+
protein (str): Name of protein being featurized.
|
| 289 |
+
use_cache (bool): Whether to cache results.
|
| 290 |
+
model_locations (list): Paths to model files.
|
| 291 |
+
wt_file (str): Path to wild-type sequence file.
|
| 292 |
+
model_type (str): Type of featurization model.
|
| 293 |
+
**kwargs: Additional keyword arguments.
|
| 294 |
+
"""
|
| 295 |
+
super().__init__(
|
| 296 |
+
protein=protein,
|
| 297 |
+
use_cache=use_cache,
|
| 298 |
+
model_locations=model_locations,
|
| 299 |
+
wt_file=wt_file,
|
| 300 |
+
model_type=model_type,
|
| 301 |
+
**kwargs
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 305 |
+
"""
|
| 306 |
+
Featurizes sequences using CSCS zero-shot prediction.
|
| 307 |
+
|
| 308 |
+
Args:
|
| 309 |
+
seqs (list): List of sequences to featurize.
|
| 310 |
+
**kwargs: Additional keyword arguments.
|
| 311 |
+
|
| 312 |
+
Returns:
|
| 313 |
+
np.ndarray: CSCS zero-shot prediction scores.
|
| 314 |
+
"""
|
| 315 |
+
from model.utils.zeroshot_utils import zero_shot_cscs as zero_shot
|
| 316 |
+
|
| 317 |
+
X = self.featurize_zeroshot(
|
| 318 |
+
seqs,
|
| 319 |
+
model_locations=self.model_locations,
|
| 320 |
+
wt_file=self.wt_file,
|
| 321 |
+
zeroshot_model=zero_shot,
|
| 322 |
+
**kwargs
|
| 323 |
+
)
|
| 324 |
+
return X
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
class ZeroshotCSCSGramFeaturizer(ZeroshotBaseFeaturizer):
|
| 328 |
+
"""Class for CSCS-Gram zero-shot featurization.
|
| 329 |
+
|
| 330 |
+
Attributes:
|
| 331 |
+
model_type (str): Type of featurization model to use.
|
| 332 |
+
name (str): Name of the featurizer.
|
| 333 |
+
protein (str): Name of protein being featurized.
|
| 334 |
+
use_cache (bool): Whether to cache featurization results.
|
| 335 |
+
flatten_features (bool): Whether to flatten output features.
|
| 336 |
+
device (torch.device): Device to use for computation.
|
| 337 |
+
model_locations (list): Paths to model files.
|
| 338 |
+
wt_file (str): Path to wild-type sequence file.
|
| 339 |
+
wt_seq (str): Wild-type protein sequence.
|
| 340 |
+
|
| 341 |
+
Example Usage:
|
| 342 |
+
|
| 343 |
+
featurizer = ZeroshotCSCSGramFeaturizer(
|
| 344 |
+
protein='protein1', # Name of protein for caching
|
| 345 |
+
use_cache=True, # Whether to cache results
|
| 346 |
+
wt_file='wt.fasta' # Path to wild-type sequence
|
| 347 |
+
)
|
| 348 |
+
features = featurizer.featurize(sequences)
|
| 349 |
+
"""
|
| 350 |
+
|
| 351 |
+
def __init__(
|
| 352 |
+
self,
|
| 353 |
+
protein=None,
|
| 354 |
+
use_cache=False,
|
| 355 |
+
model_locations=None,
|
| 356 |
+
wt_file=None,
|
| 357 |
+
model_type="zeroshot_cscs_gram",
|
| 358 |
+
**kwargs
|
| 359 |
+
):
|
| 360 |
+
"""
|
| 361 |
+
Args:
|
| 362 |
+
protein (str): Name of protein being featurized.
|
| 363 |
+
use_cache (bool): Whether to cache results.
|
| 364 |
+
model_locations (list): Paths to model files.
|
| 365 |
+
wt_file (str): Path to wild-type sequence file.
|
| 366 |
+
model_type (str): Type of featurization model.
|
| 367 |
+
**kwargs: Additional keyword arguments.
|
| 368 |
+
"""
|
| 369 |
+
super().__init__(
|
| 370 |
+
protein=protein,
|
| 371 |
+
use_cache=use_cache,
|
| 372 |
+
model_locations=model_locations,
|
| 373 |
+
wt_file=wt_file,
|
| 374 |
+
model_type=model_type,
|
| 375 |
+
**kwargs
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 379 |
+
"""
|
| 380 |
+
Featurizes sequences using CSCS-Gram zero-shot prediction.
|
| 381 |
+
|
| 382 |
+
Args:
|
| 383 |
+
seqs (list): List of sequences to featurize.
|
| 384 |
+
**kwargs: Additional keyword arguments.
|
| 385 |
+
|
| 386 |
+
Returns:
|
| 387 |
+
np.ndarray: CSCS-Gram zero-shot prediction scores.
|
| 388 |
+
"""
|
| 389 |
+
from model.utils.zeroshot_utils import zero_shot_cscs_gram as zero_shot
|
| 390 |
+
|
| 391 |
+
X = self.featurize_zeroshot(
|
| 392 |
+
seqs,
|
| 393 |
+
model_locations=self.model_locations,
|
| 394 |
+
wt_file=self.wt_file,
|
| 395 |
+
zeroshot_model=zero_shot,
|
| 396 |
+
**kwargs
|
| 397 |
+
)
|
| 398 |
+
return X
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
class ZeroshotCSCSSemFeaturizer(ZeroshotBaseFeaturizer):
|
| 402 |
+
"""Class for CSCS-Sem zero-shot featurization.
|
| 403 |
+
|
| 404 |
+
Attributes:
|
| 405 |
+
model_type (str): Type of featurization model to use.
|
| 406 |
+
name (str): Name of the featurizer.
|
| 407 |
+
protein (str): Name of protein being featurized.
|
| 408 |
+
use_cache (bool): Whether to cache featurization results.
|
| 409 |
+
flatten_features (bool): Whether to flatten output features.
|
| 410 |
+
device (torch.device): Device to use for computation.
|
| 411 |
+
model_locations (list): Paths to model files.
|
| 412 |
+
wt_file (str): Path to wild-type sequence file.
|
| 413 |
+
wt_seq (str): Wild-type protein sequence.
|
| 414 |
+
|
| 415 |
+
Example Usage:
|
| 416 |
+
|
| 417 |
+
featurizer = ZeroshotCSCSSemFeaturizer(
|
| 418 |
+
protein='protein1', # Name of protein for caching
|
| 419 |
+
use_cache=True, # Whether to cache results
|
| 420 |
+
wt_file='wt.fasta' # Path to wild-type sequence
|
| 421 |
+
)
|
| 422 |
+
features = featurizer.featurize(sequences)
|
| 423 |
+
"""
|
| 424 |
+
|
| 425 |
+
def __init__(
|
| 426 |
+
self,
|
| 427 |
+
protein=None,
|
| 428 |
+
use_cache=False,
|
| 429 |
+
model_locations=None,
|
| 430 |
+
wt_file=None,
|
| 431 |
+
model_type="zeroshot_cscs_sem",
|
| 432 |
+
**kwargs
|
| 433 |
+
):
|
| 434 |
+
"""
|
| 435 |
+
Args:
|
| 436 |
+
protein (str): Name of protein being featurized.
|
| 437 |
+
use_cache (bool): Whether to cache results.
|
| 438 |
+
model_locations (list): Paths to model files.
|
| 439 |
+
wt_file (str): Path to wild-type sequence file.
|
| 440 |
+
model_type (str): Type of featurization model.
|
| 441 |
+
**kwargs: Additional keyword arguments.
|
| 442 |
+
"""
|
| 443 |
+
super().__init__(
|
| 444 |
+
protein=protein,
|
| 445 |
+
use_cache=use_cache,
|
| 446 |
+
model_locations=model_locations,
|
| 447 |
+
wt_file=wt_file,
|
| 448 |
+
model_type=model_type,
|
| 449 |
+
**kwargs
|
| 450 |
+
)
|
| 451 |
+
|
| 452 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 453 |
+
"""
|
| 454 |
+
Featurizes sequences using CSCS-Sem zero-shot prediction.
|
| 455 |
+
|
| 456 |
+
Args:
|
| 457 |
+
seqs (list): List of sequences to featurize.
|
| 458 |
+
**kwargs: Additional keyword arguments.
|
| 459 |
+
|
| 460 |
+
Returns:
|
| 461 |
+
np.ndarray: CSCS-Sem zero-shot prediction scores.
|
| 462 |
+
"""
|
| 463 |
+
from model.utils.zeroshot_utils import zero_shot_cscs_sem as zero_shot
|
| 464 |
+
|
| 465 |
+
X = self.featurize_zeroshot(
|
| 466 |
+
seqs,
|
| 467 |
+
model_locations=self.model_locations,
|
| 468 |
+
wt_file=self.wt_file,
|
| 469 |
+
zeroshot_model=zero_shot,
|
| 470 |
+
**kwargs
|
| 471 |
+
)
|
| 472 |
+
return X
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
class ZeroshotProseFeaturizer(ZeroshotBaseFeaturizer):
|
| 476 |
+
"""Class for ProSE zero-shot featurization.
|
| 477 |
+
|
| 478 |
+
Attributes:
|
| 479 |
+
model_type (str): Type of featurization model to use.
|
| 480 |
+
name (str): Name of the featurizer.
|
| 481 |
+
protein (str): Name of protein being featurized.
|
| 482 |
+
use_cache (bool): Whether to cache featurization results.
|
| 483 |
+
flatten_features (bool): Whether to flatten output features.
|
| 484 |
+
device (torch.device): Device to use for computation.
|
| 485 |
+
model_locations (list): Paths to model files.
|
| 486 |
+
wt_file (str): Path to wild-type sequence file.
|
| 487 |
+
wt_seq (str): Wild-type protein sequence.
|
| 488 |
+
|
| 489 |
+
Example Usage:
|
| 490 |
+
|
| 491 |
+
featurizer = ZeroshotProseFeaturizer(
|
| 492 |
+
protein='protein1', # Name of protein for caching
|
| 493 |
+
use_cache=True, # Whether to cache results
|
| 494 |
+
wt_file='wt.fasta' # Path to wild-type sequence
|
| 495 |
+
)
|
| 496 |
+
features = featurizer.featurize(sequences)
|
| 497 |
+
"""
|
| 498 |
+
|
| 499 |
+
def __init__(
|
| 500 |
+
self,
|
| 501 |
+
protein=None,
|
| 502 |
+
use_cache=False,
|
| 503 |
+
model_locations=FEATURE_MODELS["zeroshot_prose"],
|
| 504 |
+
wt_file=None,
|
| 505 |
+
model_type="zeroshot_prose",
|
| 506 |
+
**kwargs
|
| 507 |
+
):
|
| 508 |
+
"""
|
| 509 |
+
Args:
|
| 510 |
+
protein (str): Name of protein being featurized.
|
| 511 |
+
use_cache (bool): Whether to cache results.
|
| 512 |
+
model_locations (list): Paths to model files.
|
| 513 |
+
wt_file (str): Path to wild-type sequence file.
|
| 514 |
+
model_type (str): Type of featurization model.
|
| 515 |
+
**kwargs: Additional keyword arguments.
|
| 516 |
+
"""
|
| 517 |
+
super().__init__(
|
| 518 |
+
protein=protein,
|
| 519 |
+
use_cache=use_cache,
|
| 520 |
+
model_locations=model_locations,
|
| 521 |
+
wt_file=wt_file,
|
| 522 |
+
model_type=model_type,
|
| 523 |
+
**kwargs
|
| 524 |
+
)
|
| 525 |
+
|
| 526 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 527 |
+
"""
|
| 528 |
+
Featurizes sequences using ProSE zero-shot prediction.
|
| 529 |
+
|
| 530 |
+
Args:
|
| 531 |
+
seqs (list): List of sequences to featurize.
|
| 532 |
+
**kwargs: Additional keyword arguments.
|
| 533 |
+
|
| 534 |
+
Returns:
|
| 535 |
+
np.ndarray: ProSE zero-shot prediction scores.
|
| 536 |
+
"""
|
| 537 |
+
from model.utils.zeroshot_utils import zero_shot_prose as zero_shot
|
| 538 |
+
|
| 539 |
+
X = self.featurize_zeroshot(
|
| 540 |
+
seqs,
|
| 541 |
+
model_locations=self.model_locations,
|
| 542 |
+
wt_file=self.wt_file,
|
| 543 |
+
zeroshot_model=zero_shot,
|
| 544 |
+
**kwargs
|
| 545 |
+
)
|
| 546 |
+
return X
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
class ZeroshotESMIFFeaturizer(ZeroshotBaseFeaturizer):
|
| 550 |
+
"""Class for ESM-IF zero-shot featurization.
|
| 551 |
+
|
| 552 |
+
Attributes:
|
| 553 |
+
model_type (str): Type of featurization model to use.
|
| 554 |
+
name (str): Name of the featurizer.
|
| 555 |
+
protein (str): Name of protein being featurized.
|
| 556 |
+
use_cache (bool): Whether to cache featurization results.
|
| 557 |
+
flatten_features (bool): Whether to flatten output features.
|
| 558 |
+
device (torch.device): Device to use for computation.
|
| 559 |
+
model_locations (list): Paths to model files.
|
| 560 |
+
wt_file (str): Path to wild-type sequence file.
|
| 561 |
+
wt_seq (str): Wild-type protein sequence.
|
| 562 |
+
pdb_file (str): Path to PDB structure file.
|
| 563 |
+
chain_id (str): Chain identifier in PDB file.
|
| 564 |
+
|
| 565 |
+
Example Usage:
|
| 566 |
+
|
| 567 |
+
featurizer = ZeroshotESMIFFeaturizer(
|
| 568 |
+
protein='protein1', # Name of protein for caching
|
| 569 |
+
use_cache=True, # Whether to cache results
|
| 570 |
+
wt_file='wt.fasta', # Path to wild-type sequence
|
| 571 |
+
pdb_file='struct.pdb', # Path to structure file
|
| 572 |
+
chain_id='A' # Chain identifier
|
| 573 |
+
)
|
| 574 |
+
features = featurizer.featurize(sequences)
|
| 575 |
+
"""
|
| 576 |
+
|
| 577 |
+
def __init__(
|
| 578 |
+
self,
|
| 579 |
+
protein=None,
|
| 580 |
+
use_cache=False,
|
| 581 |
+
model_locations=FEATURE_MODELS["zeroshot_esmif"],
|
| 582 |
+
wt_file=None,
|
| 583 |
+
model_type="zeroshot_esmif",
|
| 584 |
+
pdb_file=None,
|
| 585 |
+
chain_id='A',
|
| 586 |
+
**kwargs
|
| 587 |
+
):
|
| 588 |
+
"""
|
| 589 |
+
Args:
|
| 590 |
+
protein (str): Name of protein being featurized.
|
| 591 |
+
use_cache (bool): Whether to cache results.
|
| 592 |
+
model_locations (list): Paths to model files.
|
| 593 |
+
wt_file (str): Path to wild-type sequence file.
|
| 594 |
+
model_type (str): Type of featurization model.
|
| 595 |
+
pdb_file (str): Path to PDB structure file.
|
| 596 |
+
chain_id (str): Chain identifier in PDB file.
|
| 597 |
+
**kwargs: Additional keyword arguments.
|
| 598 |
+
"""
|
| 599 |
+
super().__init__(
|
| 600 |
+
protein=protein,
|
| 601 |
+
use_cache=use_cache,
|
| 602 |
+
model_locations=model_locations,
|
| 603 |
+
wt_file=wt_file,
|
| 604 |
+
model_type=model_type,
|
| 605 |
+
pdb_file=pdb_file,
|
| 606 |
+
chain_id=chain_id,
|
| 607 |
+
**kwargs
|
| 608 |
+
)
|
| 609 |
+
|
| 610 |
+
def custom_featurizer(self, seqs, **kwargs):
|
| 611 |
+
"""
|
| 612 |
+
Featurizes sequences using ESM-IF zero-shot prediction.
|
| 613 |
+
|
| 614 |
+
Args:
|
| 615 |
+
seqs (list): List of sequences to featurize.
|
| 616 |
+
**kwargs: Additional keyword arguments.
|
| 617 |
+
|
| 618 |
+
Returns:
|
| 619 |
+
np.ndarray: ESM-IF zero-shot prediction scores.
|
| 620 |
+
"""
|
| 621 |
+
from model.utils.zeroshot_utils import zero_shot_esm_if as zero_shot
|
| 622 |
+
|
| 623 |
+
X = self.featurize_zeroshot(
|
| 624 |
+
seqs,
|
| 625 |
+
model_locations=self.model_locations,
|
| 626 |
+
wt_file=self.wt_file,
|
| 627 |
+
zeroshot_model=zero_shot,
|
| 628 |
+
pdb_file=self.pdb_file,
|
| 629 |
+
chain_id=self.chain_id,
|
| 630 |
+
**kwargs
|
| 631 |
+
)
|
| 632 |
+
return X
|
model/multievolve_workflow.png
ADDED
|
Git LFS Details
|
model/predictors/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from model.predictors.base_regressors import *
|
| 2 |
+
from model.predictors.gaussian_process_regressors import *
|
| 3 |
+
from model.predictors.neural_net_regressors import *
|
model/predictors/base_regressors.py
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import pickle
|
| 3 |
+
from abc import ABC, abstractmethod
|
| 4 |
+
|
| 5 |
+
from matplotlib import pyplot as plt
|
| 6 |
+
import numpy as np
|
| 7 |
+
import pandas as pd
|
| 8 |
+
import scipy.stats as ss
|
| 9 |
+
from sklearn.metrics import make_scorer
|
| 10 |
+
from sklearn.linear_model import Ridge, LinearRegression
|
| 11 |
+
from sklearn.model_selection import cross_val_score
|
| 12 |
+
from sklearn.preprocessing import StandardScaler
|
| 13 |
+
from sklearn.ensemble import RandomForestRegressor as RFRegressor
|
| 14 |
+
|
| 15 |
+
from model.utils.other_utils import performance_report
|
| 16 |
+
|
| 17 |
+
def run_model_experiments(splits,
|
| 18 |
+
features,
|
| 19 |
+
models,
|
| 20 |
+
experiment_name,
|
| 21 |
+
use_cache=False,
|
| 22 |
+
show_plots=True):
|
| 23 |
+
"""
|
| 24 |
+
Trains multiple models with various data splits and features, evaluates their performance,
|
| 25 |
+
and compiles the results into a CSV file.
|
| 26 |
+
|
| 27 |
+
Args:
|
| 28 |
+
splits (list): A list of data splits to use for training
|
| 29 |
+
features (list): A list of features to use for training
|
| 30 |
+
models (list): A list of model instances to train
|
| 31 |
+
experiment_name (str): Name of the experiment for saving results
|
| 32 |
+
use_cache (bool, optional): Whether to use cached models. Defaults to False.
|
| 33 |
+
show_plots (bool, optional): Whether to show matplotlib plots. Defaults to True.
|
| 34 |
+
|
| 35 |
+
Returns:
|
| 36 |
+
pd.DataFrame: DataFrame containing evaluation results for all models
|
| 37 |
+
|
| 38 |
+
Example Usage:
|
| 39 |
+
|
| 40 |
+
run_model_experiments(splits,
|
| 41 |
+
features,
|
| 42 |
+
models,
|
| 43 |
+
experiment_name,
|
| 44 |
+
use_cache=False)
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
names = []
|
| 48 |
+
stats = []
|
| 49 |
+
|
| 50 |
+
for split in splits:
|
| 51 |
+
for feature in features:
|
| 52 |
+
for model in models:
|
| 53 |
+
|
| 54 |
+
instance = model(split, feature, use_cache=use_cache, show_plots=show_plots)
|
| 55 |
+
|
| 56 |
+
# Train and evaluate model
|
| 57 |
+
stat = instance.run_model()
|
| 58 |
+
names.append(instance.file_attrs['model_name'].split('__'))
|
| 59 |
+
stats.append(list(stat.values()))
|
| 60 |
+
|
| 61 |
+
# Return results for all training permutations
|
| 62 |
+
stats_array = np.array(stats)
|
| 63 |
+
names_array = np.array(names)
|
| 64 |
+
combined_array = np.concatenate([names_array, stats_array], axis=1)
|
| 65 |
+
columns = ['Data Split', 'Feature', 'Model'] + list(stat.keys())
|
| 66 |
+
table = pd.DataFrame(combined_array, columns=columns)
|
| 67 |
+
|
| 68 |
+
# Check if the directory exists, create it if it doesn't
|
| 69 |
+
dir_path = f'{instance.file_attrs["model_dir"]}/' + "results"
|
| 70 |
+
if not os.path.exists(dir_path):
|
| 71 |
+
os.makedirs(dir_path)
|
| 72 |
+
|
| 73 |
+
# Save the results
|
| 74 |
+
if use_cache:
|
| 75 |
+
table.to_csv(f"{dir_path}/{experiment_name}.csv", index=False)
|
| 76 |
+
|
| 77 |
+
return table
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class BaseRegressor(ABC):
|
| 81 |
+
"""
|
| 82 |
+
Abstract base class for regression models.
|
| 83 |
+
|
| 84 |
+
Args:
|
| 85 |
+
data_splitter: Object containing train/test splits
|
| 86 |
+
featurizer: Object that converts sequences to features
|
| 87 |
+
model (str, optional): Name of model. Defaults to 'Base'
|
| 88 |
+
use_cache (bool, optional): Whether to use cached models. Defaults to False
|
| 89 |
+
show_plots (bool, optional): Whether to show matplotlib plots. Defaults to True
|
| 90 |
+
**kwargs: Additional keyword arguments
|
| 91 |
+
|
| 92 |
+
Attributes:
|
| 93 |
+
model_name (str): Name of the model
|
| 94 |
+
featurizer: Featurizer object
|
| 95 |
+
use_cache (bool): Whether to use cached models
|
| 96 |
+
kwargs (dict): Additional keyword arguments
|
| 97 |
+
X_train: Training features
|
| 98 |
+
X_test: Test features
|
| 99 |
+
y_train: Training labels
|
| 100 |
+
y_test: Test labels
|
| 101 |
+
split_method (str): Name of data split method
|
| 102 |
+
file_attrs (dict): Dictionary of file attributes and paths
|
| 103 |
+
show_plots (bool): Whether to show matplotlib plots. Defaults to True
|
| 104 |
+
|
| 105 |
+
Example Usage:
|
| 106 |
+
|
| 107 |
+
regressor = BaseRegressor(data_splitter, featurizer, model='Linear', use_cache=False, show_plots=True)
|
| 108 |
+
regressor.run_model()
|
| 109 |
+
"""
|
| 110 |
+
|
| 111 |
+
def __init__(self,
|
| 112 |
+
data_splitter,
|
| 113 |
+
featurizer,
|
| 114 |
+
model='Base',
|
| 115 |
+
use_cache=False,
|
| 116 |
+
show_plots=True,
|
| 117 |
+
**kwargs):
|
| 118 |
+
|
| 119 |
+
# Set variables
|
| 120 |
+
self.model_name = model
|
| 121 |
+
self.featurizer = featurizer
|
| 122 |
+
self.use_cache = use_cache
|
| 123 |
+
self.show_plots = show_plots
|
| 124 |
+
self.kwargs = kwargs
|
| 125 |
+
|
| 126 |
+
# Setup data
|
| 127 |
+
self.X_train = data_splitter.splits['X_train']
|
| 128 |
+
self.X_test = data_splitter.splits['X_test']
|
| 129 |
+
self.y_train = data_splitter.splits['y_train']
|
| 130 |
+
self.y_test = data_splitter.splits['y_test']
|
| 131 |
+
self.split_method = data_splitter.splits['split_name']
|
| 132 |
+
|
| 133 |
+
# Check if 'X_val' is not a key in data_splitter
|
| 134 |
+
if 'X_val' in data_splitter.splits:
|
| 135 |
+
print("Validation sets do not need to be present in data splits for non-neural network models.")
|
| 136 |
+
|
| 137 |
+
# Set model directory
|
| 138 |
+
self.file_attrs = data_splitter.file_attrs
|
| 139 |
+
self.file_attrs['model_name'] = self.split_method + ' __ ' + self.featurizer.name + ' __ ' + self.model_name
|
| 140 |
+
self.file_attrs['model_dir'] = os.path.join(data_splitter.file_attrs["dataset_dir"], 'model_cache', data_splitter.file_attrs["dataset_name"])
|
| 141 |
+
self.file_attrs['model_path'] = os.path.join(self.file_attrs['model_dir'], 'objects', f'{self.file_attrs["model_name"]}.pkl')
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
# Load model if available
|
| 145 |
+
if self.file_attrs['model_path'] is not None and os.path.exists(self.file_attrs['model_path']) and self.use_cache:
|
| 146 |
+
self.load_model(self.file_attrs['model_path'])
|
| 147 |
+
|
| 148 |
+
def run_model(self, eval=True):
|
| 149 |
+
"""
|
| 150 |
+
Runs model training and evaluation.
|
| 151 |
+
|
| 152 |
+
Args:
|
| 153 |
+
eval (bool, optional): Whether to evaluate the model. Defaults to True.
|
| 154 |
+
|
| 155 |
+
Returns:
|
| 156 |
+
If eval=True:
|
| 157 |
+
dict: Dictionary of evaluation statistics
|
| 158 |
+
If eval=False:
|
| 159 |
+
None
|
| 160 |
+
"""
|
| 161 |
+
|
| 162 |
+
if self.file_attrs['model_path'] is not None and os.path.exists(self.file_attrs['model_path']) and self.use_cache:
|
| 163 |
+
pass
|
| 164 |
+
else:
|
| 165 |
+
print(f'Training model for {self.file_attrs["model_name"]}')
|
| 166 |
+
X = self.preprocess_data(self.X_train)
|
| 167 |
+
self.train(X, self.y_train)
|
| 168 |
+
|
| 169 |
+
if self.use_cache:
|
| 170 |
+
self.save_model()
|
| 171 |
+
|
| 172 |
+
if eval:
|
| 173 |
+
return self.evaluate()
|
| 174 |
+
else:
|
| 175 |
+
return None
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def load_model(self, model_path=None):
|
| 179 |
+
"""
|
| 180 |
+
Loads a pre-trained model from a pkl file.
|
| 181 |
+
|
| 182 |
+
Args:
|
| 183 |
+
model_path (str, optional): Path to model file. Defaults to None.
|
| 184 |
+
"""
|
| 185 |
+
|
| 186 |
+
# set location to load model
|
| 187 |
+
model_path = self.file_attrs['model_path'] if model_path is None else model_path
|
| 188 |
+
print(f'Loading model from {model_path}')
|
| 189 |
+
|
| 190 |
+
try:
|
| 191 |
+
with open(model_path, 'rb') as file:
|
| 192 |
+
self.model = pickle.load(file)
|
| 193 |
+
print("Model loaded successfully.")
|
| 194 |
+
except FileNotFoundError:
|
| 195 |
+
print(f"Error: The file at {model_path} was not found.")
|
| 196 |
+
except PermissionError:
|
| 197 |
+
print(f"Error: Permission denied when trying to read {model_path}.")
|
| 198 |
+
except Exception as e:
|
| 199 |
+
print(f"An error occurred while loading the model: {str(e)}")
|
| 200 |
+
|
| 201 |
+
def save_model(self, model_path=None):
|
| 202 |
+
"""
|
| 203 |
+
Saves the model to a pkl file.
|
| 204 |
+
|
| 205 |
+
Args:
|
| 206 |
+
model_path (str, optional): Path to save model to. Defaults to None.
|
| 207 |
+
"""
|
| 208 |
+
|
| 209 |
+
# set location to save model
|
| 210 |
+
model_path = self.file_attrs['model_path'] if model_path is None else model_path
|
| 211 |
+
|
| 212 |
+
dir_path = os.path.join(self.file_attrs['model_dir'], 'objects')
|
| 213 |
+
# Check if the directory exists, create it if it doesn't
|
| 214 |
+
if not os.path.exists(dir_path):
|
| 215 |
+
os.makedirs(dir_path)
|
| 216 |
+
|
| 217 |
+
# Save the model
|
| 218 |
+
print(f'Saving model to {model_path}')
|
| 219 |
+
pickle.dump(self.model, open(model_path, 'wb'))
|
| 220 |
+
|
| 221 |
+
def featurize(self, X):
|
| 222 |
+
"""
|
| 223 |
+
Featurizes a list of sequences.
|
| 224 |
+
|
| 225 |
+
Args:
|
| 226 |
+
X (list): List of sequences to featurize
|
| 227 |
+
|
| 228 |
+
Returns:
|
| 229 |
+
array: Featurized sequences
|
| 230 |
+
"""
|
| 231 |
+
|
| 232 |
+
X_featurized = self.featurizer.featurize(X)
|
| 233 |
+
return X_featurized
|
| 234 |
+
|
| 235 |
+
def preprocess_data(self, X):
|
| 236 |
+
"""
|
| 237 |
+
Featurizes and scales input data.
|
| 238 |
+
|
| 239 |
+
Args:
|
| 240 |
+
X (list): List of sequences to preprocess
|
| 241 |
+
|
| 242 |
+
Returns:
|
| 243 |
+
array: Preprocessed data
|
| 244 |
+
"""
|
| 245 |
+
X = self.featurizer.featurize(X)
|
| 246 |
+
|
| 247 |
+
X = X.reshape(X.shape[0],-1)
|
| 248 |
+
|
| 249 |
+
return X
|
| 250 |
+
|
| 251 |
+
@abstractmethod
|
| 252 |
+
def train(self, X, y):
|
| 253 |
+
"""
|
| 254 |
+
Trains the model.
|
| 255 |
+
|
| 256 |
+
Args:
|
| 257 |
+
X (array): Input features
|
| 258 |
+
y (array): Target values
|
| 259 |
+
|
| 260 |
+
Returns:
|
| 261 |
+
Trained model, also stored in self.model
|
| 262 |
+
"""
|
| 263 |
+
pass
|
| 264 |
+
|
| 265 |
+
def evaluate(self):
|
| 266 |
+
"""
|
| 267 |
+
Evaluates model on test set.
|
| 268 |
+
|
| 269 |
+
Returns:
|
| 270 |
+
dict: Dictionary of evaluation statistics
|
| 271 |
+
"""
|
| 272 |
+
|
| 273 |
+
# Evaluate model
|
| 274 |
+
y_pred = self.predict(self.X_test)
|
| 275 |
+
|
| 276 |
+
# Reshape data and get correlation stats
|
| 277 |
+
y, y_pred = np.array(self.y_test), np.array(y_pred)
|
| 278 |
+
y, y_pred = y.reshape(-1), y_pred.reshape(-1)
|
| 279 |
+
|
| 280 |
+
# Get stats
|
| 281 |
+
stats = performance_report(y, y_pred)
|
| 282 |
+
|
| 283 |
+
# Set the default parameters
|
| 284 |
+
plt.rcParams['font.size'] = 7
|
| 285 |
+
plt.rcParams['lines.linewidth'] = 0.5
|
| 286 |
+
|
| 287 |
+
# Plotting Results
|
| 288 |
+
fig, ax = plt.subplots(figsize=(4, 3)) # Adjust size as needed
|
| 289 |
+
|
| 290 |
+
## Mark data points that have activity less than 0 or greater than 1.2x the max experimental y value
|
| 291 |
+
y_max = max(y.max() * 1.2, y_pred.max() * 1.2)
|
| 292 |
+
y_min = min(y.min() * 0.8, y_pred.min() * 0.8)
|
| 293 |
+
# colors = np.where(y_pred > y_max, 'crimson', np.where(y_pred < 0, 'crimson', 'dodgerblue'))
|
| 294 |
+
# y_pred_adjusted = np.clip(y_pred, 0, y_max)
|
| 295 |
+
|
| 296 |
+
## Scatter plot for main graph
|
| 297 |
+
ax.scatter(y_pred, y, c='dodgerblue', alpha=0.4, edgecolors='w', linewidth=0.5)
|
| 298 |
+
|
| 299 |
+
## Draw x=y line
|
| 300 |
+
ax.plot([y_min, y_max], [y_min, y_max], 'k--', linewidth=0.5)
|
| 301 |
+
|
| 302 |
+
## Set labels and title for main graph
|
| 303 |
+
ax.text(0.9, 0.1, f'Pearson r={stats["Pearson r"]:.2f}', fontsize=7, ha='right', va='bottom', transform=ax.transAxes)
|
| 304 |
+
ax.text(0.9, 0.2, f'Spearman r={stats["Spearman r"]:.2f}', fontsize=7, ha='right', va='bottom', transform=ax.transAxes)
|
| 305 |
+
ax.set_xlabel('Predicted Score', fontsize=7)
|
| 306 |
+
ax.set_ylabel('True Score', fontsize=7)
|
| 307 |
+
ax.set_title('Model Performance', fontsize=7)
|
| 308 |
+
ax.set_xlim(y_min, y_max)
|
| 309 |
+
|
| 310 |
+
## Display model parameters using legend
|
| 311 |
+
model_params = self.file_attrs["model_name"].split('__') # Assuming '|' separates different parameters
|
| 312 |
+
param_text = '\n'.join(model_params)
|
| 313 |
+
props = dict(boxstyle='square', facecolor='wheat', alpha=0.2)
|
| 314 |
+
ax.text(0.02, 0.98, param_text, transform=ax.transAxes, fontsize=7, verticalalignment='top', bbox=props)
|
| 315 |
+
|
| 316 |
+
# Adjust tick parameters
|
| 317 |
+
ax.tick_params(axis='both', which='major', labelsize=7)
|
| 318 |
+
|
| 319 |
+
# Show figure
|
| 320 |
+
if self.show_plots:
|
| 321 |
+
plt.show()
|
| 322 |
+
plt.close(fig)
|
| 323 |
+
|
| 324 |
+
# Return the stats
|
| 325 |
+
return stats
|
| 326 |
+
|
| 327 |
+
@abstractmethod
|
| 328 |
+
def custom_predictor(self, X):
|
| 329 |
+
"""
|
| 330 |
+
Custom prediction method to be implemented in subclasses.
|
| 331 |
+
Inputs have been filtered by self.predict()
|
| 332 |
+
|
| 333 |
+
Args:
|
| 334 |
+
X (array): Featurized sequences
|
| 335 |
+
|
| 336 |
+
Returns:
|
| 337 |
+
array: Model predictions
|
| 338 |
+
"""
|
| 339 |
+
pass
|
| 340 |
+
|
| 341 |
+
def predict(self, X):
|
| 342 |
+
"""
|
| 343 |
+
Gets model predictions. Runs checks and calls custom_predictor.
|
| 344 |
+
|
| 345 |
+
Args:
|
| 346 |
+
X (list): List of sequences
|
| 347 |
+
|
| 348 |
+
Returns:
|
| 349 |
+
array: Model predictions
|
| 350 |
+
"""
|
| 351 |
+
|
| 352 |
+
X_featurized = self.featurizer.featurize(X)
|
| 353 |
+
|
| 354 |
+
X_featurized = X_featurized.reshape(X_featurized.shape[0],-1)
|
| 355 |
+
|
| 356 |
+
predictions = self.custom_predictor(X_featurized)
|
| 357 |
+
|
| 358 |
+
return predictions
|
| 359 |
+
|
| 360 |
+
class IdentityRegressor(BaseRegressor):
|
| 361 |
+
"""
|
| 362 |
+
Identity regressor that returns all 1's.
|
| 363 |
+
|
| 364 |
+
Args:
|
| 365 |
+
data_splitter: Object containing train/test splits
|
| 366 |
+
featurizer: Object that converts sequences to features
|
| 367 |
+
model (str, optional): Name of model. Defaults to 'Linear'
|
| 368 |
+
use_cache (bool, optional): Whether to use cached models. Defaults to False
|
| 369 |
+
**kwargs: Additional keyword arguments
|
| 370 |
+
"""
|
| 371 |
+
|
| 372 |
+
def train(self, X, y):
|
| 373 |
+
pass
|
| 374 |
+
|
| 375 |
+
def custom_predictor(self, X):
|
| 376 |
+
return [1 for _ in range(len(X))]
|
| 377 |
+
|
| 378 |
+
class LinearRegressor(BaseRegressor):
|
| 379 |
+
"""
|
| 380 |
+
Linear regression model.
|
| 381 |
+
|
| 382 |
+
Args:
|
| 383 |
+
data_splitter: Object containing train/test splits
|
| 384 |
+
featurizer: Object that converts sequences to features
|
| 385 |
+
model (str, optional): Name of model. Defaults to 'Linear'
|
| 386 |
+
use_cache (bool, optional): Whether to use cached models. Defaults to False
|
| 387 |
+
**kwargs: Additional keyword arguments
|
| 388 |
+
"""
|
| 389 |
+
def __init__(self, data_splitter, featurizer, model='Linear', use_cache=False, **kwargs):
|
| 390 |
+
super().__init__(data_splitter, featurizer, model, use_cache, **kwargs)
|
| 391 |
+
|
| 392 |
+
def train(self, X, y):
|
| 393 |
+
model = LinearRegression(
|
| 394 |
+
fit_intercept=True,
|
| 395 |
+
copy_X=True,
|
| 396 |
+
n_jobs=10,
|
| 397 |
+
)
|
| 398 |
+
model.fit(X, y)
|
| 399 |
+
self.model = model
|
| 400 |
+
|
| 401 |
+
def custom_predictor(self, X):
|
| 402 |
+
return self.model.predict(X)
|
| 403 |
+
|
| 404 |
+
class RandomForestRegressor(BaseRegressor):
|
| 405 |
+
"""
|
| 406 |
+
Random Forest regression model.
|
| 407 |
+
|
| 408 |
+
Args:
|
| 409 |
+
data_splitter: Object containing train/test splits
|
| 410 |
+
featurizer: Object that converts sequences to features
|
| 411 |
+
model (str, optional): Name of model. Defaults to 'RandomForest'
|
| 412 |
+
use_cache (bool, optional): Whether to use cached models. Defaults to False
|
| 413 |
+
n_estimators (int, optional): Number of trees. Defaults to 100
|
| 414 |
+
criterion (str, optional): Split criterion. Defaults to 'friedman_mse'
|
| 415 |
+
max_depth (int, optional): Max tree depth. Defaults to None
|
| 416 |
+
min_samples_split (int, optional): Min samples for split. Defaults to 2
|
| 417 |
+
min_samples_leaf (int, optional): Min samples in leaf. Defaults to 1
|
| 418 |
+
min_weight_fraction_leaf (float, optional): Min weight fraction in leaf. Defaults to 0.0
|
| 419 |
+
max_features (float, optional): Max features to consider. Defaults to 1.0
|
| 420 |
+
max_leaf_nodes (int, optional): Max leaf nodes. Defaults to None
|
| 421 |
+
min_impurity_decrease (float, optional): Min impurity decrease. Defaults to 0.0
|
| 422 |
+
bootstrap (bool, optional): Whether to bootstrap. Defaults to True
|
| 423 |
+
oob_score (bool, optional): Whether to use out-of-bag score. Defaults to False
|
| 424 |
+
n_jobs (int, optional): Number of parallel jobs. Defaults to 6
|
| 425 |
+
random_state (int, optional): Random seed. Defaults to 1
|
| 426 |
+
verbose (int, optional): Verbosity level. Defaults to 0
|
| 427 |
+
warm_start (bool, optional): Whether to reuse solution. Defaults to False
|
| 428 |
+
ccp_alpha (float, optional): Complexity parameter. Defaults to 0.0
|
| 429 |
+
max_samples (int, optional): Max samples for bootstrap. Defaults to None
|
| 430 |
+
**kwargs: Additional keyword arguments
|
| 431 |
+
"""
|
| 432 |
+
def __init__(self, data_splitter, featurizer, model='RandomForest', use_cache=False,
|
| 433 |
+
n_estimators=100,
|
| 434 |
+
criterion='friedman_mse',
|
| 435 |
+
max_depth=None,
|
| 436 |
+
min_samples_split=2,
|
| 437 |
+
min_samples_leaf=1,
|
| 438 |
+
min_weight_fraction_leaf=0.0,
|
| 439 |
+
max_features=1.0,
|
| 440 |
+
max_leaf_nodes=None,
|
| 441 |
+
min_impurity_decrease=0.0,
|
| 442 |
+
bootstrap=True,
|
| 443 |
+
oob_score=False,
|
| 444 |
+
n_jobs=6, # change this based on number of cores
|
| 445 |
+
random_state=1,
|
| 446 |
+
verbose=0,
|
| 447 |
+
warm_start=False,
|
| 448 |
+
ccp_alpha=0.0,
|
| 449 |
+
max_samples=None,
|
| 450 |
+
**kwargs
|
| 451 |
+
):
|
| 452 |
+
|
| 453 |
+
self.n_estimators = n_estimators
|
| 454 |
+
self.criterion = criterion
|
| 455 |
+
self.max_depth = max_depth
|
| 456 |
+
self.min_samples_split = min_samples_split
|
| 457 |
+
self.min_samples_leaf = min_samples_leaf
|
| 458 |
+
self.min_weight_fraction_leaf = min_weight_fraction_leaf
|
| 459 |
+
self.max_features = max_features
|
| 460 |
+
self.max_leaf_nodes = max_leaf_nodes
|
| 461 |
+
self.min_impurity_decrease = min_impurity_decrease
|
| 462 |
+
self.bootstrap = bootstrap
|
| 463 |
+
self.oob_score = oob_score
|
| 464 |
+
self.n_jobs = n_jobs
|
| 465 |
+
self.random_state = random_state
|
| 466 |
+
self.verbose = verbose
|
| 467 |
+
self.warm_start = warm_start
|
| 468 |
+
self.ccp_alpha = ccp_alpha
|
| 469 |
+
self.max_samples = max_samples
|
| 470 |
+
|
| 471 |
+
super().__init__(data_splitter, featurizer, model=model, use_cache=use_cache, **kwargs)
|
| 472 |
+
|
| 473 |
+
def train(self, X, y):
|
| 474 |
+
self.model = RFRegressor(n_estimators=self.n_estimators,
|
| 475 |
+
criterion=self.criterion,
|
| 476 |
+
max_depth=self.max_depth,
|
| 477 |
+
min_samples_split=self.min_samples_split,
|
| 478 |
+
min_samples_leaf=self.min_samples_leaf,
|
| 479 |
+
min_weight_fraction_leaf=self.min_weight_fraction_leaf,
|
| 480 |
+
max_features=self.max_features,
|
| 481 |
+
max_leaf_nodes=self.max_leaf_nodes,
|
| 482 |
+
min_impurity_decrease=self.min_impurity_decrease,
|
| 483 |
+
bootstrap=self.bootstrap,
|
| 484 |
+
oob_score=self.oob_score,
|
| 485 |
+
n_jobs=self.n_jobs,
|
| 486 |
+
random_state=self.random_state,
|
| 487 |
+
verbose=self.verbose,
|
| 488 |
+
warm_start=self.warm_start,
|
| 489 |
+
ccp_alpha=self.ccp_alpha,
|
| 490 |
+
max_samples=self.max_samples
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
self.model.fit(X, y)
|
| 494 |
+
|
| 495 |
+
def custom_predictor(self, X):
|
| 496 |
+
return self.model.predict(X)
|
| 497 |
+
|
| 498 |
+
class RidgeRegressor(BaseRegressor):
|
| 499 |
+
"""
|
| 500 |
+
Ridge regression model.
|
| 501 |
+
|
| 502 |
+
Args:
|
| 503 |
+
data_splitter: Object containing train/test splits
|
| 504 |
+
featurizer: Object that converts sequences to features
|
| 505 |
+
model (str, optional): Name of model. Defaults to 'Ridge'
|
| 506 |
+
use_cache (bool, optional): Whether to use cached models. Defaults to False
|
| 507 |
+
reg_coef (float, optional): Ridge regularization coefficient. If None, use CV. Defaults to None
|
| 508 |
+
linear_model_cls (class, optional): Sklearn linear model class. Defaults to Ridge
|
| 509 |
+
reg_coef_list (list, optional): List of regularization strengths for CV. Defaults to [0.1, 1.0, 2.0]
|
| 510 |
+
**kwargs: Additional keyword arguments
|
| 511 |
+
"""
|
| 512 |
+
# [TODO] edit cv to include modifiable splits
|
| 513 |
+
def __init__(self, data_splitter, featurizer, model='Ridge', use_cache=False, reg_coef=None, linear_model_cls=Ridge, reg_coef_list=None, **kwargs):
|
| 514 |
+
"""
|
| 515 |
+
Args:
|
| 516 |
+
- reg_coef: Ridge regression coefficient. If none, then train with CV
|
| 517 |
+
- linear_model_cls: sklearn linear model class
|
| 518 |
+
- reg_coef_list: list of ridge regression regularization strength (default: [0.1, 1.0])
|
| 519 |
+
"""
|
| 520 |
+
self.reg_coef = reg_coef
|
| 521 |
+
self.linear_model_cls = linear_model_cls
|
| 522 |
+
self.reg_coef_list = reg_coef_list if reg_coef_list is not None else [0.1, 1.0, 2.0]
|
| 523 |
+
super().__init__(data_splitter, featurizer, model=model, use_cache=use_cache, **kwargs)
|
| 524 |
+
|
| 525 |
+
def train(self, X, y):
|
| 526 |
+
def spearman(y_pred, y_true):
|
| 527 |
+
y_pred = np.array(y_pred)
|
| 528 |
+
y_true = np.array(y_true)
|
| 529 |
+
|
| 530 |
+
y_pred = y_pred.reshape(-1)
|
| 531 |
+
y_true = y_true.reshape(-1)
|
| 532 |
+
|
| 533 |
+
if np.var(y_pred) < 1e-6 or np.var(y_true) < 1e-6:
|
| 534 |
+
return 0.0
|
| 535 |
+
return ss.spearmanr(y_pred, y_true).correlation
|
| 536 |
+
if self.reg_coef is None or self.reg_coef == 'CV':
|
| 537 |
+
best_reg_coef, best_score = None, -np.inf
|
| 538 |
+
for sample_reg_coef in self.reg_coef_list:
|
| 539 |
+
model = self.linear_model_cls(alpha=sample_reg_coef)
|
| 540 |
+
score = cross_val_score(model, X, y,
|
| 541 |
+
cv=5,
|
| 542 |
+
scoring=make_scorer(spearman)).mean()
|
| 543 |
+
if score > best_score:
|
| 544 |
+
best_reg_coef = sample_reg_coef
|
| 545 |
+
best_score = score
|
| 546 |
+
self.model = self.linear_model_cls(alpha=best_reg_coef)
|
| 547 |
+
self.model.fit(X, y)
|
| 548 |
+
|
| 549 |
+
def custom_predictor(self, X):
|
| 550 |
+
return self.model.predict(X)
|
model/predictors/gaussian_process_regressors.py
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from joblib import Parallel, delayed
|
| 2 |
+
from math import ceil
|
| 3 |
+
|
| 4 |
+
from matplotlib import pyplot as plt
|
| 5 |
+
import matplotlib.colors as mcolors
|
| 6 |
+
import numpy as np
|
| 7 |
+
from scipy.stats import iqr
|
| 8 |
+
from sklearn.gaussian_process.kernels import (
|
| 9 |
+
ConstantKernel as C,
|
| 10 |
+
DotProduct as DP,
|
| 11 |
+
RBF
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
from model.predictors.base_regressors import BaseRegressor
|
| 15 |
+
from model.utils.other_utils import performance_report
|
| 16 |
+
|
| 17 |
+
def parallel_predict(model, X, batch_num, n_batches, verbose):
|
| 18 |
+
"""
|
| 19 |
+
Makes predictions in parallel using batches.
|
| 20 |
+
|
| 21 |
+
Args:
|
| 22 |
+
model: The trained model to make predictions with
|
| 23 |
+
X (array-like): Input features to predict on
|
| 24 |
+
batch_num (int): Current batch number
|
| 25 |
+
n_batches (int): Total number of batches
|
| 26 |
+
verbose (bool): Whether to print progress messages
|
| 27 |
+
|
| 28 |
+
Returns:
|
| 29 |
+
tuple: (mean predictions, prediction variances)
|
| 30 |
+
"""
|
| 31 |
+
mean, var = model.predict(X, return_std=True)
|
| 32 |
+
if verbose:
|
| 33 |
+
print('Finished predicting batch number {}/{}'
|
| 34 |
+
.format(batch_num + 1, n_batches))
|
| 35 |
+
return mean, var
|
| 36 |
+
|
| 37 |
+
class GPRegressor(BaseRegressor):
|
| 38 |
+
"""Base Gaussian Process regressor class.
|
| 39 |
+
|
| 40 |
+
Attributes:
|
| 41 |
+
n_restarts_ (int): Number of restarts for optimizer
|
| 42 |
+
kernel_ (sklearn.gaussian_process.kernels): Kernel function
|
| 43 |
+
normalize_y_ (bool): Whether to normalize target values
|
| 44 |
+
backend_ (str): Backend framework to use ('sklearn', 'gpy', or 'gpytorch')
|
| 45 |
+
batch_size_ (int): Batch size for predictions
|
| 46 |
+
n_jobs_ (int): Number of parallel jobs
|
| 47 |
+
verbose_ (bool): Whether to print progress messages
|
| 48 |
+
model: The trained GP model
|
| 49 |
+
uncertainties_ (array): Prediction uncertainties
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
def __init__(self,
|
| 53 |
+
data_splitter,
|
| 54 |
+
featurizer,
|
| 55 |
+
model='GPR',
|
| 56 |
+
n_restarts=0,
|
| 57 |
+
kernel=None,
|
| 58 |
+
normalize_y=True,
|
| 59 |
+
backend='sklearn',
|
| 60 |
+
batch_size=1000,
|
| 61 |
+
n_jobs=1,
|
| 62 |
+
verbose=False,
|
| 63 |
+
**kwargs
|
| 64 |
+
):
|
| 65 |
+
"""
|
| 66 |
+
Args:
|
| 67 |
+
data_splitter: Object to split data into train/test sets
|
| 68 |
+
featurizer: Object to convert sequences to numerical features
|
| 69 |
+
model (str): Model identifier string
|
| 70 |
+
n_restarts (int): Number of restarts for optimizer
|
| 71 |
+
kernel: Kernel function for GP
|
| 72 |
+
normalize_y (bool): Whether to normalize target values
|
| 73 |
+
backend (str): Framework to use ('sklearn', 'gpy', or 'gpytorch')
|
| 74 |
+
batch_size (int): Batch size for predictions
|
| 75 |
+
n_jobs (int): Number of parallel jobs
|
| 76 |
+
verbose (bool): Whether to print progress messages
|
| 77 |
+
**kwargs: Additional keyword arguments
|
| 78 |
+
"""
|
| 79 |
+
self.n_restarts_ = n_restarts
|
| 80 |
+
self.kernel_ = kernel
|
| 81 |
+
self.normalize_y_ = normalize_y
|
| 82 |
+
self.backend_ = backend
|
| 83 |
+
self.batch_size_ = batch_size
|
| 84 |
+
self.n_jobs_ = n_jobs
|
| 85 |
+
self.verbose_ = verbose
|
| 86 |
+
super().__init__(data_splitter, featurizer, model, **kwargs)
|
| 87 |
+
|
| 88 |
+
def train(self, X, y):
|
| 89 |
+
"""
|
| 90 |
+
Train the GP model.
|
| 91 |
+
|
| 92 |
+
Args:
|
| 93 |
+
X (array-like): Training features
|
| 94 |
+
y (array-like): Training target values
|
| 95 |
+
|
| 96 |
+
Returns:
|
| 97 |
+
self: The trained model instance
|
| 98 |
+
"""
|
| 99 |
+
n_samples, n_features = X.shape
|
| 100 |
+
|
| 101 |
+
if self.verbose_:
|
| 102 |
+
print('Fitting GP model on {} data points with dimension {}...'
|
| 103 |
+
.format(*X.shape))
|
| 104 |
+
|
| 105 |
+
# scikit-learn backend.
|
| 106 |
+
if self.backend_ == 'sklearn':
|
| 107 |
+
from sklearn.gaussian_process import GaussianProcessRegressor
|
| 108 |
+
self.model = GaussianProcessRegressor(
|
| 109 |
+
kernel=self.kernel_,
|
| 110 |
+
normalize_y=self.normalize_y_,
|
| 111 |
+
alpha=1e-0,
|
| 112 |
+
n_restarts_optimizer=self.n_restarts_,
|
| 113 |
+
copy_X_train=False,
|
| 114 |
+
).fit(X, y)
|
| 115 |
+
|
| 116 |
+
# GPy backend.
|
| 117 |
+
elif self.backend_ == 'gpy':
|
| 118 |
+
import GPy
|
| 119 |
+
if self.kernel_ == 'rbf':
|
| 120 |
+
kernel = GPy.kern.RBF(
|
| 121 |
+
input_dim=n_features, variance=1., lengthscale=1.
|
| 122 |
+
)
|
| 123 |
+
else:
|
| 124 |
+
raise ValueError('Kernel value {} not supported'
|
| 125 |
+
.format(self.kernel_))
|
| 126 |
+
|
| 127 |
+
self.model = GPy.models.SparseGPRegression(
|
| 128 |
+
X, y.reshape(-1, 1), kernel=kernel,
|
| 129 |
+
num_inducing=min(self.n_inducing_, n_samples)
|
| 130 |
+
)
|
| 131 |
+
self.model.Z.unconstrain()
|
| 132 |
+
self.model.optimize(messages=self.verbose_)
|
| 133 |
+
|
| 134 |
+
# GPyTorch with CUDA backend.
|
| 135 |
+
elif self.backend_ == 'gpytorch':
|
| 136 |
+
import gpytorch
|
| 137 |
+
import torch
|
| 138 |
+
|
| 139 |
+
class GPyTorchRegressor(gpytorch.models.ExactGP):
|
| 140 |
+
def __init__(self, X, y, likelihood):
|
| 141 |
+
super(GPyTorchRegressor, self).__init__(X, y, likelihood)
|
| 142 |
+
self.mean_module = gpytorch.means.ConstantMean()
|
| 143 |
+
self.covar_module = gpytorch.kernels.ScaleKernel(
|
| 144 |
+
gpytorch.kernels.RBFKernel()
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
def forward(self, X):
|
| 148 |
+
mean_X = self.mean_module(X)
|
| 149 |
+
covar_X = self.covar_module(X)
|
| 150 |
+
return gpytorch.distributions.MultivariateNormal(mean_X, covar_X)
|
| 151 |
+
|
| 152 |
+
X = torch.Tensor(X).contiguous().cuda()
|
| 153 |
+
y = torch.Tensor(y).contiguous().cuda()
|
| 154 |
+
|
| 155 |
+
likelihood = gpytorch.likelihoods.GaussianLikelihood().cuda()
|
| 156 |
+
model = GPyTorchRegressor(X, y, likelihood).cuda()
|
| 157 |
+
|
| 158 |
+
model.train()
|
| 159 |
+
likelihood.train()
|
| 160 |
+
|
| 161 |
+
# Use the Adam optimizer.
|
| 162 |
+
#optimizer = torch.optim.LBFGS([ {'params': model.parameters()} ])
|
| 163 |
+
optimizer = torch.optim.Adam([
|
| 164 |
+
{'params': model.parameters()}, # Includes GaussianLikelihood parameters.
|
| 165 |
+
], lr=1.)
|
| 166 |
+
|
| 167 |
+
# Loss for GPs is the marginal log likelihood.
|
| 168 |
+
mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, model)
|
| 169 |
+
|
| 170 |
+
training_iterations = 100
|
| 171 |
+
for i in range(training_iterations):
|
| 172 |
+
optimizer.zero_grad()
|
| 173 |
+
output = model(X)
|
| 174 |
+
loss = -mll(output, y)
|
| 175 |
+
loss.backward()
|
| 176 |
+
if self.verbose_:
|
| 177 |
+
print('Iter {}/{} - Loss: {:.3f}'
|
| 178 |
+
.format(i + 1, training_iterations, loss.item()))
|
| 179 |
+
optimizer.step()
|
| 180 |
+
|
| 181 |
+
self.model = model
|
| 182 |
+
self.likelihood_ = likelihood
|
| 183 |
+
|
| 184 |
+
if self.verbose_:
|
| 185 |
+
print('Done fitting GP model.')
|
| 186 |
+
|
| 187 |
+
return self
|
| 188 |
+
|
| 189 |
+
def evaluate(self):
|
| 190 |
+
"""
|
| 191 |
+
Evaluates the model on a test set.
|
| 192 |
+
|
| 193 |
+
Returns:
|
| 194 |
+
tuple: (dict of evaluation metrics, matplotlib figure)
|
| 195 |
+
"""
|
| 196 |
+
|
| 197 |
+
# Reshape data and get correlation stats
|
| 198 |
+
y_pred = self.predict(self.X_test)
|
| 199 |
+
y, y_pred = np.array(self.y_test), np.array(y_pred)
|
| 200 |
+
y, y_pred = y.reshape(-1), y_pred.reshape(-1)
|
| 201 |
+
stats = performance_report(y, y_pred)
|
| 202 |
+
|
| 203 |
+
# Plotting
|
| 204 |
+
fig, ax = plt.subplots(figsize=(8, 5)) # Adjust size as needed
|
| 205 |
+
|
| 206 |
+
# Clip data points that have activity less than 0 or greater than 1.2x the max experimental y value
|
| 207 |
+
y_max = y.max()*1.1
|
| 208 |
+
y_pred_adjusted = np.clip(y_pred, 0, y_max)
|
| 209 |
+
|
| 210 |
+
# Scale uncertainties using IQR and color data points based on uncertainty
|
| 211 |
+
scaled_uncertainties = (self.uncertainties_ - np.percentile(self.uncertainties_, 25)) / iqr(self.uncertainties_)
|
| 212 |
+
cmap = plt.cm.viridis
|
| 213 |
+
colors = cmap(scaled_uncertainties)
|
| 214 |
+
colors[y_pred > y_max] = mcolors.to_rgba('crimson')
|
| 215 |
+
colors[y_pred < 0] = mcolors.to_rgba('crimson')
|
| 216 |
+
|
| 217 |
+
# Scatter plot for main graph
|
| 218 |
+
scatter = ax.scatter(y_pred_adjusted, y, c=colors, alpha = 0.4)
|
| 219 |
+
|
| 220 |
+
# Uncertainty colorbar
|
| 221 |
+
cbar = plt.colorbar(scatter, ax=ax)
|
| 222 |
+
cbar.set_label('Uncertainty')
|
| 223 |
+
|
| 224 |
+
# Draw x=y line
|
| 225 |
+
ax.plot([0, y_max], [0, y_max], 'k--', linewidth=2)
|
| 226 |
+
|
| 227 |
+
# Set labels and title for main graph
|
| 228 |
+
ax.text(0.9, 0.1, f'Pearson r={stats["Pearson r"]:.2f}', fontsize=12, ha='right', va='bottom', transform=ax.transAxes)
|
| 229 |
+
ax.set_xlabel('Predicted Score')
|
| 230 |
+
ax.set_ylabel('True Score')
|
| 231 |
+
ax.set_title(f'Model Performance')
|
| 232 |
+
ax.set_xlim(0, y_max)
|
| 233 |
+
|
| 234 |
+
# Display model parameters using legend
|
| 235 |
+
model_params = self.name.split('|') # Assuming '|' separates different parameters
|
| 236 |
+
param_text = '\n'.join(model_params)
|
| 237 |
+
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
|
| 238 |
+
ax.text(0.05, 0.95, param_text, transform=ax.transAxes, fontsize=9,
|
| 239 |
+
verticalalignment='top', bbox=props)
|
| 240 |
+
|
| 241 |
+
# Return the figure and axes object
|
| 242 |
+
return stats, fig
|
| 243 |
+
|
| 244 |
+
def custom_predictor(self, X):
|
| 245 |
+
"""
|
| 246 |
+
Makes predictions using the trained GP model.
|
| 247 |
+
|
| 248 |
+
Args:
|
| 249 |
+
X (array-like): Features to predict on
|
| 250 |
+
|
| 251 |
+
Returns:
|
| 252 |
+
array: Mean predictions
|
| 253 |
+
"""
|
| 254 |
+
if self.verbose_:
|
| 255 |
+
print('Finding GP model predictions on {} data points...'
|
| 256 |
+
.format(X.shape[0]))
|
| 257 |
+
|
| 258 |
+
if self.backend_ == 'sklearn':
|
| 259 |
+
n_batches = int(ceil(float(X.shape[0]) / self.batch_size_))
|
| 260 |
+
results = Parallel(n_jobs=self.n_jobs_)(#, max_nbytes=None)(
|
| 261 |
+
delayed(parallel_predict)(
|
| 262 |
+
self.model,
|
| 263 |
+
X[batch_num*self.batch_size_:(batch_num+1)*self.batch_size_],
|
| 264 |
+
batch_num, n_batches, self.verbose_
|
| 265 |
+
)
|
| 266 |
+
for batch_num in range(n_batches)
|
| 267 |
+
)
|
| 268 |
+
mean = np.concatenate([ result[0] for result in results ])
|
| 269 |
+
var = np.concatenate([ result[1] for result in results ])
|
| 270 |
+
|
| 271 |
+
elif self.backend_ == 'gpy':
|
| 272 |
+
mean, var = self.model.predict(X, full_cov=False)
|
| 273 |
+
|
| 274 |
+
elif self.backend_ == 'gpytorch':
|
| 275 |
+
import gpytorch
|
| 276 |
+
import torch
|
| 277 |
+
|
| 278 |
+
X = torch.Tensor(X).contiguous().cuda()
|
| 279 |
+
|
| 280 |
+
# Set into eval mode.
|
| 281 |
+
self.model.eval()
|
| 282 |
+
self.likelihood_.eval()
|
| 283 |
+
|
| 284 |
+
with torch.no_grad(), \
|
| 285 |
+
gpytorch.settings.fast_pred_var(), \
|
| 286 |
+
gpytorch.settings.max_root_decomposition_size(35):
|
| 287 |
+
preds = self.model(X)
|
| 288 |
+
|
| 289 |
+
mean = preds.mean.detach().cpu().numpy()
|
| 290 |
+
var = preds.variance.detach().cpu().numpy()
|
| 291 |
+
|
| 292 |
+
if self.verbose_:
|
| 293 |
+
print('Done predicting with GP model.')
|
| 294 |
+
|
| 295 |
+
self.uncertainties_ = var.flatten()
|
| 296 |
+
return mean.flatten()
|
| 297 |
+
|
| 298 |
+
class SparseGPRegressor(BaseRegressor):
|
| 299 |
+
"""Sparse Gaussian Process regressor using inducing points.
|
| 300 |
+
|
| 301 |
+
Attributes:
|
| 302 |
+
n_inducing_ (int): Number of inducing points
|
| 303 |
+
method_ (str): Method for selecting inducing points ('uniform' or 'geosketch')
|
| 304 |
+
n_restarts_ (int): Number of restarts for optimizer
|
| 305 |
+
kernel_ (sklearn.gaussian_process.kernels): Kernel function
|
| 306 |
+
backend_ (str): Backend framework to use
|
| 307 |
+
batch_size_ (int): Batch size for predictions
|
| 308 |
+
n_jobs_ (int): Number of parallel jobs
|
| 309 |
+
verbose_ (bool): Whether to print progress messages
|
| 310 |
+
gpr_: The trained GP model
|
| 311 |
+
"""
|
| 312 |
+
|
| 313 |
+
def __init__(
|
| 314 |
+
self,
|
| 315 |
+
data_splitter,
|
| 316 |
+
featurizer,
|
| 317 |
+
model='SparseGPRegressor',
|
| 318 |
+
n_inducing=1000,
|
| 319 |
+
method='geoskech',
|
| 320 |
+
n_restarts=0,
|
| 321 |
+
kernel=None,
|
| 322 |
+
backend='sklearn',
|
| 323 |
+
batch_size=1000,
|
| 324 |
+
n_jobs=1,
|
| 325 |
+
verbose=False,
|
| 326 |
+
**kwargs
|
| 327 |
+
):
|
| 328 |
+
"""
|
| 329 |
+
Args:
|
| 330 |
+
data_splitter: Object to split data into train/test sets
|
| 331 |
+
featurizer: Object to convert sequences to numerical features
|
| 332 |
+
model (str): Model identifier string
|
| 333 |
+
n_inducing (int): Number of inducing points
|
| 334 |
+
method (str): Method for selecting inducing points
|
| 335 |
+
n_restarts (int): Number of restarts for optimizer
|
| 336 |
+
kernel: Kernel function for GP
|
| 337 |
+
backend (str): Framework to use
|
| 338 |
+
batch_size (int): Batch size for predictions
|
| 339 |
+
n_jobs (int): Number of parallel jobs
|
| 340 |
+
verbose (bool): Whether to print progress messages
|
| 341 |
+
**kwargs: Additional keyword arguments
|
| 342 |
+
"""
|
| 343 |
+
self.n_inducing_ = n_inducing
|
| 344 |
+
self.method_ = method
|
| 345 |
+
self.n_restarts_ = n_restarts
|
| 346 |
+
self.kernel_ = kernel
|
| 347 |
+
self.backend_ = backend
|
| 348 |
+
self.batch_size_ = batch_size
|
| 349 |
+
self.n_jobs_ = n_jobs
|
| 350 |
+
self.verbose_ = verbose
|
| 351 |
+
super().__init__(data_splitter, featurizer,model, **kwargs)
|
| 352 |
+
|
| 353 |
+
def train(self, X, y):
|
| 354 |
+
"""
|
| 355 |
+
Train the sparse GP model.
|
| 356 |
+
|
| 357 |
+
Args:
|
| 358 |
+
X (array-like): Training features
|
| 359 |
+
y (array-like): Training target values
|
| 360 |
+
"""
|
| 361 |
+
X, y = self.X, self.y
|
| 362 |
+
if X.shape[0] > self.n_inducing_:
|
| 363 |
+
if self.method_ == 'uniform':
|
| 364 |
+
uni_idx = np.random.choice(X.shape[0], self.n_inducing_,
|
| 365 |
+
replace=False)
|
| 366 |
+
X_sketch = X[uni_idx]
|
| 367 |
+
y_sketch = y[uni_idx]
|
| 368 |
+
|
| 369 |
+
elif self.method_ == 'geosketch':
|
| 370 |
+
from fbpca import pca
|
| 371 |
+
from geosketch import gs
|
| 372 |
+
|
| 373 |
+
U, s, _ = pca(X, k=100)
|
| 374 |
+
X_dimred = U[:, :100] * s[:100]
|
| 375 |
+
gs_idx = gs(X_dimred, self.n_inducing_, replace=False)
|
| 376 |
+
X_sketch = X[gs_idx]
|
| 377 |
+
y_sketch = y[gs_idx]
|
| 378 |
+
|
| 379 |
+
else:
|
| 380 |
+
X_sketch, y_sketch = X, y
|
| 381 |
+
|
| 382 |
+
self.gpr_ = GPRegressor(
|
| 383 |
+
n_restarts=self.n_restarts_,
|
| 384 |
+
kernel=self.kernel_,
|
| 385 |
+
backend=self.backend_,
|
| 386 |
+
batch_size=self.batch_size_,
|
| 387 |
+
n_jobs=self.n_jobs_,
|
| 388 |
+
verbose=self.verbose_,
|
| 389 |
+
).fit(X_sketch, y_sketch)
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
def custom_predictor(self, X):
|
| 393 |
+
"""
|
| 394 |
+
Makes predictions using the trained sparse GP model.
|
| 395 |
+
|
| 396 |
+
Args:
|
| 397 |
+
X (array-like): Features to predict on
|
| 398 |
+
|
| 399 |
+
Returns:
|
| 400 |
+
array: Mean predictions
|
| 401 |
+
"""
|
| 402 |
+
y_pred = self.gpr_.predict(X)
|
| 403 |
+
self.uncertainties_ = self.gpr_.uncertainties_
|
| 404 |
+
return y_pred
|
| 405 |
+
|
| 406 |
+
class GPLinearRegressor(GPRegressor):
|
| 407 |
+
"""Gaussian Process regressor with linear kernel."""
|
| 408 |
+
|
| 409 |
+
def __init__(self,
|
| 410 |
+
data_splitter,
|
| 411 |
+
featurizer,
|
| 412 |
+
model='GPLinearRegressor',
|
| 413 |
+
n_restarts=0,
|
| 414 |
+
kernel = C(1., 'fixed') * DP(1., 'fixed'),
|
| 415 |
+
normalize_y=True,
|
| 416 |
+
backend='sklearn',
|
| 417 |
+
batch_size=1000,
|
| 418 |
+
n_jobs=1,
|
| 419 |
+
verbose=False,
|
| 420 |
+
**kwargs
|
| 421 |
+
):
|
| 422 |
+
"""
|
| 423 |
+
Args:
|
| 424 |
+
data_splitter: Object to split data into train/test sets
|
| 425 |
+
featurizer: Object to convert sequences to numerical features
|
| 426 |
+
model (str): Model identifier string
|
| 427 |
+
n_restarts (int): Number of restarts for optimizer
|
| 428 |
+
kernel: Linear kernel function
|
| 429 |
+
normalize_y (bool): Whether to normalize target values
|
| 430 |
+
backend (str): Framework to use
|
| 431 |
+
batch_size (int): Batch size for predictions
|
| 432 |
+
n_jobs (int): Number of parallel jobs
|
| 433 |
+
verbose (bool): Whether to print progress messages
|
| 434 |
+
**kwargs: Additional keyword arguments
|
| 435 |
+
"""
|
| 436 |
+
|
| 437 |
+
super().__init__(data_splitter, featurizer, model, n_restarts, kernel, normalize_y, backend, batch_size, n_jobs, verbose, **kwargs)
|
| 438 |
+
|
| 439 |
+
class GPQuadRegressor(GPRegressor):
|
| 440 |
+
"""Gaussian Process regressor with quadratic kernel."""
|
| 441 |
+
|
| 442 |
+
def __init__(self,
|
| 443 |
+
data_splitter,
|
| 444 |
+
featurizer,
|
| 445 |
+
model='GPQuadRegressor',
|
| 446 |
+
n_restarts=0,
|
| 447 |
+
kernel = C(1., 'fixed') * (DP(1, 'fixed') ** 2),
|
| 448 |
+
normalize_y=True,
|
| 449 |
+
backend='sklearn',
|
| 450 |
+
batch_size=1000,
|
| 451 |
+
n_jobs=1,
|
| 452 |
+
verbose=False,
|
| 453 |
+
**kwargs
|
| 454 |
+
):
|
| 455 |
+
"""
|
| 456 |
+
Args:
|
| 457 |
+
data_splitter: Object to split data into train/test sets
|
| 458 |
+
featurizer: Object to convert sequences to numerical features
|
| 459 |
+
model (str): Model identifier string
|
| 460 |
+
n_restarts (int): Number of restarts for optimizer
|
| 461 |
+
kernel: Quadratic kernel function
|
| 462 |
+
normalize_y (bool): Whether to normalize target values
|
| 463 |
+
backend (str): Framework to use
|
| 464 |
+
batch_size (int): Batch size for predictions
|
| 465 |
+
n_jobs (int): Number of parallel jobs
|
| 466 |
+
verbose (bool): Whether to print progress messages
|
| 467 |
+
**kwargs: Additional keyword arguments
|
| 468 |
+
"""
|
| 469 |
+
|
| 470 |
+
super().__init__(data_splitter, featurizer, model, n_restarts, kernel, normalize_y, backend, batch_size, n_jobs, verbose, **kwargs)
|
| 471 |
+
|
| 472 |
+
class GPRBFRegressor(GPRegressor):
|
| 473 |
+
"""Gaussian Process regressor with RBF kernel."""
|
| 474 |
+
|
| 475 |
+
def __init__(self,
|
| 476 |
+
data_splitter,
|
| 477 |
+
featurizer,
|
| 478 |
+
model='GPRBFRegressor',
|
| 479 |
+
n_restarts=0,
|
| 480 |
+
kernel = C(1., 'fixed') * RBF(1., 'fixed'),
|
| 481 |
+
normalize_y=True,
|
| 482 |
+
backend='sklearn',
|
| 483 |
+
batch_size=1000,
|
| 484 |
+
n_jobs=1,
|
| 485 |
+
verbose=False,
|
| 486 |
+
**kwargs
|
| 487 |
+
):
|
| 488 |
+
"""
|
| 489 |
+
Args:
|
| 490 |
+
data_splitter: Object to split data into train/test sets
|
| 491 |
+
featurizer: Object to convert sequences to numerical features
|
| 492 |
+
model (str): Model identifier string
|
| 493 |
+
n_restarts (int): Number of restarts for optimizer
|
| 494 |
+
kernel: RBF kernel function
|
| 495 |
+
normalize_y (bool): Whether to normalize target values
|
| 496 |
+
backend (str): Framework to use
|
| 497 |
+
batch_size (int): Batch size for predictions
|
| 498 |
+
n_jobs (int): Number of parallel jobs
|
| 499 |
+
verbose (bool): Whether to print progress messages
|
| 500 |
+
**kwargs: Additional keyword arguments
|
| 501 |
+
"""
|
| 502 |
+
|
| 503 |
+
super().__init__(data_splitter, featurizer, model, n_restarts, kernel, normalize_y, backend, batch_size, n_jobs, verbose, **kwargs)
|
model/predictors/neural_net_regressors.py
ADDED
|
@@ -0,0 +1,746 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, wandb
|
| 2 |
+
from torch import nn, optim
|
| 3 |
+
import numpy as np
|
| 4 |
+
import matplotlib.pyplot as plt
|
| 5 |
+
import yaml
|
| 6 |
+
import os
|
| 7 |
+
import pandas as pd
|
| 8 |
+
|
| 9 |
+
from model.utils.other_utils import performance_report, log_results
|
| 10 |
+
from model.utils.data_utils import TorchDataProcessor
|
| 11 |
+
|
| 12 |
+
# Get the directory where the script is located
|
| 13 |
+
script_dir = os.path.dirname(__file__)
|
| 14 |
+
|
| 15 |
+
# Master Functions to Train and Evaluate Models
|
| 16 |
+
def run_nn_model_experiments(splits,
|
| 17 |
+
features,
|
| 18 |
+
models, # Fcn, Cnn
|
| 19 |
+
experiment_name,
|
| 20 |
+
use_cache=False,
|
| 21 |
+
sweep_depth="standard", # standard, custom, test
|
| 22 |
+
search_method="grid", # grid, bayes, test
|
| 23 |
+
count=10,
|
| 24 |
+
show_plots=True
|
| 25 |
+
):
|
| 26 |
+
"""Run neural network model experiments with hyperparameter sweeps.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
splits (list): List of DataSplitter objects containing train/val/test splits
|
| 30 |
+
features (list): List of feature types to use (e.g. ['onehot', 'esm'])
|
| 31 |
+
models (list): List of model classes to run (e.g. [Fcn, Cnn])
|
| 32 |
+
experiment_name (str): Name for the W&B experiment
|
| 33 |
+
use_cache (bool, optional): Whether to cache results. Defaults to False.
|
| 34 |
+
sweep_depth (str, optional): Sweep type - 'standard', 'custom', 'test'. Defaults to 'standard'.
|
| 35 |
+
search_method (str, optional): Search method - 'grid', 'bayes', 'test'. Defaults to 'grid'.
|
| 36 |
+
count (int, optional): Number of runs per sweep. Defaults to 10.
|
| 37 |
+
show_plots (bool, optional): Whether to show matplotlib plots. Defaults to True.
|
| 38 |
+
|
| 39 |
+
Returns:
|
| 40 |
+
None: Results are logged to W&B
|
| 41 |
+
|
| 42 |
+
Example:
|
| 43 |
+
>>> splits = [DataSplitter(data, 'random')]
|
| 44 |
+
>>> features = ['onehot']
|
| 45 |
+
>>> models = [Fcn, Cnn]
|
| 46 |
+
>>> run_nn_model_experiments(splits,
|
| 47 |
+
... features,
|
| 48 |
+
... models,
|
| 49 |
+
... experiment_name='my_experiment',
|
| 50 |
+
... sweep_depth='selective',
|
| 51 |
+
... search_method='bayes',
|
| 52 |
+
... show_plots=True)
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
for split in splits:
|
| 56 |
+
for feature in features:
|
| 57 |
+
for model in models:
|
| 58 |
+
|
| 59 |
+
"""Define sweep configuration."""
|
| 60 |
+
config_map = {
|
| 61 |
+
("Fcn", "standard", "grid"): "fcn_standard_grid_sweep.yaml",
|
| 62 |
+
("Fcn", "standard", "bayes"): "fcn_standard_bayes_sweep.yaml",
|
| 63 |
+
("Fcn", "custom", "grid"): "fcn_custom_grid_sweep.yaml",
|
| 64 |
+
("Fcn", "test", "test"): "fcn_test_sweep.yaml",
|
| 65 |
+
("Cnn", "standard", "grid"): "cnn_standard_grid_sweep.yaml",
|
| 66 |
+
("Cnn", "standard", "bayes"): "cnn_standard_bayes_sweep.yaml",
|
| 67 |
+
("Cnn", "custom", "grid"): "cnn_custom_grid_sweep.yaml",
|
| 68 |
+
("Cnn", "test", "test"): "cnn_test_sweep.yaml",
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
yaml_file = config_map.get((model.__name__, sweep_depth, search_method))
|
| 72 |
+
if yaml_file is None:
|
| 73 |
+
print(
|
| 74 |
+
f"Invalid sweep configuration: model={model}, sweep_depth={sweep_depth}, search_method={search_method}."
|
| 75 |
+
)
|
| 76 |
+
return
|
| 77 |
+
|
| 78 |
+
working_script_dir = script_dir
|
| 79 |
+
|
| 80 |
+
# Assuming 'script_dir' is defined earlier in your code
|
| 81 |
+
yaml_file_path = os.path.join(working_script_dir, "sweep_configs", yaml_file)
|
| 82 |
+
|
| 83 |
+
with open(yaml_file_path, "r") as file:
|
| 84 |
+
sweep_config = yaml.safe_load(file)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
"""initialize the sweep."""
|
| 88 |
+
#sweep_id = wandb.sweep(sweep=sweep_config, project=experiment_name)
|
| 89 |
+
|
| 90 |
+
# Define a train function for hyperparameter sweeps with WANDB
|
| 91 |
+
|
| 92 |
+
#def train_function():
|
| 93 |
+
# with wandb.init() as run:
|
| 94 |
+
|
| 95 |
+
# Grab config
|
| 96 |
+
# config = run.config
|
| 97 |
+
|
| 98 |
+
# Specify model
|
| 99 |
+
# instance = model(split, feature, use_cache=use_cache, config=config, show_plots=show_plots)
|
| 100 |
+
|
| 101 |
+
# Train and evaluate model
|
| 102 |
+
# stat = instance.run_model()
|
| 103 |
+
|
| 104 |
+
#if search_method == "grid" or search_method == "test":
|
| 105 |
+
# wandb.agent(sweep_id, train_function)
|
| 106 |
+
#elif search_method == "bayes":
|
| 107 |
+
# wandb.agent(sweep_id, train_function, count=count)
|
| 108 |
+
#LL 2026 # 手动从 sweep_config 中提取一组默认参数(取每个参数的第一个值)
|
| 109 |
+
default_config = {}
|
| 110 |
+
if 'parameters' in sweep_config:
|
| 111 |
+
for param_name, param_values in sweep_config['parameters'].items():
|
| 112 |
+
if 'value' in param_values:
|
| 113 |
+
default_config[param_name] = param_values['value']
|
| 114 |
+
elif 'values' in param_values:
|
| 115 |
+
default_config[param_name] = param_values['values'][0]
|
| 116 |
+
else:
|
| 117 |
+
default_config[param_name] = None
|
| 118 |
+
|
| 119 |
+
# 直接训练模型,不通过 wandb
|
| 120 |
+
instance = model(split, feature, use_cache=use_cache, config=default_config, show_plots=show_plots)
|
| 121 |
+
stat = instance.run_model()
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
# Neural network classes
|
| 125 |
+
class BaseNN(nn.Module):
|
| 126 |
+
"""Base neural network class implementing common functionality.
|
| 127 |
+
|
| 128 |
+
This class provides the base implementation for neural network models including
|
| 129 |
+
data loading, training loops, evaluation, and model saving/loading.
|
| 130 |
+
|
| 131 |
+
Args:
|
| 132 |
+
data_splitter: DataSplitter object containing train/val/test splits
|
| 133 |
+
featurizer: Featurizer object for processing sequences
|
| 134 |
+
nn_arch: Neural network architecture specification
|
| 135 |
+
model (str): Model name identifier. Defaults to "Base"
|
| 136 |
+
use_cache (bool): Whether to use model caching. Defaults to False
|
| 137 |
+
show_plots (bool): Whether to show matplotlib plots. Defaults to True
|
| 138 |
+
**kwargs: Additional keyword arguments
|
| 139 |
+
|
| 140 |
+
Attributes:
|
| 141 |
+
model_name (str): Name of the model
|
| 142 |
+
featurizer: Featurizer object
|
| 143 |
+
use_cache (bool): Whether caching is enabled
|
| 144 |
+
kwargs (dict): Additional arguments
|
| 145 |
+
nn_arch (str): Architecture specification string
|
| 146 |
+
device (torch.device): Device to run model on (CPU/GPU)
|
| 147 |
+
show_plots (bool): Whether to show matplotlib plots. Defaults to True
|
| 148 |
+
Example:
|
| 149 |
+
>>> splitter = DataSplitter(data, 'random')
|
| 150 |
+
>>> featurizer = OneHotFeaturizer()
|
| 151 |
+
>>> model = BaseNN(splitter, featurizer, [64,32], model='test', show_plots=True)
|
| 152 |
+
"""
|
| 153 |
+
|
| 154 |
+
def __init__(self, data_splitter, featurizer, nn_arch, model="Base", use_cache=False, show_plots=True, **kwargs):
|
| 155 |
+
super(BaseNN, self).__init__()
|
| 156 |
+
|
| 157 |
+
# Set variables
|
| 158 |
+
self.model_name = model
|
| 159 |
+
self.featurizer = featurizer
|
| 160 |
+
self.use_cache = use_cache
|
| 161 |
+
self.kwargs = kwargs
|
| 162 |
+
self.nn_arch = "-".join([str(x) for x in nn_arch])
|
| 163 |
+
self.show_plots = show_plots
|
| 164 |
+
|
| 165 |
+
# Setup data
|
| 166 |
+
self.nn_data_processor = TorchDataProcessor(data_splitter, self.featurizer, self.kwargs["config"]["batch_size"])
|
| 167 |
+
#[TODO] remove this and only process data once required
|
| 168 |
+
|
| 169 |
+
self.split_method = self.nn_data_processor.split_name
|
| 170 |
+
|
| 171 |
+
# set model directory
|
| 172 |
+
self.file_attrs = data_splitter.file_attrs
|
| 173 |
+
self.file_attrs['model_dir'] = os.path.join(data_splitter.file_attrs["dataset_dir"], 'model_cache', data_splitter.file_attrs["dataset_name"])
|
| 174 |
+
|
| 175 |
+
"""Set variables."""
|
| 176 |
+
if torch.backends.mps.is_available():
|
| 177 |
+
self.device = torch.device("mps")
|
| 178 |
+
print("MPS available. Using Apple Silicon GPU for Neural Network.")
|
| 179 |
+
elif torch.cuda.is_available():
|
| 180 |
+
self.device = torch.device("cuda:0")
|
| 181 |
+
print("CUDA available. Using Nvidia GPU for Neural Network.")
|
| 182 |
+
else:
|
| 183 |
+
self.device = torch.device("cpu")
|
| 184 |
+
print("Neither MPS nor CUDA is available. Using CPU for Neural Network.")
|
| 185 |
+
|
| 186 |
+
def setup_model(self):
|
| 187 |
+
"""Set up the model by initializing hyperparameters and loading cached model if available."""
|
| 188 |
+
|
| 189 |
+
# Retrieve hyperparameters from current run config
|
| 190 |
+
self.set_hyperparams()
|
| 191 |
+
|
| 192 |
+
# Define model
|
| 193 |
+
self.file_attrs['model_name'] = (
|
| 194 |
+
self.split_method + " __ " +
|
| 195 |
+
self.featurizer.name + " __ " +
|
| 196 |
+
self.model_name + " __ " +
|
| 197 |
+
self.nn_arch + " __ " +
|
| 198 |
+
str(self.kwargs["config"]["learning_rate"]) + " __ " +
|
| 199 |
+
str(self.kwargs["config"]["batch_size"]) + " __ " +
|
| 200 |
+
self.kwargs["config"]["optimizer"]
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
self.model_path = os.path.join(self.file_attrs['model_dir'], 'objects', f'{self.file_attrs["model_name"]}.pth')
|
| 204 |
+
|
| 205 |
+
# Load model if available
|
| 206 |
+
if self.model_path is not None and os.path.exists(self.model_path) and self.use_cache:
|
| 207 |
+
self.load_model(model_path=None)
|
| 208 |
+
self.to(self.device)
|
| 209 |
+
else:
|
| 210 |
+
self.to(self.device)
|
| 211 |
+
|
| 212 |
+
def run_model(self, eval=True):
|
| 213 |
+
"""Run the full model training and evaluation pipeline.
|
| 214 |
+
|
| 215 |
+
This method handles:
|
| 216 |
+
1. Loading cached model if available
|
| 217 |
+
2. Training the model if needed
|
| 218 |
+
3. Evaluating on test set
|
| 219 |
+
4. Saving model if caching enabled
|
| 220 |
+
|
| 221 |
+
Returns:
|
| 222 |
+
dict: Dictionary of model performance statistics
|
| 223 |
+
"""
|
| 224 |
+
|
| 225 |
+
if self.model_path is not None and os.path.exists(self.model_path):
|
| 226 |
+
model = self
|
| 227 |
+
train_loss = self.train_loop_eval_mode(model)
|
| 228 |
+
val_loss = self.val_loop(model)
|
| 229 |
+
|
| 230 |
+
else:
|
| 231 |
+
model = self
|
| 232 |
+
|
| 233 |
+
# Train model
|
| 234 |
+
|
| 235 |
+
for epoch in range(self.epochs):
|
| 236 |
+
train_loss = self.train_loop(model)
|
| 237 |
+
val_loss = self.val_loop(model)
|
| 238 |
+
|
| 239 |
+
# Log data
|
| 240 |
+
if wandb.run is not None:
|
| 241 |
+
wandb.log({"Train Loss": train_loss, "Val Loss": val_loss})
|
| 242 |
+
|
| 243 |
+
# Check for early stopping
|
| 244 |
+
if self.early_stopping_check(val_loss, epoch) == True:
|
| 245 |
+
break
|
| 246 |
+
else:
|
| 247 |
+
continue
|
| 248 |
+
|
| 249 |
+
# Save model
|
| 250 |
+
if self.use_cache:
|
| 251 |
+
self.save_model(model, model_path=None)
|
| 252 |
+
|
| 253 |
+
# Test model
|
| 254 |
+
if eval == True:
|
| 255 |
+
return self.evaluate(model)
|
| 256 |
+
else:
|
| 257 |
+
return None
|
| 258 |
+
|
| 259 |
+
def load_model(self, model_path=None):
|
| 260 |
+
"""Load a pre-trained model from disk.
|
| 261 |
+
|
| 262 |
+
Args:
|
| 263 |
+
model_path (str, optional): Path to model file. If None, uses default path.
|
| 264 |
+
"""
|
| 265 |
+
|
| 266 |
+
# set location to load model
|
| 267 |
+
model_path = self.model_path if model_path is None else model_path
|
| 268 |
+
print(f"Loading model from {model_path}")
|
| 269 |
+
# Load the trained model parameters
|
| 270 |
+
self.load_state_dict(torch.load(model_path, map_location=self.device, weights_only=True))
|
| 271 |
+
|
| 272 |
+
def save_model(self, model, model_path=None):
|
| 273 |
+
"""Save model to disk.
|
| 274 |
+
|
| 275 |
+
Args:
|
| 276 |
+
model: Model to save
|
| 277 |
+
model_path (str, optional): Path to save model to. If None, uses default path.
|
| 278 |
+
"""
|
| 279 |
+
|
| 280 |
+
# set location to save model
|
| 281 |
+
model_path = self.model_path if model_path is None else model_path
|
| 282 |
+
|
| 283 |
+
dir_path = os.path.join(self.file_attrs['model_dir'], 'objects')
|
| 284 |
+
# Check if the directory exists, create it if it doesn't
|
| 285 |
+
if not os.path.exists(dir_path):
|
| 286 |
+
os.makedirs(dir_path)
|
| 287 |
+
|
| 288 |
+
# Save the model
|
| 289 |
+
print(f"Saving model to {self.model_path}")
|
| 290 |
+
torch.save(model.state_dict(), self.model_path)
|
| 291 |
+
|
| 292 |
+
def forward(self, x):
|
| 293 |
+
"""Forward pass through the network.
|
| 294 |
+
|
| 295 |
+
Args:
|
| 296 |
+
x: Input tensor
|
| 297 |
+
|
| 298 |
+
Returns:
|
| 299 |
+
Output tensor
|
| 300 |
+
"""
|
| 301 |
+
return x
|
| 302 |
+
|
| 303 |
+
def train_loop(self, model):
|
| 304 |
+
"""Training loop for one epoch.
|
| 305 |
+
|
| 306 |
+
Args:
|
| 307 |
+
model: Model to train
|
| 308 |
+
|
| 309 |
+
Returns:
|
| 310 |
+
float: Average training loss for the epoch
|
| 311 |
+
"""
|
| 312 |
+
model.train()
|
| 313 |
+
total_train_loss = 0
|
| 314 |
+
total_samples = 0
|
| 315 |
+
|
| 316 |
+
# [TODO] new function to set up train loader if not already done
|
| 317 |
+
if not hasattr(self, 'train_loader'):
|
| 318 |
+
self.train_loader = self.nn_data_processor.setup_train_loader()
|
| 319 |
+
|
| 320 |
+
for batch in self.train_loader:
|
| 321 |
+
inputs, targets, __ = batch
|
| 322 |
+
inputs, targets = inputs.to(self.device), targets.to(self.device)
|
| 323 |
+
targets = targets.unsqueeze(1)
|
| 324 |
+
self.optimizer.zero_grad()
|
| 325 |
+
outputs = model(inputs)
|
| 326 |
+
loss = self.criterion(outputs, targets)
|
| 327 |
+
total_train_loss += loss.item()
|
| 328 |
+
loss.backward()
|
| 329 |
+
self.optimizer.step()
|
| 330 |
+
total_samples += inputs.size(0)
|
| 331 |
+
|
| 332 |
+
train_loss = total_train_loss / total_samples
|
| 333 |
+
|
| 334 |
+
return train_loss
|
| 335 |
+
|
| 336 |
+
def train_loop_eval_mode(self, model):
|
| 337 |
+
"""Training loop in evaluation mode (no gradients).
|
| 338 |
+
|
| 339 |
+
Args:
|
| 340 |
+
model: Model to evaluate
|
| 341 |
+
|
| 342 |
+
Returns:
|
| 343 |
+
float: Average training loss
|
| 344 |
+
"""
|
| 345 |
+
model.eval()
|
| 346 |
+
with torch.no_grad():
|
| 347 |
+
total_train_loss = 0
|
| 348 |
+
total_samples = 0
|
| 349 |
+
|
| 350 |
+
# [TODO] new function to set up train loader if not already done
|
| 351 |
+
if not hasattr(self, 'train_loader'):
|
| 352 |
+
self.train_loader = self.nn_data_processor.setup_train_loader()
|
| 353 |
+
|
| 354 |
+
for batch in self.train_loader:
|
| 355 |
+
inputs, targets, __ = batch
|
| 356 |
+
inputs, targets = inputs.to(self.device), targets.to(self.device)
|
| 357 |
+
targets = targets.unsqueeze(1)
|
| 358 |
+
outputs = model(inputs)
|
| 359 |
+
total_train_loss += self.criterion(outputs, targets).item()
|
| 360 |
+
total_samples += inputs.size(0)
|
| 361 |
+
|
| 362 |
+
train_loss = total_train_loss / total_samples
|
| 363 |
+
|
| 364 |
+
return train_loss
|
| 365 |
+
|
| 366 |
+
def val_loop(self, model):
|
| 367 |
+
"""Validation loop.
|
| 368 |
+
|
| 369 |
+
Args:
|
| 370 |
+
model: Model to evaluate
|
| 371 |
+
|
| 372 |
+
Returns:
|
| 373 |
+
float: Average validation loss
|
| 374 |
+
"""
|
| 375 |
+
model.eval()
|
| 376 |
+
with torch.no_grad():
|
| 377 |
+
total_val_loss = 0
|
| 378 |
+
total_samples = 0
|
| 379 |
+
# [TODO] new function to set up val loader if not already done
|
| 380 |
+
if not hasattr(self, 'val_loader'):
|
| 381 |
+
self.val_loader = self.nn_data_processor.setup_val_loader()
|
| 382 |
+
|
| 383 |
+
for batch in self.val_loader:
|
| 384 |
+
inputs, targets, __ = batch
|
| 385 |
+
inputs, targets = inputs.to(self.device), targets.to(self.device)
|
| 386 |
+
targets = targets.unsqueeze(1)
|
| 387 |
+
outputs = model(inputs)
|
| 388 |
+
total_val_loss += self.criterion(outputs, targets).item()
|
| 389 |
+
total_samples += inputs.size(0)
|
| 390 |
+
|
| 391 |
+
val_loss = total_val_loss / total_samples
|
| 392 |
+
|
| 393 |
+
return val_loss
|
| 394 |
+
|
| 395 |
+
def evaluate(self, model):
|
| 396 |
+
"""Evaluate model on test set.
|
| 397 |
+
|
| 398 |
+
Args:
|
| 399 |
+
model: Model to evaluate
|
| 400 |
+
|
| 401 |
+
Returns:
|
| 402 |
+
dict: Dictionary of performance statistics
|
| 403 |
+
"""
|
| 404 |
+
|
| 405 |
+
# Evaluate model, get metrics for validation and test set
|
| 406 |
+
model.eval()
|
| 407 |
+
|
| 408 |
+
stats_dict = {
|
| 409 |
+
"val": {},
|
| 410 |
+
"test": {}
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
loader_names = ["val", "test"]
|
| 414 |
+
|
| 415 |
+
with torch.no_grad():
|
| 416 |
+
|
| 417 |
+
# [TODO] new function to set up val and test loaders if not already done
|
| 418 |
+
if not hasattr(self, 'val_loader'):
|
| 419 |
+
self.val_loader = self.nn_data_processor.setup_val_loader()
|
| 420 |
+
if not hasattr(self, 'test_loader'):
|
| 421 |
+
self.test_loader = self.nn_data_processor.setup_test_loader()
|
| 422 |
+
|
| 423 |
+
for index, loader in enumerate([self.val_loader, self.test_loader]):
|
| 424 |
+
loader_name = loader_names[index]
|
| 425 |
+
total_loss = 0
|
| 426 |
+
total_samples = 0
|
| 427 |
+
y = []
|
| 428 |
+
y_pred = []
|
| 429 |
+
original_sequences_list = []
|
| 430 |
+
|
| 431 |
+
for batch in loader:
|
| 432 |
+
inputs, targets, original_sequences = batch
|
| 433 |
+
inputs, targets = inputs.to(self.device), targets.to(self.device)
|
| 434 |
+
targets = targets.unsqueeze(1)
|
| 435 |
+
outputs = model(inputs)
|
| 436 |
+
total_loss += self.criterion(outputs, targets).item()
|
| 437 |
+
|
| 438 |
+
# Move to CPU and convert to numpy
|
| 439 |
+
y.extend(targets.cpu().detach().numpy())
|
| 440 |
+
y_pred.extend(outputs.cpu().detach().numpy())
|
| 441 |
+
original_sequences_list.extend(original_sequences)
|
| 442 |
+
|
| 443 |
+
total_samples += inputs.size(0)
|
| 444 |
+
|
| 445 |
+
|
| 446 |
+
# Reshape data and get correlation stats
|
| 447 |
+
y = np.concatenate(y).ravel()
|
| 448 |
+
y_pred = np.concatenate(y_pred).ravel()
|
| 449 |
+
|
| 450 |
+
# Get stats
|
| 451 |
+
stats_dict[loader_name] = performance_report(y, y_pred)
|
| 452 |
+
|
| 453 |
+
# graph results for test set
|
| 454 |
+
# Set the default parameters
|
| 455 |
+
plt.rcParams['font.size'] = 7
|
| 456 |
+
plt.rcParams['lines.linewidth'] = 0.5
|
| 457 |
+
|
| 458 |
+
fig, ax = plt.subplots(figsize=(4, 3))
|
| 459 |
+
|
| 460 |
+
# Mark data points that have activity less than 0 or greater than 1.2x the max experimental y value
|
| 461 |
+
y_max = max(y.max(), y_pred.max()) * 1.2
|
| 462 |
+
colors = np.where(y_pred > y_max, 'crimson', np.where(y_pred < 0, 'crimson', 'dodgerblue'))
|
| 463 |
+
y_pred_adjusted = np.clip(y_pred, 0, y_max)
|
| 464 |
+
|
| 465 |
+
# Scatter plot for main graph
|
| 466 |
+
ax.scatter(y_pred_adjusted, y, c=colors, alpha=0.4, edgecolors='w', linewidth=0.5)
|
| 467 |
+
|
| 468 |
+
# Draw x=y line
|
| 469 |
+
ax.plot([0, y_max], [0, y_max], 'k--', linewidth=0.5)
|
| 470 |
+
|
| 471 |
+
# Set labels and title for main graph
|
| 472 |
+
ax.text(0.9, 0.1, f'Pearson r={stats_dict["test"]["Pearson r"]:.2f}', fontsize=7, ha='right', va='bottom', transform=ax.transAxes)
|
| 473 |
+
ax.text(0.9, 0.2, f'Spearman r={stats_dict["test"]["Spearman r"]:.2f}', fontsize=7, ha='right', va='bottom', transform=ax.transAxes)
|
| 474 |
+
ax.set_xlabel('Predicted Score', fontsize=7)
|
| 475 |
+
ax.set_ylabel('True Score', fontsize=7)
|
| 476 |
+
ax.set_title('Model Performance', fontsize=7)
|
| 477 |
+
ax.set_xlim(0, y_max)
|
| 478 |
+
|
| 479 |
+
# Display model parameters using legend
|
| 480 |
+
model_params = self.file_attrs['model_name'].split('__') # Assuming '|' separates different parameters
|
| 481 |
+
param_text = '\n'.join(model_params)
|
| 482 |
+
props = dict(boxstyle='square', facecolor='wheat', alpha=0.2)
|
| 483 |
+
ax.text(0.02, 0.98, param_text, transform=ax.transAxes, fontsize=7, verticalalignment='top', bbox=props)
|
| 484 |
+
|
| 485 |
+
# Adjust tick parameters
|
| 486 |
+
ax.tick_params(axis='both', which='major', labelsize=7)
|
| 487 |
+
|
| 488 |
+
self.fig = fig
|
| 489 |
+
|
| 490 |
+
if self.show_plots:
|
| 491 |
+
plt.show()
|
| 492 |
+
plt.close(fig)
|
| 493 |
+
|
| 494 |
+
# Log data
|
| 495 |
+
log_results(stats_dict, self)
|
| 496 |
+
|
| 497 |
+
# Save predictions for test set as a table
|
| 498 |
+
if self.use_cache:
|
| 499 |
+
dir_path = os.path.join(self.file_attrs['model_dir'], 'results')
|
| 500 |
+
# Check if the directory exists, create it if it doesn't
|
| 501 |
+
if not os.path.exists(dir_path):
|
| 502 |
+
os.makedirs(dir_path)
|
| 503 |
+
pred_results = pd.DataFrame({"original_sequences": original_sequences_list, "y": list(y), "y_pred": list(y_pred)})
|
| 504 |
+
pred_results.to_csv(f"{dir_path}/{self.file_attrs['model_name']}.csv", index=False)
|
| 505 |
+
|
| 506 |
+
return stats_dict['test']
|
| 507 |
+
|
| 508 |
+
def early_stopping_check(self, val_loss, epoch):
|
| 509 |
+
"""Check if early stopping criteria are met.
|
| 510 |
+
|
| 511 |
+
Args:
|
| 512 |
+
val_loss (float): Current validation loss
|
| 513 |
+
epoch (int): Current epoch number
|
| 514 |
+
|
| 515 |
+
Returns:
|
| 516 |
+
bool: True if training should stop, False otherwise
|
| 517 |
+
"""
|
| 518 |
+
# modify epoch count
|
| 519 |
+
val_loss_delta = self.val_loss_min - val_loss
|
| 520 |
+
if val_loss_delta > self.val_loss_delta_min:
|
| 521 |
+
self.val_loss_min = val_loss
|
| 522 |
+
self.epochs_no_improve = 0
|
| 523 |
+
else:
|
| 524 |
+
self.epochs_no_improve += 1
|
| 525 |
+
|
| 526 |
+
# check epoch count
|
| 527 |
+
if self.epochs_no_improve == self.patience:
|
| 528 |
+
print(f"Early stopping after {epoch} epochs with {self.val_loss_min}.")
|
| 529 |
+
return True
|
| 530 |
+
else:
|
| 531 |
+
return False
|
| 532 |
+
|
| 533 |
+
def set_hyperparams(self):
|
| 534 |
+
"""Set model hyperparameters from config."""
|
| 535 |
+
|
| 536 |
+
self.criterion = nn.MSELoss()
|
| 537 |
+
self.lr = self.kwargs["config"]["learning_rate"]
|
| 538 |
+
if self.kwargs["config"]["optimizer"] == "adam":
|
| 539 |
+
self.optimizer = optim.Adam(self.parameters(), lr=self.lr)
|
| 540 |
+
elif self.kwargs["config"]["optimizer"] == "sgd":
|
| 541 |
+
self.optimizer = optim.SGD(self.parameters(), lr=self.lr)
|
| 542 |
+
self.epochs = self.kwargs["config"]["epochs"]
|
| 543 |
+
|
| 544 |
+
# early stopping
|
| 545 |
+
self.patience = 15
|
| 546 |
+
self.val_loss_min = float("inf")
|
| 547 |
+
self.val_loss_delta_min = 0.00001
|
| 548 |
+
self.epochs_no_improve = 0 # initialize epochs_no_improve for early stopping
|
| 549 |
+
|
| 550 |
+
def custom_predictor(self, X):
|
| 551 |
+
"""Make predictions on input data.
|
| 552 |
+
|
| 553 |
+
Args:
|
| 554 |
+
X: Input features
|
| 555 |
+
|
| 556 |
+
Returns:
|
| 557 |
+
numpy.ndarray: Model predictions
|
| 558 |
+
"""
|
| 559 |
+
|
| 560 |
+
model = self
|
| 561 |
+
inputs = torch.from_numpy(X.astype(np.float32)).to(self.device)
|
| 562 |
+
|
| 563 |
+
model.eval()
|
| 564 |
+
with torch.no_grad():
|
| 565 |
+
outputs = model(inputs)
|
| 566 |
+
|
| 567 |
+
outputs_np = outputs.cpu().numpy()
|
| 568 |
+
return outputs_np
|
| 569 |
+
|
| 570 |
+
def predict(self, X, batch_size=10000):
|
| 571 |
+
"""Make predictions on sequences in batches.
|
| 572 |
+
|
| 573 |
+
Args:
|
| 574 |
+
X (list): List of sequences to predict
|
| 575 |
+
|
| 576 |
+
Returns:
|
| 577 |
+
numpy.ndarray: Array of predictions
|
| 578 |
+
"""
|
| 579 |
+
batch_size = batch_size
|
| 580 |
+
predictions = []
|
| 581 |
+
|
| 582 |
+
# Process in batches
|
| 583 |
+
for i in range(0, len(X), batch_size):
|
| 584 |
+
batch = X[i:i + batch_size]
|
| 585 |
+
X_featurized = self.featurizer.featurize(batch)
|
| 586 |
+
X_featurized = X_featurized.reshape(X_featurized.shape[0], -1)
|
| 587 |
+
batch_predictions = self.custom_predictor(X_featurized)
|
| 588 |
+
predictions.append(batch_predictions)
|
| 589 |
+
|
| 590 |
+
return np.concatenate(predictions).ravel()
|
| 591 |
+
|
| 592 |
+
class Fcn(BaseNN):
|
| 593 |
+
"""Fully connected neural network model.
|
| 594 |
+
|
| 595 |
+
Args:
|
| 596 |
+
data_splitter: DataSplitter object containing train/val/test splits
|
| 597 |
+
feature: Featurizer object for processing sequences
|
| 598 |
+
model (str): Model name identifier. Defaults to "fcn"
|
| 599 |
+
use_cache (bool): Whether to use model caching. Defaults to False
|
| 600 |
+
show_plots (bool): Whether to show matplotlib plots. Defaults to True
|
| 601 |
+
**kwargs: Additional keyword arguments including network architecture
|
| 602 |
+
|
| 603 |
+
Example:
|
| 604 |
+
>>> splitter = DataSplitter(data, 'random')
|
| 605 |
+
>>> featurizer = OneHotFeaturizer()
|
| 606 |
+
>>> model = Fcn(splitter, featurizer, config=config, use_cache=True, show_plots=True)
|
| 607 |
+
"""
|
| 608 |
+
|
| 609 |
+
def __init__(self, data_splitter, feature, model="fcn", use_cache=False, show_plots=True, **kwargs):
|
| 610 |
+
|
| 611 |
+
# Specify network architecture
|
| 612 |
+
nn_arch = [kwargs["config"]["layer_size"]] * kwargs["config"]["num_layers"]
|
| 613 |
+
|
| 614 |
+
super().__init__(data_splitter, feature, nn_arch, model, use_cache=use_cache, show_plots=show_plots, **kwargs)
|
| 615 |
+
|
| 616 |
+
# [TODO] new function to extract input features if not already done
|
| 617 |
+
|
| 618 |
+
X_train_feat_example = self.nn_data_processor.featurize([self.nn_data_processor.X_train[0]])[0]
|
| 619 |
+
input_features = X_train_feat_example.flatten().shape[0]
|
| 620 |
+
self.flatten = nn.Flatten()
|
| 621 |
+
self.layers = nn.ModuleList()
|
| 622 |
+
|
| 623 |
+
# First layer
|
| 624 |
+
self.layers.append(nn.Linear(input_features, nn_arch[0]))
|
| 625 |
+
self.layers.append(nn.LeakyReLU(negative_slope=0.2))
|
| 626 |
+
self.layers.append(nn.Dropout(p=0.2))
|
| 627 |
+
|
| 628 |
+
for i in range(0, len(nn_arch)):
|
| 629 |
+
if i < len(nn_arch) - 1:
|
| 630 |
+
self.layers.append(nn.Linear(nn_arch[i], nn_arch[i + 1]))
|
| 631 |
+
self.layers.append(nn.LeakyReLU(negative_slope=0.2))
|
| 632 |
+
self.layers.append(nn.Dropout(p=0.2))
|
| 633 |
+
if i == len(nn_arch) - 1:
|
| 634 |
+
self.layers.append(nn.Linear(nn_arch[i], 1))
|
| 635 |
+
|
| 636 |
+
# set model hyperparameters
|
| 637 |
+
self.setup_model()
|
| 638 |
+
|
| 639 |
+
def forward(self, x):
|
| 640 |
+
"""Forward pass through the network.
|
| 641 |
+
|
| 642 |
+
Args:
|
| 643 |
+
x: Input tensor
|
| 644 |
+
|
| 645 |
+
Returns:
|
| 646 |
+
Output tensor
|
| 647 |
+
"""
|
| 648 |
+
x = self.flatten(x)
|
| 649 |
+
for layer in self.layers:
|
| 650 |
+
x = layer(x)
|
| 651 |
+
return x
|
| 652 |
+
|
| 653 |
+
class Cnn(BaseNN):
|
| 654 |
+
"""Convolutional neural network model.
|
| 655 |
+
|
| 656 |
+
Args:
|
| 657 |
+
data_splitter: DataSplitter object containing train/val/test splits
|
| 658 |
+
feature: Featurizer object for processing sequences
|
| 659 |
+
model (str): Model name identifier. Defaults to "cnn"
|
| 660 |
+
use_cache (bool): Whether to use model caching. Defaults to False
|
| 661 |
+
show_plots (bool): Whether to show matplotlib plots. Defaults to True
|
| 662 |
+
**kwargs: Additional keyword arguments including network architecture
|
| 663 |
+
|
| 664 |
+
Example:
|
| 665 |
+
>>> splitter = DataSplitter(data, 'random')
|
| 666 |
+
>>> featurizer = OneHotFeaturizer()
|
| 667 |
+
>>> model = Cnn(splitter, featurizer, use_cache=True, show_plots=True)
|
| 668 |
+
"""
|
| 669 |
+
|
| 670 |
+
def __init__(self, data_splitter, feature, model="cnn", use_cache=False, show_plots=True, **kwargs):
|
| 671 |
+
|
| 672 |
+
# Specify network architecture
|
| 673 |
+
nn_arch = [kwargs["config"]["kernel_size"]] + [
|
| 674 |
+
int(x) for x in kwargs["config"]["layersize_filtersize"].split("-")
|
| 675 |
+
]
|
| 676 |
+
|
| 677 |
+
super().__init__(data_splitter, feature, nn_arch, model, use_cache=use_cache, show_plots=show_plots, **kwargs)
|
| 678 |
+
|
| 679 |
+
# [TODO] new function to extract input features if not already done
|
| 680 |
+
X_train_feat_example = self.nn_data_processor.featurize([self.nn_data_processor.X_train[0]])[0]
|
| 681 |
+
protein_len = X_train_feat_example.shape[0]
|
| 682 |
+
encoding_len = X_train_feat_example.shape[1]
|
| 683 |
+
kernel_size_dim1, layers, out_channels = nn_arch
|
| 684 |
+
|
| 685 |
+
in_channels = 1
|
| 686 |
+
|
| 687 |
+
self.conv_layers = nn.ModuleList()
|
| 688 |
+
|
| 689 |
+
for i in range(layers):
|
| 690 |
+
conv2d_layer = nn.Conv2d(
|
| 691 |
+
in_channels=in_channels,
|
| 692 |
+
out_channels=out_channels,
|
| 693 |
+
kernel_size=(kernel_size_dim1, encoding_len if i == 0 else 1),
|
| 694 |
+
stride=(1, 1),
|
| 695 |
+
)
|
| 696 |
+
self.conv_layers.append(conv2d_layer)
|
| 697 |
+
self.conv_layers.append(nn.LeakyReLU(negative_slope=0.2))
|
| 698 |
+
in_channels = out_channels
|
| 699 |
+
|
| 700 |
+
# Dynamically calculate the input size for the fully connected layer
|
| 701 |
+
self._init_fc_layers(protein_len, encoding_len, out_channels)
|
| 702 |
+
|
| 703 |
+
# set model hyperparameters
|
| 704 |
+
self.setup_model()
|
| 705 |
+
|
| 706 |
+
def _init_fc_layers(self, protein_len, encoding_len, out_channels):
|
| 707 |
+
"""Initialize fully connected layers.
|
| 708 |
+
|
| 709 |
+
Args:
|
| 710 |
+
protein_len (int): Length of protein sequence
|
| 711 |
+
encoding_len (int): Length of sequence encoding
|
| 712 |
+
out_channels (int): Number of output channels
|
| 713 |
+
"""
|
| 714 |
+
# Dummy input for calculating size
|
| 715 |
+
dummy_input = torch.randn(1, 1, protein_len, encoding_len)
|
| 716 |
+
for layer in self.conv_layers:
|
| 717 |
+
dummy_input = layer(dummy_input)
|
| 718 |
+
|
| 719 |
+
output_size = dummy_input.view(dummy_input.size(0), -1).size(1)
|
| 720 |
+
|
| 721 |
+
self.flatten = nn.Flatten()
|
| 722 |
+
self.fc1 = nn.Linear(output_size, 100)
|
| 723 |
+
self.leaky_relu = nn.LeakyReLU(negative_slope=0.2)
|
| 724 |
+
self.dropout = nn.Dropout(p=0.2)
|
| 725 |
+
self.fc2 = nn.Linear(100, 1)
|
| 726 |
+
|
| 727 |
+
def forward(self, x):
|
| 728 |
+
"""Forward pass through the network.
|
| 729 |
+
|
| 730 |
+
Args:
|
| 731 |
+
x: Input tensor
|
| 732 |
+
|
| 733 |
+
Returns:
|
| 734 |
+
Output tensor
|
| 735 |
+
"""
|
| 736 |
+
x = x.unsqueeze(1) # add a channel dimension of 1 to the data
|
| 737 |
+
for layer in self.conv_layers:
|
| 738 |
+
x = layer(x)
|
| 739 |
+
|
| 740 |
+
x = self.flatten(x)
|
| 741 |
+
x = self.fc1(x)
|
| 742 |
+
x = self.leaky_relu(x)
|
| 743 |
+
x = self.dropout(x)
|
| 744 |
+
x = self.fc2(x)
|
| 745 |
+
|
| 746 |
+
return x
|
model/predictors/sweep_configs/cnn_custom_grid_sweep.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
program: train.py
|
| 2 |
+
method: grid
|
| 3 |
+
metric:
|
| 4 |
+
name: Test Loss
|
| 5 |
+
goal: minimize
|
| 6 |
+
parameters:
|
| 7 |
+
layersize_filtersize:
|
| 8 |
+
values: ["1-32", "1-128", "1-256", "1-1024"]
|
| 9 |
+
kernel_size:
|
| 10 |
+
values: [3, 7]
|
| 11 |
+
learning_rate:
|
| 12 |
+
values: [0.0001, 0.001]
|
| 13 |
+
batch_size:
|
| 14 |
+
values: [4, 8]
|
| 15 |
+
optimizer:
|
| 16 |
+
value: "adam"
|
| 17 |
+
epochs:
|
| 18 |
+
value: 300
|
model/predictors/sweep_configs/cnn_standard_bayes_sweep.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
program: train.py
|
| 2 |
+
method: bayes
|
| 3 |
+
metric:
|
| 4 |
+
name: Test Loss
|
| 5 |
+
goal: minimize
|
| 6 |
+
parameters:
|
| 7 |
+
layersize_filtersize:
|
| 8 |
+
values: ["1-32", "1-128", "1-256", "1-1024", "2-128", "3-128", "5-128"]
|
| 9 |
+
kernel_size:
|
| 10 |
+
values: [3, 7, 13, 17]
|
| 11 |
+
learning_rate:
|
| 12 |
+
values: [0.0001, 0.001, 0.01]
|
| 13 |
+
batch_size:
|
| 14 |
+
values: [4, 8, 16, 32]
|
| 15 |
+
optimizer:
|
| 16 |
+
value: "adam"
|
| 17 |
+
epochs:
|
| 18 |
+
value: 300
|
model/predictors/sweep_configs/cnn_standard_grid_sweep.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
program: train.py
|
| 2 |
+
method: grid
|
| 3 |
+
metric:
|
| 4 |
+
name: Test Loss
|
| 5 |
+
goal: minimize
|
| 6 |
+
parameters:
|
| 7 |
+
layersize_filtersize:
|
| 8 |
+
values: ["1-32", "1-128", "1-256", "1-1024", "2-128", "3-128", "5-128"]
|
| 9 |
+
kernel_size:
|
| 10 |
+
values: [3, 7, 13, 17]
|
| 11 |
+
learning_rate:
|
| 12 |
+
values: [0.0001, 0.001, 0.01]
|
| 13 |
+
batch_size:
|
| 14 |
+
values: [4, 8, 16, 32]
|
| 15 |
+
optimizer:
|
| 16 |
+
value: "adam"
|
| 17 |
+
epochs:
|
| 18 |
+
value: 300
|
model/predictors/sweep_configs/cnn_test_sweep.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
program: train.py
|
| 2 |
+
method: grid
|
| 3 |
+
metric:
|
| 4 |
+
name: Test Loss
|
| 5 |
+
goal: minimize
|
| 6 |
+
parameters:
|
| 7 |
+
layersize_filtersize:
|
| 8 |
+
values: ["1-32"]
|
| 9 |
+
kernel_size:
|
| 10 |
+
values: [3]
|
| 11 |
+
learning_rate:
|
| 12 |
+
values: [0.0001]
|
| 13 |
+
batch_size:
|
| 14 |
+
values: [32]
|
| 15 |
+
optimizer:
|
| 16 |
+
value: "adam"
|
| 17 |
+
epochs:
|
| 18 |
+
value: 300
|
model/predictors/sweep_configs/fcn_custom_grid_sweep.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
program: train.py
|
| 2 |
+
method: grid
|
| 3 |
+
metric:
|
| 4 |
+
name: Test Loss
|
| 5 |
+
goal: minimize
|
| 6 |
+
parameters:
|
| 7 |
+
layer_size:
|
| 8 |
+
value: 100
|
| 9 |
+
num_layers:
|
| 10 |
+
values: [1, 2, 3, 4, 5]
|
| 11 |
+
learning_rate:
|
| 12 |
+
values: [0.0001, 0.001, 0.01]
|
| 13 |
+
batch_size:
|
| 14 |
+
values: [16, 32, 64, 128, 256, 512]
|
| 15 |
+
optimizer:
|
| 16 |
+
value: "adam"
|
| 17 |
+
epochs:
|
| 18 |
+
value: 500
|
model/predictors/sweep_configs/fcn_standard_bayes_sweep.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
program: train.py
|
| 2 |
+
method: bayes
|
| 3 |
+
metric:
|
| 4 |
+
name: Test Loss
|
| 5 |
+
goal: minimize
|
| 6 |
+
parameters:
|
| 7 |
+
layer_size:
|
| 8 |
+
value: 100
|
| 9 |
+
num_layers:
|
| 10 |
+
values: [1, 2, 3, 4, 5]
|
| 11 |
+
learning_rate:
|
| 12 |
+
values: [0.0001, 0.001, 0.01]
|
| 13 |
+
batch_size:
|
| 14 |
+
values: [4, 8, 16, 32]
|
| 15 |
+
optimizer:
|
| 16 |
+
value: "adam"
|
| 17 |
+
epochs:
|
| 18 |
+
value: 300
|
model/predictors/sweep_configs/fcn_standard_grid_sweep.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
program: train.py
|
| 2 |
+
method: grid
|
| 3 |
+
metric:
|
| 4 |
+
name: Test Loss
|
| 5 |
+
goal: minimize
|
| 6 |
+
parameters:
|
| 7 |
+
layer_size:
|
| 8 |
+
value: 100
|
| 9 |
+
num_layers:
|
| 10 |
+
values: [1, 2, 3, 4, 5]
|
| 11 |
+
learning_rate:
|
| 12 |
+
values: [0.0001, 0.001, 0.01]
|
| 13 |
+
batch_size:
|
| 14 |
+
values: [4, 8, 16, 32]
|
| 15 |
+
optimizer:
|
| 16 |
+
value: "adam"
|
| 17 |
+
epochs:
|
| 18 |
+
value: 300
|
model/predictors/sweep_configs/fcn_test_sweep.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
program: train.py
|
| 2 |
+
method: grid
|
| 3 |
+
metric:
|
| 4 |
+
name: Test Loss
|
| 5 |
+
goal: minimize
|
| 6 |
+
parameters:
|
| 7 |
+
layer_size:
|
| 8 |
+
value: 100
|
| 9 |
+
num_layers:
|
| 10 |
+
values: [1]
|
| 11 |
+
learning_rate:
|
| 12 |
+
values: [0.0001]
|
| 13 |
+
batch_size:
|
| 14 |
+
values: [32]
|
| 15 |
+
optimizer:
|
| 16 |
+
value: "adam"
|
| 17 |
+
epochs:
|
| 18 |
+
value: 300
|
model/proposers/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from model.proposers.base_proposers import *
|