fix: Remediate 219 glitch tokens in tokenizer.json

#50
by gkielian - opened

Glitch Token Remediation — Tokenizer Patch

Summary

This PR patches tokenizer.json to remediate 219 glitch tokens identified
by embedding vector analysis. Glitch tokens are vocabulary entries whose embeddings
have collapsed to near-identical representations, causing unpredictable model behavior
when they appear in input.

No model weights are modified — this is a tokenizer-only fix.

Technique: BPE Merge Pruning

Merge pruning (219 tokens): Removes the BPE merge rules that
produce multi-character glitch tokens. Every vocabulary entry and token ID stays intact,
but the flagged entries become unreachable — text that would have matched is now tokenized as
well-trained constituent subwords instead.

253 merges were removed for 219 merge-pruned tokens because some glitch tokens have more than one BPE merge path, and every path must be removed.

Before / after examples: google/gemma-4-E4B-it

Merge prune example: drawingCodehint, token ID 226565

Prompt: Please repeat the following string exactly, with nothing else: "drawingCodehint"

Tokens for the string Model output
Before ['drawingCodehint'] → [226565] "" ❌
After ['drawingCode', 'hint'] → [140974, 45581] "drawingCodehint" ✅

⚠️ Note for Agentic Systems

Many remediated glitch tokens are shared across Gemma model families (Gemma 1, 2, 3, 4).
In agentic pipelines, care should be taken that these token strings are not inadvertently
injected into prompts for unpatched models. We recommend applying this patch consistently
across all Gemma models used in a pipeline.

What is preserved

  • ✅ len(tokenizer) — unchanged (262,144)
  • ✅ All token IDs — stable, no re-indexing
  • ✅ Chat template — identical to original
  • ✅ tokenizer_config.json — identical to original
  • ✅ All special tokens and added_tokens — unchanged
  • ✅ Normal text tokenization — verified via benchmarks
  • ✅ Zero model weight changes

Diff summary

Original Patched Delta
Vocab size 262,144 262,144 0
Merges 514,906 514,653 −253

Disclosed side effects

  • Downstream compound tokens (5): 5 non-target vocabulary entries whose sole BPE merge path went through a pruned glitch token now decompose into constituent subwords instead of emitting a single token ID. QA inference evaluation (What is "<token>"?, original vs. patched tokenizer on identical weights) confirmed 0 behavioral regressions across these compound tokens (and eliminated garbled/hallucinated replies on compounds built from collapsed subwords).
  • Inert consuming merges retained (9): 9 surviving merge rules reference a pruned glitch token as an input operand. Because that operand can never be produced, these merges are inert at runtime (retaining them keeps the diff minimal with identical behavior).
  • Already-shadowed target merges (4): 4 flagged target tokens (▁YYYY, ▁yyyy, ▁diffformul, ▁::::::::) were already shadowed on their isolated surface strings by higher-priority BPE merges in the original tokenizer; removing their merge entries eliminates any residual boundary/substring path and is a no-op on normal text.
Click to expand all 5 downstream compound tokens
ID Token Patched subword decomposition
131116 ▁StarSXmlClass ['▁Star', 'SXml', 'Class']
137313 AWSJavaScriptSDK ['AWS', 'JavaScript', 'SDK']
151654 rightsquigarrow ['right', 'squig', 'arrow']
215095 ▁துய்யமணி ['▁து', 'ய்ய', 'மணி']
220097 MDEwOlJlcG ['MDEw', 'Ol', 'JlcG']

Removed merges (253)

