1.下载源码。pytorch下SegNet源码链接
https://github.com/meetshah1995/pytorch-semseg
该源码重点针对FCN-8s模型进行了分析测试,我主要是尝试使用SegNet测试自己的数据。
下面首先使用VOC2012数据集小试牛刀!
2.修改配置文件并执行训练。
需要注意一点,每次重新执行训练之前,要清空数据集文件夹中的pre_encoded文件夹,在我电脑里,该文件夹的路径是
D:\SemanticSegmentation\pytorch-semseg\datasets\VOC\VOC2012\SegmentationClass\pre_encoded
配置文件很关键。修改配置文件的主要依据是作者在github上的README,我是将configs文件夹中的fcn8s_pascal.yml复制了一个,改名为segnet_pascal.yml之后,再修改参数。
有一个参数需要特别注意,是sbd_path,需要改成自己工程的路径
之后按照下述命令开始执行
python train.py --config configs/segnet_pascal.yml
3.遇到的错误及相关分析:
(1).错误1
TypeError: expected str, bytes or os.PathLike object, not NoneType
解决方案如下
“The code in NO.47 line, “sbd_path=None” was written in the “pascal_voc_loader.py” file, if you download the project from the github directly. You should code it like this, sbd_path="/…/…/…/benchmark_RELEASE/(the path your saved) ".”
即修改pascal_voc_loader.py文件47行中的参数sbd_path为与yml文件中保持一直即可。
参考链接为
https://github.com/meetshah1995/pytorch-semseg/issues/188
(2).错误2,是在训练过程中出现的错误。
Traceback (most recent call last):
File "train.py", line 229, in
train(cfg, writer, logger)
File "train.py", line 118, in train
for (images, labels) in trainloader:
File "C:\Users\MSIK\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 637, in __next__
return self._process_next_batch(batch)
File "C:\Users\MSIK\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 658, in _process_next_batch
raise batch.exc_type(batch.exc_msg)
RuntimeError: Traceback (most recent call last):
File "C:\Users\MSIK\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 138, in _worker_loop
samples = collate_fn([dataset[i] for i in batch_indices])
File "C:\Users\MSIK\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 232, in default_collate
return [default_collate(samples) for samples in transposed]
File "C:\Users\MSIK\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 232, in
return [default_collate(samples) for samples in transposed]
File "C:\Users\MSIK\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 209, in default_collate
return torch.stack(batch, 0, out=out)
RuntimeError: invalid argument 0: Sizes of tensors must match except in dimension 0. Got 500 and 333 in dimension 3 at c:\a\w\1\s\tmp_conda_3.7_061434\conda\conda-bld\pytorch_1544163540495\work\aten\src\th\generic/THTensorMoreMath.cpp:1333
参考了以下博客
https://blog.csdn.net/weixin_41278720/article/details/84586734
该文中的错误为“Got 1 and 3 in dimension 1”,所以根据4维卷积原理,其错误发生在1维度,即RGB通道上,所以该博客在python脚本中添加了img = img.convert(‘RGB’)。
第二篇博客
https://oldpan.me/archives/pytorch-conmon-problem-in-training
博客中指出了两种错误可能
(a) 你输入的图像数据的维度不完全是一样的,比如是训练的数据有100组,其中99组是256256,但有一组是384384,这样会导致Pytorch的检查程序报错。
(b) 另外一个则是比较隐晦的batchsize的问题,Pytorch中检查你训练维度正确是按照每个batchsize的维度来检查的,比如你有1000组数据(假设每组数据为三通道256px256px的图像),batchsize为4,那么每次训练则提取(4,3,256,256)维度的张量来训练,刚好250个epoch解决(2504=1000)。但是如果你有999组数据,你继续使用batchsize为4的话,这样999和4并不能整除,你在训练前249组时的张量维度都为(4,3,256,256)但是最后一个批次的维度为(3,3,256,256),Pytorch检查到(4,3,256,256) != (3,3,256,256),维度不匹配,自然就会报错了,这可以称为一个小bug。
那么怎么解决,针对第一种,很简单,整理一下你的数据集保证每个图像的维度和通道数都一直即可。第二种来说,挑选一个可以被数据集个数整除的batchsize或者直接把batchsize设置为1即可。
该文章的错误是“Got 14 and 13 in dimension 0”,所以我认为应该是第2种错误。
下面回到我自己遇到的错误,Got 500 and 333 in dimension 3 ,这应该是属于图像尺寸的参数了。所以我的思路是读入图像后,规整化到同样尺寸。于是,我修改了配置文件segnet_pascal.yml中的两个参数为
img_rows: 500
img_cols: 500
问题得到解决。
4.训练结果及测试
第一次训练:配置文件如下:用时26h
model:
arch: segnet
data:
dataset: pascal
train_split: train_aug
val_split: val
img_rows: 500
img_cols: 500
path: D:/SemanticSegmentation/pytorch-semseg/datasets/VOC/VOC2012/
sbd_path: D:/SemanticSegmentation/pytorch-semseg/datasets/VOC/benchmark_RELEASE/
training:
train_iters: 100000
batch_size: 8
val_interval: 1000
n_workers: 16
print_interval: 50
optimizer:
name: 'adam'
lr: 1.0e-3
weight_decay: 0.0005
betas: [0.9,0.999]
loss:
name: 'cross_entropy'
size_average: True
lr_schedule:
name: 'multi_step'
milestones: [30000,60000]
gamma: 0.1
resume: null
训练结果如下:
INFO:ptsemseg:Iter [100000/100000] Loss: 0.5859 Time/Image: 0.0985
182it [00:57, 3.15it/s]
INFO:ptsemseg:Iter 100000 Loss: 0.9269
Overall Acc: 0.7793233236714976
INFO:ptsemseg:Overall Acc: : 0.7793233236714976
Mean Acc : 0.11327407139174522
INFO:ptsemseg:Mean Acc : : 0.11327407139174522
FreqW Acc : 0.6261152221392225
INFO:ptsemseg:FreqW Acc : : 0.6261152221392225
Mean IoU : 0.08097452127462494
INFO:ptsemseg:Mean IoU : : 0.08097452127462494
从结果来看,模型训练的并不好。
测试代码如下:
不使用CRF后处理
python test.py --model_path D:/***/segnet_pascal_best_model.pkl --dataset pascal --img_path D:/***/2007_000027.jpg --out_path D:/***/2007_000027_out.jpg
使用CRF后处理
python test.py --model_path D:/***/segnet_pascal_best_model.pkl --dataset pascal --dcrf --img_path D:/***/2007_000027.jpg --out_path D:/***/2007_000027_out.jpg
VOC2012数据集训练不成功,由于时间有限,我并没有继续调试这个数据集的训练,而是直接转到了自己数据集的构建。
首先我将自己的数据裁切成了256*256,图像是RGB3通道,标签为单通道,背景为0,地物标注序号从1开始,以只有1类地物为例,此时标签图像为0/1二值图像。
数据集不妨命名为VOC2019,文件夹如下
JPEGImages里是图像,SegmentationClass\pre_encoded存的是二值标签图像,ImageSets\Segmentation中存的是3个txt文件,分别是train.txt,trainval.txt,val.txt。
到此,数据集准备完毕,还需要修改程序参数。
设置文件segnet_pascal.yml如下,注释掉sbd_path
model:
arch: segnet
data:
dataset: pascal
train_split: train
val_split: val
img_rows: 'same'
img_cols: 'same'
path: D:/.../datasets/VOC/VOC2019/
# sbd_path: D:/.../datasets/VOC/benchmark_RELEASE/
pascal_voc_loader.py需要修改的地方如下:具体位置可以在代码中搜索。
(1)sbd_path=None
(2)self.n_classes = 2。我们只有1类地物。
(3)self.mean = np.array([104.00699, 116.66877, 122.67892]) 需要重新计算自己数据集的各通道均值。
(4)注释掉self.setup_annotations()。不需要SBD去扩充数据集,在最开始裁切时已经进了数据扩充。
(5)return np.asarray( [ [0, 0, 0], [255, 0, 0],]) 。我们只有1类地物。
(6)transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),修改为自己数据集的均值和方差。
大功告成,可以开始设置参数训练了。
总结:本工程有问题,训练曲线很完美,但是测试结果比较差,在github上也有几个和我遇到同样情况的。计划换一个工程再试。