Keras TensorFlow教程:如何从零开发一个复杂深度学习模型

做者:chen_h
微信号 & QQ:862251340
微信公众号:coderpai
个人博客:请点击这里python

Keras 是提供一些高可用的 Python API ,能帮助你快速的构建和训练本身的深度学习模型,它的后端是 TensorFlow 或者 Theano 。本文假设你已经熟悉了 TensorFlow 和卷积神经网络,若是,你尚未熟悉,那么能够先看看这个10分钟入门 TensorFlow 教程卷积神经网络教程,而后再回来阅读这个文章。git

在这个教程中,咱们将学习如下几个方面:github

  1. 为何选择 Keras?为何 Keras 被认为是深度学习的将来?算法

  2. 在Ubuntu上面一步一步安装Keras。json

  3. Keras TensorFlow教程:Keras基础知识。ubuntu

  4. 了解 Keras 序列模型
    4.1 实际例子讲解线性回归问题后端

  5. 使用 Keras 保存和回复预训练的模型数组

  6. Keras API
    6.1 使用Keras API开发VGG卷积神经网络
    6.2 使用Keras API构建并运行SqueezeNet卷积神经网络bash

1. 为何选择Keras?

Keras 是 Google 的一位工程师 François Chollet 开发的一个框架,能够帮助你在 Theano 上面进行快速原型开发。后来,这被扩展为 TensorFlow 也能够做为后端。而且最近,TensorFlow决定将其做为 contrib 文件中的一部分进行提供。微信

Keras 被认为是构建神经网络的将来,如下是一些它流行的缘由:

  1. 轻量级和快速开发:Keras 的目的是在消除样板代码。几行 Keras 代码就能比原生的 TensorFlow 代码实现更多的功能。你也能够很轻松的实现 CNN 和 RNN,而且让它们运行在 CPU 或者 GPU 上面。

  2. 框架的“赢者”:Keras 是一个API,运行在别的深度学习框架上面。这个框架能够是 TensorFlow 或者 Theano。Microsoft 也计划让 CNTK 做为 Keras 的一个后端。目前,神经网络框架世界是很是分散的,而且发展很是快。具体,你能够看看 Karpathy 的这个推文:

想象一下,咱们每一年都要去学习一个新的框架,这是多么的痛苦。到目前为止,TensorFlow 彷佛成为了一种潮流,而且愈来愈多的框架开始为 Keras 提供支持,它可能会成为一种标准。

目前,Keras 是成长最快的一种深度学习框架。由于可使用不一样的深度学习框架做为后端,这也使得它成为了流行的一个很大的缘由。你能够设想这样一个场景,若是你阅读到了一篇颇有趣的论文,而且你想在你本身的数据集上面测试这个模型。让咱们再次假设,你对TensorFlow 很是熟悉,可是对Theano了解的很是少。那么,你必须使用TensorFlow 对这个论文进行复现,可是这个周期是很是长的。可是,若是如今代码是采用Keras写的,那么你只要将后端修改成TensorFlow就可使用代码了。这将是对社区发展的一个巨大的推进做用。

2. 怎么安装Keras,而且把TensorFlow做为后端

a) 依赖安装

安装 h5py,用于模型的保存和载入:

pip install h5py复制代码

还有一些依赖包也要安装。

pip install numpy scipy
pip install pillow复制代码

若是你尚未安装TensorFlow,那么你能够按照这个教程先去安装TensorFlow。一旦,你安装完成了 TensorFlow,你只须要使用 pip 很容易的安装 Keras。

sudo pip install keras复制代码

使用如下命令来查看 Keras 版本。

>>> import keras
Using TensorFlow backend.
>>> keras.__version__
'2.0.4'复制代码

一旦,Keras 被安装完成,你须要去修改后端文件,也就是去肯定,你须要 TensorFlow 做为后端,仍是 Theano 做为后端,修改的配置文件位于 ~/.keras/keras.json 。具体配置以下:

{
    "floatx": "float32",
    "epsilon": 1e-07,
    "backend": "tensorflow",
    "image_data_format": "channels_last"
}复制代码

