pepe-cli 1.0.3__tar.gz → 1.0.4__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.
Files changed (37) hide show
  1. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/MANIFEST.in +6 -0
  2. {pepe_cli-1.0.3/src/pepe_cli.egg-info → pepe_cli-1.0.4}/PKG-INFO +1 -1
  3. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/__init__.py +1 -1
  4. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/embedders/custom_embedder.py +6 -106
  5. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/embedders/huggingface_embedder.py +171 -4
  6. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/utils.py +155 -26
  7. {pepe_cli-1.0.3 → pepe_cli-1.0.4/src/pepe_cli.egg-info}/PKG-INFO +1 -1
  8. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/tests/test_run.py +1 -1
  9. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/LICENSE +0 -0
  10. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/README.md +0 -0
  11. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/create_example_custom_model.py +0 -0
  12. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/example_protein_model/config.json +0 -0
  13. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/example_protein_model/pytorch_model.pt +0 -0
  14. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/example_protein_model/special_tokens_map.json +0 -0
  15. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/example_protein_model/tokenizer_config.json +0 -0
  16. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/example_protein_model/vocab.json +0 -0
  17. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/example_protein_model/vocab.txt +0 -0
  18. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/example_sequences.fasta +0 -0
  19. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/custom_model/example_substring.csv +0 -0
  20. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/embedding_options.md +0 -0
  21. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/examples/model_selection.md +0 -0
  22. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/pyproject.toml +0 -0
  23. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/requirements.txt +0 -0
  24. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/setup.cfg +0 -0
  25. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/setup.py +0 -0
  26. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/__main__.py +0 -0
  27. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/embedders/__init__.py +0 -0
  28. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/embedders/base_embedder.py +0 -0
  29. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/embedders/esm_embedder.py +0 -0
  30. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/model_selecter.py +0 -0
  31. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe/parse_arguments.py +0 -0
  32. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe_cli.egg-info/SOURCES.txt +0 -0
  33. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe_cli.egg-info/dependency_links.txt +0 -0
  34. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe_cli.egg-info/entry_points.txt +0 -0
  35. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe_cli.egg-info/requires.txt +0 -0
  36. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/pepe_cli.egg-info/top_level.txt +0 -0
  37. {pepe_cli-1.0.3 → pepe_cli-1.0.4}/src/tests/__init__.py +0 -0
@@ -8,3 +8,9 @@ recursive-exclude * __pycache__
8
8
  recursive-exclude * *.py[co]
9
9
  recursive-exclude * *.so
10
10
  recursive-exclude * .DS_Store
11
+ recursive-exclude .github *
12
+ recursive-exclude .git *
13
+ recursive-exclude .venv *
14
+ recursive-exclude * .pytest_cache
15
+ recursive-exclude * .coverage
16
+ exclude .gitignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pepe-cli
3
- Version: 1.0.3
3
+ Version: 1.0.4
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
@@ -2,7 +2,7 @@ import logging
2
2
  import sys
3
3
 
4
4
  # Package metadata - single source of truth
5
- __version__ = "1.0.3"
5
+ __version__ = "1.0.4"
6
6
  __package_name__ = "pepe-cli"
7
7
  __module_name__ = "pepe"
8
8
  __author__ = "Jahn Zhong"
@@ -53,8 +53,9 @@ class CustomEmbedder(BaseEmbedder):
53
53
 
54
54
  # Set up tokenizer and validate tokens
55
55
  self.valid_tokens = self._get_valid_tokens()
56
+ self.bracket_type = pepe.utils.get_bracket_type(self.tokenizer)
56
57
  pepe.utils.check_input_tokens(
57
- self.valid_tokens, self.sequences, self.model_name
58
+ self.valid_tokens, self.sequences, self.model_name,
58
59
  )
59
60
 
60
61
  # Set up special tokens
@@ -65,7 +66,7 @@ class CustomEmbedder(BaseEmbedder):
65
66
 
66
67
  # Load and tokenize data
