from datasets import dataset_to_tfrecords
if __name__ == '__main__':
dataset_to_tfrecords.run("./IMAGE/commodity/", "./IMAGE/tfrecords/commodity_tfrecords/", "commodity_2018_train")
import os
import tensorflow as tf
import xml.etree.ElementTree as ET
from datasets.utils.dataset_utils import int64_feature, float_feature, bytes_feature
from datasets.dataset_config import DIRECTORY_ANNOTATIONS, DIRECTORY_IMAGES, SAMPLES_PER_FILES, VOC_LABELS
def _process_image(dataset_dir, img_name):
"""
读取图片内容以及XML文件内容
:param dataset_dir: 文件目录
:param img_name: 文件名字 编号
:return:
"""
# 1、读取图片
# 构造图片路径+要读取的文件名
filename = dataset_dir + DIRECTORY_IMAGES + img_name + '.jpg'
# 读取图片数据
image_data = tf.gfile.FastGFile(filename, 'rb').read()
# 2、读取XML
# 需要使用ET工具读取
# 构造XML文件名字
filename_xml = dataset_dir + DIRECTORY_ANNOTATIONS + img_name + '.xml'
# 将文件内容变成一个树状结构
tree = ET.parse(filename_xml)
root = tree.getroot()
# 获取root下的一些子节点
# (1)获取size 信息
size = root.find('size')
# 把三个宽、高、通道数存在一个shape里
shape = [int(size.find('height').text),
int(size.find('width').text),
int(size.find('depth').text)]
# (2)获取object信息
# 每一个obj都含有,name, truncated, difficult, bndbox[xmin, ymin, xmax, ymax]
labels = []
labels_text = []
difficult = []
truncated = []
bboxes = []
for obj in root.findall('object'):
# 解析每一个obj
# 取出目标label,具体的物体类别
# 动物:猫、狗、等等 数字与之一一对应
label = obj.find('name').text
# 取出与之对应的物体大类别
labels.append(int(VOC_LABELS[label][0]))
labels_text.append(label.encode('ascii'))
# 取出diffcult
if obj.find('difficult'):
difficult.append(int(obj.find('difficult').text))
else:
difficult.append(0)
# 取出truncated
if obj.find('truncated'):
truncated.append(int(obj.find('truncated').text))
else:
truncated.append(0)
# 取出每一个对象四个位置,存储顺序按照ymin, xmin, ymax, xmax存储
bbox = obj.find('bndbox')
bboxes.append((float(bbox.find('ymin').text) / shape[0],
float(bbox.find('xmin').text) / shape[1],
float(bbox.find('ymax').text) / shape[0],
float(bbox.find('xmax').text) / shape[1]))
return image_data, shape, labels, difficult, truncated, bboxes, labels_text
def _convert_to_example(image_data, shape, labels, difficult, truncated, bboxes, labels_text):
""" 将一张图片数据转使用example转换成protobuffer格式
"""
# 为了转换需求,对bboxes进行格式调整,从一个obj的四个属性列表,编程四个位置单独的列表
# [ [23,46,234,13], [34,564,2,465] ] ----> ymin [23, 34] xmin [46, 564],ymax [234,2] xmax[12,465]
ymin = []
xmin = []
ymax = []
xmax = []
for b in bboxes:
ymin.append(b[0])
xmin.append(b[1])
ymax.append(b[2])
xmax.append(b[3])
# 将所有信息封装成exmaple
# 每一个存入的属性都要指定类型
image_format = b'JPEG'
example = tf.train.Example(features=tf.train.Features(feature={
'image/height': int64_feature(shape[0]),
'image/width': int64_feature(shape[1]),
'image/channels': int64_feature(shape[2]),
'image/shape': int64_feature(shape),
'image/object/bbox/xmin': float_feature(xmin),
'image/object/bbox/xmax': float_feature(xmax),
'image/object/bbox/ymin': float_feature(ymin),
'image/object/bbox/ymax': float_feature(ymax),
'image/object/bbox/label': int64_feature(labels),
'image/object/bbox/label_text': bytes_feature(labels_text),
'image/object/bbox/difficult': int64_feature(difficult),
'image/object/bbox/truncated': int64_feature(truncated),
'image/format': bytes_feature(image_format),
'image/encoded': bytes_feature(image_data)}))
return example
def _add_to_tfrecord(dataset_dir, img_name, tfrecord_writer):
"""添加一个图片文件内容以及XML内容写入到文件当中
:param outputdir: 输出文件目录
:param img_name: 图片编号
:param tfrecord_writer: 文件写入实例
:return:
"""
# 1、读取每张图片的内容,以及每张图片对应的XML内容
image_data, shape, labels, difficult, truncated, bboxes, labels_text = _process_image(dataset_dir, img_name)
# print(image_data)
# 2、将每一张图片封装成一个example
example = _convert_to_example(image_data, shape, labels, difficult, truncated, bboxes, labels_text)
# 3、用tfrecord_writer写入example的序列化结果
tfrecord_writer.write(example.SerializeToString())
return None
def _get_output_filename(outputdir, name, fdx):
"""获取输出的文件名字
:param outputdir: 输出路径
:param name: 数据集名字,参考 VOC_2007_test or VOC_2007_train
:param fdx: 0 ~
:return:
"""
return "%s/%s_%03d.tfrecord" % (outputdir, name, fdx)
def run(dataset_dir, outputdir, name="data"):
"""
运行转换代码逻辑,存入多个tfrecord文件,每个文件固定N个样本
:param dataset_dir: 数据集的目录, 里面存在annotations , Jpegimages
:param outputdir: TFRecords存储目录
:param name: 数据集名字,指定名字以及train or test
:return:
"""
# 1、判断数据集目录是否存在,创建一个目录
if tf.gfile.Exists(dataset_dir):
tf.gfile.MakeDirs(dataset_dir)
# 2、去读取某个文件夹下的所有文件名字列表
path = os.path.join(dataset_dir, DIRECTORY_ANNOTATIONS)
# 读取所有文件时,会打乱顺序,
filenames = sorted(os.listdir(path))
# 3、循环这个名字列表
# 每200个图片以及XML信息就存储到一个tfrecord文件当中
i = 0
fdx = 0
while i < len(filenames):
# 1、创建一个TFRecord文件,有第几个文件的序号
tf_filename = _get_output_filename(outputdir, name, fdx)
# 2、循环标记每隔200个图片内容,存储一次
# 新建一个TFRecord文件的存储器
with tf.python_io.TFRecordWriter(tf_filename) as tfrecord_writer:
j = 0
while i < len(filenames) and j < SAMPLES_PER_FILES:
print("转换图片进度 %d/%d " % (i+1, len(filenames)))
# 取出图片的名字以及XML的名字
# single_filename *.xml
single_filename = filenames[i]
img_name = single_filename[:-4]
# 读取图片内容以及XML文件内容,存入文件
# 默认每次构造一个图片文件存储指定文件
# 每个图片构造一个example存储到tf_filename当中
_add_to_tfrecord(dataset_dir, img_name, tfrecord_writer)
i += 1
j += 1
# 存储文件的序号也要进行增加
fdx += 1
print("完成所有图片数据集 %s 的转换" % name)
from collections import namedtuple
"""
数据集格式转换配置
"""
# 指定原始图片的XML和图片的文件夹名字
DIRECTORY_ANNOTATIONS = "Annotations/"
DIRECTORY_IMAGES = "JPEGImages/"
# 指定每个TFRecord文件存储图片数量
SAMPLES_PER_FILES = 200
# 商品数据集的类别
VOC_LABELS = {
'none': (0, 'Background'),
'clothes': (1, 'clothes'),
'pants': (2, 'pants'),
'shoes': (3, 'shoes'),
'watch': (4, 'watch'),
'phone': (5, 'phone'),
'audio': (6, 'audio'),
'computer': (7, 'computer'),
'books': (8, 'books'),
}
# VOC 2007物体类别
# VOC_LABELS = {
# 'none': (0, 'Background'),
# 'aeroplane': (1, 'Vehicle'),
# 'bicycle': (2, 'Vehicle'),
# 'bird': (3, 'Animal'),
# 'boat': (4, 'Vehicle'),
# 'bottle': (5, 'Indoor'),
# 'bus': (6, 'Vehicle'),
# 'car': (7, 'Vehicle'),
# 'cat': (8, 'Animal'),
# 'chair': (9, 'Indoor'),
# 'cow': (10, 'Animal'),
# 'diningtable': (11, 'Indoor'),
# 'dog': (12, 'Animal'),
# 'horse': (13, 'Animal'),
# 'motorbike': (14, 'Vehicle'),
# 'person': (15, 'Person'),
# 'pottedplant': (16, 'Indoor'),
# 'sheep': (17, 'Animal'),
# 'sofa': (18, 'Indoor'),
# 'train': (19, 'Vehicle'),
# 'tvmonitor': (20, 'Indoor'),
# }
"""
数据集读取的配置
"""
# 创建命名字典
DataSetParams = namedtuple('DataSetParameters', ['FILE_PATTERN',
'NUM_CLASSES',
'SPLITS_TO_SIZES',
'ITEMS_TO_DESCRIPTIONS'
])
# 定义commodity_2018数据属性配置
Cm2018 = DataSetParams(
FILE_PATTERN='commodity_2018_%s_*.tfrecord',
NUM_CLASSES=8,
SPLITS_TO_SIZES={
'train': 88,
'test': 0
},
ITEMS_TO_DESCRIPTIONS={
'image': '图片数据',
'shape': '图篇形状',
'object/bbox': '若干物体对象的bbox框组成的列表',
'object/label': '若干物体的编号'
}
)
import tensorflow as tf
class TFRecordsReaderBase(object):
"""数据集基类
"""
def __init__(self, param):
# param给不同数据集的属性配置
self.param = param
def get_data(self, train_or_test, dataset_dir):
"""
获取数据规范
:param train_or_test: train or test数据文件
:param dataset_dir: 数据集目录
:return:
"""
return None
def int64_feature(value):
"""包裹int64型特征到Example
"""
if not isinstance(value, list):
value = [value]
return tf.train.Feature(int64_list=tf.train.Int64List(value=value))
def float_feature(value):
"""包裹浮点型特征到Example
"""
if not isinstance(value, list):
value = [value]
return tf.train.Feature(float_list=tf.train.FloatList(value=value))
def bytes_feature(value):
"""包裹字节类型特征到Example
"""
if not isinstance(value, list):
value = [value]
return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))
/home/yuyang/anaconda3/envs/tensor1-6/bin/python3.5 /media/yuyang/Yinux/24期python就业班课件/物体检测项目/物体检测项目(三)--项目实现与部署/物体检测项目(三)--项目实现与部署/代码/online_class_V2.0/online_class_V2.0/dataset_to_tfrecord.py
转换图片进度 1/88
......
转换图片进度 88/88
完成所有图片数据集 commodity_2018_train 的转换
Process finished with exit code 0
数据模块含有TFRecords的读取逻辑,需要对应到不同的数据集类型处理如(Pascalvoc和Commodity).类的设计如下:
完整代码在2.1.1节中的utils.py中.如下为构建基类代码:
class TFRecordsReaderBase(object):
"""数据集基类
"""
def __init__(self, param):
# param给不同数据集的属性配置
self.param = param
def get_data(self, train_or_test, dataset_dir):
"""
获取数据规范
:param train_or_test: train or test数据文件
:param dataset_dir: 数据集目录
:return:
"""
return None
建立一个基类,TFRecordsReaderBase类
PascalvocTFRecords和CommodityTFRecords继承数据读取基类,实现属性方法和细节.
import os
import tensorflow as tf
from datasets.utils import dataset_utils
slim = tf.contrib.slim
class CommodityTFRecords(dataset_utils.TFRecordsReaderBase):
"""商品数据集读取类
"""
def __init__(self, param):
self.param = param
def get_data(self, train_or_test, dataset_dir):
"""获取数据方法
"""
# 异常抛出
if train_or_test not in ['train', 'test']:
raise ValueError("训练/测试的名字 %s 指定错误" % train_or_test)
# 判断数据集目录
if not tf.gfile.Exists(dataset_dir):
raise ValueError("数据集目录不存在")
# 构造第一个参数:数据目录+文件名
file_pattern = os.path.join(dataset_dir, self.param.FILE_PATTERN % train_or_test)
# 准备第二个参数:
reader = tf.TFRecordReader
# 准备第三个参数:decoder
# 反序列化的格式
keys_to_features = {
'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
'image/format': tf.FixedLenFeature((), tf.string, default_value='jpeg'),
'image/height': tf.FixedLenFeature([1], tf.int64),
'image/width': tf.FixedLenFeature([1], tf.int64),
'image/channels': tf.FixedLenFeature([1], tf.int64),
'image/shape': tf.FixedLenFeature([3], tf.int64),
'image/object/bbox/xmin': tf.VarLenFeature(dtype=tf.float32),
'image/object/bbox/ymin': tf.VarLenFeature(dtype=tf.float32),
'image/object/bbox/xmax': tf.VarLenFeature(dtype=tf.float32),
'image/object/bbox/ymax': tf.VarLenFeature(dtype=tf.float32),
'image/object/bbox/label': tf.VarLenFeature(dtype=tf.int64),
'image/object/bbox/difficult': tf.VarLenFeature(dtype=tf.int64),
'image/object/bbox/truncated': tf.VarLenFeature(dtype=tf.int64),
}
# 2、反序列化成高级的格式
# 其中bbox框ymin [23] xmin [46],ymax [234] xmax[12]--->[23,46,234,13]
items_to_handlers = {
'image': slim.tfexample_decoder.Image('image/encoded', 'image/format'),
'shape': slim.tfexample_decoder.Tensor('image/shape'),
'object/bbox': slim.tfexample_decoder.BoundingBox(
['ymin', 'xmin', 'ymax', 'xmax'], 'image/object/bbox/'),
'object/label': slim.tfexample_decoder.Tensor('image/object/bbox/label'),
'object/difficult': slim.tfexample_decoder.Tensor('image/object/bbox/difficult'),
'object/truncated': slim.tfexample_decoder.Tensor('image/object/bbox/truncated'),
}
# 构造decoder
decoder = slim.tfexample_decoder.TFExampleDecoder(keys_to_features, items_to_handlers)
return slim.dataset.Dataset(
data_sources=file_pattern,
reader=reader,
decoder=decoder,
num_samples=self.param.SPLITS_TO_SIZES[train_or_test],
items_to_descriptions=self.param.ITEMS_TO_DESCRIPTIONS,
num_classes=self.param.NUM_CLASSES)
import os
import tensorflow as tf
slim = tf.contrib.slim
def get_dataset(dataset_dir):
"""
获取pascalvoc2007数据集
:param dataset_dir: 数据集目录
:return: Dataset
"""
# 构造第一个参数:数据目录+文件名
file_pattern = os.path.join(dataset_dir, "VOC_2007_test_*.tfrecord")
# 准备第二个参数:
reader = tf.TFRecordReader
# 准备第三个参数:decoder
# 反序列化的格式
keys_to_features = {
'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
'image/format': tf.FixedLenFeature((), tf.string, default_value='jpeg'),
'image/height': tf.FixedLenFeature([1], tf.int64),
'image/width': tf.FixedLenFeature([1], tf.int64),
'image/channels': tf.FixedLenFeature([1], tf.int64),
'image/shape': tf.FixedLenFeature([3], tf.int64),
'image/object/bbox/xmin': tf.VarLenFeature(dtype=tf.float32),
'image/object/bbox/ymin': tf.VarLenFeature(dtype=tf.float32),
'image/object/bbox/xmax': tf.VarLenFeature(dtype=tf.float32),
'image/object/bbox/ymax': tf.VarLenFeature(dtype=tf.float32),
'image/object/bbox/label': tf.VarLenFeature(dtype=tf.int64),
'image/object/bbox/difficult': tf.VarLenFeature(dtype=tf.int64),
'image/object/bbox/truncated': tf.VarLenFeature(dtype=tf.int64),
}
# 2、反序列化成高级的格式
# 其中bbox框ymin [23] xmin [46],ymax [234] xmax[12]--->[23,46,234,13]
items_to_handlers = {
'image': slim.tfexample_decoder.Image('image/encoded', 'image/format'),
'shape': slim.tfexample_decoder.Tensor('image/shape'),
'object/bbox': slim.tfexample_decoder.BoundingBox(
['ymin', 'xmin', 'ymax', 'xmax'], 'image/object/bbox/'),
'object/label': slim.tfexample_decoder.Tensor('image/object/bbox/label'),
'object/difficult': slim.tfexample_decoder.Tensor('image/object/bbox/difficult'),
'object/truncated': slim.tfexample_decoder.Tensor('image/object/bbox/truncated'),
}
# 构造decoder
decoder = slim.tfexample_decoder.TFExampleDecoder(keys_to_features, items_to_handlers)
return slim.dataset.Dataset(
data_sources=file_pattern,
reader=reader,
decoder=decoder,
num_samples=4921,
items_to_descriptions={
'image': 'A color image of varying height and width.',
'shape': 'Shape of the image',
'object/bbox': 'A list of bounding boxes, one per each object.',
'object/label': 'A list of labels, one per each object.'
}, # 数据集返回的格式描述字典
num_classes=20)
数据集读取的配置
"""
# 创建命名字典
DataSetParams = namedtuple('DataSetParameters', ['FILE_PATTERN',
'NUM_CLASSES',
'SPLITS_TO_SIZES',
'ITEMS_TO_DESCRIPTIONS'
])
# 定义commodity_2018数据属性配置
Cm2018 = DataSetParams(
FILE_PATTERN='commodity_2018_%s_*.tfrecord',
NUM_CLASSES=8,
SPLITS_TO_SIZES={
'train': 88,
'test': 0
},
ITEMS_TO_DESCRIPTIONS={
'image': '图片数据',
'shape': '图篇形状',
'object/bbox': '若干物体对象的bbox框组成的列表',
'object/label': '若干物体的编号'
}
)
class TFRecordsReaderBase(object):
"""数据集基类
"""
def __init__(self, param):
# param给不同数据集的属性配置
self.param = param
def get_data(self, train_or_test, dataset_dir):
"""
获取数据规范
:param train_or_test: train or test数据文件
:param dataset_dir: 数据集目录
:return:
"""
return None
from datasets.dataset_init import commodity_2018
from datasets.dataset_config import Cm2018
# 定义dataset种类的字典
datasets_map = {
'commodity_2018': commodity_2018.CommodityTFRecords
}
# 逻辑
# 1、数据集名称
# 2、指定训练还是测试
# 3、数据集目录指定
def get_dataset(dataset_name, train_or_test, dataset_dir):
"""
获取指定数据名称的数据文件
:param dataset_name: 数据集名称(数据当中要存在)
:param train_or_test: train or test数据集
:param dataset_dir: 数据集目录
:return: Dataset 数据规范
"""
if dataset_name not in datasets_map:
raise ValueError("输入的数据集名称 %s 不存在" % dataset_name)
return datasets_map[dataset_name](Cm2018).get_data(train_or_test, dataset_dir)
from datasets import dataset_factory
import tensorflow as tf
slim = tf.contrib.slim
if __name__ == '__main__':
# 获取dataset
dataset = dataset_factory.get_dataset("commodity_2018", 'train', './IMAGE/tfrecords/commodity_tfrecords')
# 2、通过provider去取出数据
provider = slim.dataset_data_provider.DatasetDataProvider(
dataset,
num_readers=3
)
# 通过get方法获取指定名称的数据(是在准备规范数据dataset时高级格式的名称)
[image, shape, bbox, label] = provider.get(
['image', 'shape', 'object/bbox', 'object/label'])
print(image, shape, bbox, label)
读取结果:
/home/yuyang/anaconda3/envs/tensor1-6/bin/python3.5 /media/yuyang/Yinux/24期python就业班课件/物体检测项目/物体检测项目(三)--项目实现与部署/物体检测项目(三)--项目实现与部署/代码/online_class_V2.0/online_class_V2.0/tf_read_tfrecord.py
Tensor("case/cond/Merge:0", shape=(?, ?, 3), dtype=uint8) Tensor("Reshape_4:0", shape=(3,), dtype=int64) Tensor("transpose:0", shape=(?, 4), dtype=float32) Tensor("SparseToDense:0", shape=(?,), dtype=int64)
Process finished with exit code 0
在这里直接用Tensorflow官方封装过的接口,在程序当中做一些偶和操作.
首先在整个项目中添加一个nets文件夹,并在其中进行如下文件简建立:
from nets.nets_model import ssd_vgg_300
networks_obj = {
'ssd_vgg_300': ssd_vgg_300.SSDNet
}
# 1、训练网络名称
def get_network(network_name):
"""
获取不同的网络模型
:param network_name: 网络模型的名称
:return: 网络
"""
return networks_obj[network_name]
对图像进行平移,缩放,翻转,裁剪等操作.
from preprocessing.processing import ssd_vgg_preprocessing
preprocessing_fn_map = {
"ssd_vgg_300": ssd_vgg_preprocessing
}
# 定义函数:预处理逻辑名称,提供是否是训练的处理过程
def get_preprocessing(name, is_training=True):
"""预处理工厂获取不同的模型数据增强(预处理)方式
:param name: 模型预处理名称
:param is_training: 是否训练
:return: 返回预处理的函数
"""
if name not in preprocessing_fn_map:
raise ValueError("选择的预处理名称 %s 不在预处理模型库当中,请提供该模型预处理代码" % name)
# 返回一个处理的函数,后续再去调用这个函数
def preprocessing_fn(image, labels, bboxes,
out_shape, data_format='NHWC', **kwargs):
return preprocessing_fn_map[name].preprocess_image(image, labels, bboxes,
out_shape, data_format=data_format,
is_training=is_training, **kwargs)
return preprocessing_fn
在计算过程中,CPU与GPU之间进行分工.Tensorflow会通过标号来区分不同的GPU和CPU,如"/device:CPU:0","/device:GPU:0","/device:GPU:1","/device:GPU:2".
其中utils当中的train_tools.py为训练所用到的一些API函数,组件.训练要导入的模块以及参数.train_tools当中API将要用到的方法如下:
PRE_TRAINED_PATH=./ckpt/pre_trained/ssd_300_vgg.ckpt
TRAIN_MODEL_DIR=./ckpt/fine_tuning/
DATASET_DIR=./IMAGE/tfrecords/commodity_tfrecords/
# 1,slim.dataset_data_provider.DatasetDataProvider通过GET方法获取数据
# 2,对数据进行预处理
# 3,对获取出来的groundtruth标签和bbox。进行编码
# 4,获取的单个样本数据,要进行批处理以及返回队列
update_ops, first_clone_scope, clones = train_tools.deploy_loss_summary(deploy_config,
batch_queue,
ssd_net,
summaries,
batch_shape,
FLAGS)
"""训练初始确定事项
PRE_TRAINED_PATH=./ckpt/pre_trained/ssd_300_vgg.ckpt
TRAIN_MODEL_DIR=./ckpt/fine_tuning/
DATASET_DIR=./IMAGE/tfrecords/commodity_tfrecords/
python train_ssd_network.py --train_model_dir=${TRAIN_MODEL_DIR} --dataset_dir=${DATASET_DIR} --dataset_name="commodity_2018" --train_or_test=train --model_name=ssd_vgg_300 --pre_trained_path=${PRE_TRAINED_PATH} --weight_decay=0.0005 --optimizer=adam --learning_rate=0.001 --batch_size=2
训练学习率等值的确定:
每批次训练样本数:32或者更小
网络误差函数惩罚项值:0.0005
学习率:0.001
终止学习率:0.0001
优化器选择:adam优化器
模型名称:ssd_vgg_300
"""
import tensorflow as tf
from datasets import dataset_factory
from deployment import model_deploy
from nets import nets_factory
from preprocessing import preprocessing_factory
from utils import train_tools
slim = tf.contrib.slim
DATA_FORMAT = "NHWC"
# 设备的命令行参数配置
tf.app.flags.DEFINE_integer('num_clones', 1, "可用设备的GPU数量")
tf.app.flags.DEFINE_boolean('clone_on_cpu', False, "是否只在CPU上运行")
# 数据集相关命令行参数设置
tf.app.flags.DEFINE_string('dataset_dir', ' ', "训练数据集目录")
tf.app.flags.DEFINE_string('dataset_name', 'commodity_2018', '数据集名称参数')
tf.app.flags.DEFINE_string('train_or_test', 'train', '是否是训练集还是测试集')
# 网络相关配置
tf.app.flags.DEFINE_float(
'weight_decay', 0.00004, '网络误差函数惩罚项值,越小越防止过拟合.')
tf.app.flags.DEFINE_string(
'model_name', 'ssd_vgg_300', '用于训练的网络模型名称')
# 设备的命令行参数配置
tf.app.flags.DEFINE_integer('batch_size', 32, "每批次获取样本数")
# 训练学习率相关参数
tf.app.flags.DEFINE_string(
'optimizer', 'rmsprop', '优化器种类 可选"adadelta", "adagrad", "adam","ftrl", "momentum", "sgd" or "rmsprop".')
tf.app.flags.DEFINE_string(
'learning_rate_decay_type', 'exponential','学习率迭代种类 "fixed", "exponential", "polynomial"')
tf.app.flags.DEFINE_float(
'learning_rate', 0.01, '模型初始学习率')
tf.app.flags.DEFINE_float(
'end_learning_rate', 0.0001, '模型训练迭代后的终止学习率')
tf.app.flags.DEFINE_integer(
'max_number_of_steps', None, '训练的最大步数')
tf.app.flags.DEFINE_string(
'train_model_dir', ' ', '训练输出的模型目录')
# pre-trained模型路径.
tf.app.flags.DEFINE_string(
'pre_trained_model', None, '用于fine-tune的已训练好的基础参数文件位置')
FLAGS = tf.app.flags.FLAGS
def main(_):
if not FLAGS.dataset_dir:
raise ValueError('必须指定一个TFRecords的数据集目录')
# 设置打印级别
tf.logging.set_verbosity(tf.logging.DEBUG)
with tf.Graph().as_default():
# 在默认的图当中进行编写训练逻辑
# DeploymentConfig以及全部参数
# 配置计算机相关情况
deploy_config = model_deploy.DeploymentConfig(
num_clones=FLAGS.num_clones, # GPU设备数量
clone_on_cpu=FLAGS.clone_on_cpu, #是否只用CPU
replica_id=0,
num_replicas=1, # 配置1台计算机
num_ps_tasks=0 #默认使用第一台
)
# 定义一个全局步长参数(网络训练都会这么去进行配置)
# 使用指定设备 tf.device
with tf.device(deploy_config.variables_device()):
global_step = tf.train.create_global_step()
# 2、获取图片数据,做一些处理
# 图片有什么?image, shape, bbox, label
# image会做一些数据增强,大小变换
# 直接训练?需要将anchor bbox进行样本标记正负样本,目的使的GT目标样本的数量与default bboxes数量一致
# 将每个模块结果先获取
# (1) 通过数据工厂取出规范信息
dataset = dataset_factory.get_dataset(FLAGS.dataset_name, FLAGS.train_or_test, FLAGS.dataset_dir)
# (2)获取网络计算的anchors结果
ssd_class = nets_factory.get_network(FLAGS.model_name)
# 获取默认网络参数
ssd_params = ssd_class.default_params._replace(num_classes=9)
# 初始化网络init函数
ssd_net = ssd_class(ssd_params)
# 获取形状,用于输入到anchors函数参数当中
ssd_shape = ssd_net.params.img_shape
# 获取anchors, SSD网络当中6层的所有计算出来的默认候选框
ssd_anchors = ssd_net.anchors(ssd_shape)
# (3)获取预处理函数
image_preprocessing_fn = preprocessing_factory.get_preprocessing(
FLAGS.model_name, is_training=True
)
# 打印网络相关参数
train_tools.print_configuration(ssd_params, dataset.data_sources)
# 2.2
# 1,slim.dataset_data_provider.DatasetDataProvider通过GET方法获取数据
# 2,对数据进行预处理
# 3,对获取出来的groundtruth标签和bbox。进行编码
# 4,获取的单个样本数据,要进行批处理以及返回队列
with tf.device(deploy_config.inputs_device()):
# 给当前操作取一个作用域名称
with tf.name_scope(FLAGS.model_name + '_data_provider'):
# slim.dataset_data_provider.DatasetDataProvider通过GET方法获取数据
provider = slim.dataset_data_provider.DatasetDataProvider(
dataset,
num_readers=4,
common_queue_capacity=20 * FLAGS.batch_size,
common_queue_min=10 * FLAGS.batch_size,
shuffle=True
)
# 通过get获取数据
# 真正获取参数
[image, shape, glabels, gbboxes] = provider.get(['image', 'shape', 'object/label', 'object/bbox'])
# 直接进行数据预处理
# image [?, ?, 3]---->[300, 300, 3]
image, glabels, gbboxes = image_preprocessing_fn(image, glabels, gbboxes,
out_shape=ssd_shape,
data_format=DATA_FORMAT)
# 对原始anchor bboxes进行正负样本标记
# 得到目标值,编码之后,返回?
# 训练? 预测值类别,物体位置,物体类别概率,目标值
# 8732 anchor, 得到8732个与GT 对应的标记的anchor
# gclasses:目标类别
# glocalisations:目标类别的真是位置
# gscores:是否是正负样本
gclasses, glocalisations, gscores = ssd_net.bboxes_encode(glabels, gbboxes, ssd_anchors)
# print(gclasses, glocalisations)
# 特征值、目标
# 批处理以及队列处理
# tensor_list:tensor列表 [tensor, tensor, ]
# tf.train.batch(tensor_list, batch_size, num_threads, capacity)
# [Tensor, [6], [6], [6]] 嵌套的列表要转换成单列表形式
r = tf.train.batch(train_tools.reshape_list([image, gclasses, glocalisations, gscores]),
batch_size=FLAGS.batch_size,
num_threads=4,
capacity=5 * FLAGS.batch_size)
# r应该是一个19个Tensor组成的一个列表
# print(r)
# 批处理数据放入队列
# 1个r:批处理的样本, 5个设备,5个r, 5组32张图片
# 队列的目的是为了不同设备需求
batch_queue = slim.prefetch_queue.prefetch_queue(r,
capacity=deploy_config.num_clones)
# 3、赋值模型到不同的GPU设备,以及损失、变量的观察
# train_tools.deploy_loss_summary(deploy_config,batch_queue,ssd_net,summaries,batch_shape,FLAGS)
# summarties:摘要
summaries = set(tf.get_collection(tf.GraphKeys.SUMMARIES))
# batch_shape:解析这个batch_queue的大小,指的是获取的一个默认队列大小,指上面r的大小
batch_shape = [1] + 3 * [len(ssd_anchors)]
update_ops, first_clone_scope, clones = train_tools.deploy_loss_summary(deploy_config,
batch_queue,
ssd_net,
summaries,
batch_shape,
FLAGS)
# 4、定义学习率以及优化器
# 学习率:0.001
# 终止学习率:0.0001
# 优化器选择:adam优化器
# learning_rate = tf_utils.configure_learning_rate(FLAGS, num_samples, global_step)
# FLAGS:将会用到学习率设置相关参数
# global_step: 全局步数
# optimizer = tf_utils.configure_optimizer(FLAGS, learning_rate)
# learning_rate: 学习率
with tf.device(deploy_config.optimizer_device()):
# 定义学习率和优化器
learning_rate = train_tools.configure_learning_rate(FLAGS,
dataset.num_samples,
global_step)
optimizer = train_tools.configure_optimizer(FLAGS, learning_rate)
# 观察学习率的变化情况,添加到summaries
summaries.add(tf.summary.scalar('learning_rate', learning_rate))
# 5、计算所有设备的平均损失以及每个变量的梯度总和
train_op, summaries_op = train_tools.get_trainop(optimizer,
summaries,
clones,
global_step,
first_clone_scope, update_ops)
# 配置config以及saver
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.8) #占用GPU80%
config = tf.ConfigProto(log_device_placement=False, # 若果打印会有许多变量的设备信息出现
gpu_options=gpu_options)
# saver
saver = tf.train.Saver(max_to_keep=5, # 默认保留最近几个模型文件
keep_checkpoint_every_n_hours=1.0,
write_version=2,
pad_step_number=False)
# 6、进行训练
slim.learning.train(
train_op, # 训练优化器tensor
logdir=FLAGS.train_model_dir, # 模型存储目录
master='',
is_chief=True,
init_fn=train_tools.get_init_fn(FLAGS), # 初始化参数的逻辑,预训练模型的读取和微调模型判断
summary_op=summaries_op, # 摘要
number_of_steps=FLAGS.max_number_of_steps, # 最大步数
log_every_n_steps=10, # 打印频率
save_summaries_secs=60, # 保存摘要频率
saver=saver, # 保存模型参数
save_interval_secs=600, # 保存模型间隔
session_config=config, # 会话参数配置
sync_optimizer=None)
if __name__ == '__main__':
tf.app.run()
PRE_TRAINED_PATH=./ckpt/pre_trained/ssd_300_vgg.ckpt
TRAIN_MODEL_DIR=./ckpt/fine_tuning/
DATASET_DIR=./IMAGE/tfrecords/commodity_tfrecords/
python train_ssd_network.py --train_model_dir=${TRAIN_MODEL_DIR} --dataset_dir=${DATASET_DIR} --dataset_name="commodity_2018" --train_or_test=train --model_name=ssd_vgg_300 --pre_trained_path=${PRE_TRAINED_PATH} --weight_decay=0.0005 --optimizer=adam --learning_rate=0.001 --batch_size=2
# =========================================================================== #
# SSD 网络参数:
# =========================================================================== #
{'anchor_offset': 0.5,
'anchor_ratios': [[2, 0.5],
[2, 0.5, 3, 0.3333333333333333],
[2, 0.5, 3, 0.3333333333333333],
[2, 0.5, 3, 0.3333333333333333],
[2, 0.5],
[2, 0.5]],
'anchor_size_bounds': [0.15, 0.9],
'anchor_sizes': [(21.0, 45.0),
(45.0, 99.0),
(99.0, 153.0),
(153.0, 207.0),
(207.0, 261.0),
(261.0, 315.0)],
'anchor_steps': [8, 16, 32, 64, 100, 300],
'feat_layers': ['block4', 'block7', 'block8', 'block9', 'block10', 'block11'],
'feat_shapes': [(38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1)],
'img_shape': (300, 300),
'no_annotation_label': 21,
'normalizations': [20, -1, -1, -1, -1, -1],
'num_classes': 9,
'prior_scaling': [0.1, 0.1, 0.2, 0.2]}
# =========================================================================== #
# 训练数据dataset files:
# =========================================================================== #
['./IMAGE/tfrecords/commodity_tfrecords/commodity_2018_train_000.tfrecord']
Tensor("fifo_queue_Dequeue:0", shape=(2, 300, 300, 3), dtype=float32)
INFO:tensorflow:global_step/sec: 0
INFO:tensorflow:Recording summary at step 0.
INFO:tensorflow:global step 10: loss = 38.3997 (0.086 sec/step)
INFO:tensorflow:global step 20: loss = 155.8330 (0.087 sec/step)
INFO:tensorflow:global step 30: loss = 231.1888 (0.086 sec/step)
INFO:tensorflow:global step 40: loss = 62.8824 (0.087 sec/step)
.......
tensorboard --logdir="./"
import numpy as np
import tensorflow as tf
from PIL import Image
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import visualization
# 需要调用处理模块,我们运行以上级目录运行调用的包的名字可以不变
import sys
sys.path.append('../')
from utils.basic_tools import np_methods
slim = tf.contrib.slim
# 需要用到预处理工厂,模型工厂
from nets import nets_factory
from preprocessing import preprocessing_factory
# 使用feed_dict 与 placeholder的形式,运行时输入数据
# 1、定义输入图片数据的占位符
image_input = tf.placeholder(tf.uint8, shape=(None, None, 3))
# 定义一个输出的形状,元组表示
net_shape = (300, 300)
data_format = "NHWC"
# 2、数据输入到预处理工厂当中,进行处理得到结果
preprocessing_fn = preprocessing_factory.get_preprocessing("ssd_vgg_300", is_training=False)
img_pre, _, _, bbox_img = preprocessing_fn(image_input, None, None, net_shape, data_format)
# img_pre是三维形状,(300, 300, 3)
# 卷积神经网络要求都是四维的数据计算(1, 300, 300, 3)
# 维度的扩充
image_4d = tf.expand_dims(img_pre, 0)
# 3、定义SSD模型, 并输出预测结果
# reuse作用:在notebook当中运行,第一次创建新的变量为FALSE,但是重复运行cell,保留这些变量的命名,选择重用这些命名,已经存在内存当中了
# 没有消除,设置reuse=True
reuse = True if 'ssd_net' in locals() else False
# 网络工厂获取
ssd_class = nets_factory.get_network("ssd_vgg_300")
# 网络类当中参数,类别总数(商品数据集8+1)?
ssd_params = ssd_class.default_params._replace(num_classes=9)
# 初始化网络
ssd_net = ssd_class(ssd_params)
#获取每一层的default boxes
ssd_anchors = ssd_net.anchors(net_shape)
# 通过网络的方法获取结果
# 使用slim指定共有参数data_format,net函数里面有很多函数需要使用data_format
with slim.arg_scope(ssd_net.arg_scope(data_format=data_format)):
predictions, localisations, _, _ = ssd_net.net(image_4d, is_training=False, reuse=reuse)
# 4、定义交互式会话,初始化变量,加载模型
config = tf.ConfigProto(log_device_placement=False)
sess = tf.InteractiveSession(config=config)
#初始化变量
sess.run(tf.global_variables_initializer())
# 名字步数
ckpt_filepath = "../ckpt/fine_tuning/model.ckpt-0"
# 创建saver
saver = tf.train.Saver()
saver.restore(sess, ckpt_filepath)
INFO:tensorflow:Restoring parameters from ../ckpt/fine_tuning/model.ckpt-0
# 会话运行图片,输出结果
# 读取一张图片
img = Image.open("../IMAGE/commodity/JPEGImages/000035.jpg").convert('RGB')
# 走一个数组转换
img = np.array(img)
i, p, l, box_img = sess.run([image_4d, predictions, localisations, bbox_img], feed_dict={image_input:img})
# 进行结果的筛选了,排序、NMS
# 通过 predictions 与 select_threshold 筛选bbox
classes, scores, bboxes = np_methods.ssd_bboxes_select(
p, l, ssd_anchors,
select_threshold=0.1, img_shape=(300, 300), num_classes=9, decode=True)
# bbox边框不能超过原图片 默认原图的相对于bbox大小比例 [0, 0, 1, 1]
bboxes = np_methods.bboxes_clip(box_img, bboxes)
# 根据 scores 从大到小排序,并改变classes rbboxes的顺序
classes, scores, bboxes = np_methods.bboxes_sort(classes, scores, bboxes, top_k=400)
# 使用nms算法筛选bbox
classes, scores, bboxes = np_methods.bboxes_nms(classes, scores, bboxes, nms_threshold=.45)
# 根据原始图片的bbox,修改所有bbox的范围 [.0, .0, .1, .1]
bboxes = np_methods.bboxes_resize(box_img, bboxes)
visualization.plt_bboxes(img, classes, scores, bboxes)
docker pull tensorflow/serving
这样想当于安装好了serving服务,建议在服务器上安装,后续开启模型服务,并进行操作后,能被外网访问.
如果要使用Serving服务,需要我们将模型上传到服务其中,才能继续开启,接下来进行模型导出.
import os
import tensorflow as tf
slim = tf.contrib.slim
import sys
sys.path.append("../")
from nets.nets_model import ssd_vgg_300
data_format = "NHWC"
ckpt_filepath = "../ckpt/fine_tuning/model.ckpt-0"
def main(_):
# 1、定义好完整的模型图,去定义输入输出结果
# 输入:SSD 模型要求的数据(不是预处理的输入)
img_input = tf.placeholder(tf.float32, shape=(300, 300, 3))
# [300,300,3]--->[1,300,300,3]
img_4d = tf.expand_dims(img_input, 0)
# 输出:SSD 模型的输出结果
ssd_class = ssd_vgg_300.SSDNet
ssd_params = ssd_class.default_params._replace(num_classes=9)
ssd_net = ssd_class(ssd_params)
# 得出模型输出
with slim.arg_scope(ssd_net.arg_scope(data_format=data_format)):
predictions, localisations, _, _ = ssd_net.net(img_4d, is_training=False)
# 开启会话,加载最后保存的模型文件是的模型预测效果达到最好
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# 创建saver
saver = tf.train.Saver()
# 加载模型
saver.restore(sess, ckpt_filepath)
# 2、导出模型过程
# 路径+模型名字:"./model/commodity/"
export_path = os.path.join(
tf.compat.as_bytes("./model/commodity/"),
tf.compat.as_bytes(str(1)))
print("正在导出模型到 %s" % export_path)
# 建立builder
builder = tf.saved_model.builder.SavedModelBuilder(export_path)
# 通过该函数建立签名映射(协议)
# tf.saved_model.utils.build_tensor_info(img_input):填入的参数必须是一个Tensor
prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(
inputs={
# 给输入数据起一个别名,用在客户端读取的时候需要指定
"images": tf.saved_model.utils.build_tensor_info(img_input)
},
outputs={
'predict0': tf.saved_model.utils.build_tensor_info(predictions[0]),
'predict1': tf.saved_model.utils.build_tensor_info(predictions[1]),
'predict2': tf.saved_model.utils.build_tensor_info(predictions[2]),
'predict3': tf.saved_model.utils.build_tensor_info(predictions[3]),
'predict4': tf.saved_model.utils.build_tensor_info(predictions[4]),
'predict5': tf.saved_model.utils.build_tensor_info(predictions[5]),
'local0': tf.saved_model.utils.build_tensor_info(localisations[0]),
'local1': tf.saved_model.utils.build_tensor_info(localisations[1]),
'local2': tf.saved_model.utils.build_tensor_info(localisations[2]),
'local3': tf.saved_model.utils.build_tensor_info(localisations[3]),
'local4': tf.saved_model.utils.build_tensor_info(localisations[4]),
'local5': tf.saved_model.utils.build_tensor_info(localisations[5]),
},
method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME,
)
# 建立元图格式,写入文件
builder.add_meta_graph_and_variables(
sess, [tf.saved_model.tag_constants.SERVING],
signature_def_map={
'detected_model':
prediction_signature,
},
main_op=tf.tables_initializer(),
strip_default_attrs=True)
# 保存
builder.save()
print("Serving模型结构导出结束")
if __name__ == '__main__':
tf.app.run()
/home/yuyang/anaconda3/envs/tensor1-6/bin/python3.5 /media/yuyang/Yinux/online_class_V5.0/test/export_serving_model.py
2019-06-06 15:05:42.998243: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX AVX2 FMA
2019-06-06 15:05:43.091183: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:897] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-06-06 15:05:43.091738: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1405] Found device 0 with properties:
name: GeForce GTX 1080 Ti major: 6 minor: 1 memoryClockRate(GHz): 1.62
pciBusID: 0000:01:00.0
totalMemory: 10.91GiB freeMemory: 10.30GiB
2019-06-06 15:05:43.091749: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1484] Adding visible gpu devices: 0
2019-06-06 15:05:43.282062: I tensorflow/core/common_runtime/gpu/gpu_device.cc:965] Device interconnect StreamExecutor with strength 1 edge matrix:
2019-06-06 15:05:43.282089: I tensorflow/core/common_runtime/gpu/gpu_device.cc:971] 0
2019-06-06 15:05:43.282094: I tensorflow/core/common_runtime/gpu/gpu_device.cc:984] 0: N
2019-06-06 15:05:43.282251: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1097] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 9955 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1080 Ti, pci bus id: 0000:01:00.0, compute capability: 6.1)
正在导出模型到 b'./model/commodity/1'
Serving模型结构导出结束
Process finished with exit code 0
docker run -p 8500:8500 -p 8501:8501 --mount type=bind,source=/media/yuyang/Yinux/commodity_detect_proj/model_detector/test/model/commodity,target=/models/commodity/ -e MODEL_NAME=commodity -t tensorflow/serving
docker build -t tf-web .
数据模块接口->数据集名称commodity_2018
commodity_2018_train,commodity_2018_test
读取图片和XML数据
(1)把之前的类别进行修改,数据集名字commodity_2018_train
读取数据集
(2)读取数据集
设计一个读取数据的基类,
class TFRecordsReaderBase(object):
"""数据集基类
"""
def __init__(self, param):
# param给不同数据集的属性配置
self.param = param
def get_data(self, train_or_test, dataset_dir):
"""
获取数据规范
:param train_or_test: train or test数据文件
:param dataset_dir: 数据集目录
:return:
"""
return None
继承基类,处理不同数据集类
建立一个dataset_init目录,实现不同数据集读取逻辑
CommodityTFRecords(dataset_utils.TFRecordsReaderBase):
param:命名字典去定义数据结构
'FILE_PATTERN',
'NUM_CLASSES',
'SPLITS_TO_SIZES',
'ITEMS_TO_DESCRIPTIONS'
创建一个数据工厂作用的文件
nets目录,nets_factory文件,获取不同模型
预处理模块
多GPU训练
model_deploy
clone:指的是每个GPU,每个GPU会得到一个完整的复制模型,主要进行计算
训练逻辑代码
相关配置:
步骤编写1:
1、配置集群环境数量介绍,创建一个全局步长变量
2、
获取图片数据,做一些处理
# 图片有什么?image, shape, bbox, label
# image会做一些数据增强,大小变换
# 直接训练?需要将anchor bbox进行样本标记正负样本,目的使的GT目标样本的数量与default bboxes数量一致
将每个模块结果先获取
dataset_factory获取数据集描述信息, nets_factory获取anchors box进行正负样本标记
指定deploy_config.inputs_device()去通过provider获取单个图片的信息
进行数据的处理,image, glabels, gbboxes
做目标值的样本标记处理,ssd_net.bboxes_encode
批处理以及队列设置
3、定义模型计算结果,损失复制到不同设备clone,观察默认一个clone变量情况(GPU设备进行)
train_tools.deploy_loss_summary(deploy_config,batch_queue,ssd_net,summaries,batch_shape,FLAGS)
tf.app.flags.DEFINE_float(
'weight_decay', 0.00004, '网络误差函数惩罚项值,越小越防止过拟合.')
4、定义学习率优化器(在默认的CPU设备进行)
tf.device(deploy_config.optimizer_device()):
train_tools.configure_learning_rate
train_tools.configure_optimizer(FLAGS, learning_rate)
学习率先关参数的添加
tf.app.flags.DEFINE_string(
'optimizer', 'rmsprop', '优化器种类 可选"adadelta", "adagrad", "adam","ftrl", "momentum", "sgd" or "rmsprop".')
tf.app.flags.DEFINE_string(
'learning_rate_decay_type', 'exponential','学习率迭代种类 "fixed", "exponential", "polynomial"')
tf.app.flags.DEFINE_float(
'learning_rate', 0.01, '模型初始学习率')
tf.app.flags.DEFINE_float(
'end_learning_rate', 0.0001, '模型训练迭代后的终止学习率')
5、进行不同所有模型的损失平均值计算,然后变量的总梯度,
train_tools.get_trainop(optimizer,
summaries,
clones,
global_step,
first_clone_scope, update_ops)
6、设置配置、saver去进行训练
tf.app.flags.DEFINE_integer(
'max_number_of_steps', None, '训练的最大步数')
tf.app.flags.DEFINE_string(
'train_model_dir', ' ', '训练输出的模型目录')
# pre-trained模型路径.
tf.app.flags.DEFINE_string(
'pre_trained_model', None, '用于fine-tune的已训练好的基础参数文件位置')
log_every_n_steps=10, # 打印频率
save_summaries_secs=60, # 保存摘要频率
saver=saver, # 保存模型参数
save_interval_secs=600, # 保存模型间隔
session_config=config, # 会话参数配置
init_fn=train_tools.get_init_fn(FLAGS)
测试
TensorFlow serving
服务器的部署