File size: 3,752 Bytes
f49ce73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
"""
Türkçe + İngilizce için BPE tokenizer eğitim scripti (HuggingFace `tokenizers` kütüphanesi).

Kurulum:
    pip install tokenizers

Kullanım:
    python tokenizer.py --files data_tr.txt data_en.txt --vocab_size 32000 --out tr_en_tokenizer.json

Notlar (Türkçe için önemli detaylar):
- NFC unicode normalizasyonu kullanılır (İ/ı/ş/ğ/ç/ö/ü gibi karakterler bozulmasın diye).
- Byte-level BPE seçildi: hem Türkçe'nin zengin çekim eklerini (agglutinative yapı)
  alt-kelime (subword) parçalarına iyi bölebiliyor, hem de OOV (bilinmeyen karakter) sorunu yaşanmıyor.
- lowercase yapılmıyor çünkü Türkçe'de "İstanbul/istanbul" ve "I/ı/İ/i" ayrımı kritik;
  case-folding kelime anlamını bozabilir (İ <-> i, I <-> ı farkı İngilizce'den farklı çalışır).
- Özel tokenlar hem TR hem EN kullanım senaryolarını (chat, kod, doldurma) kapsayacak şekilde eklendi.
"""

import argparse

from tokenizers import Tokenizer, normalizers, pre_tokenizers, decoders, processors
from tokenizers.models import BPE
from tokenizers.trainers import BpeTrainer
from tokenizers.normalizers import NFC


SPECIAL_TOKENS = [
    "<pad>",
    "<unk>",
    "<bos>",
    "<eos>",
    "<sep>",
    "<mask>",
    "<|user|>",
    "<|assistant|>",
    "<|system|>",
]


def build_tokenizer() -> Tokenizer:
    tok = Tokenizer(BPE(unk_token="<unk>"))
    # NFC normalizasyonu: Türkçe aksanlı/nokta karakterlerinin (İ, ı, ş, ğ, ç, ö, ü)
    # tutarlı tek bir unicode formunda kalmasını sağlar.
    tok.normalizer = normalizers.Sequence([NFC()])
    # Byte-level pre-tokenizer: hem TR hem EN metni kayıpsız şekilde byte'lara ayırır,
    # bilinmeyen karakter problemi (OOV) oluşmaz.
    tok.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
    tok.decoder = decoders.ByteLevel()
    tok.post_processor = processors.TemplateProcessing(
        single="<bos> $A <eos>",
        pair="<bos> $A <eos> <bos> $B <eos>",
        special_tokens=[("<bos>", 2), ("<eos>", 3)],
    )
    return tok


def train(files, vocab_size, out_path, min_frequency=2):
    tok = build_tokenizer()
    trainer = BpeTrainer(
        vocab_size=vocab_size,
        min_frequency=min_frequency,
        special_tokens=SPECIAL_TOKENS,
        show_progress=True,
        # Türkçe agglutinative yapı için ek/kelime köklerinin ayrı token olarak
        # öğrenilebilmesi adına alfabeyi baştan tüm byte'larla başlatıyoruz.
        initial_alphabet=pre_tokenizers.ByteLevel.alphabet(),
    )
    tok.train(files, trainer)
    tok.save(out_path)
    print(f"Tokenizer kaydedildi: {out_path}")
    print(f"Gerçek vocab boyutu: {tok.get_vocab_size()}")
    return tok


def quick_test(tok_path):
    tok = Tokenizer.from_file(tok_path)
    samples = [
        "Merhaba dünya, bugün hava çok güzel ve İstanbul'da yürüyüş yapıyorum.",
        "Hello world, this is a quick tokenizer sanity check for the model.",
        "Yapay zeka modelleri büyük miktarda veriyle eğitiliyor.",
    ]
    for s in samples:
        enc = tok.encode(s)
        print(f"\nMetin: {s}")
        print(f"Token sayısı: {len(enc.ids)}")
        print(f"Tokenlar: {enc.tokens}")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--files", nargs="+", required=True, help="TR ve EN düz metin dosyaları (.txt)")
    parser.add_argument("--vocab_size", type=int, default=36864)
    parser.add_argument("--out", type=str, default="tr_en_tokenizer.json")
    parser.add_argument("--test", action="store_true", help="Eğitimden sonra örnek cümlelerle test et")
    args = parser.parse_args()

    train(args.files, args.vocab_size, args.out)
    if args.test:
        quick_test(args.out)