单隐层神经网络实现代码详解_基于吴恩达第三次编程作业

import numpy as np
import matplotlib.pyplot as plt
from testCases import *
import sklearn #机器学习库,分类,聚类,回归,降维和加载或生成数据集
import sklearn.datasets

import sklearn.linear_model

from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets

关于 testCases.py 以及 planar_utils.py如有需要可以留言。

np.random.seed(1) #指定固定随机数

调用其他的数据集

noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure = load_extra_datasets()

以字典的方式来选择使用哪个数据集

datasets = {"noisy_circles": noisy_circles,
"noisy_moons": noisy_moons,
"blobs": blobs,
"gaussian_quantiles": gaussian_quantiles}

dataset = "noisy_circles" #选择有噪声的数据集

X, Y = datasets[dataset]
X, Y = X.T, Y.reshape(1, Y.shape[0])

if dataset == "blobs":
Y = Y % 2

plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral)

plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral)

X[0, :]:散点图横坐标,X[1, :]:散点图纵坐标,C:色彩label标记,Spectral为不同的标记涂上不同的颜色(例如:0代表红,1代表蓝)

'''X,Y=load_planar_dataset();

plt.scatter(X[0,:],X[1,:],c=Y,s=40,cmap=plt.cm.Spectral)

绘制散点图

plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral)'''

shape_X=X.shape
shape_Y=Y.shape
m=Y.shape[1]
print("X的维度为:"+str(shape_X)) #(2,200,其中2代表横纵坐标数,200为数量)
print("Y的维度为:"+str(shape_Y)) #(1,200,200个0,1标记)
print("数据集里面的数据有:"+str(m)+"个")#200

--------使用逻辑回归分类器---------------

clf=sklearn.linear_model.LogisticRegressionCV()
clf.fit(X.T,Y.T) #进行训练

-------绘图

plot_decision_boundary(lambda x: clf.predict(x),X,Y) #绘制预测后的决策边缘
plt.title("Logistic Regression")
LR_predictions=clf.predict(X.T)
print ("逻辑回归的准确性: %d " % float((np.dot(Y, LR_predictions) +
np.dot(1 - Y,1 - LR_predictions)) / float(Y.size) * 100) +
"% " + "(预测出正确的0和1标记数据点所占的百分比)") #dot矩阵相乘,multiply,*对应元素相乘

------------定义神经网络结构-------

def layer_size(X,Y):
"""
参数:
X - 输入数据集,维度为(输入的数量,训练/测试的数量)2,200
Y - 标签,维度为(输出的数量,训练/测试数量)1,200

返回:
 n_x - 输入层的数量
 n_h - 隐藏层的数量
 n_y - 输出层的数量
"""
n_x=X.shape[0] #输入层 2
n_h=4 #隐藏层,硬编码为4
n_y=Y.shape[0] #输出层 1
"""print("输入层: n_x = " + str(n_x))
print("隐藏层: n_h= " + str(n_h))
print("输出层: n_y = " + str(n_y))"""
return (n_x,n_h,n_y) #2,4,1

-----------测试layer sizes-----------

print("-------------测试layer sizes------------")
X_asses,Y_asses=layer_sizes_test_case();
(n_x,n_h,n_y)=layer_size(X_asses,Y_asses)
print("输入层的节点数量为: n_x = " + str(n_x))
print("隐藏层的节点数量为: n_h = " + str(n_h))
print("输出层的节点数量为: n_y = " + str(n_y))

-------------初始化模型参数----------------

