Tensorflow Usage

一、Model类

(1)初始化方法

1.可扩展参数:kwargs字典里获取,可限制key的取值

  • name: model变量域scope名称,获取不到采用self.__class__.__name__.lower()取类的名字
  • logging: 是否开启TensorBoard直方图仪表板,获取不到为False
  1. 其他参数

vars: name scope下的变量(字典)
placeholders: 外部变量占位,一般是特征和标签(字典)
layers: 神经网络的layer(列表)
activations: 每个layer的输出结果(列表)
inputs: 输入
output: 输出
loss: 损失
accuracy: 准确率
optimizer:优化器
opt_op:最优化的op操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def __init__(self, **kwargs):
allowed_kwargs = {'name', 'logging'}
for kwarg in kwargs.keys():
assert kwarg in allowed_kwargs, 'Invalid keyword argument: ' + kwarg
name = kwargs.get('name')
if not name:
name = self.__class__.__name__.lower()
self.name = name

logging = kwargs.get('logging', False)
self.logging = logging

self.vars = {}
self.placeholders = {}

self.layers = []
self.activations = []

self.inputs = None
self.outputs = None

self.loss = 0
self.accuracy = 0
self.optimizer = None
self.opt_op = None_build

(2)build方法

_build 是私有build方法,在继承Model的具体实现时对layers进行append操作,下面介绍build方法:

调用_build,所有变量设置共享空间(self.name
构建模型序列:给输入,通过layer()返回输出,又将这个输出再次作为输入到下一个layer()中,循环这一过程;最终,取最后一层layer的结果作为output
保存name scope下的变量到self.vars
模型效果度量:_loss方法,_accuracy方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def _build(self):
raise NotImplementedError

def build(self):
""" Wrapper for _build() """
with tf.variable_scope(self.name):
self._build()

# Build sequential layer model
self.activations.append(self.inputs)
for layer in self.layers:
hidden = layer(self.activations[-1])
self.activations.append(hidden)
self.outputs = self.activations[-1]

# Store model variables for easy access
variables = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=self.name)
self.vars = {var.name: var for var in variables}

# Build metrics
self._loss()
self._accuracy()

self.opt_op = self.optimizer.minimize(self.loss)

(3)保存与加载方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def save(self, sess=None):
if not sess:
raise AttributeError("TensorFlow session not provided.")
saver = tf.train.Saver(self.vars)
save_path = saver.save(sess, "tmp/%s.ckpt" % self.name)
print("Model saved in file: %s" % save_path)

def load(self, sess=None):
if not sess:
raise AttributeError("TensorFlow session not provided.")
saver = tf.train.Saver(self.vars)
save_path = "tmp/%s.ckpt" % self.name
saver.restore(sess, save_path)
print("Model restored from file: %s" % save_path)

二、数据读取

常用的结构化数据文件格式有csv、txt 、libsvm,本篇文章主要说明结构化数据(csv/txt)如何在TF框架进行高效、灵活的读取,避免一些不合理的方式,迈出算法开发标准化、工程化的第一步。

使用pandas读取数据

1
2
3
4
5
6
7
8
def load_data_with_pandas(filename):
# load data as DataFrame to the memory
data = pd.read_csv(filename, sep=',', header=0)
features_df = data.iloc[:, :-1]
label_df = data.iloc[:, -1]
features = features_df.astype(np.float32).values
labels = label_df.astype(np.int64).values
return features, labels

最常见的读取数据的方式是利用pandas包将csv、txt读取为DataFrame,一次全部放入内存。这是一种非常低效的方式,应该尽量避免这种读取方法。其他第三方封装python读取方式的包(TFLearn等)也不建议使用,推荐使用TF框架的OP操作进行数据读取。

TensorFlow的数据读取机制

一、tensorflow读取机制图解

首先需要思考的一个问题是,什么是数据读取?以图像数据为例,读取数据的过程可以用下图来表示:

假设我们的硬盘中有一个图片数据集0001.jpg,0002.jpg,0003.jpg……我们只需要把它们读取到内存中,然后提供给GPU或是CPU进行计算就可以了。这听起来很容易,但事实远没有那么简单。事实上,我们必须要把数据先读入后才能进行计算,假设读入用时0.1s,计算用时0.9s,那么就意味着每过1s,GPU都会有0.1s无事可做,这就大大降低了运算的效率。