请注意,参数 image_data_format channels_last ,也就是说这个后端是 TensorFlow。由于,在TensorFlow中图像的存储方式是[height, width, channels],可是在Theano中是彻底不一样的,也就是 [channels, height, width]。所以,若是你没有正确的设置这个参数,那么你模型的中间结果将是很是奇怪的。对于Theano来讲,这个参数就是channels_first

那么,至此你已经准备好了,使用Keras来构建模型,而且把TensorFlow做为后端。

3. Keras基础知识

在Keras中主要的数据结构是 model ,该结构定义了一个完整的图。你能够向已经存在的图中加入任何的网络结构。

import keras复制代码

Keras 有两种不一样的建模方式:

  1. Sequential models:这种方法用于实现一些简单的模型。你只须要向一些存在的模型中添加层就好了。

  2. Functional API:Keras的API是很是强大的,你能够利用这些API来构造更加复杂的模型,好比多输出模型,有向无环图等等。

在本文的下一节中,咱们将学习Keras的Sequential models 和 Functional API的理论和实例。

4. Keras Sequential models

在这一部分中,我未来介绍Keras Sequential models的理论。我将快速的解释它是如何工做的,还会利用具体代码来解释。以后,咱们将解决一个简单的线性回归问题,你能够在阅读的同时运行代码,来加深印象。

如下代码是如何开始导入和构建序列模型。

from keras.models import Sequential
models = Sequential()复制代码

接下来咱们能够向模型中添加 Dense(full connected layer),Activation,Conv2D,MaxPooling2D函数。

from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, Dropout
model.add(Conv2D(64, (3,3), activation='relu', input_shape = (100,100,32)))
# This ads a Convolutional layer with 64 filters of size 3 * 3 to the graph复制代码

如下是如何将一些最流行的图层添加到网络中。我已经在卷积神经网络教程中写了不少关于图层的描述。

1. 卷积层

这里咱们使用一个卷积层,64个卷积核,维度是3*3的,以后采用 relu 激活函数进行激活,输入数据的维度是 100*100*32。注意,若是是第一个卷积层,那么必须加上输入数据的维度,后面几个这个参数能够省略。

model.add(Conv2D(64, (3,3), activation='relu', input_shape = (100,100,32)))复制代码

2. MaxPooling 层

指定图层的类型,而且指定赤的大小,而后自动完成赤化操做,酷毙了!

model.add(MaxPooling2D(pool_size=(2,2)))复制代码

3. 全链接层

这个层在 Keras 中称为被称之为 Dense 层,咱们只须要设置输出层的维度,而后Keras就会帮助咱们自动完成了。

model.add(Dense(256, activation='relu'))复制代码

4. Dropout

model.add(Dropout(0.5))复制代码

5. 扁平层

model.add(Flatten())复制代码

数据输入

网络的第一层须要读入训练数据。所以咱们须要去制定输入数据的维度。所以,input_shape参数被用于制定输入数据的维度大小。

model.add(Conv2D(32, (3,3), activation='relu', input_shape=(224, 224, 3)))复制代码

在这个例子中,数据输入的第一层是一个卷积层,输入数据的大小是 224*224*3

以上操做就帮助你利用序列模型构建了一个模型。接下来,让咱们学习最重要的一个部分。一旦你指定了一个网络架构,你还须要指定优化器和损失函数。咱们在Keras中使用compile函数来达到这个功能。好比,在下面的代码中,咱们使用 rmsprop 来做为优化器,binary_crossentropy 来做为损失函数值。

model.compile(loss='binary_crossentropy', optimizer='rmsprop')复制代码

若是你想要使用随机梯度降低,那么你须要选择合适的初始值和超参数:

from keras.optimizers import SGD
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd)复制代码

如今,咱们已经构建完了模型。接下来,让咱们向模型中输入数据,在Keras中是经过 fit 函数来实现的。你也能够在该函数中指定 batch_sizeepochs 来训练。

model.fit(x_train, y_train, batch_size = 32, epochs = 10, validation_data(x_val, y_val))复制代码

最后,咱们使用 evaluate 函数来测试模型的性能。

score = model.evaluate(x_test, y_test, batch_size = 32)复制代码

这些就是使用序列模型在Keras中构建神经网络的具体操做步骤。如今,咱们来构建一个简单的线性回归模型。

