TensorFlow搭建神经网络

TensorFlow搭建神经网络

实验目的

使用TensorFlow搭建一个神经网络。在此次实验中,实现TensorFlow模型有两部分

  • 创建计算图
  • 运行计算图

实验内容

导入TensorFlow库

import math
import numpy as np
import h5py
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.python.framework import ops
from tf_utils import load_dataset, random_mini_batches, convert_to_one_hot, predict

%matplotlib inline
np.random.seed(1)

数据集

此次使用的数据集为手语图片,你需要构建一个算法,以便于从语言障碍人士到不懂手语的人进行沟通。

  • 训练集:1080张手势图片,每张图片像素为(64×64),手势共有6类,分别表示从0到5的数字(每种有180张图片)
  • 测试集:120张手势图片,每张图片像素为(64×64),手势共有6类,分别表示从0到5的数字(每种有20张图片)
请注意,这是SINGS数据集的一个子集,完整的数据集包含更多的图像。 以下是每个数字的示例,以及如何表示标签。 这些在将图像的像素降低到64×64像素之前的原始图片。

下列代码用于加载数据集:

# 加载数据集
X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = load_dataset()

更改下面代码中的index并运行代码,可视化数据集中的一个样本。

# 图片样本
index = 0
plt.imshow(X_train_orig[index])
print ("y = " + str(np.squeeze(Y_train_orig[:, index])))

代码运行结果为:

y = 5

将数据集平坦化,然后通过对数据矩阵除以255对其进行归一化。并且将标签转换为one-hot编码的向量,运行下面的代码执行此操作。

# 平坦化数据集
X_train_flatten = X_train_orig.reshape(X_train_orig.shape[0], -1).T
X_test_flatten = X_test_orig.reshape(X_test_orig.shape[0], -1).T
# 归一化数据集
X_train = X_train_flatten/255.
X_test = X_test_flatten/255.
# 将标签转换为one-hot编码向量
Y_train = convert_to_one_hot(Y_train_orig, 6)
Y_test = convert_to_one_hot(Y_test_orig, 6)

print ("number of training examples = " + str(X_train.shape[1]))
print ("number of test examples = " + str(X_test.shape[1]))
print ("X_train shape: " + str(X_train.shape))
print ("Y_train shape: " + str(Y_train.shape))
print ("X_test shape: " + str(X_test.shape))
print ("Y_test shape: " + str(Y_test.shape))

代码运行结果为:

number of training examples = 1080
number of test examples = 120
X_train shape: (12288, 1080)
Y_train shape: (6, 1080)
X_test shape: (12288, 120)
Y_test shape: (6, 120)

注意:12288=64×64×3。每个图片为64×64像素,3是RGB颜色。 目的:搭建一个高精度识别手势的算法。要做到这一点,需要建立一个TensorFlow模型,最后使用softmax层输出。 模型:LINEAR - > RELU - > LINEAR - > RELU - > LINEAR - > SOFTMAX。

创建placeholders

首先为X和Y创建placeholder,在运行session时传递训练数据。 练习:补充下面的函数,在TensorFlow中创建placeholder。

# GRADED FUNCTION: create_placeholders

def create_placeholders(n_x, n_y):
"""
为session创建placeholder

Arguments:
n_x -- scalar, 图片向量的大小 (num_px * num_px = 64 * 64 * 3 = 12288)
n_y -- scalar, 类别数量 (从0到5的数字, 因此为6)

Returns:
X -- 输入数据集的placeholder, shape:[n_x, None] ,dtype:"float"
Y -- 输出标签的placeholder, shape:[n_y, None] ,dtype:"float"

Tips:
- 创建placeholder时使用None可以灵活地处理placeholder的数量,
因为训练集和测试集的样本数量是不相同的。
"""

### 填写代码 ###
X = None
Y = None
### 完成代码 ###

return X, Y

检验:

X, Y = create_placeholders(12288, 6)
print ("X = " + str(X))
print ("Y = " + str(Y))

代码运行结果为:

X = Tensor("Placeholder:0", shape=(12288, ?), dtype=float32)
Y = Tensor("Placeholder_1:0", shape=(6, ?), dtype=float32)

初始化参数变量

接下来需要在TensorFlow中初始化参数。 练习:实现下面的功能来初始化tensorflow中的参数。使用Xavier初始化权重,Zero 初始化偏置项。参数的形状如下。给出例子,W1和B1:

  • W1 = tf.get_variable("W1", [25,12288], initializer =tf.contrib.layers.xavier_initializer(seed = 1))
  • b1 = tf.get_variable("b1", [25,1], initializer = tf.zeros_initializer())

为了确保所有人的输出值都相同,使用seed=1。

# GRADED FUNCTION: initialize_parameters

def initialize_parameters():
"""
初始化神经网络的参数,其形状为:
W1 : [25, 12288]
b1 : [25, 1]
W2 : [12, 25]
b2 : [12, 1]
W3 : [6, 12]
b3 : [6, 1]

Returns:
parameters -- 存储W1, b1, W2, b2, W3, b3张量的字典
"""