Click to expand the full list of 253 removed merges
[".\"+\"|", "\"+\""]  →  ."+"|"+"
["AWS", "JavaScript"]  →  AWSJavaScript
["AllFilesIn", "MkvDir"]  →  AllFilesInMkvDir
["CYCL", "ONEDB"]  →  CYCLONEDB
["Case", "Missense"]  →  CaseMissense
["Case", "PTV"]  →  CasePTV
["Collin", "ToDO"]  →  CollinToDO
["Consec", "HtIdx"]  →  ConsecHtIdx
["Control", "PTV"]  →  ControlPTV
["Custom", "Glare"]  →  CustomGlare
["CustomGlare", "Def"]  →  CustomGlareDef
["DT", "MakeRectInCM"]  →  DTMakeRectInCM
["Days", "GE"]  →  DaysGE
["Denovo", "Mis"]  →  DenovoMis
["ELEASE", "STR"]  →  ELEASESTR
["ENEMY", "PLACE"]  →  ENEMYPLACE
["Fcm", "Php"]  →  FcmPhp
["Foldout", "GC"]  →  FoldoutGC
["GTBase", "Alert"]  →  GTBaseAlert
["Get", "ParaPkg"]  →  GetParaPkg
["GoObject", "AllRef"]  →  GoObjectAllRef
["Go", "PrintError"]  →  GoPrintError
["Go", "RawContext"]  →  GoRawContext
["Go", "SrvGroupIndex"]  →  GoSrvGroupIndex
["H", "IMQTTRVM"]  →  HIMQTTRVM
["IMQTTR", "VM"]  →  IMQTTRVM
["KAKKIA", "INEN"]  →  KAKKIAINEN
["LuaPush", "Int"]  →  LuaPushInt
["Lua", "ToJavaResult"]  →  LuaToJavaResult
["MDEw", "Ol"]  →  MDEwOl
["MakeRect", "InCM"]  →  MakeRectInCM
["Mkv", "Dir"]  →  MkvDir
["MyShop", "name"]  →  MyShopname
["Ordinate", "Tuple"]  →  OrdinateTuple
["PAF", "Struct"]  →  PAFStruct
["P", "QAry"]  →  PQAry
["PQ", "Ary"]  →  PQAry
["RefIn", "SrvGroup"]  →  RefInSrvGroup
["Ret", "JpegLen"]  →  RetJpegLen
["SAFER", "ELEASESTR"]  →  SAFERELEASESTR
["SRP", "Basic"]  →  SRPBasic
["SRP", "BinBuf"]  →  SRPBinBuf
["SRPInterface", "Item"]  →  SRPInterfaceItem
["SRP", "ParaPkg"]  →  SRPParaPkg
["SRPS", "XML"]  →  SRPSXML
["SRP", "SrvGroup"]  →  SRPSrvGroup
["SRPS", "rvGroup"]  →  SRPSrvGroup
["Same", "WordInterval"]  →  SameWordInterval
["Simulator", "Flood"]  →  SimulatorFlood
["SrvGroup", "Index"]  →  SrvGroupIndex
["StarBinBuf", "Body"]  →  StarBinBufBody
["StarObject", "Body"]  →  StarObjectBody
["StarParaPkg", "Body"]  →  StarParaPkgBody
["StarSXml", "Body"]  →  StarSXmlBody
["StarService", "Body"]  →  StarServiceBody
["StarSrvGroup", "Body"]  →  StarSrvGroupBody
["StructOf", "Object"]  →  StructOfObject
["TP", "ASDW"]  →  TPASDW
["Term", "ObjectDefer"]  →  TermObjectDefer
["Transc", "Hist"]  →  TranscHist
["U", "imcoords"]  →  Uimcoords
["VSLU", "ATYPE"]  →  VSLUATYPE
["WMN", "MDA"]  →  WMNMDA
["WeakTable", "Mutex"]  →  WeakTableMutex
["WithSize", "InCM"]  →  WithSizeInCM
["Ziel", "Panel"]  →  ZielPanel
["addKill", "Bonus"]  →  addKillBonus
["addKill", "Penalty"]  →  addKillPenalty
["adig", "anik"]  →  adiganik
["adigan", "ik"]  →  adiganik
["ak", "arantadhatu"]  →  akarantadhatu
["angolo", "Rad"]  →  angoloRad
["angolo", "Tocco"]  →  angoloTocco
["arant", "adhatu"]  →  arantadhatu
["arantad", "hatu"]  →  arantadhatu
["atthavid", "u"]  →  atthavidu
["attup", "adani"]  →  attupadani
["attu", "vasena"]  →  attuvasena
["ban", "ipi"]  →  banipi
["bani", "pi"]  →  banipi
["blusas", "Fem"]  →  blusasFem
["capture", "cpu"]  →  capturecpu
["ch", "ccgi"]  →  chccgi
["check", "katore"]  →  checkkatore
["co", "OrdinateTuple"]  →  coOrdinateTuple
["colour", "CodeDict"]  →  colourCodeDict
["country", "geocode"]  →  countrygeocode
["custom", "Glare"]  →  customGlare
["df", "sonic"]  →  dfsonic
["dfs", "onic"]  →  dfsonic
["done", "ProcessAvg"]  →  doneProcessAvg
["draw", "countery"]  →  drawcountery
["drawingCode", "hint"]  →  drawingCodehint
["dw", "RetJpegLen"]  →  dwRetJpegLen
["eco", "expr"]  →  ecoexpr
["edLeft", "Shape"]  →  edLeftShape
["edRight", "Shape"]  →  edRightShape
["eltat", "double"]  →  eltatdouble
["faulse", "Ans"]  →  faulseAns
["foe", "Place"]  →  foePlace
["get", "HDRProcessor"]  →  getHDRProcessor
["get", "starcore"]  →  getstarcore
["getstarcore", "data"]  →  getstarcoredata
["gmzy", "h"]  →  gmzyh
["grafo", "Existe"]  →  grafoExiste
["him", "qttrvm"]  →  himqttrvm
["ineed", "follower"]  →  ineedfollower
["inertia", "Seq"]  →  inertiaSeq
["int", "Fragmentation"]  →  intFragmentation
["jols", "endev"]  →  jolsendev
["js", "bpmOb"]  →  jsbpmOb
["kit", "opssynth"]  →  kitopssynth
["last", "DamageTook"]  →  lastDamageTook
["local", "instrList"]  →  localinstrList
["m", "BlitzID"]  →  mBlitzID
["mark", "UpdateChoice"]  →  markUpdateChoice
["ops", "synth"]  →  opssynth
["opss", "ynth"]  →  opssynth
["pJ", "PEGBuf"]  →  pJPEGBuf
["paren", "macro"]  →  parenmacro
["partial", "owner"]  →  partialowner
["pmm", "Imp"]  →  pmmImp
["pos", "Tocco"]  →  posTocco
["qttr", "vm"]  →  qttrvm
["respArray", "All"]  →  respArrayAll
["right", "squig"]  →  rightsquig
["sad", "urdu"]  →  sadurdu
["sadurdu", "poetry"]  →  sadurdupoetry
["sal", "expr"]  →  salexpr
["selectTable", "X"]  →  selectTableX
["selectTable", "Y"]  →  selectTableY
["smo", "io"]  →  smoio
["sor", "finaly"]  →  sorfinaly
["squarePos", "Vecchio"]  →  squarePosVecchio
["start", "ZielPanel"]  →  startZielPanel
["tcp", "UniqueID"]  →  tcpUniqueID
["testGet", "Popup"]  →  testGetPopup
["time", "PlusEvents"]  →  timePlusEvents
["tochy", "odikwa"]  →  tochyodikwa
["total", "BlockFit"]  →  totalBlockFit
["waved", "eltatdouble"]  →  wavedeltatdouble
["wired", "Elems"]  →  wiredElems
["தல", "மோங்கு"]  →  தலமோங்கு
["ய்ய", "மணி"]  →  ய்யமணி
["వెట్‌", "స్కీ"]  →  వెట్‌స్కీ
["▁", "::::::::"]  →  ▁::::::::
["▁::", "::::::"]  →  ▁::::::::
["▁A", "fdPar"]  →  ▁AfdPar
["▁Afd", "Par"]  →  ▁AfdPar
["▁Archers", "Unit"]  →  ▁ArchersUnit
["▁Atr", "ayam"]  →  ▁Atrayam
["▁Atra", "yam"]  →  ▁Atrayam
["▁C", "AdxRtList"]  →  ▁CAdxRtList
["▁CC", "BUNDLE"]  →  ▁CCBUNDLE
["▁Control", "Missense"]  →  ▁ControlMissense
["▁", "ControlPTV"]  →  ▁ControlPTV
["▁Control", "PTV"]  →  ▁ControlPTV
["▁DT", "MakeRect"]  →  ▁DTMakeRect
["▁", "FuncParamNum"]  →  ▁FuncParamNum
["▁Func", "ParamNum"]  →  ▁FuncParamNum
["▁GoObject", "To"]  →  ▁GoObjectTo
["▁", "GoRawContext"]  →  ▁GoRawContext
["▁Go", "RawContext"]  →  ▁GoRawContext
["▁Go", "SRP"]  →  ▁GoSRP
["▁", "GoSrvGroupIndex"]  →  ▁GoSrvGroupIndex
["▁Go", "SrvGroupIndex"]  →  ▁GoSrvGroupIndex
["▁", "HIMQTTRVM"]  →  ▁HIMQTTRVM
["▁H", "IMQTTRVM"]  →  ▁HIMQTTRVM
["▁HRM", "Client"]  →  ▁HRMClient
["▁LG", "AGEmoji"]  →  ▁LGAGEmoji
["▁Lua", "ToGoObject"]  →  ▁LuaToGoObject
["▁", "LuaToJavaResult"]  →  ▁LuaToJavaResult
["▁Lua", "ToJavaResult"]  →  ▁LuaToJavaResult
["▁ND", "IndexArray"]  →  ▁NDIndexArray
["▁", "NewParaPkg"]  →  ▁NewParaPkg
["▁New", "ParaPkg"]  →  ▁NewParaPkg
["▁", "PafHandle"]  →  ▁PafHandle
["▁Paf", "Handle"]  →  ▁PafHandle
["▁RUTARE", "AL"]  →  ▁RUTAREAL
["▁RUTARE", "L"]  →  ▁RUTAREL
["▁RUTAREL", "ATIV"]  →  ▁RUTARELATIV
["▁Ref", "ToGoObject"]  →  ▁RefToGoObject
["▁SIINFE", "KLC"]  →  ▁SIINFEKLC
["▁SIINFEKL", "C"]  →  ▁SIINFEKLC
["▁SRP", "Go"]  →  ▁SRPGo
["▁SRPGo", "Get"]  →  ▁SRPGoGet
["▁SRPGoGet", "Str"]  →  ▁SRPGoGetStr
["▁SRPGo", "SetStr"]  →  ▁SRPGoSetStr
["▁Spring", "ObjectID"]  →  ▁SpringObjectID
["▁SrvGroup", "Class"]  →  ▁SrvGroupClass
["▁", "StarSXml"]  →  ▁StarSXml
["▁Star", "SXml"]  →  ▁StarSXml
["▁", "TPASDW"]  →  ▁TPASDW
["▁TP", "ASDW"]  →  ▁TPASDW
["▁", "TermObjectDefer"]  →  ▁TermObjectDefer
["▁Term", "ObjectDefer"]  →  ▁TermObjectDefer
["▁TestAvg", "Callback"]  →  ▁TestAvgCallback
["▁", "YYYY"]  →  ▁YYYY
["▁YY", "YY"]  →  ▁YYYY
["▁add", "ConfigureArg"]  →  ▁addConfigureArg
["▁add", "SBOM"]  →  ▁addSBOM
["▁ak", "ammak"]  →  ▁akammak
["▁app", "asidd"]  →  ▁appasidd
["▁atth", "udd"]  →  ▁atthudd
["▁bhuv", "adigane"]  →  ▁bhuvadigane
["▁check", "HDROffsets"]  →  ▁checkHDROffsets
["▁coi", "Alarm"]  →  ▁coiAlarm
["▁", "cytyle"]  →  ▁cytyle
["▁cy", "tyle"]  →  ▁cytyle
["▁cyt", "yle"]  →  ▁cytyle
["▁dSample", "Height"]  →  ▁dSampleHeight
["▁dSample", "Width"]  →  ▁dSampleWidth
["▁dhat", "ussa"]  →  ▁dhatussa
["▁dhatu", "ssa"]  →  ▁dhatussa
["▁diff", "formul"]  →  ▁diffformul
["▁ditt", "iyam"]  →  ▁dittiyam
["▁", "doneProcessAvg"]  →  ▁doneProcessAvg
["▁done", "ProcessAvg"]  →  ▁doneProcessAvg
["▁", "ecoexpr"]  →  ▁ecoexpr
["▁eco", "expr"]  →  ▁ecoexpr
["▁evam", "adisu"]  →  ▁evamadisu
["▁hetuk", "att"]  →  ▁hetukatt
["▁icc", "adini"]  →  ▁iccadini
["▁iccad", "ini"]  →  ▁iccadini
["▁ic", "capi"]  →  ▁iccapi
["▁icc", "api"]  →  ▁iccapi
["▁jaû", "nes"]  →  ▁jaûnes
["▁jobSearch", "Repo"]  →  ▁jobSearchRepo
["▁m", "SwisTrackCore"]  →  ▁mSwisTrackCore
["▁magick", "woods"]  →  ▁magickwoods
["▁", "matchStudentNo"]  →  ▁matchStudentNo
["▁match", "StudentNo"]  →  ▁matchStudentNo
["▁mdl", "MeshVD"]  →  ▁mdlMeshVD
["▁min", "Goto"]  →  ▁minGoto
["▁", "nohVP"]  →  ▁nohVP
["▁noh", "VP"]  →  ▁nohVP
["▁o", "LetterLocation"]  →  ▁oLetterLocation
["▁process", "PerRow"]  →  ▁processPerRow
["▁sc", "StudentVector"]  →  ▁scStudentVector
["▁student", "LVector"]  →  ▁studentLVector
["▁subTest", "Avg"]  →  ▁subTestAvg
["▁subTest", "HDR"]  →  ▁subTestHDR
["▁subTest", "Panorama"]  →  ▁subTestPanorama
["▁suddhak", "att"]  →  ▁suddhakatt
["▁total", "BlockUsed"]  →  ▁totalBlockUsed
["▁totalBlockUsed", "A"]  →  ▁totalBlockUsedA
["▁", "yyyy"]  →  ▁yyyy
["▁yy", "yy"]  →  ▁yyyy
["▁চিদা", "ভ"]  →  ▁চিদাভ
["▁শরনার্থ", "িদের"]  →  ▁শরনার্থিদের
["▁শরনার্থি", "দের"]  →  ▁শরনার্থিদের
["▁బ్లా", "వెట్‌స్కీ"]  →  ▁బ్లావెట్‌స్కీ
["加入", "参数向量中"]  →  加入参数向量中

Files changed

  • tokenizer.json — BPE merge rules pruned (indent=2 formatting preserved for clean line-by-line diffs)

How to verify

from transformers import AutoTokenizer

orig = AutoTokenizer.from_pretrained("google/gemma-4-E4B-it")
patched = AutoTokenizer.from_pretrained("google/gemma-4-E4B-it", revision="refs/pr/50")

# Vocab size and normal text tokenization are unchanged
assert len(orig) == len(patched) == 262144
text = "Hello world! 你好世界 🎉 def foo(): return 42"
assert orig(text)["input_ids"] == patched(text)["input_ids"]

# Glitch token `drawingCodehint` (ID 226565) is no longer produced
assert 226565 in orig("drawingCodehint")["input_ids"]
assert 226565 not in patched("drawingCodehint")["input_ids"]

Algorithm Details

For full details on the glitch token collection algorithm and remediation techniques,
reach out to @gkielian .

Related

This is part of a series of tokenizer fixes across all Gemma model repositories
(Gemma 1, 2, 3, 4, and MedGemma — 18 models total).

cc @dougreid @ssmoot

Ready to merge
This branch is ready to get merged automatically.

Sign up or log in to comment