forked from johnsonandjohnson/HELM
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
154 lines (129 loc) · 5.9 KB
/
Copy pathutils.py
File metadata and controls
154 lines (129 loc) · 5.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import torch
import numpy as np
from kmer_vocab import CODON_TO_ID_1, CODON_TO_ID_3, CODON_TO_ID_6, ID_TO_CODON_1, ID_TO_CODON_3, ID_TO_CODON_6
AA_TO_ID = {'<cls>': 0,
'<pad>': 1,
'<eos>': 2,
'<unk>': 3,
'L': 4,
'A': 5,
'G': 6,
'V': 7,
'S': 8,
'E': 9,
'R': 10,
'T': 11,
'I': 12,
'D': 13,
'P': 14,
'K': 15,
'Q': 16,
'N': 17,
'F': 18,
'Y': 19,
'M': 20,
'H': 21,
'W': 22,
'C': 23,
'X': 24,
'B': 25,
'U': 26,
'Z': 27,
'O': 28,
'.': 29,
'-': 30,
"start_codon": 31,
"end_codon": 32,
'<mask>': 33,
"<distill_token>": 34,}
ID_TO_AA = {v: k for k, v in AA_TO_ID.items()}
def encode_sequence_aa(sequences):
"""Tokenize a sequence of amino acids and add a cls token at the beginning."""
encoded_sequences = []
for sequence in sequences:
sequence = clean_sequence_aa(sequence)
tokenized_sequence = [AA_TO_ID[aa] if aa in AA_TO_ID else AA_TO_ID['<unk>'] for aa in sequence]
tokenized_sequence = [AA_TO_ID['<cls>']] + tokenized_sequence + [AA_TO_ID['<eos>']]
encoded_sequences.append(tokenized_sequence)
return encoded_sequences
def decode_sequence_aa(sequence):
"""Decode a sequence of tokens."""
return "".join([ID_TO_AA[token] if token in ID_TO_AA else "<unk>" for token in sequence])
def clean_sequence_aa(sequence):
"""Remove gaps and convert all residues to upper case."""
return sequence.replace("-", "").upper()
def encode_sequence_codon(sequences, tree=None, overlap=0, k=3):
"""Tokenize a sequence of amino acids and add a cls token at the beginning."""
assert k in [1, 3, 6], "k must be 1, 3, or 6."
assert overlap < k, "Overlap must be smaller than k."
if tree is not None:
assert k == 3, "Tree can only be used with k=3."
CODON_TO_ID = CODON_TO_ID_1 if k == 1 else CODON_TO_ID_3 if k == 3 else CODON_TO_ID_6
encoded_sequences = []
for sequence in sequences:
sequence = clean_sequence_codon(sequence)
sequence = [sequence[i:i+k] for i in range(0, len(sequence) - k + 1, k-overlap)]
if tree is not None:
tokenized_sequence = [tree.index(codon) if codon in tree else tree.index('<unk>') for codon in sequence]
tokenized_sequence = [tree.index("<cls>")] + tokenized_sequence + [tree.index("<eos>")]
else:
tokenized_sequence = [CODON_TO_ID[codon] if codon in CODON_TO_ID else CODON_TO_ID['<unk>'] for codon in sequence]
tokenized_sequence = [CODON_TO_ID["<cls>"]] + tokenized_sequence + [CODON_TO_ID["<eos>"]]
encoded_sequences.append(tokenized_sequence)
return encoded_sequences
def decode_sequence_codon(sequence, overlap=0, k=3):
"""Decode a sequence of tokens."""
assert overlap < k, "Overlap must be smaller than k."
assert k in [1, 3, 6], "k must be 1, 3, or 6."
ID_TO_CODON = ID_TO_CODON_1 if k == 1 else ID_TO_CODON_3 if k == 3 else ID_TO_CODON_6
sequence = [ID_TO_CODON[token] if token in ID_TO_CODON else "<unk>" for token in sequence]
return "".join(sequence)[::k-overlap]
def clean_sequence_codon(sequence):
"""Remove gaps and convert all residues to upper case."""
sequence = sequence.strip()
sequence = sequence.replace("-", "").upper()
sequence = sequence.replace("U", "T").upper()
return sequence
def train_tokenizer(data, field, model_type, vocab_size=1000):
from tokenizers import Tokenizer
from tokenizers.models import BPE, WordPiece, Unigram
from tokenizers.trainers import BpeTrainer, WordPieceTrainer, UnigramTrainer
from tokenizers.normalizers import Lowercase
if model_type == 'bpe':
model = BPE(special_tokens=["<cls>", "<eos>", "<unk>", "<mask>", "<pad>", "<distill_token>"])
trainer = BpeTrainer(special_tokens=["<cls>", "<eos>", "<unk>", "<mask>", "<pad>", "<distill_token>"], vocab_size=vocab_size)
elif model_type == 'wp':
model = WordPiece(special_tokens=["<cls>", "<eos>", "<unk>", "<mask>", "<pad>", "<distill_token>"])
trainer = WordPieceTrainer(special_tokens=["<cls>", "<eos>", "<unk>", "<mask>", "<pad>", "<distill_token>"], vocab_size=vocab_size)
elif model_type == 'ug':
model = Unigram()
trainer = UnigramTrainer(special_tokens=["<cls>", "<eos>", "<unk>", "<mask>", "<pad>", "<distill_token>"], vocab_size=vocab_size)
else:
raise ValueError("Model type must be one of 'bpe', 'wp', or 'ug'.")
tokenizer = Tokenizer(model)
tokenizer.normalizer = Lowercase()
def batch_iterator():
for i in range(0, len(data['train']), 1000):
cleaned_seq = []
for seq in data['train'][i:i+1000][field]:
cleaned_seq.append("<cls>" + clean_sequence_codon(seq) + "<eos>")
yield cleaned_seq
tokenizer.train_from_iterator(batch_iterator(), trainer=trainer)
return tokenizer
def sample_lengths(len_seq, mask_fraction):
"""
Sample a length uniformly from 1 to max_L*self.mask_fraction (must be bigger than 1).
If the length is larger than max_L, return max_L.
"""
length = np.random.randint(1, max(int(len_seq * mask_fraction), 2))
return length
if __name__ == "__main__":
from datasets import Features, Value, ClassLabel
from datasets import load_dataset
class_names = ['class_label_1']
ft = Features({'sequence_heavy': Value('string')})
dataset = load_dataset("csv", data_files="SRR9179282_paired 2.csv", skiprows=1, features=ft)
tokenizer = train_tokenizer(dataset, "sequence_heavy", "ug", 1000)
print("Tokenizer trained successfully.")
print(tokenizer.encode("<cls>ACGTACGTACGT<eos><pad><pad>").ids)
print(tokenizer.get_vocab())