// this will be the 1st version of tokenization written in cpp, following this video by karpathy: https://www.youtube.com/watch?v=zduSFxRajkE // so read this blog from hf: https://huggingface.co/learn/llm-course/en/chapter6/5 // and also gonna take some ref from karpathy's nanochat repo: https://github.com/karpathy/nanochat/blob/master/rustbpe/src/lib.rs // now that we have all files ready, like the vob and the merges, the only thing remaining is the encode and decode, basically tokenize and de-tokenize #include using namespace std; unordered_map vocab; unordered_map vocab_rev; map, int> merges; int get_utf8_char_len(unsigned char c) { if ((c & 0x80) == 0) return 1; if ((c & 0xE0) == 0xC0) return 2; if ((c & 0xF0) == 0xE0) return 3; if ((c & 0xF8) == 0xF0) return 4; return 1; } void load_vocab_and_merge_thing(){ ifstream ucm("unique_char_map.txt"); if(!ucm.is_open()){ cerr<<"Error: unable to open unique_char_map.txt"<> id1 >> id2 >> new_id) { merges[{id1, id2}] = new_id; string part1 = vocab_rev[id1]; string part2 = vocab_rev[id2]; vocab_rev[new_id] = part1 + part2; } mf.close(); } vector encode(string text){ vector ids; for(int i=0;i best_pair = {-1, -1}; // finding the pair with the lowest rank (earliest merge) for(int i=0; i pair_key = {ids[i], ids[i+1]}; if(merges.count(pair_key)){ // in BPE, earlier merges have lower "rank" // we need to find which merge came first // for now, we'll use the new_id as a proxy (lower = earlier) int new_id = merges[pair_key]; if(new_id < best_rank){ best_rank = new_id; best_pair = pair_key; best_idx = i; } } } if(best_idx == -1) break; vector new_ids; for(int i=0; i& ids){ string text = ""; for(int id : ids){ if(vocab_rev.count(id)){ text += vocab_rev[id]; }else{ text += ""; } } return text; } int main(){ load_vocab_and_merge_thing(); string text = "To be, or not to be, that is the question: 🙄"; cout<<"Original Text: "< encoded_ids = encode(text); cout<<"Encoded IDs: "; for(int id : encoded_ids){ cout<