tensorflow 学习

2019-10-29  本文已影响0人  此间不留白

前言

在之前的中,已经通过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实现一个损失函数的计算,损失函数的公式,如下所示:
loss = \mathcal{L}(\hat{y}, y) = (\hat y^{(i)} - y^{(i)})^2 \tag{1}


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程序的四个步骤:

事实上,创建张量语句并不会给这些张量赋值,当初始化之后,在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)所示:
Y = WX+b\tag{2}
其中参数W的维数为(4,3)和,输入X的维数为(3,1),偏置参数b的维数是$(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

计算损失

交叉熵损失的计算公式如下所示:
J = - \frac{1}{m} \sum_{i = 1}^m \large ( \small y^{(i)} \log a^{ [2] (i)} + (1-y^{(i)})\log (1-a^{ [2] (i)} )\large )\small\tag{3}
在代码实现过程中,输入z,利用激活函数求得a,再根据公式(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

独热编码

对于多元分类问题,假设要分为C类,则可以用一个(0,C-1)范围的向量表示,但是为了计算的仿表,需要将其转换为用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构建一个算法用来识别手势,整个数据集分为以下两部分:

一些图片数据集和表示的数字之间的对应关系如下图所示:


首先,加载训练集和测试集,具体如下代码所示:


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表示按单位矩阵的列索引取值。

创建输入X和输出Y的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)))
上一篇下一篇

猜你喜欢

热点阅读