在一步步实现卷积神经网络的文章中,详细介绍了如何利用numpy
实现了一个卷积神经网络,本篇文章将利用tensorflow
实现一个卷积神经网络的手势识别项目。
导入相关库
利用tensorflow实现一个卷积神经网络时,首先,需要导入需要用到的python库,如下所示:
import math
import numpy as np
import h5py
import matplotlib.pyplot as plt
import scipy
from PIL import Image
from scipy import ndimage
import tensorflow as tf
from tensorflow.python.framework import ops
from cnn_utils import *
%matplotlib inline
np.random.seed(1)
数据集处理
加载数据集
首先,加载准备好的数据集,如下代码所示:
def load_dataset():
train_dataset = h5py.File('datasets/train_signs.h5', "r")
train_set_x_orig = np.array(train_dataset["train_set_x"][:]) #训练集特征
train_set_y_orig = np.array(train_dataset["train_set_y"][:]) # 训练集标签
test_dataset = h5py.File('datasets/test_signs.h5', "r")
test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # 测试集特征
test_set_y_orig = np.array(test_dataset["test_set_y"][:]) # 测试集标签
classes = np.array(test_dataset["list_classes"][:]) # 类别列表
train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes
数据集的表示如下所示:
加载数据集,并随机显示其中的一个数据,如下所示:
X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = load_dataset()
index = 6
plt.imshow(X_train_orig[index])
print ("y = " + str(np.squeeze(Y_train_orig[:, index])))
数据归一化处理
图像矩阵的取值范围是(0-255),需要对图像数据归一化处理,即每个像素值除以255,具体代码实现如下所示:
X_train = X_train_orig/255.
X_test = X_test_orig/255.
独热编码处理
对于多元分类问题,需要使用独热编码进行处理,关于独热编码的实现,有两种方法,一种是利用numpy
实现,另外一种是tensorflow
实现,这两种方法都在tensorflow中有过介绍,这里,直接给出独热编码的numpy实现,如下代码所示:
def convert_to_one_hot(Y, C):
Y = np.eye(C)[Y.reshape(-1)].T
return Y
为了确保后续程序的运行正确,可以打印出数据集的维数,如下代码所示:
Y_train = convert_to_one_hot(Y_train_orig, 6).T
Y_test = convert_to_one_hot(Y_test_orig, 6).T
print ("number of training examples = " + str(X_train.shape[0]))
print ("number of test examples = " + str(X_test.shape[0]))
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))
创建placeholder
tensorflow程序需要在运行之前,创建placeholder,当运行tensorflow的session时,再将真正数据填入其中,类似于函数形参和实参的概念,具体实现如下代码所示:
def create_placeholders(n_H0, n_W0, n_C0, n_y):
"""
为tensorflow的session创建placeholder
参数:
n_H0 -- 标量,输入图像的高度
n_W0 -- 标量,输入图像的宽度
n_C0 --标量,输入图像的通道
n_y -- 标量,分类的类别数
Returns:
X -- placeholder for the data input, of shape [None, n_H0, n_W0, n_C0] and dtype "float"
Y -- placeholder for the input labels, of shape [None, n_y] and dtype "float"
"""
X = tf.placeholder(name='X', shape=(None, n_H0, n_W0, n_C0), dtype=tf.float32)
Y = tf.placeholder(name='Y', shape=(None, n_y), dtype=tf.float32)
return X, Y
初始化权重参数
在tensorflow实现权重参数时,只需要实现参数即可,而不需要手动实现偏置参数。另外,用tensorflow实现卷积神经网络时,只需初始化卷积层的权重参数即可,而全连接层的参数,tensorflow会自动实现。
具体实现,如下代码所示;
def initialize_parameters():
tf.set_random_seed(1)
W1 = tf.get_variable(name='W1', dtype=tf.float32, shape=(4, 4, 3, 8), initializer=tf.contrib.layers.xavier_initializer(seed = 0))
W2 = tf.get_variable(name='W2', dtype=tf.float32, shape=(2, 2, 8, 16), initializer=tf.contrib.layers.xavier_initializer(seed = 0))
parameters = {"W1": W1,
"W2": W2}
return parameters
用tensorflow实现前向传播
用tensorflow实现卷积神经网络前向传播的过程如下所示:
def forward_propagation(X, parameters):
"""
实现前向传播的模型
CONV2D -> RELU -> MAXPOOL -> CONV2D -> RELU -> MAXPOOL -> FLATTEN -> FULLYCONNECTED
参数:
X -- 输入数据集
参数 -- python字典,包含权重参数
返回值:
Z3 -- 最后一层线性单元的输出
"""
#得到参数
W1 = parameters['W1']
W2 = parameters['W2']
# 卷积步长是1,填充方式为same
Z1 = tf.nn.conv2d(input=X, filter=W1, strides=(1, 1, 1, 1), padding='SAME')
# 线性激活函数
A1 = tf.nn.relu(Z1)
# 最大池化层,步长为1
P1 = tf.nn.max_pool(value=A1, ksize=(1, 8, 8, 1), strides=(1, 8, 8, 1), padding='SAME')
# 卷积层,过滤矩阵为W2
Z2 = tf.nn.conv2d(input=P1, filter=W2, strides=(1, 1, 1, 1), padding='SAME')
# 线性激活单元
A2 = tf.nn.relu(Z2)
# 最大池化层,4×4的窗口,填充方式为‘SAME’
P2 = tf.nn.max_pool(value=A2, ksize=(1, 4, 4, 1), strides=(1, 4, 4, 1), padding='SAME')
#转化为一维
P2 = tf.contrib.layers.flatten(inputs=P2)
# 使用非线性激活函数的全连接层
# 输出层,包含六个神经元.
Z3 = tf.contrib.layers.fully_connected(P2, 6, activation_fn=None)
return Z3
计算损失
tensoflow提供了以下两个函数用以计算损失,具体解释如下所示:
- tf.nn.softmax_cross_entropy_with_logits(logits = Z3, labels = Y): 该函数既可以计算softmax激活函数,也可以计算由此产生的softmax的熵损失,详细介绍可以查阅 相关文档。
- cost=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=Z3, labels=Y)):计算整个张量维度的元素均值,用它来对所有样本的损失求和,以获得总体损失。详细信息,可以参考相关文档。
def compute_cost(Z3, Y):
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=Z3, labels=Y))
return cost
构建模型
通过以上准备,现在可以构建一个卷积神经网络,具体步骤,如下所示:
- 创建placeholder
- 初始化参数
- 前向传播
- 计算损失
- 进行优化
具体实现,如下代码所示:
def model(X_train, Y_train, X_test, Y_test, learning_rate = 0.009,
num_epochs = 100, minibatch_size = 64, print_cost = True):
"""
用tensorflow实现一个三层的卷积神经网络:
CONV2D -> RELU -> MAXPOOL -> CONV2D -> RELU -> MAXPOOL -> FLATTEN -> FULLYCONNECTED
参数:
X_train --训练集 shape (None, 64, 64, 3)
Y_train -- 训练集输出,shape (None, n_y = 6)
X_test -- 测试集输入 (None, 64, 64, 3)
Y_test -测试集输出 shape (None, n_y = 6)
learning_rate --学习率
num_epochs -- 迭代次数
minibatch_size -- min-batch的尺寸
print_cost -- 为True时,打印出每100次迭代的损失
返回值:
train_accuracy -- 训练集的精度(X_train)
test_accuracy -- 测试集的精度 (X_test)
parameters -- 用于预测的模型参数
"""
ops.reset_default_graph() # 能够在不覆盖tf变量的情况下重新运行模型
tf.set_random_seed(1) # 随机种子,保持结果一致
seed = 3
(m, n_H0, n_W0, n_C0) = X_train.shape
n_y = Y_train.shape[1]
costs = [] # 跟踪损失
# 创建一个placeholder
X, Y = create_placeholders(n_H0, n_W0, n_C0, n_y)
# 初始化参数
parameters = initialize_parameters()
# 前向传播
Z3 = forward_propagation(X, parameters)
#损失函数
cost = compute_cost(Z3, Y)
# 反向传播,定义一个tensorflow优化器,用 AdamOptimizer 最小化损失
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
# 初始化全局变量
init = tf.global_variables_initializer()
# 运行一个session进行计算
with tf.Session() as sess:
sess.run(init)
# 迭代训练
for epoch in range(num_epochs):
minibatch_cost = 0.
num_minibatches = int(m / minibatch_size) # min-batch的数量
seed = seed + 1
minibatches = random_mini_batches(X_train, Y_train, minibatch_size, seed)
for minibatch in minibatches:
# 选择一个minbatch
(minibatch_X, minibatch_Y) = minibatch
_ , temp_cost = sess.run([optimizer, cost], feed_dict={X:minibatch_X, Y:minibatch_Y})
minibatch_cost += temp_cost / num_minibatches
# 打印出每次迭代的损失
if print_cost == True and epoch % 5 == 0:
print ("Cost after epoch %i: %f" % (epoch, minibatch_cost))
if print_cost == True and epoch % 1 == 0:
costs.append(minibatch_cost)
# 绘制损失
plt.plot(np.squeeze(costs))
plt.ylabel('cost')
plt.xlabel('iterations (per tens)')
plt.title("Learning rate =" + str(learning_rate))
plt.show()
# 预测,取得输出值为1的索引
predict_op = tf.argmax(Z3, 1)
correct_prediction = tf.equal(predict_op, tf.argmax(Y, 1))
# 计算测试集的精度
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print(accuracy)
train_accuracy = accuracy.eval({X: X_train, Y: Y_train})
test_accuracy = accuracy.eval({X: X_test, Y: Y_test})
print("Train Accuracy:", train_accuracy)
print("Test Accuracy:", test_accuracy)
return train_accuracy, test_accuracy, parameters
最后,绘制出的损失曲线如下所示和精度如下所示:
网友评论