多层神经网络实现猫图片的二分类问题

一、作业概述
本次作业要利用多层神经网络实现猫咪图片的二分类问题 。其中训练集共有图片209张,测试集共有图片50张 。隐藏层数量为一层,隐藏层上的神经元个数为5个 。隐藏层的激活函数采用Relu函数,输出层的激活函数为函数 。
二、完成步骤
1、数据预处理
要将图片转化为向量 。每张图片的大小为64*64色素点,每个色素点由RGB三原色组成,所以每张图片需要64*64*3=12288个数据点确定 。所以训练集图片数据维度为(12288,209) 。而由于此问题是二分类问题,故输出的标签只有0-1两种可能,标签集的维度为(1,209) 。处理完后还要将图片集的数据除以255,其目的是归一化 。数据预处理的代码如下:
#1、数据预处理import h5pyimport numpy as npfrom d2l import torch as d2limport osos.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"def load_dataset():train_dataset=h5py.File('datasets/train_catvnoncat.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_catvnoncat.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"][:])#保存的是以bytes类型保存的两个字符串数据:‘cat','non-cat'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"""每张图片的大小为64*64色素点,每个色素点由RGB三原色组成,所以每张图片的数据维度为(64,64,3),一张图片共需要12288个数据点确定 。load_dataset函数的返回值意义如下:train_set_x_orig:训练集图像数据,一共209张,数据维度为(209,64,64,3)train_set_y_orig:训练集的标签集,维度为(1,209)test_set_x_orig:测试集图像数据,一共50张,维度为(50,64,64,3)test_set_y_orig:测试集的标签集,维度为(1,50)classes : 保存的是以bytes类型保存的两个字符串数据,数据为:[b’non-cat’ b’cat’]"""train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()#调用load_dataset函数d2l.plt.imshow(train_set_x_orig[27])d2l.plt.show()#打印出当前的训练标签值#使用np.squeeze的目的是压缩维度,【未压缩】train_set_y[:,index]的值为[1] , 【压缩后】np.squeeze(train_set_y[:,index])的值为1#print("【使用np.squeeze:" + str(np.squeeze(train_set_y[:,index])) + ",不使用np.squeeze: " + str(train_set_y[:,index]) + "】")#只有压缩后的值才能进行解码操作index=25print("y=" + str(train_set_y[ :,index]) + ", it's a " + classes[np.squeeze(train_set_y[:,index])].decode("utf-8") + "' picture")m_train=train_set_y.shape[1]#训练集里面图片的数量m_test=test_set_y.shape[1]#测试集里面图片的数量num_px=train_set_x_orig.shape[1]#训练集和测试集图片的高度和宽度,在这里均为64print('训练集图片的数量为'+str(m_train))print('测试集图片的数量为'+str(m_test))print('训练集和测试集图片的高/宽为'+str(num_px))print('每张图片大小为:'+"("+str(num_px)+","+str(num_px)+","+str(train_set_x_orig.shape[3])+")")print('测试集图片的维数为:'+str(train_set_x_orig.shape))#(209,64,64,3)print('测试集标签的维数为:'+str(train_set_y.shape))#(1,209)print('训练集图片的维数为:'+str(test_set_x_orig.shape))#(50,64,64,3)print('训练集标签的维数:'+str(test_set_y.shape))#(1,50)train_set_x_orig_flatten=train_set_x_orig.reshape(train_set_x_orig.shape[0],-1).T#将训练集图片降维并转置,处理后的shape为(64*64*3,209)test_set_x_orig_flatten=test_set_x_orig.reshape(test_set_x_orig.shape[0],-1).Tprint('训练集图片降维后的维数为:'+str(train_set_x_orig_flatten.shape))#(12288,209)print('训练集标签的维数为:'+str(train_set_y.shape))#(1,209)print('测试集图片降维后的维数为:'+str(test_set_x_orig_flatten.shape))#(12288,50)print('测试集标签的维数为:'+str(test_set_y.shape))#(1,50)#像素值实际上是0-255范围内的三个数字的向量,因此可以对图片向量除以255来对其进行标准化
2、激活函数
激活函数有两个,分别为和relu函数,具体代码实现分别见下 。
def sigmoid_function(X):return 1/(1+np.exp(-X))def relu_function(X):a=np.maximum(0,X)assert (a.shape==X.shape)return a
3、参数初始化
W采用随机高斯分布进行初始化,b采用零进行初始化 。传入的参数包括X,Y,以及隐藏层的神经元数量 。返回值为一个字典,字典中包含初始化好的参数 。代码如下 。

多层神经网络实现猫图片的二分类问题