67
68
  self.data_loader, self.max_length = self._load_data(
68
- self.sequences, self.substring_dict
69
+ self.sequences, self.substring_dict, self.bracket_type
69
70
  )
70
71
 
71
72
  # Initialize output objects
@@ -297,13 +298,14 @@ class CustomEmbedder(BaseEmbedder):
297
298
  layers = [(i + self.num_layers + 1) % (self.num_layers + 1) for i in layers]
298
299
  return layers
299
300
 
300
- def _load_data(self, sequences, substring_dict=None):
301
+ def _load_data(self, sequences, substring_dict=None, bracket_type=None):
301
302
  """Load and tokenize sequences."""
302
303
  # Create dataset
303
- dataset = CustomDataset(
304
+ dataset = pepe.utils.CustomDataset(
304
305
  sequences,
305
306
  substring_dict,
306
307
  self.context,
308
+ bracket_type,
307
309
  self.tokenizer,
308
310
  self.max_length,
309
311
  add_special_tokens=not self.disable_special_tokens,
@@ -543,105 +545,3 @@ class DefaultProteinTokenizer:
543
545
  }
544
546
 
545
547
  return tokenized[0] if len(tokenized) == 1 else tokenized
546
-
547
-
548
- class CustomDataset(pepe.utils.SequenceDictDataset):
549
- """
550
- Dataset class for custom embedder.
551
- """
552
-
553
- def __init__(
554
- self,
555
- sequences,
556
- substring_dict,
557
- context,
558
- tokenizer,
559
- max_length,
560
- add_special_tokens=True,
561
- ):
562
- super().__init__(sequences, substring_dict, context)
563
- self.tokenizer = tokenizer
564
- self.max_length = max_length
565
- self.add_special_tokens = add_special_tokens
566
- self.pad_token_id = getattr(tokenizer, "pad_token_id", 0)
567
-
568
- # Encode sequences
569
- self.encoded_data = self._encode_sequences(
570
- self.data, tokenizer, max_length, add_special_tokens
571
- )
572
-
573
- # Handle substring sequences if provided
574
- if self.substring_dict:
575
- logger.info("Tokenizing substrings...")
576
- self.encoded_substring_data = self._encode_sequences(
577
- self.filtered_substring_data,
578
- tokenizer,
579
- "max_length",
580
- add_special_tokens=False,
581
- )
582
- self.substring_masks = self._get_substring_masks()
583
-
584
- def _encode_sequences(self, data, tokenizer, max_length, add_special_tokens):
585
- """Encode sequences using the tokenizer."""
586
- labels, strs = zip(*data)
587
-
588
- # Convert max_length to int if needed
589
- if max_length == "max_length":
590
- max_length = max(len(s) for s in strs) + (2 if add_special_tokens else 0)
591
-
592
- encoded_sequences = []
593
- for label, seq in zip(labels, strs):
594
- # Tokenize the sequence
595
- tokens = tokenizer(
596
- seq,
597
- truncation=True,
598
- padding="max_length",
599
- max_length=max_length,
600
- add_special_tokens=add_special_tokens,
601
- return_tensors="pt",
602
- )
603
-
604
- encoded_sequences.append(
605
- (
606
- label,
607
- seq,
608
- tokens["input_ids"].squeeze(0),
609
- tokens["attention_mask"].squeeze(0),
610
- )
611
- )
612
-
613
- return encoded_sequences
614
-
615
- def get_max_encoded_length(self):
616
- """Get maximum encoded sequence length."""
617
- return max(len(toks) for _, _, toks, _ in self.encoded_data)
618
-
619
- def safe_collate(self, batch):
620
- """Collate function for data loader."""
621
- if self.substring_dict:
622
- labels, seqs, toks, attn_masks, substring_masks = zip(*batch)
623
- return (
624
- list(labels),
625
- list(seqs),
626
- torch.stack(toks),
627
- torch.stack(attn_masks),
628
- torch.stack(substring_masks),
629
- )
630
- else:
631
- labels, seqs, toks, attn_masks = zip(*batch)
632
- return (
633
- list(labels),
634
- list(seqs),
635
- torch.stack(toks),
636
- torch.stack(attn_masks),
637
- None,
638
- )
639
-
640
- def __getitem__(self, idx):
641
- """Get item from dataset."""
642
- labels, seqs, toks, attn_mask = self.encoded_data[idx]
643
- if self.substring_dict:
644
- substring_masks = self.substring_masks[idx]
645
- return labels, seqs, toks, attn_mask, substring_masks
646
- else:
647
- return labels, seqs, toks, attn_mask
@@ -14,6 +14,7 @@ def _import_transformers():
14
14
  from transformers.models.roformer.modeling_roformer import (
15
15
  RoFormerSinusoidalPositionalEmbedding,
16
16
  )
17
+ from transformers import AutoModel, AutoTokenizer, AutoModelForCausalLM
17
18
 
18
19
  return (
19
20
  T5EncoderModel,
@@ -21,6 +22,9 @@ def _import_transformers():
21
22
  RoFormerTokenizer,
22
23
  RoFormerModel,
23
24
  RoFormerSinusoidalPositionalEmbedding,
25
+ AutoModel,
26
+ AutoTokenizer,
27
+ AutoModelForCausalLM,
24
28
  )
25
29
  except ImportError as e:
26
30
  logger.error(f"Failed to import transformers: {e}")
@@ -63,13 +67,14 @@ class HuggingfaceEmbedder(BaseEmbedder):
63
67
  ]
