File size: 6,696 Bytes
b287045
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
"""

train.py - البرنامج الرئيسي لتدريب نموذج XO.

الاستخدام: python train.py

يعتمد على: tokenizer.py و model.py

"""

import os
import json
import random
import numpy as np
from datasets import load_dataset
from transformers import DataCollatorWithPadding, Trainer, TrainingArguments
from sklearn.metrics import accuracy_score

# استيراد الدوال من الملفين المجاورين
from tokenizer import build_tokenizer, load_tokenizer
from model import build_model

# ------------------------------------------------
# توليد البيانات إن لم تكن موجودة
# ------------------------------------------------
def generate_dataset(output_file="xo_dataset.json", n_games=10000):
    """

    تولد مباريات XO عشوائية وتحفظها كـ JSON Lines.

    كل سطر: {"board": "X..O.....", "move": 4}

    """
    def empty_board():
        return ['.'] * 9

    def legal_moves(board):
        return [i for i, cell in enumerate(board) if cell == '.']

    def make_move(board, pos, player):
        new_board = board[:]
        new_board[pos] = player
        return new_board

    def check_win(board, player):
        wins = [
            (0,1,2), (3,4,5), (6,7,8),
            (0,3,6), (1,4,7), (2,5,8),
            (0,4,8), (2,4,6)
        ]
        return any(all(board[i] == player for i in combo) for combo in wins)

    def random_game():
        board = empty_board()
        history = []
        players = ['X', 'O']
        turn = 0
        while True:
            player = players[turn % 2]
            moves = legal_moves(board)
            if not moves:
                break
            move = random.choice(moves)
            if player == 'X':
                history.append((''.join(board), move))
            board = make_move(board, move, player)
            if check_win(board, player):
                break
            turn += 1
        return history

    print(f"🔄 جاري توليد {n_games} مباراة عشوائية...")
    all_samples = []
    for _ in range(n_games):
        all_samples.extend(random_game())

    with open(output_file, 'w', encoding='utf-8') as f:
        for board_str, move in all_samples:
            f.write(json.dumps({"board": board_str, "move": move}) + '\n')

    print(f"✅ تم إنشاء {len(all_samples)} عينة في {output_file}")
    return output_file

# ------------------------------------------------
# تجهيز البيانات للتدريب
# ------------------------------------------------
def prepare_datasets(data_file, tokenizer):
    """

    تحميل بيانات JSON، تقسيمها، تطبيق الترميز، وإرجاع مجمّع البيانات.

    """
    dataset = load_dataset('json', data_files=data_file, split='train')
    dataset = dataset.train_test_split(test_size=0.1)
    train_dataset = dataset['train']
    eval_dataset = dataset['test']

    def preprocess_function(examples):
        tokenized = tokenizer(
            examples['board'],
            truncation=True,
            padding=False,
            max_length=12,
        )
        tokenized['labels'] = examples['move']
        return tokenized

    train_dataset = train_dataset.map(
        preprocess_function, batched=True, remove_columns=['board', 'move']
    )
    eval_dataset = eval_dataset.map(
        preprocess_function, batched=True, remove_columns=['board', 'move']
    )

    train_dataset.set_format(type='torch', columns=['input_ids', 'attention_mask', 'labels'])
    eval_dataset.set_format(type='torch', columns=['input_ids', 'attention_mask', 'labels'])

    data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

    print(f"✅ بيانات التدريب: {len(train_dataset)} عينة | الاختبار: {len(eval_dataset)} عينة")
    return train_dataset, eval_dataset, data_collator

# ------------------------------------------------
# التدريب
# ------------------------------------------------
def run_training(model, tokenizer, train_dataset, eval_dataset, data_collator):
    """

    إعداد Trainer وتشغيل التدريب، ثم حفظ النموذج.

    """
    # إصلاحات صغيرة للبيئة
    os.environ["TENSORBOARD_LOGGING_DIR"] = "./logs"
    import datasets.config
    datasets.config.TORCHVISION_AVAILABLE = False

    training_args = TrainingArguments(
        output_dir="./xo_model",
        num_train_epochs=5,
        per_device_train_batch_size=64,
        per_device_eval_batch_size=64,
        eval_strategy="epoch",
        save_strategy="epoch",
        logging_dir="./logs",
        logging_steps=100,
        load_best_model_at_end=True,
        metric_for_best_model="eval_loss",
    )

    def compute_metrics(eval_pred):
        logits, labels = eval_pred
        predictions = np.argmax(logits, axis=-1)
        return {"accuracy": accuracy_score(labels, predictions)}

    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=train_dataset,
        eval_dataset=eval_dataset,
        data_collator=data_collator,
        compute_metrics=compute_metrics,
    )

    print("\n🚀 بدء التدريب...")
    trainer.train()

    # حفظ النموذج والـ Tokenizer
    model.save_pretrained("./xo_model")
    tokenizer.save_pretrained("./xo_tokenizer")
    print("✅ تم حفظ النموذج والـ Tokenizer في ./xo_model و ./xo_tokenizer")

# ------------------------------------------------
# البرنامج الرئيسي
# ------------------------------------------------
def main():
    # 1. ملف البيانات
    data_file = "xo_dataset.json"
    if not os.path.exists(data_file):
        generate_dataset(data_file, n_games=10000)
    else:
        print(f"📂 تم العثور على ملف البيانات: {data_file}")

    # 2. Tokenizer (بناء جديد أو تحميل موجود)
    if not os.path.exists("./xo_tokenizer"):
        tokenizer = build_tokenizer(save_path="./xo_tokenizer")
    else:
        tokenizer = load_tokenizer("./xo_tokenizer")

    # 3. بناء النموذج
    model = build_model(tokenizer)

    # 4. تجهيز البيانات
    train_dataset, eval_dataset, data_collator = prepare_datasets(data_file, tokenizer)

    # 5. تدريب
    run_training(model, tokenizer, train_dataset, eval_dataset, data_collator)
    print("\n✨ تم الانتهاء من التدريب بنجاح.")


if __name__ == "__main__":
    main()