def initialize_parameter(n_x,n_h,n_y):
"""
参数:
n_x - 输入层节点的数量
n_h - 隐藏层节点的数量
n_y - 输出层节点的数量

返回:
    parameters - 包含参数的字典:
        W1 - 权重矩阵,维度为(n_h,n_x)#设置这样的参数维度才能用于后面矩阵的计算
        b1 - 偏向量,维度为(n_h,1)
        W2 - 权重矩阵,维度为(n_y,n_h)
        b2 - 偏向量,维度为(n_y,1)
       维度:W1(4,2),b1(4,1) ,W2(1,4),b2(1,1)
"""
np.random.seed(2) #指定一个随机种子,以便你的输出与我们的一样。
W1=np.random.randn(n_h,n_x)*0.01  #随机初始化最初的参数值
b1=np.zeros(shape=(n_h,1))
W2=np.random.randn(n_y,n_h)*0.01
b2=np.zeros(shape=(n_y,1))
#使用断言确保我的数据格式是正确的
assert(W1.shape == ( n_h , n_x ))
assert(b1.shape == ( n_h , 1 ))
assert(W2.shape == ( n_y , n_h ))
assert(b2.shape == ( n_y , 1 ))
parameters={"W1":W1,
            "b1":b1,
            "W2":W2,
            "b2":b2}
return parameters

-----------------测试初始化参数-----------

print("------------测试初始化参数-----------")
n_x,n_h,n_y= initialize_parameters_test_case()
parameters=initialize_parameter(n_x,n_h,n_y)
print("W1="+str(parameters["W1"]))
print("b1="+str(parameters["b1"]))
print("W2="+str(parameters["W2"]))
print("b2="+str(parameters["b2"]))

----------向前传播------------------------

def forward_propagation(X, parameters):

 """
参数:
     X - 维度为(n_x,m)的输入数据。
     parameters - 初始化函数(initialize_parameters)的输出

返回:
     A2 - 使用sigmoid()函数计算的第二次激活后的数值
     cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典类型变量
 """
 W1=parameters["W1"]  #从字典中取出参数
 b1=parameters["b1"]
 W2=parameters["W2"]
 b2=parameters["b2"]
#向前传播计算A2
 Z1=np.dot(W1,X)+b1  #按照向前传播公式计算,维度(4,2).(2,200)=(4,200)
 A1=np.tanh(Z1)   #从输入层到隐藏层使用激活函数tanh(4,200)
 Z2=np.dot(W2,A1)+b2  #继续向前传播(1,4).(4,200)=(1,200)
 A2=sigmoid(Z2)  #二分的  #从隐藏层到输出层使用激活函数sigmoid,特别适用于只有二分情况,A2即为向前传播时对数据的预测值,若A2值大于0.5即预测结果为标记1。(1,200)
#使用断言确保我的数据格式是正确的
 assert(A2.shape == (1,X.shape[1]))
 cache={"Z1":Z1,
        "A1":A1,
        "Z2":Z2,
        "A2":A2}
 return (A2,cache)

测试forward_propagation

print("=========================测试forward_propagation=========================")
X_assess, parameters = forward_propagation_test_case()
A2, cache = forward_propagation(X_assess, parameters)
print(np.mean(cache["Z1"]), np.mean(cache["A1"]), np.mean(cache["Z2"]), np.mean(cache["A2"]))

--------------计算cost(损失、成本)-----------------

计算交叉熵损失

def compute_cost(A2,Y,parameters):
"""
计算方程(6)中给出的交叉熵成本,

参数:
     A2 - 使用sigmoid()函数计算的第二次激活后的数值
     Y - "True"标签向量,维度为(1,数量)
     parameters - 一个包含W1,B1,W2和B2的字典类型的变量

返回:
     成本 - 交叉熵成本给出方程(13)
"""
m=Y.shape[1]    

#计算成本
logprobs= logprobs=np.multiply(np.log(A2),Y)+np.multiply((1-Y),np.log(1-A2))
cost=-np.sum(logprobs)/m
cost=float(np.squeeze(cost))
assert(isinstance(cost,float))
return cost

测试compute_cost

print("=========================测试compute_cost=========================")
A2 , Y_assess , parameters = compute_cost_test_case()
print("cost = " + str(compute_cost(A2,Y_assess,parameters)))

--------------向后传播-------------------------------

