Yolov-1-TX2上用YOLOv3训练自己数据集的流程(VOC2007-TX2-GPU)
Yolov--2--一文全面了解深度学习性能优化加速引擎---TensorRT
Yolov--3--TensorRT中yolov3性能优化加速(基于caffe)
yolov-5-目标检测:YOLOv2算法原理详解
yolov--8--Tensorflow实现YOLO v3
yolov--9--YOLO v3的剪枝优化
yolov--10--目标检测模型的参数评估指标详解、概念解析
yolov--11--YOLO v3的原版训练记录、mAP、AP、recall、precision、time等评价指标计算
yolov--12--YOLOv3的原理深度剖析和关键点讲解
mobilenetv2改进测试记录
什么是TF-Slim?
TF-slim是用于定义,训练和评估复杂模型的TensorFlow(tensorflow.contrib.slim)的新型轻量级高级API。 可以把它理解为TensorFlow提供的一种更高级的封装吧,其实它和迁移学习没什么关系,只是在后面的内容中会用到,所以在这里提一下。
TF-Slim实现迁移学习的流程
在TensorFlow的github网址中提供了一个包含了数据准备+训练+预测的例程—Flowers,它只需我们运行几个脚本或命令行,不需要该任何代码就可以,我们先把这个例程解释一下: 1.准备工作: 首先我们需要再https://github.com/tensorflow/models/blob/master/research/slim/README.md#training-a-model-from-scratch把 tensorflow models下载下来,放在本地一个位置上,比如D盘根目录。
tensorflow models
在tensorflow models中有官方维护和非官方维护的models,official models就是官方维护的models,里面使用的接口都是一些官方的接口,比如tf.layers.conv2d之类。而research models是tensorflow的研究人员自己实现的一些流行网络,不受官方支持,里面会用到一些slim之类的非官方接口。但是因为research models实现的网络非常多,而且提供了完整的训练和评价方案,所以我们现在基于research models中的实现来部署网络。
环境配置
首先要保证tf.contrib.slim在你的tensorflow环境中是存在的,运行下面的脚本保证没有错误发生。
python -c "import tensorflow.contrib.slim as slim; eval = slim.evaluation.evaluate_once"
slim代码准备
TF的库里面没有TF-slim的内容,所以我们需要将代码clone到本地
cd $HOME/workspace
git clone https://github.com/tensorflow/models/
运行以下脚本确定是否可用
cd $HOME/workspace/models/research/slim
python -c "from nets import cifarnet; mynet = cifarnet.cifarnet"
其实我们只需要使用research中的slim的代码,所以我是直接拷贝了slim的代码到本地,基于slim代码进行修改。
训练flower数据集
下载数据并创建tfrecord
官网提供了下载并且转换数据集的方法,运行如下脚本即可,脚本会直接下载flower数据集并且存储为TFRecord的格式。
python download_and_convert_data.py \
--dataset_name=flowers \
--dataset_dir=./tmp/data/flowers
为何官网要使用TFRecord呢?因为TFRecord和tensorflow内部有一个加速机制。实际读取tfrecord数据时,先以相应的tfrecord文件为参数,创建一个输入队列,这个队列有一定的容量,在一部分数据出队列时,tfrecord中的其他数据就可以通过预取进入队列,这个过程和网络的计算是独立进行的。也就是说,网络每一个iteration的训练不必等待数据队列准备好再开始,队列中的数据始终是充足的,而往队列中填充数据时,也可以使用多线程加速。
下载pre-trained checkpoint
每个网络对应的checkpoint可以从官网上找到,官网也提供了下载inception v3的checkpoint的例子
$ mkdir ./tmp/checkpoints
$ wget http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz
$ tar -xvf inception_v3_2016_08_28.tar.gz
$ mv inception_v3.ckpt ./tmp/checkpoints
$ rm inception_v3_2016_08_28.tar.gz
从头开始训练
slim框架训练模型 下载slim 和inception v4模型
https://github.com/tensorflow/models/tree/master/research/slim
将slim下载后拷贝到project目录下,然后进行以下准备工作。
1.将图片放置到指定的目录下:
图片需要按照文件夹进行分类,文件夹名就是分类的名称,具体可以参考下图:
这里我将分类数据集放到images目录下,images是在slim目录下新建的文件夹。
2.运行代码,转换格式
新建transform.py,如下
#导入相应的模块
import tensorflow as tf
import os
import random
import math
import sys
#划分验证集训练集
_NUM_TEST = 500
#random seed
_RANDOM_SEED = 0
#数据块
_NUM_SHARDS = 2
#数据集路径
DATASET_DIR = 'E:/SVN/Gavin/Learn/Python/pygame/slim/images/'
#标签文件
LABELS_FILENAME = 'E:/SVN/Gavin/Learn/Python/pygame/slim/images/labels.txt'
#定义tfrecord 的路径和名称
def _get_dataset_filename(dataset_dir,split_name,shard_id):
output_filename = 'image_%s_%05d-of-%05d.tfrecord' % (split_name,shard_id,_NUM_SHARDS)
return os.path.join(dataset_dir,output_filename)
#判断tfrecord文件是否存在
def _dataset_exists(dataset_dir):
for split_name in ['train','test']:
for shard_id in range(_NUM_SHARDS):
#定义tfrecord的路径名字
output_filename = _get_dataset_filename(dataset_dir,split_name,shard_id)
if not tf.gfile.Exists(output_filename):
return False
return True
#获取图片以及分类
def _get_filenames_and_classes(dataset_dir):
#数据目录
directories = []
#分类名称
class_names = []
for filename in os.listdir(dataset_dir):
#合并文件路径
path = os.path.join(dataset_dir,filename)
#判断路径是否是目录
if os.path.isdir(path):
#加入数据目录
directories.append(path)
#加入类别名称
class_names.append(filename)
photo_filenames = []
#循环分类的文件夹
for directory in directories:
for filename in os.listdir(directory):
path = os.path.join(directory,filename)
#将图片加入图片列表中
photo_filenames.append(path)
#返回结果
return photo_filenames ,class_names
def int64_feature(values):
if not isinstance(values,(tuple,list)):
values = [values]
return tf.train.Feature(int64_list=tf.train.Int64List(value=values))
def bytes_feature(values):
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[values]))
#图片转换城tfexample函数
def image_to_tfexample(image_data,image_format,class_id):
return tf.train.Example(features=tf.train.Features(feature={
'image/encoded': bytes_feature(image_data),
'image/format': bytes_feature(image_format),
'image/class/label': int64_feature(class_id)
}))
def write_label_file(labels_to_class_names,dataset_dir,filename=LABELS_FILENAME):
label_filename = os.path.join(dataset_dir,filename)
with tf.gfile.Open(label_filename,'w') as f:
for label in labels_to_class_names:
class_name = labels_to_class_names[label]
f.write('%d:%s\n' % (label, class_name))
#数据转换城tfrecorad格式
def _convert_dataset(split_name,filenames,class_names_to_ids,dataset_dir):
assert split_name in ['train','test']
#计算每个数据块的大小
num_per_shard = int(len(filenames) / _NUM_SHARDS)
with tf.Graph().as_default():
with tf.Session() as sess:
for shard_id in range(_NUM_SHARDS):
#定义tfrecord的路径名字
output_filename = _get_dataset_filename(dataset_dir,split_name,shard_id)
with tf.python_io.TFRecordWriter(output_filename) as tfrecord_writer:
#每个数据块开始的位置
start_ndx = shard_id * num_per_shard
#每个数据块结束的位置
end_ndx = min((shard_id+1) * num_per_shard,len(filenames))
for i in range(start_ndx,end_ndx):
try:
sys.stdout.write('\r>> Converting image %d/%d shard %d '% (i+1,len(filenames),shard_id))
sys.stdout.flush()
#读取图片
image_data = tf.gfile.FastGFile(filenames[i],'rb').read()
#获取图片的类别名称
#basename获取图片路径最后一个字符串
#dirname是除了basename之外的前面的字符串路径
class_name = os.path.basename(os.path.dirname(filenames[i]))
#获取图片的id
class_id = class_names_to_ids[class_name]
#生成tfrecord文件
example = image_to_tfexample(image_data,b'jpg',class_id)
#写入数据
tfrecord_writer.write(example.SerializeToString())
except IOError as e:
print ('could not read:',filenames[1])
print ('error:' , e)
print ('skip it \n')
sys.stdout.write('\n')
sys.stdout.flush()
if __name__ == '__main__':
#判断tfrecord文件是否存在
if _dataset_exists(DATASET_DIR):
print ('tfrecord exists')
else:
#获取图片以及分类
photo_filenames,class_names = _get_filenames_and_classes(DATASET_DIR)
#将分类的list转换成dictionary{‘animal':0,'flowers:1'}
class_names_to_ids = dict(zip(class_names,range(len(class_names))))
#切分数据为测试训练集
random.seed(_RANDOM_SEED)
random.shuffle(photo_filenames)
training_filenames = photo_filenames[_NUM_TEST:]
testing_filenames = photo_filenames[:_NUM_TEST]
#数据转换
_convert_dataset('train',training_filenames,class_names_to_ids,DATASET_DIR)
_convert_dataset('test',testing_filenames,class_names_to_ids,DATASET_DIR)
#输出lables文件
#与前面的 class_names_to_ids中的元素位置相反{0:'animal',1:'flowers'}
labels_to_class_names = dict(zip(range(len(class_names)),class_names))
write_label_file(labels_to_class_names,DATASET_DIR)
完成后,生成了以下文件,包括训练集数据块和测试集数据块,另外有个label标签文件
验证模型 接下来就是本文的重点了,上一步完成后我们已经得到tfrecord格式的文件了。下一步我们就要用这些文件进行分类验证测试。
1.在slim/datasets文件夹下,找到训练的数据集,由于写法比较 一致,我们只需要拷贝其中一个,比如训练flowers用的数据源,新建一个我们自己训练使用的数据集,命名为myimages.py(可自由命名),将代码拷贝到新文件中,最后修改。
代码如下,需要修改的几个地方我作了中文标记。代码实现的其实就是读取tfrecord文件。
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Provides data for the flowers dataset.
The dataset scripts used to create the dataset can be found at:
tensorflow/models/research/slim/datasets/download_and_convert_flowers.py
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import tensorflow as tf
from datasets import dataset_utils
slim = tf.contrib.slim
_FILE_PATTERN = 'image_%s_*.tfrecord' # 这里修改pattern,格式和生成tfrecord文件下的格式一致
SPLITS_TO_SIZES = {'train': 1026, 'validation': 50} #修改训练集和验证集图片数量
_NUM_CLASSES = 2 # 这里修改数据块个数
_ITEMS_TO_DESCRIPTIONS = {
'image': 'A color image of varying size.',
'label': 'A single integer between 0 and 4',
}
def get_split(split_name, dataset_dir, file_pattern=None, reader=None):
"""Gets a dataset tuple with instructions for reading flowers.
Args:
split_name: A train/validation split name.
dataset_dir: The base directory of the dataset sources.
file_pattern: The file pattern to use when matching the dataset sources.
It is assumed that the pattern contains a '%s' string so that the split
name can be inserted.
reader: The TensorFlow reader type.
Returns:
A `Dataset` namedtuple.
Raises:
ValueError: if `split_name` is not a valid train/validation split.
"""
if split_name not in SPLITS_TO_SIZES:
raise ValueError('split name %s was not recognized.' % split_name)
if not file_pattern:
file_pattern = _FILE_PATTERN
file_pattern = os.path.join(dataset_dir, file_pattern % split_name)
# Allowing None in the signature so that dataset_factory can use the default.
if reader is None:
reader = tf.TFRecordReader
keys_to_features = {
'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
'image/format': tf.FixedLenFeature((), tf.string, default_value='png'),
'image/class/label': tf.FixedLenFeature(
[], tf.int64, default_value=tf.zeros([], dtype=tf.int64)),
}
items_to_handlers = {
'image': slim.tfexample_decoder.Image(),
'label': slim.tfexample_decoder.Tensor('image/class/label'),
}
decoder = slim.tfexample_decoder.TFExampleDecoder(
keys_to_features, items_to_handlers)
labels_to_names = None
if dataset_utils.has_labels(dataset_dir):
labels_to_names = dataset_utils.read_label_file(dataset_dir)
return slim.dataset.Dataset(
data_sources=file_pattern,
reader=reader,
decoder=decoder,
num_samples=SPLITS_TO_SIZES[split_name],
items_to_descriptions=_ITEMS_TO_DESCRIPTIONS,
num_classes=_NUM_CLASSES,
labels_to_names=labels_to_names)
注意,我们还需要修改 dataset_factory.py文件下的datasets_map字典,如下
datasets_map = {
'cifar10': cifar10,
'flowers': flowers,
'imagenet': imagenet,
'mnist': mnist,
'myimages': myimages, # 这里新增个人训练的数据集
}
其中,'cifar10','flowers','imagenet','mnist'是官方提供的数据集,而‘myimages’就是我们刚刚新建的文件,新增到datasets_map。
3.在slim文件夹下新建一个model文件夹,用于保存训练生成的模型
4.如果标签是中文,修改slim/datasets/dataset_utils.py
sys.setdefaultencoding("utf-8") #中文标签,增加utf-8
5.在slim目录下编写执行训练数据的脚本。(在Linux中使用GPU)--重要
简单解释下:
train_dir 训练生成的模型存放位置 dataset_split_name=train 代表使用的是训练集,之前拆分为训练集和测试集 dataset_dir 训练图片存放位置 6.执行预测脚本,使用eval_image_classifier.py文件
如果使用的CPU,那么训练时间是比较漫长的。
采用原版2
1、生成训练数据集:
将数据集转化为TensorFlow训练格式的数据:
python create_dataset.py train
生成验证数据集:
python create_dataset.py val
改尺寸为416,运行此文件,生成新的pb文件 :
python freeze_graph.py
2、训练
CUDA_VISIBLE_DEVICES='2' python train.py | tee log/mobilenetv2-flower-class7-batch16-iter10000-2020-1-1-2020.log
CUDA_VISIBLE_DEVICES='2' python train.py
测试
CUDA_VISIBLE_DEVICES='3' python test.py
权衡超参数
如同我们之前在MobileNetV1中一样,我们可以通过设置宽度因子和分辨率因子这两个超参来实际调整我们的模型,使得可以在模型的准确率与性能之间做一个折衷。当我们把宽度因子从0.35调整到1.4,输入分辨率由96调整到224,模型的计算量增加了7倍达到了 585M 次MAdds,模型的参数量由 1.7M 变化到了 6.9M。
- ckpt文件是旧版本的输出saver.save(sess),相当于你的.ckpt-data(见下文)
- “checkpoint”文件仅用于告知某些TF函数,这是最新的检查点文件。
- .ckpt-meta 包含元图,即计算图的结构,没有变量的值(基本上你可以在tensorboard / graph中看到)。
- .ckpt-data包含所有变量的值,没有结构。要在python中恢复模型,您通常会使用元数据和数据文件(但您也可以使用该.pb文件): saver = tf.train.import_meta_graph(path_to_ckpt_meta) saver.restore(sess, path_to_ckpt_data)
- 我不确切地知道.ckpt-index,我想这是内部需要的某种索引来正确映射前两个文件。无论如何,它通常不是必需的,你可以只用.ckpt-meta和恢复一个模型.ckpt-data。
- 该.pb文件可以保存您的整个图表(元+数据)。要在c ++中加载和使用(但不训练)图形,您通常会使用它来创建freeze_graph,它会.pb从元数据和数据创建文件。要小心,(至少在以前的TF版本和某些人中)py提供的功能freeze_graph不能正常工作,所以你必须使用脚本版本。Tensorflow还提供了一种tf.train.Saver.to_proto()方法,但我不知道它究竟是做什么的。
tensorflow自作tfrecord数据集发生的错误
tensorflow.python.framework.errors_impl.OutOfRangeError: RandomShuffleQueue '_0_shuffle_batch/random_shuffle_queue' is closed and has insufficient elements (requested 25, current size 0) 关键:(requested 25, current size 0)
发现当前返回的size为0,证明制作的数据集没有被简析出来,:
原因:
1. 数据集本身的问题,例如数据格式不同意,常见的是通道数不同,数据集中既有彩色图,也有灰度图;
2. 数据在转换与解析中格式没有对应 tfrecord需要将数据转换为tostring,那么简析的时候同样需要如此