大家好,欢迎来到IT知识分享网。
1.概述
ENet(Efficient Neural Network)是一种专为实时语义分割任务设计的深度神经网络架构。与SegNet相比,ENet在尺寸和推理时间上都有显著的优势,同时能够提供相当或更好的准确性。
2.网络架构
2.1 Initial模块
2.2 Bottleneck模块
每个bottleneck模块由三个卷积层组成:
- 1×1投影卷积:这个卷积层用于减小通道数目,为后续的主卷积层做准备。
- 主卷积层:这是模块的核心,可以是标准卷积、空洞卷积或完全卷积。在原始论文中,这些卷积层被描述为具有3×3卷积核的conv操作。
- 1×1扩展卷积:这个卷积层用于将通道数目扩展回原来的大小,以便于与其他特征图进行合并。
在这些卷积层之间,使用批量归一化(Batch Normalization, BN)和PReLU激活函数。如果bottleneck模块负责下采样,那么会在主分支上添加一个最大池化层,并且第一个1×1投影卷积会被一个步长为2的2×2卷积替换,以匹配特征图的数量。
2.3 编码器和解码器
- 编码器:由前三个阶段组成,每个阶段包含多个bottleneck模块。这些阶段的目的是通过连续的卷积和池化操作提取图像的特征。
- 解码器:由后两个阶段组成,它们使用上采样和空间卷积操作来重建图像,并生成最终的分割结果。
2.4 特殊设计
- 空间Dropout:为了正则化和防止过拟合,ENet在bottleneck2.0之前使用0.01的Dropout比率,在之后使用0.1的Dropout比率。
- 无偏置的投影卷积:为了降低卷积的调用和内存溢出,投影卷积中没有偏置项,这一点对准确率没有影响。
- max uppooling:在解码器中,最大池化被max uppooling操作所取代,这样可以与后续的空间卷积更好地结合。
- 空间卷积:padding被空间卷积所取代,这有助于更好地控制上采样过程中的特征图大小。
2.5 性能优化
- 最后一个全卷积模块:出于性能考虑,ENet决定仅将全卷积作为网络的最后一个模块。这个模块单独占用了解码器处理时间的相当一部分,但为了保持实时性能,这是一个必要的权衡。
3.设计选择
3.1 特征图分辨率的权衡
在语义分割任务中,特征图分辨率的调整是一个双刃剑。一方面,降低分辨率可以减少计算负担,提高处理速度;另一方面,这可能导致空间信息的损失,影响分割的精确度。为了平衡这两个方面,ENet借鉴了SegNet的策略,通过索引保存和稀疏上采样技术,有效地减少了内存消耗,同时尽可能地保留了空间细节。
3.2 空洞卷积的应用
空洞卷积(Dilated Convolution)作为一种能够增大感受野的技术,对于提高模型对上下文信息的捕捉能力至关重要。在复杂的视觉场景中,如道路环境中的行人和骑行者识别,空洞卷积使得模型能够更好地理解对象与其周围环境的关系。ENet的实验结果表明,空洞卷积在提高分割精度方面起到了关键作用。
3.3 早期下采样的策略
ENet的早期下采样策略体现了对计算资源的精细管理。通过在网络的初始阶段大幅度减小输入尺寸,ENet有效地降低了后续层的计算负担。这种策略基于一个核心假设:视觉信息在空间上存在高度的冗余性,可以通过更紧凑的表示形式进行编码。这一策略的成功实施,为后续的特征提取和上采样操作奠定了坚实的基础。
3.4 编码器与解码器的结构优化
ENet的编码器-解码器架构经过精心设计,以确保编码器能够充分提取图像特征,而解码器则专注于恢复这些特征以实现精确的像素级预测。这种不对称的结构设计反映了两个部分在任务中的不同重要性,同时也减少了模型的参数数量,提高了整体的运行效率。
3.5 非线性激活函数的优化
在ENet中,传统的ReLU激活函数被PReLU所取代,这一改变基于对网络行为的深入分析。PReLU允许每个特征图独立学习非线性斜率,提供了更大的灵活性和适应性。这种激活函数的选择不仅提高了模型的表达能力,还有助于在不同的网络层中实现更精细的信息处理。
3.6 信息保存维度的创新
ENet在信息保存维度上采用了创新的策略,通过并行执行池化和卷积操作,并连接结果特征图,显著提高了模型的推理速度。这种策略的实施,不仅优化了信息流,还减少了信息的丢失,提高了分割的准确性。
3.7 分解卷积的计算优化
ENet通过将大的卷积核分解为多个小的卷积核,有效地减少了模型的参数数量和计算复杂度。这种分解策略不仅提高了模型的运行速度,还通过增加非线性层,增强了模型的表达能力。
3.8 空洞卷积的性能提升
空洞卷积在不增加计算负担的情况下,显著提高了模型的感受野。这种技术的应用使得ENet在保持分辨率的同时,能够捕捉到更广泛的上下文信息,从而提高了分割的精度。
3.9 正则化技术的探索
在面对小规模数据集时,ENet通过空间Dropout技术有效地防止了过拟合。这种正则化方法不仅提高了模型的泛化能力,还通过随机丢弃部分特征图,增加了模型的鲁棒性。
4.数据集处理
5.模型训练
5.1 环境安装
conda create --name enet python=3.7 source activate enet conda install pytorch torchvision cudatoolkit=10.2 -c pytorch pip install cython matplotlib tqdm opencv-python scipy pillow
5.2 数据处理
import os import sys import glob import json import math import uuid import random import numpy as np import PIL.Image import PIL.ImageDraw from tqdm import tqdm def shape_to_mask(img_shape, points, shape_type=None, line_width=10, point_size=5): mask = np.zeros(img_shape[:2], dtype=np.uint8) mask = PIL.Image.fromarray(mask) draw = PIL.ImageDraw.Draw(mask) xy = [tuple(point) for point in points] if shape_type == 'circle': assert len(xy) == 2, 'Shape of shape_type=circle must have 2 points' (cx, cy), (px, py) = xy d = math.sqrt((cx - px) 2 + (cy - py) 2) draw.ellipse([cx - d, cy - d, cx + d, cy + d], outline=1, fill=1) elif shape_type == 'rectangle': assert len(xy) == 2, 'Shape of shape_type=rectangle must have 2 points' draw.rectangle(xy, outline=1, fill=1) elif shape_type == 'line': assert len(xy) == 2, 'Shape of shape_type=line must have 2 points' draw.line(xy=xy, fill=1, width=line_width) elif shape_type == 'linestrip': draw.line(xy=xy, fill=1, width=line_width) elif shape_type == 'point': assert len(xy) == 1, 'Shape of shape_type=point must have 1 points' cx, cy = xy[0] r = point_size draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=1, fill=1) else: assert len(xy) > 2, 'Polygon must have points more than 2' draw.polygon(xy=xy, outline=1, fill=1) mask = np.array(mask, dtype=bool) return mask def shapes_to_label(img_shape, shapes, label_name_to_value): cls = np.zeros(img_shape[:2], dtype=np.int32) ins = np.zeros_like(cls) instances = [] for shape in shapes: points = shape['points'] label = shape['label'] group_id = shape.get('group_id') if group_id is None: group_id = uuid.uuid1() shape_type = shape.get('shape_type', None) cls_name = label instance = (cls_name, group_id) if instance not in instances: instances.append(instance) ins_id = instances.index(instance) + 1 cls_id = 1 # cls_id = label_name_to_value[cls_name] mask = shape_to_mask(img_shape[:2], points, shape_type) cls[mask] = cls_id ins[mask] = ins_id return cls, ins def lblsave(filename, lbl): if os.path.splitext(filename)[1] != '.png': filename += '.png' # Assume label ranses [-1, 254] for int32, # and [0, 255] for uint8 as VOC. if lbl.min() >= 0 and lbl.max() <= 255: lbl_pil = PIL.Image.fromarray(lbl.astype(np.uint8), mode='L') lbl_pil.save(filename) else: raise ValueError( '[%s] Cannot save the pixel-wise class label as PNG. ' 'Please consider using the .npy format.' % filename ) if __name__ == '__main__': data_path = sys.argv[1] out_path = sys.argv[2] if not os.path.exists(out_path): os.makedirs(out_path) label_name_to_value = {
'_background_': 0, 'a': 1, } json_fns = glob.glob(os.path.join(data_path, '/*.json'), recursive=True) out_lst = [] for json_fn in tqdm(json_fns): with open(json_fn, 'r') as f: data = json.load(f) img_shape = (data['imageHeight'], data['imageWidth']) lbl, _ = shapes_to_label(img_shape, data['shapes'], label_name_to_value) image_fn = json_fn.replace('.json', '.jpg') label_fn = json_fn.replace('.json', '_label.png') if not os.path.exists(image_fn): print(image_fn + ' not exists') continue else: img = PIL.Image.open(image_fn) mask = PIL.Image.open(label_fn) if img.size != mask.size: print(image_fn, img.size, mask.size) continue lblsave(label_fn, lbl) out_lst.append(image_fn + ',' + label_fn) random.shuffle(out_lst) trn_num = int(len(out_lst) * 0.9) with open(os.path.join(out_path, 'train.txt'), 'w') as f: f.write('\n'.join(out_lst[:trn_num])) with open(os.path.join(out_path, 'val.txt'), 'w') as f: f.write('\n'.join(out_lst[trn_num:]))
执行以下命令:
python generate_label.py /path/to/ datasets
5.3 训练
运行以下命令:
python main.py -m train --save-dir save/ENet_Card --name ENet --dataset card --dataset-dir datasets --epochs 100 --height 512 --width 512 --print-step
5.4 测试
运行以下命令:
python demo.py demo_imgs save/ENet_Card/ENet results
5.5 转成onnx
运行以下命令:
python convert_to_onnx.py --input save/ENet_Card/ENet --output save/ENet_Card/ENet.onnx
6.模型推理
#include <iostream> #include <string> #include <vector> #include <fstream> #include <sstream> #include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> void show_img(std::string name, const cv::Mat& img) {
cv::namedWindow(name, 0); int max_rows = 500; int max_cols = 600; if (img.rows >= img.cols) {
cv::resizeWindow(name, cv::Size(img.cols * max_rows / img.rows, max_rows)); } else {
cv::resizeWindow(name, cv::Size(max_cols, img.rows * max_cols / img.cols)); } cv::imshow(name, img); } int main(int argc, char* argv[]) {
std::string path = "D:/data/DataSet/book/"; std::vector<std::string> filenames; cv::glob(path, filenames, false); for (auto img_name : filenames) {
cv::Mat img = cv::imread(img_name); cv::Size reso(512, 512); cv::Mat blob = cv::dnn::blobFromImage(img, 1.0 / 255, reso, cv::Scalar(0, 0, 0), false, false); cv::dnn::Net net = cv::dnn::readNet("model/ENet.onnx"); net.setInput(blob); auto t0 = cv::getTickCount(); cv::Mat out = net.forward(); std::cout << out << std::endl; auto t1 = cv::getTickCount(); std::cout << "elapsed time: " << (t1 - t0) * 1000.0 / cv::getTickFrequency() << "ms" << std::endl; cv::Mat segm = cv::Mat::zeros(out.size[2], out.size[3], CV_8UC1); for (int i = 0; i < out.size[2] * out.size[3]; ++i) {
if (out.ptr<float>(0, 0)[i] < out.ptr<float>(0, 1)[i]) {
segm.data[i] = 255; } } cv::resize(segm, segm, img.size(), 0.0, 0.0, cv::INTER_NEAREST); show_img("img", img); show_img("out", segm); cv::waitKey(); } return 0; }
7.实验结果
这些性能指标的比较揭示了ENet在语义分割任务的几个关键评估领域中展现出更优的性能。具体来说,ENet在类别IoU方面的表现超过了SegNet,这意味着ENet在识别和分割不同类别的像素时更为准确。类别IoU是一个衡量模型在特定类别上分割精度的指标,它通过计算预测的分割区域与真实标注之间的重叠程度来评估模型的性能。
此外,ENet在iIoU方面也展现了更好的结果,这表明ENet在处理实例级别的分割任务时同样表现出色。实例分割不仅需要识别图像中的不同类别,还需要区分属于同一类别的不同实例,这对于理解复杂场景中的个体对象非常重要。
最后,整体类别IoU的提升进一步证实了ENet在整体分割性能上的优势。这一指标综合考虑了所有类别的分割结果,提供了模型整体性能的一个全面评估。
这些结果表明,ENet在处理语义分割任务时,不仅在单个类别上表现出色,而且在整体分割精度上也超越了SegNet。这使得ENet成为实时语义分割应用中一个非常有力的竞争者,尤其是在需要高精度分割结果的场景中。
8.总结
ENet所提出的这一创新神经网络架构,专为语义分割任务量身打造,其核心设计理念在于充分发挥嵌入式设备上有限资源的潜力。在这一目标的指引下,ENet的工作取得了显著的成效,不仅成功实现了与现有技术相比更为高效的计算性能,而且在某些情况下,还实现了与之相匹配甚至更优的结果,即便这些现有技术对计算能力和内存的需求远高于ENet的设计。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/134403.html