tensorflow 学习
前言
在之前的中,已经通过python中的numpy库实现了神经网络。现在,可以通过一些深度学习框架更高效的实现神经网络,这些深度学习框架包括pytroch,tensorflow,caffe和keras等。本篇文章将通过利用tensorflow框架实现一个神经网络,并实现一个手势识别的项目。
tensorflow初体验
导入相关库
首先,需要导入相关python库,如下代码所示:
import math
import numpy as np
import h5py
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.python.framework import ops
%matplotlib inline
np.random.seed(1)
tensorflow张量定义及使用
首先,通过tensorflow实现一个损失函数的计算,损失函数的公式,如下所示:
y_hat = tf.constant(36, name='y_hat') # 定义y_hat常量,36
y = tf.constant(39, name='y') # 定义y,39
loss = tf.Variable((y - y_hat)**2, name='loss') # 创建一个损失变量
init = tf.global_variables_initializer() # 创建一个全局变量初始化
# 损失变量计算前被初始化
with tf.Session() as session: # 创建一个会话并打印输出
session.run(init) # 初始化变量
print(session.run(loss)) # 打印输出
以上,可以总结运行一个tensorflow程序的四个步骤:
- 创建未被执行的tensorflow张量(变量)
- 操作这些张量(建立表达式)
- 初始化这些张量
- 创建一个Session
- 运行Session,这个Session会计算张量的表达式
事实上,创建张量语句并不会给这些张量赋值,当初始化之后,在session中这些张量才会被赋值并运算。
placeholder是一个只允许稍后指定其值的对象,给placeholder赋值时,可以用feed dictionary
实现,具体如下代码所示:
x = tf.placeholder(tf.int64, name = 'x')
print(sess.run(2 * x, feed_dict = {x: 3}))
sess.close()
如上述代码所示,定义了一个x
张量,但是并未赋值,通过placeholder对象的声明,可以在其运行过程中利用feed_dict
字典参数为其赋值。
tensorflow实现线性函数
现在可以通过tensorflow实现一个线性函数了,其函数表达式如公式(2)所示:
其中参数的维数为和,输入的维数为,偏置参数的维数是$(4,1),具体的实现代码如下所示:
def linear_function():
np.random.seed(1)
X = tf.constant(np.random.randn(3,1),name = "X")
W = tf.constant(np.random.randn(4,3),name="W")
b = tf.constant(np.random.randn(4,1),name = "b")
Y = tf.constant(np.random.randn(4,1),name="Y")
sess = tf.Session()
result = sess.run(tf.add(tf.matmul(W,X),b))
sess.close()
return result
tensorflow实现激活函数
使用tensorflow可以很方便的实现激活函数,实现激活函数是,需要注意的一点是需要利用到plaaceholder对象,具体实现代码如下所示:
def sigmoid(z):
x = tf.placeholder(tf.float32,name ='x')
sigmoid = tf.sigmoid(x)
with tf.Session() as sess:
result = sess.run(sigmoid,feed_dict={x:z})
return result
计算损失
交叉熵损失的计算公式如下所示:
在代码实现过程中,输入,利用激活函数求得,再根据公式(3)求得交叉熵损失。具体实现代码如下所示:
def cost(logits, labels):
z = tf.placeholder(tf.float32,name = 'z')
y = tf.placeholder(tf.float32,name='y')
cost = tf.nn.sigmoid_cross_entropy_with_logits(logits = z,labels = y)
sess = tf.Session()
cost = sess.run(cost,feed_dict={z:logits,y:labels})
sess.close()
return cost
独热编码
对于多元分类问题,假设要分为类,则可以用一个范围的向量表示,但是为了计算的仿表,需要将其转换为用0和1表示的矩阵,具体过程可以如下图所示:
根据上图描述,在tensorflow中可以很轻松的实现,具体如下所示:
def one_hot_matrix(labels, C):
C =tf.constant(C,name = 'C')
one_hot_matrix = tf.one_hot(labels,C,axis = 0)
sess = tf.Session()
one_hot = sess.run(one_hot_matrix)
sess.close()
return one_hot
初始化为0和1
初始化为0和1的矩阵在tensorflow中用tf.ones(shape)
实现,shape
表示的是初始化后的矩阵形状,在tensorflow中初始化一个全为1的矩阵可以用以下代码实现,具体实现如下所示:
def ones(shape):
ones = tf.ones(shape)
sess = tf.Session()
ones = sess.run(ones)
sess.close()
return ones
用tensorflow搭建一个神经网络
这一部分将利用tensorflow构建一个算法用来识别手势,整个数据集分为以下两部分:
- 训练数据集:1080张64×64像素的手势图片用来表示,每个手势的图片是180张。
- 测试数据集:120帐64×64像素的手势图片,每个手势的图片是20张。
一些图片数据集和表示的数字之间的对应关系如下图所示:
首先,加载训练集和测试集,具体如下代码所示:
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"][:]) # 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
数据集标签和图像之间的示例如下所示:
index = 0
plt.imshow(X_train_orig[index])
print ("y = " + str(np.squeeze(Y_train_orig[:, index])))
对于图像数据集,归一化的方法是每一个输入特征都除以255,对于矩阵表示的3通道图像,需要将其转化为一个向量,实现方式是3×64×64
(3表示图片是rgb的3通道,64表示图像像素),并且,对于输出标签,需将其实现独热编码,具体的实现,如下代码所示:
# 转化训练集和测试集特征为向量
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.
# 转化为独热编码矩阵
Y_train = convert_to_one_hot(Y_train_orig, 6)
Y_test = convert_to_one_hot(Y_test_orig, 6)
用numpy实现的独热编码转化实现如下代码所示:
def convert_to_one_hot(Y, C):
Y = np.eye(C)[Y.reshape(-1)].T
return Y
以上代码中,reshape(-1)
表示将其转化为一个一维向量,np.eye(C)
生成一个单位矩阵,而Y = np.eye(C)[Y.reshape(-1)].T
表示按单位矩阵的列索引取值。
创建输入和输出的placeholder
首先,需要利用tensorflow创建输入变量和输出变量的placholder,以便后续session计算,具体实现代码如下:
def create_placeholders(n_x, n_y):
X = tf.placeholder(tf.float32,shape=(12288,None),name = 'X')
Y = tf.placeholder(tf.float32,shape=(6,None),name = 'Y')
return X, Y
参数初始化
利用tensorflow实现参数初始化的方式如下代码所示,在实现过程中,需要注意每个参数的维数,实现代码如下:
def initialize_parameters():
"""
用tensorflow初始化权重参数,各个参数的维数如下所示
W1 : [25, 12288]
b1 : [25, 1]
W2 : [12, 25]
b2 : [12, 1]
W3 : [6, 12]
b3 : [6, 1]
"""
tf.set_random_seed(1)
W1 = tf.get_variable("W1",[25,12288],initializer = tf.contrib.layers.xavier_initializer())
b1 = tf.get_variable("b1",[25,1],initializer = tf.zeros_initializer())
W2 = tf.get_variable("W2",[12,25],initializer = tf.contrib.layers.xavier_initializer())
b2 = tf.get_variable("b2",[12,1],initializer = tf.zeros_initializer())
W3 = tf.get_variable("W3",[6,12],initializer = tf.contrib.layers.xavier_initializer())
b3 = tf.get_variable("b3",[6,1],initializer = tf.zeros_initializer())
parameters = {"W1": W1,
"b1": b1,
"W2": W2,
"b2": b2,
"W3": W3,
"b3": b3}
return parameters
tensorflow实现前向传播
根据前向传播的实现方式,tensorflow的前向传播如下所示:
def forward_propagation(X, parameters):
W1 = parameters['W1']
b1 = parameters['b1']
W2 = parameters['W2']
b2 = parameters['b2']
W3 = parameters['W3']
b3 = parameters['b3']
Z1 = tf.add(tf.matmul(W1, X), b1)
A1 = tf.nn.relu(Z1)
Z2 = tf.add(tf.matmul(W2, A1),b2)
A2 = tf.nn.relu(Z2)
Z3 = tf.add(tf.matmul(W3,A2),b3)
return Z3
计算损失
在tensorflow中,可以很方便的计算损失,具体代码如下所示,其中参数logits
代表前向传播的输出,参数labels
代表输出的标签。
具体实现代码如下所示:
def compute_cost(Z3, Y):
logits = tf.transpose(Z3)
labels = tf.transpose(Y)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = logits,labels=labels))
return cost
构建模型
在tensorflow中实现反向传播和参数更新可以用一行代码轻松实现,也可以直接在tensorflow中的session中实现,并且加以优化。具体的实现代码如下所示:
def model(X_train, Y_train, X_test, Y_test, learning_rate = 0.0001,
num_epochs = 1500, minibatch_size = 32, print_cost = True):
"""
实现一个三层神经网络: LINEAR->RELU->LINEAR->RELU->LINEAR->SOFTMAX.
参数:
X_train -- 训练数据集 维数 (input size = 12288, number of training examples = 1080)
Y_train -- 训练集输出, 维数 (output size = 6, number of training examples = 1080)
X_test -- 测试集输入,维数 (input size = 12288, number of training examples = 120)
Y_test -- 测试集输出,维数 (output size = 6, number of test examples = 120)
learning_rate --学习率
num_epochs -- 迭代次数
minibatch_size --mini—batch大小
print_cost --设置为True,每1000次迭代打印损失
返回值:
parameters -- 模型参数,用来预测
"""
ops.reset_default_graph() # 能够返回不用重写的变量
tf.set_random_seed(1) # 保存随机种子
seed = 3
(n_x, m) = X_train.shape # 训练集维数(n_x,m),创建placeholder时,列数设置为None的原因
n_y = Y_train.shape[0] # n_y : 输出标签的大小
costs = [] # 保存损失
X, Y = create_placeholders(n_x,n_y)
#初始化参数
parameters = initialize_parameters()
#前向传播的实现
Z3 = forward_propagation(X,parameters)
#计算损失
cost = compute_cost(Z3,Y)
#反向传播的定义,使用Adam优化算法
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cost)
# 初始化变量
init = tf.global_variables_initializer()
# 创建Session
with tf.Session() as sess:
# 运行初始化
sess.run(init)
# 迭代训练
for epoch in range(num_epochs):
epoch_cost = 0. # 定义每次迭代相关的损失
num_minibatches = int(m / minibatch_size) # 训练集mini-batch的大小
seed = seed + 1
minibatches = random_mini_batches(X_train, Y_train, minibatch_size, seed)
for minibatch in minibatches:
# 选择一个mini-batch
(minibatch_X, minibatch_Y) = minibatch
_ , minibatch_cost = sess.run([optimizer, cost], feed_dict={X:minibatch_X, Y:minibatch_Y})
epoch_cost += minibatch_cost / num_minibatches
# 打印出每次迭代的损失
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
经过多次迭代之后,结果如下所示:
预测
根据上述训练的参数,可以很方便的用来预测自己的图片,具体实现如下代码所示:
import scipy
from PIL import Image
from scipy import ndimage
#图片路径和名称
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)))