使用 TensorFlow 搭建多层神经网络

使用 TensorFlow 搭建多层神经网络

使用 TensorFlow 搭建多层神经网络

目的:尝试使用 TensorFlow 来搭建一个多层神经网络结构

挑战介绍

  • TensorFlow 原理及使用
  • 多层神经网络

内容

一个经典的神经网络结构,包含有 1 个输入层,一个隐含层以及一个输出层,如下图所示:

document-uid214893labid6102timestamp1532053837335.png

其实,像上图中的隐含层在深度学习中又有一个名字,那就叫全连接层。正如其名,全连接层指的是其每一个节点都与上一层每个节点相连。与此同时,为了方便,我们通常会向全连接层中封装激活函数。

实现全连接层

挑战:使用 TensorFlow 提到的数学运算方法实现全连接层,并添加 Relu 作为激活函数。

规定:只能使用 TensorFlow 模块提供的类和方法。

提示:可能会用到 matmul 计算矩阵乘法,以及 tf.nn.relu() 激活函数方法。

import tensorflow as tf

def fully_connected(input_layer, weights, biases):
    """
    参数:
    input_layer -- 输入层 Variable
    weights -- 权重项 Variable
    biases -- 截距项 Variable

    返回:
    output -- 输出层 Variable
    """
    ### 代码开始 ### (≈ 2 行代码)
    layer = None
    output = None
    ### 代码结束 ###
    return output

解答:

import tensorflow as tf

def fully_connected(input_layer, weights, biases):
    """
    参数:
    input_layer -- 输入层 Variable
    weights -- 权重项 Variable
    biases -- 截距项 Variable

    返回:
    output -- 输出层 Variable
    """
    ### 代码开始 ### (≈ 2 行代码)
    layer = tf.add(tf.matmul(input_layer, weights), biases)
    output = tf.nn.relu(layer)
    ### 代码结束 ###
    return output

测试:

input_layer = tf.Variable(tf.random_normal([4, 4], stddev=10, seed=10))
weights = tf.Variable(tf.random_normal([4, 2], stddev=10, seed=10))
biases = tf.Variable(tf.random_normal([2], stddev=10, seed=10))
fully_connected(input_layer, weights, biases)

结果:

<tf.Tensor 'Relu:0' shape=(4, 2) dtype=float32>

挑战:得到上面测试数据在全连接层中的输出结果。

提示:建立会话并执行。

### 代码开始 ### (≈ 4 行代码)

### 代码结束 ###

output

解答:

# 代码开始 ### (≈ 4 行代码)
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    output = sess.run(fully_connected(input_layer, weights, biases))
### 代码结束 ###
output

输出:

array([[  0.     ,   0.     ],
       [  0.     , 286.99103],
       [371.80936,   0.     ],
       [  0.     ,   0.     ]], dtype=float32)

数据预处理

我们使用过手写字符数据集 Digits。该数据集包含有 1797 张数字 09 的手写字符图像转换后的数字矩阵,目标值是 0-9。为了方便,这里直接使用 scikit-learn 提供的 load_digits 方法加载该数据集。

import numpy as np
from sklearn import datasets

digits = datasets.load_digits()

加载完成的 digits 数据集中包含 3 个属性:

属性 描述
images 8×8 矩阵,记录每张手写字符图像对应的像素灰度值
data 将 images 对应的 8×8 矩阵转换为行向量
target 记录 1797 张影像各自代表的数字

本次挑战不涉及模型评估,所以这里不再划分训练集和测试集,直接使用全部数据传入模型训练。

这里,我们需要将数据从 ndarray 处理为 Variable Tensor

train_x = tf.Variable(digits.data, dtype="float32")
train_y = tf.one_hot(tf.Variable(digits.target), depth=10)

你可能会疑惑上面针对 train_y 的处理方式,这里我们使用到一种叫做独热编码的手段。独热编码的过程如下图所示。一般情况下面对类别的特征,你可能想到的是把苹果、香蕉、菠萝依次替换成 1, 2, 3 数值传入模型训练。实际上,如果模型对数值大小敏感,这种方式是存在问题的。

独热编码的过程中,我们的特征从 1 列扩充为 3。如果特征是苹果,那么苹果对应的值为 1,其余为 0。这样既可以避免数值大小对训练过程的影响,还可以扩充训练集。所以,独热编码好处多多。

document-uid214893labid6102timestamp1532053837788.png

本次挑战中,我们针对目标值进行独热编码当然不是上面扩充数据集的用途。针对目标值独热编码的原因下面细说。

搭建多层神经网络实现手写字符识别

前面的挑战中,我们是用 scikit-learn 搭建了一个人工神经网络对手写字符进行了分类。今天的挑战中,我们会使用上面通过 TensorFlow 构建的全连接层搭建一个多层神经网络用于分类。挑战拟构建多层神经网络结构如下:

document-uid214893labid6102timestamp1532053838276.png