64
68
  return layers
65
69
 
66
- def _load_data(self, sequences, substring_dict):
70
+ def _load_data(self, sequences, substring_dict, bracket_type):
67
71
  """Tokenize sequences and create a DataLoader."""
68
72
  # Tokenize sequences
69
73
  dataset = pepe.utils.HuggingFaceDataset(
70
74
  sequences,
71
75
  substring_dict,
72
76
  self.context,
77
+ bracket_type,
73
78
  self.tokenizer, # type: ignore
74
79
  self.max_length,
75
80
  add_special_tokens=not self.disable_special_tokens,
@@ -139,6 +144,7 @@ class Antiberta2Embedder(HuggingfaceEmbedder):
139
144
  self.embedding_size,
140
145
  ) = self._initialize_model(self.model_link)
141
146
  self.valid_tokens = set(self.tokenizer.get_vocab().keys())
147
+ self.bracket_type = pepe.utils.get_bracket_type(self.tokenizer)
142
148
  pepe.utils.check_input_tokens(
143
149
  self.valid_tokens, self.sequences, self.model_name
144
150
  )
@@ -147,7 +153,7 @@ class Antiberta2Embedder(HuggingfaceEmbedder):
147
153
  )
148
154
  self.layers = self._load_layers(self.layers)
149
155
  self.data_loader, self.max_length = self._load_data(
150
- self.sequences, self.substring_dict
156
+ self.sequences, self.substring_dict, self.bracket_type
151
157
  )
152
158
  self._set_output_objects()
153
159
  assert self.max_length <= 256, "AntiBERTa2 only supports max_length <= 256"
@@ -168,6 +174,9 @@ class Antiberta2Embedder(HuggingfaceEmbedder):
168
174
  RoFormerTokenizer,
169
175
  RoFormerModel,
170
176
  RoFormerSinusoidalPositionalEmbedding,
177
+ AutoModel,
178
+ AutoTokenizer,
179
+ AutoModelForCausalLM,
171
180
  ) = _import_transformers()
172
181
 
173
182
  tokenizer = RoFormerTokenizer.from_pretrained(model_link, use_fast=True)
@@ -192,15 +201,16 @@ class T5Embedder(HuggingfaceEmbedder):
192
201
  self.embedding_size,
193
202
  ) = self._initialize_model(self.model_link)
194
203
  self.valid_tokens = self.get_valid_tokens()
