pepe-cli 1.1__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pepe_cli-1.1/src/pepe_cli.egg-info → pepe_cli-1.3.0}/PKG-INFO +24 -5
- {pepe_cli-1.1 → pepe_cli-1.3.0}/README.md +22 -4
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/model_selection.md +15 -1
- {pepe_cli-1.1 → pepe_cli-1.3.0}/pyproject.toml +2 -1
- {pepe_cli-1.1 → pepe_cli-1.3.0}/requirements.txt +2 -1
- {pepe_cli-1.1 → pepe_cli-1.3.0}/setup.py +2 -1
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/__init__.py +1 -1
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/embedders/custom_embedder.py +3 -2
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/embedders/esm_embedder.py +3 -2
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/embedders/huggingface_embedder.py +289 -21
- pepe_cli-1.3.0/src/pepe/model_selecter.py +169 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/parse_arguments.py +0 -1
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/utils.py +8 -4
- {pepe_cli-1.1 → pepe_cli-1.3.0/src/pepe_cli.egg-info}/PKG-INFO +24 -5
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe_cli.egg-info/SOURCES.txt +5 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe_cli.egg-info/requires.txt +1 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/tests/test_device_logic.py +5 -1
- pepe_cli-1.3.0/src/tests/test_esmc.py +61 -0
- pepe_cli-1.3.0/src/tests/test_esmc_modes.py +93 -0
- pepe_cli-1.3.0/src/tests/test_load_layers_default.py +30 -0
- pepe_cli-1.3.0/src/tests/test_model_selection.py +104 -0
- pepe_cli-1.3.0/src/tests/test_parse_arguments.py +114 -0
- pepe_cli-1.1/src/pepe/model_selecter.py +0 -114
- {pepe_cli-1.1 → pepe_cli-1.3.0}/LICENSE +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/MANIFEST.in +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/create_example_custom_model.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/example_protein_model/config.json +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/example_protein_model/pytorch_model.pt +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/example_protein_model/special_tokens_map.json +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/example_protein_model/tokenizer_config.json +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/example_protein_model/vocab.json +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/example_protein_model/vocab.txt +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/example_sequences.fasta +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/custom_model/example_substring.csv +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/examples/embedding_options.md +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/setup.cfg +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/__main__.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/api.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/embedders/__init__.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe/embedders/base_embedder.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe_cli.egg-info/dependency_links.txt +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe_cli.egg-info/entry_points.txt +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/pepe_cli.egg-info/top_level.txt +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/tests/__init__.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/tests/test_api_unittest.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/tests/test_run.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/tests/test_splitting.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/tests/verify_cross_tool_consistency.py +0 -0
- {pepe_cli-1.1 → pepe_cli-1.3.0}/src/tests/verify_readme.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pepe-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Pipeline for Easy Protein Embedding - Extract embeddings and attention matrices from protein sequences
|
|
5
5
|
Home-page: https://github.com/csi-greifflab/pepe-cli
|
|
6
6
|
Author: Jahn Zhong
|
|
@@ -13,7 +13,7 @@ Project-URL: Documentation, https://github.com/csi-greifflab/pepe-cli#readme
|
|
|
13
13
|
Keywords: protein,embeddings,bioinformatics,machine-learning,nlp,transformers
|
|
14
14
|
Classifier: Development Status :: 3 - Alpha
|
|
15
15
|
Classifier: Intended Audience :: Science/Research
|
|
16
|
-
Classifier: License :: OSI Approved ::
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
@@ -31,6 +31,7 @@ Requires-Dist: sentencepiece
|
|
|
31
31
|
Requires-Dist: numpy
|
|
32
32
|
Requires-Dist: protobuf
|
|
33
33
|
Requires-Dist: alive_progress
|
|
34
|
+
Requires-Dist: rjieba
|
|
34
35
|
Dynamic: author
|
|
35
36
|
Dynamic: home-page
|
|
36
37
|
Dynamic: license-file
|
|
@@ -40,9 +41,16 @@ Dynamic: requires-python
|
|
|
40
41
|
|
|
41
42
|
PEPE (Pipeline for Easy Protein Embedding) is a tool for extracting embeddings and attention matrices from protein sequences using pre-trained models. This tool supports various configurations for extracting embeddings and attention matrices, including options for handling CDR3 sequences. Currently implemented models are ESM2 from the 2023 paper ["Evolutionary-scale prediction of atomic-level protein structure with a language model"](https://science.org/doi/10.1126/science.ade2574) and AntiBERTa2-CSSP from the 2023 conference paper ["Enhancing Antibody Language Models with Structural Information"](https://www.mlsb.io/papers_2023/Enhancing_Antibody_Language_Models_with_Structural_Information.pdf). PEPE also supports custom PLMs from local files or from Huggingface Hub addresses.
|
|
42
43
|
|
|
44
|
+
### Citation
|
|
45
|
+
> **PEPE: Scalable extraction of multi-modal protein language model representations**
|
|
46
|
+
> Jahn Zhong, Niccolò Cardente, Geir Kjetil Sandve, Habib Bashour, Maria Francesca Abbate, Victor Greiff
|
|
47
|
+
> *bioRxiv* (2026)
|
|
48
|
+
> [DOI: 10.1101/2025.10.13.680902](https://doi.org/10.1101/2025.10.13.680902)
|
|
49
|
+
|
|
43
50
|
## Quick start
|
|
44
51
|
|
|
45
|
-
1. Install PEPE
|
|
52
|
+
1. Install PEPE
|
|
53
|
+
|
|
46
54
|
From PyPI:
|
|
47
55
|
```sh
|
|
48
56
|
pip install pepe-cli
|
|
@@ -57,7 +65,13 @@ PEPE (Pipeline for Easy Protein Embedding) is a tool for extracting embeddings a
|
|
|
57
65
|
cd pepe-cli
|
|
58
66
|
pip install .
|
|
59
67
|
```
|
|
60
|
-
|
|
68
|
+
|
|
69
|
+
2. *(Optional)* For ESMC models (e.g. `biohub/ESMC-300M`), install Biohub's transformers fork:
|
|
70
|
+
```sh
|
|
71
|
+
pip install git+https://github.com/Biohub/transformers.git@main
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
3. Extract embeddings:\
|
|
61
75
|
Extract mean pooled embeddings from protein amino acid sequences in FASTA file:
|
|
62
76
|
```sh
|
|
63
77
|
pepe --experiment_name <optional_string> --fasta_path <file_path> --output_path <directory> --model_name <model_name>
|
|
@@ -67,7 +81,7 @@ PEPE (Pipeline for Easy Protein Embedding) is a tool for extracting embeddings a
|
|
|
67
81
|
|
|
68
82
|
PEPE can also be used as a Python library. This allows for programmatic access to protein embeddings without using the command-line interface.
|
|
69
83
|
|
|
70
|
-
1. Install PEPE (see
|
|
84
|
+
1. Install PEPE (see above for details).
|
|
71
85
|
2. Use the `pepe.embed()` function in your script:
|
|
72
86
|
|
|
73
87
|
```python
|
|
@@ -220,6 +234,10 @@ results = pepe.embed(
|
|
|
220
234
|
- RoFormer models
|
|
221
235
|
- alchemab/antiberta2-cssp
|
|
222
236
|
- alchemab/antiberta2
|
|
237
|
+
- ESMC models (requires Biohub transformers fork; see install instructions above)
|
|
238
|
+
- biohub/ESMC-300M
|
|
239
|
+
- biohub/ESMC-600M
|
|
240
|
+
- biohub/ESMC-6B
|
|
223
241
|
- Custom Hugging Face models
|
|
224
242
|
- Any compatible model from Hugging Face Hub: `username/model-name`
|
|
225
243
|
- Private models with authentication
|
|
@@ -234,6 +252,7 @@ results = pepe.embed(
|
|
|
234
252
|
### Required Arguments
|
|
235
253
|
- **`--model_name`** (str): Name of model or link to model. Choose from [List of supported models](../README.md#list-of-supported-models) or use custom models:
|
|
236
254
|
- ESM models: `esm2_t33_650M_UR50D`
|
|
255
|
+
- ESMC models: `biohub/ESMC-300M` (requires Biohub transformers fork; see Quick start)
|
|
237
256
|
- Hugging Face models: `username/model-name`
|
|
238
257
|
- Custom PyTorch models: `/path/to/model.pt` or `/path/to/model_directory/`
|
|
239
258
|
- Local HF models: `/path/to/local_hf_directory/`
|
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
PEPE (Pipeline for Easy Protein Embedding) is a tool for extracting embeddings and attention matrices from protein sequences using pre-trained models. This tool supports various configurations for extracting embeddings and attention matrices, including options for handling CDR3 sequences. Currently implemented models are ESM2 from the 2023 paper ["Evolutionary-scale prediction of atomic-level protein structure with a language model"](https://science.org/doi/10.1126/science.ade2574) and AntiBERTa2-CSSP from the 2023 conference paper ["Enhancing Antibody Language Models with Structural Information"](https://www.mlsb.io/papers_2023/Enhancing_Antibody_Language_Models_with_Structural_Information.pdf). PEPE also supports custom PLMs from local files or from Huggingface Hub addresses.
|
|
4
4
|
|
|
5
|
+
### Citation
|
|
6
|
+
> **PEPE: Scalable extraction of multi-modal protein language model representations**
|
|
7
|
+
> Jahn Zhong, Niccolò Cardente, Geir Kjetil Sandve, Habib Bashour, Maria Francesca Abbate, Victor Greiff
|
|
8
|
+
> *bioRxiv* (2026)
|
|
9
|
+
> [DOI: 10.1101/2025.10.13.680902](https://doi.org/10.1101/2025.10.13.680902)
|
|
10
|
+
|
|
5
11
|
## Quick start
|
|
6
12
|
|
|
7
|
-
1. Install PEPE
|
|
13
|
+
1. Install PEPE
|
|
14
|
+
|
|
8
15
|
From PyPI:
|
|
9
16
|
```sh
|
|
10
17
|
pip install pepe-cli
|
|
@@ -19,7 +26,13 @@ PEPE (Pipeline for Easy Protein Embedding) is a tool for extracting embeddings a
|
|
|
19
26
|
cd pepe-cli
|
|
20
27
|
pip install .
|
|
21
28
|
```
|
|
22
|
-
|
|
29
|
+
|
|
30
|
+
2. *(Optional)* For ESMC models (e.g. `biohub/ESMC-300M`), install Biohub's transformers fork:
|
|
31
|
+
```sh
|
|
32
|
+
pip install git+https://github.com/Biohub/transformers.git@main
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. Extract embeddings:\
|
|
23
36
|
Extract mean pooled embeddings from protein amino acid sequences in FASTA file:
|
|
24
37
|
```sh
|
|
25
38
|
pepe --experiment_name <optional_string> --fasta_path <file_path> --output_path <directory> --model_name <model_name>
|
|
@@ -29,7 +42,7 @@ PEPE (Pipeline for Easy Protein Embedding) is a tool for extracting embeddings a
|
|
|
29
42
|
|
|
30
43
|
PEPE can also be used as a Python library. This allows for programmatic access to protein embeddings without using the command-line interface.
|
|
31
44
|
|
|
32
|
-
1. Install PEPE (see
|
|
45
|
+
1. Install PEPE (see above for details).
|
|
33
46
|
2. Use the `pepe.embed()` function in your script:
|
|
34
47
|
|
|
35
48
|
```python
|
|
@@ -182,6 +195,10 @@ results = pepe.embed(
|
|
|
182
195
|
- RoFormer models
|
|
183
196
|
- alchemab/antiberta2-cssp
|
|
184
197
|
- alchemab/antiberta2
|
|
198
|
+
- ESMC models (requires Biohub transformers fork; see install instructions above)
|
|
199
|
+
- biohub/ESMC-300M
|
|
200
|
+
- biohub/ESMC-600M
|
|
201
|
+
- biohub/ESMC-6B
|
|
185
202
|
- Custom Hugging Face models
|
|
186
203
|
- Any compatible model from Hugging Face Hub: `username/model-name`
|
|
187
204
|
- Private models with authentication
|
|
@@ -196,6 +213,7 @@ results = pepe.embed(
|
|
|
196
213
|
### Required Arguments
|
|
197
214
|
- **`--model_name`** (str): Name of model or link to model. Choose from [List of supported models](../README.md#list-of-supported-models) or use custom models:
|
|
198
215
|
- ESM models: `esm2_t33_650M_UR50D`
|
|
216
|
+
- ESMC models: `biohub/ESMC-300M` (requires Biohub transformers fork; see Quick start)
|
|
199
217
|
- Hugging Face models: `username/model-name`
|
|
200
218
|
- Custom PyTorch models: `/path/to/model.pt` or `/path/to/model_directory/`
|
|
201
219
|
- Local HF models: `/path/to/local_hf_directory/`
|
|
@@ -239,4 +257,4 @@ When False, all outputs are stored in RAM and written to disk at once after comp
|
|
|
239
257
|
|
|
240
258
|
### Performance Configuration
|
|
241
259
|
- **`--num_workers`** (int, optional): Number of workers for asynchronous data writing. Only relevant when `--streaming_output` is enabled. Default is `8`.
|
|
242
|
-
- **`--flush_batches_after`** (int, optional): Size (in MB) of outputs to accumulate in RAM per worker before flushing to disk. Default is `128`.
|
|
260
|
+
- **`--flush_batches_after`** (int, optional): Size (in MB) of outputs to accumulate in RAM per worker before flushing to disk. Default is `128`.
|
|
@@ -10,7 +10,7 @@ pepe \
|
|
|
10
10
|
--output_path "src/tests/test_files/test_output"
|
|
11
11
|
```
|
|
12
12
|
## Models hosted on Huggingface
|
|
13
|
-
The user can also choose from
|
|
13
|
+
The user can also choose from PLMs hosted on Huggingface Hub by passing the repository path as `--model_name`:
|
|
14
14
|
```sh
|
|
15
15
|
pepe \
|
|
16
16
|
--experiment_name "test" \
|
|
@@ -19,6 +19,20 @@ pepe \
|
|
|
19
19
|
--output_path "src/tests/test_files/test_output"
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
### ESMC models
|
|
23
|
+
ESMC (ESM Cambrian) is a distinct architecture from ESM2. It requires Biohub's transformers fork, which coexists with ESM1's `fair-esm` package:
|
|
24
|
+
```sh
|
|
25
|
+
pip install git+https://github.com/Biohub/transformers.git@main
|
|
26
|
+
pepe \
|
|
27
|
+
--experiment_name "esmc_test" \
|
|
28
|
+
--model_name "biohub/ESMC-300M" \
|
|
29
|
+
--fasta_path "src/tests/test_files/test.fasta" \
|
|
30
|
+
--output_path "src/tests/test_files/test_output" \
|
|
31
|
+
--extract_embeddings mean_pooled \
|
|
32
|
+
--device cpu \
|
|
33
|
+
--layers -1
|
|
34
|
+
```
|
|
35
|
+
|
|
22
36
|
## Custom models
|
|
23
37
|
To specify your own custom model, pass the path to a directory containing:
|
|
24
38
|
- .pt or .pth file (model weights)
|
|
@@ -15,7 +15,7 @@ requires-python = ">=3.8"
|
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 3 - Alpha",
|
|
17
17
|
"Intended Audience :: Science/Research",
|
|
18
|
-
"License :: OSI Approved ::
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
19
|
"Operating System :: OS Independent",
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
21
21
|
"Programming Language :: Python :: 3.8",
|
|
@@ -33,6 +33,7 @@ dependencies = [
|
|
|
33
33
|
"numpy",
|
|
34
34
|
"protobuf",
|
|
35
35
|
"alive_progress",
|
|
36
|
+
"rjieba",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[project.urls]
|
|
@@ -28,7 +28,7 @@ setup(
|
|
|
28
28
|
classifiers=[
|
|
29
29
|
"Development Status :: 3 - Alpha",
|
|
30
30
|
"Intended Audience :: Science/Research",
|
|
31
|
-
"License :: OSI Approved ::
|
|
31
|
+
"License :: OSI Approved :: MIT License",
|
|
32
32
|
"Operating System :: Linux",
|
|
33
33
|
"Operating System :: macOS",
|
|
34
34
|
"Programming Language :: Python :: 3",
|
|
@@ -47,6 +47,7 @@ setup(
|
|
|
47
47
|
"numpy",
|
|
48
48
|
"protobuf",
|
|
49
49
|
"alive_progress",
|
|
50
|
+
"rjieba",
|
|
50
51
|
],
|
|
51
52
|
entry_points={
|
|
52
53
|
"console_scripts": [
|
|
@@ -288,9 +288,10 @@ class CustomEmbedder(BaseEmbedder):
|
|
|
288
288
|
|
|
289
289
|
def _load_layers(self, layers):
|
|
290
290
|
"""Process layer specification."""
|
|
291
|
+
if layers is None:
|
|
292
|
+
return list(range(1, self.num_layers + 1))
|
|
291
293
|
if not layers:
|
|
292
|
-
layers =
|
|
293
|
-
return layers
|
|
294
|
+
layers = [-1]
|
|
294
295
|
|
|
295
296
|
# Validate layer indices
|
|
296
297
|
assert all(
|
|
@@ -102,9 +102,10 @@ class ESMEmbedder(BaseEmbedder):
|
|
|
102
102
|
return special_token_ids
|
|
103
103
|
|
|
104
104
|
def _load_layers(self, layers):
|
|
105
|
+
if layers is None:
|
|
106
|
+
return list(range(1, self.model.num_layers + 1)) # type: ignore
|
|
105
107
|
if not layers:
|
|
106
|
-
layers =
|
|
107
|
-
return layers
|
|
108
|
+
layers = [-1]
|
|
108
109
|
# Checking if the specified representation layers are valid
|
|
109
110
|
assert all(
|
|
110
111
|
-(self.model.num_layers + 1) <= i <= self.model.num_layers for i in layers # type: ignore
|
|
@@ -15,6 +15,7 @@ def _import_transformers():
|
|
|
15
15
|
RoFormerSinusoidalPositionalEmbedding,
|
|
16
16
|
)
|
|
17
17
|
from transformers import AutoModel, AutoTokenizer, AutoModelForCausalLM
|
|
18
|
+
from transformers import AutoModelForMaskedLM
|
|
18
19
|
|
|
19
20
|
return (
|
|
20
21
|
T5EncoderModel,
|
|
@@ -25,6 +26,7 @@ def _import_transformers():
|
|
|
25
26
|
AutoModel,
|
|
26
27
|
AutoTokenizer,
|
|
27
28
|
AutoModelForCausalLM,
|
|
29
|
+
AutoModelForMaskedLM,
|
|
28
30
|
)
|
|
29
31
|
except ImportError as e:
|
|
30
32
|
logger.error(f"Failed to import transformers: {e}")
|
|
@@ -51,20 +53,13 @@ class HuggingfaceEmbedder(BaseEmbedder):
|
|
|
51
53
|
|
|
52
54
|
def _load_layers(self, layers):
|
|
53
55
|
"""Check if the specified representation layers are valid."""
|
|
56
|
+
num_layers = self.num_layers # type: ignore
|
|
57
|
+
if layers is None:
|
|
58
|
+
return list(range(1, num_layers + 1))
|
|
54
59
|
if not layers:
|
|
55
|
-
layers =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
-(self.model.config.num_hidden_layers + 1) # type: ignore
|
|
59
|
-
<= i
|
|
60
|
-
<= self.model.config.num_hidden_layers # type: ignore
|
|
61
|
-
for i in layers
|
|
62
|
-
)
|
|
63
|
-
layers = [
|
|
64
|
-
(i + self.model.config.num_hidden_layers + 1) # type: ignore
|
|
65
|
-
% (self.model.config.num_hidden_layers + 1) # type: ignore
|
|
66
|
-
for i in layers
|
|
67
|
-
]
|
|
60
|
+
layers = [-1]
|
|
61
|
+
assert all(-(num_layers + 1) <= i <= num_layers for i in layers)
|
|
62
|
+
layers = [(i + num_layers + 1) % (num_layers + 1) for i in layers]
|
|
68
63
|
return layers
|
|
69
64
|
|
|
70
65
|
def _load_data(self, sequences, substring_dict, bracket_type):
|
|
@@ -116,14 +111,18 @@ class HuggingfaceEmbedder(BaseEmbedder):
|
|
|
116
111
|
else:
|
|
117
112
|
attention_matrices = None
|
|
118
113
|
if return_embeddings:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
114
|
+
dtype = self._precision_to_dtype(self.precision, "torch")
|
|
115
|
+
hidden_states = outputs.hidden_states
|
|
116
|
+
if isinstance(hidden_states, torch.Tensor):
|
|
117
|
+
representations = {
|
|
118
|
+
layer: hidden_states[layer].to(dtype).cpu()
|
|
119
|
+
for layer in self.layers # type: ignore
|
|
120
|
+
}
|
|
121
|
+
else:
|
|
122
|
+
representations = {
|
|
123
|
+
layer: hidden_states[layer].to(dtype).cpu()
|
|
124
|
+
for layer in self.layers # type: ignore
|
|
125
|
+
}
|
|
127
126
|
torch.cuda.empty_cache()
|
|
128
127
|
else:
|
|
129
128
|
representations = None
|
|
@@ -182,6 +181,7 @@ class Antiberta2Embedder(HuggingfaceEmbedder):
|
|
|
182
181
|
AutoModel,
|
|
183
182
|
AutoTokenizer,
|
|
184
183
|
AutoModelForCausalLM,
|
|
184
|
+
AutoModelForMaskedLM,
|
|
185
185
|
) = _import_transformers()
|
|
186
186
|
|
|
187
187
|
tokenizer = RoFormerTokenizer.from_pretrained(model_link, use_fast=True)
|
|
@@ -246,6 +246,7 @@ class T5Embedder(HuggingfaceEmbedder):
|
|
|
246
246
|
AutoModel,
|
|
247
247
|
AutoTokenizer,
|
|
248
248
|
AutoModelForCausalLM,
|
|
249
|
+
AutoModelForMaskedLM,
|
|
249
250
|
) = _import_transformers()
|
|
250
251
|
|
|
251
252
|
tokenizer = T5Tokenizer.from_pretrained(model_link, use_fast=True)
|
|
@@ -257,6 +258,272 @@ class T5Embedder(HuggingfaceEmbedder):
|
|
|
257
258
|
return model, tokenizer, num_heads, num_layers, embedding_size
|
|
258
259
|
|
|
259
260
|
|
|
261
|
+
def _resolve_esm2_model_link(model_name):
|
|
262
|
+
if "/" in model_name:
|
|
263
|
+
return model_name
|
|
264
|
+
return f"facebook/{model_name}"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class ESM2Embedder(HuggingfaceEmbedder):
|
|
268
|
+
"""ESM-2 embedder using HuggingFace transformers instead of fair-esm."""
|
|
269
|
+
|
|
270
|
+
def __init__(self, args):
|
|
271
|
+
BaseEmbedder.__init__(self, args)
|
|
272
|
+
self.sequences = pepe.utils.fasta_to_dict(args.fasta_path)
|
|
273
|
+
self.num_sequences = len(self.sequences)
|
|
274
|
+
(
|
|
275
|
+
self.model,
|
|
276
|
+
self.tokenizer,
|
|
277
|
+
self.num_heads,
|
|
278
|
+
self.num_layers,
|
|
279
|
+
self.embedding_size,
|
|
280
|
+
) = self._initialize_model(self.model_name)
|
|
281
|
+
self.valid_tokens = self._get_valid_tokens()
|
|
282
|
+
self.bracket_type = pepe.utils.get_bracket_type(self.tokenizer)
|
|
283
|
+
self._check_max_input_length()
|
|
284
|
+
pepe.utils.check_input_tokens(
|
|
285
|
+
self.valid_tokens,
|
|
286
|
+
self.sequences,
|
|
287
|
+
self.model_name,
|
|
288
|
+
split_long_sequences=self.split_long_sequences,
|
|
289
|
+
)
|
|
290
|
+
self.special_tokens = torch.tensor(
|
|
291
|
+
self.tokenizer.all_special_ids, device=self.device, dtype=torch.int8
|
|
292
|
+
)
|
|
293
|
+
self.layers = self._load_layers(self.layers)
|
|
294
|
+
self.data_loader, self.max_input_length = self._load_data(
|
|
295
|
+
self.sequences, self.substring_dict, self.bracket_type
|
|
296
|
+
)
|
|
297
|
+
self._set_output_objects()
|
|
298
|
+
|
|
299
|
+
def _get_valid_tokens(self):
|
|
300
|
+
return {tok for tok in self.tokenizer.get_vocab().keys() if len(tok) == 1}
|
|
301
|
+
|
|
302
|
+
def _load_data(self, sequences, substring_dict, bracket_type):
|
|
303
|
+
dataset = pepe.utils.HuggingFaceDataset(
|
|
304
|
+
sequences,
|
|
305
|
+
substring_dict,
|
|
306
|
+
self.context,
|
|
307
|
+
bracket_type,
|
|
308
|
+
self.tokenizer,
|
|
309
|
+
self.max_input_length,
|
|
310
|
+
add_special_tokens=not self.disable_special_tokens,
|
|
311
|
+
)
|
|
312
|
+
dataset.pad_token_id = self.tokenizer.pad_token_id
|
|
313
|
+
logger.info("Batching sequences...")
|
|
314
|
+
batch_sampler = pepe.utils.TokenBudgetBatchSampler(
|
|
315
|
+
dataset=dataset, token_budget=self.batch_size
|
|
316
|
+
)
|
|
317
|
+
data_loader = torch.utils.data.DataLoader(
|
|
318
|
+
dataset, batch_sampler=batch_sampler, collate_fn=dataset.safe_collate
|
|
319
|
+
)
|
|
320
|
+
max_length = dataset.get_max_encoded_length()
|
|
321
|
+
logger.info("Finished tokenizing and batching sequences")
|
|
322
|
+
return data_loader, max_length
|
|
323
|
+
|
|
324
|
+
def _initialize_model(self, model_name):
|
|
325
|
+
model_link = _resolve_esm2_model_link(model_name)
|
|
326
|
+
if torch.cuda.is_available() and self.device.type == "cuda":
|
|
327
|
+
device = torch.device("cuda")
|
|
328
|
+
logger.info("Transferred model to GPU")
|
|
329
|
+
else:
|
|
330
|
+
device = torch.device("cpu")
|
|
331
|
+
logger.info("No GPU available, using CPU")
|
|
332
|
+
|
|
333
|
+
(
|
|
334
|
+
T5EncoderModel,
|
|
335
|
+
T5Tokenizer,
|
|
336
|
+
RoFormerTokenizer,
|
|
337
|
+
RoFormerModel,
|
|
338
|
+
RoFormerSinusoidalPositionalEmbedding,
|
|
339
|
+
AutoModel,
|
|
340
|
+
AutoTokenizer,
|
|
341
|
+
AutoModelForCausalLM,
|
|
342
|
+
AutoModelForMaskedLM,
|
|
343
|
+
) = _import_transformers()
|
|
344
|
+
|
|
345
|
+
logger.info(f"Loading ESM-2 model from HuggingFace: {model_link}")
|
|
346
|
+
tokenizer = AutoTokenizer.from_pretrained(model_link)
|
|
347
|
+
model_kwargs = {}
|
|
348
|
+
if self.return_contacts:
|
|
349
|
+
model_kwargs["attn_implementation"] = "eager"
|
|
350
|
+
|
|
351
|
+
if self.return_logits:
|
|
352
|
+
model = AutoModelForMaskedLM.from_pretrained(
|
|
353
|
+
model_link, **model_kwargs
|
|
354
|
+
).to(device)
|
|
355
|
+
else:
|
|
356
|
+
model = AutoModel.from_pretrained(model_link, **model_kwargs).to(device)
|
|
357
|
+
model.eval()
|
|
358
|
+
|
|
359
|
+
config = model.config
|
|
360
|
+
num_heads = config.num_attention_heads
|
|
361
|
+
num_layers = config.num_hidden_layers
|
|
362
|
+
embedding_size = config.hidden_size
|
|
363
|
+
return model, tokenizer, num_heads, num_layers, embedding_size
|
|
364
|
+
|
|
365
|
+
def _compute_outputs(
|
|
366
|
+
self,
|
|
367
|
+
model,
|
|
368
|
+
toks,
|
|
369
|
+
attention_mask,
|
|
370
|
+
return_embeddings,
|
|
371
|
+
return_contacts,
|
|
372
|
+
return_logits=False,
|
|
373
|
+
):
|
|
374
|
+
outputs = model(
|
|
375
|
+
input_ids=toks,
|
|
376
|
+
attention_mask=attention_mask,
|
|
377
|
+
output_hidden_states=return_embeddings,
|
|
378
|
+
output_attentions=return_contacts,
|
|
379
|
+
)
|
|
380
|
+
if return_logits:
|
|
381
|
+
logits = (
|
|
382
|
+
outputs.logits
|
|
383
|
+
.to(dtype=self._precision_to_dtype(self.precision, "torch"))
|
|
384
|
+
.permute(2, 0, 1)
|
|
385
|
+
.cpu()
|
|
386
|
+
)
|
|
387
|
+
torch.cuda.empty_cache()
|
|
388
|
+
else:
|
|
389
|
+
logits = None
|
|
390
|
+
|
|
391
|
+
if return_contacts:
|
|
392
|
+
attention_matrices = (
|
|
393
|
+
torch.stack(outputs.attentions) # type: ignore
|
|
394
|
+
.to(self._precision_to_dtype(self.precision, "torch")) # type: ignore
|
|
395
|
+
.cpu()
|
|
396
|
+
)
|
|
397
|
+
torch.cuda.empty_cache()
|
|
398
|
+
else:
|
|
399
|
+
attention_matrices = None
|
|
400
|
+
|
|
401
|
+
if return_embeddings:
|
|
402
|
+
representations = {
|
|
403
|
+
layer: outputs.hidden_states[layer]
|
|
404
|
+
.to(self._precision_to_dtype(self.precision, "torch"))
|
|
405
|
+
.cpu()
|
|
406
|
+
for layer in self.layers # type: ignore
|
|
407
|
+
}
|
|
408
|
+
torch.cuda.empty_cache()
|
|
409
|
+
else:
|
|
410
|
+
representations = None
|
|
411
|
+
|
|
412
|
+
return logits, representations, attention_matrices
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _get_config_attr(config, *names, default=None):
|
|
416
|
+
for name in names:
|
|
417
|
+
if hasattr(config, name):
|
|
418
|
+
return getattr(config, name)
|
|
419
|
+
return default
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class ESMCEmbedder(HuggingfaceEmbedder):
|
|
423
|
+
"""ESMC embedder using Biohub transformers fork (model_type esmc)."""
|
|
424
|
+
|
|
425
|
+
def __init__(self, args):
|
|
426
|
+
BaseEmbedder.__init__(self, args)
|
|
427
|
+
self.sequences = pepe.utils.fasta_to_dict(args.fasta_path)
|
|
428
|
+
self.num_sequences = len(self.sequences)
|
|
429
|
+
(
|
|
430
|
+
self.model,
|
|
431
|
+
self.tokenizer,
|
|
432
|
+
self.num_heads,
|
|
433
|
+
self.num_layers,
|
|
434
|
+
self.embedding_size,
|
|
435
|
+
) = self._initialize_model(self.model_link)
|
|
436
|
+
self.valid_tokens = self._get_valid_tokens()
|
|
437
|
+
self.bracket_type = pepe.utils.get_bracket_type(self.tokenizer)
|
|
438
|
+
self._check_max_input_length()
|
|
439
|
+
pepe.utils.check_input_tokens(
|
|
440
|
+
self.valid_tokens,
|
|
441
|
+
self.sequences,
|
|
442
|
+
self.model_name,
|
|
443
|
+
split_long_sequences=self.split_long_sequences,
|
|
444
|
+
)
|
|
445
|
+
self.special_tokens = torch.tensor(
|
|
446
|
+
self.tokenizer.all_special_ids, device=self.device, dtype=torch.int8
|
|
447
|
+
)
|
|
448
|
+
self.layers = self._load_layers(self.layers)
|
|
449
|
+
self.data_loader, self.max_input_length = self._load_data(
|
|
450
|
+
self.sequences, self.substring_dict, self.bracket_type
|
|
451
|
+
)
|
|
452
|
+
self._set_output_objects()
|
|
453
|
+
|
|
454
|
+
def _get_valid_tokens(self):
|
|
455
|
+
return {tok for tok in self.tokenizer.get_vocab().keys() if len(tok) == 1}
|
|
456
|
+
|
|
457
|
+
def _load_data(self, sequences, substring_dict, bracket_type):
|
|
458
|
+
dataset = pepe.utils.HuggingFaceDataset(
|
|
459
|
+
sequences,
|
|
460
|
+
substring_dict,
|
|
461
|
+
self.context,
|
|
462
|
+
bracket_type,
|
|
463
|
+
self.tokenizer,
|
|
464
|
+
self.max_input_length,
|
|
465
|
+
add_special_tokens=not self.disable_special_tokens,
|
|
466
|
+
gapped_sequences=False,
|
|
467
|
+
)
|
|
468
|
+
dataset.pad_token_id = self.tokenizer.pad_token_id
|
|
469
|
+
logger.info("Batching sequences...")
|
|
470
|
+
batch_sampler = pepe.utils.TokenBudgetBatchSampler(
|
|
471
|
+
dataset=dataset, token_budget=self.batch_size
|
|
472
|
+
)
|
|
473
|
+
data_loader = torch.utils.data.DataLoader(
|
|
474
|
+
dataset, batch_sampler=batch_sampler, collate_fn=dataset.safe_collate
|
|
475
|
+
)
|
|
476
|
+
max_length = dataset.get_max_encoded_length()
|
|
477
|
+
logger.info("Finished tokenizing and batching sequences")
|
|
478
|
+
return data_loader, max_length
|
|
479
|
+
|
|
480
|
+
def _initialize_model(self, model_link):
|
|
481
|
+
if torch.cuda.is_available() and self.device.type == "cuda":
|
|
482
|
+
device = torch.device("cuda")
|
|
483
|
+
logger.info("Transferred model to GPU")
|
|
484
|
+
else:
|
|
485
|
+
device = torch.device("cpu")
|
|
486
|
+
logger.info("No GPU available, using CPU")
|
|
487
|
+
|
|
488
|
+
(
|
|
489
|
+
T5EncoderModel,
|
|
490
|
+
T5Tokenizer,
|
|
491
|
+
RoFormerTokenizer,
|
|
492
|
+
RoFormerModel,
|
|
493
|
+
RoFormerSinusoidalPositionalEmbedding,
|
|
494
|
+
AutoModel,
|
|
495
|
+
AutoTokenizer,
|
|
496
|
+
AutoModelForCausalLM,
|
|
497
|
+
AutoModelForMaskedLM,
|
|
498
|
+
) = _import_transformers()
|
|
499
|
+
|
|
500
|
+
logger.info(f"Loading ESMC model from HuggingFace: {model_link}")
|
|
501
|
+
tokenizer = AutoTokenizer.from_pretrained(model_link)
|
|
502
|
+
model_kwargs = {}
|
|
503
|
+
if self.return_contacts:
|
|
504
|
+
model_kwargs["attn_implementation"] = "eager"
|
|
505
|
+
|
|
506
|
+
if self.return_logits:
|
|
507
|
+
model = AutoModelForMaskedLM.from_pretrained(
|
|
508
|
+
model_link, **model_kwargs
|
|
509
|
+
).to(device)
|
|
510
|
+
else:
|
|
511
|
+
model = AutoModel.from_pretrained(model_link, **model_kwargs).to(device)
|
|
512
|
+
model.eval()
|
|
513
|
+
|
|
514
|
+
config = model.config
|
|
515
|
+
num_heads = _get_config_attr(
|
|
516
|
+
config, "num_attention_heads", "n_heads", "num_heads"
|
|
517
|
+
)
|
|
518
|
+
num_layers = _get_config_attr(
|
|
519
|
+
config, "num_hidden_layers", "n_layers", "num_layers"
|
|
520
|
+
)
|
|
521
|
+
embedding_size = _get_config_attr(
|
|
522
|
+
config, "hidden_size", "d_model", "embed_dim"
|
|
523
|
+
)
|
|
524
|
+
return model, tokenizer, num_heads, num_layers, embedding_size
|
|
525
|
+
|
|
526
|
+
|
|
260
527
|
class GenericHuggingFaceEmbedder(HuggingfaceEmbedder):
|
|
261
528
|
"""Generic HuggingFace embedder that can handle models with unknown architectures using AutoModel and AutoTokenizer."""
|
|
262
529
|
|
|
@@ -320,6 +587,7 @@ class GenericHuggingFaceEmbedder(HuggingfaceEmbedder):
|
|
|
320
587
|
AutoModel,
|
|
321
588
|
AutoTokenizer,
|
|
322
589
|
AutoModelForCausalLM,
|
|
590
|
+
AutoModelForMaskedLM,
|
|
323
591
|
) = _import_transformers()
|
|
324
592
|
|
|
325
593
|
# For models that commonly require custom code, use trust_remote_code=True immediately
|