tf.set_random_seed(1) # 为了确保所有人的输出值都相同

### 填写代码 ###
W1 = None
b1 = None
W2 = None
b2 = None
W3 = None
b3 = None
### 完成代码 ###

parameters = {"W1": W1,
"b1": b1,
"W2": W2,
"b2": b2,
"W3": W3,
"b3": b3}

return parameters

测试代码:

tf.reset_default_graph()
with tf.Session() as sess:
parameters = initialize_parameters()
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

代码运行结果为:

W1 = <tf.Variable 'W1:0' shape=(25, 12288) dtype=float32_ref>
b1 = <tf.Variable 'b1:0' shape=(25, 1) dtype=float32_ref>
W2 = <tf.Variable 'W2:0' shape=(12, 25) dtype=float32_ref>
b2 = <tf.Variable 'b2:0' shape=(12, 1) dtype=float32_ref>

此时参数还未进行赋值。

前向传播

现在实现TensorFlow前向传播的模块。该函数将接收参数字典并完成正向传播。可以使用的函数是:

  • tf.add(...,...) 矩阵加法
  • tf.matmul(...,...) 矩阵乘法
  • tf.nn.relu(...) ReLu激活函数

问题:实现神经网络的正向传播。将numpy和TensorFlow进行比较。注意前向传播在z3处停止,因为在TensorFlow中,最后的线性层输出作为损失函数的输入。因此,不需要计算a3.

# GRADED FUNCTION: forward_propagation

def forward_propagation(X, parameters):
"""
前向传播模型: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SOFTMAX

Arguments:
X -- input dataset placeholder, of shape (input size, number of examples)
parameters -- python字典,包含 "W1", "b1", "W2", "b2", "W3", "b3"
其形状由initialize_parameters函数给出

Returns:
Z3 -- 最后一个线性层的输出
"""

# 从"parameters"字典中恢复参数
W1 = parameters['W1']
b1 = parameters['b1']
W2 = parameters['W2']
b2 = parameters['b2']
W3 = parameters['W3']
b3 = parameters['b3']


### 填写代码 ### # Numpy 等式:
Z1 = None # Z1 = np.dot(W1, X) + b1
A1 = None # A1 = relu(Z1)
Z2 = None # Z2 = np.dot(W2, a1) + b2
A2 = None # A2 = relu(Z2)
Z3 = None # Z3 = np.dot(W3,Z2) + b3
### 完成代码 ###

return Z3

代码运行结果为:

Z3 = Tensor("add_2:0", shape=(6, ?), dtype=float32)

此时发现前向传播不会输出任何值,在反向传播的过程中可以知道原因。

代价函数

在之前的实验中,代价函数可以根据一行代码给出:

tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = ..., labels = ...))

问题:补充代价函数

  • tf.nn.softmax_cross_entropy_with_logits的“logits”和“labels”输入具有的形状应为(样本数,类别数),这里提供已经变形后的Z3和Y
  • tf.reduce_mean是对样本进行求和
# GRADED FUNCTION: compute_cost 

def compute_cost(Z3, Y):
"""
计算损失

Arguments:
Z3 -- 前向传播得到的输出 (模型最后一层线性层的输出), 形状为 (6, 样本数)
Y -- 标签向量placeholder, 和Z3有相同的形状

Returns:
cost - 代价函数的张量
"""

# 需要用到 tf.nn.softmax_cross_entropy_with_logits(...,...)
logits = tf.transpose(Z3)
labels = tf.transpose(Y)

### 填写代码 ###
cost = None
### 完成代码 ###

return cost

测试代码:

tf.reset_default_graph()

with tf.Session() as sess:
X, Y = create_placeholders(12288, 6)
parameters = initialize_parameters()
Z3 = forward_propagation(X, parameters)
cost = compute_cost(Z3, Y)
print("cost = " + str(cost))

代码运行结果为:

cost = Tensor("Mean:0", shape=(), dtype=float32)

反向传播

使用TensorFlow时,所有反向传播的参数更新都使用1行代码处理。计算了代价函数后,创建一个“optimizer”对象,运行tf.session时,对象必须和代价函数同时运行。当它被调用时,优化给定的方法和学习率的代价函数。例如,使用梯度下降法优化代价函数:

optimizer = tf.train.GradientDescentOptimizer(learning_rate = learning_rate).minimize(cost)

为了运行这个对象,需要:

_ , c = sess.run([optimizer, cost], feed_dict={X: minibatch_X, Y: minibatch_Y})

在计算图中,从成本到输入,反向地计算,来更新参数值。 注意:在编码时,通常使用“ _ ”作为一次性变量,存储以后不需要使用的值,c为代价的值。

创建模型

将上面的模块融合到一起就是一个神经网络的模型。 练习:创建model,使用上面完成的函数。

