阅读 152

代码详解:用TensorFlow的Keras API建立多层神经网络

全文共6538字,预计学习时长20分钟或更长

图片来源:unspash.com/@herfrechness

本文将介绍如何利用TensorFlow的高级API-Keras建立并训练多层感知器.

Keras自2015年初不断发展,如今已成为基于Theano和TensorFlow的最受欢迎且应用最广的深度学习库之一。Keras最显著的一个特征是其具有非常直观且便于使用的API,用户只需写几行代码就能运行神经网络。

Keras 1.1.0及以上的版本均可植入TensorFlow,作为其中的Con肋骨模板(包含由参与者开发给TensorFlow的数据包,为实验性代码)的一部分.

本教程将从以下几方面介绍该TensorFlow的高级API:

·前馈神经网络的基础知识

·载入并准备时下流行的MNIST数据集的方法

·建立图像分类器的操作

·训练神经网络和评估其准确性的方法

本教程改编自Next Tech“Python机器学习”丛书系列的第四部分.从零开始教会你Python的机器学习和深度学习算法.教程也涉及到浏览器内沙盒环境,其中包含所有必要软件、需要预安装的数据库,以及使用公共数据集的项目.

教程传送门:https:/next.tech/xyz/python-机器学习-第4部分


多层感知器

多层前馈神经网络是一种独特的具有多个单一神经元的完全连接网络,也被称为多层感知器(MultilayerPerceptrons)。下图展示了由三个层级组成的MLP的概念:

上图所示的MLP有一个输入层、一个隐藏层以及一个输出层.隐藏层的单元均与输入层连接,输出层的单元也全部与隐藏层连接.具有多个隐藏层的网络被称为深层人工神经网络.

给MLP添加任意数量的隐藏层,来创建更深层的网络结构.实际上,添加的层级和单元的数量可被视为对既定问题任务进行优化的额外超参数.

正如上图所示,将第i层中的第i个激活单位标志为i^(L)。为了使数学和代码运行更加直观,给输入层使用in上标,隐藏层用h上标,输出层用o上标.

例如,i^(In)表示输入层中的第i个单元,i^(H)代表隐藏层中的第i个单元,i^(Out)则表示输出层中的第i个单元。此处,_0^(In)和_0^(Out)激活单元是偏置项,设为等同于1。激活输入层的单元只需输入单元和偏置项.


l层的每个单元均通过权重系数与l+1层的所有单元连接.比方说,l层的第k个单元与l+1层的第j个单元的连接写为w_{k,j}^(L)。再看回上一个图,将连接输入层和隐藏层的权重矩阵标志为W^(H),连接隐藏层和输出层的权重矩阵标志为W^(Out)。

将连接输入层和隐藏层的权重矩阵汇总为:

此处,d表示隐藏层单元的数量,m表示输入层中包括偏置项的单元数量.由于彻底理解该表示法对于掌握本教程后面的概念非常重要,我们在下图简化的3-4-3多层感知器的描述性说明中总结一下刚刚学到的内容:

MNIST数据集

混合美国国家标准技术研究所(混合国家标准与技术研究所,MNIST)数据库是时下非常流行的针对机器学习算法的基准数据集。为了研究神经网络训练通过tensorflow.keras(tf.keras)高级api运作的情况,需要运行多层感知器来区分手写数字和运作的情况,需要运行多层感知器来区分手写数字和MNIST数据集中的数字.

https://next.tech/register(要依照本教程的代码片段,请使用该Next Tech沙盒),里面设置有MNIST数据集合所有必需的数据包.http://yann.lecun.com/exdb/mnist/(或者也可以在本地环境下下载数据集)。

MNIST数据集有四个部分,所示如下:

·训练组图像:列车-图像-idx3-ubyte.gz-60000个样本

·训练组标签:列车标签.idx1-ubyte.gz-60000个标签

·测试组图像:t10k-图像-idx3-ubyte.gz-10000个样本

·测试组标签:t10k标签-idx1-ubyte.gz-10000个标签

训练组包含250名人士(50%为高中学生,50%为人口普查局员工)的手写数字.)测试组包含不同人写的数字.

请注意TensorFlow也提供相同的数据集,如下所示:

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data复制代码

