BertTokenizer: `RemoveNonSpacingMarks = true` fails to strip accents from precomposed characters under invariant globalization
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Facilidade para iniciantes
- 48/100
- Tipo de issue
- Bug
- Clareza
- Razoavelmente clara
- Status de atividade
- Ativa
- Stack de tecnologia
- csharp
- Domínio
- machine-learning
Direção de pesquisa
Start at BertTokenizer.Create and the RemoveNonSpacingMarks handling, then run the supplied reproduction once with ICU and once with InvariantGlobalization=true. The issue is done when precomposed accents are handled consistently across both modes, or creation fails clearly when decomposition is unavailable; add coverage for the reported inputs and configuration.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
BertOptions.RemoveNonSpacingMarks = true is the setting that makes BertTokenizer strip accents, as uncased BERT vocabularies expect: café becomes cafe. Accent stripping works by decomposing the text to Unicode Form D and dropping the non-spacing marks. Under globalization-invariant mode, string.Normalize returns its input unchanged — that is documented behaviour — so there are no marks to drop. Precomposed accented characters remain intact; for vocabularies such as all-MiniLM-L6-v2, this can cause affected words to miss their expected WordPiece decomposition and become [UNK].
Nothing signals this. BertTokenizer.Create succeeds, encoding succeeds, and for precomposed text the output is identical to having left the option off.
Invariant globalization is common in size-optimised .NET deployments. Microsoft's Alpine and Ubuntu Chiseled images ship without ICU (their extra variants add it), and the .NET 10 Native AOT templates enable it: dotnet new console --aot, dotnet new worker --aot and dotnet new webapiaot all set <InvariantGlobalization>true</InvariantGlobalization>. A service that tokenizes correctly in development can silently stop stripping accents when deployed in one of these configurations.
Reproduction
The reproduction below uses the vocab.txt from sentence-transformers/all-MiniLM-L6-v2.
using System.Text;
using Microsoft.ML.Tokenizers;
var vocabPath = args[0];
var vocab = File.ReadAllLines(vocabPath);
var tokenizer = BertTokenizer.Create(vocabPath, new BertOptions { RemoveNonSpacingMarks = true });
Console.WriteLine($"Normalize(FormD) of \"\\u00e9\": {"\u00e9".Normalize(NormalizationForm.FormD).Length} code unit(s)");
var ids = tokenizer.EncodeToIds("Café crème brûlée in São Paulo, naïve résumé");
Console.WriteLine(string.Join(' ', ids.Select(id => vocab[id])));
Run it once as built, and once in invariant mode:
dotnet run -- path/to/vocab.txt
dotnet run -p:InvariantGlobalization=true -- path/to/vocab.txt
Under ICU:
Normalize(FormD) of "\u00e9": 2 code unit(s)
[CLS] cafe cr ##eme br ##ule ##e in sao paulo , naive resume [SEP]
Under invariant globalization:
Normalize(FormD) of "\u00e9": 1 code unit(s)
[CLS] [UNK] [UNK] [UNK] in [UNK] paulo , [UNK] [UNK] [SEP]
The ICU output matches Hugging Face's BertTokenizer exactly. The invariant output is identical to running with RemoveNonSpacingMarks = false.
Only precomposed input is affected; input that is already decomposed is handled correctly because its combining marks are present to be dropped.
Expected behaviour
Either of these would remove the silent failure:
- Strip accents without relying on
string.Normalize, for example by carrying the decomposition data the option needs, so it behaves the same in every globalization mode; or - Fail loudly: throw from
BertTokenizer.CreatewhenRemoveNonSpacingMarks = trueand decomposition is unavailable, with a message naming the fix (<InvariantGlobalization>false</InvariantGlobalization>, or an ICU-carrying image such as a chiseled-extravariant).
The check for (2) is one line at construction: "\u00e9".Normalize(NormalizationForm.FormD).Length == 2.
Impact
In my test with all-MiniLM-L6-v2, mean pooling, and cosine similarity against the ICU tokenization, the embeddings of affected text diverge substantially:
| input | ICU | invariant |
|---|---|---|
Café crème brûlée in São Paulo, naïve résumé |
1.000000 | 0.333027 |
| a French sentence | 1.000000 | 0.545866 |
| a Spanish sentence | 1.000000 | 0.502484 |
| a German sentence | 1.000000 | 0.572320 |
accented capitals (ÉCOLE ÉLÉMENTAIRE) |
1.000000 | 0.089375 |
| plain ASCII | 1.000000 | 1.000000 |
Versions
- Microsoft.ML.Tokenizers 2.0.0 and 3.0.0-preview.26457.2, identical output
- .NET 10.0.12
Related
#7724 and #7725 report other BertTokenizer differences from Hugging Face's tokenizer. This one is independent of them: the output is correct under ICU and wrong only because of the globalization mode.
- Linguagem predominante
- C#
- Estrelas
- 9.4k
- Forks
- 2k
- Merge médio
- 2d 18h
- PRs com merge (30d)
- 64
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Tem um modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de dotnet/machinelearning
-
area-Native untriaged
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
dotnet/machinelearning#7837 ·
Mantenedores costumam responder em até 1 dia
-
area-TensorFlow untriaged
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 62/100
dotnet/machinelearning#7836 ·
Mantenedores costumam responder em até 1 dia
-
ToArrowRecordBatches accumulates arrays from previous batches, producing invalid record batchesAbertaarea-DataFrame bug untriaged
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 62/100
dotnet/machinelearning#7799 ·
Mantenedores costumam responder em até 1 dia
-
LlamaTokenizer encodes some runs of repeated characters differently from SentencePieceTalvez já em andamento @Laurianti assumiu há 42 dias. Abertaarea-Tokenizers untriaged
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 88/100
dotnet/machinelearning#7751 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
BertTokenizer merges words separated only by `\n`, `\t` or `\r`Talvez já em andamento @Laurianti assumiu há 23 dias. Abertaarea-Tokenizers green
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
dotnet/machinelearning#7724 · 3 comentários ·
Mantenedores costumam responder em até 1 dia
Todas as issues de dotnet/machinelearning
Issues semelhantes
-
bug emitter:client:csharp
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 84/100
microsoft/typespec#12572 · 1 comentário · 1 reação ·
Mantenedores costumam responder em até 1 dia
-
needs-area-label
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 2 dias
-
AWS Lambda: KeyNotFoundException drops HTTP-triggered transactions when allowAllHeaders is disabledAbertabug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 70/100
newrelic/newrelic-dotnet-agent#3949 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
area-networking
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
dotnet/aspnetcore#70545 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 87/100
Mantenedores costumam responder em até 1 dia