204
+ self.bracket_type = pepe.utils.get_bracket_type(self.tokenizer)
195
205
  pepe.utils.check_input_tokens(
196
- self.valid_tokens, self.sequences, self.model_name
206
+ self.valid_tokens, self.sequences, self.model_name, self.bracket_type
197
207
  )
198
208
  self.special_tokens = torch.tensor(
199
209
  self.tokenizer.all_special_ids, device=self.device, dtype=torch.int8
200
210
  )
201
211
  self.layers = self._load_layers(self.layers)
202
212
  self.data_loader, self.max_length = self._load_data(
203
- self.sequences, self.substring_dict
213
+ self.sequences, self.substring_dict, self.bracket_type
204
214
  )
205
215
  self._set_output_objects()
206
216
 
@@ -228,6 +238,9 @@ class T5Embedder(HuggingfaceEmbedder):
228
238
  RoFormerTokenizer,
229
239
  RoFormerModel,
230
240
  RoFormerSinusoidalPositionalEmbedding,
241
+ AutoModel,
242
+ AutoTokenizer,
243
+ AutoModelForCausalLM,
231
244
  ) = _import_transformers()
232
245
 
233
246
  tokenizer = T5Tokenizer.from_pretrained(model_link, use_fast=True)
@@ -237,3 +250,157 @@ class T5Embedder(HuggingfaceEmbedder):
237
250
  num_layers = model.config.num_layers
238
251
  embedding_size = model.config.hidden_size
239
252
  return model, tokenizer, num_heads, num_layers, embedding_size