然而,本教程使用MNIST数据集作为外部数据集来分别学习数据预处理的所有步骤.依照这个方式便可学会如何使用自己的数据集.

第一步是通过在终端运行以下命令来解压缩MNIST数据集的四个部分:

cd mnist/
gzip *ubyte.gz -d复制代码

import os
import struct

def load_mnist(path, kind='train'):
    """Load MNIST data from`path`"""
    labels_path = os.path.join(
        path, f'{kind}-labels-idx1-ubyte'
    )
    images_path = os.path.join(
        path, f'{kind}-images-idx3-ubyte'
    )

    with open(labels_path, 'rb') as lbpath:
        magic, n = struct.unpack('>II',lbpath.read(8))
        labels = np.fromfile(lbpath,dtype=np.uint8)

    with open(images_path, 'rb') as imgpath:
        magic, num, rows, cols =struct.unpack(">IIII", imgpath.read(16))
        images = np.fromfile(imgpath,dtype=np.uint8).reshape(len(labels), 784)
        images = ((images / 255.) - .5) *

 2return images,labels复制代码

load_mnist函数返回两个数组.第一个数组是n×m维度的NumPy数组(图像)。其中,n表示样本数,MIS表示要点值(此处为像素)。MNIST数据集中的图像为28x28像素,每个像素由灰度强度值表示。在此将28x28像素图像展开为单维行矢量,表示图像数组中的行(每行或每图的数值为784)。Load_mnist函数返回的第二个数组(标签)包含相应的目标变量,即手写数字的类标签(整数0-9)。

然后,按照如下方式加载和准备数据集:

# loading the data
X_train, y_train = load_mnist('./mnist/', kind='train')
print(f'Rows: {X_train.shape[0]},  Columns: {X_train.shape[1]}')

X_test, y_test = load_mnist('./mnist/', kind='t10k')
print(f'Rows: {X_test.shape[0]},  Columns: {X_test.shape[1]}')

# mean centering and normalization:
mean_vals = np.mean(X_train, axis=0)
std_val = np.std(X_train)

X_train_centered = (X_train - mean_vals)/std_val
X_test_centered = (X_test - mean_vals)/std_val

del X_train, X_test

print(X_train_centered.shape, y_train.shape)
print(X_test_centered.shape, y_test.shape)复制代码

[Out:]
 Rows: 60000,  Columns: 784
 Rows: 10000,  Columns: 784
 (60000, 784) (60000,)
(10000, 784) (10000,)复制代码

为了了解MNIST中这些图像的情况,通过Matplotlib的imShow函数来看看数字0-9的示例:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(nrows=2, ncols=5, 
                      sharex=True, sharey=True)
ax = ax.flatten()
for i in range(10):
    img = X_train_centered[y_train == i][0].reshape(28, 28)
    ax[i].imshow(img, cmap='Greys')

ax[0].set_yticks([])
ax[0].set_xticks([])
plt.tight_layout()
plt.show()复制代码

现在可以看到2×5的子图片,展示了每个不同数字的代表图像.

现在开始建立自己的模型!

使用TensorFlow的KerasAPI创建一个MLP

首先,为NumPy和TensorFlow设置随机种子数,获得如下相同结果:

import tensorflow.contrib.keras as keras

np.random.seed(123)
tf.set_random_seed(123)复制代码


继续准备训练数据,将类标签(整数0-9)转化为独热形式。幸运的是,Keras为实现该操作提供了便利的工具:

y_train_onehot = keras.utils.to_categorical(y_train)

print('First 3 labels: ', y_train[:3])
print('\nFirst 3 labels (one-hot):\n', y_train_onehot[:3])复制代码

First 3labels:  [5 0 4]
First 3 labels(one-hot): 
[[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]]复制代码

现在开始运行神经网络!简单来说,网络包含三个层级.前两个层(输入层和隐藏层)各有50个具有tanh激活函数的单元.最后一个层(输出层)有针对10个类标签的10个层级,能使用Softmax来传递每类标签的操作可能性.Keras让这些任务变得异常简单:


# initialize model
model = keras.models.Sequential()

# add input layer
model.add(keras.layers.Dense(
    units=50,
    input_dim=X_train_centered.shape[1],  
  kernel_initializer='glorot_uniform',
    bias_initializer='zeros',
    activation='tanh')
 )
