anzhi2710gmailcom commited on
Commit
6f1e670
·
verified ·
1 Parent(s): 7d6ea21

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +23 -10
  2. .streamlit/config.toml +6 -0
  3. LICENSE +201 -0
  4. MANIFEST.in +1 -0
  5. README.md +500 -0
  6. config.json +25 -0
  7. data/README.md +13 -0
  8. data/benchmark/README.md +10 -0
  9. data/benchmark/dataset_summary.csv +13 -0
  10. data/example_multichain_protein/combo_muts.csv +10 -0
  11. data/example_multichain_protein/example_dataset.csv +254 -0
  12. data/example_multichain_protein/multichain_protein.cif +0 -0
  13. data/example_multichain_protein/vh_chain1.fasta +2 -0
  14. data/example_multichain_protein/vl_chain2.fasta +2 -0
  15. data/example_protein/APEX_33overhang.fasta +2 -0
  16. data/example_protein/MULTI-assembly_input.csv +15 -0
  17. data/example_protein/apex.cif +0 -0
  18. data/example_protein/apex.fasta +2 -0
  19. data/example_protein/combo_muts.csv +15 -0
  20. data/example_protein/example_dataset.csv +185 -0
  21. data/example_protein/run.sh +43 -0
  22. env.yml +26 -0
  23. env_mac.yml +27 -0
  24. hub/checkpoints/esm2_t36_3B_UR50D-contact-regression.pt +3 -0
  25. model/__init__.py +6 -0
  26. model/featurizers/__init__.py +7 -0
  27. model/featurizers/ankh_featurizers.py +82 -0
  28. model/featurizers/base_featurizers.py +266 -0
  29. model/featurizers/combinatorial_featurizers.py +425 -0
  30. model/featurizers/esm_featurizers.py +620 -0
  31. model/featurizers/model_choices.py +70 -0
  32. model/featurizers/model_locations.py +38 -0
  33. model/featurizers/msa_featurizers.py +381 -0
  34. model/featurizers/prott5_featurizers.py +65 -0
  35. model/featurizers/unirep_featurizers.py +42 -0
  36. model/featurizers/zeroshot_featurizers.py +632 -0
  37. model/multievolve_workflow.png +3 -0
  38. model/predictors/__init__.py +3 -0
  39. model/predictors/base_regressors.py +550 -0
  40. model/predictors/gaussian_process_regressors.py +503 -0
  41. model/predictors/neural_net_regressors.py +746 -0
  42. model/predictors/sweep_configs/cnn_custom_grid_sweep.yaml +18 -0
  43. model/predictors/sweep_configs/cnn_standard_bayes_sweep.yaml +18 -0
  44. model/predictors/sweep_configs/cnn_standard_grid_sweep.yaml +18 -0
  45. model/predictors/sweep_configs/cnn_test_sweep.yaml +18 -0
  46. model/predictors/sweep_configs/fcn_custom_grid_sweep.yaml +18 -0
  47. model/predictors/sweep_configs/fcn_standard_bayes_sweep.yaml +18 -0
  48. model/predictors/sweep_configs/fcn_standard_grid_sweep.yaml +18 -0
  49. model/predictors/sweep_configs/fcn_test_sweep.yaml +18 -0
  50. 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

  • SHA256: 80a141357222a3a6954ae57cc299054b507283aed254e87c5780a1397286d0b7
  • Pointer size: 131 Bytes
  • Size of remote file: 291 kB
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 *