一、项目全景它在做什么核心任务是让计算机识别出图像中的车道线属于语义分割Semantic Segmentation——即对图像中的每个像素进行分类是车道线/不是车道线。整个流程分为四大阶段环境搭建 → 数据预处理 → 模型训练 → 模型导出部署二、逐模块详解1. 环境搭建第1个代码块!git clone https://gitee.com/paddlepaddle/PaddleSeg.git !pip install pycocotools !pip install -r requirements.txt --user !python setup.py install作用克隆飞桨PaddlePaddle官方的语义分割工具库 PaddleSeg安装依赖。知识点PaddleSeg 集成了大量分割模型U-Net、DeepLab、BiSeNet等提供统一的训练/评估/导出接口降低开发门槛。2. 数据转换COCO JSON → 灰度掩码第2-3个代码块# 解压百度智能标注平台导出的数据 !unzip COCO_RES.zip -d data/raw # 将 COCO 格式标注转为灰度掩码图 gray_mask[ann_mask 1] cat_id 1 Image.fromarray(gray_mask).save(output_path)为什么需要这一步格式说明COCO JSON标注信息存储在 JSON 文件中记录每个物体的多边形顶点坐标灰度掩码PNG每张图片对应一张同尺寸的灰度图像素值代表类别0背景1车道线语义分割模型需要像素级标签即掩码图而标注平台导出的是 COCO 格式所以必须转换。3. 数据集划分第4个代码块random.shuffle(matched_names) split_idx int(len(matched_names) * 0.8) train_names matched_names[:split_idx] # 80% val_names matched_names[split_idx:] # 20%生成train.txt和val.txt每行格式为image001.jpg image001.png这是 PaddleSeg 要求的文件列表格式将原图与掩码一一对应。4. 模型配置BiSeNetV2第5-7个代码块为什么选 BiSeNetV2BiSeNet 全称Bilateral Segmentation Network双边分割网络专为实时语义分割设计。它的核心创新是双路径架构输入图像 ├── Spatial Path空间路径 │ ↓ 浅层网络高分辨率保留空间细节 │ ↓ 输出1/8 原图尺寸的特征图 │ └── Context Path上下文路径 ↓ 深层网络快速下采样捕获大感受野语义 ↓ 输出1/32 原图尺寸的特征图 ↓ 特征融合模块Feature Fusion Module ↓ 最终预测输出设计哲学空间路径用少量卷积层保持高分辨率负责边缘细节在哪里上下文路径用轻量骨干网络如轻量ResNet大幅下采样负责这个区域是什么融合模块将两者结合兼顾速度与精度 这非常适合车道线检测——需要实时性车载场景又需要准确定位细长的车道线边缘。5. 训练配置关键参数解读model: type: BiSeNetV2 num_classes: 2 # 背景 车道线 batch_size: 16 # 每批16张图 iters: 1000 # 总共训练1000次迭代 loss: types: - CrossEntropyLoss # ×5个辅助监督头 coef: [1, 1, 1, 1, 1] lr_scheduler: type: PolynomialDecay # 多项式衰减 learning_rate: 0.00125 power: 0.9为什么有5个损失函数BiSeNetV2 有多个辅助输出头在上下文路径的不同阶段每个都参与损失计算。这是一种深度监督策略能加速收敛、提升精度。6. 数据增强Data Augmentationtransforms: - ResizeStepScaling # 多尺度缩放0.5~2.0倍 - RandomPaddingCrop # 随机裁剪到 480×480 - RandomHorizontalFlip # 水平翻转 - RandomDistort # 亮度/对比度/饱和度扰动为什么需要这些操作增加数据多样性防止过拟合让模型对不同光照、尺度、角度都鲁棒对车道线任务尤为关键——实际路况千变万化7. 模型训练第9个代码块!python tools/train.py --config configs/bisenet/mybisenet.yml \ --do_eval --use_vdl --save_interval 50--do_eval训练中定期评估验证集--use_vdl启用 VisualDL 可视化飞桨的 TensorBoard 替代--save_interval 50每50次迭代保存一次模型8. 模型推理测试与导出第10-12个代码块推理测试验证模型效果!python tools/predict.py --model_path output/iter_950/model.pdparams \ --image_path data/lane/0914.png模型导出动态图 → 静态图!python tools/export.py --input_shape 1 3 240 320 \ --model_path output/best_model/model.pdparams为什么导出训练用的是动态图方便调试部署需要静态图速度快、可优化、可移植到移动端/嵌入式设备input_shape [1,3,240,320]指定了部署时的输入尺寸便于推理引擎优化三、核心网络知识补充语义分割 vs 图像分类 vs 目标检测任务输出粒度图像分类整张图一个标签图像级目标检测边界框 类别物体级语义分割逐像素类别像素级分割模型的评价指标mIoU平均交并比预测区域与真实区域的交集除以并集最核心指标Pixel Accuracy像素正确分类的比例损失函数CrossEntropyLoss对每个像素计算其中 ycyc 是真实类别one-hotpcpc 是预测概率。四、总结框图五、核心源码{ cells: [ { cell_type: markdown, metadata: { execution: { iopub.execute_input: 2026-04-22T02:46:01.933582Z, iopub.status.busy: 2026-04-22T02:46:01.933170Z, iopub.status.idle: 2026-04-22T02:46:01.936572Z, shell.execute_reply: 2026-04-22T02:46:01.935934Z, shell.execute_reply.started: 2026-04-22T02:46:01.933556Z } }, source: [ # 配置环境 ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true, tags: [] }, outputs: [], source: [ # 克隆PaddleSeg仓库已经克隆过就将第2行注释从第4行开始运行\n, !git clone https://gitee.com/paddlepaddle/PaddleSeg.git\n, \n, # 更新pip版本\n, !pip install --upgrade pip\n, \n, # 安装依赖\n, !pip install pycocotools\n, %cd PaddleSeg\n, !pip install -r requirements.txt --user\n, \n, # 编译安装paddledet\n, !python setup.py install\n, %cd ~ ] }, { cell_type: markdown, metadata: {}, source: [ # 数据转换\n, 先将百度智能标注的结果进行数据格式转换为灰度掩码,这里使用我上传的数据集作为案例 ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true, tags: [] }, outputs: [], source: [ # 在使用前先将导出的COCO格式数据解压到 data/raw文件夹备用\n, !unzip /home/aistudio/data/datasets/380583/baidu_smartcar_lane_2026/COCO_RES.zip -d /home/aistudio/data/raw ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true, tags: [] }, outputs: [], source: [ \\\\n, 百度云智能标注导出脚本\n, 使用说明\n, 1. 在百度云智能标注平台上传图片 (https://console.bce.baidu.com/easydata/app/proj-f83f94154f7b4e38/datav/annotate/interactive?branchvis)\n, 2. 进行 4 轮难例标注迭代优化模型预测结果\n, 3. 标注完成后导出为 COCO JSON 格式\n, 4. 将 Annotations/coco_info.json 放置在本目录\n, 5. 运行本脚本生成灰度掩码图像\n, \n, 灰度值规则背景0类别01类别12...\n, \\\\n, import json\n, import os\n, import numpy as np\n, from pycocotools.coco import COCO\n, from PIL import Image\n, \n, # 路径配置\n, ANNOTATION_PATH \/home/aistudio/data/raw/Annotations/coco_info.json\\n, IMAGE_DIR \/home/aistudio/data/raw/Images\\n, OUTPUT_DIR \/home/aistudio/data/raw/masks\\n, \n, # 创建输出目录\n, os.makedirs(OUTPUT_DIR, exist_okTrue)\n, \n, # 加载 COCO 格式标注\n, coco COCO(ANNOTATION_PATH)\n, \n, # 获取所有图片ID\n, img_ids coco.getImgIds()\n, print(f\找到 {len(img_ids)} 张图片\)\n, \n, # 遍历每张图片\n, for img_id in img_ids:\n, img_info coco.loadImgs(img_id)[0]\n, img_name img_info[\file_name\]\n, img_height img_info[\height\]\n, img_width img_info[\width\]\n, \n, ann_ids coco.getAnnIds(imgIdsimg_id)\n, anns coco.loadAnns(ann_ids)\n, \n, if not anns:\n, continue\n, \n, # 灰度掩码背景0类别id1\n, gray_mask np.zeros((img_height, img_width), dtypenp.uint8)\n, \n, categories_found set()\n, for ann in anns:\n, cat_id ann[\category_id\]\n, ann_mask coco.annToMask(ann)\n, gray_mask[ann_mask 1] cat_id 1\n, categories_found.add(cat_id)\n, \n, # 保存图像\n, output_name os.path.splitext(img_name)[0] \.png\\n, output_path os.path.join(OUTPUT_DIR, output_name)\n, Image.fromarray(gray_mask).save(output_path)\n, \n, # 打印类别信息\n, cat_info [f\CATEGORY{c}:{c1}\ for c in sorted(categories_found)]\n, print(f\{output_name} - [BACKGROUND:0, {, .join(cat_info)}]\)\n ] }, { cell_type: markdown, metadata: {}, source: [ # 数据集划分 ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true, tags: [] }, outputs: [], source: [ \\\\n, PaddleSeg BiSeNet 数据划分工具\n, 将 Images/ 中的 JPG 和 masks/ 中的 PNG 按文件名匹配后随机划分为训练集和验证集8:2\n, \\\\n, import os\n, import shutil\n, import random\n, \n, # 路径配置\n, IMAGE_DIR \/home/aistudio/data/raw/Images\\n, MASK_DIR \/home/aistudio/data/raw/masks\\n, OUTPUT_DIR \/home/aistudio/data/lane\\n, TRAIN_FILE \train.txt\\n, VAL_FILE \val.txt\\n, TRAIN_RATIO 0.8\n, \n, # 设置随机种子以保证可复现\n, random.seed(42)\n, \n, # 创建输出目录\n, os.makedirs(OUTPUT_DIR, exist_okTrue)\n, \n, # 获取所有 JPG 文件名不含扩展名\n, jpg_files set()\n, for f in os.listdir(IMAGE_DIR):\n, if f.lower().endswith((.jpg, .jpeg)):\n, name os.path.splitext(f)[0]\n, jpg_files.add(name)\n, \n, # 获取所有 PNG 文件名不含扩展名\n, png_files set()\n, for f in os.listdir(MASK_DIR):\n, if f.lower().endswith(.png):\n, name os.path.splitext(f)[0]\n, png_files.add(name)\n, \n, # 取交集找出匹配的 JPG 和 PNG\n, matched_names sorted(jpg_files png_files)\n, print(f\找到 {len(matched_names)} 对匹配的文件{len(jpg_files)} JPG {len(png_files)} PNG\)\n, \n, if not matched_names:\n, print(\没有找到匹配的文件对请检查 Images/ 和 masks/ 目录\)\n, exit(1)\n, \n, # 随机打乱并划分\n, random.shuffle(matched_names)\n, split_idx int(len(matched_names) * TRAIN_RATIO)\n, train_names matched_names[:split_idx]\n, val_names matched_names[split_idx:]\n, \n, print(f\训练集: {len(train_names)} 对\)\n, print(f\验证集: {len(val_names)} 对\)\n, \n, # 复制文件并生成划分文件\n, def copy_and_write(names, output_file):\n, with open(output_file, w) as f:\n, for name in names:\n, jpg_src os.path.join(IMAGE_DIR, f\{name}.jpg\)\n, png_src os.path.join(MASK_DIR, f\{name}.png\)\n, jpg_dst os.path.join(OUTPUT_DIR, f\{name}.jpg\)\n, png_dst os.path.join(OUTPUT_DIR, f\{name}.png\)\n, \n, shutil.copy2(jpg_src, jpg_dst)\n, shutil.copy2(png_src, png_dst)\n, \n, f.write(f\{name}.jpg {name}.png\\n\)\n, \n, train_path os.path.join(OUTPUT_DIR, TRAIN_FILE)\n, val_path os.path.join(OUTPUT_DIR, VAL_FILE)\n, \n, copy_and_write(train_names, train_path)\n, copy_and_write(val_names, val_path)\n, \n, print(f\数据集已保存到 {OUTPUT_DIR} 目录\)\n, print(f\ - {TRAIN_FILE}: {len(train_names)} 条\)\n, print(f\ - {VAL_FILE}: {len(val_names)} 条\)\n ] }, { cell_type: markdown, metadata: {}, source: [ ### 配置参数文件 ] }, { cell_type: markdown, metadata: {}, source: [ #### 模型配置\n, 这里我们选用的是BisenetV2网络模型配置文件路径在“PaddleSeg/configs/bisenet/mybisenet.yml”如需修改请到文件夹中修改。\n, \n, yml\n, # mybisenet.yml\n, \n, \n, # 继承基础配置这里指向了预定义的 Lane 数据集基础配置\n, # 包含数据增强、路径等默认设置你可以根据需要修改为自己的数据集配置文件。\n, _base_: ../_base_/lane.yml \n, \n, model:\n, type: BiSeNetV2 # 模型架构选择指定使用 BiSeNetV2这是一种专为实时语义分割设计的轻量级网络\n, num_classes: 2 # 类别数量设置为2通常指背景 1个目标类别如车道线请根据实际标签数调整\n, \n, optimizer:\n, type: SGD # 优化器类型使用随机梯度下降配合动量使用适合分割任务\n, weight_decay: 0.0005 # 权重衰减用于正则化防止模型过拟合即防止模型死记硬背训练数据\n, \n, loss:\n, # 损失函数配置BiSeNetV2 通常包含多个辅助监督头因此这里定义了5个损失函数\n, types:\n, - type: CrossEntropyLoss # 主损失或辅助损失1交叉熵损失用于衡量预测图与真实标签的差异\n, - type: CrossEntropyLoss # 辅助损失2用于深层网络的梯度回传加速收敛\n, - type: CrossEntropyLoss # 辅助损失3\n, - type: CrossEntropyLoss # 辅助损失4\n, - type: CrossEntropyLoss # 辅助损失5\n, coef: [1, 1, 1, 1, 1] # 损失系数定义上述5个损失在总损失中的权重这里均为1表示同等重要\n, \n, batch_size: 16 # 批大小每次训练迭代送入GPU的样本数量16是常见设置需根据显存大小调整\n, iters: 1000 # 训练迭代次数总的训练步数注意这不是轮数(Epoch)而是梯度更新的次数\n, \n, lr_scheduler:\n, type: PolynomialDecay # 学习率策略多项式衰减随着训练进行学习率会逐渐降低\n, learning_rate: 0.00125 # 初始学习率训练开始时的学习率SGD优化器通常使用较小的值\n, end_lr: 0.0 # 最终学习率训练结束时学习率将衰减到的值\n, power: 0.9 # 衰减幂次控制学习率下降的曲线形状0.9是常用的平滑衰减参数\n, \n, \n ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true, tags: [] }, outputs: [], source: [ yml_content \n, # 继承基础配置这里指向了预定义的 Lane 数据集基础配置\n, # 包含数据增强、路径等默认设置你可以根据需要修改为自己的数据集配置文件。\n, _base_: ../_base_/lane.yml \n, \n, model:\n, type: BiSeNetV2 # 模型架构选择指定使用 BiSeNetV2这是一种专为实时语义分割设计的轻量级网络\n, num_classes: 2 # 类别数量设置为2通常指背景 1个目标类别如车道线请根据实际标签数调整\n, \n, optimizer:\n, type: SGD # 优化器类型使用随机梯度下降配合动量使用适合分割任务\n, weight_decay: 0.0005 # 权重衰减用于正则化防止模型过拟合即防止模型死记硬背训练数据\n, \n, loss:\n, # 损失函数配置BiSeNetV2 通常包含多个辅助监督头因此这里定义了5个损失函数\n, types:\n, - type: CrossEntropyLoss # 主损失或辅助损失1交叉熵损失用于衡量预测图与真实标签的差异\n, - type: CrossEntropyLoss # 辅助损失2用于深层网络的梯度回传加速收敛\n, - type: CrossEntropyLoss # 辅助损失3\n, - type: CrossEntropyLoss # 辅助损失4\n, - type: CrossEntropyLoss # 辅助损失5\n, coef: [1, 1, 1, 1, 1] # 损失系数定义上述5个损失在总损失中的权重这里均为1表示同等重要\n, \n, batch_size: 16 # 批大小每次训练迭代送入GPU的样本数量16是常见设置需根据显存大小调整\n, iters: 1000 # 训练迭代次数总的训练步数注意这不是轮数(Epoch)而是梯度更新的次数\n, \n, lr_scheduler:\n, type: PolynomialDecay # 学习率策略多项式衰减随着训练进行学习率会逐渐降低\n, learning_rate: 0.00125 # 初始学习率训练开始时的学习率SGD优化器通常使用较小的值\n, end_lr: 0.0 # 最终学习率训练结束时学习率将衰减到的值\n, power: 0.9 # 衰减幂次控制学习率下降的曲线形状0.9是常用的平滑衰减参数\n, \n, \n, with open(/home/aistudio/PaddleSeg/configs/bisenet/mybisenet.yml, w) as f:\n, f.write(yml_content) ] }, { cell_type: markdown, metadata: {}, source: [ #### 数据集配置\n, \n, 接下来需要根据自己的数据集类型进行数据集配置路径为”PaddleSeg/configs/_base_/lane.yml“如需修改请到文件夹中修改。\n, \n, \n, yml\n, # lane.yml\n, \n, train_dataset: # 训练集配置\n, type: Dataset # 数据集类型\n, dataset_root: /home/aistudio/data/lane/ # 数据集根目录\n, train_path: /home/aistudio/data/lane/train.txt # 训练集路径\n, num_classes: 2 # 分割的类别数\n, transforms: # 数据增强/预处理\n, - type: ResizeStepScaling # 多尺度 resize\n, min_scale_factor: 0.5 # 最小缩放因子\n, max_scale_factor: 2.0 # 最大缩放因子\n, scale_step_size: 0.25 # 缩放步长\n, - type: RandomPaddingCrop # 随机裁剪并 padding\n, crop_size: [480, 480] # 裁剪尺寸\n, - type: RandomHorizontalFlip # 随机水平翻转\n, - type: RandomDistort # 随机亮度/对比度/饱和度扰动\n, brightness_range: 0.4 # 亮度扰动范围\n, contrast_range: 0.4 # 对比度扰动范围\n, saturation_range: 0.4 # 饱和度扰动范围\n, - type: Normalize # 图像归一化\n, mode: train # 训练模式\n, \n, val_dataset: # 验证集配置, 参数含义同训练集\n, type: Dataset # 数据集类型\n, dataset_root: /home/aistudio/data/lane/ # 数据集根目录\n, val_path: /home/aistudio/data/lane/val.txt # 验证集路径\n, num_classes: 2 # 分割的类别数\n, transforms: # 数据增强/预处理\n, - type: Normalize # 图像归一化\n, mode: val # 验证模式\n, \n, ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true }, outputs: [], source: [ yml_content \n, train_dataset: # 训练集配置\n, type: Dataset # 数据集类型\n, dataset_root: /home/aistudio/data/lane/ # 数据集根目录\n, train_path: /home/aistudio/data/lane/train.txt # 训练集路径\n, num_classes: 2 # 分割的类别数\n, transforms: # 数据增强/预处理\n, - type: ResizeStepScaling # 多尺度 resize\n, min_scale_factor: 0.5 # 最小缩放因子\n, max_scale_factor: 2.0 # 最大缩放因子\n, scale_step_size: 0.25 # 缩放步长\n, - type: RandomPaddingCrop # 随机裁剪并 padding\n, crop_size: [480, 480] # 裁剪尺寸\n, - type: RandomHorizontalFlip # 随机水平翻转\n, - type: RandomDistort # 随机亮度/对比度/饱和度扰动\n, brightness_range: 0.4 # 亮度扰动范围\n, contrast_range: 0.4 # 对比度扰动范围\n, saturation_range: 0.4 # 饱和度扰动范围\n, - type: Normalize # 图像归一化\n, mode: train # 训练模式\n, \n, val_dataset: # 验证集配置, 参数含义同训练集\n, type: Dataset # 数据集类型\n, dataset_root: /home/aistudio/data/lane/ # 数据集根目录\n, val_path: /home/aistudio/data/lane/val.txt # 验证集路径\n, num_classes: 2 # 分割的类别数\n, transforms: # 数据增强/预处理\n, - type: Normalize # 图像归一化\n, mode: val \n, \n, \n, with open(/home/aistudio/PaddleSeg/configs/_base_/lane.yml, w) as f:\n, f.write(yml_content) ] }, { cell_type: markdown, metadata: {}, source: [ 参数配置完成后即可启动训练\n, 其中参数“--do_eval”为在训练过程中同时进行评估“--save_interval”为每训练50轮保存一次模型“--use_vdl”表示启用VisualDL服务\n, 模型保存在 /home/aistudio/PaddleSeg/output ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true, tags: [] }, outputs: [], source: [ %cd /home/aistudio/PaddleSeg\n, !python tools/train.py --config configs/bisenet/mybisenet.yml --do_eval --use_vdl --save_interval 50 ] }, { cell_type: markdown, metadata: {}, source: [ ### 模型导出 ] }, { cell_type: markdown, metadata: {}, source: [ 首先对训练好的模型进行推理测试效果满意后再进行导出 ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true }, outputs: [], source: [ image_name \0914\ # 修改这里可以指定测试哪张图片 \n, image_path f\/home/aistudio/data/lane/{image_name}.png\ ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true, tags: [] }, outputs: [], source: [ !python tools/predict.py \\\n, --config configs/bisenet/mybisenet.yml \\\n, --model_path /home/aistudio/PaddleSeg/output/iter_950/model.pdparams \\\n, --devicegpu \\\n, --save_dir output/result \\\n, --image_path {image_path} ] }, { cell_type: markdown, metadata: {}, source: [ \n, \n, \n ] }, { cell_type: markdown, metadata: {}, source: [ PaddleSeg提供了模型导出程序将训练时的动态图模型转化为静态图 ] }, { cell_type: code, execution_count: null, metadata: { scrolled: true, tags: [] }, outputs: [], source: [ %cd ~\n, %cd PaddleSeg\n, !python tools/export.py --config configs/bisenet/mybisenet.yml \\\n, --input_shape 1 3 240 320 \\\n, --model_path /home/aistudio/PaddleSeg/output/best_model/model.pdparams ] }, { cell_type: markdown, metadata: { tags: [] }, source: [ 至此训练好的模型保存在“PaddleSeg/output/inference_model”路径下 ] } ], metadata: { kernelspec: { display_name: Python 3, language: python, name: py35-paddle1.2.0 }, language_info: { codemirror_mode: { name: ipython, version: 3 }, file_extension: .py, mimetype: text/x-python, name: python, nbconvert_exporter: python, pygments_lexer: ipython3, version: 3.7.4 } }, nbformat: 4, nbformat_minor: 4 }