# add hidden layer
model.add( 
   keras.layers.Dense( 
       units=50, 
       input_dim=50,
       kernel_initializer='glorot_uniform',
       bias_initializer='zeros',
       activation='tanh')
    )
# add output layer
model.add(
    keras.layers.Dense(
        units=y_train_onehot.shape[1],
        input_dim=50,
        kernel_initializer='glorot_uniform',
        bias_initializer='zeros',
        activation='softmax')
    )

# define SGD optimizer
sgd_optimizer = keras.optimizers.SGD(
    lr=0.001, decay=1e-7, momentum=0.9
)
# compile model
model.compile( 
   optimizer=sgd_optimizer,
    loss='categorical_crossentropy'
)复制代码

首先,使用序列类初始化新模型来运行前馈神经网络.然后,根据自身喜好添加层级.但是,因为添加的第一个层是输入层,需要确保输入_DIM属性与训练组中的要点数(列数)相配(神经网络运行时有784个要点或像素)。

同时也要确保两个连续层中的输出单元数(单位)和输入单元数(INPUT_DIM)相符。头两个层级各有50个单元以及1个偏置项.输出层中的单元数应等同于特殊类标签的数量,即独热编码类标签数组中的列数.

请注意本教程使用glorot_Uniform作为权重矩阵的初始化算法.Glorot初始化对激活深度神经网络而言更加有效。偏差被初始化为零,这是常见设置,实际上也是Keras中的默认设置.

在编写模型之前,还需定义优化器.在此选择随机梯度下降优化法.此外,还可设置权重衰减常数和冲量学习的值,从而调整每个阶段的学习率.最后,将成本(损失)函数设置为分类交叉熵.

二元交叉熵是逻辑回归中成本函数的专业术语,分类交叉熵则是通过Softmax进行多类预测的概括.

编写完成后,可调用拟合方法来训练模型.此处采用小批量随机梯度方法,批量大小为每批64个训练模板.训练次数超过50次,并通过设置详细=1来跟踪训练过程中成本函数的优化。

验证_拆分参数尤其便于操作。因为它会在每次训练后保留10%的训练数据(此处为6,000个样本)用以验证,这样就便于监控模型在训练期间是否过度拟合:

# train model
history = model.fit( 
   X_train_centered, y_train_onehot,
   batch_size=64, epochs=50,
   verbose=1, validation_split=0.1
)复制代码

训练期间打印成本函数的值非常有用,可以快速发现训练期间成本是否下降,并提前停止算法.否则就需要调整超参数值.

为了预测类标签,可以使用预测类方法直接将类标签作为整数返回:

y_train_pred = model.predict_classes(X_train_cent
ered, verbose=0)
print('First 3 predictions: ', y_train_pred[:3])复制代码

First 3 predictions: [5 0 4]复制代码

最后,在训练和测试组中印出模型准确性;

# calculate training accuracy
y_train_pred = model.predict_classes(X_train_cent
ered, verbose=0)
correct_preds = np.sum(y_train == y_train_pred, ax
is=0)
train_acc = correct_preds / y_train.shape[0]

print(f'Training accuracy: {(train_acc * 100):.2f}')

# calculate testing accuracy
y_test_pred = model.predict_classes(X_test_cente
red, verbose=0)
correct_preds = np.sum(y_test == y_test_pred, axis=0)
test_acc = correct_preds / y_test.shape[0]

print(f'Test accuracy: {(test_acc * 100):.2f}')复制代码

Training accuracy: 98.81
Test accuracy: 96.27复制代码

本教程简述如何使用TensorFlow的KerasAPI创建和训练多层神经网络来进行图像分类.请注意这只是一个非常简单的神经网络,没有优化的调整参数.

实践过程中,需要弄清楚如何通过调整学习率、冲量、权值衰减,以及隐藏单元的数量来优化模型.还需要学习如何处理梯度消失的问题,其中随着更多层被添加到网络中,误差梯度会变得越来越小.

算法的公平性也可以量化?试试这三个指标吧

留言 点赞 关注

我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”


(添加小编微信:dxsxbb,加入读者圈,一起讨论最新鲜的人工智能科技哦~)


关注下面的标签,发现更多相似文章
评论