深度学习项目拆解:识别猫的项目

Posted Debroon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度学习项目拆解:识别猫的项目相关的知识,希望对你有一定的参考价值。

神经网络训练步骤

数据准备

数据下载:https://download.csdn.net/download/qq_41739364/22020268(都是一些动物的图片,像猫、狗、鸟)

import h5py                                                      # 数据是 H5 文件,需要 h5 模块
import matplotlib.pyplot as plt                                  # 绘图库,之后会查看图片
import numpy as np

train_dataset = h5py.File('./我的数据集/train_catvnoncat.h5','r') # 读取训练集(创建了一个字典格式的对象)
test_dataset = h5py.File('./我的数据集/test_catvnoncat.h5','r')   # 读取测试集(创建了一个字典格式的对象)

# 因为对象是字典,所以用数据之前,还需要键名
for key1 in train_dataset.keys():
	print( train_dataset[key1].name )                           # 查看训练集键名(标签类别list_classes、图片特征train_set_x、图片标签train_set_y)
	
for key2 in test_dataset.keys():
	print( test_dataset[key2].name )                            # 查看测试集键名(标签类别list_classes、图片特征test_set_x、图片标签test_set_y)

train_set_x = np.array(train_dataset["train_set_x"][:])         # 通过键名,访问所有数据,并保存到数组里
train_set_y = np.array(train_dataset["train_set_y"][:])         # 通过键名,访问所有数据,并保存到数组里

test_set_x = np.array(test_dataset["test_set_x"][:])            # 通过键名,访问所有数据,并保存到数组里
test_set_y = np.array(test_dataset["test_set_y"][:])            # 通过键名,访问所有数据,并保存到数组里


print("train_set_x.shape = ",train_set_x.shape)                 # 查看训练集数组维度( (209, 64, 64, 3) 意思是,209张图片,尺寸是 64*64*3,64x64x3 = 12288个特征 )
print("test_set_x.shape = ",test_set_x.shape)                   # 查看测试集数组维度( (50, 64, 64, 3) 意思是,50张图片,尺寸是 64*64*3,64x64x3 = 12288个特征 )


一张图片是一个三维数组 64 * 64 * 3,64 是长和宽,3 是 RGB 三个通道,因为这是一张彩色图片,每个像素点必须用三个数来代表,一张图片就要用 1 万多个数来描述。

209 张图片,是一个四维数组 209 * 64 * 64 * 3,而神经元的输入是二维的列矩阵 209 * 1,所以我们要转换一下。

# 图片特征 xx_set_x:(209, 64, 64, 3) -> (12288,209)
train_set_x = train_set_x.reshape(train_set_x.shape[0], -1).T    # (209, 64, 64, 3) 变成 (12288,209)
test_set_x = test_set_x.reshape(test_set_x.shape[0], -1).T       # (209, 64, 64, 3) 变成 (12288,50)

# 图片标签 xx_set_y:(209,) -> (1,209)
train_set_y = train_set_y.reshape(1,train_set_y.shape[0])        # (209,) 变成 (1,209)
test_set_y = test_set_y.reshape(1,test_set_y.shape[0])           # (50,) 变成 (1,50)

# P.S. 标签类别list_classes 非0即1,0即非猫,1即是猫。

最后归一化,我们要把数据经过处理后使之限定在一定的范围内。比如通常限制在区间 [0, 1]

train_set_x = train_set_x/255.0       
test_set_x = test_set_x/255.0
# 除以255.0是为了使数据的取值范围在sigmoid激励函数的取值范围内
#【注:灰度图像素取值为0-255,相除后取值在0-1之间,符合激励函数的输出范围】

好,现在我们把数据准备这部分封装为一个函数。

import h5py                                                      # 数据是 H5 文件,需要 h5 模块
import matplotlib.pyplot as plt                                  # 绘图库,之后会查看图片
import numpy as np

def load_dataset():
    # 创建文件对象
    train_dataset = h5py.File('./我的数据集/train_catvnoncat.h5','r')
    test_dataset = h5py.File('./我的数据集/test_catvnoncat.h5','r')
    
    # 读取数据
    train_set_x = np.array(train_dataset["train_set_x"][:])
    train_set_y = np.array(train_dataset["train_set_y"][:])
    test_set_x = np.array(test_dataset["test_set_x"][:])
    test_set_y = np.array(test_dataset["test_set_y"][:])

	# 查看第 110 张图片
    plt.figure(figsize=(2,2))
    plt.imshow(train_set_x[110])
    plt.show()
    
    # 变化维度以适应神经网络输入
    train_set_x = train_set_x.reshape(train_set_x.shape[0],-1).T  
    test_set_x = test_set_x.reshape(test_set_x.shape[0],-1).T  
    train_set_y = train_set_y.reshape(1,train_set_y.shape[0]) 
    test_set_y = test_set_y.reshape(1,test_set_y.shape[0]) 

	# 数据归一化
    train_set_x = train_set_x/255.0       
    test_set_x = test_set_x/255.0
    
    return train_set_x,train_set_y,test_set_x,test_set_y