你可以看到,我们输入数据的 shape 是 N * 64,N 代表训练集的数据量,准确来讲就是 (1797, 64)。上面的神经网络一共有 2 个全连接层,其中第一层将输入数据处理成 (1797, 30),接着第二个全连接层将训练数据处理成 (1797, 10),最后直接作为输出层输出。

上面留下一个问题,那就是「为什么针对目标值进行独热编码?」你可以发现,我们构建的神经网络最后的输出是 (1797, 10),而原目标值(data.target)的 shape(1797, 1)。由于原目标值中正好包含 09 的数字类别,我们通过独热编码可以将 (1797, 1) 扩充为 (1797, 10) 的形状。这样做的目的是方便后面定义损失函数并进行优化计算

你可以试一试:

digits.target.shape, train_y.shape # 查看前后形状的变化

可以看到,目标值的确是由 (1797, 1) 扩充为 (1797, 10) 的形状。

接下来,开始搭建神经网络结构。


挑战:使用上面实现的全连接层函数搭建如图规定的神经网络结构。

规定:全连接层中,初始化权重和偏置项均为 1。第一层将输入数据处理成 (1797, 30),接着第二个全连接层将训练数据处理成 (1797, 10)

提示:可能会用到 tf.ones() 生成全为 1 的张量。

"""完成 2 个全连接层
"""

input_layer = train_x # 输入层

### 代码开始 ### (≈ 4 行代码)

# 全连接层 1
weight_1 = None
bias_1 = None
fc_layer_1 = None

# 全连接层 2
weight_2 = None
bias_2 = None
fc_layer_2 = None

### 代码结束 ###
output_layer = fc_layer_2 # 输出层

解答:

"""完成 2 个全连接层
"""

input_layer = train_x  # 输入层

# 代码开始 ### (≈ 4 行代码)

# 全连接层 1
weight_1 = tf.Variable(tf.ones([64, 30]))
bias_1 = tf.Variable(tf.ones([30]))
fc_layer_1 = fully_connected(input_layer, weight_1, bias_1)

# 全连接层 2
weight_2 = tf.Variable(tf.ones([30, 10]))
bias_2 = tf.Variable(tf.ones([10]))
fc_layer_2 = fully_connected(fc_layer_1, weight_2, bias_2)

### 代码结束 ###
output_layer = fc_layer_2  # 输出层

此时,我们需要指定==损失函数==和==优化器==。其中,损失函数使用到交叉熵损失函数。交叉熵(Cross Entropy)是信息论中一个重要概念,主要用于度量两个概率分布间的差异性信息。交叉熵损失函数会随着正确类别的概率不断降低,返回的损失值越来越大,所以被用于分类问题。

下面,我们已经给出了交叉熵损失函数的定义方法,你只需要补充优化器的相关设定即可。


挑战:完成优化器设定。

规定:使用梯度下降优化器,设置学习率为 0.5,其他使用默认参数即可。

# 交叉熵损失函数,该步骤无需理解,后面实验详细介绍
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=train_y, logits=output_layer))

# 定义损失函数和优化器
### 代码开始 ### (≈ 1~2 行代码)

train_step = None

### 代码结束 ###

解答:

# 交叉熵损失函数,该步骤无需理解,后面实验详细介绍
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(
    labels=train_y, logits=output_layer))

# 定义损失函数和优化器
# 代码开始 ### (≈ 1~2 行代码)

train_step = tf.train.GradientDescentOptimizer(.5).minimize(loss)

### 代码结束 ###

最后,我们就可以开始训练模型了。


挑战:开始训练模型,执行优化迭代过程,最后绘制出损失函数值的变化曲线。

规定:优化迭代 10 次即可。

提示:需要初始化、建立会话。

from matplotlib import pyplot as plt
%matplotlib inline

### 代码开始 ### (≈ 8~10 行代码)

### 代码结束 ###

解答:

from matplotlib import pyplot as plt
%matplotlib inline

# 代码开始 ### (≈ 8~10 行代码)
loss_list = []

init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    for _ in range(10):
        sess.run(train_step)
        loss_list.append(sess.run(loss))

plt.plot(loss_list)
loss_list
### 代码结束 ###

结果:

[3191.4922,
2.3109665,
2.310335,
2.3097448,
2.3091934,
2.3086793,
2.3082006,
2.307755,
2.3073409,
2.3069558]

下载.png

赞赏
Nemo版权所有丨如未注明,均为原创丨本网站采用BY-NC-SA协议进行授权,转载请注明转自:https://nemo.cool/239.html
没有标签
首页      Dev      ML      使用 TensorFlow 搭建多层神经网络

Nemo

文章作者

发表回复

textsms
account_circle
email

使用 TensorFlow 搭建多层神经网络
使用 TensorFlow 搭建多层神经网络 目的:尝试使用 TensorFlow 来搭建一个多层神经网络结构 挑战介绍 TensorFlow 原理及使用 多层神经网络 内容 一个经典的神经网络结构,包含有 1 个输…
扫描二维码继续阅读
2019-07-21