GitLab

accessible_worlds.aw_create_tokenizer

  1from transformers import AutoTokenizer, PreTrainedTokenizer
  2import orjson
  3import re
  4from pathlib import Path
  5from typing import override
  6
  7from .aw_vocabulary import AwVocabulary
  8
  9from .aw_utils import (
 10    split_and_preserve_names,
 11    load_json,
 12    safe_save
 13)
 14
 15import numpy as np
 16
 17def _normalize_word_spacing( text: str ) -> str :
 18
 19    # add space before opening quotes
 20    text = re.sub(r'"(?=[A-Za-z0-9])', '" ', text)
 21
 22    return text
 23
 24def _save_map_back( 
 25    remap_table : list[int] | np.ndarray, 
 26    map_back : dict[tuple,int], 
 27    filepath : str | Path 
 28) -> None :
 29
 30    data = {
 31        "aw_to_gemma" : [ int( id ) for id in remap_table ],
 32        "gemma_to_aw" : {",".join(map(str, k)): v for k, v in map_back.items()}
 33    }
 34    safe_save( data=data, filepath=filepath )
 35
 36def _load_map_back( filepath : str | Path ) -> tuple[ np.ndarray, dict[tuple,int] ] :
 37    
 38    data = load_json( filepath )
 39    return ( 
 40        data[ "aw_to_gemma" ], 
 41        { tuple(int(x) for x in k.split(",")): v for k, v in data["gemma_to_aw"].items() }
 42    )
 43
 44class AwTokenizer( ) :
 45
 46    # Note: Since Gemma 4 uses BPE with separate " x" vs "x" tokens, 
 47    # we opt for Moses-style processing and detokenization, ensuring spaces 
 48    # exist so we always get the " x" version.
 49
 50    def __init__( 
 51        self,
 52        tokenizer_path : str | Path,
 53        data_dir : str | Path,
 54        lowercase : bool,
 55        output_dir : str | Path ) :
 56
 57        base_tokenizer = AutoTokenizer.from_pretrained( tokenizer_path )
 58
 59        vocabulary_path = Path( data_dir ) / f"vocabulary.json"
 60
 61        if not vocabulary_path.exists() :
 62            raise ValueError( f"Vocabulary {vocabulary_path} not found" )
 63
 64        world_path = Path( data_dir ) / "world.json"
 65
 66        if not world_path.exists() :
 67            raise ValueError( f"World configuration {world_path} not found" )
 68
 69        exposition_path = Path( data_dir ) / "exposition_settings.json"
 70
 71        if not exposition_path.exists() :
 72            raise ValueError( f"Exposition configuration {exposition_path} not found" )
 73
 74        data_file_paths = [  
 75            Path( data_dir ) / f"data_{i}.json" for i in range( 1000 )
 76        ]
 77
 78        data_file_paths = [ p for p in data_file_paths if p.exists() ]
 79
 80        if not data_file_paths :
 81            raise ValueError( f"No files found in data_dir {data_dir}" )
 82
 83        vocabulary = AwVocabulary.from_file( vocabulary_path )
 84        world      = load_json( world_path )
 85        exposition = load_json( exposition_path )
 86
 87        token_set = set()
 88
 89        current_file_idx = 0
 90        while current_file_idx < len( data_file_paths ) :
 91            data = load_json( data_file_paths[ current_file_idx ] )    
 92            for instance in data["instances"] :
 93                text = instance["completion"].lower() if lowercase else instance["completion"]
 94                text = " " + _normalize_word_spacing( text )
 95                tokens = base_tokenizer.encode( text )
 96                token_set.update( tokens )
 97            current_file_idx += 1
 98
 99        for token in token_set :
