| """
|
| 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()
|
|
|
|
|
| model.save_pretrained("./xo_model")
|
| tokenizer.save_pretrained("./xo_tokenizer")
|
| print("✅ تم حفظ النموذج والـ Tokenizer في ./xo_model و ./xo_tokenizer")
|
|
|
|
|
|
|
|
|
| def main():
|
|
|
| data_file = "xo_dataset.json"
|
| if not os.path.exists(data_file):
|
| generate_dataset(data_file, n_games=10000)
|
| else:
|
| print(f"📂 تم العثور على ملف البيانات: {data_file}")
|
|
|
|
|
| if not os.path.exists("./xo_tokenizer"):
|
| tokenizer = build_tokenizer(save_path="./xo_tokenizer")
|
| else:
|
| tokenizer = load_tokenizer("./xo_tokenizer")
|
|
|
|
|
| model = build_model(tokenizer)
|
|
|
|
|
| train_dataset, eval_dataset, data_collator = prepare_datasets(data_file, tokenizer)
|
|
|
|
|
| run_training(model, tokenizer, train_dataset, eval_dataset, data_collator)
|
| print("\n✨ تم الانتهاء من التدريب بنجاح.")
|
|
|
|
|
| if __name__ == "__main__":
|
| main() |