pepe-cli 1.0.0__py3-none-any.whl
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/__init__.py +41 -0
- pepe/__main__.py +23 -0
- pepe/embedders/__init__.py +0 -0
- pepe/embedders/base_embedder.py +797 -0
- pepe/embedders/custom_embedder.py +647 -0
- pepe/embedders/esm_embedder.py +164 -0
- pepe/embedders/huggingface_embedder.py +200 -0
- pepe/model_selecter.py +99 -0
- pepe/parse_arguments.py +167 -0
- pepe/utils.py +905 -0
- pepe_cli-1.0.0.dist-info/METADATA +137 -0
- pepe_cli-1.0.0.dist-info/RECORD +18 -0
- pepe_cli-1.0.0.dist-info/WHEEL +5 -0
- pepe_cli-1.0.0.dist-info/entry_points.txt +3 -0
- pepe_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
- pepe_cli-1.0.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/test_run.py +43 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import torch
|
|
3
|
+
from esm import pretrained
|
|
4
|
+
from pepe.embedders.base_embedder import BaseEmbedder
|
|
5
|
+
import pepe.utils
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger("src.embedders.esm_embedder")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ESMEmbedder(BaseEmbedder):
|
|
11
|
+
def __init__(self, args):
|
|
12
|
+
super().__init__(args)
|
|
13
|
+
self.sequences = pepe.utils.fasta_to_dict(args.fasta_path)
|
|
14
|
+
self.num_sequences = len(self.sequences)
|
|
15
|
+
(
|
|
16
|
+
self.model,
|
|
17
|
+
self.alphabet,
|
|
18
|
+
self.num_heads,
|
|
19
|
+
self.num_layers,
|
|
20
|
+
self.embedding_size,
|
|
21
|
+
self.prepend_bos,
|
|
22
|
+
self.append_eos,
|
|
23
|
+
) = self._initialize_model(self.model_name)
|
|
24
|
+
self.valid_tokens = set(self.alphabet.all_toks)
|
|
25
|
+
pepe.utils.check_input_tokens(
|
|
26
|
+
self.valid_tokens, self.sequences, self.model_name
|
|
27
|
+
)
|
|
28
|
+
self.special_tokens = self.get_special_tokens()
|
|
29
|
+
self.layers = self._load_layers(self.layers)
|
|
30
|
+
self.data_loader, self.max_length = self._load_data(
|
|
31
|
+
self.sequences, self.substring_dict
|
|
32
|
+
) # tokenize and batch sequences and update max_length
|
|
33
|
+
self._set_output_objects()
|
|
34
|
+
|
|
35
|
+
def _initialize_model(self, model_name):
|
|
36
|
+
"""Initialize the model, tokenizer"""
|
|
37
|
+
# Loading the pretrained model and alphabet for tokenization
|
|
38
|
+
logger.info("Loading model...")
|
|
39
|
+
# model, alphabet = pretrained.load_model_and_alphabet(model_name)
|
|
40
|
+
model, alphabet = pretrained.load_model_and_alphabet_hub(model_name)
|
|
41
|
+
model.eval() # Setting the model to evaluation mode
|
|
42
|
+
if not self.disable_special_tokens:
|
|
43
|
+
model.append_eos = True if not model_name.startswith("esm1") else False # type: ignore
|
|
44
|
+
model.prepend_bos = True # type: ignore
|
|
45
|
+
else:
|
|
46
|
+
model.append_eos = False # type: ignore
|
|
47
|
+
model.prepend_bos = False # type: ignore
|
|
48
|
+
|
|
49
|
+
num_heads = model.layers[0].self_attn.num_heads # type: ignore
|
|
50
|
+
num_layers = len(model.layers) # type: ignore
|
|
51
|
+
embedding_size = (
|
|
52
|
+
model.embed_tokens.embedding_dim # type: ignore
|
|
53
|
+
if model_name.startswith("esm1")
|
|
54
|
+
else model.embed_dim
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Moving the model to GPU if available for faster processing
|
|
58
|
+
if torch.cuda.is_available() and self.device == "cuda":
|
|
59
|
+
model = model.cuda()
|
|
60
|
+
logger.info("Transferred model to GPU")
|
|
61
|
+
else:
|
|
62
|
+
logger.info("No GPU available, using CPU")
|
|
63
|
+
return (
|
|
64
|
+
model,
|
|
65
|
+
alphabet,
|
|
66
|
+
num_heads,
|
|
67
|
+
num_layers,
|
|
68
|
+
embedding_size,
|
|
69
|
+
model.prepend_bos,
|
|
70
|
+
model.append_eos,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def get_special_tokens(self):
|
|
74
|
+
special_tokens = self.alphabet.all_special_tokens
|
|
75
|
+
special_token_ids = torch.tensor(
|
|
76
|
+
[self.alphabet.tok_to_idx[tok] for tok in special_tokens],
|
|
77
|
+
device=self.device,
|
|
78
|
+
dtype=torch.int8,
|
|
79
|
+
)
|
|
80
|
+
return special_token_ids
|
|
81
|
+
|
|
82
|
+
def _load_layers(self, layers):
|
|
83
|
+
if not layers:
|
|
84
|
+
layers = list(range(1, self.model.num_layers + 1)) # type: ignore
|
|
85
|
+
return layers
|
|
86
|
+
# Checking if the specified representation layers are valid
|
|
87
|
+
assert all(
|
|
88
|
+
-(self.model.num_layers + 1) <= i <= self.model.num_layers for i in layers # type: ignore
|
|
89
|
+
)
|
|
90
|
+
layers = [
|
|
91
|
+
(i + self.model.num_layers + 1) % (self.model.num_layers + 1) # type: ignore
|
|
92
|
+
for i in layers
|
|
93
|
+
]
|
|
94
|
+
return layers
|
|
95
|
+
|
|
96
|
+
def _load_data(self, sequences, substring_dict=None):
|
|
97
|
+
# Creating a dataset from the input fasta file
|
|
98
|
+
dataset = pepe.utils.ESMDataset(
|
|
99
|
+
sequences,
|
|
100
|
+
substring_dict,
|
|
101
|
+
self.context,
|
|
102
|
+
self.alphabet,
|
|
103
|
+
self.max_length,
|
|
104
|
+
self.prepend_bos, # type: ignore
|
|
105
|
+
self.append_eos, # type: ignore
|
|
106
|
+
)
|
|
107
|
+
# Generating batch indices based on token count
|
|
108
|
+
logger.info("Generating batches...")
|
|
109
|
+
batches = pepe.utils.TokenBudgetBatchSampler(dataset, self.batch_size)
|
|
110
|
+
# DataLoader to iterate through batches efficiently
|
|
111
|
+
data_loader = torch.utils.data.DataLoader(
|
|
112
|
+
dataset, batch_sampler=batches, collate_fn=dataset.safe_collate
|
|
113
|
+
)
|
|
114
|
+
logger.info("Data loaded")
|
|
115
|
+
# Getting the maximum sequence length from the dataset
|
|
116
|
+
max_length = dataset.get_max_encoded_length()
|
|
117
|
+
return data_loader, max_length
|
|
118
|
+
|
|
119
|
+
def _compute_outputs(
|
|
120
|
+
self,
|
|
121
|
+
model,
|
|
122
|
+
toks,
|
|
123
|
+
attention_mask,
|
|
124
|
+
return_embeddings,
|
|
125
|
+
return_contacts,
|
|
126
|
+
return_logits,
|
|
127
|
+
):
|
|
128
|
+
outputs = model(
|
|
129
|
+
toks,
|
|
130
|
+
repr_layers=self.layers,
|
|
131
|
+
return_contacts=return_contacts,
|
|
132
|
+
)
|
|
133
|
+
if return_logits:
|
|
134
|
+
logits = (
|
|
135
|
+
outputs["logits"]
|
|
136
|
+
.to(dtype=self._precision_to_dtype(self.precision, "torch"))
|
|
137
|
+
.permute(2, 0, 1)
|
|
138
|
+
.cpu()
|
|
139
|
+
) # permute to match the shape of the representations
|
|
140
|
+
torch.cuda.empty_cache()
|
|
141
|
+
else:
|
|
142
|
+
logits = None
|
|
143
|
+
|
|
144
|
+
if return_contacts:
|
|
145
|
+
attention_matrices = (
|
|
146
|
+
outputs["attentions"]
|
|
147
|
+
.to(dtype=self._precision_to_dtype(self.precision, "torch"))
|
|
148
|
+
.permute(1, 0, 2, 3, 4)
|
|
149
|
+
).cpu() # permute to match the shape of the representations
|
|
150
|
+
torch.cuda.empty_cache()
|
|
151
|
+
else:
|
|
152
|
+
attention_matrices = None
|
|
153
|
+
# Extracting layer representations and moving them to CPU
|
|
154
|
+
if return_embeddings:
|
|
155
|
+
representations = {
|
|
156
|
+
layer: t.to(
|
|
157
|
+
dtype=self._precision_to_dtype(self.precision, "torch")
|
|
158
|
+
).cpu()
|
|
159
|
+
for layer, t in outputs["representations"].items()
|
|
160
|
+
}
|
|
161
|
+
torch.cuda.empty_cache()
|
|
162
|
+
else:
|
|
163
|
+
representations = None
|
|
164
|
+
return logits, representations, attention_matrices
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import torch
|
|
4
|
+
import pepe.utils
|
|
5
|
+
from pepe.embedders.base_embedder import BaseEmbedder
|
|
6
|
+
from transformers import T5EncoderModel, T5Tokenizer
|
|
7
|
+
from transformers import RoFormerTokenizer, RoFormerModel
|
|
8
|
+
from transformers.models.roformer.modeling_roformer import (
|
|
9
|
+
RoFormerSinusoidalPositionalEmbedding,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Set max_split_size_mb
|
|
14
|
+
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128"
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger("src.embedders.huggingface_embedder")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class HuggingfaceEmbedder(BaseEmbedder):
|
|
20
|
+
def __init__(self, args):
|
|
21
|
+
super().__init__(args)
|
|
22
|
+
if self.return_logits:
|
|
23
|
+
logger.warning(
|
|
24
|
+
"Warning: Logits are not supported for this model. Setting to False."
|
|
25
|
+
)
|
|
26
|
+
self.return_logits = False
|
|
27
|
+
self.output_types.remove("logits")
|
|
28
|
+
|
|
29
|
+
def _load_layers(self, layers):
|
|
30
|
+
"""Check if the specified representation layers are valid."""
|
|
31
|
+
if not layers:
|
|
32
|
+
layers = list(range(1, self.model.config.num_hidden_layers + 1)) # type: ignore
|
|
33
|
+
return layers
|
|
34
|
+
assert all(
|
|
35
|
+
-(self.model.config.num_hidden_layers + 1) # type: ignore
|
|
36
|
+
<= i
|
|
37
|
+
<= self.model.config.num_hidden_layers # type: ignore
|
|
38
|
+
for i in layers
|
|
39
|
+
)
|
|
40
|
+
layers = [
|
|
41
|
+
(i + self.model.config.num_hidden_layers + 1) # type: ignore
|
|
42
|
+
% (self.model.config.num_hidden_layers + 1) # type: ignore
|
|
43
|
+
for i in layers
|
|
44
|
+
]
|
|
45
|
+
return layers
|
|
46
|
+
|
|
47
|
+
def _load_data(self, sequences, substring_dict):
|
|
48
|
+
"""Tokenize sequences and create a DataLoader."""
|
|
49
|
+
# Tokenize sequences
|
|
50
|
+
dataset = pepe.utils.HuggingFaceDataset(
|
|
51
|
+
sequences,
|
|
52
|
+
substring_dict,
|
|
53
|
+
self.context,
|
|
54
|
+
self.tokenizer, # type: ignore
|
|
55
|
+
self.max_length,
|
|
56
|
+
add_special_tokens=not self.disable_special_tokens,
|
|
57
|
+
)
|
|
58
|
+
logger.info("Batching sequences...")
|
|
59
|
+
batch_sampler = pepe.utils.TokenBudgetBatchSampler(
|
|
60
|
+
dataset=dataset, token_budget=self.batch_size
|
|
61
|
+
)
|
|
62
|
+
data_loader = torch.utils.data.DataLoader(
|
|
63
|
+
dataset, batch_sampler=batch_sampler, collate_fn=dataset.safe_collate
|
|
64
|
+
)
|
|
65
|
+
max_length = dataset.get_max_encoded_length()
|
|
66
|
+
logger.info("Finished tokenizing and batching sequences")
|
|
67
|
+
|
|
68
|
+
return data_loader, max_length
|
|
69
|
+
|
|
70
|
+
def _compute_outputs(
|
|
71
|
+
self,
|
|
72
|
+
model,
|
|
73
|
+
toks,
|
|
74
|
+
attention_mask,
|
|
75
|
+
return_embeddings,
|
|
76
|
+
return_contacts,
|
|
77
|
+
return_logits=False,
|
|
78
|
+
):
|
|
79
|
+
outputs = model(
|
|
80
|
+
input_ids=toks,
|
|
81
|
+
attention_mask=attention_mask,
|
|
82
|
+
output_hidden_states=return_embeddings,
|
|
83
|
+
output_attentions=return_contacts,
|
|
84
|
+
)
|
|
85
|
+
if return_contacts:
|
|
86
|
+
attention_matrices = (
|
|
87
|
+
torch.stack(outputs.attentions) # type: ignore
|
|
88
|
+
.to(self._precision_to_dtype(self.precision, "torch")) # type: ignore
|
|
89
|
+
.cpu()
|
|
90
|
+
) # stack attention matrices across layers
|
|
91
|
+
torch.cuda.empty_cache()
|
|
92
|
+
else:
|
|
93
|
+
attention_matrices = None
|
|
94
|
+
if return_embeddings:
|
|
95
|
+
representations = {
|
|
96
|
+
layer: outputs.hidden_states[layer]
|
|
97
|
+
.to(
|
|
98
|
+
self._precision_to_dtype(self.precision, "torch"),
|
|
99
|
+
)
|
|
100
|
+
.cpu()
|
|
101
|
+
for layer in self.layers # type: ignore
|
|
102
|
+
}
|
|
103
|
+
torch.cuda.empty_cache()
|
|
104
|
+
else:
|
|
105
|
+
representations = None
|
|
106
|
+
logits = None # Model doesn't return logits
|
|
107
|
+
return logits, representations, attention_matrices
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Antiberta2Embedder(HuggingfaceEmbedder):
|
|
111
|
+
def __init__(self, args):
|
|
112
|
+
super().__init__(args)
|
|
113
|
+
self.sequences = pepe.utils.fasta_to_dict(args.fasta_path)
|
|
114
|
+
self.num_sequences = len(self.sequences)
|
|
115
|
+
(
|
|
116
|
+
self.model,
|
|
117
|
+
self.tokenizer,
|
|
118
|
+
self.num_heads,
|
|
119
|
+
self.num_layers,
|
|
120
|
+
self.embedding_size,
|
|
121
|
+
) = self._initialize_model(self.model_link)
|
|
122
|
+
self.valid_tokens = set(self.tokenizer.get_vocab().keys())
|
|
123
|
+
pepe.utils.check_input_tokens(
|
|
124
|
+
self.valid_tokens, self.sequences, self.model_name
|
|
125
|
+
)
|
|
126
|
+
self.special_tokens = torch.tensor(
|
|
127
|
+
self.tokenizer.all_special_ids, device=self.device, dtype=torch.int8
|
|
128
|
+
)
|
|
129
|
+
self.layers = self._load_layers(self.layers)
|
|
130
|
+
self.data_loader, self.max_length = self._load_data(
|
|
131
|
+
self.sequences, self.substring_dict
|
|
132
|
+
)
|
|
133
|
+
self._set_output_objects()
|
|
134
|
+
assert self.max_length <= 256, "AntiBERTa2 only supports max_length <= 256"
|
|
135
|
+
|
|
136
|
+
def _initialize_model(self, model_link="alchemab/antiberta2-cssp"):
|
|
137
|
+
"""Initialize the model, tokenizer, and device."""
|
|
138
|
+
if torch.cuda.is_available() and self.device == "cuda":
|
|
139
|
+
device = torch.device("cuda")
|
|
140
|
+
logger.info("Transferred model to GPU")
|
|
141
|
+
else:
|
|
142
|
+
device = torch.device("cpu")
|
|
143
|
+
logger.info("No GPU available, using CPU")
|
|
144
|
+
tokenizer = RoFormerTokenizer.from_pretrained(model_link, use_fast=True)
|
|
145
|
+
model = RoFormerModel.from_pretrained(model_link).to(device) # type: ignore
|
|
146
|
+
model.eval()
|
|
147
|
+
num_heads = model.config.num_attention_heads
|
|
148
|
+
num_layers = model.config.num_hidden_layers
|
|
149
|
+
embedding_size = model.config.hidden_size
|
|
150
|
+
return model, tokenizer, num_heads, num_layers, embedding_size
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class T5Embedder(HuggingfaceEmbedder):
|
|
154
|
+
def __init__(self, args):
|
|
155
|
+
super().__init__(args)
|
|
156
|
+
self.sequences = self.fasta_to_dict(args.fasta_path) # type: ignore
|
|
157
|
+
self.num_sequences = len(self.sequences)
|
|
158
|
+
(
|
|
159
|
+
self.model,
|
|
160
|
+
self.tokenizer,
|
|
161
|
+
self.num_heads,
|
|
162
|
+
self.num_layers,
|
|
163
|
+
self.embedding_size,
|
|
164
|
+
) = self._initialize_model(self.model_link)
|
|
165
|
+
self.valid_tokens = self.get_valid_tokens()
|
|
166
|
+
pepe.utils.check_input_tokens(
|
|
167
|
+
self.valid_tokens, self.sequences, self.model_name
|
|
168
|
+
)
|
|
169
|
+
self.special_tokens = torch.tensor(
|
|
170
|
+
self.tokenizer.all_special_ids, device=self.device, dtype=torch.int8
|
|
171
|
+
)
|
|
172
|
+
self.layers = self._load_layers(self.layers)
|
|
173
|
+
self.data_loader, self.max_length = self._load_data(
|
|
174
|
+
self.sequences, self.substring_dict
|
|
175
|
+
)
|
|
176
|
+
self._set_output_objects()
|
|
177
|
+
|
|
178
|
+
def get_valid_tokens(self):
|
|
179
|
+
valid_tokens = set(
|
|
180
|
+
k[1:] if k.startswith("▁") else k
|
|
181
|
+
for k in set(self.tokenizer.get_vocab().keys())
|
|
182
|
+
)
|
|
183
|
+
return valid_tokens
|
|
184
|
+
|
|
185
|
+
def _initialize_model(self, model_link="Rostlab/prot_t5_xl_half_uniref50-enc"):
|
|
186
|
+
"""Initialize the model, tokenizer, and device."""
|
|
187
|
+
|
|
188
|
+
if torch.cuda.is_available() and self.device == "cuda":
|
|
189
|
+
device = torch.device("cuda")
|
|
190
|
+
logger.info("Transferred model to GPU")
|
|
191
|
+
else:
|
|
192
|
+
device = torch.device("cpu")
|
|
193
|
+
logger.info("No GPU available, using CPU")
|
|
194
|
+
tokenizer = T5Tokenizer.from_pretrained(model_link, use_fast=True)
|
|
195
|
+
model = T5EncoderModel.from_pretrained(model_link).to(device) # type: ignore
|
|
196
|
+
model.eval()
|
|
197
|
+
num_heads = model.config.num_heads
|
|
198
|
+
num_layers = model.config.num_layers
|
|
199
|
+
embedding_size = model.config.hidden_size
|
|
200
|
+
return model, tokenizer, num_heads, num_layers, embedding_size
|
pepe/model_selecter.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from pepe.embedders.esm_embedder import ESMEmbedder
|
|
2
|
+
from pepe.embedders.huggingface_embedder import T5Embedder, Antiberta2Embedder
|
|
3
|
+
from pepe.embedders.custom_embedder import CustomEmbedder
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def select_model(model_name):
|
|
10
|
+
if "esm2" in model_name.lower():
|
|
11
|
+
return ESMEmbedder
|
|
12
|
+
elif "esm1" in model_name.lower():
|
|
13
|
+
return ESMEmbedder
|
|
14
|
+
# elif "antiberta2" in model_name.lower() and model_name.startswith("alchemab"):
|
|
15
|
+
# return Antiberta2Embedder
|
|
16
|
+
# elif "t5" in model_name.lower() and model_name.startswith("Rostlab"):
|
|
17
|
+
# return T5Embedder
|
|
18
|
+
elif (
|
|
19
|
+
model_name.endswith(".pt")
|
|
20
|
+
or model_name.endswith(".pth")
|
|
21
|
+
or model_name.startswith("custom:")
|
|
22
|
+
or (
|
|
23
|
+
os.path.exists(model_name)
|
|
24
|
+
and (os.path.isfile(model_name) or os.path.isdir(model_name))
|
|
25
|
+
)
|
|
26
|
+
):
|
|
27
|
+
return CustomEmbedder
|
|
28
|
+
elif "/" in model_name:
|
|
29
|
+
# Assume it's a Hugging Face model (username/model-name format)
|
|
30
|
+
# Try to determine the architecture automatically
|
|
31
|
+
try:
|
|
32
|
+
from transformers import AutoConfig
|
|
33
|
+
|
|
34
|
+
config = AutoConfig.from_pretrained(model_name)
|
|
35
|
+
model_type = config.model_type.lower()
|
|
36
|
+
|
|
37
|
+
if model_type in ["t5", "mt5"]:
|
|
38
|
+
return T5Embedder
|
|
39
|
+
elif model_type in ["roformer"]:
|
|
40
|
+
return Antiberta2Embedder
|
|
41
|
+
elif model_type in ["bert"]:
|
|
42
|
+
# For BERT-like models, we could potentially use a generic embedder
|
|
43
|
+
# but for now, suggest using CustomEmbedder or creating a specific one
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"BERT-like models are not yet directly supported. Consider using a PyTorch version of the model with CustomEmbedder."
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
# For other architectures, you might want to add more specific embedders
|
|
49
|
+
# or use a generic HuggingfaceEmbedder
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"Model architecture '{model_type}' not yet supported for custom Hugging Face models"
|
|
52
|
+
)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
# Check if it's a Keras/TensorFlow model
|
|
55
|
+
error_msg = str(e)
|
|
56
|
+
if "Unrecognized model" in error_msg or "model_type" in error_msg:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"Model {model_name} appears to be a Keras/TensorFlow model or has an unsupported architecture. EmbedAIRR currently supports PyTorch models only. Consider using a PyTorch version or converting the model."
|
|
59
|
+
)
|
|
60
|
+
else:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"Could not determine model architecture for {model_name}: {e}"
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError(f"Model {model_name} not supported")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
supported_models = [
|
|
69
|
+
# ESM models
|
|
70
|
+
"esm1_t34_670M_UR50S",
|
|
71
|
+
"esm1_t34_670M_UR50D",
|
|
72
|
+
"esm1_t34_670M_UR100",
|
|
73
|
+
"esm1_t12_85M_UR50S",
|
|
74
|
+
"esm1_t6_43M_UR50S",
|
|
75
|
+
"esm1b_t33_650M_UR50S",
|
|
76
|
+
#'esm_msa1_t12_100M_UR50S',
|
|
77
|
+
#'esm_msa1b_t12_100M_UR50S',
|
|
78
|
+
"esm1v_t33_650M_UR90S_1",
|
|
79
|
+
"esm1v_t33_650M_UR90S_2",
|
|
80
|
+
"esm1v_t33_650M_UR90S_3",
|
|
81
|
+
"esm1v_t33_650M_UR90S_4",
|
|
82
|
+
"esm1v_t33_650M_UR90S_5",
|
|
83
|
+
#'esm_if1_gvp4_t16_142M_UR50',
|
|
84
|
+
"esm2_t6_8M_UR50D",
|
|
85
|
+
"esm2_t12_35M_UR50D",
|
|
86
|
+
"esm2_t30_150M_UR50D",
|
|
87
|
+
"esm2_t33_650M_UR50D",
|
|
88
|
+
"esm2_t36_3B_UR50D",
|
|
89
|
+
"esm2_t48_15B_UR50D",
|
|
90
|
+
# Pre-defined Hugging Face models
|
|
91
|
+
"Rostlab/prot_t5_xl_half_uniref50-enc",
|
|
92
|
+
"Rostlab/ProstT5",
|
|
93
|
+
"alchemab/antiberta2-cssp",
|
|
94
|
+
"alchemab/antiberta2",
|
|
95
|
+
# Custom models examples:
|
|
96
|
+
# - PyTorch models: "/path/to/model.pt", "/path/to/model_directory/", "custom:/path/to/model.pt"
|
|
97
|
+
# - Hugging Face models: "username/model-name", "./local_hf_model"
|
|
98
|
+
# - See documentation for details on custom model requirements
|
|
99
|
+
]
|
pepe/parse_arguments.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from pepe.model_selecter import supported_models
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def str2bool(value):
|
|
6
|
+
if isinstance(value, bool):
|
|
7
|
+
return value
|
|
8
|
+
if value.lower() in ("true", "t", "yes", "y", "1"):
|
|
9
|
+
return True
|
|
10
|
+
elif value.lower() in ("false", "f", "no", "n", "0"):
|
|
11
|
+
return False
|
|
12
|
+
else:
|
|
13
|
+
raise argparse.ArgumentTypeError("Boolean value expected.")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def str2ints(value):
|
|
17
|
+
"""Convert a string to a list of integers."""
|
|
18
|
+
if isinstance(value, str):
|
|
19
|
+
if value.lower() == "all":
|
|
20
|
+
return None
|
|
21
|
+
elif value.lower() == "last":
|
|
22
|
+
return [-1]
|
|
23
|
+
else:
|
|
24
|
+
try:
|
|
25
|
+
return [int(x) for x in value.split(" ")]
|
|
26
|
+
except ValueError:
|
|
27
|
+
raise argparse.ArgumentTypeError(
|
|
28
|
+
"Invalid input. Expected integer(s) or spaced list of integers or 'all' or 'last'."
|
|
29
|
+
)
|
|
30
|
+
elif isinstance(value, int):
|
|
31
|
+
return [value]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Parsing command-line arguments for input and output file paths
|
|
35
|
+
def parse_arguments():
|
|
36
|
+
"""Parse command-line arguments for input and output file paths."""
|
|
37
|
+
parser = argparse.ArgumentParser(description="Input path")
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"--experiment_name",
|
|
40
|
+
type=str,
|
|
41
|
+
default=None,
|
|
42
|
+
help="Name of the experiment. Will be used to name the output files. If not provided, the output files will be named after the input file.",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"--model_name",
|
|
46
|
+
type=str,
|
|
47
|
+
required=True,
|
|
48
|
+
help="Model name or path to custom model. For custom models, use path to .pt/.pth file or directory containing model files. For predefined models, use model name from supported list.",
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--tokenizer_from",
|
|
52
|
+
type=str,
|
|
53
|
+
default=None,
|
|
54
|
+
help="Huggingface address of the tokenizer to use. If not provided, will use the tokenizer from the model. If using a custom model, provide the path to the tokenizer directory.",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--fasta_path",
|
|
58
|
+
type=str,
|
|
59
|
+
required=True,
|
|
60
|
+
help="Path to the input FASTA file. Required. If no experiment name is provided, the output files will be named after the input file.",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--output_path",
|
|
64
|
+
type=str,
|
|
65
|
+
required=True,
|
|
66
|
+
help="Directory for output files \n Will generate a subdirectory for outputs of each output_type.\n Will output multiple files if multiple layers are specified with '--layers'. Output file is a single tensor or a list of tensors when --pooling is False.",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--substring_path",
|
|
70
|
+
default=None,
|
|
71
|
+
type=str,
|
|
72
|
+
help=" Path to a CSV file with columns 'sequence_id' and 'substring'. Only required when selecting 'substring' embedding option.",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--context",
|
|
76
|
+
default=0,
|
|
77
|
+
type=int,
|
|
78
|
+
help="Only specify when including 'substring_pooling' in '--extract_embeddings' option. Number of amino acids to include before and after the substring sequence. Default is 0.",
|
|
79
|
+
)
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"--layers",
|
|
82
|
+
type=str2ints,
|
|
83
|
+
nargs="*",
|
|
84
|
+
default=[[-1]],
|
|
85
|
+
help="Representation layers to extract from the model. Default is the last layer. Example: argument '--layers -1 6' will output the last layer and the sixth layer.",
|
|
86
|
+
)
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--extract_embeddings",
|
|
89
|
+
choices=[
|
|
90
|
+
"per_token",
|
|
91
|
+
"mean_pooled",
|
|
92
|
+
"substring_pooled",
|
|
93
|
+
"attention_head",
|
|
94
|
+
"attention_layer",
|
|
95
|
+
"attention_model",
|
|
96
|
+
"logits",
|
|
97
|
+
],
|
|
98
|
+
default=["mean_pooled"],
|
|
99
|
+
nargs="+",
|
|
100
|
+
help="Set the embedding return types. Choose one or more from: 'per_token', 'mean_pooled', 'substring_pooled', 'attention_head', 'attention_layer', 'attention_model' and 'logits' (experimental). Default is 'pooled'.",
|
|
101
|
+
)
|
|
102
|
+
parser.add_argument(
|
|
103
|
+
"--batch_size",
|
|
104
|
+
type=int,
|
|
105
|
+
default=1024,
|
|
106
|
+
help="Number of tokens (not sequences!) per batch. Default is 1024.",
|
|
107
|
+
)
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
"--discard_padding",
|
|
110
|
+
action="store_true",
|
|
111
|
+
help="Discard padding tokens from per_token embeddings output. Not compatible with --streaming_output Default is False.",
|
|
112
|
+
)
|
|
113
|
+
parser.add_argument(
|
|
114
|
+
"--max_length",
|
|
115
|
+
default="max_length",
|
|
116
|
+
help="Length to which sequences will be padded. Default is longest sequence.",
|
|
117
|
+
)
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
"--streaming_output",
|
|
120
|
+
type=str2bool,
|
|
121
|
+
choices=[True, False],
|
|
122
|
+
default=True,
|
|
123
|
+
help="Preallocate output files and concurrently write embeddings to disk in batches while computing embeddings. Default is True.",
|
|
124
|
+
)
|
|
125
|
+
parser.add_argument(
|
|
126
|
+
"--num_workers",
|
|
127
|
+
type=int,
|
|
128
|
+
default=8,
|
|
129
|
+
help="Number of workers for asynchronous data writing. Only relevant when --batch_writing is enabled. Default is 8.",
|
|
130
|
+
)
|
|
131
|
+
parser.add_argument(
|
|
132
|
+
"--disable_special_tokens",
|
|
133
|
+
type=str2bool,
|
|
134
|
+
choices=[True, False],
|
|
135
|
+
default=False,
|
|
136
|
+
help="Disable special tokens in the model. Default is False.",
|
|
137
|
+
)
|
|
138
|
+
parser.add_argument(
|
|
139
|
+
"--flatten",
|
|
140
|
+
type=str2bool,
|
|
141
|
+
choices=[True, False],
|
|
142
|
+
default=False,
|
|
143
|
+
help="Flatten the output tensors. Default is False.",
|
|
144
|
+
)
|
|
145
|
+
parser.add_argument(
|
|
146
|
+
"--flush_batches_after",
|
|
147
|
+
type=int,
|
|
148
|
+
default=128,
|
|
149
|
+
help="Size (in MB) of outputs to accumulate in RAM per worker (--num_workers) before flushing to disk. Default is 128.",
|
|
150
|
+
)
|
|
151
|
+
parser.add_argument(
|
|
152
|
+
"--precision",
|
|
153
|
+
type=str,
|
|
154
|
+
default="32",
|
|
155
|
+
choices=["float16", "16", "half", "float32", "32", "full"],
|
|
156
|
+
help="Precision of the output data. Inference during embedding is not affected. Default is 'float32'.",
|
|
157
|
+
)
|
|
158
|
+
parser.add_argument(
|
|
159
|
+
"--device",
|
|
160
|
+
type=str.lower,
|
|
161
|
+
default="cuda",
|
|
162
|
+
choices=["cuda", "cpu"],
|
|
163
|
+
help="Device to run the model on. Default is 'cuda'.",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
args = parser.parse_args()
|
|
167
|
+
return args
|