100            print( base_tokenizer.decode( [ token ] ) )
101
102        self.remap_table = np.array( list(token_set), dtype=int )
103        for idx, token_id in enumerate( token_set ) :
104            self.remap_table[ idx ] = token_id
105            
106        self.vocab = {
107            base_tokenizer.encode( self.remap_table[ base_id ] ) : id
108            for id, base_id in enumerate( self.remap_table )
109        }
110
111        self.map_back = {}
112        for idx in range( len( self.remap_table ) ) :
113            
114            self.map_back[ (self.remap_table[ idx ],) ] = idx
115            word = base_tokenizer.decode( [ self.remap_table[ idx ] ] )
116            
117            # Map all unsupported variants back to the space normalized lowercase variant
118            if len( word ) > 1 and word[ 0 ] == ' ' :
119                
120                capitialized =  ' ' + word[ 1 ].upper() + word[2:]
121                ct = base_tokenizer.encode( capitialized )
122                self.map_back[ tuple( ct ) ] = idx
123
124                capitialized_no_space = capitialized[1:]
125                ctns = base_tokenizer.encode( capitialized_no_space )
126                self.map_back[ tuple( ctns ) ] = idx
127
128                no_space = word[1:]
129                ns = base_tokenizer.encode( no_space )
130                self.map_back[ tuple( ns ) ] = idx
131        
132        # _save_map_back( 
133        #     remap_table=self.remap_table, 
134        #     map_back=self.map_back, 
135        #     filepath=Path(output_dir)/"map_back.json" 
136        # )
137
138
139if __name__ == "__main__":
140
141    sw_tokenizer = AwTokenizer( 
142        tokenizer_path="google/gemma-4-26B-A4B-it",
143        data_dir="/media/ubuntu/VisData/a-machine/data/simpler_worlds/aw-v1",
144        lowercase=True,
145        output_dir="/media/ubuntu/VisData/a-machine/data/simpler_worlds/aw-v1/tokenizer"
146    )
class AwTokenizer:
 45class AwTokenizer( ) :
 46
 47    # Note: Since Gemma 4 uses BPE with separate " x" vs "x" tokens, 
 48    # we opt for Moses-style processing and detokenization, ensuring spaces 
 49    # exist so we always get the " x" version.
 50
 51    def __init__( 
 52        self,
 53        tokenizer_path : str | Path,
 54        data_dir : str | Path,
 55        lowercase : bool,
 56        output_dir : str | Path ) :
 57
 58        base_tokenizer = AutoTokenizer.from_pretrained( tokenizer_path )
 59
 60        vocabulary_path = Path( data_dir ) / f"vocabulary.json"
 61
 62        if not vocabulary_path.exists() :
 63            raise ValueError( f"Vocabulary {vocabulary_path} not found" )
 64
 65        world_path = Path( data_dir ) / "world.json"
 66
 67        if not world_path.exists() :
 68            raise ValueError( f"World configuration {world_path} not found" )
 69
 70        exposition_path = Path( data_dir ) / "exposition_settings.json"
 71
 72        if not exposition_path.exists() :
 73            raise ValueError( f"Exposition configuration {exposition_path} not found" )
 74
 75        data_file_paths = [  
 76            Path( data_dir ) / f"data_{i}.json" for i in range( 1000 )
 77        ]
 78
 79        data_file_paths = [ p for p in data_file_paths if p.exists() ]
 80
 81        if not data_file_paths :
 82            raise ValueError( f"No files found in data_dir {data_dir}" )
 83
 84        vocabulary = AwVocabulary.from_file( vocabulary_path )
 85        world      = load_json( world_path )
 86        exposition = load_json( exposition_path )
 87
 88        token_set = set()
 89
 90        current_file_idx = 0
 91        while current_file_idx < len( data_file_paths ) :
 92            data = load_json( data_file_paths[ current_file_idx ] )    
 93            for instance in data["instances"] :
 94                text = instance["completion"].lower() if lowercase else instance["completion"]
 95                text = " " + _normalize_word_spacing( text )
 96                tokens = base_tokenizer.encode( text )
 97                token_set.update( tokens )
 98            current_file_idx += 1
 99