4.1 实际例子讲解线性回归问题

问题陈述

在线性回归问题中,你能够获得不少的数据点,而后你须要使用一条直线去拟合这些离散点。在这个例子中,咱们建立了100个离散点,而后用一条直线去拟合它们。

a) 建立训练数据

TrainX 的数据范围是 -1 到 1,TrainY 与 TrainX 的关系是3倍,而且咱们加入了一些噪声点。

import keras
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
 
trX = np.linspace(-1, 1, 101)
trY = 3 * trX + np.random.randn(*trX.shape) * 0.33复制代码

b) 构建模型

首先咱们须要构建一个序列模型。咱们须要的只是一个简单的连接,所以咱们只须要使用一个 Dense 层就够了,而后用线性函数进行激活。

model = Sequential()
model.add(Dense(input_dim=1, output_dim=1, init='uniform', activation='linear'))复制代码

下面的代码将设置输入数据 x,权重 w 和偏置项 b。然咱们来看看具体的初始化工做。以下:

weights = model.layers[0].get_weights()
w_init = weights[0][0][0]
b_init = weights[1][0]
print('Linear regression model is initialized with weights w: %.2f, b: %.2f' % (w_init, b_init)) 
## Linear regression model is initialized with weight w: -0.03, b: 0.00复制代码

如今,咱们能够l利用本身构造的数据

trX
trY
来训练这个线性模型,其中
trY
trX
的3倍。所以,权重 w 的值应该是 3。

咱们使用简单的梯度降低来做为优化器,均方偏差(MSE)做为损失值。以下:

model.compile(optimizer='sgd', loss='mse')复制代码

最后,咱们使用 fit 函数来输入数据。

model.fit(trX, trY, nb_epoch=200, verbose=1)复制代码

在通过训练以后,咱们再次打印权重:

weights = model.layers[0].get_weights()
w_final = weights[0][0][0]
b_final = weights[1][0]
print('Linear regression model is trained to have weight w: %.2f, b: %.2f' % (w_final, b_final))
     
##Linear regression model is trained to have weight w: 2.94, b: 0.08复制代码

正如你所看到的,在运行 200 轮以后,如今权重很是接近于 3。你能够将运行的轮数修改成区间 [100, 300] 之间,而后观察输出结构有什么变化。如今,你已经学会了利用不多的代码来构建一个线性回归模型,若是要构建一个相同的模型,在 TensorFlow 中须要用到更多的代码。

5. 使用 Keras 保存和回复预训练的模型

HDF5 二进制格式

一旦你利用Keras完成了训练,你能够将你的网络保存在HDF5里面。固然,你须要先安装 h5py。HDF5 格式很是适合存储大量的数字收,并从 numpy 处理这些数据。好比,咱们能够轻松的将存储在磁盘上的多TB数据集进行切片,就好像他们是真正的 numpy 数组同样。你还能够将多个数据集存储在单个文件中,遍历他们或者查看 .shape.dtype 属性。

若是你须要信心,那么告诉你,NASA也在使用 HDF5 进行数据存储。h5py 是python对HDF5 C API 的封装。几乎你能够用C在HDF5上面进行的任何操做均可以用python在h5py上面操做。

保存权重

若是你要保存训练好的权重,那么你能够直接使用 save_weights 函数。

model.save_weights("my_model.h5")复制代码

载入预训练权重

若是你想要载入之前训练好的模型,那么你可使用 load_weights 函数。

model.load_weights('my_model_weights.h5')复制代码

6. Keras API

若是对于简单的模型和问题,那么序列模型是很是好的方式。可是若是你要构建一个现实世界中复杂的网络,那么你就须要知道一些功能性的API,在不少流行的神经网络中,咱们都有一个最小的网络结构,完整的模型是根据这些最小的模型进行叠加完成的。这些基础的API可让你一层一层的构建模型。所以,你只须要不多的代码就能够来构建一个完整的复杂神经网络。

让咱们来看看它是如何工做的。首先,你须要导入一些包。

from keras.models import Model复制代码

如今,你须要去指定输入数据,而不是在顺序模型中,在最后的 fit 函数中输入数据。这是序列模型和这些功能性的API之间最显著的区别之一。咱们使用 input() 函数来申明一个 1*28*28 的张量。