253
+
254
+
255
+ class GenericHuggingFaceEmbedder(HuggingfaceEmbedder):
256
+ """Generic HuggingFace embedder that can handle models with unknown architectures using AutoModel and AutoTokenizer."""
257
+
258
+ def __init__(self, args):
259
+ super().__init__(args)
260
+ self.sequences = pepe.utils.fasta_to_dict(args.fasta_path)
261
+ self.num_sequences = len(self.sequences)
262
+ (
263
+ self.model,
264
+ self.tokenizer,
265
+ self.num_heads,
266
+ self.num_layers,
267
+ self.embedding_size,
268
+ ) = self._initialize_model(self.model_link)
269
+ self.valid_tokens = self._get_valid_tokens()
270
+ self.bracket_type = pepe.utils.get_bracket_type(self.tokenizer)
271
+ pepe.utils.check_input_tokens(
272
+ self.valid_tokens, self.sequences, self.model_name
273
+ )
274
+ self.special_tokens = torch.tensor(
275
+ self.tokenizer.all_special_ids, device=self.device, dtype=torch.int8
276
+ )
277
+ self.layers = self._load_layers(self.layers)
278
+ self.data_loader, self.max_length = self._load_data(
279
+ self.sequences, self.substring_dict, self.bracket_type
280
+ )
281
+ self._set_output_objects()
282
+
283
+ def _get_valid_tokens(self):
284
+ """Get valid tokens from the tokenizer."""
285
+ if hasattr(self.tokenizer, "get_vocab"):
286
+ vocab = self.tokenizer.get_vocab()
287
+ # Handle different tokenizer types
288
+ if hasattr(self.tokenizer, "decoder") and self.tokenizer.decoder:
289
+ # T5-style tokenizer
290
+ valid_tokens = set(
291
+ k[1:] if k.startswith("▁") else k for k in vocab.keys()
292
+ )
293
+ else:
294
+ # Standard tokenizer
295
+ valid_tokens = set(vocab.keys())
296
+ return valid_tokens
297
+ return set()
298
+
299
+ def _initialize_model(self, model_link):
300
+ """Initialize the model, tokenizer, and device using AutoModel and AutoTokenizer."""
301
+ if torch.cuda.is_available() and self.device == "cuda":
302
+ device = torch.device("cuda")
303
+ logger.info("Transferred model to GPU")
304
+ else:
305
+ device = torch.device("cpu")
306
+ logger.info("No GPU available, using CPU")
307
+
308
+ # Lazy import transformers components
309
+ (
310
+ T5EncoderModel,
311
+ T5Tokenizer,
312
+ RoFormerTokenizer,
313
+ RoFormerModel,
314
+ RoFormerSinusoidalPositionalEmbedding,
315
+ AutoModel,
316
+ AutoTokenizer,
317
+ AutoModelForCausalLM,
318
+ ) = _import_transformers()
319
+
320
+ # For models that commonly require custom code, use trust_remote_code=True immediately
321
+ requires_custom_code = any(
322
+ pattern in model_link.lower()
323
+ for pattern in ["progen", "protgpt", "esm-fold", "esmfold", "alphafold"]
324
+ )
325
+
326
+ if requires_custom_code:
327
+ tokenizer = AutoTokenizer.from_pretrained(
328
+ model_link, use_fast=True, trust_remote_code=True
329
+ )
330
+ try:
331
+ model = AutoModel.from_pretrained(
332
+ model_link, trust_remote_code=True
333
+ ).to(device)
334
+ except ValueError as model_error:
335
+ # If AutoModel fails, try AutoModelForCausalLM
336
+ model = AutoModelForCausalLM.from_pretrained(
337
+ model_link, trust_remote_code=True
338
+ ).to(device)
339
+ else:
340
+ # Try without trust_remote_code first, then with it if needed
341
+ try:
342
+ tokenizer = AutoTokenizer.from_pretrained(model_link, use_fast=True)
343
+ try:
344
+ model = AutoModel.from_pretrained(model_link).to(device)
345
+ except ValueError as model_error:
346
+ # If AutoModel fails, try AutoModelForCausalLM
347
+ model = AutoModelForCausalLM.from_pretrained(model_link).to(device)
348
+ except (OSError, ValueError) as e:
349
+ # If the model requires custom code, try with trust_remote_code=True
350
+ if (
351
+ "trust_remote_code" in str(e).lower()
352
+ or "custom code" in str(e).lower()
353
+ ):
354
+ tokenizer = AutoTokenizer.from_pretrained(
355
+ model_link, use_fast=True, trust_remote_code=True
356
+ )
357
+ try:
358
+ model = AutoModel.from_pretrained(
359
+ model_link, trust_remote_code=True
360
+ ).to(device)
361
+ except ValueError as model_error:
362
+ # If AutoModel fails, try AutoModelForCausalLM
363
+ model = AutoModelForCausalLM.from_pretrained(
364
+ model_link, trust_remote_code=True
365
+ ).to(device)
366
+ else:
367
+ raise
368
+
369
+ # Handle tokenizer padding token
370
+ if tokenizer.pad_token is None:
371
+ if tokenizer.eos_token is not None:
372
+ tokenizer.pad_token = tokenizer.eos_token
373
+ elif tokenizer.unk_token is not None:
374
+ tokenizer.pad_token = tokenizer.unk_token
375
+ else:
376
+ # Add a special padding token
377
+ tokenizer.add_special_tokens({"pad_token": "[PAD]"})
378
+ model.resize_token_embeddings(len(tokenizer))
379
+
380
+ model.eval()
381
+
382
+ # Get model configuration
383
+ config = model.config
384
+ num_heads = getattr(
385
+ config,
386
+ "num_attention_heads",
387
+ getattr(config, "num_heads", getattr(config, "n_head", 12)),
388
+ )
389
+ num_layers = getattr(
390
+ config,
391
+ "num_hidden_layers",
392
+ getattr(config, "num_layers", getattr(config, "n_layer", 12)),
393
+ )
394
+ embedding_size = getattr(
395
+ config,
396
+ "hidden_size",
397
+ getattr(config, "d_model", getattr(config, "embed_dim", 768)),
398
+ )
399
+
400
+ logger.info(f"Loaded generic HuggingFace model: {model_link}")
401
+ logger.info(f"Model type: {config.model_type}")
402
+ logger.info(
403
+ f"Number of heads: {num_heads}, layers: {num_layers}, embedding size: {embedding_size}"
404
+ )
405
+
406
+ return model, tokenizer, num_heads, num_layers, embedding_size
@@ -7,7 +7,7 @@ import time
7
7
  import gc
