Tensorflow简单项目讲解

Tensorflow简单项目讲解

转载请注明出处:https://blog.csdn.net/qq_41007606/article/details/81906486
这里对Tensorflow就不在做具体介绍了,直接切入正题。

这是我毕业时做的一个毕业设计,基本实现方式就是通过自己通过爬虫等方式从网上获取数据集,然后对数据集进行清洗等工作,搭建深度神经网络进行训练,最后将训练好的模型用到一个python编写的GUI界面上,实现对输入图片的识别。
最后的识别效果如下图:
这里写图片描述
这里写图片描述
接下来我们来看一下具体实现。

第一步,数据的搜集与预处理

对于深度神经网络来说,数据集的好坏对训练的精度有很大的影响,一个小的、“不干净”的样本会导致训练欠拟合从而达不到实际需要的效果,为了确保神经网路能够训练出一个理想的模型,我们在训练之前首先要获取足够多的样本数据。
搜集样本数据的方式有很多,可以通过手工从各个网站上下载,使用相机等工具自己实际取景(这里只针对图像样本),也可以从专业的数据网站获取,当然你也可以通过写一个网络爬虫去下载你想要的数据样本。对于数据集来说,你所获取的训练数据集越大,训练的效果也会更好,因为大的数据集有利于防止训练陷入局部极小值、过拟合等。在本次设计中,我所使用的数据集来自于Python编写的网络爬虫爬取以及从数据集地址下载了部分图片,下载的图片被我总共分为了6类,共3378张图片数据集,当然,实际工业需求中,这点数据集还远远不够。
上面已经说过,为了获得好的训练效果,我们还需要使数据集“变干净”。这里说的“变干净”是指对数据集进行清洗和必要的预处理。对数据的清洗主要是指去除我们获取到的样本集中不属于我们所要分类的种类的图像数据,在本次设计中的6类样本分别为公交车、跑车、草莓、狮子、老虎、斑马,也就是说,除了这6类之外的种类我们要将他们删除,同时,我们还要选择性的删除一些模糊的、包含种类多的、目标占比小的图片数据。
因为我们在这里做的是图像的分类,我们还要将每种图像打上标签,也就是设置Label,在这里,我采用的设置标签的方式为将每类图像划分到6个不同的文件夹,每个文件夹采用图像信息所代表的种类当做文件夹的名称。
为了使样本数据更庞大,更具有代表性,我们这里采用了图像数据增强。所谓的图像数据增强是指利用平移、缩放、颜色等变换,人工增大训练样本集样本的个数,从而获得更充足的训练数据,使训练效果更好。

常用的图像数据增强的方法如下

平移:将图像在一定尺度范围内平移。

旋转:将图像在一定角度范围内进行旋转。

翻转:水平翻转或上下翻转图像。

裁减:在原有图像上裁剪出一块。

缩放:将图像在一定尺度内放大或缩小。

颜色变换:对图像的RGB颜色空间进行一些变换。

噪声扰动:给图像加入一些人工生成的噪音。

注意,使用数据增强的方法前提条件是,这些数据增强方法不会改变图像的原有标签。

第二步,神经网络模型的选取与搭建
由于硬件所限制,同时又想的到相对较好的训练效果,在这里我选用了AlexNet对数据进行训练。
下面介绍Python和Tensorflow环境下搭建AlexNet网络框架的部分代码。
1、导入搭建训练神经网络所需要的包或模块
cv2为OpenCV图像处理模块,glob为python自带的文件操作相关模块,os主要用于处理字符串路径。tensorflow为机器学习框架模块,numpy为科学计算的基本模块。代码如下:

import cv2
import glob
import os
import tensorflow as tf
import numpy as np

2、读取图片并获取图片对应的标签。这里我们将图片共分为6个种类并存放在6个不同名称的文件夹下,每个文件夹的名字以对应的图片标签命名。读取图片并获取标签的时候,需要遍历每一个文件夹,将每个文件夹的名字当做标签存入labels集合中,然后遍历每一个文件夹下的每张图片,存入imgs集合中。
在读取图片时使用了OpenCV中的imread函数对图片进行读取,之后对图片进行的尺寸上的修改,由于我们所采用的是AlexNet模型,所以在这里我们将图片的大小全部转化为227x227大小。这里,使用了OpenCV上一个非常简单的函数实现对图片的缩放。其使用方法如下:
img = cv2.resize(dst,(m,n))
resize内有两个参数,第一个dst为目标图像,第二个是其缩减的比例或缩减后的尺寸。具体代码如下:

