Biosaic is a specialized tokenizer library designed for biological sequences, supporting DNA, RNA and protein sequence tokenization. It provides k-mer based tokenization with support for continuous (sliding-window) and non-continuous (fixed-chunk) modes, along with special token handling for advanced biological sequence processing.
- Triple Mode Support: DNA, RNA and protein sequence tokenization
- Flexible K-mer Sizes: Up to 8-mers for DNA/RNA, up to 4-mers for proteins
- Tokenization Modes: Continuous (overlapping) and non-continuous (fixed-chunk)
- Special Tokens: Support for sequence markup and control tokens
- Remote Vocabularies: Pre-trained vocabularies loaded from remote repositories
- Comprehensive Methods: Encoding, decoding, tokenization, and biological utilities
from biosaic import Tokenizer# Initialize DNA tokenizer with 3-mers
dna_tokenizer = Tokenizer(mode="dna", kmer=3, continuous=True)
# Tokenize a DNA sequence
sequence = "ATCGATCG"
tokens = dna_tokenizer.tokenize(sequence)
print(tokens) # ['ATC', 'TCG', 'CGA', 'GAT', 'ATC', 'TCG']
# Encode to IDs
ids = dna_tokenizer.encode(sequence)
print(ids) # [45, 89, 67, 123, 45, 89]
# Decode back to sequence
decoded = dna_tokenizer.decode(ids)
print(decoded) # "ATCGATCG"# Initialize RNA tokenizer with 3-mers
dna_tokenizer = Tokenizer(mode="rna", kmer=3, continuous=True)
# Tokenize a RNA sequence
sequence = "AUCGAUCG"
tokens = dna_tokenizer.tokenize(sequence)
print(tokens) # ['AUC', 'UCG', 'CGA', 'GAU', 'AUC', 'UCG']
# Encode to IDs
ids = dna_tokenizer.encode(sequence)
print(ids) # [45, 89, 67, 123, 45, 89]
# Decode back to sequence
decoded = dna_tokenizer.decode(ids)
print(decoded) # "AUCGAUCG"# Initialize protein tokenizer with 2-mers
protein_tokenizer = Tokenizer(mode="protein", kmer=2, continuous=True)
# Tokenize a protein sequence
sequence = "MKVLWAALLVTFLAGC"
tokens = protein_tokenizer.tokenize(sequence)
print(tokens) # ['MK', 'KV', 'VL', 'LW', 'WA', ...]
# Get vocabulary size
print(protein_tokenizer.vocab_size) # 441 (21^2 amino acids)The main tokenizer class that handles both DNA and protein sequences.
Tokenizer(mode: str, kmer: int, continuous: bool = False, special_tokens = None)Parameters:
mode(str): Sequence type - "dna" or "protein"kmer(int): K-mer length (max 8 for DNA/RNA, max 4 for protein)continuous(bool): Tokenization modeTrue: Sliding-window (overlapping k-mers)False: Fixed-chunk (non-overlapping k-mers)
special_tokens(list/None/False): Special token configurationNone: Default tokens['<S>', '</S>', '<P>', '<C>', '<M>'](only with continuous=False)list: Custom special tokensFalse: No special tokens
Raises:
AssertionError: Invalid mode or k-mer sizeValueError: Special tokens used with continuous=True
Converts biological sequence to integer token IDs.
dna_tokenizer = Tokenizer("dna", kmer=3, continuous=True)
ids = dna_tokenizer.encode("ATCGAT")
print(ids) # [45, 89, 67, 123]Converts token IDs back to original sequence.
sequence = dna_tokenizer.decode([45, 89, 67, 123])
print(sequence) # "ATCGAT"Splits sequence into k-mer tokens.
tokens = dna_tokenizer.tokenize("ATCGAT")
print(tokens) # ['ATC', 'TCG', 'CGA', 'GAT']Reconstructs sequence from k-mer tokens.
sequence = dna_tokenizer.detokenize(['ATC', 'TCG', 'CGA', 'GAT'])
print(sequence) # "ATCGAT"Creates one-hot encoding matrix.
dna_tokenizer = Tokenizer("dna", kmer=2, continuous=False)
one_hot_matrix = dna_tokenizer.one_hot("ATCG")
print(one_hot_matrix.shape) # (2, vocab_size)Returns reverse complement of DNA sequence (DNA only).
dna_tokenizer = Tokenizer("dna", kmer=3)
rev_comp = dna_tokenizer.reverse_complement("ATCG")
print(rev_comp) # "CGAT"Pads sequence to target length.
padded = dna_tokenizer.pad_sequence("ATCG", 10)
print(padded) # "ATCG------"Returns vocabulary size.
print(dna_tokenizer.vocab_size) # 625 (5^4 for 4-mer DNA)Returns vocabulary dictionary mapping tokens to IDs.
print(dna_tokenizer.vocab['ATG']) # 45Generates overlapping k-mers using a sliding window approach.
tokenizer = Tokenizer("dna", kmer=3, continuous=True)
tokens = tokenizer.tokenize("ATCGATCG")
print(tokens) # ['ATC', 'TCG', 'CGA', 'GAT', 'ATC', 'TCG']Use Cases:
- Preserving sequence continuity
- Maximum information retention
- Machine learning applications requiring dense representation
Splits sequence into non-overlapping k-mer chunks.
tokenizer = Tokenizer("dna", kmer=3, continuous=False)
tokens = tokenizer.tokenize("ATCGATCG")
print(tokens) # ['ATC', 'GAT', 'CG']Use Cases:
- Memory-efficient processing
- When overlapping information is not needed
- Compatible with special tokens
Special tokens provide sequence markup and control capabilities. They only work with continuous=False.
<S>: Start of sequence</S>: End of sequence<P>: Padding token<C>: Control token<M>: Mask token
# Initialize with special tokens
tokenizer = Tokenizer("dna", kmer=3, continuous=False, special_tokens=['<S>', '</S>'])
# Use special tokens in sequence
sequence = "<S>ATCGATCG</S>"
tokens = tokenizer.tokenize(sequence)
print(tokens) # ['<S>', 'ATC', 'GAT', 'CG', '</S>']
# Encode with special tokens
ids = tokenizer.encode(sequence)
decoded = tokenizer.decode(ids)
print(decoded) # "<S>ATCGATCG</S>"custom_tokens = ['<START>', '<END>', '<GENE>', '<PROMOTER>']
tokenizer = Tokenizer("dna", kmer=2, continuous=False, special_tokens=custom_tokens)def process_sequences(sequences, mode="dna", kmer=3):
tokenizer = Tokenizer(mode=mode, kmer=kmer, continuous=True)
results = []
for seq in sequences:
try:
tokens = tokenizer.tokenize(seq)
ids = tokenizer.encode(seq)
results.append({
'sequence': seq,
'tokens': tokens,
'ids': ids,
'length': len(tokens)
})
except ValueError as e:
print(f"Error processing {seq}: {e}")
return results
# Example usage
dna_sequences = ["ATCGATCG", "GCTAGCTA", "TTAACCGG"]
results = process_sequences(dna_sequences)def create_padded_batch(sequences, tokenizer, max_length=None):
# Tokenize all sequences
all_tokens = [tokenizer.tokenize(seq) for seq in sequences]
# Find maximum length if not specified
if max_length is None:
max_length = max(len(tokens) for tokens in all_tokens)
# Pad and encode
batch_ids = []
for tokens in all_tokens:
# Truncate or pad token list
if len(tokens) > max_length:
tokens = tokens[:max_length]
else:
tokens.extend(['<P>'] * (max_length - len(tokens)))
# Convert to IDs
ids = [tokenizer.vocab.get(token, 0) for token in tokens]
batch_ids.append(ids)
return batch_ids
# Example
tokenizer = Tokenizer("protein", kmer=2, continuous=False, special_tokens=['<P>'])
sequences = ["MKVLWAALL", "VTFLAGC", "ACDEFGHIKLMNPQRSTVWY"]
batch = create_padded_batch(sequences, tokenizer, max_length=10)def analyze_sequence_composition(sequence, tokenizer):
tokens = tokenizer.tokenize(sequence)
# Count token frequencies
from collections import Counter
token_counts = Counter(tokens)
# Calculate statistics
total_tokens = len(tokens)
unique_tokens = len(token_counts)
most_common = token_counts.most_common(5)
return {
'total_tokens': total_tokens,
'unique_tokens': unique_tokens,
'vocabulary_coverage': unique_tokens / tokenizer.vocab_size,
'most_common_tokens': most_common,
'token_distribution': dict(token_counts)
}
# Example analysis
dna_tokenizer = Tokenizer("dna", kmer=3, continuous=True)
sequence = "ATCGATCGATCGATCG" * 10 # Longer sequence for analysis
analysis = analyze_sequence_composition(sequence, dna_tokenizer)
print(f"Vocabulary coverage: {analysis['vocabulary_coverage']:.2%}")try:
tokenizer = Tokenizer("dna", kmer=3)
tokenizer.encode("ATCXGATCG") # X is invalid
except ValueError as e:
print(f"Invalid character error: {e}")
# Solution: Clean sequence or use valid DNA bases (A, T, G, C, -)try:
tokenizer = Tokenizer("dna", kmer=3, continuous=True, special_tokens=['<S>'])
except ValueError as e:
print(f"Configuration error: {e}")
# Solution: Use continuous=False with special tokenstry:
tokenizer = Tokenizer("protein", kmer=5) # Too large
except AssertionError as e:
print(f"K-mer size error: {e}")
# Solution: Use kmer <= 4 for proteins, kmer <= 8 for DNA- Continuous mode: Higher memory usage due to overlapping tokens
- Non-continuous mode: More memory efficient
- Large k-mers: Exponentially larger vocabularies
# Pre-load tokenizer for repeated use
tokenizer = Tokenizer("dna", kmer=4, continuous=True)
# Batch process sequences
sequences = ["ATCG" * 100 for _ in range(1000)]
start_time = time.time()
all_ids = [tokenizer.encode(seq) for seq in sequences]
print(f"Processed {len(sequences)} sequences in {time.time() - start_time:.2f}s")import numpy as np
def prepare_ml_data(sequences, tokenizer, max_length=100):
# Tokenize and encode
encoded_sequences = []
for seq in sequences:
ids = tokenizer.encode(seq)
# Pad or truncate
if len(ids) > max_length:
ids = ids[:max_length]
else:
ids.extend([0] * (max_length - len(ids))) # 0 as padding ID
encoded_sequences.append(ids)
return np.array(encoded_sequences)
# Usage with neural networks
tokenizer = Tokenizer("dna", kmer=3, continuous=True)
sequences = ["ATCGATCG", "GCTAGCTA", "TTAACCGG"]
X = prepare_ml_data(sequences, tokenizer)
print(f"Input shape for ML model: {X.shape}")def process_fasta_like_data(sequence_data, mode="dna", kmer=3):
tokenizer = Tokenizer(mode=mode, kmer=kmer, continuous=False,
special_tokens=['<S>', '</S>'])
processed_data = []
for seq_id, sequence in sequence_data.items():
# Add sequence markers
marked_sequence = f"<S>{sequence}</S>"
# Tokenize and encode
tokens = tokenizer.tokenize(marked_sequence)
ids = tokenizer.encode(marked_sequence)
processed_data.append({
'id': seq_id,
'original': sequence,
'tokens': tokens,
'encoded': ids,
'length': len(sequence)
})
return processed_data
# Example usage
sequences = {
'seq1': 'ATCGATCGATCG',
'seq2': 'GCTAGCTAGCTA',
'seq3': 'TTAACCGGTTAA'
}
processed = process_fasta_like_data(sequences)- Small k-mers (2-3): Better for short sequences, higher generalization
- Large k-mers (4-8): More specific, better for pattern recognition
- Consider sequence length: k-mer should be much smaller than typical sequence length
-
Use continuous=True for:
- Machine learning applications
- When sequence context is important
- Pattern recognition tasks
-
Use continuous=False for:
- Memory-constrained environments
- When special tokens are needed
- Simple sequence analysis
- Use meaningful special tokens for your application
- Keep special tokens short and distinctive
- Consider vocabulary size impact
- Document special token meanings for reproducibility
- Vocabulary loading fails: Check internet connection and URL accessibility
- Memory errors with large sequences: Use smaller k-mers or non-continuous mode
- Inconsistent results: Ensure same tokenizer configuration across sessions
- Special tokens not working: Verify continuous=False is set
The library includes debug output for vocabulary loading:
DEBUGG INFO[104] [Saved] Vocabulary saved to /path/to/vocab.modelThis helps verify that vocabularies are being loaded correctly from remote sources.
This documentation covers the Biosaic tokenizer library with support for:
- DNA sequences (bases: A, T, G, C, -)
- RNA sequences (bases: A, U, G, C, -)
- Protein sequences (20 standard amino acids + gap character)
- Remote vocabulary loading from GitHub repositories
- K-mer sizes up to 8 for DNA and 4 for proteins
- Flexible tokenization modes and special token support