if __name__ == '__main__':
    train_set_x, train_set_y, test_set_x, test_set_y = load_dataset()
    # 加载数据

 


定义神经网络结构

加载数据后,定义神经网络结构。

if __name__ == '__main__':
    load_dataset()
    # 加载数据
   
	fc_net = [12288, 4, 3, 2, 1] 
	# 用数组定义神经网络结构,输入层(12288个神经元)->第1层->第2层->第3层->第4层->a,有多少层、一层多少神经元,都取决于工程师的直觉,差不多就行   
	# 输出值a,a>0.5 ? 1:0

 


解析神经网络、初始化参数(w、b)

定义神经网络结构之后,解析神经网络

if __name__ == '__main__':
    train_set_x, train_set_y, test_set_x, test_set_y = load_dataset()
    # 加载数据
    
    fc_net = [12288, 4, 3, 2, 1] 
	# 用数组定义神经网络结构,输入层(12288个神经元)->第1层->第2层->第3层->第4层->a  
	# 输出值a,a>0.5 ? 1:0
    
    parameters = init_parameters(fc_net)
    # 解析神经网络,初始化权值

现在我们来实现初始化权值 init_parameters 的实现。

# 初始化参数
def init_parameters(fc_net):  
    parameters =                
    # 定义一个字典,存放参数矩阵W1,b1,W2,b2,W3,b3,W4,b4
    Layer_num = len(fc_net)       
    # 神经网络的层数,通过获取数组长度可得
    
    for L in range(1, Layer_num):  # 遍历层数,从第一层开始,但忽略输入层(第 0 层)
        parameters["W"+str(L)] = np.random.randn(fc_net[L], fc_net[L-1])*0.01   
        # 从标准正态分布中(取值范围[-3,3])选取值,为方便激活函数(取值范围[-1,1]),乘以0.01后,再赋值给创建的键值对(W1、W2、W3···)
        parameters["b"+str(L)] = np.zeros((fc_net[L], 1))
        
    for L in range(1, Layer_num):
        print("W"+str(L)+" = ", parameters["W"+str(L)].shape)
        print("b"+str(L)+" = ",  parameters["b"+str(L)].shape)
        
    return parameters

 


输入数据,前向传播

if __name__ == '__main__':
    train_set_x, train_set_y, test_set_x, test_set_y = load_dataset()
    # 加载数据
    
    fc_net = [12288, 4, 3, 2, 1] 
	# 输入层->第1层->第2层->第3层->第4层->a    
	# 输出值a,a>0.5 ? 1:0
    
    parameters = init_parameters(fc_net)
    # 初始化权值
    
    AL, cache = forward_pass(train_set_x, parameters, active_func = "tanh")
    # 最后一层激活值AL = train_set_x 输入数据、parameters W、b 保存位置、激活函数是 tanh

现在我们来实现激活函数。

def sigmoid(Z):
    return 1 / (1 + np.exp(-Z)) 

def tanh(Z): 
    return np.tanh(Z)

def ReLU(Z):    
    return np.maximum(0,Z) 

再来实现前向传播 forward_pass

def forward_pass(A0, parameters, active_func = "ReLU"):
    Layer_num = len(parameters) // 2  # 因为 parameters 是由 W、b 组成,所以除 2
    A = A0
    cache =         				  # 缓存 A 的字典
    cache["A0"] = A0  				  # 先缓存A0
     
    for L in range(1,Layer_num):      
        A_prev = A
        Z = np.dot(parameters["W"+str(L)],A_prev) + parameters["b"+str(L)]  # Z = WX + b
        cache["Z"+str(L)] = Z         # 缓存Z1 Z2 Z3 Z4
        
        if active_func == "sigmoid":  
            A = sigmoid(Z)            # sigmoid函数,适合用于深度网络
        elif active_func == "tanh":
            A = tanh(Z)               # tanh函数激活
        else:
            A = ReLU(Z)               # 1~3层用 ReLU 函数激活     
        cache["A"+str(L)] = A         # 继续缓存 A1 A2 A3 A4
    
    # 最末层采用sigmoid函数激活
    ZL = np.dot(parameters["W" + str(Layer_num)], A) + parameters["b" + str(Layer_num)]    #  1,2   2,209
    cache["Z" + str(Layer_num)] = ZL  # 缓存最末层的Z4
    AL = sigmoid(ZL)                  # sigmoid函数激活
    cache["A"+str(Layer_num)]=AL      # 继续缓存最末层的A4   
    
    return AL, cache

对比《深度学习食用指南》识别猫的项目,这里多了一个要缓存 A,这是为啥?

其实是一种算法策略:空间换时间,因为前向传播过程如下所示:

一般的前向传播过程,我们是一个一个计算的,处理完一个样本再处理下一个样本,等所有样本都处理完了,才能反向传播,求参数的梯度。

能不能同时计算呢?

  • a 0 a_0 a0 是输入样本,是不需要计算就可以得到的,我们把上面的 a 0 a_0 a0 堆叠起来。
  • b i b_i bi 也堆叠起来,后面的输出值 a i a_i ai 也堆叠起来。
  • w i w_i wi 矩阵也移过来。

