2025-04-22 10:11:44 +08:00
|
|
|
|
from utils import os_utils as osu
|
2025-04-17 11:03:05 +08:00
|
|
|
|
from application.settings import *
|
2025-04-17 15:57:16 +08:00
|
|
|
|
from . import schemas, models, crud
|
2025-04-17 11:03:05 +08:00
|
|
|
|
from utils.websocket_server import room_manager
|
2025-04-17 15:57:16 +08:00
|
|
|
|
from apps.business.project import models as proj_models, crud as proj_crud
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
|
import asyncio
|
|
|
|
|
import subprocess
|
|
|
|
|
from redis.asyncio import Redis
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def before_train(proj_info: proj_models.ProjectInfo, db: AsyncSession):
|
|
|
|
|
"""
|
|
|
|
|
yolov5执行训练任务
|
|
|
|
|
:param proj_info: 项目信息
|
|
|
|
|
:param db: 数据库session
|
|
|
|
|
:return:
|
|
|
|
|
"""
|
|
|
|
|
img_dal = proj_crud.ProjectImageDal(db)
|
|
|
|
|
label_dal = proj_crud.ProjectLabelDal(db)
|
|
|
|
|
# 先查询两个图片列表
|
2025-04-22 10:11:44 +08:00
|
|
|
|
project_images_train = await img_dal.get_datas(
|
|
|
|
|
v_where=[proj_models.ProjectImage.project_id == proj_info.id, proj_models.ProjectImage.img_type == 'train'],
|
|
|
|
|
limit=0,
|
|
|
|
|
v_return_count=False,
|
|
|
|
|
v_return_objs=True)
|
|
|
|
|
project_images_val = await img_dal.get_datas(
|
|
|
|
|
v_where=[proj_models.ProjectImage.project_id == proj_info.id, proj_models.ProjectImage.img_type == 'val'],
|
|
|
|
|
limit=0,
|
|
|
|
|
v_return_count=False,
|
|
|
|
|
v_return_objs=True)
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
# 得到训练版本
|
|
|
|
|
version_path = 'v' + str(proj_info.train_version + 1)
|
|
|
|
|
|
|
|
|
|
# 创建训练的根目录
|
2025-04-22 10:11:44 +08:00
|
|
|
|
train_path = osu.create_folder(datasets_url, proj_info.project_no, version_path)
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
# 查询项目所属标签,返回两个 id,name一一对应的数组
|
2025-04-22 10:11:44 +08:00
|
|
|
|
label_id_list, label_name_list = await label_dal.get_label_for_train(proj_info.id)
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
# 创建图片的的两个文件夹
|
2025-04-22 10:11:44 +08:00
|
|
|
|
img_path_train = osu.create_folder(train_path, 'images', 'train')
|
|
|
|
|
img_path_val = osu.create_folder(train_path, 'images', 'val')
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
# 创建标签的两个文件夹
|
2025-04-22 10:11:44 +08:00
|
|
|
|
label_path_train = osu.create_folder(train_path, 'labels', 'train')
|
|
|
|
|
label_path_val = osu.create_folder(train_path, 'labels', 'val')
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
# 在根目录下创建yaml文件
|
2025-04-22 10:11:44 +08:00
|
|
|
|
yaml_file = osu.file_path(train_path, proj_info.project_no + '.yaml')
|
2025-04-17 11:03:05 +08:00
|
|
|
|
yaml_data = {
|
|
|
|
|
'path': train_path,
|
|
|
|
|
'train': 'images/train',
|
|
|
|
|
'val': 'images/val',
|
|
|
|
|
'test': None,
|
|
|
|
|
'names': {i: name for i, name in enumerate(label_name_list)}
|
|
|
|
|
}
|
|
|
|
|
with open(yaml_file, 'w', encoding='utf-8') as file:
|
|
|
|
|
yaml.dump(yaml_data, file, allow_unicode=True, default_flow_style=False)
|
|
|
|
|
|
|
|
|
|
# 开始循环复制图片和生成label.txt
|
|
|
|
|
# 先操作train
|
2025-04-22 10:11:44 +08:00
|
|
|
|
await operate_img_label(project_images_train, img_path_train, label_path_train, db, label_id_list)
|
2025-04-17 11:03:05 +08:00
|
|
|
|
# 再操作val
|
2025-04-22 10:11:44 +08:00
|
|
|
|
await operate_img_label(project_images_val, img_path_val, label_path_val, db, label_id_list)
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
# 开始执行异步训练
|
|
|
|
|
data = yaml_file
|
2025-04-22 10:11:44 +08:00
|
|
|
|
project = osu.file_path(runs_url, proj_info.project_no)
|
2025-04-17 11:03:05 +08:00
|
|
|
|
name = version_path
|
|
|
|
|
|
|
|
|
|
return data, project, name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def operate_img_label(
|
2025-04-22 10:11:44 +08:00
|
|
|
|
img_list: list[proj_models.ProjectImage],
|
2025-04-17 11:03:05 +08:00
|
|
|
|
img_path: str,
|
|
|
|
|
label_path: str,
|
|
|
|
|
db: AsyncSession,
|
|
|
|
|
label_id_list: []):
|
|
|
|
|
"""
|
|
|
|
|
生成图片和标签内容
|
|
|
|
|
:param label_id_list:
|
|
|
|
|
:param db: 数据库session
|
|
|
|
|
:param img_list:
|
|
|
|
|
:param img_path:
|
|
|
|
|
:param label_path:
|
|
|
|
|
:return:
|
|
|
|
|
"""
|
|
|
|
|
for i in range(len(img_list)):
|
|
|
|
|
image = img_list[i]
|
|
|
|
|
# 先复制图片,并把图片改名,不改后缀
|
|
|
|
|
file_name = 'image' + str(i)
|
2025-04-22 10:11:44 +08:00
|
|
|
|
osu.copy_and_rename_file(image.image_url, img_path, file_name)
|
2025-04-17 11:03:05 +08:00
|
|
|
|
# 查询这张图片的label信息然后生成这张照片的txt文件
|
|
|
|
|
img_label_list = await proj_crud.ProjectImgLabelDal(db).get_img_label_list(image.id)
|
2025-04-22 10:11:44 +08:00
|
|
|
|
label_txt_path = osu.file_path(label_path, file_name + '.txt')
|
2025-04-17 11:03:05 +08:00
|
|
|
|
with open(label_txt_path, 'w', encoding='utf-8') as file:
|
|
|
|
|
for image_label in img_label_list:
|
|
|
|
|
index = label_id_list.index(image_label.label_id)
|
|
|
|
|
file.write(str(index) + ' ' + image_label.mark_center_x + ' '
|
|
|
|
|
+ image_label.mark_center_y + ' '
|
|
|
|
|
+ image_label.mark_width + ' '
|
|
|
|
|
+ image_label.mark_height + '\n')
|
|
|
|
|
|
|
|
|
|
|
2025-04-22 10:11:44 +08:00
|
|
|
|
def run_event_loop(
|
2025-04-17 11:03:05 +08:00
|
|
|
|
data: str,
|
|
|
|
|
project: str,
|
|
|
|
|
name: str,
|
|
|
|
|
train_in: schemas.ProjectTrainIn,
|
|
|
|
|
project_id: int,
|
2025-04-22 10:11:44 +08:00
|
|
|
|
train_info: models.ProjectTrain,
|
|
|
|
|
is_gup: str):
|
|
|
|
|
# 运行异步函数,开始训练
|
|
|
|
|
loop_run = asyncio.new_event_loop()
|
|
|
|
|
asyncio.set_event_loop(loop_run)
|
|
|
|
|
loop_run.run_until_complete(run_commend(data, project, name, train_in.epochs, train_in.patience,
|
|
|
|
|
project_id, train_info, is_gup))
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def run_commend(
|
|
|
|
|
data: str,
|
|
|
|
|
project: str,
|
|
|
|
|
name: str,
|
|
|
|
|
epochs: int,
|
|
|
|
|
patience: int,
|
|
|
|
|
project_id: int,
|
2025-04-22 10:11:44 +08:00
|
|
|
|
train_info: models.ProjectTrain,
|
|
|
|
|
is_gpu: str):
|
2025-04-17 11:03:05 +08:00
|
|
|
|
"""
|
|
|
|
|
执行训练
|
|
|
|
|
:param data: 训练数据集
|
|
|
|
|
:param project: 训练结果的项目目录
|
|
|
|
|
:param name: 实验名称
|
|
|
|
|
:param epochs: 训练轮数
|
|
|
|
|
:param patience: 早停耐心值
|
|
|
|
|
:param weights: 权重文件
|
|
|
|
|
:param project_id: 项目id
|
2025-04-22 10:11:44 +08:00
|
|
|
|
:param train_info: 训练信息
|
|
|
|
|
:param is_gpu: 是否是gpu环境
|
2025-04-17 11:03:05 +08:00
|
|
|
|
:return:
|
|
|
|
|
"""
|
2025-04-22 10:11:44 +08:00
|
|
|
|
yolo_path = osu.file_path(yolo_url, 'train.py')
|
2025-04-17 11:03:05 +08:00
|
|
|
|
room = 'train_' + str(project_id)
|
|
|
|
|
await room_manager.send_to_room(room, f"AiCheckV2.0: 模型训练开始,请稍等。。。\n")
|
|
|
|
|
commend = ["python", '-u', yolo_path, "--data=" + data, "--project=" + project, "--name=" + name,
|
|
|
|
|
"--epochs=" + str(epochs), "--batch-size=8", "--exist-ok", "--patience=" + str(patience)]
|
|
|
|
|
|
2025-04-22 10:11:44 +08:00
|
|
|
|
# 增加权重文件,在之前训练的基础上重新训练
|
|
|
|
|
if train_info is not None:
|
|
|
|
|
commend.append("--weights=" + train_info.best_pt)
|
2025-04-17 11:03:05 +08:00
|
|
|
|
|
|
|
|
|
# 判断是否存在cuda版本
|
|
|
|
|
if is_gpu == 'True':
|
|
|
|
|
commend.append("--device=0")
|
|
|
|
|
# 启动子进程
|
|
|
|
|
with subprocess.Popen(
|
|
|
|
|
commend,
|
|
|
|
|
bufsize=1, # bufsize=0时,为不缓存;bufsize=1时,按行缓存;bufsize为其他正整数时,为按照近似该正整数的字节数缓存
|
|
|
|
|
shell=False,
|
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
|
stderr=subprocess.STDOUT, # 这里可以显示yolov5训练过程中出现的进度条等信息
|
|
|
|
|
text=True, # 缓存内容为文本,避免后续编码显示问题
|
|
|
|
|
encoding='utf-8',
|
|
|
|
|
) as process:
|
|
|
|
|
while process.poll() is None:
|
|
|
|
|
line = process.stdout.readline()
|
|
|
|
|
process.stdout.flush() # 刷新缓存,防止缓存过多造成卡死
|
|
|
|
|
if line != '\n' and '0%' not in line:
|
|
|
|
|
await room_manager.send_to_room(room, line + '\n')
|
|
|
|
|
|
|
|
|
|
# 等待进程结束并获取返回码
|
|
|
|
|
return_code = process.wait()
|
|
|
|
|
if return_code != 0:
|
|
|
|
|
await room_manager.send_to_room(room, 'error')
|
|
|
|
|
else:
|
|
|
|
|
await room_manager.send_to_room(room, 'success')
|
2025-04-22 10:11:44 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def add_train(
|
|
|
|
|
db,
|
|
|
|
|
project_id,
|
|
|
|
|
name,
|
|
|
|
|
project,
|
|
|
|
|
data,
|
|
|
|
|
train_in,
|
|
|
|
|
user_id):
|
|
|
|
|
# 更新版本信息
|
|
|
|
|
await proj_crud.ProjectInfoDal(db).update_version(data_id=project_id)
|
|
|
|
|
# 增加训练版本信息
|
|
|
|
|
train = models.ProjectTrain()
|
|
|
|
|
train.project_id = project_id
|
|
|
|
|
train.train_version = name
|
|
|
|
|
train_url = osu.file_path(project, name)
|
|
|
|
|
train.train_url = train_url
|
|
|
|
|
train.train_data = data
|
|
|
|
|
train.user_id = user_id
|
|
|
|
|
bast_pt_path = osu.file_path(train_url, 'weights', 'best.pt')
|
|
|
|
|
last_pt_path = osu.file_path(train_url, 'weights', 'last.pt')
|
|
|
|
|
train.best_pt = bast_pt_path
|
|
|
|
|
train.last_pt = last_pt_path
|
|
|
|
|
if train_in is not None:
|
|
|
|
|
train.weights_id = train_in.weights_id
|
|
|
|
|
train.weights_name = train_in.weights_name
|
|
|
|
|
train.patience = train_in.patience
|
|
|
|
|
train.epochs = train_in.epochs
|
|
|
|
|
await crud.ProjectTrainDal(db).create_model(data=train)
|