如何解决这个问题?方法就是将读入数据和计算分别放在两个线程中,将数据读入内存的一个队列,如下图所示:

读取线程源源不断地将文件系统中的图片读入到一个内存的队列中,而负责计算的是另一个线程,计算需要数据时,直接从内存队列中取就可以了。这样就可以解决GPU因为IO而空闲的问题!

而在tensorflow中,为了方便管理,在内存队列前又添加了一层所谓的“文件名队列”

为什么要添加这一层文件名队列?我们首先得了解机器学习中的一个概念:epoch。对于一个数据集来讲,运行一个epoch就是将这个数据集中的图片全部计算一遍。如一个数据集中有三张图片A.jpg、B.jpg、C.jpg,那么跑一个epoch就是指对A、B、C三张图片都计算了一遍。两个epoch就是指先对A、B、C各计算一遍,然后再全部计算一遍,也就是说每张图片都计算了两遍。

tensorflow使用文件名队列+内存队列双队列的形式读入文件,可以很好地管理epoch。下面我们用图片的形式来说明这个机制的运行方式。如下图,还是以数据集A.jpg, B.jpg, C.jpg为例,假定我们要跑一个epoch,那么我们就在文件名队列中把A、B、C各放入一次,并在之后标注队列结束。

程序运行后,内存队列首先读入A(此时A从文件名队列中出队):

再依次读入B和C:

此时,如果再尝试读入,系统由于检测到了“结束”,就会自动抛出一个异常(OutOfRange)。外部捕捉到这个异常后就可以结束程序了。这就是tensorflow中读取数据的基本机制。如果我们要跑2个epoch而不是1个epoch,那只要在文件名队列中将A、B、C依次放入两次再标记结束就可以了。

二、tensorflow读取数据机制的对应函数

如何在tensorflow中创建上述的两个队列呢?

对于文件名队列,我们使用tf.train.string_input_producer函数。这个函数需要传入一个文件名list,系统会自动将它转为一个文件名队列。

此外tf.train.string_input_producer还有两个重要的参数,一个是num_epochs,它就是我们上文中提到的epoch数。另外一个就是shuffle,shuffle是指在一个epoch内文件的顺序是否被打乱。若设置shuffle=False,如下图,每个epoch内,数据还是按照A、B、C的顺序进入文件名队列,这个顺序不会改变:

如果设置shuffle=True,那么在一个epoch内,数据的前后顺序就会被打乱,如下图所示:

在tensorflow中,内存队列不需要我们自己建立,我们只需要使用reader对象从文件名队列中读取数据就可以了,具体实现可以参考下面的实战代码。

除了tf.train.string_input_producer外,我们还要额外介绍一个函数:tf.train.start_queue_runners。初学者会经常在代码中看到这个函数,但往往很难理解它的用处,在这里,有了上面的铺垫后,我们就可以解释这个函数的作用了。

在我们使用tf.train.string_input_producer创建文件名队列后,整个系统其实还是处于“停滞状态”的,也就是说,我们文件名并没有真正被加入到队列中(如下图所示)。此时如果我们开始计算,因为内存队列中什么也没有,计算单元就会一直等待,导致整个系统被阻塞。

而使用tf.train.start_queue_runners之后,才会启动填充队列的线程,这时系统就不再“停滞”。此后计算单元就可以拿到数据并进行计算,整个程序也就跑起来了,这就是函数tf.train.start_queue_runners的用处。

三、实战代码

我们用一个具体的例子感受tensorflow中的数据读取。如图,假设我们在当前文件夹中已经有A.jpg、B.jpg、C.jpg三张图片,我们希望读取这三张图片5个epoch并且把读取的结果重新存到read文件夹中。

对应的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
># 导入tensorflow
>import tensorflow as tf

