Tensorflow-Keras简单介绍

前言

本文是为初学者对Tensorflow和Keras的一个简单的介绍,主要基于Youtube上Tensorflow向导教程,国内B站搬运地址:https://www.bilibili.com/video/av23035841
github演示文档地址:https://github.com/Hvass-Labs/TensorFlow-Tutorials

在自己没有搭建好tensorflow环境的时候,如果可以翻墙,可以使用google的colab在线执行tensorflow的程序,还可以提供GPU服务。

另外一些知识来源其他参考资料:

  1. Deeplearning——动态图 vs. 静态图

感想

对于像我一样一开始接触Tensorflow的人来说,Tensorflow的一些自身定义的一些概念会然人很困惑,而且其运行方式也与通常与我们编写计算的程序不是很相同,会给人一种很不直观的感觉。为了解决这样的困惑,是需要稍微了解一些Tensorflow的框架设计的一些基本的思想的,这些特点对于理解Tensorflow中的那些概念非常有帮助,这些会在下面的记录说明。

Tenserflow理解

1. Tensorflow封装的层次

通常我们把Tenserflow都归类到深度学习神经网络库,但是Tensorflow更本质的是提供低级的数值计算的库,所以才会有Keras这样的专门针对深度神经网络更高级的封装,其后端可以基于Tensorflow。想使用Tensorflow底层的API就必须理解其定义的一大堆概念,但是实际使用时使用Keras这样高级的API会更加方便。

2. Tensorflow的计算图

Tensorflow把整个计算过程称为computational graph计算图,在整个计算过程都构建完成后再做计算。所以它不仅比原生python计算更块,也比numpy更快,因为numpy只能知道每一次操作的计算。Tensorflow的计算图属于静态的图,而像PyTorch的动态图则更加灵活(MxNet两者都支持)。不过最新版本的Tensorflow也可以支持动态图了。

动态计算意味着程序将按照我们编写命令的顺序进行执行。这种机制将使得调试更加容易,并且也使得我们将大脑中的想法转化为实际代码变得更加容易。而静态计算则意味着程序在编译执行时将先生成神经网络的结构,然后再执行相应操作。从理论上讲,静态计算这样的机制允许编译器进行更大程度的优化,但是这也意味着你所期望的程序与编译器实际执行之间存在着更多的代沟。这也意味着,代码中的错误将更加难以发现(比如,如果计算图的结构出现问题,你可能只有在代码执行到相应操作的时候才能发现它)。

Tenserflow重要数据类型

注意下面代码,是总jupyter notebook中转过来的,在jupyter可以直接运行,但是在这里除了重要的结果,我不会将运行结果复制过来,需要时可以直接到python环境运行,tensor版本为1.11.0。

1. Placeholder

placeholder就是作为给计算图数据输入的特殊的变量,其定义和numpy中的数据类似,比较简单:

1
2
3
4
5
6
import tensorflow as tf
import numpy as np
print(tf.__version__)

x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32, [None, None])
2. Variable

一般的变量类型,但是相比placeholder必须初始化,指定维度和初值。Variable可以使用普通的运算符来计算,不过更丰富的运算需要使用tensorflow定义的运算函数。需要注意的是,Variable只是Tensorflow计算图中的一个结构,不等同与变量本身。如下判断两个Variable是否相等的操作,就不能使用“==”操作符了。

1
2
3
4
5
6
w = tf.Variable(tf.zeros([3, 4]),  name='w', dtype=tf.float32)
b = tf.Variable(tf.ones([3, 4]))
k = tf.Variable(tf.constant(0.05, shape=[3,4]))
r = w + b*10
c0 = (w == b)
c1 = tf.equal(w, b)

3. Session

tensorflow使用Session类型对象来执行计算,从上面的print信息可以看到,并没有得到我们需要的计算结果,实际上那些语句只是再构造计算图,要开始计算需要使用Session。使用Session的一般流程是:先创建,然后使用Session初始化变量,最后使用Session运行计算图。

1
2
3
4
session = tf.Session()
session.run(tf.global_variables_initializer())
# session.run(tf.initialize_all_variables()) 老的api函数
session.run(r)

更加常见的用法是使用feed_dict参数喂数据,当有计算图有placeholder时;需要run来计算结果的变量可以组成一个列表来传入。

1
2
3
4
ret = tf.matmul(x, w)
session.run(tf.global_variables_initializer())
input_data = np.arange(12*2).reshape((8,3))
session.run([ret, r], feed_dict={x:input_data})

Tensorflow神经网络基本接口

在tensorflow中nn模块时专门提供神经网络层设计的,以下为最基本的几个接口函数。

1. conv2d

conv2d是基本的卷积运算,注意strides参数一般就改变中间两项,表示在图像的水平和垂直方向的strides;padding参数设置为“same”的目的是保持输入和输入的尺寸一样在没有stride的情况下。