8
8
  import os
9
9
  import json
10
- from transformers import RoFormerTokenizer
10
+ from transformers import RoFormerTokenizer, T5Tokenizer
11
11
  import threading, queue
12
12
  from alive_progress import alive_bar
13
13
  import shutil
@@ -43,7 +43,7 @@ class TokenBudgetBatchSampler:
43
43
 
44
44
 
45
45
  class SequenceDictDataset(Dataset):
46
- def __init__(self, sequences, substring_dict, context):
46
+ def __init__(self, sequences, substring_dict, context, bracket_type = "square"):
47
47
  self.data = list(sequences.items()) # (label, seq)
48
48
  if substring_dict:
49
49
  self.substring_dict = substring_dict
@@ -53,7 +53,7 @@ class SequenceDictDataset(Dataset):
53
53
  self.substring_dict = None
54
54
  self.context = None
55
55
  self.filtered_substring_data = None
56
- pass
56
+ self.bracket_type = bracket_type
57
57
 
58
58
  def __getitem__(self, idx):
59
59
  return self.data[idx]
@@ -128,13 +128,15 @@ class HuggingFaceDataset(SequenceDictDataset):
128
128
  sequences,
129
129
  substring_dict,
130
130
  context,
131
+ bracket_type,
131
132
  tokenizer,
132
133
  max_length,
133
134
  add_special_tokens=True,
134
135
  ):
135
- super().__init__(sequences, substring_dict, context)
136
+ super().__init__(sequences, substring_dict, context, bracket_type)
137
+
136
138
  self.encoded_data = self._encode_sequences(
137
- self.data, tokenizer, max_length, add_special_tokens
139
+ self.data, tokenizer, max_length, add_special_tokens, gapped_sequences = True
138
140
  ) # (label, seq, toks, attention_mask)
139
141
  self.pad_token_id = tokenizer.pad_token_type_id
140
142
  if self.substring_dict:
@@ -144,16 +146,14 @@ class HuggingFaceDataset(SequenceDictDataset):
144
146
  tokenizer,
145
147
  "max_length",
146
148
  add_special_tokens=False,
149
+ gapped_sequences=True
147
150
  ) # (label, seq, toks, attention_mask)
148
151
  self.substring_masks = self._get_substring_masks()
149
152
 
150
- def _encode_sequences(self, data, tokenizer, max_length, add_special_tokens):
153
+ def _encode_sequences(self, data, tokenizer, max_length, add_special_tokens, gapped_sequences = True):
151
154
  labels, strs = zip(*data)
152
- if isinstance(tokenizer, RoFormerTokenizer):
153
- # RoFormerTokenizer requires space-separated tokenization
154
- # for the input sequences
155
- logger.info("Using RoFormerTokenizer, applying gap_sequence.")
156
- strs = self._gap_sequence(strs)
155
+ if gapped_sequences:
156
+ strs = gap_sequence(strs, self.bracket_type)
157
157
  max_token_length = max(len(seq.split(" ")) for seq in strs)
158
158
  else:
159
159
  # For other tokenizers, use the default tokenization
@@ -187,11 +187,6 @@ class HuggingFaceDataset(SequenceDictDataset):
187
187
  attention_masks = torch.cat(loop_attention_mask, dim=0)
188
188
  return list(zip(labels, strs, list(toks), list(attention_masks)))
189
189
 
190
- def _gap_sequence(self, sequences: Sequence[str]) -> Sequence[str]:
191
- """Space-separated tokenization for RoFormer input."""
192
- seqs = [" ".join(re.findall(r"\[.*?\]|.", sequence)) for sequence in sequences]
193
- return seqs
194
-
195
190
  def get_max_encoded_length(self):