def model(X_train, Y_train, X_test, Y_test, learning_rate = 0.0001,
num_epochs = 1500, minibatch_size = 32, print_cost = True):
"""
创建一个三层的TensorFlow神经网络: LINEAR->RELU->LINEAR->RELU->LINEAR->SOFTMAX.

Arguments:
X_train -- 训练集,形状为: (输入特征 = 12288, 训练样本数 = 1080)
Y_train -- 训练集的标签,形状为 (输出标签 = 6, 训练样本数 = 1080)
X_test -- 测试集,形状为 (输入特征 = 12288, 测试样本数 = 120)
Y_test -- 测试集的标签,形状为 (输出标签 = 6, 测试样本数 = 120)
learning_rate -- 学习率
num_epochs -- 迭代次数
minibatch_size -- 小批量的大小
print_cost -- 每迭代100次打印一次代价值

Returns:
parameters -- 模型学习得到的参数,可以直接用于预测。
"""

ops.reset_default_graph()
tf.set_random_seed(1) # 便于产生相同的输出
seed = 3 # 便于产生相同的输出
(n_x, m) = X_train.shape # (n_x: 输入样本特征, m : 训练集样本数)
n_y = Y_train.shape[0] # n_y : 输出标签数
costs = [] # 记录cost

# 创建形状为 (n_x, n_y)的placeholder
### 填写代码 ###
X, Y = None
### 完成代码 ###

# 初始化参数
### 填写代码 ###
parameters = None
### 完成代码 ###


# 前向传播: 创建前向传播计算图
### 填写代码 ###
Z3 = None
### 完成代码 ###

# 代价函数: 在计算图中增加代价函数
### 填写代码 ###
cost = None
### 完成代码 ###

# 反向传播: 使用 AdamOptimizer优化代价函数。
### 填写代码 ###
optimizer = None
### 完成代码 ###

# 初始化所有变量
init = tf.global_variables_initializer()

# 运行session计算TensorFlow图
with tf.Session() as sess:

# 运行初始化
sess.run(init)

# 循环训练
for epoch in range(num_epochs):

epoch_cost = 0. # 定义每次迭代的代价函数
num_minibatches = int(m / minibatch_size) # 训练集选择的小批量的大小
seed = seed + 1
minibatches = random_mini_batches(X_train, Y_train, minibatch_size, seed)

for minibatch in minibatches:

# 选择一个批量
(minibatch_X, minibatch_Y) = minibatch

# 运行session执行 "optimizer"最小化 "cost"
### 填写代码 ###
_ , minibatch_cost = None
### 完成代码 ###

epoch_cost += minibatch_cost / num_minibatches

# 每100次迭代输出一次代价值
if print_cost == True and epoch % 100 == 0:
print ("Cost after epoch %i: %f" % (epoch, epoch_cost))
if print_cost == True and epoch % 5 == 0:
costs.append(epoch_cost)

# 画出代价值随迭代次数变化的图
plt.plot(np.squeeze(costs))
plt.ylabel('cost')
plt.xlabel('iterations (per tens)')
plt.title("Learning rate =" + str(learning_rate))
plt.show()

# 存储参数变量
parameters = sess.run(parameters)
print ("Parameters have been trained!")

# 计算正确率
correct_prediction = tf.equal(tf.argmax(Z3), tf.argmax(Y))

# 计算测试集的正确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

print ("Train Accuracy:", accuracy.eval({X: X_train, Y: Y_train}))
print ("Test Accuracy:", accuracy.eval({X: X_test, Y: Y_test}))

return parameters

测试代码:

parameters = model(X_train, Y_train, X_test, Y_test)

代码运行结果:

Train Accuracy: 0.999074
Test Accuracy: 0.725

  • 模型看起来可以很好地训练数据集,但是,由于训练集和测试集的准确率之间差异过大,可以尝试添加L2或Dropout正则化来减少过拟合。
  • 将Session看作是一个训练模型的代码块,每次都在小批量的训练数据上运行会话训练参数。训练过1500次之后可以得到很好的参数。

测试自己的图片

将自己想要测试的图片添加到“images”文件夹中,再运行下列代码:

import scipy
from PIL import Image
from scipy import ndimage

### 填写代码 ### (PUT YOUR IMAGE NAME)
my_image = "thumbs_up.jpg"
### 完成代码 ###

fname = "images/" + my_image
image = np.array(ndimage.imread(fname, flatten=False))
my_image = scipy.misc.imresize(image, size=(64,64)).reshape((1, 64*64*3)).T
my_image_prediction = predict(my_image, parameters)

plt.imshow(image)
print("Your algorithm predicts: y = " + str(np.squeeze(my_image_prediction)))

总结:

  • TensorFlow是用于深度学习的编程框架

  • TensorFlow中的两个主要对象类是张量和操作符。

  • 在TensorFlow中编码时,必须采取以下步骤:

    • 创建一个包含张量(变量,占位符...)和操作(tf.matmul,tf.add,...)的图形
    • 创建一个会话
    • 初始化会话
    • 运行会话以执行图形
  • 可以多次执行计算图,如本实验中的model()

  • 在“优化器”对象上运行会话时,会自动完成反向传播和优化

Author: NYY
Link: http://yoursite.com/2018/12/06/tensorflow/2.1create_neural_network/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.