浅层神经网络的搭建(原创) @water
声明
首先声明本文参考【Kulbear】的github上的文章,本文参考Planar data classification with one hidden layer,我基于他的文章加以自己的理解发表这篇博客。
目标:
- 构建具有单隐藏层的2类分类神经网络。
- 使用具有非线性**功能**函数,例如tanh,rule。
- 计算交叉熵损失(损失函数)。
- 实现向前和向后传播。
本文所使用的资料已上传到百度网盘点击下载,请在开始之前下载好所需资料。
【博主使用的python版本:2.7 】
# -*- coding: utf-8 -*-
from __future__ import division
第一行是可以进行中文编码
第二行是python2可以整除一个数,且保留小数
在开始之前,我们有需要引入的库:
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
from dnn_utils import relu,relu_backward
- numpy :是用Python进行科学计算的基本软件包。
- matplotlib:是一个著名的库,用于在Python中绘制图表。
- sklearn:为数据挖掘和数据分析提供的简单高效的工具。
- testCases:提供了一些测试示例来评估函数的正确性,参见下载的资料或者在底部查看它的代码。
- planar_utils :提供了在这个任务中使用的各种有用的功能,参见下载的资料或者在底部查看它的代码
一、加载数据集
这里有两个数据集,你可以自行选择,并对比算法在不同数据集下的性能。
####################################################################\
#Data 1
########
# X, Y = load_planar_dataset()
# #plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral) #绘制散点图
# shape_X = X.shape
# shape_Y = Y.shape
# m = Y.shape[1] # 训练集里面的数量
#
# print ("X的维度为: " + str(shape_X))
# print ("Y的维度为: " + str(shape_Y))
# print ("数据集里面的数据有:" + str(m) + " 个")
########
#Data 2
########
# 数据集
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_moons"
X, Y = datasets[dataset]
X, Y = X.T, Y.reshape(1, Y.shape[0])
shape_X = X.shape
shape_Y = Y.shape
m = Y.shape[1] # 训练集里面的数量
if dataset == "blobs":
Y = Y % 2
print ("X的维度为: " + str(shape_X))
print ("Y的维度为: " + str(shape_Y))
print ("数据集里面的数据有:" + str(m) + " 个")
plt.figure(1)
plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.c
这里有两个数据集,分别如下图所示
数据集这样就处理 好了,下一步我们来创建神经网络模型
二、创建网络模型
建立神经网络的主要步骤是:
- 定义模型结构(例如输入特征的数量)
- 初始化模型的参数
- 循环:
- 3.1 计算当前损失(正向传播)
- 3.2 计算当前梯度(反向传播)
- 3.3 更新参数(梯度下降)
def layer_sizes(X , Y):
"""
参数:
X - 输入数据集,维度为(输入的数量,训练/测试的数量)
Y - 标签,维度为(输出的数量,训练/测试数量)
返回:
n_x - 输入层的数量
n_h - 隐藏层的数量
n_y - 输出层的数量
"""
n_x = X.shape[0] #输入层
n_h = 4 #,隐藏层,硬编码为4
n_y = Y.shape[0] #输出层
return (n_x,n_h,n_y)
def initialize_parameters( 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)
"""
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
初始化参数的函数已经构建好了,现在就可以执行"前向“后向”传播步骤来学习参数。
这里构建网络模型有两种方法:
- tanh( ) -->sigmoid( )
- relu( ) -->sigmoid( )
def forward_propagation( X , parameters ):
"""
参数:
X - 维度为(3,m)(n_x,m)的输入数据。
parameters - 初始化函数(initialize_parameters)的输出
返回:
A2 - 使用sigmoid()函数计算的第二次**后的数值
cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典类型变量
"""
W1 = parameters["W1"]#(4,3)( n_h , n_x )
b1 = parameters["b1"]#(4,1)( n_h , 1 )
W2 = parameters["W2"]#(1,4)( n_y , n_h)
b2 = parameters["b2"]#(1,1)( n_y , 1 )
#前向传播计算A2
Z1 = np.dot(W1 , X) + b1 #(4,m)
A1,a1 = relu(Z1) #(4,m)
# A1 = np.tanh(Z1)
# a1 =1
Z2 = np.dot(W2 , A1) + b2#(1,m)
A2 = sigmoid(Z2) #(1,m)
#使用断言确保我的数据格式是正确的
# assert(A2.shape == (1,X.shape[1]))
cache = {"Z1": Z1,
"A1": A1,
"Z2": Z2,
"A2": A2,
"a1": a1}
return (A2, cache)
def compute_cost(A2,Y,parameters):
"""
计算方程(6)中给出的交叉熵成本,
参数:
A2 - 使用sigmoid()函数计算的第二次**后的数值
Y - "True"标签向量,维度为(1,数量)
parameters - 一个包含W1,B1,W2和B2的字典类型的变量
返回:
成本 - 交叉熵成本给出方程(13)
"""
m = Y.shape[1]
W1 = parameters["W1"]
W2 = parameters["W2"]
#计算成本
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
#测试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"]))
#测试compute_cost
print("=========================测试compute_cost=========================")
A2 , Y_assess , parameters = compute_cost_test_case()
print("cost = " + str(compute_cost(A2,Y_assess,parameters)))
测试一下
打印出来的结果
X的维度为: (2, 200)
Y的维度为: (1, 200)
数据集里面的数据有:200 个
=========================测试forward_propagation=========================
(-0.0004997557777419913, 0.009407450992324358, 0.000320787397351217, 0.5000801968460385)
=========================测试compute_cost=========================
cost = 0.692919893776
对应的反向传播也有两种
-
利用Rule**函数来反向传播
dZ2= A2 - Y
dW2 = (1 / m) * np.dot(dZ2, A1.T)
db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True)
dA1 = np.dot(W2.T, dZ2)
dZ1 = relu_backward(dA1,a1)
dW1 = (1 / m) * np.dot(dZ1, X.T)
db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True) -
利用tanh**函数来反向传播
dZ2= A2 - Y
dW2 = (1 / m) * np.dot(dZ2, A1.T)
db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True)
dZ1 = np.multiply(np.dot(W2.T, dZ2), 1 - np.power(A1, 2))
dW1 = (1 / m) * np.dot(dZ1, X.T)
db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True)
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]
W1 = parameters["W1"]
W2 = parameters["W2"]
A1 = cache["A1"]
a1 = cache["a1"]
A2 = cache["A2"]
dZ2= A2 - Y
dW2 = (1 / m) * np.dot(dZ2, A1.T)
db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True)
dA1 = np.dot(W2.T, dZ2)
dZ1 = relu_backward(dA1,a1)
# dZ1 = np.multiply(np.dot(W2.T, dZ2), 1 - np.power(A1, 2))
dW1 = (1 / m) * np.dot(dZ1, X.T)
db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True)
grads = {"dW1": dW1,
"db1": db1,
"dW2": dW2,
"db2": db2 }
return grads
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
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
def nn_model(X,Y,n_h,learn_rate,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_sizes(X, Y)[0]
n_y = layer_sizes(X, Y)[2]
parameters = initialize_parameters(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 = learn_rate)
if print_cost:
if i%1000 == 0:
print"第 ",i," 次循环,成本为:"+str(cost)
return parameters
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
parameters = nn_model(X, Y, n_h = 6,learn_rate=0.5, 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(6))
predictions = predict(parameters, X)
print ('准确率: %d' % float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100) + '%')
from matplotlib.colors import ListedColormap
hidden_layer_sizes = [2,4,6 ,12,16,64,128] #隐藏层数量
cmap_light = ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF'])
learning_ratesaa = [0.1,0.3, 0.5,0.8, 1]
plt.figure(2)
for j in learning_ratesaa:
acc=[]
print ("learning rate is: " + str(j))
for i, n_h in enumerate(hidden_layer_sizes):
parameters = nn_model(X, Y, n_h,learn_rate=j , num_iterations=5000)
predictions = predict(parameters, X)
accuracy = float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100)
acc = np.append(acc,accuracy)
print ("隐藏层的节点数量: {} ,准确率: {} %".format(n_h, accuracy))
print acc
plt.plot(hidden_layer_sizes,acc,'-',label='learning_rate ='+str(j))
# plt.ylim((50,100))
legend = plt.legend(loc='best', shadow=True)
plt.xlabel('hidden_layer_sizes')
plt.ylabel('accuracy')
plt.title('Activation: Rule-->sigmoid')
plt.show()
三、结果分析
rule有一定的机遇性,但总体趋势还是精度随着层数的增加而增加,但层数太高也会出现过拟合的情况。
X的维度为: (2, 200)
Y的维度为: (1, 200)
数据集里面的数据有:200 个
=========================测试forward_propagation=========================
(-0.0004997557777419913, 0.009407450992324358, 0.000320787397351217, 0.5000801968460385)
=========================测试compute_cost=========================
cost = 0.692919893776
第 0 次循环,成本为:0.693154144009
第 1000 次循环,成本为:0.278040613491
第 2000 次循环,成本为:0.195258293538
第 3000 次循环,成本为:0.10509693607
第 4000 次循环,成本为:0.0991809031961
第 5000 次循环,成本为:0.0973635539258
第 6000 次循环,成本为:0.0964981300555
第 7000 次循环,成本为:0.0960294839933
第 8000 次循环,成本为:0.0957545594441
第 9000 次循环,成本为:0.0955833219085
准确率: 96%
learning rate is: 0.1
隐藏层的节点数量: 2 ,准确率: 86.0 %
隐藏层的节点数量: 4 ,准确率: 87.0 %
隐藏层的节点数量: 6 ,准确率: 87.0 %
隐藏层的节点数量: 12 ,准确率: 87.5 %
隐藏层的节点数量: 16 ,准确率: 87.0 %
隐藏层的节点数量: 64 ,准确率: 87.0 %
隐藏层的节点数量: 128 ,准确率: 87.0 %
[86. 87. 87. 87.5 87. 87. 87. ]
learning rate is: 0.3
隐藏层的节点数量: 2 ,准确率: 86.0 %
隐藏层的节点数量: 4 ,准确率: 87.0 %
隐藏层的节点数量: 6 ,准确率: 96.0 %
隐藏层的节点数量: 12 ,准确率: 96.0 %
隐藏层的节点数量: 16 ,准确率: 87.0 %
隐藏层的节点数量: 64 ,准确率: 87.0 %
隐藏层的节点数量: 128 ,准确率: 96.0 %
[86. 87. 96. 96. 87. 87. 96.]
learning rate is: 0.5
隐藏层的节点数量: 2 ,准确率: 86.0 %
隐藏层的节点数量: 4 ,准确率: 87.0 %
隐藏层的节点数量: 6 ,准确率: 96.0 %
隐藏层的节点数量: 12 ,准确率: 96.0 %
隐藏层的节点数量: 16 ,准确率: 87.0 %
隐藏层的节点数量: 64 ,准确率: 96.0 %
隐藏层的节点数量: 128 ,准确率: 95.5 %
[86. 87. 96. 96. 87. 96. 95.5]
learning rate is: 0.8
隐藏层的节点数量: 2 ,准确率: 86.0 %
隐藏层的节点数量: 4 ,准确率: 87.0 %
隐藏层的节点数量: 6 ,准确率: 96.0 %
隐藏层的节点数量: 12 ,准确率: 95.5 %
隐藏层的节点数量: 16 ,准确率: 87.0 %
隐藏层的节点数量: 64 ,准确率: 97.0 %
隐藏层的节点数量: 128 ,准确率: 95.5 %
[86. 87. 96. 95.5 87. 97. 95.5]
learning rate is: 1
隐藏层的节点数量: 2 ,准确率: 86.0 %
隐藏层的节点数量: 4 ,准确率: 87.0 %
隐藏层的节点数量: 6 ,准确率: 96.0 %
隐藏层的节点数量: 12 ,准确率: 95.5 %
隐藏层的节点数量: 16 ,准确率: 87.0 %
隐藏层的节点数量: 64 ,准确率: 97.0 %
隐藏层的节点数量: 128 ,准确率: 96.0 %
[86. 87. 96. 95.5 87. 97. 96. ]