def read_img(path):
    cate = [path + x for x in os.listdir(path) if os.path.isdir(path + x)]
      imgs = []
      labels = []
      for idx, folder in enumerate(cate):
          for im in glob.glob(folder + '/*.bmp'):
              print('reading the images:%s' % (im))
              img = cv2.imread(im)
    img=cv2.resize(img,(w,h),interpolation=cv2.INTER_CUBIC)
              imgs.append(img)
              labels.append(idx) 
    return np.asarray(imgs, np.float32), np.asarray(labels, np.int32)

3、将数据集分为训练集和测试集
顾名思义,训练集是为模型进行训练寻找特征而设置的图片数据集,测试集是为了测试模型的训练效果准备的图片数据集。在这里,我将总图片的80%用作训练集,剩余的20%当做测试集。代码如下:

ratio = 0.8
s = np.int(num_example * ratio)
x_train = data[:s]
y_train = label[:s]
x_val = data[s:]
y_val = label[s:]

4、构建AlexNet深度神经网络框架。
AlexNet网络结构如下图所示。此处可以按照下图的网络结构进行一层一层的搭建即可。 (图片摘自网络)
图片摘自网络
由于此处网络模型代码过多,不再进行全部代码的展示,只说明构建网络中比较重要的部分函数的用法以及网络结构。
卷积神经网络模型主要有卷积层,Relu层,池化层等部分组成。
(1)构建卷积层
可以使用Tensorflow中的一个函数来完成卷积层的构建,函数用法如下:

h_conv = tf.nn.conv2d(x_image,w_conv,strides = [1,width,height,1],padding=’TYPE’)

上面这个函数是Tensorflow封装好的一个构建卷积层的函数,其中,x_image代表上一层输入的数据,w_conv指的是权重,而strides中需要填写的两个参数既为卷积核每次移动的步长的大小尺寸,最后的padding为卷积操作的填充方式,这里共有两种填充方式,SAME为全零填充,VALID为非零填充。两种填充的区别这里不再做详细说明。
(2)构建Relu层
Tensorflow同样提供了一个函数可以直接完成这个功能,不需要自己手动去写一个Relu激活函数,函数用法如下:

relu1 = tf.nn.relu(h_conv + b_conv)

此处也是有两个参数,h_conv是指上面构建的卷积层,而b_conv为偏置向量。
(3)构建池化层
在Tensorflow中构建池化层也只需要一行代码,和构建卷积层类似,但是更简单。函数用法如下:

h_pool = tf.nn.max_pool(relu1,ksize=[1,kw,kh,1],strides = [1,w,h,1],padding=’type’)

这里的relu1就是relu层的结果,ksize是指池化矩阵的大小,strides是池化层在每一维度上的滑动步长。在此,我要强调一下池化层的作用:
<1> 对卷积层所提取的信息做更进一步的降维,减少计算量。
<2> 加强图像特征的不变性,使其对对图像的抗偏移、旋转等方面的鲁棒性得到增加。
5、创建loss损失函数
这里我使用的是tf.nn.sparse_softmax_cross_entropy_with_logits函数,这个函数和tf.nn.softmax_cross_entropy_with_logits函数比较明显的区别在于它的参数labels的不同,这里的参数label是非稀疏表示的,比如表示一个3分类的一个样本的标签,稀疏表示的形式为[0,0,1]这个表示这个样本为第3个分类,而非稀疏表示就表示为2(因为从0开始算,0,1,2,就能表示三类),同理[0,1,0]就表示样本属于第二个分类,而其非稀疏表示为[2]。
tf.nn.sparse_softmax_cross_entropy_with_logits()比tf.nn.softmax_cross_entropy
_with_logits多了一步将labels稀疏化的操作。因为深度学习中,图片一般是用非稀疏的标签的,所以用tf.nn.sparse_softmax_cross_entropy_with_logits()的频率比tf.nn.softmax_cross_entropy_with_logits高。具体代码如下:

Loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=y_)

6、创建优化器。优化器我们选择的是梯度下降优化器,此处的learning_rate为学习率,这里我们取值为0.0005。当然你也可以采用一个动态的学习率,比如将设置一个关于训练步数负相关的函数。具体代码如下:

train_op = tf.train.AdamOptimizer(learning_rate=0.0005).minimize(loss)

