@深度学习的工程化应用
工程化流程
作为一名深度学习的初学者,很高兴能与大家分享一下我的学习心得,首先说一下我进行深度学习的目的:将深度学习网络模型工程化。展开说就是用不同的开发工具(如:C/C++、C#、Java等)调用你训练好的网络模型,进而将其工程化。
深度学习平台搭建
本人笔记本配置:9th CORE i7处理器(CPU),GTX1060TI(显卡),内存16G(RAM),WIN10(操作系统)
在整个的开发过过程中我们所需要的相关软件(以下版本为我所使用完美运行版本,仅供参考):
- Pycharm —version: pycharm-community-2020.2
下载网址:https://www.jetbrains.com/pycharm/download/#section=windows - Anaconda —version:4.8.4
(这个软件能够迅速帮我们完成平台的搭建,因此必备) - VS2017
上述的软件只是“肉身”,下面我们开始为其注入灵魂,配置python和Tensorflow版本(https://www.cnblogs.com/montai/p/13460291.html),
由于最开始急于求成,因此并没有将算法包与工程一一对应,例如:你建立了一个A工程,其中需要用到的算法包分别为:a、b、c,那么自然你在进行开发的时候需要将上述三个算法包进行加载。因为后续我们还会建立数以万计个工程,因此大家思考一个问题,如果你将所有的算法包全都配置到一个环境当中是不是就显得特别愚蠢,如果按照这种模式去做,不光为后续的算法移植带来麻烦,还有可能出现意想不到问题(比如两个本来用不到的包出现了版本不兼容的问题,导致程序异常崩溃)。那么如何避免这个问题的发生呢?Anaconda登场,按照下面的步骤你可以轻松完成python和tensorflow的配置。
第一步:打开anaconda的prompt
第二步:创建虚拟环境并安装python
输入:conda create -n your_env_name1 python=3.6.2
然后输入y,下面的也是一致不再赘述
python的版本根据你的实际需求设定
第三步:激活虚拟环境
发现环境发生变化,由base->your_env_name1
第四步:安装tensorflow
因为我通过python和tensorflow的版本匹配信息了解到,3.6版本的python和1.12.0版本的tf是兼容的,因此我们在your_env_name1下进行tensorflow的安装
输入:conda install tensorflow=1.12.0 ;然后为了查看安装是否成功直接输入cond list,可以看到python和tensorflow都已经成功安装了
第五步:使用pycharm在your_env_name1的化境下建立工程(注意环境与工程不要混淆)
步骤1:新建工程(File->NewProject)
步骤2:配置环境
步骤3:验证当前的工程和环境配置是否正确
File->Settings->Project:Test->Python interpreter
OK!!如果你成功到达了这步,那么恭喜你,你已经成功的完成了平台的配置,后续可以直接进行代码的编写。不过在这里要注意:在上述的环境中我只是安装了python和tensorflow,后续如果你还要安装其它算法包那么你只是需要重复上述步骤即可,例如你想要安装matplotlib算法包,那么你需要在固定环境下输入:conda install matplotlib即可
深度学习模型训练
我就以猫狗二分类模型为Demo为大家进行一下简单介绍,在对该模型进行训练的时候我采用的是Keras架构,此架构的封装性较高,因此只要按照步骤去搭建模型一般不会存在什么问题。
数据集规模:训练集:25000张图片,验证集:200张图片,测试集:600张图片。数据来源:Kaggle官网。
准确率如下:
验证集和训练集的过程准确率变化和损失变化如上图所示
大家如果没有下载到可以在下面留言然后我分享给大家!!!!
代码如下:
# 基于keras框架进行神经网络模型开发需要以下五个步骤
# 1.模型选择
# 2.构建网络层
# 3.编译
# 4.训练
# 5.预测
import os
import time
import numpy as np
import tensorflow as tf
from keras import layers
from keras import models
from keras.engine.saving import load_model
from keras.preprocessing import image
from PIL import Image
###猫狗识别:猫0,狗1
# 设置文件目录
# 训练集
trainDir = 'D:/TensorflwCode/DogoorCatfinal/data/train/'
trainDogDir = 'D:/TensorflwCode/DogoorCatfinal/data/train/train_dog/'
trainCatDir = 'D:/TensorflwCode/DogoorCatfinal/data/train/train_cat/'
#
#
# 验证集
from tensorflow.python.saved_model.loader_impl import load
valDir = 'D:/TensorflwCode/DogoorCatfinal/data/check_keras/'
valDogDir = 'D:/TensorflwCode/DogoorCatfinal/data/check_keras/check_dog/'
valCatDir = 'D:/TensorflwCode/DogoorCatfinal/data/check_keras/check_cat/'
##导入数据
def get_all_files(file_path):
""" 获取图片路径及其标签 :param file_path: a sting, 图片所在目录 :param is_random: True or False, 是否乱序 :return: """
image_list = []
label_list = []
cat_count = 0
dog_count = 0
for item in os.listdir(file_path):
item_path = file_path + '\\' + item
item_label = item.split('.')[0] # 文件名形如 cat.0.jpg,只需要取第一个
if os.path.isfile(item_path):
image_list.append(item_path)
else:
raise ValueError('文件夹中有非文件项.')
if item_label == 'cat': # 猫标记为'0'
label_list.append(0)
cat_count += 1
else: # 狗标记为'1'
label_list.append(1)
dog_count += 1
print('数据集中有%d只猫,%d只狗.' % (cat_count, dog_count))
image_list = np.asarray(image_list)
label_list = np.asarray(label_list)
# 乱序文件
rnd_index = np.arange(len(image_list))
np.random.shuffle(rnd_index)
image_list = image_list[rnd_index]
label_list = label_list[rnd_index]
return image_list, label_list
def get_one_image(train):
'''Randomly pick one image from training data Return: ndarray '''
img_dir = train
image = Image.open(img_dir)
# plt.imshow(image)
image = image.resize([150, 150])
image = np.array(image)
return image
##模型训练
def train():
# #创建模型
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
# 卷积层,输出空间的维数为32,也可以说是输出特征图的深度为32,提取信息的窗口大小(3,3),卷积核的大小也为(3,3)
# 激活函数relu,输入图片大小(150,150,3)
model.add(layers.MaxPooling2D((2, 2)))
# 池化层,窗口大小为(2,2),缩小特征图的尺寸
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
# 扁平层,将多维的输入转化为一维的输出
model.add(layers.Dense(512, activation='relu'))
# 全连接层,将提取的特征组合,得出结果
model.add(layers.Dense(1, activation='sigmoid'))
# 设置损失函数,优化器,模型在训练和测试时的性能指标
from keras import optimizers
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
# 配置图片生成器
from keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(rescale=1. / 255)
test_datagen = ImageDataGenerator(rescale=1. / 255)
# 创建图片生成器
train_generator = train_datagen.flow_from_directory(
trainDir, # 图片地址
target_size=(150, 150), # 将图片调整为(150,150)大小
batch_size=20, # 设置批量数据的大小为20
class_mode='categorical')
# class_mode='binary')#设置返回标签的类型
val_generator = test_datagen.flow_from_directory(
valDir,
target_size=(150, 150),
batch_size=20,
class_mode='categorical')
# class_mode='binary')
# 拟合模型
history = model.fit_generator(
train_generator,
steps_per_epoch=100, # 迭代进入下一轮次需要抽取的批次
epochs=30, # 数据迭代的轮数
#epochs=100,
validation_data=val_generator,
validation_steps=50) # 验证集用于评估的批次
# 保存模型
model.save('cats_and_dogs_small_4.h5')
# 画出结果
import matplotlib.pyplot as plt
# 查看变量,发现history.history中就只有这四个值,分别是准确度,验证集准确度,损失,验证集损失
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
# 画两个图,分别是正确率和验证损失率
# 正确率
plt.figure(1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.savefig('acc.png')
plt.show()
# 损失
plt.figure(2)
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.savefig('loss.png')
plt.show()
##测试
def test(test_image, imagename):
# ###对于数据进行测试
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis=0)
test_image = test_image / 255
result = model.predict(test_image)
return result
def get_one_image(train):
'''Randomly pick one image from training data Return: ndarray '''
img_dir = train
image = Image.open(img_dir)
# plt.imshow(image)
image = image.resize([150, 150])
image = np.array(image)
return image
def test_image():
rightcount = 0
rightpercent = 0
test_dir = 'D:/TensorflwCode/DogoorCatfinal/data/test_keras/testall'
###test_label存放目标的标签,其中cat为0,dog为1
test, test_label = get_all_files(test_dir )
###wym,add,对于每一个数据都进行一次验证
for k in range(0, len(test)):
print('%s' % test[k])
image_array = get_one_image(test[k])
result = test(image_array, test[k])
if result[0][0] >= 0.5:
prediction = 'cat'
if test_label[k] == 0:
rightcount = rightcount + 1
else:
rightcount = rightcount
else:
prediction = 'dog'
if test_label[k] == 1:
rightcount = rightcount + 1
else:
rightcount = rightcount
rightpercent = rightcount / len(test) * 100
print('测试集准确率为:%', rightpercent)
if __name__ == '__main__':
##先将model和test_image屏蔽掉,进行模型训练,然后再将train屏蔽进行测试
#model = load_model('D:/TensorflwCode/DogorCatfial-keras/cats_and_dogs_small_1.h5')
start = time.clock()
train()
#test_image()
elapsed = (time.clock() - start)
print("用时%f 小时:" % (elapsed / 3600))
深度学习网络模型调用
我们通过上述的操作已经得到了训练好的网络模型(.h5文件),因为后续工程需要我必须要采用C#对于模型进行调用,那么应该如何进行操作呢?
步骤1:将.h5模型转换为.pb形式
步骤2:安装tensorflowsharp
步骤3:调用网络模型
模型转换
######采用keras框架训练出来的模型为.h5文件,需要将其转换成pb文件以供后续调用##########
""" 将keras的.h5的模型文件,转换成TensorFlow的pb文件 """
# ==========================================================
from keras.models import load_model
import tensorflow as tf
import os.path as osp
import os
from keras import backend
def h5_to_pb(h5_model, output_dir, model_name, out_prefix="output_", log_tensorboard=True):
""".h5模型文件转换成pb模型文件 Argument: h5_model: str .h5模型文件 output_dir: str pb模型文件保存路径 model_name: str pb模型文件名称 out_prefix: str 根据训练,需要修改 log_tensorboard: bool 是否生成日志文件 Return: pb模型文件 """
if os.path.exists(output_dir) == False:
os.mkdir(output_dir)
out_nodes = []
for i in range(len(h5_model.outputs)):
out_nodes.append(out_prefix + str(i + 1))
tf.identity(h5_model.output[i], out_prefix + str(i + 1))
sess = backend.get_session()
from tensorflow.python.framework import graph_util, graph_io
# 写入pb模型文件
init_graph = sess.graph.as_graph_def()
main_graph = graph_util.convert_variables_to_constants(sess, init_graph, out_nodes)
graph_io.write_graph(main_graph, output_dir, name=model_name, as_text=False)
# 输出日志文件
if log_tensorboard:
from tensorflow.python.tools import import_pb_to_tensorboard
import_pb_to_tensorboard.import_to_tensorboard(os.path.join(output_dir, model_name), output_dir)
if __name__ == '__main__':
# .h模型文件路径参数
input_path = 'D:/TensorflwCode/DogorCatfial-keras'
weight_file = 'cats_and_dogs_small_1.h5'
weight_file_path = os.path.join(input_path, weight_file)
output_graph_name = weight_file[:-3] + '.pb'
output_dir = osp.join(os.getcwd(),"trans_model")
h5_model = load_model(weight_file_path)
h5_to_pb(h5_model, output_dir=output_dir, model_name=output_graph_name)
print ('Finished')
tensorflowsharp安装
首先我为大家介绍一下,到底什么是Tensorflowsharp?
TensorflowSharp是对Tensorflow C语言版接口的封装,便于C#开发人员在项目中使用Tensorflow。因此我们可以直接使用其直接完成对于训练模型的调用。
第一步:安装NuGet
https://www.nuget.org/download
大家可以在上述网址上下载你想要的版本(我用的是5.7.0)
第二步:安装tensorflowsharp
用cmd打开windows下的命令行窗口,然后将位置设定为NuGet的下载位置
然后输入命令
nuget install tensorflowsharp
接着你需要到你的文件夹下验证一下tensorflowsharp是否下载成功
调用网络模型
步骤1:在VS2017中配置tensorflowsharp
(1)建立一个C#的控制台工程(假装你已经建好了工程~~~~~)
(2)在工程中直接对于tensorflow.dll进行引用
点击工程->添加->引用->添加你的tensorflow.dll(位置就在你成功安装tensorflowsharp的那个文件夹哈)
步骤2:编写代码进行网络模型的调用
该程序中有几个地方需要进行修改,例如:模型的路径、原始图片的大小、以及runner.AddInput(graph[“conv2d_1_input”][0], tensor);和runner.Run(graph.Sigmoid(graph[“dense_2/Sigmoid”][0]));输入层和输出层的名字(根据你自身的实际情况进行修改)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TensorFlow;
namespace Tftransfer
{
class Program
{
private static string model_file = @"D:\TensorflwCode\h5_to_pb\trans_model\cats_and_dogs_small_1.pb";
static void Main(string[] args)
{
using (var session = new TFSession())
{
// 创建图
var graph = new TFGraph();
//重点是下面的这句,把训练好的pb文件给读出来字节,然后导入
var model = File.ReadAllBytes(model_file);
graph.Import(model);
Console.WriteLine("请输入一个图片的地址");
var src = Console.ReadLine();
var tensor = ImageUtil.CreateTensorFromImageFile(src);
using (var sess = new TFSession(graph))
{
var runner = sess.GetRunner();
runner.AddInput(graph["conv2d_1_input"][0], tensor);
var r = runner.Run(graph.Sigmoid(graph["dense_2/Sigmoid"][0]));
var v = (float[,])r.GetValue();
Console.WriteLine(v[0, 0]);
}
}
Console.ReadKey();
}
}
}
public static class ImageUtil
{
public static TFTensor CreateTensorFromImageFile(byte[] contents, TFDataType destinationDataType = TFDataType.Float)
{
var tensor = TFTensor.CreateString(contents);
TFOutput input, output;
// Construct a graph to normalize the image
using (var graph = ConstructGraphToNormalizeImage(out input, out output, destinationDataType))
{
// Execute that graph to normalize this one image
using (var session = new TFSession(graph))
{
var normalized = session.Run(
inputs: new[] { input },
inputValues: new[] { tensor },
outputs: new[] { output });
return normalized[0];
}
}
}
// Convert the image in filename to a Tensor suitable as input to the Inception model.
public static TFTensor CreateTensorFromImageFile(string file, TFDataType destinationDataType = TFDataType.Float)
{
var contents = File.ReadAllBytes(file);
// DecodeJpeg uses a scalar String-valued tensor as input.
var tensor = TFTensor.CreateString(contents);
TFOutput input, output;
// Construct a graph to normalize the image
using (var graph = ConstructGraphToNormalizeImage(out input, out output, destinationDataType))
{
// Execute that graph to normalize this one image
using (var session = new TFSession(graph))
{
var normalized = session.Run(
inputs: new[] { input },
inputValues: new[] { tensor },
outputs: new[] { output });
return normalized[0];
}
}
}
// The inception model takes as input the image described by a Tensor in a very
// specific normalized format (a particular image size, shape of the input tensor,
// normalized pixel values etc.).
//
// This function constructs a graph of TensorFlow operations which takes as
// input a JPEG-encoded string and returns a tensor suitable as input to the
// inception model.
private static TFGraph ConstructGraphToNormalizeImage(out TFOutput input, out TFOutput output, TFDataType destinationDataType = TFDataType.Float)
{
// Some constants specific to the pre-trained model at:
// https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip
//
// - The model was trained after with images scaled to 224x224 pixels.
// - The colors, represented as R, G, B in 1-byte each were converted to
// float using (value - Mean)/Scale.
const int W = 150;
const int H = 150;
const float Mean = 0;
const float Scale = 1f;
var graph = new TFGraph();
input = graph.Placeholder(TFDataType.String);
output = graph.Cast(
graph.Div(x: graph.Sub(x: graph.ResizeBilinear(images: graph.ExpandDims(input: graph.Cast(graph.DecodeJpeg(contents: input, channels: 3), DstT: TFDataType.Float),
dim: graph.Const(0, "make_batch")),
size: graph.Const(new int[] { W, H }, "size")),
y: graph.Const(Mean, "mean")),
y: graph.Const(Scale, "scale")), destinationDataType);
return graph;
}
}
代码运行
输入一张狗狗的图片
结果成功输出:
总结:实际在学习的过程中我也遇到了很多这样那样的问题,不过不用担心,你遇到的问题估计前人都曾遇到过,因此你可以从网上或者通过其他方式去耐心寻找解决的方法。我呼吁大家可以将自己解决问题的心路历程都无私的拿出来分享给他人,这样既可以帮助别人,又是对于自己阶段性成果的一个总结。
上述只是本人的一点看法,不喜勿喷,后续博客还会定期更新,希望大家多多支持,谢谢!!!!