File size: 4,928 Bytes
26d5b81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import json
import os
import argparse
import logging

logger = logging.getLogger(__name__)

SPECIAL_TOKENS = {"<pad>": 0, "<s>": 1, "</s>": 2, "<unk>": 3}


def build_chinese_chars(start_id=4, count=3755):
    vocab = {}
    idx = start_id
    for code in range(0x4E00, 0x4E00 + count):
        if idx >= start_id + count:
            break
        char = chr(code)
        vocab[char] = idx
        idx += 1
    return vocab


def build_english_subwords(start_id, count=500):
    vocab = {}
    idx = start_id
    prefixes = ["un", "re", "pre", "dis", "mis", "over", "under", "out", "sub", "inter", "anti", "non", "semi", "multi", "bi", "co", "ex", "in", "im", "il", "ir"]
    suffixes = ["ing", "ed", "er", "est", "ly", "tion", "sion", "ment", "ness", "ity", "ous", "ive", "able", "ible", "ful", "less", "al", "ial", "ic", "ical"]
    roots = ["the", "be", "to", "of", "and", "a", "in", "that", "have", "it", "for", "not", "on", "with", "he", "as", "you", "do", "at", "this", "but", "his", "by", "from", "they", "we", "say", "her", "she", "or", "an", "will", "my", "one", "all", "would", "there", "their", "what", "so", "up", "out", "if", "about", "who", "get", "which", "go", "me", "when", "make", "can", "like", "time", "no", "just", "him", "know", "take", "people", "into", "year", "your", "good", "some", "could", "them", "see", "other", "than", "then", "now", "look", "only", "come", "its", "over", "think", "also", "back", "after", "use", "two", "how", "our", "work", "first", "well", "way", "even", "new", "want", "because", "any", "these", "give", "day", "most", "us"]
    for r in roots:
        if idx >= start_id + count:
            break
        vocab[r] = idx
        idx += 1
    for p in prefixes:
        for r in roots[:50]:
            if idx >= start_id + count:
                break
            token = p + r
            if token not in vocab:
                vocab[token] = idx
                idx += 1
    for s in suffixes:
        for r in roots[:50]:
            if idx >= start_id + count:
                break
            token = r + s
            if token not in vocab:
                vocab[token] = idx
                idx += 1
    return vocab


def build_punctuation(start_id):
    vocab = {}
    idx = start_id
    puncts = list("。,、;:!?…—""''()【】《》·~、.,!?;:\"'()[]{}<>-_/\\|@#$%^&*+=~`")
    for p in puncts:
        vocab[p] = idx
        idx += 1
    return vocab


def build_digits(start_id):
    vocab = {}
    idx = start_id
    for d in "0123456789":
        vocab[d] = idx
        idx += 1
    for combo in ["10", "00", "01", "20", "30", "50", "100", "200", "500", "1000"]:
        vocab[combo] = idx
        idx += 1
    return vocab


def build_merges(vocab, max_merges=2000):
    merges = []
    tokens = sorted(vocab.keys(), key=lambda t: len(t), reverse=True)
    for i, t1 in enumerate(tokens):
        if len(merges) >= max_merges:
            break
        for t2 in tokens[i:i+10]:
            if len(merges) >= max_merges:
                break
            combined = t1 + t2
            if combined in vocab:
                merges.append(f"{t1} {t2}")
    return merges


def generate_tokenizer_json(vocab_size=5000):
    vocab = dict(SPECIAL_TOKENS)
    next_id = len(vocab)

    cn = build_chinese_chars(next_id, 3755)
    vocab.update(cn)
    next_id = max(vocab.values()) + 1

    en = build_english_subwords(next_id, 500)
    vocab.update(en)
    next_id = max(vocab.values()) + 1

    punct = build_punctuation(next_id)
    vocab.update(punct)
    next_id = max(vocab.values()) + 1

    digits = build_digits(next_id)
    vocab.update(digits)

    while len(vocab) > vocab_size:
        max_id = max(vocab.values())
        for k, v in list(vocab.items()):
            if v == max_id:
                del vocab[k]
                break

    merges = build_merges(vocab)

    return {
        "model_type": "bpe",
        "vocab_size": vocab_size,
        "special_tokens": SPECIAL_TOKENS,
        "vocab": vocab,
        "merges": merges,
    }


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    parser = argparse.ArgumentParser(description="NeuroFlow BPE词表适配器")
    parser.add_argument("--vocab-size", type=int, default=5000, help="词表大小")
    parser.add_argument("--output", type=str, default="configs/tokenizer_cn_013.json", help="输出路径")
    args = parser.parse_args()

    tokenizer = generate_tokenizer_json(args.vocab_size)
    os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True)
    with open(args.output, "w", encoding="utf-8") as f:
        json.dump(tokenizer, f, indent=2, ensure_ascii=False)
    logger.info(f"词表已保存到 {args.output}, vocab_size={len(tokenizer['vocab'])}, merges={len(tokenizer['merges'])}")