From e2f95faca0d1f7be082d6422fc51202e58c8c4a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=B5=A9?= <971817787@qq.com> Date: Mon, 8 Dec 2025 09:06:13 +0800 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BC=A0=E9=80=92?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E6=97=A0=E9=9C=80=E7=9A=84=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/app/workers/finetune_worker.py | 22 +++++++++---------- .../app/workers/perturbation_worker.py | 3 ++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/backend/app/workers/finetune_worker.py b/src/backend/app/workers/finetune_worker.py index 0129c9c..7bd8c8d 100644 --- a/src/backend/app/workers/finetune_worker.py +++ b/src/backend/app/workers/finetune_worker.py @@ -81,16 +81,13 @@ def run_finetune_task(task_id, finetune_method, train_images_dir, class_prompt = instance_prompt.replace('sks ', '') logger.info(f"Using prompts from database - instance: '{instance_prompt}', class: '{class_prompt}'") - # 清空输出目录(避免旧文件残留) - logger.info(f"Clearing output directories...") + # 彻底清空输出目录(避免旧文件残留,特别是 textual_inversion 的 token) + logger.info(f"Completely clearing output directories...") for dir_path in [output_model_dir, validation_output_dir]: if os.path.exists(dir_path): - for item in os.listdir(dir_path): - item_path = os.path.join(dir_path, item) - if os.path.isfile(item_path): - os.unlink(item_path) - elif os.path.isdir(item_path): - shutil.rmtree(item_path) + logger.info(f"Removing and recreating directory: {dir_path}") + shutil.rmtree(dir_path) + os.makedirs(dir_path, exist_ok=True) # 清理旧的 coords.json 文件 if os.path.exists(coords_save_path): @@ -176,9 +173,12 @@ def _run_real_finetune(finetune_method, task_id, train_images_dir, output_model_ raise ValueError(f"Finetune method {finetune_method} not configured") # 覆盖提示词参数(从数据库读取) - default_params['instance_prompt'] = instance_prompt - default_params['class_prompt'] = class_prompt - default_params['validation_prompt'] = validation_prompt + if 'instance_prompt' in default_params: + default_params['instance_prompt'] = instance_prompt + if 'class_prompt' in default_params: + default_params['class_prompt'] = class_prompt + if 'validation_prompt' in default_params: + default_params['validation_prompt'] = validation_prompt # 合并自定义参数 params = {**default_params, **(custom_params or {})} diff --git a/src/backend/app/workers/perturbation_worker.py b/src/backend/app/workers/perturbation_worker.py index 148888a..ec345f0 100644 --- a/src/backend/app/workers/perturbation_worker.py +++ b/src/backend/app/workers/perturbation_worker.py @@ -162,7 +162,8 @@ def _run_real_algorithm(script_path, conda_env, algorithm_code, task_id, default_params = AlgorithmConfig.get_default_params(algorithm_code) # 覆盖提示词参数(从数据库读取) - default_params['instance_prompt'] = instance_prompt + if 'instance_prompt' in default_params: + default_params['instance_prompt'] = instance_prompt if 'class_prompt' in default_params: default_params['class_prompt'] = class_prompt -- 2.34.1 From 5742562d31a01bb2f7b76b34ff58c73ee1c5e59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=B5=A9?= <971817787@qq.com> Date: Mon, 8 Dec 2025 09:06:55 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B93D=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E4=BF=9D=E5=AD=98=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/app/services/task_service.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/app/services/task_service.py b/src/backend/app/services/task_service.py index eb0d92d..4fdd4fc 100644 --- a/src/backend/app/services/task_service.py +++ b/src/backend/app/services/task_service.py @@ -521,7 +521,7 @@ class TaskService: str(user_id), str(task.flow_id), str(task_id), - 'original_coords.json' + 'original_coords.csv' ) # 获取加噪坐标保存路径(3D可视化) @@ -530,7 +530,7 @@ class TaskService: str(user_id), str(task.flow_id), str(task_id), - 'perturbed_coords.json' + 'perturbed_coords.csv' ) # 加入RQ队列 @@ -585,7 +585,7 @@ class TaskService: str(user_id), str(task.flow_id), str(task_id), - 'coords.json' + 'coords.csv' ) # 加入RQ队列 -- 2.34.1 From c1dd9281ae058b8ee847136f5372079a70cfe4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=B5=A9?= <971817787@qq.com> Date: Mon, 8 Dec 2025 10:44:13 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=20=E4=BF=AE=E6=94=B9lora=E7=AE=97?= =?UTF-8?q?=E6=B3=953D=E8=B7=AF=E5=BE=84=E4=BF=9D=E5=AD=98=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../algorithms/finetune/train_lora_gen_trace.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/backend/app/algorithms/finetune/train_lora_gen_trace.py b/src/backend/app/algorithms/finetune/train_lora_gen_trace.py index 5827fc5..e474846 100644 --- a/src/backend/app/algorithms/finetune/train_lora_gen_trace.py +++ b/src/backend/app/algorithms/finetune/train_lora_gen_trace.py @@ -1377,7 +1377,11 @@ def main(args): coords_list, columns=['step', 'X_LoRA_Weight_Norm', 'Y_Grad_Norm', 'Z_LDM_Loss'] ) - save_path = Path(args.positions_save_path) / "coords_live.csv" + + # 假设 args.positions_save_path 是目标文件路径 (如 ./data/coords.csv) + save_path = Path(args.positions_save_path) + if not save_path.suffix: + save_path = save_path / "coords.csv" save_path.parent.mkdir(parents=True, exist_ok=True) df.to_csv(save_path, index=False) @@ -1508,8 +1512,12 @@ def main(args): coords_list, columns=['step', 'X_LoRA_Weight_Norm', 'Y_Grad_Norm', 'Z_LDM_Loss'] ) - save_path = Path(args.positions_save_path) / "coords.csv" - + + # 假设 args.positions_save_path 是目标文件路径 (如 ./data/coords.csv) + save_path = Path(args.positions_save_path) + if not save_path.suffix: + save_path = save_path / "coords.csv" + save_path.parent.mkdir(parents=True, exist_ok=True) df.to_csv(save_path, index=False) logger.info(f"训练结束:已将所有 {len(coords_list)} 步可视化坐标数据保存到 {save_path}") -- 2.34.1 From fad003af51ff3f5729ba724f4e328e35b147aed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=B5=A9?= <971817787@qq.com> Date: Mon, 8 Dec 2025 11:00:49 +0800 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20=E4=BF=AE=E6=94=B9.gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 7 +++++-- src/backend/.gitignore | 35 ----------------------------------- 2 files changed, 5 insertions(+), 37 deletions(-) delete mode 100644 src/backend/.gitignore diff --git a/.gitignore b/.gitignore index b1bec9d..370ae49 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ __pycache__/ *.jpg *.jpeg +# 数据文件 +*.csv + # 环境配置文件(包含敏感信息) *.env @@ -28,8 +31,8 @@ uploads/ # vscode 配置 .vscode/ -#github 工作流配置 +# github 工作流配置 .github/ -#pycharm 配置 +# pycharm 配置 .idea/ \ No newline at end of file diff --git a/src/backend/.gitignore b/src/backend/.gitignore deleted file mode 100644 index f61c295..0000000 --- a/src/backend/.gitignore +++ /dev/null @@ -1,35 +0,0 @@ -# Python 编译缓存 -__pycache__/ - -# 图片文件 -*.png -*.jpg -*.jpeg - -# 环境配置文件(包含敏感信息) -*.env - -# 日志及进程文件 -logs/ -*.log -*.pid - -# 上传文件临时目录 -uploads/ - -# 微调生成文件 -*.json -*.bin -*.pkl -*.safetensors -*.pt -*.txt - -# 模型文件 -hf_models/ - -#数据库迁移文件 -migrations/ - -#测试文件 -test/ \ No newline at end of file -- 2.34.1 From da86d0ee5f82499b03c5a15007eaedd2c8ff46aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E6=B5=A9?= <971817787@qq.com> Date: Mon, 8 Dec 2025 19:07:52 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A03D=E5=8F=AF?= =?UTF-8?q?=E8=A7=86=E5=8C=96=E5=9D=90=E6=A0=87=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/controllers/task_controller.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/backend/app/controllers/task_controller.py b/src/backend/app/controllers/task_controller.py index 4dcc0f9..4741991 100644 --- a/src/backend/app/controllers/task_controller.py +++ b/src/backend/app/controllers/task_controller.py @@ -679,3 +679,114 @@ def get_evaluate_task(task_id, current_user_id): if not task: return TaskService.json_error('任务不存在或无权限', 404) return jsonify({'task': TaskService.serialize_task(task)}), 200 + + +# ==================== 3D可视化坐标接口 ==================== + +@task_bp.route('/finetune//coords', methods=['GET']) +@int_jwt_required +def get_finetune_coords(task_id, current_user_id): + """ + 获取微调任务的3D可视化坐标CSV文件 + + 返回格式: + - 基于加噪任务的微调:返回 original_coords.csv 和 perturbed_coords.csv + - 上传图片的微调:返回 coords.csv + """ + import os + import csv + from config.settings import Config + + # 验证任务存在且属于当前用户 + task = TaskService.load_task_for_user(task_id, current_user_id, expected_type='finetune') + if not task: + return TaskService.json_error('任务不存在或无权限', 404) + + # 获取任务详情 + finetune = Finetune.query.get(task_id) + if not finetune: + return TaskService.json_error('微调任务详情不存在', 404) + + # 判断微调类型 + try: + source = TaskService.determine_finetune_source(task) + except ValueError as exc: + return TaskService.json_error(str(exc), 500) + + # 构建CSV文件路径 + coords_base_path = TaskService._build_path( + Config.COORDS_SAVE_FOLDER, + str(current_user_id), + str(task.flow_id), + str(task_id) + ) + + result = { + 'task_id': task_id, + 'flow_id': task.flow_id, + 'source': source, + 'coords': [] + } + + if source == 'perturbation': + # 基于加噪任务的微调,返回两个CSV文件 + original_csv_path = os.path.join(coords_base_path, 'original_coords.csv') + perturbed_csv_path = os.path.join(coords_base_path, 'perturbed_coords.csv') + + # 读取 original_coords.csv + if os.path.exists(original_csv_path): + try: + with open(original_csv_path, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f) + original_data = [row for row in reader] + result['coords'].append({ + 'type': 'original', + 'filename': 'original_coords.csv', + 'path': original_csv_path, + 'data': original_data + }) + except Exception as e: + return TaskService.json_error(f'读取原图坐标文件失败: {str(e)}', 500) + else: + return TaskService.json_error('原图坐标文件不存在', 404) + + # 读取 perturbed_coords.csv + if os.path.exists(perturbed_csv_path): + try: + with open(perturbed_csv_path, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f) + perturbed_data = [row for row in reader] + result['coords'].append({ + 'type': 'perturbed', + 'filename': 'perturbed_coords.csv', + 'path': perturbed_csv_path, + 'data': perturbed_data + }) + except Exception as e: + return TaskService.json_error(f'读取加噪图坐标文件失败: {str(e)}', 500) + else: + return TaskService.json_error('加噪图坐标文件不存在', 404) + + else: # source == 'uploaded' + # 上传图片的微调,返回一个CSV文件 + coords_csv_path = os.path.join(coords_base_path, 'coords.csv') + + if os.path.exists(coords_csv_path): + try: + with open(coords_csv_path, 'r', encoding='utf-8') as f: + reader = csv.DictReader(f) + coords_data = [row for row in reader] + result['coords'].append({ + 'type': 'uploaded', + 'filename': 'coords.csv', + 'path': coords_csv_path, + 'data': coords_data + }) + except Exception as e: + return TaskService.json_error(f'读取坐标文件失败: {str(e)}', 500) + else: + return TaskService.json_error('坐标文件不存在', 404) + + return jsonify(result), 200 + + -- 2.34.1