># 新建一个Session
>with tf.Session() as sess:
# 我们要读三幅图片A.jpg, B.jpg, C.jpg
filename = ['A.jpg', 'B.jpg', 'C.jpg']
# string_input_producer会产生一个文件名队列
filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)
# reader从文件名队列中读数据。对应的方法是reader.read
reader = tf.WholeFileReader()
key, value = reader.read(filename_queue)
# tf.train.string_input_producer定义了一个epoch变量,要对它进行初始化
tf.local_variables_initializer().run()
# 使用start_queue_runners之后,才会开始填充队列
threads = tf.train.start_queue_runners(sess=sess)
i = 0
while True:
i += 1
# 获取图片数据并保存
image_data = sess.run(value)
with open('read/test_%d.jpg' % i, 'wb') as f:
f.write(image_data)

我们这里使用filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)建立了一个会跑5个epoch的文件名队列。并使用reader读取,reader每次读取一张图片并保存。
运行代码后,我们得到就可以看到read文件夹中的图片,正好是按顺序的5个epoch:

如果我们设置filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)中的shuffle=True,那么在每个epoch内图像就会被打乱,如图所示:

这里只是用三张图片举例,实际应用中一个数据集肯定不止3张图片,不过涉及到的原理都是共通的。

高效的 TensorFlow 读取方式是将数据读取转换成 OP,通过 session run的方式拉去数据。读取线程源源不断地将文件系统中的文件读入到一个内存的队列中,而负责计算的是另一个线程,计算需要数据时,直接从内存队列中取就可以了,这样就可以解决GPU因为IO而空闲的问题。同时,不会一次性的preload到内存,再大的数据量也不会超出内存的限制。

三、TensorFlow API

1.TensorFlow 相关函数理解


  • tf.nn.conv2d ————卷积
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=True,
    data_format='NHWC',
    name=None
    )

    #Example
    import tensorflow as tf
    a = tf.constant([1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,0,0,1,1,0,0],dtype=tf.float32,shape=[1,5,5,1])
    b = tf.constant([1,0,1,0,1,0,1,0,1],dtype=tf.float32,shape=[3,3,1,1])
    c = tf.nn.conv2d(a,b,strides=[1, 2, 2, 1],padding='VALID')
    d = tf.nn.conv2d(a,b,strides=[1, 2, 2, 1],padding='SAME')
    with tf.Session() as sess:
    print ("c shape:")
    print (c.shape)
    print ("c value:")
    print (sess.run(c))
    print ("d shape:")
    print (d.shape)
    print ("d value:")
    print (sess.run(d))
    |参数名| 必选| 类型| 说明|
    |:—-:|:—-:|:—-:|:—-:|
    |input| 是| tensor| 是一个 4 维的 tensor,即 [ batch, in_height, in_width, in_channels ](若 input 是图像,[ 训练时一个 batch 的图片数量, 图片高度, 图片宽度, 图像通道数 ])|
    |filter |是 |tensor |是一个 4 维的 tensor,即 [ filter_height, filter_width, in_channels, out_channels ](若 input 是图像,[ 卷积核的高度,卷积核的宽度,图像通道数,卷积核个数 ]),filter 的 in_channels 必须和 input 的 in_channels 相等|
    |strides| 是 |列表 |长度为 4 的 list,卷积时候在 input 上每一维的步长,一般 strides[0] = strides[3] = 1|
    |padding| 是 |string |只能为 “ VALID “,” SAME “ 中之一,这个值决定了不同的卷积方式。VALID 丢弃方式;SAME:补全方式|
    |use_cudnn_on_gpu| 否 |bool |是否使用 cudnn 加速,默认为 true|
    |data_format| 否| string |只能是 “ NHWC “, “ NCHW “,默认 “ NHWC “|
    |name| 否 |string| 运算名称|