当网络构建完后以后我们就可以进行训练了,训练的时长大约花费了25个小时,因为数据集比较小而且网络的深度也比较小,因此训练时间并不算长,跟工业应用中动辄几周的训练时间相比,这个训练时长确实也不算什么。
第三步,模型的重载调用
对于模型的重载与调用也算是整个设计比较重要的部分,因为训练出来一个网络模型,如果不知道怎么调用,或者这个模型的调用不能成功,那是没有任何意义的。所以,如果我们想要验证模型的训练结果是否准确,那么我们必须知道该如何去调用这个模型。在调用模型之前,要申明一点,由于模型训练的可移植性问题,训练最好在Linux环境下进行,这样训练的结果可以在任何机器上运行,而Windows下调用模型的路径必须和训练时保存的路径完全一样才可以。
如何调用模型呢?
首先,采用一个字典的形式存放训练数据的标签,代码如下:

_dict = {0: '狮子', 1: '公交车', 2: '跑车', 3: '草莓', 4: '老虎',5:'斑马'}

其次,我们需要将图片的尺寸和处理方式和训练时保持一致,这样才能确保最后喂入模型中的数据格式和训练时的相同,w,h分别为转化后的图片的宽和高。代码如下:

w = 227
h = 227
image = cv2.imread(imgpath)
image = cv2.resize(image,(w,h),interpolation=cv2.INTER_CUBIC)
image = np.asarray(image)
data = []
data.append(image)

最后,重载模型并喂入数据,由于训练后的模型是以文件的格式进行存储的,因此,在测试模型之前必须先要将模型读取进来才行,这里的读取模型专业上称之为重载模型或调用模型,我们使用saver=tf.train.import_meta_graph(‘模型路径’),saver.restore(sess, tf.train.latest_checkpoint(‘模型路径’))两句代码对模型进行重载,重载进来模型之后需要给模型喂入数据,也就是要将需要测试的图片输入给模型。这里使用feed_dict = {x: data}给模型喂入(传入)数据,这里的x是输入层给输入数据的命名,data就是要喂入的数据。使用sess.run运行模型。由于返回的结果标签这里采用的是键值对的形式存放的,所以最后的flower_dict[i]为类对应的键,而键所对应的值即是识别后的类别信息。具体代码如下:

with tf.Session() as sess:
    saver=tf.train.import_meta_graph('模型路径')
    saver.restore(sess, tf.train.latest_checkpoint('模型路径'))
    graph = tf.get_default_graph()
    x = graph.get_tensor_by_name("x:0")
    feed_dict = {x: data}
    logits = graph.get_tensor_by_name("logits_eval:0")
    classification_result = sess.run(logits, feed_dict)
    out = sess.run(tf.argmax(classification_result, 1))
    i = out[0]
    recogResult=dict[i]

第四步,图形界面设计实现
这里将要实现一个简单的图像识别系统界面,基本功能操作有:通过点击“Select”按钮选择要识别的图片,然后点击“确认并显示图片”按钮显示图片,点击“开始识别”按钮进行图片识别,识别的结果在文本框中显示出来,通过点击“清除”按钮可以删除对话框中的全部信息。以上就是界面要实现的基本功能,下面来看一下界面及其功能的具体实现。
1、导入用户界面设计及调用模型所要用到的包。

import tkinter.filedialog
import tkinter
from skimage import io,transform
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from PIL import ImageTk,Image
import cv2

2、定义模型调用函数,为“开始识别”按钮定义触发事件。
def recognize():
详细代码和上文中的模型重载基本相同,在此不在进行重复陈述。
3、定义文件路径选择函数,为“select”选择文件路径按钮定义触发事件。
这里使用tkinter.filedialog.askopenfilename()函数对系统进行文件路径的选择操作并返回路径信息。代码如下:

def selectPath():
    path_ =tkinter.filedialog.askopenfilename()
    path.set(path_)

4、获取“select”按钮获取到的文件路径信息将其转换为字符串。

def getuser():
    user=roadEntry.get()
    return user

5、定义清除文本信息函数,为“清除”按钮定义触发事件。
这里使用了tkinter自带的delete函数进行文本的删除操作。代码如下:

def clearText():
    resultE.delete(0, tkinter.END)

6、定义图像显示函数,为“确认并显示图片”按钮定义触发事件。
这里使用OpenCV中的imread函数读取图片,然后使用transform包中resize函数将图片转换为300x580大小用于显示,由于tkinter只能显示几种特定类型的图片,因此我们将图片转化为PNG格式,便于显示。代码如下:

