赞
踩
前面基本已经将yolo3的大致细节都分析了,那么现在就要训练自己下载的数据集来看下效果,进行迁移学习,首先我会先对github本身自定义的custom数据集进行训练,只有一张照片,一个标签签,之后训练自己的数据集是要从xml文件先提取标签,完全按照custom中的格式进行布局,然后修改一下cfg文件就可以运行。dataset源码是对txt文件的处理,在实际运行中对数据进行分析是利用panda,所以直接利用panda生成csv文件进行读取更加方便。代码https://github.com/eriklindernoren/PyTorch-YOLOv3
在data文件夹下,这里一种有二个文件夹,三个文件,这里主要是一个模板,用于将自己的数据集处理成这种形式方便训练,这里也可以直接训练custom数据集,现在先来操作一下训练custom
在config文件夹下,如果是linux系统就可以直接运行.sh文件产生custom的cfg文件,这个文件的作用是darknet的网络部分,由于数据集的类别变化,输出的信息是由变化的,window系统可以直接复制yolo3.cfg文件 然后修改里面的参数,从新命名为yolo3-custom.cfg文件。具体修改如下(打开文件在最下面)
custom 中只有一个分类的train,所以类别为1,还有filters=3*(5+1)=18,3是yolo3中每个预测大小都有三种,5是框的坐标和置信度,1是我们需要训练的类别,
这里只写个一处位置的修改,上面还有二处,找到【yolo】的位置,修改classes,和上一层的filters=18就行,
打开train.py文件,修改这二个地方,改成custom的数据集即可。到这里是可以运行的,当然你的电脑配置可能到不到设置的要求,在按照下面的修改一下batch 和num,主要看你的电脑配置了,
到目前为止,应该可以正常训练,如果不行,就是包的问题,
注意这里的tensorflow是cpu版本,cpu和gpu不能同时安装,主要是利用tensorboard,如果是这里有问题的话,可以将train.py利用tensorboard的代码全部注释掉,如下
首先看下我下载数据
二个文件夹,一个放xml文件,一个是照片,
这里我们需要读取每一个xml文件来生成label,一个xml文件生成一个label,名字也是对应照片上的名字。这里还有一个小细节,就是在生成之前label,我们需要全部的分类,比如这里有五个分类目标,这五个分类目标是遍历每一个xml物体的名称,去掉重复的部分,就是分类了,这部分代码我没有展示,可以需要自己写(比较简单),下面放的这个代码是遍历全部的xml文件生成每一个对应的label,而且这里的框的位置是xywh形式。
import os,shutil import numpy as np import xml.etree.ElementTree as ET import os # 海胆 全息 扇贝 海星 海草 _classes = ('echinus', 'holothurian', 'scallop', 'starfish', 'waterweeds') from PIL import Image import torchvision.transforms as transforms _class_to_ind = dict(zip(_classes, range(5))) _ind_to_class = dict(zip(range(5), _classes)) a='E:\pytorch\JPEGImages/train/box/' e='E:\pytorch\JPEGImages/train/label/' d = 'E:\pytorch\JPEGImages/train/image/' b=os.listdir(a) xml = [] txt = [] img = [] for i in range(len(b)): xml.append(a + b[i]) path,filename = os.path.split(xml[i]) name = os.path.splitext(filename) txt.append(e+name[0]+'.txt') img.append(d+name[0]+'.jpg') for j in range(len(b)): xml_list = [] nn = [] d = img[j] image = transforms.ToTensor()(Image.open(d).convert('RGB')) _, height, weight = image.shape a = xml[j] tree = ET.parse(a) root = tree.getroot() for member in root.findall('object'): a = member[0].text b = _class_to_ind[a] value = (b, int(member[1][0].text), int(member[1][1].text), int(member[1][2].text), int(member[1][3].text) ) xml_list.append(value) for ii in range(len(xml_list)): tt = int(xml_list[ii][0]) x1 = float(xml_list[ii][1]) y1 = float(xml_list[ii][2]) x2 = float(xml_list[ii][3]) y2 = float(xml_list[ii][4]) x = ((x2+x1)/2)/weight y = ((y2+y1)/2)/height w = (x2-x1)/weight h = (y2-y1)/height x = round(x,8) y = round(y,8) w = round(w,8) h = round(h,8) sum = (tt,x,y,w,h) nn.append(sum) e = txt[j] file = open(e, 'w') for p in range(len(nn)): label = str(nn[p][0]) x = str(nn[p][1]) y = str(nn[p][2]) w = str(nn[p][3]) h = str(nn[p][4]) file.write(label + ' ') file.write(x + ' ') file.write(y + ' ') file.write(w + ' ') file.write(h + '\n') file.close()
代码对应每一个不同的xml可能不同,主要是你需要学会读取xml文件的信息,在我之前的博客有讲解ET的使用。生成效果如下
这里边处理好全部的label了,还需要在整理二个txt文件保存路径即可,自己创建train.txt文件,保存训练照片的路径,在创建valid.txt保存验证数据集的路径,还需要穿件一个classes.names文件,保存类整体如下,对比custom还是很容易实现这一步的。
之后就是修改cfg文件了,一共有二个位置修改网络yolo3中的【yolo】层calsses修改自己分类和fitlers=3*(5+分类数)和创建custom.data用来数据集读取,具体如下之后再train.py再一次修改一下就可以了。
import os import glob import pandas as pd import xml.etree.ElementTree as ET _classes = ('echinus', 'holothurian', 'scallop', 'starfish', 'waterweeds') from PIL import Image import torchvision.transforms as transforms _class_to_ind = dict(zip(_classes, range(5))) _ind_to_class = dict(zip(range(5), _classes)) d = 'E:\pytorch\JPEGImages/train/image/' def xml_to_csv(path): xml_list = [] for xml_file in glob.glob(path + '/*.xml'): print(xml_file) path, filename = os.path.split(xml_file) name = os.path.splitext(filename) num = int(name[0]) img=(d + name[0] + '.jpg') image = transforms.ToTensor()(Image.open(img).convert('RGB')) _, height, weight = image.shape tree = ET.parse(xml_file) root = tree.getroot() for member in root.findall('object'): a = member[0].text classes = _class_to_ind[a] value = (num, img, weight, height, classes, int(member[1][0].text), int(member[1][1].text), int(member[1][2].text), int(member[1][3].text) ) xml_list.append(value) column_name = ['num','filename','width','height','class','xmin','ymin','xmax','ymax'] xml_df = pd.DataFrame(xml_list,columns=column_name) return xml_df def main(): image_path = os.path.join(os.getcwd(),'E:\pytorch\JPEGImages/train/box') xml_df = xml_to_csv(image_path) xml_df.to_csv('E:\pytorch\JPEGImages/train/labes2.csv',index=None) #带路径 'E://' print('finish') main()
这一步是生成csv文件, 这里方便讲解就放了一点数据,如下图所示,我写num的目的是为了标记,比如全部是0的时候,说明这些信息全是一张照片的信息,一个照片可能有多个框,所以这里是为了一张照片的信息。
from PIL import Image import pandas as pd import numpy as np import torchvision.transforms as transforms from torch.utils.data import Dataset, DataLoader import torch import torch.nn.functional as F import random # 定义读取文件的格式 def pad_to_square(img, pad_value): c, h, w = img.shape dim_diff = np.abs(h - w) # (upper / left) padding and (lower / right) padding pad1, pad2 = dim_diff // 2, dim_diff - dim_diff // 2 # Determine padding pad = (0, 0, pad1, pad2) if h <= w else (pad1, pad2, 0, 0) # Add padding img = F.pad(img, pad, "constant", value=pad_value) return img, pad def resize(image, size): image = F.interpolate(image.unsqueeze(0), size=size, mode="nearest").squeeze(0) return image def random_resize(images, min_size=288, max_size=448): new_size = random.sample(list(range(min_size, max_size + 1, 32)), 1)[0] images = F.interpolate(images, size=new_size, mode="nearest") return images def horisontal_flip(images, targets): images = torch.flip(images, [-1]) targets[:, 2] = 1 - targets[:, 2] return images, targets class Mydataset(Dataset): def __init__(self,csv,transform=None, target_transform=None,): super(Mydataset,self).__init__() self.path = [] self.df=pd.read_csv(csv) for i in range(3): a = self.df[self.df['num'] == i] n = a['filename'].index[0] self.path.append(a['filename'][n]) self.transform = transform self.target_transform = target_transform def __getitem__(self, index): path=self.path[index] img = transforms.ToTensor()(Image.open(path).convert('RGB')) a = self.df[self.df['num'] == index] d = a.iloc[:, 4:9] e = np.array(d) targets = np.zeros((len(a), 6)) targets[:, 1:] = e img, pad = pad_to_square(img, 0) _, padded_h, padded_w = img.shape x1 = targets[:, 2] y1 = targets[:, 3] x2 = targets[:, 4] y2 = targets[:, 5] x1 += pad[0] y1 += pad[2] x2 += pad[1] y2 += pad[3] targets[:, 2] = ((x1 + x2) / 2) / padded_w targets[:, 3] = ((y1 + y2) / 2) / padded_h targets[:, 4] = (x2 - x1) / padded_w targets[:, 5] = (y2 - y1) / padded_h if np.random.random() < 0.5: img, targets = horisontal_flip(img, targets) return path,img,targets def collate_fn(self, batch): paths, imgs, targets = list(zip(*batch)) targets = [boxes for boxes in targets if boxes is not None] # Add sample index to targets for i, boxes in enumerate(targets): boxes[:, 0] = i targets = torch.cat(targets, 0) # Selects new image size every tenth batch if self.multiscale and self.batch_count % 10 == 0: self.img_size = random.choice(range(self.min_size, self.max_size + 1, 32)) # Resize images to input shape imgs = torch.stack([resize(img, self.img_size) for img in imgs]) self.batch_count += 1 return paths, imgs, targets def __len__(self): return len(self.path) val_data=Mydataset(csv='E:\pytorch\JPEGImages/train/22.csv', transform=transforms.ToTensor()) print(len(val_data)) trainloader = DataLoader(val_data, batch_size=1,shuffle=True, num_workers=0) for i,(p,img,e) in enumerate(trainloader): print(img.shape) print(e)
重写dataset的核心就是要让输出值有img和targets,代码细节不做过多讲解了,主要说明这里的 len(self):这个函数决定这__getitem__(self, index): index,这一步是读取csv文件的关键点。利用csv文件可以进行数据分析,比如各个大小形式的照片比例是多少,框的大小,都影响着yolo3的效果,
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。