from keras.layers import Input
## First, define the vision modules

digit_input = Input(shape=(1, 28, 28))复制代码

如今,让咱们来利用API设计一个卷积层,咱们须要指定要在在哪一个层使用卷积网络,具体代码这样操做:

x = Conv2D(64, (3, 3))(digit_input)
x = Conv2D(64, (3, 3))(x)
x = MaxPooling2D((2, 2))(x)
out = Flatten()(x)复制代码

最后,咱们对于指定的输入和输出数据来构建一个模型。

vision_model = Model(digit_input, out)复制代码

固然,咱们还须要指定损失函数,优化器等等。但这些和咱们在序列模型中的操做同样,你可使用 fit 函数和 compile 函数来进行操做。

接下来,让咱们来构建一个vgg-16模型,这是一个很大很“老”的模型,可是因为它的简洁性,它是一个很好的学习模型。

6.1 使用Keras API开发VGG卷积神经网络

VGG:

VGG卷积神经网络是牛津大学在2014年提出来的模型。当这个模型被提出时,因为它的简洁性和实用性,立刻成为了当时最流行的卷积神经网络模型。它在图像分类和目标检测任务中都表现出很是好的结果。在2014年的ILSVRC比赛中,VGG 在Top-5中取得了92.3%的正确率。 该模型有一些变种,其中最受欢迎的固然是 vgg-16,这是一个拥有16层的模型。你能够看到它须要维度是 224*224*3 的输入数据。

*Vgg 16 architecture*

让咱们来写一个独立的函数来完整实现这个模型。

img_input = Input(shape=input_shape)
# Block 1
x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(img_input)
x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)

# Block 2
x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x)
x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)

# Block 3
x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(x)
x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(x)
x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)

# Block 4
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(x)
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(x)
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)

# Block 5
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(x)
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(x)
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(x)

x = Flatten(name='flatten')(x)
x = Dense(4096, activation='relu', name='fc1')(x)
x = Dense(4096, activation='relu', name='fc2')(x)
x = Dense(classes, activation='softmax', name='predictions')(x)复制代码

咱们能够将这个完整的模型,命名为 vgg16.py。

在这个例子中,咱们来运行

imageNet
数据集中的某一些数据来进行测试。具体代码以下:

model = applications.VGG16(weights='imagenet')
img = image.load_img('cat.jpeg', target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
preds = model.predict(x)
for results in decode_predictions(preds):
    for result in results:
        print('Probability %0.2f%% => [%s]' % (100*result[2], result[1]))复制代码

正如你在图中看到的,模型会对图片中的物体进行一个识别预测。

咱们经过API构建了一个VGG模型,可是因为VGG是一个很简单的模型,因此并无彻底将API的能力开发出来。接下来,咱们经过构建一个 SqueezeNet模型,来展现API的真正能力。

6.2 使用Keras API构建并运行SqueezeNet卷积神经网络

SequeezeNet 是一个很是了不得的网络架构,它的显著点不在于对正确性有多少的提升,而是减小了计算量。当SequeezeNet的正确性和AlexNet接近时,可是ImageNet上面的预训练模型的存储量小于5 MB,这对于在现实世界中使用CNN是很是有利的。SqueezeNet模型引入了一个 Fire模型,它由交替的 Squeeze 和 Expand 模块组成。

*SqueezeNet fire module*

如今,咱们对 fire 模型进行屡次复制,从而来构建完整的网络模型,具体以下:

为了去构建这个网络,咱们将利用API的功能首先来构建一个单独的 fire 模块。

# Squeeze part of fire module with 1 * 1 convolutions, followed by Relu
x = Convolution2D(squeeze, (1, 1), padding='valid', name='fire2/squeeze1x1')(x)
x = Activation('relu', name='fire2/relu_squeeze1x1')(x)

#Expand part has two portions, left uses 1 * 1 convolutions and is called expand1x1 
left = Convolution2D(expand, (1, 1), padding='valid', name='fire2/expand1x1')(x)
left = Activation('relu', name='fire2/relu_expand1x1')(left)

#Right part uses 3 * 3 convolutions and is called expand3x3, both of these are follow#ed by Relu layer, Note that both receive x as input as designed. 
right = Convolution2D(expand, (3, 3), padding='same', name='fire2/expand3x3')(x)
right = Activation('relu', name='fire2/relu_expand3x3')(right)

# Final output of Fire Module is concatenation of left and right. 
x = concatenate([left, right], axis=3, name='fire2/concat')复制代码

为了重用这些代码,咱们能够将它们转换成一个函数:

sq1x1 = "squeeze1x1"
exp1x1 = "expand1x1"
exp3x3 = "expand3x3"
relu = "relu_"
WEIGHTS_PATH = "https://github.com/rcmalli/keras-squeezenet/releases/download/v1.0/squeezenet_weights_tf_dim_ordering_tf_kernels.h5"复制代码

模块化处理

sq1x1 = "squeeze1x1"
exp1x1 = "expand1x1"
exp3x3 = "expand3x3"
relu = "relu_"

def fire_module(x, fire_id, squeeze=16, expand=64):
   s_id = 'fire' + str(fire_id) + '/'
   x = Convolution2D(squeeze, (1, 1), padding='valid', name=s_id + sq1x1)(x)
   x = Activation('relu', name=s_id + relu + sq1x1)(x)

   left = Convolution2D(expand, (1, 1), padding='valid', name=s_id + exp1x1)(x)
   left = Activation('relu', name=s_id + relu + exp1x1)(left)

   right = Convolution2D(expand, (3, 3), padding='same', name=s_id + exp3x3)(x)
   right = Activation('relu', name=s_id + relu + exp3x3)(right)

   x = concatenate([left, right], axis=3, name=s_id + 'concat')
return x复制代码

如今,咱们能够利用咱们构建好的单独的 fire 模块,来构建完整的模型。

x = Convolution2D(64, (3, 3), strides=(2, 2), padding='valid', name='conv1')(img_input)
x = Activation('relu', name='relu_conv1')(x)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool1')(x)

x = fire_module(x, fire_id=2, squeeze=16, expand=64)
x = fire_module(x, fire_id=3, squeeze=16, expand=64)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool3')(x)

x = fire_module(x, fire_id=4, squeeze=32, expand=128)
x = fire_module(x, fire_id=5, squeeze=32, expand=128)
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), name='pool5')(x)

x = fire_module(x, fire_id=6, squeeze=48, expand=192)
x = fire_module(x, fire_id=7, squeeze=48, expand=192)
x = fire_module(x, fire_id=8, squeeze=64, expand=256)
x = fire_module(x, fire_id=9, squeeze=64, expand=256)
x = Dropout(0.5, name='drop9')(x)

x = Convolution2D(classes, (1, 1), padding='valid', name='conv10')(x)
x = Activation('relu', name='relu_conv10')(x)
x = GlobalAveragePooling2D()(x)
out = Activation('softmax', name='loss')(x)

model = Model(inputs, out, name='squeezenet')复制代码

完整的网络模型咱们放置在

squeezenet.py
文件里。咱们应该先下载 imageNet 预训练模型,而后在咱们本身的数据集上面进行训练和测试。下面的代码就是实现了这个功能:

import numpy as np
from keras_squeezenet import SqueezeNet
from keras.applications.imagenet_utils import preprocess_input, decode_predictions
from keras.preprocessing import image

model = SqueezeNet()

img = image.load_img('pexels-photo-280207.jpeg', target_size=(227, 227))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)

preds = model.predict(x)
all_results = decode_predictions(preds)
for results in all_results:
  for result in results:
    print('Probability %0.2f%% => [%s]' % (100*result[2], result[1]))复制代码

对于相同的一幅图预测,咱们能够获得以下的预测几率。

至此,咱们的Keras TensorFlow教程就结束了。但愿能够帮到你 :-)


CoderPai 是一个专一于算法实战的平台,从基础的算法到人工智能算法都有设计。若是你对算法实战感兴趣,请快快关注咱们吧。加入AI实战微信群,AI实战QQ群,ACM算法微信群,ACM算法QQ群。详情请关注 “CoderPai” 微信号(coderpai)。

相关文章
相关标签/搜索