image


  • tf.nn.relu ————relu激活函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    relu(
    features,
    name=None
    )

    #Example
    import tensorflow as tf

    a = tf.constant([1,-2,0,4,-5,6])
    b = tf.nn.relu(a)
    with tf.Session() as sess:
    print (sess.run(b))
    |参数名| 必选 |类型| 说明|
    |:—-:|:—-:|:—-:|:—-:|
    |features| 是| tensor| 是以下类型float32, float64, int32, int64, uint8, int16, int8, uint16, half|
    |name| 否| string| 运算名称|

  • tf.nn.max_pool————池化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    max_pool(
    value,
    ksize,
    strides,
    padding,
    data_format='NHWC',
    name=None
    )


    #Example
    import tensorflow as tf

    a = tf.constant([1,3,2,1,2,9,1,1,1,3,2,3,5,6,1,2],dtype=tf.float32,shape=[1,4,4,1])
    b = tf.nn.max_pool(a,ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1],padding='VALID')
    c = tf.nn.max_pool(a,ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1],padding='SAME')
    with tf.Session() as sess:
    print ("b shape:")
    print (b.shape)
    print ("b value:")
    print (sess.run(b))
    print ("c shape:")
    print (c.shape)
    print ("c value:")
    print (sess.run(c))
参数名 必选 类型 说明
value tensor 4 维的张量,即 [ batch, height, width, channels ],数据类型为 tf.float32
ksize 列表 池化窗口的大小,长度为 4 的 list,一般是 [1, height, width, 1],因为不在 batch 和 channels 上做池化,所以第一个和最后一个维度为 1
strides 列表 池化窗口在每一个维度上的步长,一般 strides[0] = strides[3] = 1
padding string 只能为 “ VALID “,” SAME “ 中之一,这个值决定了不同的池化方式。VALID 丢弃方式;SAME:补全方式
data_format string 只能是 “ NHWC “, “ NCHW “,默认” NHWC “
name string 运算名称

  • tf.nn.dropout————防止或减轻过拟合
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    dropout(
    x,
    keep_prob,
    noise_shape=None,
    seed=None,
    name=None
    )

    #Example
    import tensorflow as tf

    a = tf.constant([1,2,3,4,5,6],shape=[2,3],dtype=tf.float32)
    b = tf.placeholder(tf.float32)
    c = tf.nn.dropout(a,b,[2,1],1)
    with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print (sess.run(c,feed_dict={b:0.75}))
参数名 必选 类型 说明
x tensor 输出元素是 x 中的元素以 keep_prob 概率除以 keep_prob,否则为 0
keep_prob scalar Tensor dropout 的概率,一般是占位符
noise_shape tensor 默认情况下,每个元素是否 dropout 是相互独立。如果指定 noise_shape,若 noise_shape[i] == shape(x)[i],该维度的元素是否 dropout 是相互独立,若 noise_shape[i] != shape(x)[i] 该维度元素是否 dropout 不相互独立,要么一起 dropout 要么一起保留
seed 数值 如果指定该值,每次 dropout 结果相同
name string 运算名称

  • tf.nn.sigmoid_cross_entropy_with_logits————先对 logits 通过 sigmoid 计算,再计算交叉熵
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    sigmoid_cross_entropy_with_logits(
    _sentinel=None,
    labels=None,
    logits=None,
    name=None
    )

    #Example
    import tensorflow as tf
    x = tf.constant([1,2,3,4,5,6,7],dtype=tf.float64)
    y = tf.constant([1,1,1,0,0,1,0],dtype=tf.float64)
    loss = tf.nn.sigmoid_cross_entropy_with_logits(labels = y,logits = x)
    with tf.Session() as sess:
    print (sess.run(loss))
参数名 必选 类型 说明
_sentinel None 没有使用的参数
labels Tensor type, shape 与 logits相同
logits Tensor type 是 float32 或者 float64
name string 运算名称

  • tf.truncated_normal————产生截断正态分布随机数,取值范围为 [ mean - 2 stddev, mean + 2 stddev ]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    truncated_normal(
    shape,
    mean=0.0,
    stddev=1.0,
    dtype=tf.float32,
    seed=None,
    name=None
    )

    #Example
    import tensorflow as tf
    initial = tf.truncated_normal(shape=[3,3], mean=0, stddev=1)
    print(tf.Session().run(initial))