def backward_propagation(parameters,cache,X,Y):
"""
使用上述说明搭建反向传播函数。

参数:
 parameters - 包含我们的参数的一个字典类型的变量。
 cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典类型的变量。
 X - 输入数据,维度为(2,数量)
 Y - “True”标签,维度为(1,数量)

返回:
 grads - 包含W和b的导数一个字典类型的变量。
"""
m=X.shape[1]  #200
W1=parameters["W1"]  
W2=parameters["W2"]
A1=cache["A1"]
A2=cache["A2"]

dZ2=A2-Y  #维度(1,200)-(1-200),(1,200)
dW2=(1/m)*np.dot(dZ2,A1.T)#维度(1,200).(200,4)=(1,4)
db2=(1/m)*np.sum(dZ2,axis=1,keepdims=True) #axis=1指按行进行求和计算(1,1)
dZ1=np.multiply(np.dot(W2.T,dZ2),1-np.power(A1,2))#维度(4,1).(1,200)=(4,200),(4,200)
dW1=(1/m)*np.dot(dZ1,X.T)#维度(4,200).(200,2)=(4,2)
db1=(1/m)*np.sum(dZ1,axis=1,keepdims=True)#维度(4,1)
grads={"dW1":dW1,
       "db1":db1,
       "dW2":dW2,
       "db2":db2}
return grads

测试backward_propagation

print("=========================测试backward_propagation===================")
parameters, cache, X_assess, Y_assess = backward_propagation_test_case()

grads = backward_propagation(parameters, cache, X_assess, Y_assess)
print ("dW1 = "+ str(grads["dW1"]))
print ("db1 = "+ str(grads["db1"]))
print ("dW2 = "+ str(grads["dW2"]))
print ("db2 = "+ str(grads["db2"]))

----------------更新参数----使用(dW1, db1, dW2, db2)来更新(W1, b1, W2, b2)

def update_parameters(parameters,grads,learning_rate=1.2):
"""
使用上面给出的梯度下降更新规则更新参数

参数:
 parameters - 包含参数的字典类型的变量。
 grads - 包含导数值的字典类型的变量。
 learning_rate - 学习速率

返回:
 parameters - 包含更新参数的字典类型的变量。
"""
W1,W2=parameters["W1"],parameters["W2"]
b1,b2=parameters["b1"],parameters["b2"]

dW1,dW2=grads["dW1"],grads["dW2"]
db1,db2=grads["db1"],grads["db2"]

W1=W1-learning_rate*dW1  #使用梯度下降法,循环num_iterations次
b1=b1-learning_rate*db1
W2=W2-learning_rate*dW2
b2=b2-learning_rate*db2
parameters = {"W1": W1,
              "b1": b1,
              "W2": W2,
              "b2": b2}
return parameters

测试update_parameters

print("=========================测试update_parameters=========================")
parameters, grads = update_parameters_test_case()
parameters = update_parameters(parameters, grads)

print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

---------------整合-------------

def nn_model(X,Y,n_h,num_iterations,print_cost=False): #程序的入口
"""
参数:
X - 数据集,维度为(2,示例数)
Y - 标签,维度为(1,示例数)
n_h - 隐藏层的数量
num_iterations - 梯度下降循环中的迭代次数
print_cost - 如果为True,则每1000次迭代打印一次成本数值

返回:
    parameters - 模型学习的参数,它们可以用来进行预测。
 """

np.random.seed(3) #指定随机种子
n_x = layer_size(X, Y)[0]  #取出返回参数的第一个值
n_y = layer_size(X, Y)[2]  #取出返回参数的第三个值

parameters = initialize_parameter(n_x,n_h,n_y)
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]

for i in range(num_iterations):
    A2 , cache = forward_propagation(X,parameters)
    cost = compute_cost(A2,Y,parameters)
    grads = backward_propagation(parameters,cache,X,Y)
    parameters = update_parameters(parameters,grads,learning_rate = 0.5)

    if print_cost:
        if i%1000 == 0:
            print("第 ",i," 次循环,成本为:"+str(cost))