文章插图
def initialiaze_parameters(X,n_h,Y):parameters={}W1=np.random.randn(n_h,X.shape[0])*0.01b1 = np.zeros(shape=(n_h,1))W2=np.random.randn(Y.shape[0],n_h)*0.01b2=np.zeros(shape=(Y.shape[0],1))parameters["W1"]=W1parameters["b1"] = b1parameters["W2"] = W2parameters["b2"] = b2return parameters#返回一个字典,里面包含初始化好的参数
【多层神经网络实现猫图片的二分类问题】4、前向传播
利用前向传播得到Y的估计值A2,并用cache保留每次计算中产生的A和Z,以供后续反向传播计算可用 。传入的参数只有X和字典 。返回A2和cache 。代码如下 。
def forward_pro(X,parameters):caches=[]W1=parameters['W1']W2=parameters['W2']b1=parameters['b1']b2=parameters['b2']Z1=np.dot(W1,X)+b1A1=relu_function(Z1)cache=(Z1,A1)caches.append(cache)Z2=np.dot(W2,A1)+b2A2 = sigmoid_function(Z2)cache=(Z2,A2)caches.append(cache)return A2,caches
5、计算损失函数
利用交叉熵作为损失函数,代码如下:
def compute_cost(A2,Y):m=Y.shape[1]cost=(-1/m)*np.sum(np.multiply(Y,np.log(A2)+np.multiply(np.log(1-A2),1-Y)))cost=np.squeeze(cost)assert (cost.shape==())return cost
6、反向传播
从后往前对参数进行求导,并建立一个字典保存计算好的梯度,返回这个字典 。并且用语句确保dw和w的维数是一样的,db和b的维数是一样的 。
def backward_pro(parameters,caches,X,Y):m=X.shape[1]W1=parameters["W1"]b1=parameters["b1"]W2=parameters["W2"]b2=parameters["b2"]Z1,A1=caches[0]Z2,A2=caches[1]dZ2=A2-Ydb2=(1/m)*np.sum(dZ2,axis=1,keepdims=True)assert (db2.shape==b2.shape)dW2=(1/m)*np.dot(dZ2,A1.T)assert (dW2.shape==W2.shape)dA1=np.dot(W2.T,dZ2)dZ1=np.array(dA1,copy=True)dZ1[Z1<=0]=0assert (dZ1.shape==Z1.shape)dW1=(1/m)*np.dot(dZ1,X.T)assert (dW1.shape==W1.shape)db1=(1/m)*np.sum(dZ1,axis=1,keepdims=True)assert (db1.shape==b1.shape)grad={}grad['W1']=dW1grad['W2']=dW2grad['b1']=db1grad['b2']=db2returngrad
7、更新参数
传入学习率,字典,字典grad,返回更新好的参数
def renew_parameters(parameters,grad,learning_rate):dW1=grad["W1"]dW2=grad["W2"]db1=grad["b1"]db2=grad['b2']parameters['W1']-=dW1*learning_rateparameters["W2"]-=dW2*learning_rateparameters["b1"]-=db1*learning_rateparameters["b2"]-=db2*learning_ratereturn parameters
8、模型学习
传入学习率,X,Y,以及迭代次数 。不断更新参数,直至迭代完成,每迭代100次打印一次cost,观察成本函数变化趋势 。返回学习好的参数 。
def nn_model(X,Y,num_epoches,learning_rate):parameters = initialiaze_parameters(X, 5,Y)for i in range(num_epoches):A2, caches = forward_pro(X, parameters)cost = compute_cost(A2, Y)grads=backward_pro(parameters,caches,X,Y)parameters=renew_parameters(parameters,grads,learning_rate)if(i0==0):print(f'第{i}次迭代的loss为:{cost}')return parameters
三、结果输出
将学习好的参数喂入数据,观察在训练集和测试集上的准确率 。
parameters=nn_model(train_set_x,train_set_y,3000,0.0075)print(parameters)print(parameters['W1'].shape)print(parameters['b1'].shape)print(parameters['W2'].shape)print(parameters['b2'].shape)def predict(parameters,X):A2,caches=forward_pro(X,parameters)m=A2.shape[1]Y_prediction=np.zeros((1,m))for i in range(m):Y_prediction[0,i]=1 if A2[0,i]>0.5 else 0return Y_predictiondef accuracy(Y_prediction,Y):return 1-np.mean(np.abs(Y_prediction-Y))accuracy_train=accuracy(predict(parameters,train_set_x),train_set_y)accuracy_test=accuracy(predict(parameters,test_set_x),test_set_y)print('训练集的准确率为'+str(accuracy_train*100)+'%')print('测试集的准确率为'+str(accuracy_test*100)+'%')
本次迭代次数采用3000次,学习率为0.0075 。模型在训练集上的准确率为100%,测试集上的准确率为72%