100        for token in token_set :
101            print( base_tokenizer.decode( [ token ] ) )
102
103        self.remap_table = np.array( list(token_set), dtype=int )
104        for idx, token_id in enumerate( token_set ) :
105            self.remap_table[ idx ] = token_id
106            
107        self.vocab = {
108            base_tokenizer.encode( self.remap_table[ base_id ] ) : id
109            for id, base_id in enumerate( self.remap_table )
110        }
111
112        self.map_back = {}
113        for idx in range( len( self.remap_table ) ) :
114            
115            self.map_back[ (self.remap_table[ idx ],) ] = idx
116            word = base_tokenizer.decode( [ self.remap_table[ idx ] ] )
117            
118            # Map all unsupported variants back to the space normalized lowercase variant
119            if len( word ) > 1 and word[ 0 ] == ' ' :
120                
121                capitialized =  ' ' + word[ 1 ].upper() + word[2:]
122                ct = base_tokenizer.encode( capitialized )
123                self.map_back[ tuple( ct ) ] = idx
124
125                capitialized_no_space = capitialized[1:]
126                ctns = base_tokenizer.encode( capitialized_no_space )
127                self.map_back[ tuple( ctns ) ] = idx
128
129                no_space = word[1:]
130                ns = base_tokenizer.encode( no_space )
131                self.map_back[ tuple( ns ) ] = idx
132        
133        # _save_map_back( 
134        #     remap_table=self.remap_table, 
135        #     map_back=self.map_back, 
136        #     filepath=Path(output_dir)/"map_back.json" 
137        # )
AwTokenizer( tokenizer_path: str | pathlib.Path, data_dir: str | pathlib.Path, lowercase: bool, output_dir: str | pathlib.Path)
 51    def __init__( 
 52        self,
 53        tokenizer_path : str | Path,
 54        data_dir : str | Path,
 55        lowercase : bool,
 56        output_dir : str | Path ) :
 57
 58        base_tokenizer = AutoTokenizer.from_pretrained( tokenizer_path )
 59
 60        vocabulary_path = Path( data_dir ) / f"vocabulary.json"
 61
 62        if not vocabulary_path.exists() :
 63            raise ValueError( f"Vocabulary {vocabulary_path} not found" )
 64
 65        world_path = Path( data_dir ) / "world.json"
 66
 67        if not world_path.exists() :
 68            raise ValueError( f"World configuration {world_path} not found" )
 69
 70        exposition_path = Path( data_dir ) / "exposition_settings.json"
 71
 72        if not exposition_path.exists() :
 73            raise ValueError( f"Exposition configuration {exposition_path} not found" )
 74
 75        data_file_paths = [  
 76            Path( data_dir ) / f"data_{i}.json" for i in range( 1000 )
 77        ]
 78
 79        data_file_paths = [ p for p in data_file_paths if p.exists() ]
 80
 81        if not data_file_paths :
 82            raise ValueError( f"No files found in data_dir {data_dir}" )
 83
 84        vocabulary = AwVocabulary.from_file( vocabulary_path )
 85        world      = load_json( world_path )
 86        exposition = load_json( exposition_path )
 87
 88        token_set = set()
 89
 90        current_file_idx = 0
 91        while current_file_idx < len( data_file_paths ) :
 92            data = load_json( data_file_paths[ current_file_idx ] )    
 93            for instance in data["instances"] :
 94                text = instance["completion"].lower() if lowercase else instance["completion"]
 95                text = " " + _normalize_word_spacing( text )
 96                tokens = base_tokenizer.encode( text )
 97                token_set.update( tokens )
 98            current_file_idx += 1
 99
100        for token in token_set :
101            print( base_tokenizer.decode( [ token ] ) )
102
103        self.remap_table = np.array( list(token_set), dtype=int )
104        for idx, token_id in enumerate( token_set ) :
105            self.remap_table[ idx ] = token_id
106            
107        self.vocab = {
108            base_tokenizer.encode( self.remap_table[ base_id ] ) : id
109            for id, base_id in enumerate( self.remap_table )
110        }
111
112        self.map_back = {}
113        for idx in range( len( self.remap_table ) ) :
114            
115            self.map_back[ (self.remap_table[ idx ],) ] = idx
116            word = base_tokenizer.decode( [ self.remap_table[ idx ] ] )
117            
118            # Map all unsupported variants back to the space normalized lowercase variant
119            if len( word ) > 1 and word[ 0 ] == ' ' :
120                
121                capitialized =  ' ' + word[ 1 ].upper() + word[2:]
122                ct = base_tokenizer.encode( capitialized )
123                self.map_back[ tuple( ct ) ] = idx
124
125                capitialized_no_space = capitialized[1:]
126                ctns = base_tokenizer.encode( capitialized_no_space )
127                self.map_back[ tuple( ctns ) ] = idx
128
129                no_space = word[1:]
130                ns = base_tokenizer.encode( no_space )
131                self.map_back[ tuple( ns ) ] = idx
132        
133        # _save_map_back( 
134        #     remap_table=self.remap_table, 
135        #     map_back=self.map_back, 
136        #     filepath=Path(output_dir)/"map_back.json" 
137        # )
remap_table
vocab
map_back