# Twinkle 客户端 Twinkle Client 是原生客户端,设计理念是:**将 `from twinkle import` 改为 `from twinkle_client import`,即可将本地训练代码迁移为远端调用,原有训练逻辑无需改动**。 ## 初始化 ```python from twinkle_client import init_twinkle_client # 初始化客户端,连接到 Twinkle Server client = init_twinkle_client( base_url='http://127.0.0.1:8000', # Server 地址 api_key='your-api-key' # 认证令牌(可通过环境变量 TWINKLE_SERVER_TOKEN 设置) ) ``` 初始化完成后,`client` 对象(`TwinkleClient`)提供以下管理功能: ```python # 健康检查 client.health_check() # 列出当前用户的训练运行 runs = client.list_training_runs(limit=20) # 获取特定训练运行详情 run = client.get_training_run(run_id='xxx') # 列出检查点 checkpoints = client.list_checkpoints(run_id='xxx') # 获取检查点路径(用于恢复训练) path = client.get_checkpoint_path(run_id='xxx', checkpoint_id='yyy') # 获取最新检查点路径 latest_path = client.get_latest_checkpoint_path(run_id='xxx') ``` ## 从本地代码迁移到远端 迁移非常简单,只需将 import 路径从 `twinkle` 替换为 `twinkle_client`: ```python # 本地训练代码(原始) from twinkle.dataloader import DataLoader from twinkle.dataset import Dataset from twinkle.model import MultiLoraTransformersModel # 远端训练代码(迁移后) # DataLoader 和 Dataset 使用本地 twinkle 或远端 twinkle_client 均可 from twinkle.dataloader import DataLoader # 或 from twinkle_client.dataloader import DataLoader from twinkle.dataset import Dataset # 或 from twinkle_client.dataset import Dataset from twinkle_client.model import MultiLoraTransformersModel ``` 训练循环、数据处理等逻辑完全不需要修改。 ## 完整训练示例(Transformers 后端) ```python import dotenv dotenv.load_dotenv('.env') from peft import LoraConfig from twinkle import get_logger from twinkle.dataset import DatasetMeta from twinkle_client import init_twinkle_client # DataLoader 和 Dataset 使用本地 twinkle 或远端 twinkle_client 均可 from twinkle.dataloader import DataLoader from twinkle.dataset import Dataset from twinkle_client.model import MultiLoraTransformersModel logger = get_logger() base_model = 'Qwen/Qwen3.5-4B' base_url = 'http://localhost:8000' api_key = 'EMPTY_API_KEY' # Step 1: 初始化客户端 client = init_twinkle_client(base_url=base_url, api_key=api_key) # 列出服务器支持的模型 print('Available models:') for item in client.get_server_capabilities().supported_models: print('- ' + item.model_name) # Step 2: 查询已有训练运行(可选,用于恢复训练) runs = client.list_training_runs() resume_path = None for run in runs: logger.info(run.model_dump_json(indent=2)) checkpoints = client.list_checkpoints(run.training_run_id) for checkpoint in checkpoints: logger.info(checkpoint.model_dump_json(indent=2)) # 取消注释以从检查点恢复: # resume_path = checkpoint.twinkle_path # Step 3: 准备数据集 # data_slice 可限制加载的数据量 dataset = Dataset(dataset_meta=DatasetMeta('ms://swift/self-cognition', data_slice=range(500))) # 设置 chat 模板,使数据匹配模型的输入格式 dataset.set_template('Qwen3_5Template', model_id=f'ms://{base_model}', max_length=512) # 数据预处理:替换占位符为自定义名称 dataset.map('SelfCognitionProcessor', init_args={'model_name': 'twinkle模型', 'model_author': 'ModelScope社区'}) # 编码数据集为模型可用的 token dataset.encode(batched=True) # 创建 DataLoader dataloader = DataLoader(dataset=dataset, batch_size=4) # Step 4: 配置模型 model = MultiLoraTransformersModel(model_id=f'ms://{base_model}') # 配置 LoRA:对所有线性层应用低秩适配器 lora_config = LoraConfig(target_modules='all-linear') # gradient_accumulation_steps=2 表示累积 2 个 micro-batch 的梯度后再执行一次优化器更新 model.add_adapter_to_model('default', lora_config, gradient_accumulation_steps=2) # 设置模板、处理器、损失函数 model.set_template('Qwen3_5Template') model.set_processor('InputProcessor', padding_side='right') model.set_loss('CrossEntropyLoss') # 设置优化器(如果服务器使用 Megatron 后端,仅支持 Adam 优化器) model.set_optimizer('Adam', lr=1e-4) # 设置学习率调度器(如果服务器使用 Megatron 后端,不支持 LR 调度器) # model.set_lr_scheduler('LinearLR') # Step 5: 恢复训练(可选) start_step = 0 if resume_path: logger.info(f'Resuming from checkpoint {resume_path}') progress = model.resume_from_checkpoint(resume_path) dataloader.resume_from_checkpoint(progress['consumed_train_samples']) start_step = progress['cur_step'] # Step 6: 训练循环 logger.info(model.get_train_configs().model_dump()) for epoch in range(3): logger.info(f'Starting epoch {epoch}') for cur_step, batch in enumerate(dataloader, start=start_step + 1): # 前向传播 + 反向传播 model.forward_backward(inputs=batch) # 梯度裁剪 + 优化器更新(等价于依次调用 clip_grad_norm / step / zero_grad / lr_step) model.clip_grad_and_step() # 每 2 步打印一次指标(与 gradient_accumulation_steps 对齐) if cur_step % 2 == 0: metric = model.calculate_metric(is_training=True) logger.info(f'Current is step {cur_step} of {len(dataloader)}, metric: {metric.result}') # Step 7: 保存检查点 twinkle_path = model.save( name=f'twinkle-epoch-{epoch}', save_optimizer=True, consumed_train_samples=dataloader.get_state()['consumed_train_samples'], ) logger.info(f'Saved checkpoint: {twinkle_path}') # Step 8: 上传到 ModelScope Hub(可选) # YOUR_USER_NAME = "your_username" # hub_model_id = f'{YOUR_USER_NAME}/twinkle-self-cognition' # model.upload_to_hub( # checkpoint_dir=twinkle_path, # hub_model_id=hub_model_id, # async_upload=False # ) ``` Twinkle Client 场景下,推荐的断点续训流程是: 1. 先通过 `client.list_checkpoints(...)` 或 `client.get_latest_checkpoint_path(...)` 获取已有 checkpoint 路径。 2. 调用 `model.resume_from_checkpoint(resume_path)` 恢复权重、优化器、调度器、随机数状态和训练进度元数据。 3. 使用返回结果中的 `consumed_train_samples` 调用 `dataloader.resume_from_checkpoint(...)`,跳过已经训练过的数据。 完整示例可直接参考 `cookbook/client/twinkle/self_host/self_cognition.py`。 ## Megatron 后端的差异 使用 Megatron 后端时,客户端代码的主要差异: ```python # Megatron 后端不需要显式设置 loss(由 Megatron 内部计算) # model.set_loss('CrossEntropyLoss') # 不需要 # 优化器和 LR 调度器使用 Megatron 内置默认值 model.set_optimizer('default', lr=1e-4) model.set_lr_scheduler('default', lr_decay_steps=1000, max_lr=1e-4) ``` 其余数据处理、训练循环、检查点保存等代码完全相同。