From 42fce46a9ac31cb15ee8987bda09f3564618a96a Mon Sep 17 00:00:00 2001 From: xming521 <1223398803@qq.com> Date: Tue, 22 Apr 2025 21:26:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0README.md=E4=BB=A5=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E6=95=B0=E6=8D=AE=E9=9B=86=E8=B7=AF=E5=BE=84=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=EF=BC=8C=E8=B0=83=E6=95=B4settings.json=E4=B8=AD?= =?UTF-8?q?=E7=9A=84deepspeed=E9=85=8D=E7=BD=AE=E6=B3=A8=E9=87=8A=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E6=B5=8B=E8=AF=95=E8=84=9A=E6=9C=AC=E4=B8=AD?= =?UTF-8?q?=E7=9A=84Checkpoint=E6=A3=80=E6=9F=A5=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E9=BB=98=E8=AE=A4=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E8=87=B345=E7=A7=92=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +-- settings.json | 2 +- tests/test_full_pipeline.py | 104 ++++++++++++++++++++++++------------ 3 files changed, 73 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index ce6fea0..c358cc4 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ > 新特性:[WeClone-audio](https://github.com/xming521/WeClone/tree/master/weclone-audio) 模块,支持对微信语音进行克隆。 > [!IMPORTANT] ->

0.2.0版本进行了全面重构,数据集目录和脚本路径全部进行了修改,拉取新代码后,数据放在`./dataset/csv`目录下,并且需要重新安装依赖。

+>

0.2.0版本进行了全面重构,数据集目录和脚本路径全部进行了修改,拉取新代码后,`csv`文件夹放在`dataset`下,并且需要重新安装依赖。

> [!IMPORTANT] @@ -73,7 +73,7 @@ python -c "import torch; print('CUDA是否可用:', torch.cuda.is_available());" ### 数据准备 -请使用[PyWxDump](https://github.com/xaoyaoo/PyWxDump)提取微信聊天记录。下载软件并解密数据库后,点击聊天备份,导出类型为CSV,可以导出多个联系人或群聊,然后将导出的位于`wxdump_tmp/export` 的 `csv` 文件夹放在`./dataset`目录即可,也就是不同人聊天记录的文件夹一起放在 `./dataset/csv`。 示例数据位于[dataset/example_chat.csv](dataset/example_chat.csv)。 +请使用[PyWxDump](https://github.com/xaoyaoo/PyWxDump)提取微信聊天记录。下载软件并解密数据库后,点击聊天备份,导出类型为CSV,可以导出多个联系人或群聊,然后将导出的位于`wxdump_tmp/export` 的 `csv` 文件夹放在`./dataset`目录即可,也就是不同人聊天记录的文件夹一起放在 `./dataset/csv`。 ### 数据预处理 @@ -105,7 +105,7 @@ python weclone/train/train_sft.py ``` #### 多卡训练 - +取消`settings.json`中`deepspeed`行代码注释,使用以下命令多卡训练: ```bash uv pip install deepspeed deepspeed --num_gpus=使用显卡数量 weclone/train/train_sft.py diff --git a/settings.json b/settings.json index 03613a2..924da38 100644 --- a/settings.json +++ b/settings.json @@ -40,7 +40,7 @@ "plot_loss": true, "fp16": true, "flash_attn": "fa2", - "deepspeed": "ds_config.json" + // "deepspeed": "ds_config.json" //多卡训练 }, "infer_args": { "repetition_penalty": 1.2, diff --git a/tests/test_full_pipeline.py b/tests/test_full_pipeline.py index 247ba1e..81c19c5 100644 --- a/tests/test_full_pipeline.py +++ b/tests/test_full_pipeline.py @@ -25,7 +25,7 @@ api_service_script = "weclone/server/api_service.py" eval_script = "weclone/eval/test_model.py" web_demo_script = "weclone/eval/web_demo.py" -DEFAULT_TIMEOUT: Optional[Union[int, float]] = 30 +DEFAULT_TIMEOUT: Optional[Union[int, float]] = 45 API_STARTUP_WAIT = 20 API_TERMINATE_WAIT = 15 WEB_DEMO_STARTUP_WAIT = 20 @@ -498,44 +498,78 @@ if __name__ == "__main__": # 步骤 2: Train SFT if run_train: logger.info("-" * 10 + " 步骤 2: SFT 训练 " + "-" * 10) - # 删除 model_output 目录 + + # --- 开始:添加 Checkpoint 检查 --- model_output_dir = os.path.join(project_root, "model_output") - if os.path.exists(model_output_dir): - logger.info(f"删除现有的 model_output 目录: {model_output_dir}") + checkpoint_exists = False + if os.path.isdir(model_output_dir): + logger.info(f"检查目录 {model_output_dir} 是否存在 checkpoint...") try: - shutil.rmtree(model_output_dir) - logger.success("成功删除 model_output 目录") + for item in os.listdir(model_output_dir): + item_path = os.path.join(model_output_dir, item) + if os.path.isdir(item_path) and item.startswith("checkpoint"): + logger.warning(f"找到现有的 Checkpoint 目录: {item_path},将跳过训练。") + checkpoint_exists = True + break + if not checkpoint_exists: + logger.info("未找到现有的 Checkpoint 目录。") except Exception as e: - logger.error(f"删除 model_output 目录时出错: {e}") - - # 尝试禁用 tqdm - run_script(train_script, timeout=DEFAULT_TIMEOUT, ignore_timeout_error=True, env={'TQDM_DISABLE': '1'}) - steps_completed.append(f"{STEP_TRAIN}: 成功或超时跳过") - - # 步骤 2.1: 复制 Checkpoint (只有在训练运行后才可能执行) - if run_copy_checkpoint: - logger.info("-" * 10 + " 步骤 2.1: 复制 Checkpoint 到 model_output " + "-" * 10) - source_dir = os.path.join(project_root, "model_output", "checkpoint-2") - dest_dir = os.path.join(project_root, "model_output") - if os.path.isdir(source_dir): - try: - logger.info(f"开始将 {source_dir} 的内容复制到 {dest_dir}...") - shutil.copytree(source_dir, dest_dir, dirs_exist_ok=True) - logger.success(f"--- {STEP_COPY_CKPT} 成功 ---") - steps_completed.append(f"{STEP_COPY_CKPT}: 成功") - except Exception as e: - # Embed identifier in the error for the except block - error_msg = f"{STEP_COPY_CKPT} 时发生错误: {e}" - logger.error(error_msg) - # Add a unique marker to identify this step in the except block - raise PipelineStepError(f"{error_msg} ###step_id:copy_checkpoint###") - else: - logger.warning(f"源 Checkpoint 目录 {source_dir} 不存在或不是目录,跳过复制。") - steps_completed.append(f"{STEP_COPY_CKPT}: 跳过 (源不存在)") - # raise PipelineStepError(f"必需的源 Checkpoint 目录 {source_dir} 不存在") # 如果必须,取消此行注释 + logger.error(f"检查 Checkpoint 时出错: {e}") + # Treat check error as reason to skip + checkpoint_exists = True + logger.warning("由于检查 Checkpoint 时出错,将跳过训练。") else: - logger.info(f"{STEP_COPY_CKPT}: 跳过 (配置)") - steps_completed.append(f"{STEP_COPY_CKPT}: 跳过 (配置)") + logger.info(f"目录 {model_output_dir} 不存在,无需检查 Checkpoint。") + + if checkpoint_exists: + steps_completed.append(f"{STEP_TRAIN}: 跳过 (存在 Checkpoint)") + # 如果训练跳过,复制步骤也必须跳过 + logger.info(f"{STEP_COPY_CKPT}: 跳过 (训练未运行)") + steps_completed.append(f"{STEP_COPY_CKPT}: 跳过 (训练未运行)") + else: + # --- 结束:添加 Checkpoint 检查 --- + # 只有在 checkpoint 不存在时才执行以下操作 + logger.info("没有找到 Checkpoint,继续执行训练步骤。") + # 删除 model_output 目录 + if os.path.exists(model_output_dir): + logger.info(f"删除现有的 model_output 目录: {model_output_dir}") + try: + shutil.rmtree(model_output_dir) + logger.success("成功删除 model_output 目录") + except Exception as e: + logger.error(f"删除 model_output 目录时出错: {e}") + # Let's log and raise, consistent with other errors. + raise PipelineStepError(f"删除 model_output 目录失败: {e}") + + # 尝试禁用 tqdm + run_script(train_script, timeout=2000, ignore_timeout_error=True, env={'TQDM_DISABLE': '1'}) + steps_completed.append(f"{STEP_TRAIN}: 成功或超时跳过") + + # 步骤 2.1: 复制 Checkpoint (只有在训练运行后才可能执行) + if run_copy_checkpoint: + logger.info("-" * 10 + " 步骤 2.1: 复制 Checkpoint 到 model_output " + "-" * 10) + source_dir = os.path.join(project_root, "model_output", "checkpoint-2") # Note: This assumes checkpoint-2 specifically. + dest_dir = os.path.join(project_root, "model_output") + if os.path.isdir(source_dir): + try: + logger.info(f"开始将 {source_dir} 的内容复制到 {dest_dir}...") + shutil.copytree(source_dir, dest_dir, dirs_exist_ok=True) + logger.success(f"--- {STEP_COPY_CKPT} 成功 ---") + steps_completed.append(f"{STEP_COPY_CKPT}: 成功") + except Exception as e: + # Embed identifier in the error for the except block + error_msg = f"{STEP_COPY_CKPT} 时发生错误: {e}" + logger.error(error_msg) + # Add a unique marker to identify this step in the except block + raise PipelineStepError(f"{error_msg} ###step_id:copy_checkpoint###") + else: + logger.warning(f"源 Checkpoint 目录 {source_dir} 不存在或不是目录,跳过复制。") + steps_completed.append(f"{STEP_COPY_CKPT}: 跳过 (源不存在)") + # Consider if missing checkpoint-2 after training is an error + # raise PipelineStepError(f"必需的源 Checkpoint 目录 {source_dir} 不存在") + else: + logger.info(f"{STEP_COPY_CKPT}: 跳过 (配置)") + steps_completed.append(f"{STEP_COPY_CKPT}: 跳过 (配置)") else: logger.info(f"{STEP_TRAIN}: 跳过 (配置)")