File size: 5,870 Bytes
3ab86b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ff4d952
 
3ab86b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env python3
"""
Train a SentencePiece tokenizer for Fusion models.

This creates the tokenizer.model file needed by Fusion's 100K vocabulary.
Training data should be a plain text file with one sentence per line.

Usage:
    python scripts/train_tokenizer.py --input data/tokenizer_train.txt --vocab_size 100000 --output tokenizers/

Requirements:
    pip install sentencepiece

Author: Zhu Zizhan
Project: Fusion-LLM
License: Apache 2.0
"""

import argparse
import os
import sys


def train_tokenizer(input_path: str, vocab_size: int, output_dir: str, model_type: str = "unigram"):
    """Train a SentencePiece tokenizer."""
    try:
        import sentencepiece as spm
    except ImportError:
        print("[ERROR] sentencepiece not installed. Run: pip install sentencepiece")
        sys.exit(1)

    if not os.path.exists(input_path):
        print(f"[ERROR] Training data not found: {input_path}")
        print("Create a plain text file with one sentence per line.")
        print("For bilingual (zh+en) tokenizer, mix Chinese and English text.")
        sys.exit(1)

    os.makedirs(output_dir, exist_ok=True)
    model_prefix = os.path.join(output_dir, "tokenizer")

    print(f"[Tokenizer] Training SentencePiece model...")
    print(f"  Input: {input_path}")
    print(f"  Vocab size: {vocab_size}")
    print(f"  Model type: {model_type}")
    print(f"  Output: {output_dir}/")

    # Special tokens for Fusion
    control_symbols = [
        "<|pad|>", "<|start|>", "<|end|>",
        "<|think_depth_0|>", "<|think_depth_1|>",
        "<|think_depth_2|>", "<|think_depth_3|>",
    ]

    spm.SentencePieceTrainer.train(
        input=input_path,
        model_prefix=model_prefix,
        vocab_size=vocab_size,
        model_type=model_type,
        character_coverage=0.9995,  # High coverage for CJK
        input_sentence_size=10000000,
        shuffle_input_sentence=True,
        control_symbols=control_symbols,
        unk_id=0,
        bos_id=1,
        eos_id=2,
        pad_id=3,
        byte_fallback=True,  # Important for multilingual
        split_by_unicode_script=True,
        allow_whitespace_only_pieces=True,
        normalization_rule_name='identity',  # Avoid nmt_nfkc precompiled charsmap issues

    )

    model_path = os.path.join(output_dir, "tokenizer.model")
    vocab_path = os.path.join(output_dir, "tokenizer.vocab")

    print(f"\n[Done] Tokenizer trained successfully!")
    print(f"  Model: {model_path}")
    print(f"  Vocab: {vocab_path}")

    # Verify
    sp = spm.SentencePieceProcessor()
    sp.load(model_path)
    print(f"  Actual vocab size: {sp.get_piece_size()}")

    # Test
    test_zh = "Fusion是一个开源大语言模型"
    test_en = "Fusion is an open-source language model"
    print(f"\n  Test encode (zh): {test_zh}")
    print(f"    -> {sp.encode(test_zh)}")
    print(f"  Test encode (en): {test_en}")
    print(f"    -> {sp.encode(test_en)}")


def create_sample_training_data(output_path: str, num_lines: int = 100000):
    """Create sample training data for tokenizer (for testing only)."""
    import random

    print(f"[Sample Data] Creating sample training data: {output_path}")
    samples_zh = [
        "人工智能是计算机科学的一个重要分支",
        "深度学习使用多层神经网络来模拟人脑",
        "自然语言处理帮助计算机理解人类语言",
        "机器学习使计算机能够从数据中学习",
        "大语言模型在文本生成任务中表现出色",
        "Transformer架构彻底改变了自然语言处理领域",
        "注意力机制是现代深度学习的核心组件",
        "预训练语言模型通过大规模语料学习语言知识",
        "微调技术使模型适应特定下游任务",
        "开源模型推动了人工智能技术的普及",
    ]
    samples_en = [
        "Artificial intelligence is a branch of computer science",
        "Deep learning uses multi-layer neural networks",
        "Natural language processing helps computers understand language",
        "Machine learning enables computers to learn from data",
        "Large language models excel at text generation tasks",
        "The Transformer architecture revolutionized NLP",
        "Attention mechanisms are core components of modern deep learning",
        "Pre-trained models learn language knowledge from large corpora",
        "Fine-tuning adapts models to specific downstream tasks",
        "Open-source models promote the democratization of AI",
    ]

    with open(output_path, 'w', encoding='utf-8') as f:
        for _ in range(num_lines):
            if random.random() > 0.5:
                f.write(random.choice(samples_zh) + "\n")
            else:
                f.write(random.choice(samples_en) + "\n")

    print(f"  Generated {num_lines} lines")


def main():
    parser = argparse.ArgumentParser(description="Train Fusion SentencePiece tokenizer")
    parser.add_argument("--input", type=str, default="data/tokenizer_train.txt",
                        help="Path to training data (one sentence per line)")
    parser.add_argument("--vocab_size", type=int, default=100000,
                        help="Vocabulary size (default: 100000)")
    parser.add_argument("--output", type=str, default="tokenizers/",
                        help="Output directory")
    parser.add_argument("--model_type", type=str, default="unigram",
                        choices=["unigram", "bpe"], help="Model type")
    parser.add_argument("--create_sample_data", action="store_true",
                        help="Create sample training data for testing")
    args = parser.parse_args()

    if args.create_sample_data:
        create_sample_training_data(args.input)
        return

    train_tokenizer(args.input, args.vocab_size, args.output, args.model_type)


if __name__ == "__main__":
    main()