就把多个样本中用 for 循环实现的多步串行计算,改成了用矩阵实现的一步完成的并行计算。

 


得到本轮迭代的损失值

if __name__ == '__main__':
    train_set_x, train_set_y, test_set_x, test_set_y = load_dataset()
    # 加载数据
    
    fc_net = [12288, 4, 3, 2, 1] 
	# 输入层->第1层->第2层->第3层->第4层->a    
	# 输出值a,a>0.5 ? 1:0
    
    parameters = init_parameters(fc_net)
    # 初始化权值
    
    AL,cache= forward_pass(train_set_x, parameters)
    # 最后一层激活值AL = train_set_x 输入数据、parameters W、b 保存位置

	cost = compute_cost(AL, train_set_y)

使用损失函数计算,预测值与标签值(真实值)的差距。

def compute_cost(AL, Y):
    m = Y.shape[1]                           # Y =(1,209)
    cost = (1/m)*np.sum((1/2)*(AL-Y)*(AL-Y)) # 代价函数
    return cost

 


求最末层误差,反向传播计算各层梯度

if __name__ == '__main__':
    train_set_x, train_set_y, test_set_x, test_set_y = load_dataset()
    # 加载数据
    
    fc_net = [12288, 4, 3, 2, 1] 
	# 输入层->第1层->第2层->第3层->第4层->a    
	# 输出值a,a>0.5 ? 1:0
    
    parameters = init_parameters(fc_net)
    # 初始化权值
    
    AL,cache= forward_pass(train_set_x, parameters)
    # 最后一层激活值AL = train_set_x 输入数据、parameters W、b 保存位置
    
    cost = compute_cost(AL, train_set_y)
    
    gradient = backward_pass(AL, parameters, cache, train_set_y)
    # 反向传播计算梯度

我们来实现反向传播 backward_pass

def backward_pass(AL, parameters, cache, Y):
    m = Y.shape[1]                         # 样本总数
    gradient =    					   # 保存各层参数梯度的字典
    Layer_num = len(parameters) // 2
    dZL = (AL-Y)*(AL*(1-AL))               # 获取最末层误差信号 dZL.shape = (1,209)  
    gradient["dW"+str(Layer_num)] = (1/m)*np.dot(dZL,cache["A"+str(Layer_num-1)].T)
    gradient["db"+str(Layer_num)] = (1/m)*np.sum(dZL,axis=1,keepdims = True)
    
    for L in reversed(range(1,Layer_num)): # 遍历[3,2,1],其中reversed函数[1,2,3]颠倒为[3,2,1]
        dZL = np.dot(parameters["W"+str(L+1)].T,dZL)*(AL*(1-AL))
        gradient["dW"+str(L)] = (1/m)*np.dot(dZL,cache["A"+str(L-1)].T)
        gradient["db"+str(L)] = (1/m)*np.sum(dZL,axis=1,keepdims = True)
        
    return gradient  

 


根据各层的w、b梯度,使用梯度下降更新一次参数

if __name__ == '__main__':
    train_set_x, train_set_y, test_set_x, test_set_y = load_dataset()
    # 加载数据
    
    fc_net = [12288, 4, 3, 2, 1] 
	# 输入层->第1层->第2层->第3层->第4层->a    
	# 输出值a,a>0.5 ? 1:0
    
    parameters = init_parameters(fc_net)
    # 初始化权值
    
    AL,cache= forward_pass(train_set_x, parameters)
    # 最后一层激活值AL = train_set_x 输入数据、parameters W、b 保存位置
    
    cost = compute_cost(AL, train_set_y)
    
    gradient = backward_pass(AL, parameters, cache, train_set_y)
    # 反向传播计算梯度
    
    iterations = 500
    LearnRate = 0.01
    costs = []                               
    # 保存我们每次迭代计算得到的代价值
    parameters = update_parameters(gradient, parameters, LearnRate)
    # 根据梯度更新一次参数

得到梯度后,实现梯度下降。

def update_parameters(gradient,parameters,LearnRate):
    # w : =w-r*dw、b := b-r*db
    Layer_num = len(parameters) // 2
    for L in range(1,Layer_num+1): 
        parameters["W"+str(L)] = parameters["W"+str(L)] - LearnRate*gradient["dW"+str(L)]
        parameters["b"+str(L)] = parameters["b"+str(L)] - LearnRate*gradient["db"+str(L)]
    
    return parameters

 


重复以上步骤,直到损失值低于设定阈值,模型收敛

if __name__ 

以上是关于深度学习项目拆解:识别猫的项目的主要内容,如果未能解决你的问题,请参考以下文章

深度解析抖音手绘动画短视频项目,流程步骤拆解,经验分享

Python项目演练:使用深度学习自动识别车牌号附源代码

深度学习练手项目——DNN识别手写数字

深度学习基于卷积神经网络(tensorflow)的人脸识别项目

深度学习基于卷积神经网络(tensorflow)的人脸识别项目

基于CNN卷积神经网络的TensorFlow+Keras深度学习的人脸识别