参数名 必选 类型 说明
shape 1 维整形张量或 array 输出张量的维度
mean 0 维张量或数值 均值
stddev 0 维张量或数值 标准差
dtype dtype 输出类型
seed 数值 随机种子,若 seed 赋值,每次产生相同随机数
name string 运算名称

  • tf.constant————根据 value 的值生成一个 shape 维度的常量张量
    1
    2
    3
    4
    5
    6
    7
    constant(
    value,
    dtype=None,
    shape=None,
    name='Const',
    verify_shape=False
    )
    |参数名| 必选| 类型| 说明|
    |:—-:|:—-:|:—-:|:—-:|
    |value| 是| 常量数值或者 list| 输出张量的值|
    |dtype| 否| dtype |输出张量元素类型|
    |shape| 否| 1 维整形张量或 array| 输出张量的维度|
    |name| 否| string| 张量名称|
    |verify_shape| 否 |Boolean| 检测 shape 是否和 value 的 shape 一致,若为 Fasle,不一致时,会用最后一个元素将 shape 补全|

  • tf.placeholder————是一种占位符,在执行时候需要为其提供数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    placeholder(
    dtype,
    shape=None,
    name=None
    )

    #Example
    import tensorflow as tf
    import numpy as np

    x = tf.placeholder(tf.float32,[None,3])
    y = tf.matmul(x,x)
    with tf.Session() as sess:
    rand_array = np.random.rand(3,3)
    print(sess.run(y,feed_dict={x:rand_array}))
    |参数名| 必选| 类型| 说明|
    |:—-:|:—-:|:—-:|:—-:|
    |dtype| 是| dtype |占位符数据类型|
    |shape| 否|1 维整形张量或 array| 占位符维度|
    |name| 否| string |占位符名称|

  • tf.nn.bias_add————将偏差项 bias 加到 value 上面,可以看做是 tf.add 的一个特例,其中 bias 必须是一维的,并且维度和 value 的最后一维相同,数据类型必须和 value 相同。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    bias_add(
    value,
    bias,
    data_format=None,
    name=None
    )

    #Example
    import tensorflow as tf
    import numpy as np

    a = tf.constant([[1.0, 2.0],[1.0, 2.0],[1.0, 2.0]])
    b = tf.constant([2.0,1.0])
    c = tf.constant([1.0])
    sess = tf.Session()
    print (sess.run(tf.nn.bias_add(a, b)))
    #print (sess.run(tf.nn.bias_add(a,c))) error
    print ("##################################")
    print (sess.run(tf.add(a, b)))
    print ("##################################")
    print (sess.run(tf.add(a, c)))
参数名 必选 类型 说明
value 张量 数据类型为 float, double, int64, int32, uint8, int16, int8, complex64, or complex128
bias 一维张量 维度必须和 value 最后一维维度相等
data_format string 数据格式,支持 ‘ NHWC ‘ 和 ‘ NCHW ‘
name string 运算名称

  • tf.reduce_mean————计算张量 input_tensor 平均值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    reduce_mean(
    input_tensor,
    axis=None,
    keep_dims=False,
    name=None,
    reduction_indices=None
    )

    #Example
    import tensorflow as tf
    import numpy as np

    initial = [[1.,1.],[2.,2.]]
    x = tf.Variable(initial,dtype=tf.float32)
    init_op = tf.global_variables_initializer()
    with tf.Session() as sess:
    sess.run(init_op)
    print(sess.run(tf.reduce_mean(x)))
    print(sess.run(tf.reduce_mean(x,0))) #Column
    print(sess.run(tf.reduce_mean(x,1))) #row
    |参数名| 必选| 类型| 说明|
    |:—-:|:—-:|:—-:|:—-:|
    |input_tensor |是| 张量 输入待求平均值的张量|
    |axis |否 |None、0、1 |None:全局求平均值;0:求每一列平均值;1:求每一行平均值|
    |keep_dims |否 |Boolean |保留原来的维度(例如不会从二维矩阵降为一维向量)|
    |name |否 |string| 运算名称|
    |reduction_indices| 否 |None| 和 axis 等价,被弃用|

  • tf.squared_difference————计算张量 x、y 对应元素差平方
    1
    2
    3
    4
    5
    squared_difference(
    x,
    y,
    name=None
    )
参数名 必选 类型 说明
x 张量 是 half, float32, float64, int32, int64, complex64, complex128 其中一种类型
y 张量 是 half, float32, float64, int32, int64, complex64, complex128 其中一种类型
name string 运算名称

  • tf.square————计算张量对应元素平方
    1
    2
    3
    4
    square(
    x,
    name=None
    )
