在一个风和日丽,雷电交加的…好吧不编了~
因为这学期有人工智能的课,然后呢也有个像自己弄个小模型出来。
在跟某研二师兄的交流中,他叫我去团队跑下模型做个对比实验,顺便带着发个paper。
我心想:我在机器学习领域,属于纯小白~ 试试吧!然后再开始了漫长的学习之路~
在开始之前,我简直属于是打昏的小鸡摸不着头脑。啥是啥~
我只知道,当我第一次硬着头皮,找哪些N年前的博客啥的,进行复盘时的痛苦
我当时想弄个SSD啥的,找了一篇像样子的开始做(当时环境搭好了)。
走一步卡半个小时,改源码,根据报错一个个找,才痛苦
回归正题!
搭建环境:https://tangshusen.me/Dive-into-DL-PyTorch/#/
上面这个网站是一个翻译的开源了的机器学习教程。我先翻看了仓库…
搭建环境直接在左侧的2.1进行配置,这个文章里面介绍了2篇知乎的文章,点进去手把手教你~
Mac的话搭建的时候会变一下(2021-11),也就是最新的更那个有一点不同,但是谷歌下就能出来。
Windows的话存在显卡,要去下载Cuda然后配置环境啥的,网速嘛这个东西dddd~
最好换成镜像,下载好镜像轮子安装这样是最快的~
当你能打印出️torch的版本的时候就是成功了。
Mac标注文件的下载就很简单,直接谷歌搜索labellmg,这里我贴个网址
结合brew就很快,Windows我就没试过了
如何使用的话,网上大片教程,不多赘述~
正当我一筹莫展的时候,师兄推荐了一个博主
B站搜索:Bubbliiiing 在动态里面搜索pytorch
这是你就会发现:手把手教你训练自己的数据集
这位博主的教学思路是:
简单介绍网络的训练思路到每一层网络如何运作
再到如何训练自己的数据集。
我从头到尾的看了YoloV4的教学视频后就开始动手了。
根据自己的数据集所列出来的标签️
添加到/model_data/voc_classes.txt
格式相同,一行一个~
这里的数据集你可以下载博主分享的数据集 提取码: uack
也可以自己制作,但是自己制作得保证自己的数量跟得上
目录结构:VOC数据集解析 VOC2007解析
我的话是师兄已经清洗过的(实际上没清洗完全的)
我拿过来直接删除了ImageSet
下main中的文件
因为这个博主存在voc_annotation.py
的文件会随机自动的分成9:1
然后在主目录下出现2007_val.txt
与2007_train.txt
所以如果你是在本机上弄好的话,记住放上去重新生成下,因为路径变了~
注意⚠️: 以下都是在有显卡的环境下进行的
到对应的仓库下,选择对应的预训练权重。下载⏬上传⏫到/model/
下
开始编辑train.py
,学问逐渐体现:
首先是配置好class,model路径
其次是开始动手配置参数
batch_size就是:一次训练所选取的样本数。
lr 就是:学习率
num_workers就是: 线程
如果你的配置好一点:
Freeze_batch_size:16;Unfreeze_batch_size:8
差一点就:
Freeze_batch_size:8;Unfreeze_batch_size:4
Freeze_batch_size:4;Unfreeze_batch_size:2
Freeze_lr :1e-3(0.001);Unfreeze_lr: 1e-4
Freeze_lr :1e-4(0.0001);Unfreeze_lr: 1e-5
这里面有炼丹的学问了。
首先呢:UnFreeze_Epoch=100;Freeze_Epoch=50;
它会跑100Epoch,所以保证你的空间不会炸。
其次:loss会逐渐缩小,逐渐稳定到一个范围
这时候跑出来的mAP如果不满意。
有几个方法:
- 加迭代次数:UnFreeze_Epoch=150次 loss可能会下去点
- 调试lr,batch_size
最后:
当你loss为Nan的时候,就可以停了,然后调试参数。
具体怎么炼丹,经验与运气,推荐:Github,知乎看看文章
如果你碰到了问题:
解决方法:
最后附一个多个cuda错误:就是没规定哪张显卡的错❌:
# 在train.py加入这几句话
# 编号就是你要用的卡
import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'
os.environ["CUDA_VISIBLE_DEVICES"] = "1,4,7"
到这里你的train.py应该就是跑起来了~坐等吧。
当你跑完100迭代个时候,就要开始测试了。
你的训练权重都在logs下面~
编辑yolo.py
与get_map.py
改几处path就能跑起来了。
然后会输出map_out文件,拿到map_out/reslut/mAP.png
就好了。
但是一个个跑是不是不现实?
我最开始的解决方式是跑10,20,30…共10个。
但是我发现到后面loss逐渐稳定,mAP才会高起来~
所以你可以先删除前N十个,留下20个左右来测试。
反正看谁顺眼!测试他!
这个我没怎么用~看看效果呗
当你看到有框框出现的时候,还是挺开心的
同样的道理faster,SSD一模一样的道理
当我看到faster的用上面那个博主训练出来的时候不理想的时候。
我就准备开始换个库了
我就试试github星星✨最多的库
仓库地址:https://github.com/jwyang/faster-rcnn.pytorch
我直接贴步骤吧:
# 克隆
$ git clone https://github.com/jwyang/faster-rcnn.pytorch.git
# 切换版本
$ git checkout pytorch-1.0
# 你应该有Anaconda包了,基本都有了
$ cd faster-rcnn.pytorch && mkdir data
$ cd data && mkdir pretrained_model
$ cd ../
$ cd lib
$ python setup.py build develop
VGG 16:VT Server
ResNet 101 VT Server
放到/data/pretrained_model
下面
将数据集除了主目录下出现2007_val.txt
与2007_train.txt
不要
整个拿过来放到data
下面重新命名为:VOCdevkit2007
这里特别注意⚠️大小写
请以你的标注class为准!!!不然测试mAP全是0
编辑lib/datasets/pascal_voc.py
换上自己的
self._classes = ('__background__', # always index 0
'Dark shoes','Light shoes')
如果开始跑的时候报错❌不存在keyvalue。
你看他的报错栈,会发现一个lower方法~ 直接到pascal_voc.py里面删除了就好了
$ CUDA_VISIBLE_DEVICES=0 python trainval_net.py --dataset pascal_voc --net vgg16 --epochs 30 --bs 8 --nw 4 --lr 1e-3 --lr_decay_step 6 --use_tfb --mGPUs --cuda
结果是在models,如果你要重新训练的话
记得删除缓存与logs,缓存在data下面
test_net.py --dataset pascal_voc --net res101 --checksession 1 --checkepoch 30 --checkpoint 610 --cuda
–checksession 1 --checkepoch 30 --checkpoint 610 怎么选?
到model/res101/pascal_voc
下面看例如faster_rcnn_1_30_610.pth
懂了吧
他不会生成对应的文件png,所以留意输出的
我当时私信B站博主的时候~他说自己去搜下
然后我根据自己的理解写个对应的版本
from nets.ssd import SSD300
import torchvision.models as models
from ptflops import get_model_complexity_info
def get_classes(classes_path): # 去取name和数量
with open(classes_path, encoding='utf-8') as f:
class_names = f.readlines()
class_names = [c.strip() for c in class_names]
return class_names, len(class_names)
if __name__ == "__main__":
classes_path = 'model_data/voc_classes.txt'
class_names, num_classes = get_classes(classes_path)
# 对应的模型创建
myNet = SSD300(num_classes + 1, 'vgg')
# 根据层数 以及跑的图片大小进行设置
flops, params = get_model_complexity_info(myNet, (3,416,416), as_strings=True, print_per_layer_stat=True)
print("Flops: {}".format(flops))
print("Params: " + params)
from nets.yolo import YoloBody
from ptflops import get_model_complexity_info
def get_classes(classes_path): # 去取name和数量
with open(classes_path, encoding='utf-8') as f:
class_names = f.readlines()
class_names = [c.strip() for c in class_names]
return class_names, len(class_names)
if __name__ == "__main__":
classes_path = 'model_data/voc_clothes_classes.txt'
class_names, num_classes = get_classes(classes_path)
# 对应的模型创建
anchors_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
myNet = YoloBody(anchors_mask, num_classes)
# 根据层数 以及跑的图片大小进行设置
flops, params = get_model_complexity_info(myNet, (3,416,416), as_strings=True, print_per_layer_stat=True)
print("Flops: {}".format(flops))
print("Params: " + params)
from nets.frcnn import FasterRCNN
import torchvision.models as models
from ptflops import get_model_complexity_info
def get_classes(classes_path):
with open(classes_path, encoding='utf-8') as f:
class_names = f.readlines()
class_names = [c.strip() for c in class_names]
return class_names, len(class_names)
if __name__ == "__main__":
# print("Load model.")
# ssd = SSD(confidence = 0.01, nms_iou = 0.5)
# print("Load model done.")
# net = models.vgg16() #可以为自己搭建的模型
classes_path = 'model_data/voc_classes.txt'
class_names, num_classes = get_classes(classes_path)
myNet = FasterRCNN(num_classes, "predict", anchor_scales = [8, 16, 32], backbone = 'vgg')
flops, params = get_model_complexity_info(myNet, (3,416,416), as_strings=True, print_per_layer_stat=True)
print("Flops: {}".format(flops))
print("Params: " + params)
经验多去搜搜,多去尝试!
会Screen命令以及 watch -n 0.5 nvidia-smi 命名抢显卡
首先你先把觉得肯定不行的删除,避免浪费时间⌚️
def main(model_path):
####
if __name__ == "__main__":
Path = os.path.abspath(os.path.abspath(os.path.dirname(__file__)))
logs_path = Path + '/logs'
files= os.listdir(logs_path)
want = []
for each in files:
if '.pth' in each:
want.append(each)
# want = ['ep144-loss2.422-val_loss3.563.pth'] # 单独测试
for each in want:
path = 'logs/' + each
with open(Path + '/log.txt', "a+", encoding='utf-8') as f:
f.write(each + '*****')
main(path)
#---------------------------------------------------#
# 初始化YOLO
#---------------------------------------------------#
def __init__(self, model_path, **kwargs): # 手动添加接受 model_path
self.__dict__.update(self._defaults)
for name, value in kwargs.items():
setattr(self, name, value)
#---------------------------------------------------#
# 获得种类和先验框的数量
#---------------------------------------------------#
self.class_names, self.num_classes = get_classes(self.classes_path)
self.anchors, self.num_anchors = get_anchors(self.anchors_path)
self.bbox_util = DecodeBox(self.anchors, self.num_classes, (self.input_shape[0], self.input_shape[1]), self.anchors_mask)
#---------------------------------------------------#
# 画框设置不同的颜色
#---------------------------------------------------#
hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)]
self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors))
self.generate(model_path) # 传入model_path
#---------------------------------------------------#
# 生成模型
#---------------------------------------------------#
def generate(self, model_path):
#---------------------------------------------------#
# 建立yolo模型,载入yolo模型的权重
#---------------------------------------------------#
self.net = YoloBody(self.anchors_mask, self.num_classes)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
self.net.load_state_dict(torch.load(model_path, map_location=device)) # 不要self
self.net = self.net.eval()
print('{} model, anchors, and classes loaded.'.format(model_path)) # 不要self
if self.cuda:
self.net = nn.DataParallel(self.net)
self.net = self.net.cuda()
if show_animation:
cv2.destroyAllWindows()
results_file.write("\n# mAP of all classes\n")
mAP = sum_AP / n_classes
text = "mAP = {0:.2f}%".format(mAP*100)
# 写入
PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
with open(PATH + '/log.txt', "a+", encoding='utf-8') as f:
f.write(text + '\n')
results_file.write(text + "\n")
print(text)
就是因为数据集量太少了!加上随机分配的话
你训练的变成了你测试的,当然高了
修改voc_annotation.py 从63行开始
for xml in temp_xml:
if xml.endswith(".xml"):
total_xml.append(xml)
total_xml.sort()
num = len(total_xml)
list = range(num)
tv = int(num*trainval_percent)
tr = int(tv*train_percent)
# trainval= random.sample(list,tv)
trainval= list[:tv]
# train = random.sample(trainval,tr)
train = trainval[:tr]
我开始去补基础了。累~
实践再来学习,我觉得就没那么抽象了吧~
暂时总结到这里了!