自己的原文哦~ https://blog.51cto.com/whaosoft/14099334
一、TensorFlow2.x模型转onnx、TensorRT
这里探究了如何将基于TensorFlow2.x框架训练的模型转换成通用性较强的onnx静态图模型,以及推理效率较高的TensorRT模型。
每当碰到台风天气,或者平时下暴雨,现在的城市排水系统都有滞后性,如果碰上短时间强降雨就很容易导致路面积水,危害人民生命财产安全。因此需要设计算法可自动识别地面积水情况,从而通知相关部门快速处理。
对于上述应用场景,精度、性能以及模型部署的可迁移性都是在算法设计实现过程中需要重点考虑的因素,为了提高算法效率,同时增强模型的可移植性,本文探究了如何将基于TensorFlow2.x框架训练的模型转换成通用性较强的onnx静态图模型,以及推理效率较高的TensorRT模型。
TensorFlow是主流深度学习框架之一,在全球有着数十万名用户。由于TensorFlow1.x代码构建及接口问题饱含诟病,Google在2019年推出Tensorflow2避免TensorFlow 1.x 版本的诸多缺陷,Tensorflow2.x与Keras的代码构建方式类似,且包含了大量Keras接口,同时TensorFlow 2.x 也支持通过 tf.function 将动态图优先模式的代码转化为静态图模式,实现开发和运行效率双赢。
NVIDIA®TensorRT™是一个深度学习平台,用于优化神经网络模型,其核心库是使用C++去加速NVIDIA生产的GPU。一般来说从tensorflow2.x到tensorrt部署主要有两种方式:
- model.h5->model.pb->model.uff
- model.h5->model.pb-> model.onnx->model.engine
由于极市平台提供的demo通常支持通过.onnx转engine(demo地址:src/SampleDetector.cpp · cvmart/ev_sdk_demo4.0_vehicle_plate_detection - 码云 - 开源中国 (gitee.com)),因此本文主要介绍第二种部署方式。
运行环境:
TensorFlow 2.4
CUDA 11.1
CUDNN 8
TensorRT 8.2.1.8
tf2onnx 1.13.0
onnx 1.12.0
部署步骤:
通常来说,采用model.fit()接口生成的模型文件后缀为.h5,首先需要将该模型转换为静态图.pb格式,python代码如下:
from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2
def h5_to_pb(model):
model.load_weights(h5_save_path, by_name=True, skip_mismatch=True)
print(tf.__version__)
model.summary()
full_model = tf.function(lambda Input: model(Input))
full_model = full_model.get_concrete_function(tf.TensorSpec(model.inputs[0].shape, model.inputs[0].dtype))
# Get frozen ConcreteFunction
frozen_func = convert_variables_to_constants_v2(full_model)
frozen_func.graph.as_graph_def()
layers = [op.name for op in frozen_func.graph.get_operations()]
print("-" * 50)
print("Frozen model layers: ")
for layer in layers:
print(layer)
print("-" * 50)
print("Frozen model inputs: ")
print(frozen_func.inputs)
print("Frozen model outputs: ")
print(frozen_func.outputs)
# Save frozen graph from frozen ConcreteFunction to hard drive
tf.io.write_graph(graph_or_graph_def=frozen_func.graph,
logdir=h5_save_paths,
name="model.pb",
as_text=False)
其中model为加载到内存中的动态图模型。.pb模型转换成功后,通过tf2onnx库将其转换为onnx通用模型文件,代码如下:
python -m tf2onnx.convert --graphdef /project/train/models/model.pb --output /project/train/models/seg_model.onnx --inputs Input:0 --outputs Identity:0
需要注意输入和输出层名称(该名称在上一步转pb的过程中会打印出来)
极市部署demo中已经继承了从onnx到trt的过程,如果需要自行转换trt文件则在配置好TensorRT环境后,通过以下代码输出:
/project/train/TensorRT-8.2.1.8/bin/trtexec
--onnx=/project/train/models/seg_model.onnx
--saveEngine=/project/train/models/seg_model.trt --workspace=2048 --fp16
在trt部署过程中需要注意一点,由于pytorch默认输入为C×H×W,而TensorFlow默认输入维度顺序为H×W×C,所以在进行数据预处理时存在一些差异,在C++部署过程中采用以下代码进行数据预处理:
void normalize_(Mat &img)
{
img.convertTo(img, CV_32F);
int row = img.rows;
int col = img.cols;
std::vector<float> mean {0.406, 0.456, 0.485};
std::vector<float> stds{ 0.225, 0.224, 0.229};
for (int c = 0; c < 3; c++)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
float pix = img.ptr<float>(i)[j * 3 + c];
img.ptr<float>(i)[j * 3 + c] = (pix / 255.0 - mean[c]) / stds[c];
}
}
}
}
通过上述步骤即完成了TensorFlow2.x从.h5到TensorRT的.engine转换。使用TensorRT进行FP16推理通常能够比TensorFlow推理.pb速度提升三倍所有,比推理.h5速度提升8倍左右。
好简单就完事了哦
二、TensorFlow GNN 1.0(TF-GNN)
在我们的世界中,物体及其相互关系无处不在,理解一个物体,其关系往往和单独的属性一样重要,例如,如交通网络、生产网络,知识图谱,或是社交网络等。离散数学和计算机科学长期以来一直在将这些网络以图的形式进行建模,图以多种复杂方式互连的节点和边构成。但是,大多数机器学习(ML)算法仅支持输入对象之间规则和统一的关系,比如像素的网格、一系列的单词,或者完全没有关系。
图神经网络(GNN)已经成为一种强大的技术,不仅可以利用图的连接性(如老旧算法 DeepWalk 和 Node2Vec 所做的那样),还能利用各节点和边上的输入特征。GNN 能够对整个图(例如,这个分子是否以特定方式反应?)、图中的单个节点(这篇文档的主题是什么,给出它的引用来源?)或是潜在的边(这个产品是否可能与那个产品一起被购买?)进行预测。除了对图进行预测,GNN 也是强大的工具,可以帮助过渡到更常见的神经网络应用场景。它们将图的离散、关系信息编码为连续形式,使得这些信息可以自然地融入到其他深度学习系统中。
现在,我们非常高兴地宣布 TensorFlow GNN 1.0(TF-GNN)发布了,这是一个经过生产级测试的库,专为大规模构建 GNN 而设计。它不仅支持在 TensorFlow 中进行模型构建和训练,还支持从庞大的数据存储中提取输入图。TF-GNN 从基础上为处理异构图而构建,这些图中的对象和关系类型通过不同的节点和边集合来表示。在现实世界中,不同类型的对象及其关系普遍存在,TF-GNN 的设计重点放在异构图上,使得这种多样性的表示成为可能。
在 TensorFlow 内部,图结构是通过一种名为 tfgnn.GraphTensor 的对象表示的。这是一个复合张量类型(即一个 Python 类中包含的一组张量),它在 tf.data.Dataset、tf.function 等中作为一等公民被接受。它不仅存储了图的结构,还存储了附加在节点、边缘以及整个图上的特征。GraphTensors 的可训练转换可以在高级 Keras API 中定义为层(Layers)对象,或者直接使用 tfgnn.GraphTensor 基础设施来定义。
#01 GNN:在上下文中对对象进行预测
以 TF-GNN 的一个典型应用为例:预测一个庞大数据库的交叉引用表格中定义的图上某个类型节点的属性。举个例子,就像是计算机科学(CS)领域 arXiv 论文的引用数据库,它有着一对多的引用以及多对一的被引用关系,我们希望能预测每篇论文所属的研究领域。
像大多数神经网络一样,GNN 在大量标记样本(大约数百万)的数据集上进行训练,但每次训练只涉及一小批训练样本(例如,数百个)。为了能够处理数百万级别的数据,GNN 通过从底层图中不断取出相对较小的子图流进行训练。每个子图包含了足够多的原始数据,能够计算出中心标记节点的 GNN 结果,并据此训练模型。这一过程 —— 通常称为子图采样 —— 对于 GNN 的训练至关重要。大多数现有的工具都是以批处理方式进行采样,生成静态的子图用于训练。TF-GNN 则提供了一种通过动态和交互式方式进行采样的改进工具。
TF-GNN 1.0 推出了一个灵活的 Python API,允许用户在所有相关的规模上配置动态或批处理子图采样:无论是在 Colab 笔记本中进行交互式操作,高效采样存储在单个训练主机主内存中的小型数据集,还是通过 Apache Beam 分布式处理存储在网络文件系统中的大型数据集(高达数亿个节点和数十亿条边)。更多细节,请参见:
内存采样用户指南:https://github.com/tensorflow/gnn/blob/main/tensorflow_gnn/docs/guide/inmemory_sampler.md
基于 beam 的采样用户指南:https://github.com/tensorflow/gnn/blob/main/tensorflow_gnn/docs/guide/beam_sampler.md
在这些被采样的子图上,GNN 的任务是计算根节点处的一个隐藏(或潜在)状态;这个隐藏状态会聚合并编码根节点周围邻域的相关信息。一个典型的方法是消息传递神经网络。在每一轮消息传递中,节点通过传入的边缘接收来自邻居的消息,并基于这些消息更新自己的隐藏状态。经过 n 轮更新后(如下图所示,n = 2),根节点的隐藏状态就能反映出所有在 n 跳边内节点的综合信息。这些消息和新的隐藏状态是由神经网络的隐藏层计算出来的。在一个多元化的图中,为不同类型的节点和边缘使用专门训练的隐藏层往往是有益的。
如图所示,一个简易的消息传递神经网络过程中,节点状态会从外围节点向中心节点传递,并在此过程中聚合这些信息以形成新的节点状态。当这一过程触及到根节点时,我们便能根据累积的信息做出最终的预测。
训练过程通过在标记节点的 GNN 隐藏状态之上增加一个输出层来实现,这个输出层负责计算损失值(用来评估预测的准确性),并通过标准的反向传播方法更新模型权重,这与常规的神经网络训练流程相同。
除了标准的监督式训练 —— 即通过最小化标签定义的损失来训练 ——GNN 也可以进行非监督式训练,即在没有标签的情况下进行。这种训练方式允许我们为节点及其特征的离散图结构创建一个连续的表示(或称为嵌入),这些嵌入表示随后可以在其他机器学习系统中使用。这样,图中编码的离散和关系信息就可以融入到更常见的神经网络应用场景中。TF-GNN 支持为异构图定制非监督学习目标的细节。
#02 构建 GNN 架构
TF-GNN 库提供了多种级别的支持,以构建和训练 GNN。
在最高层次,用户可以选择库中预先定义的模型,这些模型是以 Keras 层的形式表达的。除了一些研究文献中的模型外,TF-GNN 还提供了一个高度可配置的模型模板,这个模板集成了一系列我们发现在众多内部问题上能提供强有力基准的建模选项。这些模板实现了 GNN 的各个层,用户只需负责初始化这些 Keras 层即可。
在最基础层面,用户可以完全从零开始构建 GNN 模型,利用图数据传递的基础操作,如从一个节点向所有外连边广播数据,或者将所有进入边的数据汇总到一个节点(比如,累加所有传入的信息)。TF-GNN 的图数据模型平等对待节点、边缘以及整个输入图的特征或隐藏状态,这让构建从节点中心模型(如前文讨论的 MPNN)到更泛化的 GraphNets 都变得简单直接。这种模型构建既可以在 TensorFlow 的核心上直接使用 Keras 作为建模框架,也可以不用。
想了解更多详细信息和中级建模方法,请参考:
TF-GNN 用户指南:https://github.com/tensorflow/gnn/blob/main/tensorflow_gnn/docs/guide/gnn_modeling.md
模型集合:https://github.com/tensorflow/gnn/tree/main/tensorflow_gnn/models
#03 训练编排
尽管高级用户可以自行定制模型训练,但 TF-GNN Runner 提供了一种高效编排 Keras 模型训练的简洁方法。一个典型的调用示例如下:
Runner 为机器学习的常见难题,比如分布式训练和在 Cloud TPU 上针对固定形状进行 tfgnn。GraphTensor 填充,提供了即插即用的解决方案。除了单任务训练外,它还支持同时针对多个(两个或更多)任务的联合训练。比如,可以将非监督任务与监督任务结合起来,共同优化最终的连续表示(或嵌入),这些表示融入了特定应用的归纳偏见。调用者仅需将任务参数换成任务映射即可:
此外,TF-GNN Runner 还实现了集成梯度方法,用于模型的归因分析。集成梯度的输出是一个 GraphTensor,其连接性与观测到的 GraphTensor 相同,但特征被梯度值替换,这些梯度值中的较大值对 GNN 的预测贡献更大。用户可以通过检查梯度值来识别他们的 GNN 最依赖哪些特征。
#04 结论
总之,我们希望 TF-GNN 能够帮助推动 GNN 在 TensorFlow 中的大规模应用,并进一步促进该领域的创新。如果你想深入了解,请尝试我们基于流行的 OGBN-MAG 基准的 Colab 演示(无需安装,直接在浏览器中运行)。
用户指南和 Colab 演示:https://github.com/tensorflow/gnn/blob/main/tensorflow_gnn/docs/guide/overview.md
论文:https://arxiv.org/abs/2207.03522
三、图像分割TensorFlow
手把手图像分割模型构建实操,TensorFlow 附详解代码过程。
图像分割是计算机视觉中的一项任务,涉及将特定图像划分为多个片段,其中每个片段代表图像中的对象或区域。这项任务对于对象检测、图像识别和自动驾驶等应用非常重要。TensorFlow 是一个开源框架,用于构建和训练机器学习模型,在我们的例子中是图像分割模型。Tensorflow 提供执行图像分割任务所需的工具和预训练模型。图像分割有一些现实世界的用例。他们包括:
- 对象识别和跟踪: 图像分割用于实时跟踪和识别人、车辆和动物等对象。这主要用于安全系统、监视和自主机器人。
- 医学成像: 图像分割用于查看和分割身体中的结构,例如器官、肿瘤和血管。这些数据用于诊断、治疗和研究。
- 自动驾驶: 检测和分类道路上的行人和车辆等对象,以避免发生事故和碰撞
学习目标
- 该项目的目标是训练一个可以为 59 个类别创建分割蒙版的模型。第一类代表个人的背景,而其余 58 类代表服装项目,例如衬衫、头发、裤子、皮肤、鞋子、眼镜等。
- 除此之外,是从图像中可视化模型创建的蒙版,并将它们与正确的蒙版进行比较,以评估模型的准确性。
- 此外,这旨在让用户了解图像分割过程以及如何实现它。
术语
- 深度学习: 是机器学习的一个子集,它使用具有三层或更多层的神经网络来模拟人脑从数据中学习的行为。
- 图像分割: 将图像划分为片段或区域的过程,每个片段或区域代表一个单独的对象或图像的一部分。
- Mask: 图像的一部分,与图像的其余部分隔离开来。
- 数据增强: 一种通过对现有数据应用转换来人为增加数据集大小的方法。
- 全卷积神经网络(FCNN) 是一种只执行卷积(和子采样或上采样)操作的神经网络。该网络包括三种主要类型的层:卷积层、池化层和全连接层。
- UNet 架构: 一种 U 形编码器-解码器网络架构,包括四个编码器块和四个使用桥连接的解码器块。
- DenseNet121: 架构由四个密集块和三个过渡层组成。每个密集块都有不同数量的层,每个层都有两个卷积来执行卷积操作。
- Upstack: 也称为上采样或转置卷积层。它们用于网络的解码器部分,以提高特征图的空间分辨率。
- Downstack: 也称为最大池化层。它们用于网络的编码器部分,以降低特征图的空间分辨率。
- Skip Connections: 用于连接相应的encoder和decoder层。
数据集描述
该数据集由 1000 张图像和 1000 个相应的PNG 格式语义分割蒙版组成。每个图像的大小为 825 像素 x 550 像素。
分割蒙版属于 59 个类,第一类是个人背景,其余 58 个类属于服装项目,例如衬衫、头发、裤子、皮肤、鞋子、眼镜等。
这个数据集在 Kaggle 上可用:https://www.kaggle.com/datasets/rajkumarl/people-clothing-segmentation
导入必要的库和依赖
导入执行此项目中的任务所需的库。
import os
import cv2
import numpy as np
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import matplotlib as mpl
from tqdm import tqdm
from sklearn.model_selection import train_test_split
数据集准备
我们将首先创建两个单独的列表来收集图像和蒙版数据集的路径,然后我们将使用 os.walk() 函数遍历所有文件。最后,我们将打印出两个列表的长度。
# a list to collect paths of 1000 images
image_path = []
for root, dirs, files in os.walk('/content/png_images'):
# iterate over 1000 images
for file in files:
# create path
path = os.path.join(root,file)
# add path to list
image_path.append(path)
len(image_path)
# a list to collect paths of 1000 masks
mask_path = []
for root, dirs, files in os.walk('/content/png_masks'):
#iterate over 1000 masks
for file in files:
# obtain the path
path = os.path.join(root,file)
# add path to the list
mask_path.append(path)
len(mask_path)
这将分别打印出 1000 张图像和 1000 个蒙版的长度。在整理数据集以获得正确的图像蒙版对之后,我们将解码图像和蒙版以将它们存储在单独的列表中。为此,我们将使用 Tensorflow 函数将每个 PNG 文件读入内存。然后将它们解码为张量并附加到两个单独的列表:蒙版和图像。
# create a list to store images
images = []
# iterate over 1000 image paths
for path in tqdm(image_path):
# read file
file = tf.io.read_file(path)
# decode png file into a tensor
image = tf.image.decode_png(file, channels=3, dtype=tf.uint8)
# append to the list
images.append(image)
# create a list to store masks
masks = []
# iterate over 1000 mask paths
for path in tqdm(mask_path):
# read the file
file = tf.io.read_file(path)
# decode png file into a tensor
mask = tf.image.decode_png(file, channels=1, dtype=tf.uint8)
# append mask to the list
masks.append(mask)
可视化数据集样本
下面的代码使用 matplotlib 使用 for 循环创建 4 到 6 范围内的图像图形。
plt.figure(figsize=(25,13))
# Iterate over the images in the range 4-6
for i in range(4,7):
# Create a subplot for each image
plt.subplot(4,6,i)
# Get the i-th image from the list
img = images[i]
# Show the image with a colorbar
plt.imshow(img)
plt.colorbar()
# Turn off the axis labels
plt.axis('off')
# Display the figure
plt.show()
输出:
我们再次使用 matplotlib 打印出相应的蒙版。我们定义了一个规范化器,以便蒙版具有一致性。
# Define a normalizer that can be applied while visualizing masks to have a consistency
NORM = mpl.colors.Normalize(vmin=0, vmax=58)
# plot masks
plt.figure(figsize=(25,13))
for i in range(4,7):
plt.subplot(4,6,i)
img = masks[i]
plt.imshow(img, cmap='jet', norm=NORM)
plt.colorbar()
plt.axis('off')
plt.show()
数据预处理
我们将使用两个函数开始数据预处理,以从数据集中获取图像和相应的蒙版,并将它们的大小调整为 128 x 128 像素的固定大小。
函数 resize_image 将重新缩放像素值,而 tf.image.resize 会将图像调整为所需的大小。使用 tf.image.resize 函数调整蒙版的大小,而不缩放像素值。
最后,它将蒙版张量的数据类型转换为 uint8。
然后,我们将使用列表推导式将调整大小函数分别应用于原始图像和蒙版列表中的每个图像和蒙版。调整大小的图像和蒙版的结果列表与原始列表具有相同的大小。
#functions to resize the images and masks
def resize_image(image):
# scale the image
image = tf.cast(image, tf.float32)
image = image/255.0
# resize image
image = tf.image.resize(image, (128,128))
return image
def resize_mask(mask):
# resize the mask
mask = tf.image.resize(mask, (128,128))
mask = tf.cast(mask, tf.uint8)
return mask
X = [resize_image(i) for i in images]
y = [resize_mask(m) for m in masks]
len(X), len(y)
这会在 X 和 y 轴上打印出 1000 的长度。
可视化调整大小的图像和蒙版的样本。
#visualizing a resized image and respective mask
# plot an image
plt.imshow(X[36])
plt.colorbar()
plt.show()
#plot a mask
plt.imshow(y[36], cmap='jet')
plt.colorbar()
plt.show()
输出:
将数据拆分为训练和验证
我们将从将数据集 X 和 y 拆分为训练集和验证集开始。验证数据将是训练数据的 20%,并且 random_state 设置为 0 以实现可重复性。
之后,我们将使用张量切片方法从 NumPy 数组 train_X、val_X、train_y 和 val_y 创建 TensorFlow Dataset 对象。
# split data into 80/20 ratio
train_X, val_X,train_y, val_y = train_test_split(X, y, test_size=0.2,
random_state=0
)
# develop tf Dataset objects
train_X = tf.data.Dataset.from_tensor_slices(train_X)
val_X = tf.data.Dataset.from_tensor_slices(val_X)
train_y = tf.data.Dataset.from_tensor_slices(train_y)
val_y = tf.data.Dataset.from_tensor_slices(val_y)
# verify the shapes and data types
train_X.element_spec, train_y.element_spec, val_X.element_spec, val_y.element_spec
数据增强
数据增强是一种通过使用现有数据创建数据集的修改副本来人为增加训练集的方法。以下是函数及其作用:
- Brightness: 调整图像的亮度
- Gamma: 调整图像的Gamma值。蒙版没有改变。
- Hue: 调整图像的色调。蒙版没有被转换。
- Crop: 裁剪图像和蒙版并调整它们的大小。
- Flip_hori: 水平翻转图像和蒙版。
- Flip_vert: 垂直翻转图像和蒙版。
- Rotate: 将图像和蒙版都顺时针旋转 90 度。
每个函数都将图像和蒙版的张量作为输入,并返回结果图像张量和原始蒙版张量。这些变换被设计为以相同的方式应用于图像和蒙版张量,以便它们保持对齐。这用于从原始数据生成新的训练示例。
# adjust brightness of image
# don't alter in mask
def brightness(img, mask):
img = tf.image.adjust_brightness(img, 0.1)
return img, mask
# adjust gamma of image
# don't alter in mask
def gamma(img, mask):
img = tf.image.adjust_gamma(img, 0.1)
return img, mask
# adjust hue of image
# don't alter in mask
def hue(img, mask):
img = tf.image.adjust_hue(img, -0.1)
return img, mask
def crop(img, mask):
# crop both image and mask identically
img = tf.image.central_crop(img, 0.7)
# resize after cropping
img = tf.image.resize(img, (128,128))
mask = tf.image.central_crop(mask, 0.7)
# resize afer cropping
mask = tf.image.resize(mask, (128,128))
# cast to integers as they are class numbers
mask = tf.cast(mask, tf.uint8)
return img, mask
# flip both image and mask identically
def flip_hori(img, mask):
img = tf.image.flip_left_right(img)
mask = tf.image.flip_left_right(mask)
return img, mask
# flip both image and mask identically
def flip_vert(img, mask):
img = tf.image.flip_up_down(img)
mask = tf.image.flip_up_down(mask)
return img, mask
# rotate both image and mask identically
def rotate(img, mask):
img = tf.image.rot90(img)
mask = tf.image.rot90(mask)
return img, mask
然后我们将解压缩图像和蒙版文件,应用增强函数,并将新数据连接到训练集。
# zip images and masks
train = tf.data.Dataset.zip((train_X, train_y))
val = tf.data.Dataset.zip((val_X, val_y))
# perform augmentation on train data only
a = train.map(brightness)
b = train.map(gamma)
c = train.map(hue)
d = train.map(crop)
e = train.map(flip_hori)
f = train.map(flip_vert)
g = train.map(rotate)
# concatenate every new augmented sets
train = train.concatenate(a)
train = train.concatenate(b)
train = train.concatenate(c)
train = train.concatenate(d)
train = train.concatenate(e)
train = train.concatenate(f)
我们现在有一个数据集是原来的800*7=5600加上原来的800一共是6400个训练样例。之后,设置批量大小和缓冲区大小,为模型构建做好准备。
#setting the batch size
BATCH = 64
AT = tf.data.AUTOTUNE
#buffersize
BUFFER = 1000
STEPS_PER_EPOCH = 800//BATCH
VALIDATION_STEPS = 200//BATCH
train = train.cache().shuffle(BUFFER).batch(BATCH).repeat()
train = train.prefetch(buffer_size=AT)
val = val.batch(BATCH)
定义和构建模型
我们将使用 FCNN(全卷积神经网络),如上所述,它包含两个部分:编码器(下层)和解码器(上层)。
- 编码器是卷积神经层的下层堆栈,负责从输入图像中提取特征。
- 解码器是转置卷积神经层的上层堆栈,可根据提取的特征构建分割图像。
在这个项目中,我们将使用 U-Net 架构。我们希望使用 U-Net 架构的功能方法,但我们将拥有适合我们功能的架构。下层堆栈可以是为图像分类训练的预训练 CNN(例如,MobileNetV2、ResNet、NASNet、Inception、DenseNet 或 EfficientNet)。它可以有效地提取特征。但是我们必须构建我们的上层堆栈以匹配我们的类(此处为 59),构建跳过连接,并使用我们的数据对其进行训练。在这种情况下,我们将使用 Keras 的 DenseNet121。
# Use pre-trained DenseNet121 without head
base = keras.applications.DenseNet121(input_shape=[128,128,3],
include_top=False,
weights='imagenet')
接下来,我们为 CNN 模型定义一个跳过连接列表。跳跃连接用于缓解深度神经网络中的梯度消失问题,这种问题在训练多层网络时可能会发生。
跳跃连接的想法是跳过一个或多个层并将较早的层直接连接到较晚的层,从而使梯度在训练过程中更容易流动。它们用于 U-Net 架构以提高语义分割的准确性。
#final ReLU activation layer for each feature map size, i.e. 4, 8, 16, 32, and 64, required for skip-connections
skip_names = ['conv1/relu', # size 64*64
'pool2_relu', # size 32*32
'pool3_relu', # size 16*16
'pool4_relu', # size 8*8
'relu' # size 4*4
]
构建下层
我们正在构建下层堆栈,用于从输入图像中提取特征并对其进行下采样以降低空间分辨率。它使用 DenseNet 模型,输入、输出和权重设置不更新。
#output of these layers
skip_outputs = [base.get_layer(name).output for name in skip_names]
#Building the downstack with the above layers. We use the pre-trained model as such, without any fine-tuning.
downstack = keras.Model(inputs=base.input,
outputs=skip_outputs)
# freeze the downstack layers
downstack.trainable = False
构建上层
上层堆栈用于 U-Net 架构的解码器部分以进行图像分割。我们将为 up-stack pix2pix 模板使用上采样模板,该模板在 TensorFlow 示例存储库中以开源方式提供。上层堆栈由四个上采样层组成,通过执行 2x 最近邻上采样,然后是步幅为 1 的 3×3 卷积层,将特征图的空间分辨率加倍。输出通道的数量在每个连续层中从 512 减少到64.
!pip install -q git+https://github.com/tensorflow/examples.git --quiet
from tensorflow_examples.models.pix2pix import pix2pix
# Four upstack layers for upsampling sizes
# 4->8, 8->16, 16->32, 32->64
upstack = [pix2pix.upsample(512,3),
pix2pix.upsample(256,3),
pix2pix.upsample(128,3),
pix2pix.upsample(64,3)]
通过将下层堆栈和上层堆栈与跳跃连接合并,构建具有跳跃连接的 U-Net 模型。该代码使用前面部分中定义的堆栈下层和堆栈上层定义了用于图像分割的完整 U-Net 架构。
下层堆栈对图像进行下采样并提取特征,上层堆栈用于将特征映射上采样到图像的原始输入大小,并将它们与下层堆栈的相应跳过连接起来,以细化分割输出。
最后,将具有 59 个过滤器和内核大小为 3 的 Conv2DTranspose 层应用于输出特征图,以获得最终的分割图。
# define the input layer
inputs = keras.layers.Input(shape=[128,128,3])
# downsample
down = downstack(inputs)
out = down[-1]
# prepare skip-connections
skips = reversed(down[:-1])
# choose the last layer at first 4 --> 8
# upsample with skip-connections
for up, skip in zip(upstack,skips):
out = up(out)
out = keras.layers.Concatenate()([out,skip])
# define the final transpose conv layer
# image 128 by 128 with 59 classes
out = keras.layers.Conv2DTranspose(59, 3,
strides=2,
padding='same',
)(out)
# complete unet model
unet = keras.Model(inputs=inputs, outputs=out)
编译和训练模型
编译模型的函数,学习率为 0.001,精度为评估指标。
# compiling the model
def Compile_Model():
unet.compile(loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
optimizer=keras.optimizers.RMSprop(learning_rate=0.001),
metrics=['accuracy'])
Compile_Model()
在训练集上拟合模型并对模型进行微调。
#training and fine-tuning
hist_1 = unet.fit(train,
validation_data=val,
steps_per_epoch=STEPS_PER_EPOCH,
validation_steps=VALIDATION_STEPS,
epochs=20,
verbose=2)
使用模型进行蒙版预测。
# select a validation data batch
img, mask = next(iter(val))
# make prediction
pred = unet.predict(img)
plt.figure(figsize=(20,28))
k = 0
for i in pred:
# plot the predicted mask
plt.subplot(4,3,1+k*3)
i = tf.argmax(i, axis=-1)
plt.imshow(i,cmap='jet', norm=NORM)
plt.axis('off')
plt.title('Prediction')
# plot the groundtruth mask
plt.subplot(4,3,2+k*3)
plt.imshow(mask[k], cmap='jet', norm=NORM)
plt.axis('off')
plt.title('Ground Truth')
# plot the actual image
plt.subplot(4,3,3+k*3)
plt.imshow(img[k])
plt.axis('off')
plt.title('Actual Image')
k += 1
if k == 4: break
plt.suptitle('Predition After 20 Epochs (No Fine-tuning)', color='red', size=20)
plt.show()
从第 21 个 epoch 到第 40 个 epoch 训练模型和微调。
downstack.trainable = True
# compile again
Compile_Model()
# train from epoch 20 to 40
hist_2 = unet.fit(train,
validation_data=val,
steps_per_epoch=STEPS_PER_EPOCH,
validation_steps=VALIDATION_STEPS,
epochs=40, initial_epoch = 20,
verbose = 2
)
使用模型进行蒙版预测。
# select a validation data batch
img, mask = next(iter(val))
# make prediction
pred = unet.predict(img)
plt.figure(figsize=(20,30))
k = 0
for i in pred:
# plot the predicted mask
plt.subplot(4,3,1+k*3)
i = tf.argmax(i, axis=-1)
plt.imshow(i,cmap='jet', norm=NORM)
plt.axis('off')
plt.title('Prediction')
# plot the groundtruth mask
plt.subplot(4,3,2+k*3)
plt.imshow(mask[k], cmap='jet', norm=NORM)
plt.axis('off')
plt.title('Ground Truth')
# plot the actual image
plt.subplot(4,3,3+k*3)
plt.imshow(img[k])
plt.axis('off')
plt.title('Actual Image')
k += 1
if k == 4: break
plt.suptitle('Predition After 40 Epochs (By Fine-tuning from 21th Epoch)', color='red', size=20)
plt.show()
模型有了很大的改进。
性能曲线
我们将使用代码来可视化跨多个时期的深度学习模型的训练和验证准确性。
history_1 = hist_1.history
acc=history_1['accuracy']
val_acc = history_1['val_accuracy']
history_2 = hist_2.history
acc.extend(history_2['accuracy'])
val_acc.extend(history_2['val_accuracy'])
plt.plot(acc[:150], '-', label='Training')
plt.plot(val_acc[:150], '--', label='Validation')
plt.plot([50,50],[0.7,1.0], '--g', label='Fine-Tuning')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.ylim([0.7,1.0])
plt.legend()
plt.show()
该模型肯定会随着epoch和训练数据的增加而改进。
该项目实现了训练一个模型的目标,该模型可以为 59 类服装生成蒙版。我们能够将生成的蒙版与数据集中相应的蒙版进行比较。我们准备了数据,将其解码为张量,调整大小,拆分数据集,执行数据扩充,并使用 UNet 架构训练模型。
- 对象分割在许多领域都有实际应用,包括计算机视觉、医学成像、机器人和独立驾驶。
- 使用 TensorFlow 进行对象分割的过程包括数据集准备、数据预处理、数据扩充、定义模型、将数据拆分为训练集和验证集,然后训练和微调模型以获得所需的结果。
- 对象分割可用于从随机图像创建衣服蒙版,用于时尚行业。
github:https://github.com/KevKibe/Semantic-Image-Segmentation-using-Tensorflow-Keras/tree/main