参数名 必选 类型 说明
x 张量 是half, float32, float64, int32, int64, complex64, complex128 其中一种类型
name string 运算名称

2.Tensorflow相关类理解

  • tf.Variable————维护图在执行过程中的状态信息,例如神经网络权重值的变化
1
2
3
4
5
6
7
8
9
10
11
12
__init__(
initial_value=None,
trainable=True,
collections=None,
validate_shape=True,
caching_device=None,
name=None,
variable_def=None,
dtype=None,
expected_shape=None,
import_scope=None
)
参数名 类型 说明
initial_value 张量 Variable 类的初始值,这个变量必须指定 shape 信息,否则后面 validate_shape 需设为 False
trainable Boolean 是否把变量添加到 collection GraphKeys.TRAINABLE_VARIABLES 中(collection 是一种全局存储,不受变量名生存空间影响,一处保存,到处可取)
collections Graph collections 全局存储,默认是 GraphKeys.GLOBAL_VARIABLES
validate_shape Boolean 是否允许被未知维度的 initial_value 初始化
caching_device string 指明哪个 device 用来缓存变量
name string 变量名
dtype dtype 如果被设置,初始化的值就会按照这个类型初始化
expected_shape TensorShape 要是设置了,那么初始的值会是这种维度

—-

四、LeNet网络的构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#模型训练
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt


#打开数据集
path='C:/Users/86769/.keras/datasets/mnist.npz'
f = np.load(path)
x_train, y_train = f['x_train'], f['y_train']
x_test, y_test = f['x_test'], f['y_test']
f.close()

#格式化
x_train = np.pad(x_train,((0,0),(2,2),(2,2)),'constant',constant_values=0)
x_train = x_train.astype('float32')
x_train /=255
x_train=x_train.reshape(x_train.shape[0],32,32,1)

x_test = np.pad(x_test,((0,0),(2,2),(2,2)),'constant',constant_values=0)
x_test = x_test.astype('float32')
x_test /=255
x_test=x_test.reshape(x_test.shape[0],32,32,1)