196
191
  return max(len(toks) for _, _, toks, _ in self.encoded_data)
197
192
 
@@ -319,21 +314,137 @@ class ESMDataset(SequenceDictDataset):
319
314
  else:
320
315
  return labels, seqs, toks, None, None
321
316
 
317
+ class CustomDataset(SequenceDictDataset):
318
+ """
319
+ Dataset class for custom embedder.
320
+ """
321
+
322
+ def __init__(
323
+ self,
324
+ sequences,
325
+ substring_dict,
326
+ context,
327
+ bracket_type,
328
+ tokenizer,
329
+ max_length,
330
+ add_special_tokens=True,
331
+ gapped_sequences = True,
332
+ ):
333
+ super().__init__(sequences, substring_dict, context, bracket_type)
334
+ self.tokenizer = tokenizer
335
+ self.max_length = max_length
336
+ self.add_special_tokens = add_special_tokens
337
+ self.pad_token_id = getattr(tokenizer, "pad_token_id", 0)
338
+ self.gapped_sequences = gapped_sequences
339
+ self.bracket_type = bracket_type
340
+
341
+ # Encode sequences
342
+ self.encoded_data = self._encode_sequences(
343
+ self.data, tokenizer, max_length, add_special_tokens, self.gapped_sequences
344
+ )
345
+
346
+ # Handle substring sequences if provided
347
+ if self.substring_dict:
348
+ logger.info("Tokenizing substrings...")
349
+ self.encoded_substring_data = self._encode_sequences(
350
+ self.filtered_substring_data,
351
+ tokenizer,
352
+ "max_length",
353
+ add_special_tokens=False,
354
+ )
355
+ self.substring_masks = self._get_substring_masks()
356
+
357
+ def _encode_sequences(self, data, tokenizer, max_length, add_special_tokens, gapped_sequences = True):
358
+ """Encode sequences using the tokenizer."""
359
+ labels, strs = zip(*data)
360
+
361
+ if gapped_sequences:
362
+ strs = gap_sequence(strs, self.bracket_type)
363
+ max_token_length = max(len(seq.split(" ")) for seq in strs)
364
+ else:
365
+ # For other tokenizers, use the default tokenization
366
+ max_token_length = max(len(seq) for seq in strs)
367
+
368
+ # Convert max_length to int if needed
369
+ if max_length == "max_length":
370
+ max_token_length = max(len(s) for s in strs) + (2 if add_special_tokens else 0)
371
+
372
+ encoded_sequences = []
373
+ for label, seq in zip(labels, strs):
374
+ # Tokenize the sequence
375
+ tokens = tokenizer(
376
+ seq,
377
+ truncation=True,
378
+ padding="max_length",
379
+ max_length=max_token_length,
380
+ add_special_tokens=add_special_tokens,
381
+ return_tensors="pt",
382
+ )
383
+
384
+ encoded_sequences.append(
385
+ (
386
+ label,
387
+ seq,
388
+ tokens["input_ids"].squeeze(0),
389
+ tokens["attention_mask"].squeeze(0),
390
+ )
391
+ )
392
+
393
+ return encoded_sequences
394
+
395
+ def get_max_encoded_length(self):
396
+ """Get maximum encoded sequence length."""
397
+ return max(len(toks) for _, _, toks, _ in self.encoded_data)
398
+
399
+ def safe_collate(self, batch):
400
+ """Collate function for data loader."""
401
+ if self.substring_dict:
402
+ labels, seqs, toks, attn_masks, substring_masks = zip(*batch)
403
+ return (
404
+ list(labels),
405
+ list(seqs),
406
+ torch.stack(toks),
407
+ torch.stack(attn_masks),
408
+ torch.stack(substring_masks),
409
+ )
410
+ else:
411
+ labels, seqs, toks, attn_masks = zip(*batch)
412
+ return (
413
+ list(labels),
414
+ list(seqs),
415
+ torch.stack(toks),
416
+ torch.stack(attn_masks),
417
+ None,
418
+ )
419
+
420
+ def __getitem__(self, idx):
421
+ """Get item from dataset."""
422
+ labels, seqs, toks, attn_mask = self.encoded_data[idx]
423
+ if self.substring_dict:
424
+ substring_masks = self.substring_masks[idx]
425
+ return labels, seqs, toks, attn_mask, substring_masks
426
+ else:
427
+ return labels, seqs, toks, attn_mask
428
+
429
+ def check_input_tokens(valid_tokens, sequences, model_name, bracket_type = "square"):
430
+ def __str_to_list(sequence, bracket_type = "square"):
431
+ if bracket_type == "square":
432
+ return re.findall(r"\[.*?\]|.", sequence)
433
+ elif bracket_type == "angle":
434
+ return re.findall(r"<.*?>|.", sequence)
435
+ else:
436
+ raise ValueError(f"Invalid bracket type: {bracket_type}")
322
437
 
