目录
Tensorflow(tf)、matplotlib.pyplot(plt)
案例:求y=(3*x1+4*x2) ^2在(2,3)处的偏导(梯度:(F'x1、F'x2))
tensorflow基础
张量
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
'''
import tensorflow as tf
print(tf.__version__)
# constant
t = tf.constant([1,1,2], tf.float32)
# 打印张量信息而已,如尺寸、类型等
print(t)
Tensor to Numpy'ndarray
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
'''
import tensorflow as tf
print(tf.__version__)
# constant
t = tf.constant([1,1,2], tf.float32)
'''
Tensor转ndarray
方法一:
'''
# 创建会话
session = tf.Session()
# 张量转Numpy的数据结构ndarray
array = session.run(t)
print(type(array))
print(array)
'''
Tensor转ndarray
方法二:
'''
session = tf.Session()
array = t.eval(session = session)
print(array)
numpy'ndarray to tensor
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
'''
import tensorflow as tf
import numpy as np
print(tf.__version__)
print(np.__version__)
array = np.array([1,2,3], np.float32)
t = tf.convert_to_tensor(array,tf.float32,name='t')
print(t)
Tensorflow(tf)、matplotlib.pyplot(plt)
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
'''
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
print(tf.__version__)
print(np.__version__)
# "图片文件解码为Tensor"
image = tf.read_file("test.png", 'r')
image_tensor = tf.image.decode_jpeg(image)
# "图片张量的形状"
shape = tf.shape(image_tensor)
# Tensor转ndarray
session = tf.Session()
image_ndarray = image_tensor.eval(session=session)
# 显示ndarray
plt.imshow(image_ndarray)
plt.show()
案例:求y=(3*x1+4*x2) ^2在(2,3)处的偏导(梯度:(F'x1、F'x2))
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
求 Y = (3*X1+4*X2)^2 在(3,2)处的偏导
[3,4]*[X1,X2]'
'''
import tensorflow as tf
import numpy as np
# 常量
w = tf.constant([[3,4]], tf.float32)
# 传入变量
x = tf.placeholder(tf.float32, (2,1))
# w*x
y = tf.matmul(w, x)
# 平方和
F = tf.pow(y, 2)
# 求梯度
grads = tf.gradients(F, x)
# 会话
session = tf.Session()
print(session.run(grads, {x:np.array([[2],[3]])}))
三个参数:张量、常量、占位符、变量
占位符:占位符是计算图等着喂数据的地方。告诉系统这里会有一个这种格式的张量,但是还没有传入具体的值。
常量:不可更改的张量。
变量:
输入输出数据在tf中是用placeholder占位符来定义的,网络参数是用tf.Variable来定义的。
y=3*x1+4*x2——》[3,4]*[x1,x2]'——》张量[3,4]、占位符[x1,x2]
y=w*x——》[w]*[x]——》变量[w]、占位符[x]
# 占位符
x = tf.placeholder(tf.float32,[2,none],name='x')
x = tf.placeholder(tf.float32,(2,1))
# 常量\张量
x = tf.constant([1,2,3],tf.float32)
# 变量————创建Variable对象
x = tf.Variable(tf.constant([2,3],tf.float32))
session = tf.Session()
# 必须进行初始化,才能使用Variable对象的值
session.run(tf.global_variable_initializer())
案例——求极小值
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
求 Y = (X-1)^2 的最小值点
X:变量、Y(X)函数、利用梯度下降求最小值的点
'''
import tensorflow as tf
import numpy as np
x = tf.Variable(4, dtype=tf.float32)
y = tf.pow(x-1, 2)
poti = tf.train.GradientDescentOptimizer(0.25).minimize(y)
session = tf.Session()
session.run(tf.global_variables_initializer())
for i in range(3):
session.run(poti)
print(session.run(x))
案例——梯度下降求最小值点
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
求 Y = (X-1)^2 的最小值点
X:变量、 Y(X) 函数、利用梯度下降求最小值的点
'''
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import math
x = tf.Variable(20, dtype=tf.float32)
y = tf.pow(x-1, 2)
poti = tf.train.GradientDescentOptimizer(0.001).minimize(y)
# "画 y(x) 的曲线 y_value — value "
value = np.arange(-15, 17, 0.01)
y_value = np.power(value-1, 2.0)
plt.plot(value, y_value)
# "创建会话"
session = tf.Session()
session.run(tf.global_variables_initializer())
for i in range(1000):
session.run(poti)
if(i%10 == 0):
v = session.run(x)
plt.plot(v, math.pow(v-1, 2.0), 'go')
print('第 %d 次迭代: %f'%(i+1, v))
plt.show()
案例——单图输入(卷积、偏置、激活、池化、拉伸示例)
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
图*卷积核+偏置+激活+池化+拉伸
'''
import tensorflow as tf
# 输入
# 1个【】,各包含3个【】,*各包含3个【】, *各包含2个值
# 尺寸:1*3*3*2
input_tensor = tf.constant(
[
[
[
[2,5],
[3,3],
[8,2]
],
[
[6,1],
[1,2],
[5,4]
],
[
[7,9],
[2,8],
[1,3]
]
]
], tf.float32)
print(input_tensor)
session = tf.Session()
print(session.run(input_tensor))
# 卷积核
# 2【】,*各包含2【】,*各包含2【】,*各包含3值
# 尺寸:2*2*2*3
kernel = tf.constant(
[
[
[
[-1,1,0],
[1,-1,-1]
],
[
[0,0,-1],
[0,0,0]
]
],
[
[
[0,0,0],
[0,0,1]
],
[
[1,-1,1],
[-1,1,0]
]
]
],tf.float32
)
print(kernel)
session = tf.Session()
print(session.run(kernel))
# 卷积
conv2d = tf.nn.conv2d(input_tensor, kernel, (1, 1, 1, 1), 'SAME')
print(conv2d)
session = tf.Session()
print(session.run(conv2d))
# 偏置
bias = tf.constant([1, 2, 3], tf.float32)
conv2d_add_bias = tf.add(conv2d, bias)
print(conv2d_add_bias)
# 激活
active = tf.nn.relu(conv2d_add_bias)
print(active)
# 池化
active_maxPool = tf.nn.max_pool(active, (1,2,2,1), (1,1,1,1), 'VALID')
print(active_maxPool)
# 拉伸(shape的长、宽、高)
shape = active_maxPool.get_shape()
num = shape[1].value*shape[2].value*shape[3].value
flatten = tf.reshape(active_maxPool, [-1,num])
# 打印结果
session = tf.Session()
print(session.run(flatten))
案例——多图输入(卷积、偏置、激活、池化、拉伸示例)
# coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
多个图*卷积核+偏置+激活+池化+拉伸
'''
import tensorflow as tf
import numpy as np
# 输入
# 1个【】,各包含3个【】,*各包含3个【】, *各包含2个值
# 尺寸:1*3*3*2
input_tensor = tf.placeholder(tf.float32, [None, 3, 3, 2])
# 卷积核
# 2【】,*各包含2【】,*各包含2【】,*各包含3值
# 尺寸:2*2*2*3
kernel = tf.constant(
[
[
[
[-1,1,0],
[1,-1,-1]
],
[
[0,0,-1],
[0,0,0]
]
],
[
[
[0,0,0],
[0,0,1]
],
[
[1,-1,1],
[-1,1,0]
]
]
],tf.float32
)
print(kernel)
session = tf.Session()
print(session.run(kernel))
# 卷积
conv2d = tf.nn.conv2d(input_tensor, kernel, (1, 1, 1, 1), 'SAME')
# 偏置
bias = tf.constant([1, 2, 3], tf.float32)
conv2d_add_bias = tf.add(conv2d, bias)
print(conv2d_add_bias)
# 激活
active = tf.nn.relu(conv2d_add_bias)
print(active)
# 池化
active_maxPool = tf.nn.max_pool(active, (1,2,2,1), (1,1,1,1), 'VALID')
print(active_maxPool)
# 拉伸(shape的长、宽、高)
shape = active_maxPool.get_shape()
num = shape[1].value*shape[2].value*shape[3].value
flatten = tf.reshape(active_maxPool, [-1,num])
# 打印结果
session = tf.Session()
print(session.run(flatten, feed_dict={input_tensor:np.array(
[
[
[[2,5],[3,3],[3,3]],
[[3,3],[3,3],[3,3]],
[[3,3],[3,33],[3,3]]
],
[
[[3,3],[3,3],[3,3]],
[[3,43],[23,3],[3,3]],
[[3,3],[3,3],[3,3]]
],
[
[[3,53],[13,3],[3,3]],
[[3,73],[3,3],[3,3]],
[[3,3],[3,83],[3,3]]
]
]
)}))
示例:制作tfrecord数据
data里面有:
#coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
创建 tfrecord 类型的数据流
'''
import os
import tensorflow as tf
from PIL import Image
imgpath = '.\\data\\'
# 当前文件夹
cwd = os.getcwd()
# 指定文件夹的子文件夹名【'**','**'】
classes = os.listdir(cwd + imgpath)
# 创建 TFRecordWriter对象 和 "train.tfrecords"文件
writer = tf.python_io.TFRecordWriter("train.tfrecords")
for index, name in enumerate(classes):
# index:第几个{0,1,...}
print(index)
# name:子文件夹的名称{**,**,**,...}
print(name)
# 每类图的绝对路径
class_path = cwd + imgpath + name + "\\"
print(class_path)
if os.path.isdir(class_path):
for img_name in os.listdir(class_path):
print(img_name)
img_path = class_path + img_name
# 读取每个图片并进行尺寸调整
image = Image.open(img_path)
image = image.resize((224, 224))
# "二进制化"
img_raw = image.tobytes()
# "存储内容设置"
example = tf.train.Example(
features=
tf.train.Features(
feature=
{
'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[int(name)])),
'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
}#特征序列为:标签+图片数据
)
)
# "字符串序列化后写入文件"
writer.write(example.SerializeToString())
# 关闭文件流
writer.close()
封装子程序
#coding=utf-8
'''
Copyright 2019
License()
Author:
This is ...
创建 tfrecord 类型的数据流
'''
import os
import tensorflow as tf
from PIL import Image
def creat_tfrecord(imgpath):
# 当前文件夹
cwd = os.getcwd()
# 指定文件夹的子文件夹名【'**','**'】
classes = os.listdir(cwd + imgpath)
# 创建 TFRecordWriter对象 和 "train.tfrecords"文件
writer = tf.python_io.TFRecordWriter("train.tfrecords")
for index, name in enumerate(classes):
# index:第几个{0,1,...}
print(index)
# name:子文件夹的名称{**,**,**,...}
print(name)
# 每类图的绝对路径
class_path = cwd + imgpath + name + "\\"
print(class_path)
if os.path.isdir(class_path):
for img_name in os.listdir(class_path):
print(img_name)
img_path = class_path + img_name
# 读取每个图片并进行尺寸调整
image = Image.open(img_path)
image = image.resize((224, 224))
# "二进制化"
img_raw = image.tobytes()
# "存储内容设置"
example = tf.train.Example(
features=
tf.train.Features(
feature=
{
'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[int(name)])),
'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
}#特征序列为:标签+图片数据
)
)
# "字符串序列化后写入文件"
writer.write(example.SerializeToString())
# 关闭文件流
writer.close()
if __name__ == '__main__':
imgpath = '.\\data\\'
creat_tfrecord(imgpath)
tensorflow实验
打包exe识别模型(训练好的)——测试成功
'''
第一步:主函数
功能:
利用训练好的模型,进行实时手写体识别
作者:yuhansgg
博客: https://blog.csdn.net/u011389706
日期: 2018/08/06
'''
import sys
from PyQt5.QtWidgets import QApplication
from MyMnistWindow import MyMnistWindow
sys.setrecursionlimit(5000)
if __name__ == "__main__":
app = QApplication(sys.argv)
mymnist = MyMnistWindow()
mymnist.show()
app.exec_()
'''
第二步:界面
功能:
利用训练好的模型,进行实时手写体识别
作者:yuhansgg
博客: https://blog.csdn.net/u011389706
日期: 2018/08/06
'''
import tensorflow as tf
from PyQt5.QtWidgets import (QWidget, QPushButton, QLabel)
from PyQt5.QtGui import (QPainter, QPen, QFont)
from PyQt5.QtCore import Qt
from PIL import ImageGrab, Image
class MyMnistWindow(QWidget):
def __init__(self):
super(MyMnistWindow, self).__init__()
self.resize(284, 330) # resize设置宽高
self.move(100, 100) # move设置位置
self.setWindowFlags(Qt.FramelessWindowHint) # 窗体无边框
#setMouseTracking设置为False,否则不按下鼠标时也会跟踪鼠标事件
self.setMouseTracking(False)
self.pos_xy = [] #保存鼠标移动过的点
# 添加一系列控件
self.label_draw = QLabel('', self)
self.label_draw.setGeometry(2, 2, 280, 280)
self.label_draw.setStyleSheet("QLabel{border:1px solid black;}")
self.label_draw.setAlignment(Qt.AlignCenter)
self.label_result_name = QLabel('识别结果:', self)
self.label_result_name.setGeometry(2, 290, 60, 35)
self.label_result_name.setAlignment(Qt.AlignCenter)
self.label_result = QLabel(' ', self)
self.label_result.setGeometry(64, 290, 35, 35)
self.label_result.setFont(QFont("Roman times", 8, QFont.Bold))
self.label_result.setStyleSheet("QLabel{border:1px solid black;}")
self.label_result.setAlignment(Qt.AlignCenter)
self.btn_recognize = QPushButton("识别", self)
self.btn_recognize.setGeometry(110, 290, 50, 35)
self.btn_recognize.clicked.connect(self.btn_recognize_on_clicked)
self.btn_clear = QPushButton("清空", self)
self.btn_clear.setGeometry(170, 290, 50, 35)
self.btn_clear.clicked.connect(self.btn_clear_on_clicked)
self.btn_close = QPushButton("关闭", self)
self.btn_close.setGeometry(230, 290, 50, 35)
self.btn_close.clicked.connect(self.btn_close_on_clicked)
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
pen = QPen(Qt.black, 30, Qt.SolidLine)
painter.setPen(pen)
'''
首先判断pos_xy列表中是不是至少有两个点了
然后将pos_xy中第一个点赋值给point_start
利用中间变量pos_tmp遍历整个pos_xy列表
point_end = pos_tmp
判断point_end是否是断点,如果是
point_start赋值为断点
continue
判断point_start是否是断点,如果是
point_start赋值为point_end
continue
画point_start到point_end之间的线
point_start = point_end
这样,不断地将相邻两个点之间画线,就能留下鼠标移动轨迹了
'''
if len(self.pos_xy) > 1:
point_start = self.pos_xy[0]
for pos_tmp in self.pos_xy:
point_end = pos_tmp
if point_end == (-1, -1):
point_start = (-1, -1)
continue
if point_start == (-1, -1):
point_start = point_end
continue
painter.drawLine(point_start[0], point_start[1], point_end[0], point_end[1])
point_start = point_end
painter.end()
def mouseMoveEvent(self, event):
'''
按住鼠标移动事件:将当前点添加到pos_xy列表中
调用update()函数在这里相当于调用paintEvent()函数
每次update()时,之前调用的paintEvent()留下的痕迹都会清空
'''
#中间变量pos_tmp提取当前点
pos_tmp = (event.pos().x(), event.pos().y())
#pos_tmp添加到self.pos_xy中
self.pos_xy.append(pos_tmp)
self.update()
def mouseReleaseEvent(self, event):
'''
重写鼠标按住后松开的事件
在每次松开后向pos_xy列表中添加一个断点(-1, -1)
然后在绘画时判断一下是不是断点就行了
是断点的话就跳过去,不与之前的连续
'''
pos_test = (-1, -1)
self.pos_xy.append(pos_test)
self.update()
def btn_recognize_on_clicked(self):
bbox = (104, 104, 380, 380)
im = ImageGrab.grab(bbox) # 截屏,手写数字部分
im = im.resize((28, 28), Image.ANTIALIAS) # 将截图转换成 28 * 28 像素
recognize_result = self.recognize_img(im) # 调用识别函数
self.label_result.setText(str(recognize_result)) # 显示识别结果
self.update()
def btn_clear_on_clicked(self):
self.pos_xy = []
self.label_result.setText('')
self.update()
def btn_close_on_clicked(self):
self.close()
def recognize_img(self, img):
myimage = img.convert('L') # 转换成灰度图
tv = list(myimage.getdata()) # 获取图片像素值
tva = [(255 - x) * 1.0 / 255.0 for x in tv] # 转换像素范围到[0 1], 0是纯白 1是纯黑
init = tf.global_variables_initializer()
saver = tf.train.Saver # 不带括号
with tf.Session() as sess:
sess.run(init)
saver = tf.train.import_meta_graph('minst_cnn_model.ckpt.meta') # 载入模型结构
saver.restore(sess, 'minst_cnn_model.ckpt') # 载入模型参数
graph = tf.get_default_graph() # 加载计算图
x = graph.get_tensor_by_name("x:0") # 从模型中读取占位符变量
keep_prob = graph.get_tensor_by_name("keep_prob:0")
y_conv = graph.get_tensor_by_name("y_conv:0") # 关键的一句 从模型中读取占位符变量
prediction = tf.argmax(y_conv, 1)
predint = prediction.eval(feed_dict={x: [tva], keep_prob: 1.0}, session=sess) # feed_dict输入数据给placeholder占位符
print(predint[0])
return predint[0]
第三步:【制作exe文件】
第一步:
pip install pyinstaller
第二步:
pyinstaller main.py
最后:
pyinstaller -F main.spec 打包成功
备注:
除了程序打包,其他资源(图、模型等要手动添加到exe文件夹)
问题解决:
出现:'str' object has no attribute 'items'
解决:
管理员:pip install -U --pre setuptools
出现:RecursionError: maximum recursion depth exceeded
解决:
在此总结下解决步骤:
1)pyinstaller -F xxx.py
这一步肯定会报上述错误导致失败,但是会产生一个xxx.spec文件
2)在xxx.spec文件中增加两行(添加在原文件第二行):
import sys
sys.setrecursionlimit(5000)
3)pyinstaller xxx.spec
出现: Cannot find existing PyQt5 plugin directories
Paths checked:
解决:pip install PyQt5
——————————————————————————————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————————————————————————
Tensorflow(3):创建画板,实时在线手写体识别--终极篇(PyQt5)
文章链接:https://blog.csdn.net/u011389706/article/details/81460820
需要的库: Python3 + Tensorflow + PyQt5 + PIL
文件说明:
MyMnistWindow.py--------GUI类(画板)
main.py-----------------主程序
mnist_cnn_model.ckpt----训练好的手写体模型,识别率可以达到99.2% (模型训练方法参见博客:https://blog.csdn.net/u011389706/article/details/81455750)
资源文件(整体项目):我的实验结果-PYQT_MNIST系统
VGG16_tfrecords_17分类
项目地址(包含图片、训练、测试):pan.baidu.com/s/1q1gtXscbTXhEls1YlTSWtA
随意定义一个卷积网络进行四分类
从我的github项目里面下载:https://github.com/DJdongbudong/Tensorflow-cnn
改正后的项目:https://pan.baidu.com/s/1sK5Hel_ohENz9G1zyeg51A
包含:data-{0,1,2,3}、logs-{checkpoint等训练结果保存}、tfrecords.py、model.py、train.py、eval.py
data-{0,1,2,3}
logs-{checkpoint等训练结果保存}
子函数:
数据读取:tfrecords.py
网络模型:model.py
主函数:
train.py
测试函数:
eval.py
训练:
python train.py
{data的数据生成tfrecords格式、训练train.tfrecords数据、保存到logs}
python eval.py
{加载logs的模型、测试test的数据}
编译器的操作
经常出现空格和tab的格式问题:
cnn 2分类
程序比较清晰:
常用参数设置—》输入数据模块—》卷积网络模块—》训练模块
程序位置:
dir/main.py、dir/data/0/**.jpg、dir/data/1/**.jpg
卷积池化:
不足:
没有用tfrecord数据类型,训练受硬盘读取速度影响,之后进行改进。
# coding:utf-8
import os
import numpy as np
import tensorflow as tf
# 变量声明
N_CLASSES = 2 # 分类类别
IMG_W = 28 # resize图像,太大的话训练时间久
IMG_H = 28
BATCH_SIZE = 20 # 每个batch要放多少张图片
CAPACITY = 200 # 一个队列最大多少
MAX_STEP = 10000 # 一般大于10K
learning_rate = 0.0001 # 一般小于0.0001
class inputData:
# 训练数据及标签
def get_files(filename):
class_train = []
label_train = []
num_pic = 0
num_class = 0
for train_class in os.listdir(filename):
num_class = num_class + 1
for file in os.listdir(filename + train_class):
num_pic = num_pic + 1
class_train.append(filename + train_class + '/' + file)
# print (class_train)
label_train.append(train_class)
# print (label_train)
# 打印出提取图片的情况,检测是否正确提取
print("There are %c class\n" % (train_class), end="")
print("There are %d files\n" % (num_pic))
temp = np.array([class_train, label_train])
temp = temp.transpose()
# shuffle the samples随机打乱数据
np.random.shuffle(temp)
# after transpose, images is in dimension 0 and label in dimension 1
image_list = list(temp[:, 0])
label_list = list(temp[:, 1])
label_list = [int(i) for i in label_list]
# print(label_list)
return image_list, label_list
# 将image和label转为list格式数据,因为后边用到的的一些tensorflow函数接收的是list格式数据
# 为了方便网络的训练,输入数据进行batch处理
# image_W, image_H, :图像高度和宽度
# batch_size:每个batch要放多少张图片
# capacity:一个队列最大多少
# 获取批次batch
def get_batches(image, label, resize_w, resize_h, batch_size, capacity):
# 转换类型
image = tf.cast(image, tf.string)
label = tf.cast(label, tf.int64)
# tensor生成器 制作 一个输入tensor
queue = tf.train.slice_input_producer([image, label])
label = queue[1]
image_c = tf.read_file(queue[0])
# step2:将图像解码,使用相同类型的图像
image = tf.image.decode_jpeg(image_c, channels=3)
# step3:数据预处理,对图像进行旋转、缩放、裁剪、归一化等操作,让计算出的模型更健壮。
image = tf.image.resize_image_with_crop_or_pad(image, resize_w, resize_h)
# (x - mean) / adjusted_stddev对resize后的图片进行标准化处理
image = tf.image.per_image_standardization(image)
# step4:生成batch
image_batch, label_batch = tf.train.batch([image, label],
batch_size=batch_size,
num_threads=64,
capacity=capacity)
images_batch = tf.cast(image_batch, tf.float32)
labels_batch = tf.reshape(label_batch, [batch_size])
# 获取两个batch,两个batch即为传入神经网络的数据
return images_batch, labels_batch
# 函数申明
def conv2d(x, W):
# 卷积遍历各方向步数为1,SAME:边缘外自动补0,遍历相乘
# padding 一般只有两个值
# 卷积层后输出图像大小为:(W+2P-f)/stride+1并向下取整
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x, name):
# 池化卷积结果(conv2d)池化层采用kernel大小为3*3,步数也为2,SAME:周围补0,取最大值。数据量缩小了4倍
# x 是 CNN 第一步卷积的输出量,其shape必须为[batch, height, weight, channels];
# ksize 是池化窗口的大小, shape为[batch, height, weight, channels]
# stride 步长,一般是[1,stride, stride,1]
# 池化层输出图像的大小为(W-f)/stride+1,向上取整
return tf.nn.max_pool(x, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)
def weight_variable(shape, n):
# tf.truncated_normal(shape, mean, stddev)这个函数产生正态分布,均值和标准差自己设定。
# shape表示生成张量的维度,mean是均值
# stddev是标准差,,默认最大为1,最小为-1,均值为0
initial = tf.truncated_normal(shape, stddev=n, dtype=tf.float32)
return initial
def bias_variable(shape):
# 创建一个结构为shape矩阵也可以说是数组shape声明其行列,初始化所有值为0.1
initial = tf.constant(0.1, shape=shape, dtype=tf.float32)
return initial
class MODEL:
def model(images, batch_size, n_classes):
# 搭建网络
# 第一层卷积
# 第一二参数值得卷积核尺寸大小,即patch;第三个参数是通道数;第四个是卷积核个数
with tf.variable_scope('conv1') as scope:
# 所谓名字的scope,指当绑定了一个名字到一个对象的时候,该名字在程序文本中的可见范围
w_conv1 = tf.Variable(weight_variable([3, 3, 3, 64], 1.0), name='weights', dtype=tf.float32)
b_conv1 = tf.Variable(bias_variable([64]), name='biases', dtype=tf.float32) # 64个偏置值
# tf.nn.bias_add 是 tf.add 的一个特例:tf.add(tf.matmul(x, w), b) == tf.matmul(x, w) + b
# h_conv1 = tf.nn.relu(tf.nn.bias_add(conv2d(images, w_conv1), b_conv1), name=scope.name)
h_conv1 = tf.nn.relu(conv2d(images, w_conv1) + b_conv1, name='conv1') # 得到128*128*64(假设原始图像是128*128)
# 第一层池化
# 3x3最大池化,步长strides为2,池化后执行lrn()操作,局部响应归一化,增强了模型的泛化能力。
# tf.nn.lrn(input,depth_radius=None,bias=None,alpha=None,beta=None,name=None)
with tf.variable_scope('pooling1_lrn') as scope:
pool1 = max_pool_2x2(h_conv1, 'pooling1') # 得到64*64*64
norm1 = tf.nn.lrn(pool1, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm1')
# 第二层卷积
# 32个3x3的卷积核(16通道),padding=’SAME’,表示padding后卷积的图与原图尺寸一致,激活函数relu()
with tf.variable_scope('conv2') as scope:
w_conv2 = tf.Variable(weight_variable([3, 3, 64, 32], 0.1), name='weights', dtype=tf.float32)
b_conv2 = tf.Variable(bias_variable([32]), name='biases', dtype=tf.float32) # 32个偏置值
h_conv2 = tf.nn.relu(conv2d(norm1, w_conv2) + b_conv2, name='conv2') # 得到64*64*32
# 第二层池化
# 3x3最大池化,步长strides为2,池化后执行lrn()操作
with tf.variable_scope('pooling2_lrn') as scope:
pool2 = max_pool_2x2(h_conv2, 'pooling2') # 得到32*32*32
norm2 = tf.nn.lrn(pool2, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm2')
# 第三层卷积
# 16个3x3的卷积核(16通道),padding=’SAME’,表示padding后卷积的图与原图尺寸一致,激活函数relu()
with tf.variable_scope('conv3') as scope:
w_conv3 = tf.Variable(weight_variable([3, 3, 32, 16], 0.1), name='weights', dtype=tf.float32)
b_conv3 = tf.Variable(bias_variable([16]), name='biases', dtype=tf.float32) # 16个偏置值
h_conv3 = tf.nn.relu(conv2d(norm2, w_conv3) + b_conv3, name='conv3') # 得到32*32*16
# 第三层池化
# 3x3最大池化,步长strides为2,池化后执行lrn()操作
with tf.variable_scope('pooling3_lrn') as scope:
pool3 = max_pool_2x2(h_conv3, 'pooling3') # 得到16*16*16
norm3 = tf.nn.lrn(pool3, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm3')
# 第四层全连接层
# 128个神经元,将之前pool层的输出reshape成一行,激活函数relu()
with tf.variable_scope('local3') as scope:
reshape = tf.reshape(norm3, shape=[batch_size, -1])
dim = reshape.get_shape()[1].value
w_fc1 = tf.Variable(weight_variable([dim, 128], 0.005), name='weights', dtype=tf.float32)
b_fc1 = tf.Variable(bias_variable([128]), name='biases', dtype=tf.float32)
h_fc1 = tf.nn.relu(tf.matmul(reshape, w_fc1) + b_fc1, name=scope.name)
# 第五层全连接层
# 128个神经元,激活函数relu()
with tf.variable_scope('local4') as scope:
w_fc2 = tf.Variable(weight_variable([128, 128], 0.005), name='weights', dtype=tf.float32)
b_fc2 = tf.Variable(bias_variable([128]), name='biases', dtype=tf.float32)
h_fc2 = tf.nn.relu(tf.matmul(h_fc1, w_fc2) + b_fc1, name=scope.name)
# 对卷积结果执行dropout操作
# keep_prob = tf.placeholder(tf.float32)
h_fc2_dropout = tf.nn.dropout(h_fc2, 0.5)
# tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None)
# 第二个参数keep_prob: 设置神经元被选中的概率,在初始化时keep_prob是一个占位符
# Softmax回归层
# 将前面的FC层输出,做一个线性回归,计算出每一类的得分,在这里是2类,所以这个层输出的是两个得分。
with tf.variable_scope('softmax_linear') as scope:
weights = tf.Variable(weight_variable([128, n_classes], 0.005), name='softmax_linear', dtype=tf.float32)
biases = tf.Variable(bias_variable([n_classes]), name='biases', dtype=tf.float32)
softmax_linear = tf.add(tf.matmul(h_fc2_dropout, weights), biases, name='softmax_linear')
# softmax_linear = tf.nn.softmax(tf.add(tf.matmul(h_fc2_dropout, weights), biases, name='softmax_linear'))
return softmax_linear
# 最后返回softmax层的输出
# loss计算
# 传入参数:logits,网络计算输出值。labels,真实值,在这里是0或者1
# 返回参数:loss,损失值
def losses(logits, labels):
with tf.variable_scope('loss') as scope:
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels,
name='xentropy_per_example')
loss = tf.reduce_mean(cross_entropy, name='loss')
tf.summary.scalar(scope.name + '/loss', loss)
return loss
# loss损失值优化
# 输入参数:loss。learning_rate,学习速率。
# 返回参数:train_op,训练op,这个参数要输入sess.run中让模型去训练。
def trainning(loss, learning_rate):
with tf.name_scope('optimizer'):
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
global_step = tf.Variable(0, name='global_step', trainable=False)
train_op = optimizer.minimize(loss, global_step=global_step)
return train_op
# 评价/准确率计算
# 输入参数:logits,网络计算值。labels,标签,也就是真实值,在这里是0或者1。
# 返回参数:accuracy,当前step的平均准确率,也就是在这些batch中多少张图片被正确分类了。
def evaluation(logits, labels):
with tf.variable_scope('accuracy') as scope:
correct = tf.nn.in_top_k(logits, labels, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float16))
tf.summary.scalar(scope.name + '/accuracy', accuracy)
return accuracy
def RUN():
train_dir = './data/' # 训练样本的读入路径data\1、data\2
logs_train_dir = './logs/' # logs存储路径
'''data'''
print("数据读写中")
input = inputData
# 训练数据及标签
train, train_label = input.get_files(train_dir)
print(train_label)
# 获取批次batch
train_batch, train_label_batch = input.get_batches(train, train_label, IMG_W, IMG_H, BATCH_SIZE, CAPACITY)
print("train_batch/n")
print(train_batch)
# 训练操作定义
Model = MODEL
train_logits = Model.model(train_batch, BATCH_SIZE, N_CLASSES)
train_loss = Model.losses(train_logits, train_label_batch)
train_op = Model.trainning(train_loss, learning_rate)
train_acc = Model.evaluation(train_logits, train_label_batch)
# 这个是log汇总记录
summary_op = tf.summary.merge_all()
# 产生一个会话
sess = tf.Session()
train_writer = tf.summary.FileWriter(logs_train_dir, sess.graph)
# 产生一个saver来存储训练好的模型
saver = tf.train.Saver()
# 所有节点初始化
sess.run(tf.global_variables_initializer())
# 队列监控
coord = tf.train.Coordinator() # 设置多线程协调器
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
# 进行batch的训练
try:
# 执行MAX_STEP步的训练,一步一个batch
for step in np.arange(MAX_STEP):
if coord.should_stop():
break
# 启动以下操作节点,有个疑问,为什么train_logits在这里没有开启?
_, tra_loss, tra_acc = sess.run([train_op, train_loss, train_acc])
# 每隔50步打印一次当前的loss以及acc,同时记录log,写入writer
if step % 100 == 0:
print('Step %d, train loss = %.2f, train accuracy = %.2f%%' % (step, tra_loss, tra_acc * 100.0))
summary_str = sess.run(summary_op)
train_writer.add_summary(summary_str, step)
checkpoint_path = os.path.join(logs_train_dir, 'thing.ckpt')
saver.save(sess, checkpoint_path)
'''
# 每隔100步,保存一次训练好的模型
if (step + 1) == MAX_STEP:
checkpoint_path = os.path.join(logs_train_dir, 'thing.ckpt')
saver.save(sess, checkpoint_path, global_step=step)
'''
except tf.errors.OutOfRangeError:
print('Done training -- epoch limit reached')
finally:
coord.request_stop()
coord.join(threads)
sess.close()
if __name__ == '__main__':
RUN()
用了区分性比较强的图做试验。
推荐网址:CNN网络架构演进:从LeNet到DenseNet
(keras近代网络实现):https://www.cnblogs.com/skyfsm/p/8451834.html
卷积神经网络图片识别关键发展
2012年alexnet:relu激活函数,lrn层,dropdout,重叠最大池化,数据增强
2014年vggnet:两个3x3卷积代替一个5x5卷积,lrn层用处不大,卷积层越深效果基本越好,1x1卷积核性价比很高
2014年的Inception net v1:全局平均池化层+维度变换代替最后一个全链接层,多分支小神经网络结构堆叠成大网络,辅助分类节点
2015年的Inception net v2:学习vggnet两个3x3代替一个5x5,提出batch normalization大幅加速学习
2015年的Inception net v3:用1*7和7*1代替7*7比vgg的那种方法效果更好
2015年的ResNet:加入了skip方式,残差学习模块解决了超深神经网络的学习问题
2016年的Inception net v4:参考了ResNet,加入了skip方式
这此使用tensorflow编码google的Inception net v3,采用tensorflow的slim模块大幅建华设计代码量
slim模块教程:https://www.2cto.com/kf/201706/649266.html
完整代码:https://github.com/joliph/tensorflow/blob/master/InceptionV3.py
我的tensorflow系列代码仓库:https://github.com/joliph/tensorflow/
科普文章 为什么batch normalization效果这么明显:https://www.zhihu.com/question/38102762
用slim简化网络层代码后光网络设计
CNN 多分类
# coding:utf-8
import os
import numpy as np
import tensorflow as tf
# 变量声明
N_CLASSES = 6 # 分类类别
IMG_W = 28 # resize图像,太大的话训练时间久
IMG_H = 28
BATCH_SIZE = 20 # 每个batch要放多少张图片
CAPACITY = 200 # 一个队列最大多少
MAX_STEP = 10000 # 一般大于10K
learning_rate = 0.0001 # 一般小于0.0001
class inputData:
# 训练数据及标签
def get_files(filename):
class_train = []
label_train = []
num_pic = 0
num_class = 0
for train_class in os.listdir(filename):
num_class = num_class + 1
for file in os.listdir(filename + train_class):
num_pic = num_pic + 1
class_train.append(filename + train_class + '/' + file)
# print (class_train)
label_train.append(train_class)
# print (label_train)
# 打印出提取图片的情况,检测是否正确提取
print("There are %d class\n" % (num_class), end="")
print("There are %d files\n" % (num_pic))
temp = np.array([class_train, label_train])
temp = temp.transpose()
# shuffle the samples随机打乱数据
np.random.shuffle(temp)
# after transpose, images is in dimension 0 and label in dimension 1
image_list = list(temp[:, 0])
label_list = list(temp[:, 1])
label_list = [int(i) for i in label_list]
# print(label_list)
return image_list, label_list
# 将image和label转为list格式数据,因为后边用到的的一些tensorflow函数接收的是list格式数据
# 为了方便网络的训练,输入数据进行batch处理
# image_W, image_H, :图像高度和宽度
# batch_size:每个batch要放多少张图片
# capacity:一个队列最大多少
# 获取批次batch
def get_batches(image, label, resize_w, resize_h, batch_size, capacity):
# 转换类型
image = tf.cast(image, tf.string)
label = tf.cast(label, tf.int64)
# tensor生成器 制作 一个输入tensor
queue = tf.train.slice_input_producer([image, label])
label = queue[1]
image_c = tf.read_file(queue[0])
# step2:将图像解码,使用相同类型的图像
image = tf.image.decode_jpeg(image_c, channels=3)
# step3:数据预处理,对图像进行旋转、缩放、裁剪、归一化等操作,让计算出的模型更健壮。
image = tf.image.resize_image_with_crop_or_pad(image, resize_w, resize_h)
# (x - mean) / adjusted_stddev对resize后的图片进行标准化处理
image = tf.image.per_image_standardization(image)
# step4:生成batch
image_batch, label_batch = tf.train.batch([image, label],
batch_size=batch_size,
num_threads=64,
capacity=capacity)
images_batch = tf.cast(image_batch, tf.float32)
labels_batch = tf.reshape(label_batch, [batch_size])
# 获取两个batch,两个batch即为传入神经网络的数据
return images_batch, labels_batch
# 函数申明
def conv2d(x, W):
# 卷积遍历各方向步数为1,SAME:边缘外自动补0,遍历相乘
# padding 一般只有两个值
# 卷积层后输出图像大小为:(W+2P-f)/stride+1并向下取整
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x, name):
# 池化卷积结果(conv2d)池化层采用kernel大小为3*3,步数也为2,SAME:周围补0,取最大值。数据量缩小了4倍
# x 是 CNN 第一步卷积的输出量,其shape必须为[batch, height, weight, channels];
# ksize 是池化窗口的大小, shape为[batch, height, weight, channels]
# stride 步长,一般是[1,stride, stride,1]
# 池化层输出图像的大小为(W-f)/stride+1,向上取整
return tf.nn.max_pool(x, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)
def weight_variable(shape, n):
# tf.truncated_normal(shape, mean, stddev)这个函数产生正态分布,均值和标准差自己设定。
# shape表示生成张量的维度,mean是均值
# stddev是标准差,,默认最大为1,最小为-1,均值为0
initial = tf.truncated_normal(shape, stddev=n, dtype=tf.float32)
return initial
def bias_variable(shape):
# 创建一个结构为shape矩阵也可以说是数组shape声明其行列,初始化所有值为0.1
initial = tf.constant(0.1, shape=shape, dtype=tf.float32)
return initial
class MODEL:
def model(images, batch_size, n_classes):
# 搭建网络
# 第一层卷积
# 第一二参数值得卷积核尺寸大小,即patch;第三个参数是通道数;第四个是卷积核个数
with tf.variable_scope('conv1') as scope:
# 所谓名字的scope,指当绑定了一个名字到一个对象的时候,该名字在程序文本中的可见范围
w_conv1 = tf.Variable(weight_variable([3, 3, 3, 64], 1.0), name='weights', dtype=tf.float32)
b_conv1 = tf.Variable(bias_variable([64]), name='biases', dtype=tf.float32) # 64个偏置值
# tf.nn.bias_add 是 tf.add 的一个特例:tf.add(tf.matmul(x, w), b) == tf.matmul(x, w) + b
# h_conv1 = tf.nn.relu(tf.nn.bias_add(conv2d(images, w_conv1), b_conv1), name=scope.name)
h_conv1 = tf.nn.relu(conv2d(images, w_conv1) + b_conv1, name='conv1') # 得到128*128*64(假设原始图像是128*128)
# 第一层池化
# 3x3最大池化,步长strides为2,池化后执行lrn()操作,局部响应归一化,增强了模型的泛化能力。
# tf.nn.lrn(input,depth_radius=None,bias=None,alpha=None,beta=None,name=None)
with tf.variable_scope('pooling1_lrn') as scope:
pool1 = max_pool_2x2(h_conv1, 'pooling1') # 得到64*64*64
norm1 = tf.nn.lrn(pool1, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm1')
# 第二层卷积
# 32个3x3的卷积核(16通道),padding=’SAME’,表示padding后卷积的图与原图尺寸一致,激活函数relu()
with tf.variable_scope('conv2') as scope:
w_conv2 = tf.Variable(weight_variable([3, 3, 64, 32], 0.1), name='weights', dtype=tf.float32)
b_conv2 = tf.Variable(bias_variable([32]), name='biases', dtype=tf.float32) # 32个偏置值
h_conv2 = tf.nn.relu(conv2d(norm1, w_conv2) + b_conv2, name='conv2') # 得到64*64*32
# 第二层池化
# 3x3最大池化,步长strides为2,池化后执行lrn()操作
with tf.variable_scope('pooling2_lrn') as scope:
pool2 = max_pool_2x2(h_conv2, 'pooling2') # 得到32*32*32
norm2 = tf.nn.lrn(pool2, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm2')
# 第三层卷积
# 16个3x3的卷积核(16通道),padding=’SAME’,表示padding后卷积的图与原图尺寸一致,激活函数relu()
with tf.variable_scope('conv3') as scope:
w_conv3 = tf.Variable(weight_variable([3, 3, 32, 16], 0.1), name='weights', dtype=tf.float32)
b_conv3 = tf.Variable(bias_variable([16]), name='biases', dtype=tf.float32) # 16个偏置值
h_conv3 = tf.nn.relu(conv2d(norm2, w_conv3) + b_conv3, name='conv3') # 得到32*32*16
# 第三层池化
# 3x3最大池化,步长strides为2,池化后执行lrn()操作
with tf.variable_scope('pooling3_lrn') as scope:
pool3 = max_pool_2x2(h_conv3, 'pooling3') # 得到16*16*16
norm3 = tf.nn.lrn(pool3, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm3')
# 第四层全连接层
# 128个神经元,将之前pool层的输出reshape成一行,激活函数relu()
with tf.variable_scope('local3') as scope:
reshape = tf.reshape(norm3, shape=[batch_size, -1])
dim = reshape.get_shape()[1].value
w_fc1 = tf.Variable(weight_variable([dim, 128], 0.005), name='weights', dtype=tf.float32)
b_fc1 = tf.Variable(bias_variable([128]), name='biases', dtype=tf.float32)
h_fc1 = tf.nn.relu(tf.matmul(reshape, w_fc1) + b_fc1, name=scope.name)
# 第五层全连接层
# 128个神经元,激活函数relu()
with tf.variable_scope('local4') as scope:
w_fc2 = tf.Variable(weight_variable([128, 128], 0.005), name='weights', dtype=tf.float32)
b_fc2 = tf.Variable(bias_variable([128]), name='biases', dtype=tf.float32)
h_fc2 = tf.nn.relu(tf.matmul(h_fc1, w_fc2) + b_fc1, name=scope.name)
# 对卷积结果执行dropout操作
# keep_prob = tf.placeholder(tf.float32)
h_fc2_dropout = tf.nn.dropout(h_fc2, 0.5)
# tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None)
# 第二个参数keep_prob: 设置神经元被选中的概率,在初始化时keep_prob是一个占位符
# Softmax回归层
# 将前面的FC层输出,做一个线性回归,计算出每一类的得分,在这里是2类,所以这个层输出的是两个得分。
with tf.variable_scope('softmax_linear') as scope:
weights = tf.Variable(weight_variable([128, n_classes], 0.005), name='softmax_linear', dtype=tf.float32)
biases = tf.Variable(bias_variable([n_classes]), name='biases', dtype=tf.float32)
softmax_linear = tf.add(tf.matmul(h_fc2_dropout, weights), biases, name='softmax_linear')
# softmax_linear = tf.nn.softmax(tf.add(tf.matmul(h_fc2_dropout, weights), biases, name='softmax_linear'))
return softmax_linear
# 最后返回softmax层的输出
# loss计算
# 传入参数:logits,网络计算输出值。labels,真实值,在这里是0或者1
# 返回参数:loss,损失值
def losses(logits, labels):
with tf.variable_scope('loss') as scope:
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels,
name='xentropy_per_example')
loss = tf.reduce_mean(cross_entropy, name='loss')
tf.summary.scalar(scope.name + '/loss', loss)
return loss
# loss损失值优化
# 输入参数:loss。learning_rate,学习速率。
# 返回参数:train_op,训练op,这个参数要输入sess.run中让模型去训练。
def trainning(loss, learning_rate):
with tf.name_scope('optimizer'):
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
global_step = tf.Variable(0, name='global_step', trainable=False)
train_op = optimizer.minimize(loss, global_step=global_step)
return train_op
# 评价/准确率计算
# 输入参数:logits,网络计算值。labels,标签,也就是真实值,在这里是0或者1。
# 返回参数:accuracy,当前step的平均准确率,也就是在这些batch中多少张图片被正确分类了。
def evaluation(logits, labels):
with tf.variable_scope('accuracy') as scope:
correct = tf.nn.in_top_k(logits, labels, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float16))
tf.summary.scalar(scope.name + '/accuracy', accuracy)
return accuracy
def RUN():
train_dir = './data/' # 训练样本的读入路径data\1、data\2
logs_train_dir = './logs/' # logs存储路径
'''data'''
print("数据读写中")
input = inputData
# 训练数据及标签
train, train_label = input.get_files(train_dir)
print(train_label)
# 获取批次batch
train_batch, train_label_batch = input.get_batches(train, train_label, IMG_W, IMG_H, BATCH_SIZE, CAPACITY)
print("train_batch/n")
print(train_batch)
# 训练操作定义
Model = MODEL
train_logits = Model.model(train_batch, BATCH_SIZE, N_CLASSES)
train_loss = Model.losses(train_logits, train_label_batch)
train_op = Model.trainning(train_loss, learning_rate)
train_acc = Model.evaluation(train_logits, train_label_batch)
# 这个是log汇总记录
summary_op = tf.summary.merge_all()
# 产生一个会话
sess = tf.Session()
train_writer = tf.summary.FileWriter(logs_train_dir, sess.graph)
# 产生一个saver来存储训练好的模型
saver = tf.train.Saver()
# 所有节点初始化
sess.run(tf.global_variables_initializer())
# 队列监控
coord = tf.train.Coordinator() # 设置多线程协调器
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
# 进行batch的训练
try:
# 执行MAX_STEP步的训练,一步一个batch
for step in np.arange(MAX_STEP):
if coord.should_stop():
break
# 启动以下操作节点,有个疑问,为什么train_logits在这里没有开启?
_, tra_loss, tra_acc = sess.run([train_op, train_loss, train_acc])
# 每隔50步打印一次当前的loss以及acc,同时记录log,写入writer
if step % 100 == 0:
print('Step %d, train loss = %.2f, train accuracy = %.2f%%' % (step, tra_loss, tra_acc * 100.0))
summary_str = sess.run(summary_op)
train_writer.add_summary(summary_str, step)
checkpoint_path = os.path.join(logs_train_dir, 'thing.ckpt')
saver.save(sess, checkpoint_path)
'''
# 每隔100步,保存一次训练好的模型
if (step + 1) == MAX_STEP:
checkpoint_path = os.path.join(logs_train_dir, 'thing.ckpt')
saver.save(sess, checkpoint_path, global_step=step)
'''
except tf.errors.OutOfRangeError:
print('Done training -- epoch limit reached')
finally:
coord.request_stop()
coord.join(threads)
sess.close()
if __name__ == '__main__':
RUN()