#创建模型
model=tf.keras.models.Sequential([
tf.keras.layers.Conv2D(filters=6,kernel_size=(5,5),padding='valid',activation=tf.nn.relu,input_shape=(32,32,1)),
tf.keras.layers.AveragePooling2D(pool_size=(2,2),strides=(2,2),padding='same'),
tf.keras.layers.Conv2D(filters=16,kernel_size=(5,5),padding='valid',activation=tf.nn.relu),
tf.keras.layers.AveragePooling2D(pool_size=(2,2),strides=(2,2),padding='same'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(units=120,activation=tf.nn.relu),
# tf.keras.layers.Conv2D(filters=120,kernel_size=(5,5),strides=(1,1),activation='tanh',padding='valid'),
# tf.keras.layers.Flatten(),
tf.keras.layers.Dense(units=84,activation=tf.nn.relu),
tf.keras.layers.Dense(units=10,activation=tf.nn.softmax)
])

model.summary()


#超参数配置
num_epochs=10 #训练轮数
batch_size=64 #批大小
learning_rate=0.001 #学习率

#优化器
adam_optimizer=tf.keras.optimizers.Adam(learning_rate)

model.compile(optimizer=adam_optimizer,
loss=tf.keras.losses.sparse_categorical_crossentropy,metrics=['accuracy'])

import datetime
start_time=datetime.datetime.now()

model.fit(x=x_train,
y=y_train,
batch_size=batch_size,
epochs=num_epochs)
end_time=datetime.datetime.now()
time_cost=end_time-start_time
print("time_cost = ",time_cost) #CPU time cost: 5min, GPU time cost: less than 1min

#模型保存
model.save('E:/ML/LeNet_Model.hui')

#评估指标
print(model.evaluate(x_test, y_test)) #loss value & metrics value

#预测
image_index=4444
print(x_test[image_index].shape)
plt.imshow(x_test[image_index].reshape(32,32),cmap='Greys')

pred = model.predict((x_test[image_index].reshape(1,32,32,1)))
print(pred.argmax())

神经网络中的几个重要概念

梯度下降:梯度下降是一个在机器学习中用于寻找较佳结果(曲线的最小值)的迭代优化算法。梯度的含义是斜率或者斜坡的倾斜度。下降的含义是代价函数的下降。
 算法是迭代的,意思是需要多次使用算法获取结果,以得到最优化结果。梯度下降的迭代性质能使欠拟合演变成获得对数据的较佳拟合。

 梯度下降中有一个称为学习率的参量。如上图左所示,刚开始学习率较大,因此下降步长更大。随着点的下降,学习率变得越来越小,从而下降步长也变小。同时,代价函数也在减小,或者说代价在减小,有时候也称为损失函数或者损失,两者是一样的。(损失/代价的减小是一个概念)。

 只有在数据很庞大的时候(在机器学习中,数据一般情况下都会很大),我们才需要使用epochs,batch size,iteration这些术语,在这种情况下,一次性将数据输入计算机是不可能的。因此,为了解决这个问题,我们需要把数据分成小块,一块一块的传递给计算机,在每一步的末端更新神经网络的权重,拟合给定的数据。

(1)batchsize:批大小。在深度学习中,一般采用SGD训练,即每次训练在训练集中取batchsize个样本训练;
(2)iteration:1个iteration等于使用batchsize个样本训练一次;
(3)epoch:1个epoch等于使用训练集中的全部样本训练一次;

  • batchsize:批大小(批尺寸)
    批大小将决定我们一次训练的样本数目。
    batch_size将影响到模型的优化程度和速度。

batchsize的正确选择是为了在内存效率和内存容量之间寻找最佳平衡。
Batch_Size的取值:

全批次(蓝色)
如果数据集比较小,我们就采用全数据集。全数据集确定的方向能够更好的代表样本总体,从而更准确的朝向极值所在的方向。
注:对于大的数据集,我们不能使用全批次,因为会得到更差的结果。

迷你批次(绿色)
选择一个适中的Batch_Size值。就是说我们选定一个batch的大小后,将会以batch的大小将数据输入深度学习的网络中,然后计算这个batch的所有样本的平均损失,即代价函数是所有样本的平均。

随机(Batch_Size等于1的情况)(红色)
每次修正方向以各自样本的梯度方向修正,横冲直撞各自为政,难以达到收敛。

适当的增加Batch_Size的优点:

  • 1.通过并行化提高内存利用率。
  • 2.单次epoch的迭代次数减少,提高运行速度。(单次epoch=(全部训练样本/batchsize)/iteration=1)
  • 3.适当的增加Batch_Size,梯度下降方向准确度增加,训练震动的幅度减小。(看上图便可知晓)
    经验总结:
    相对于正常数据集,如果Batch_Size过小,训练数据就会非常难收敛,从而导致underfitting。
    增大Batch_Size,相对处理速度加快,所需内存容量增加(epoch的次数需要增加以达到最好的结果)
    这里我们发现上面两个矛盾的问题,因为当epoch增加以后同样也会导致耗时增加从而速度下降。因此我们需要寻找最好的Batch_Size。

iteration:迭代
迭代是重复反馈的动作,神经网络中我们希望通过迭代进行多次的训练以达到所需的目标或结果。
每一次迭代得到的结果都会被作为下一次迭代的初始值。
一个迭代=一个正向通过+一个反向通过

Epoch
当一个完整的数据集通过了神经网络一次并且返回了一次,这个过程称为一次epoch。然而,当一个epoch对于计算机而言太庞大的时候,就需要把它分成多个小块。
为什么要使用多于一个epoch?
在神经网络中传递完整的数据集一次是不够的,而且我们需要将完整的数据集在同样的神经网络中传递多次。但请记住,我们使用的是有限的数据集,并且我们使用一个迭代过程即梯度下降来优化学习过程。如下图所示。因此仅仅更新一次或者说使用一个epoch是不够的。

随着epoch数量增加,神经网络中的权重的更新次数也在增加,曲线从欠拟合变得过拟合。

打赏

扫一扫,分享到微信

微信分享二维码

请我喝杯咖啡吧~

支付宝
微信