323
- def check_input_tokens(valid_tokens, sequences, model_name):
324
438
  with alive_bar(
325
439
  len(sequences), title="Checking input sequences for invalid tokens..."
326
440
  ) as bar:
327
441
  for label, sequence in sequences.items():
328
- if "esm" not in model_name:
329
- sequence = re.findall(r"\[.*?\]|.", sequence)
330
- if "antiberta" in model_name: # check for longest sequence
331
- assert (
332
- len(sequence) <= 256
333
- ), f"Antiberta2 does not support sequences longer than 256 tokens. Found {len(sequence)} tokens in sequence {label}."
442
+ sequence = __str_to_list(sequence, bracket_type)
443
+ if "antiberta" in model_name: # check for longest sequence
444
+ assert (
445
+ len(sequence) <= 256
446
+ ), f"Antiberta2 does not support sequences longer than 256 tokens. Found {len(sequence)} tokens in sequence {label}."
334
447
 
335
- else:
336
- sequence = re.findall(r"<.*?>|.", sequence)
337
448
  if not set(sequence).issubset(valid_tokens):
338
449
  raise ValueError(
339
450
  f"Invalid tokens found in sequence {label}: {set(sequence) - set(valid_tokens)}"
@@ -365,7 +476,25 @@ def fasta_to_dict(fasta_path):
365
476
  flush()
366
477
 
367
478
  return seq_dict
368
-
479
+ def get_bracket_type(tokenizer):
480
+ try:
481
+ special_token_bracket = tokenizer.special_tokens_map["additional_special_tokens"][0][0]
482
+ except KeyError:
483
+ special_token_bracket = tokenizer.all_special_tokens[0][0]
484
+ if special_token_bracket == "<":
485
+ return "angle"
486
+ elif special_token_bracket == "[":
487
+ return "square"
488
+ else:
489
+ raise ValueError(f"Invalid special token bracket: {special_token_bracket}")
490
+ def gap_sequence(sequences: Sequence[str], bracket_type = "square") -> Sequence[str]:
491
+ if bracket_type == "square":
492
+ seqs = [" ".join(re.findall(r"\[.*?\]|.", sequence)) for sequence in sequences]
493
+ elif bracket_type == "angle":
494
+ seqs = [" ".join(re.findall(r"<.*?>|.", sequence)) for sequence in sequences]
495
+ else:
496
+ raise ValueError(f"Invalid bracket type: {bracket_type}")
497
+ return seqs
369
498
 
370
499
  def flush_memmaps(obj):
371
500
  """Recursively flush memory maps."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pepe-cli
3
- Version: 1.0.3
3
+ Version: 1.0.4
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
@@ -10,7 +10,7 @@ sys.argv = [
10
10
  "--experiment_name",
11
11
  "test",
12
12
  "--model_name",
13
- "/home/jahn/embedairr/examples/custom_model/example_protein_model",
13
+ "ZYMScott/antiberty",
14
14
  "--fasta_path",
15
15
  "src/tests/test_files/test.fasta",
16
16
  "--output_path",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes