车道线项目总结
无人车车道线检测
思路分析
- 数据预处理
1)读取数据
2)分析数据 - 模型的设计
- 模型的配置(超参数)
- 训练过程(观察loss变化)
- 保存模型,进行测试
数据分析
车道线数据图像分辨率非常的大,3384x1710。
经过对 label 的分析,首先裁掉了图片最上部的 3384x690 的图像,因为这部分都是天空和树木,没有正样本的存在,裁掉后还可以减小图像的压缩比例,一举两得。
采用了3种分辨率(768x256, 1024x384, 1536x512)进行训练,基于小分辨率的预训练,确实可以帮助大分辨率更好地进行收敛。
在设计机器学习算法的时候,决不能使用测试集来进行调优。如果你使用测试集来调优,算法容易对测试集过拟合。从另一个角度来说,如果使用测试集来调优,实际上就是把测试集当做训练集,由测试集训练出来的算法再跑测试集,自然性能看起来会很好。这其实是过于乐观了,实际部署起来效果就会差很多。所以,最终测试的时候再使用测试集,可以很好地近似度量你所设计的分类器的泛化性能。一般我们从训练集中取出一部分数据用来调优,我们称之为验证集(validation set)。
分析模型:
- 统计每类的分布,明确每类的含义
- 可视化每类,有个大概认知,比如位置,形态
- 统计分析每类的错误,确定错误类型
- 同时可视化预测结果,进一步确认错误类型与细节
- 根据自己的统计与分析,提出错误猜想,然后进行验证
训练结果如下图所示:
代码
# -*- coding: utf-8 -*-
# Author : Shun Qiang
# Date : 01/03/2020
import argparse
from tqdm import tqdm
import torch
import os
import shutil
from utils.metric import compute_iou
import torch.nn.functional as F
from torchvision import transforms
from torch.utils.data import DataLoader
from utils.image_process import LaneDataset, ImageAug, DeformAug
from utils.image_process import ScaleAug, CutOut, ToTensor
from utils.loss import MySoftmaxCrossEntropyLoss,FocalLoss
from utils.metric import Evaluator
from model.deeplab import DeepLab
from utils.logconfig import getLogger
from model.unet import ResNetUNet
from inference import inference
from model.models.deeplabv3_plus import DeepLabV3Plus
logger = getLogger()
def start_seg():
"""start semantic segment
"""
logger.info("======start semantic segment=======")
parser = argparse.ArgumentParser(description="PyTorch Training")
parser.add_argument('--save-path', action='store_true', default='result', help='the path of training result')
parser.add_argument('--cuda', type=bool, default=False, help='wheather use gpu')
parser.add_argument('--base-lr', type=float, default=0.0006, help='learning rate baseline')
parser.add_argument('--weight-decay', type=float, default=0.0001, help='weight decay')
parser.add_argument('--num-works', type=int, default=0, help='mutil process for dataload')
parser.add_argument('--epochs', type=int, default=4, help='training times')
parser.add_argument('--number-class', type=int, default=8, help='final classes')
parser.add_argument('--net', type=str, default='deeplabv3p', help='network class name')
parser.add_argument('--backbone', type=str, default='drn', help='backbone for deplabv3plus')
parser.add_argument('--sync-bn', type=bool, default=False, help='')
parser.add_argument('--freeze-bn', type=bool, default=False, help='')
parser.add_argument('--gpu-ids', type=str, default='7', help='the number of gpu')
parser.add_argument('--batch-size', type=int, default=2, help='the batch size of trianing')
parser.add_argument('--continual', type=bool, default=False, help='trianing continual')
args = parser.parse_args()
logger.info("Training config : {}".format(args))
if args.cuda:
os.environ["CUDA_VISIBLE_DEVICES"] = args.gpu_ids
logger.info("GPU Device: {}".format(args.gpu_ids))
if os.path.exists(args.save_path):
shutil.rmtree(args.save_path)
os.makedirs(args.save_path, exist_ok=True)
lane = Lane(args)
lane.train()
inference(args)
class Lane(object):
def __init__(self, args):
self.args = args
def train(self):
predict_net = self.args.net
nets = {'deeplabv3p': DeepLab, 'unet': ResNetUNet}
trainF = open(os.path.join(self.args.save_path, "train.csv"), 'w')
valF = open(os.path.join(self.args.save_path, "test.csv"), 'w')
kwargs = {'num_workers': self.args.num_works, 'pin_memory': True} if torch.cuda.is_available() else {}
train_dataset = LaneDataset("train.csv", transform=transforms.Compose([ImageAug(), DeformAug(), ScaleAug(), CutOut(32, 0.5), ToTensor()]))
train_data_batch = DataLoader(train_dataset, batch_size=self.args.batch_size, shuffle=True, drop_last=True, **kwargs)
val_dataset = LaneDataset("val.csv", transform=transforms.Compose([ToTensor()]))
val_data_batch = DataLoader(val_dataset, batch_size=2, shuffle=False, drop_last=True, **kwargs)
# net = nets[predict_net](self.args)
net = DeepLabV3Plus()
if self.args.cuda:
net = torch.nn.DataParallel(net).cuda()
# self.model = self.model.to(device)
# net = self.model.cuda()
optimizer = torch.optim.Adam(net.parameters(), lr=self.args.base_lr, weight_decay=self.args.weight_decay)
# Training and test
for epoch in range(self.args.epochs):
# 在 train_epoch 中
self.train_epoch(net, epoch, train_data_batch, optimizer, trainF, self.args)
self.val_epoch(net, epoch, val_data_batch, valF, self.args)
if epoch % 2 == 0:
torch.save({'state_dict': net.state_dict()},
os.path.join(os.getcwd(), self.args.save_path, "laneNet{}.pth.tar".format(epoch)))
trainF.close()
valF.close()
torch.save({'state_dict': net.state_dict()}, os.path.join(os.getcwd(), "result", "finalNet_unet_new.pth.tar"))
def train_epoch(self, net, epoch, dataLoader, optimizer, trainF, args):
logger.info("======start training epoch step=======")
net.train()
if self.args.continual:
model_path = os.path.join('weight', 'continual.pth.tar')
# device type cpu or cuda:id
model_param = torch.load(model_path, map_location=torch.device('cpu'))['state_dict']
model_param = {k.replace('module.', ''): v for k, v in model_param.items()}
net.load_state_dict(model_param)
total_mask_loss = 0.0
dataprocess = tqdm(dataLoader)
accumulation_setps = 10
i = 0
for batch_item in dataprocess:
image, mask = batch_item['image'], batch_item['mask']
if args.cuda:
image, mask = image.cuda(), mask.cuda()
# optimizer.zero_grad()
out = net(image)
# logger.info("train predict shape: {}".format(out.shape))
# mask_loss = MySoftmaxCrossEntropyLoss(nbclasses=args.number_class)(out[0], mask)
mask_loss = FocalLoss(gamma=2, alpha=0.75)(out, mask)
mask_loss = mask_loss['loss'] / accumulation_setps
total_mask_loss += mask_loss.item()
mask_loss.backward()
if(i % accumulation_setps) == 0:
optimizer.step()
optimizer.zero_grad()
i += 1
# optimizer 进行更新
# optimizer.step()
dataprocess.set_description_str("epoch:{}".format(epoch))
dataprocess.set_postfix_str("mask_loss:{:.4f}".format(mask_loss.item()))
# 记录数据迭代了多少次
trainF.write("Epoch:{}, mask loss is {:.4f} \n".format(epoch, total_mask_loss / len(dataLoader)))
trainF.flush()
def val_epoch(self, net, epoch, dataLoader, valF, args):
logger.info("======start val epoch step=======")
net.eval()
total_mask_loss = 0.0
sum_iou = 0.0
dataprocess = tqdm(dataLoader)
result = {"TP": {i: 0 for i in range(8)}, "TA": {i: 0 for i in range(8)}}
evaluator = Evaluator(args.number_class)
with torch.no_grad():
for batch_item in dataprocess:
image, mask = batch_item['image'], batch_item['mask']
if args.cuda:
image, mask = image.cuda(), mask.cuda()
out = net(image)
mask_loss = MySoftmaxCrossEntropyLoss(nbclasses=args.number_class)(out[0], mask)
total_mask_loss += mask_loss.detach().item()
pred = torch.argmax(F.softmax(out[0], dim=1), dim=1)
result = compute_iou(pred, mask, result)
evaluator.add_batch(mask.cpu().numpy(), pred.cpu().numpy())
dataprocess.set_description_str("epoch:{}".format(epoch))
dataprocess.set_postfix_str("mask_loss:{:.4f}".format(mask_loss))
Acc = evaluator.Pixel_Accuracy()
Acc_class = evaluator.Pixel_Accuracy_Class()
mIoU = evaluator.Mean_Intersection_over_Union()
FWIoU = evaluator.Frequency_Weighted_Intersection_over_Union()
valF.write("Epoch:{}, val/mIoU is {:.4f} \n".format(epoch, mIoU))
valF.write("Epoch:{}, val/Acc is {:.4f} \n".format(epoch, Acc))
valF.write("Epoch:{}, val/Acc_class is {:.4f} \n".format(epoch, Acc_class))
valF.write("Epoch:{}, val/FWIoU is {:.4f} \n".format(epoch, FWIoU))
for i in range(8):
sum_iou += result["TP"][i] / result["TA"][i]
iou = "{}: {:.4f} \n".format(i, result["TP"][i] / result["TA"][i])
logger.info("val class result {}".format(iou))
valF.write(iou)
valF.write("mean iou: {:.4f} ".format(sum_iou/8))
logger.info("mean iou: {}".format(sum_iou/8))
valF.write("Epoch:{}, mask loss is {:.4f} \n".format(epoch, total_mask_loss / len(dataLoader)))
valF.write("=======================================")
logger.info("Acc:{}, Acc_class:{}, mIoU:{}, fwIoU: {}".format(Acc, Acc_class, mIoU, FWIoU))
valF.flush()
if __name__ == "__main__":
logger.info("=======Lane Segmentation Start...=======")
start_seg()
CV 型任务搭建模板
- 数据处理
封装函数 读文件 预处理 异步 - 模型设计
1)网络结构 单层网络 多层网络 激活函数 CNN
2)损失函数 均方误差 Cross Entropy + SoftMax - 训练配置
1)优化器 SGD 学习率 Momentum/AdaGrad/Adam 正则化
2)资源配置 单机CPU GPU 多机多卡 单机多卡 - 训练过程
训练 评价指标 校验 作图 - 保存/加载
预测场景 恢复训练场景
TIP:
训练出来的颜色标注可以用
- 假设训练错误/成本很高,而且验证成本/错误几乎与之相等。这是什么意思?我们应该做些什么?
这表明欠拟合。我们可以添加更多参数,增加模型的复杂性或减少正则化。
参考链接:
1. 你有哪些deep learning(rnn、cnn)调参的经验?
2. 训练集、验证集和测试集
3. 无人车车道线检测训练集-初赛
4. 无人车车道线检测训练集-复赛
请多多指教。
文章标题:车道线项目总结
本文作者:顺强
发布时间:2020-03-23, 23:59:00
原始链接:http://shunqiang.ml/lane-summary/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。