1
2
3
4
5
input_tensor = tf.Variable(tf.ones([10,100,100,3]))
weight_tensor = tf.Variable(tf.ones([3, 3, 3, 2]))
layer = tf.nn.conv2d(input=input_tensor, filter=weight_tensor, strides=[1,1,1,1], padding='SAME')
session.run(tf.global_variables_initializer())
dat = session.run(layer)
2. max_pool

使用pooling层时一般都是max pooling,来保持图像局部的最大特征,同时下采样。注意ksize是pooling的kernel size,其尺寸为2x2,但是要做运行需要时四维的,前后维度值都设1就可以了。

1
2
3
layer = tf.nn.max_pool(value=layer, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')
session.run(tf.global_variables_initializer())
dat = session.run(layer)
3. relu

使用tf.nn.relu即可。

6. 代价函数

代价函数也在tensorflow的nn模块中,如tf.nn.softmax_cross_entropy_with_logits。当然这个函数的输出是Tensor,需要将其变为一个数值来方便优化,可以使用tf.reduce_mean函数。

6. 优化器

tensorflow提供的优化其在train模块中,如:tf.train.AdamOptimizer。

使用layers来辅助构建网络

使用上面最基本计算操作来构建网络是比较麻烦的,所以在tensorflow中有提供layers模块来给我们辅助搭建网络,尽管它在易用性上仍然不如像prettytensor、tf.contrib等其他基于tensorflow底层API的封装,但是layers毕竟是tensorflow自带的。

1. 使用tf.layers.conv2d构建卷积层

相比nn中的conv2d只是单纯的卷积操作,layers中的conv2d是构建一层通用的卷积层网络,自动处理指定的卷积核数以及激活函数。

1
net = tf.layers.conv2d(inputs=input_tensor, name='conv_layer1', padding='SAME', filters=16, kernel_size=5, activation=tf.nn.relu, strides=1)

2. 使用tf.layers.max_pooling2d创建池化层

对比nn中的max_pool操作,Layers中的max_pooling2d函数特指2维数组的池化,提供的接口参数更简单。

1
net = tf.layers.max_pooling2d(inputs=net, pool_size=2, strides=2)

3. 使用tf.layers.flatten和tf.layers.dense创建全连接

有些版本在layers中没有添加flatten函数,不过我现在的1.11.0版本是有的,它将四维的tensor转为全连接输入的二维数组;然后用dense函数创建全连接层。

1
2
net = tf.layers.flatten(net)
net = tf.layers.dense(inputs=net, name='fc1', units=128, activation=tf.nn.relu)

使用Keras API

尽管有了tf.layers、prettytensor以及tf.contrib这些模块,但是现在人们更多的使用的是Keras,原因是前面两个模块或多或少被开发者所遗忘,或者使用仍然不方便。

1. 导入Keras模块

由于Keras是一个独立的库,所以在安装了tensorflow后还需要再安装Keras,当前可以如下导入Keras库。

1
2
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import InputLayer, Input, Reshape, MaxPooling2D, Conv2D, Dense, Flatten

2. 使用Sequential模块创建网络

Keras有两种方式构建网络:一种是使用Sequential模块;另一种是使用Fuction model。使用Sequential是非常简单的,它只需要添加各种已有的网络层即可,但是这样也让你不能对网络结构做细致的改变。Fuction model的使用会有些怪异,不过它能够做更发杂的网络。

如下代码为使用Sequential构建网络的实例,Inputlayer与tensorflow中的placeholder作用是类似的。

1
2
3
4
5
6
7
8
9
10
model = Sequential()
model.add(InputLayer(input_shape=(100,100,3)))
model.add(Conv2D(kernel_size=5, strides=1, filters=16, padding='same', activation='relu', name='kersa_conv_layer1'))
model.add(MaxPooling2D(pool_size=2, strides=2))
model.add(Conv2D(kernel_size=5, strides=1, filters=36, padding='same', activation='relu', name='kersa_conv_layer2'))
model.add(MaxPooling2D(pool_size=2, strides=2))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(2, activation='softmax'))
model.summary()

执行如上代码,model.summary()会打印一个很清晰的网络简介结构表:

3. model compilation

在Keras中为模型设置优化器、和loss function被称为“compilation”

1
2
3
4
5
6
from tensorflow.python.keras.optimizers import Adam

optimizer = Adam(lr=1e-3)
model.compile(optimizer=optimizer,
loss='categorical_crossentropy',
metrics=['accuracy'])

Training、Evaluation,实例使用:model.fit(x=data.x_train, y=data.y_train, epochs=1, batch_size=128)和result = model.evaluate(x=data.x_test,y=data.y_test)

Keras的Save与Load Model

使用Keras的Save与Load要比Tensorflow原生的功能要方便很多,不过在使用前需要先安装好”h5py”库。

1
2
3
4
5
6
from tensorflow.python.keras.models import load_model

path_model = 'model.keras'
model.save(path_model)
del model
model = load_model(path_model)

原教程中为上面的保存方法,会出现类似:“ValueError: You are trying to load a weight file containing 4 layers into a model with 0 layers”的问题,使用下面代码可以运行。

1
2
model.save_weights('model.keras')
model.load_weights('model.keras',by_name=True)
Compartir