def showPicture():
    imgpath = getuser()
    global photopath
    photopath = 'D:/picture.png'
    photo = plt.imread(imgpath)
    photo = transform.resize(photo, (300, 580), mode='constant')
    photo=plt.imsave(photopath, photo)
    img_open = Image.open(photopath)
    photo = ImageTk.PhotoImage(img_open)
    label.config(image=photo,compound = tkinter.CENTER)
    label.image=photo

7、创建界面组件。
(1)生成主界面
在这里,使用tkinter.TK生成主界面框,设置其初始窗口大小为940x640,设置其颜色代码为“#8080c0”,并在主界面上方添加字符串信息标签。代码如下:

root = tkinter.Tk()
root.geometry('940x640')
root.wm_title('齐鲁工业大学 电气工程与自动化学院 通信14-1 李鑫玉  毕业设计识别演示')
root.config(bg='#8080c0')

(2)添加文件选择功能
首先,需要使用 tkinter.Label创建“文件路径”标签,并设置字体格式、大小、标签背景颜色,及使用grid为标签设置排版位置。然后,使用tkinter.Entry创建用于显示文件路径的文本框,并设置字体格式、大小、标签背景颜色、宽度,及使用grid为文本矿区设置排版位置。最后,使用tkinter.Button创建“Select”按钮,并设置字体格式、大小、标签背景颜色、宽度,添加触发事件及为按钮设置排版位置。代码如下:

roadLabel = tkinter.Label(root, font=('黑体', 18), text='文件
路径:',width=10,bg='#8080c0')
roadLabel.grid(row=7, column=0)
roadEntry = tkinter.Entry(root,font=('黑体','18') ,textvariable=path, width=47)
roadEntry.grid(row=7, column=1)
Selectbutton = tkinter.Button(root, text="Select√", font=('黑体', '16'),bg='#ffa042',height=1, command=selectPath)
Selectbutton.grid(row=7, column=2)

(3)添加图片显示功能
首先,需要使用 tkinter.Label创建用于显示图片的标签,并设置字体格式、大小、标签背景颜色,及使用grid为标签设置排版位置。然后,使用tkinter.Button创建“确认并显示”按钮,并设置字体格式、大小、标签背景颜色、宽度,添加触发事件及为按钮设置排版位置。代码如下:

label = tkinter.Label(root,bg='#8080c0')
label.grid(row=3, column=1)
OkButton = tkinter.Button(root,text='确认并显示',relief=
'raise',font=('楷体','14'),command=
showPicture,bg='#ffa042').grid(row=6,column=1)

(4)添加识别功能
首先,需要使用 tkinter.Label创建“识别结果”标签,并设置字体格式、大小、标签背景颜色,及使用grid为标签设置排版位置。然后,使用tkinter.Entry创建用于显示识别结果的文本框,并设置字体格式、大小、标签背景颜色、宽度,及使用grid为文本矿区设置排版位置。最后,使用tkinter.Button创建“开始识别”按钮以及“删除”按钮,并设置字体格式、大小、标签背景颜色、宽度,添加触发事件及为按钮设置排版位置。

resultLabel = tkinter.Label(root,font=('黑体', 18), text='识别结果:',width=10,bg='#8080c0')
resultLabel.grid(row=5, column=0)
resultE = tkinter.Entry(root, font=('黑体', '66'), bg='#46a3ff', width=13)
resultE.grid(row=5, column=1)
startbutton = tkinter.Button(root, text="开始识别", font=('楷体', '18'),bg='#ffa042', 
width=20, height=2,command=recognize)
startbutton.grid(row=8, column=1)
deleteb = tkinter.Button(root, text="清除:",font=('黑体','18'),bg='#ffa042',width=7,
           height=3, command=clearText)
deleteb.grid(row=5,column=2)

界面设计程序运行结果如下图。
这里写图片描述

以上就是本次设计的大部分内容,希望对不知道怎么应用的朋友有一点帮助吧。
有需要源代码与PDF版论文以及模型文件的朋友可以找博主要喔!

如果觉得博主的文章对您有所帮助,记得关注一下呦!您的支持就是我不断更新下去的最强动力。

如有不对的地方请指正,谢谢
有需要深度学习及机器视觉相关开发环境的可加博主QQ获取,有问题请联系下方QQ直接与博主本人交流。博主会定期更新视觉相关算法使用及实际项目讲解,谢谢各位
博主QQ:2021907249

猜你喜欢

转载自blog.csdn.net/qq_41007606/article/details/81906486