return parameters        

测试nn_model

print("=========================测试nn_model=========================")
X_assess, Y_assess = nn_model_test_case()
parameters = nn_model(X_assess, Y_assess, 4, num_iterations=10000, print_cost=False)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

--------------预测------------------------------------------------

def predict(parameters,X):
"""
使用学习的参数,为X中的每个示例预测一个类

参数:
    parameters - 包含参数的字典类型的变量。
    X - 输入数据(n_x,m)

返回
    predictions - 我们模型预测的向量(红色:0 /蓝色:1)

"""
A2,cache=forward_propagation(X,parameters)
predictions=np.round(A2)
return predictions

测试predict

print("=========================测试predict=========================")

parameters, X_assess = predict_test_case()

predictions = predict(parameters, X_assess)
print("预测的平均值 = " + str(np.mean(predictions)))

-------------正式的运行---------------


parameters=nn_model(X,Y,n_h=4,num_iterations=10000,print_cost=True)

绘制边界

plot_decision_boundary(lambda x:predict(parameters,x.T),X,Y)

plt.title("Decision Boundary for hidden layer size " + str(4))

predictions=predict(parameters,X)

print('准确率:%d'%float((np.dot(Y,predictions.T)+np.dot(1-Y,1-predictions.T))/float(Y.size)*100)+'%')


--------------更深入的探讨-----------------

更改隐藏层节点数

plt.figure(figsize=(16,32))
hidden_layer_sizes=[1,2,3,4,5,20,50]
for i, n_h in enumerate(hidden_layer_sizes):
plt.subplot(5,2,i+1)
plt.title("Hidden Layer of size %d" %n_h)
parameters = nn_model(X,Y,n_h,num_iterations=8000)
plot_decision_boundary(lambda x:predict(parameters,x.T),X,Y)
predictions=predict(parameters,X)
accuracy=float((np.dot(Y,predictions.T)+np.dot(1-Y,1-predictions.T))/float(Y.size)*100)
#正确标记的数据点所占的百分比,正确标记的红色点和蓝色点数量之和
print("隐藏层的节点数量:{},准确率:{}%".format(n_h,accuracy))

---------------------------------------------对代码理解的总结---------------------------------------------------------

程序首先需要定义这种单隐层神经网络结构即每一层节点的数量,本次程序输入层数量为2,隐藏层数量为4,输出层数量为1。然后基于该结构来初始化模型的参数,一般会赋随机初始值。接着使用向前传播公式计算出Z1,使用激活函数计算出A1,再计算出Z2,再使用激活函数计算出A2,若A2大于0.5则,预测值为1。然后使用向后传播的方式,由A2算出dW2,db2,dW1,db1,通过回传的方式以梯度下降法来更新模型最开始设置的随机W1,b1,W2,b2的值,通过不断的迭代,最后训练出较好的W1,b1,W2,b2的值,来获得想要的A2预测值,计算出该模型的准确率。我们使用成本函数cost分别对参数Z2,W2,b2,Z1,W1,b1求偏导,经过计算简化后的等式如代码所列计算公式,包括对激活函数求导,使用不同的激活函数,最后求解的例如dZ2等参数值的导数值会不同,不再是代码所列公式。
---------------------------------------------------分割线(实验的结果)---------------------------------------------


单隐层神经网络实现代码详解_基于吴恩达第三次编程作业_第1张图片
![结果2.png](https://upload-images.jianshu.io/upload_images/12096915-ba8581a4679cce33.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

单隐层神经网络实现代码详解_基于吴恩达第三次编程作业_第2张图片
结果.png

单隐层神经网络实现代码详解_基于吴恩达第三次编程作业_第3张图片
结果2.png

可以看出该模型有一定的效果,相比逻辑回归效果好些。

你可能感兴趣的:(单隐层神经网络实现代码详解_基于吴恩达第三次编程作业)