代码来源:https://github.com/JKBox/YOLOv3-quadrangle
主要使用于四边形文本检测。
代码阅读
代码结构如下:
1. 配置文件 cfg/
共2个文件,yolov3.cfg定义了darknet+yolo_head的整个结构,ICDAR2015.data说明了训练路径、参数等信息。
2. 模型结构构建 models.py
主要看YOLOLayer和Darknet类,对应yolov3.cfg来进行网络结构的搭建,网络结构图如下,讲解可见[1]:
3. 配置文件读取 utils/parse_config.py
主要关注parse_model_config中的读取方法,第30行,key和value是用“=”来获取的,因此需注意yolov3.cfg中不能出现其他注释信息,且=两端不能有空格,如:
4. 图像、标签读取 utils/datasets.py
图像标签为txt文件,根据自己标签的不同,主要关注load_images_and_labels()方法(训练阶段)load_images()方法(推理阶段)。
上图为指向.jpg和.txt路径的txt文件的修改,已经修改为我自己的数据集的格式,我的格式为:
<img_file_path>/img1.jpg <txt_file_path>/img1.txt
共n行,每行2个路径,前一个是图片路径,后一个是与图片同名的对应标签的路径,两者用空格隔开。
上图是对标注内容的修改,代码默认的格式为:
由于我的数据集仅检测文本,不检测具体类别,因此没有类别标记,只有8个数,且是逗号分隔的:
故修改代码,在每行的前方添加一个类别0。
对推理时图片读取路径的修改:
5. 一些方法类 utils/utils.py
主要关注build_targets()方法,它在models.py的YOLOLayer类中被调用:
6. 训练入口 train.py
设置对应参数,运行即可训练:
7. 推理 detect.py
根据自己需要推理的图片格式进行相应修改,默认是可对一张图或一个文件夹中的所有图进行推理。
img_size应设置为跟训练时相同,batch_size=1不变,两个阈值可根据实际情况调整。
代码调试
1. RuntimeError: Index put requires the source and destination dtypes match, got Float for the destination and Double for the source.
找到对应utils/utils.py 249行,可以发现是gp1_x和gp_x_center的type不一样,其中gp1_x为double,应改为tensor。
往上查找,可发现gp1_x由t求得:
因此将t改为float即可:
2. RuntimeError: Index put requires the source and destination dtypes match, got Byte for the destination and Bool for the source.
和上面那个报错一样,也是类型不匹配。对应utils.py 270行:
由提示信息可得,应该将byte改为bool,观察代码可知,TP/FP/FN的类型为byte(127行):
输出TP类型和数值有:
而utils.py 270行等式右边为bool类型,因此需转换格式:
之后遇到另一个报错:
观察models.py 272行可知,i的类型是np.float32,却作为了下标:
将i转为int即可:
3. UserWarning: indexing with dtype torch.uint8 is now deprecated, please use a dtype torch.bool instead.
报警而不是报错,不解决不影响运行,但是输出提示太多了,影响输出日志的阅读。
观察提示,对应的是models.py的163和174行:
由提示信息可得,建议我们将uint8替换为bool类型, 而上述代码中的tcls和mask是one-hot编码的,输出查看可发现其确实是uint8类型。
因此添加该语句即可:
4. NameError: name 'nC' is not defined
对应models.py 185行,将nC改为self.nC:
5. UnicodeDecodeError: 'gbk' codec can't decode byte 0x8c in position xx: illegal multibyte sequence
.cfg配置文件中出现了多余注释信息或其他信息,删除所有信息,仅保留配置网络结构即可。
运行结果
conf到R都是从开始训练到目前为止的记录。conf和cls表示的是到目前为止的置信度损失和分类损失,由于我的数据集只有一个类别因此类别损失为0。loss是总损失,该总损失为置信损失+分类损失+8个坐标点损失之和,P和R表示的是平均准确率和召回率。
nTargets到FN都是当前一个batch的记录。nTargets是指该batch中的所有图片的所有文本框的总数。TP/FP/FN含义不作赘述。
time是上一条输出信息到这一条信息的运行时间,默认是一个batch打印一次,我做了修改,100batch打印一次。
训练产生的权重文件将保存在weights/下,训练过程中的一些记录值保存在results.txt里。
参数调试
代码中有几个阈值没有参数化:
1. 训练时的iou阈值 utils.py build_targets()中,默认0.1:
2. 模型结构中的置信度阈值 models.py YOLOLayer中,默认0.5:
3. 训练中,FP等指标计算时的阈值 utils.py build_targets()中,默认0.5:
基本上,如果训练结果中精准率比召回率低很多,那么需要调高1中的iou阈值。
已参数化的可改参数,train.py中:
其中,img_size需设置为跟自己的数据集差不多的大小,且为32的倍数。
输入图片会先统一padding resize到[img_size, img_size]的大小,之后再输入网络: