带有一个隐藏层的平面数据分类
解释一下,就是一个二维平面有两种不同的花色,我们通过花色将这个平面进行划分
预备知识
我们先来看一下我们需要的包
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 np.random.seed(1) #设置一个固定的随机种子,以保证接下来的步骤中我们的结果是一致的。
前两个不需要解释,在上一次的作业中,我们已经使用过了
第三个包,是课后作业自带的代码,看名字就知道,这个是为我们提供测试用例的,我们先来看一看,了解一个大概的样子
def layer_sizes_test_case():
np.random.seed(1)
X_assess = np.random.randn(5, 3)
Y_assess = np.random.randn(2, 3)
return X_assess, Y_assess
def initialize_parameters_test_case():
n_x, n_h, n_y = 2, 4, 1
return n_x, n_h, n_y
def forward_propagation_test_case():
np.random.seed(1)
X_assess = np.random.randn(2, 3)
parameters = {'W1': np.array([[-0.00416758, -0.00056267],
[-0.02136196, 0.01640271],
[-0.01793436, -0.00841747],
[ 0.00502881, -0.01245288]]),
'W2': np.array([[-0.01057952, -0.00909008, 0.00551454, 0.02292208]]),
'b1': np.array([[ 0.],
[ 0.],
[ 0.],
[ 0.]]),
'b2': np.array([[ 0.]])}
return X_assess, parameters我们每编写完一段代码,这个库就会为我们提供测试的用例,一会正式编写的时候,我来具体解释
import sklearn import sklearn.datasets import sklearn.linear_model from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets
前三行为我们提供数据分析的工具,我们一会正式开始在来分析,最后一行也是,我们用到了在进行分析
分析数据集
我们来看一下我们的数据集
X, Y = load_planar_dataset()
执行了这个函数,我们就获取到了我们的数据集,这个数据就是我们上面说的
from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets
给出来的,我们使用到了其中的load_planar_dataset这个函数,进入其中,我们看看大概的内容
def load_planar_dataset():
np.random.seed(1)
m = 400 # number of examples
N = int(m/2) # number of points per class
D = 2 # dimensionality
X = np.zeros((m,D)) # data matrix where each row is a single example
Y = np.zeros((m,1), dtype='uint8') # labels vector (0 for red, 1 for blue)
a = 4 # maximum ray of the flower
for j in range(2):
ix = range(N*j,N*(j+1))
t = np.linspace(j*3.12,(j+1)*3.12,N) + np.random.randn(N)*0.2 # theta
r = a*np.sin(4*t) + np.random.randn(N)*0.2 # radius
X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
Y[ix] = j
X = X.T
Y = Y.T
return X, Y具体代码的意思大概就是创建一个二维的平面,然后给其中填上不同颜色的小点点,最后我们返回的X和Y并不是这个二维平面的坐标,我们先来验证一下
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) + " 个")根据前两行的意思,我们可以推断出X和Y就是两个矩阵,根据第三行我们可以知道Y的第二维表示的数据集的数量,我们来看一下运行结果
X的维度为: (2, 400) Y的维度为: (1, 400) 数据集里面的数据有:400 个
我们发现了,X和Y的第二维表示就是数据集的数量,然后X的一维一直都是两个数,表示的是这个小点点在二维平面图的具***置,Y的第一维1,也就是0和1分别表示不同的颜色,这样说起来还不是太过的直观,我们通过绘图工具来直观的看一下,并重新分析
plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral) #绘制散点图 plt.show()
和我们预想的一样,X表示的就是图片(2,400),通俗的解释一下有400个数据集,每一个数据集都用两个属性,也就是x和y,进一步说明,有400个(x,y),400个坐标
然后就是Y(1,400),400个坐标对应这400个不同的结果,每一个结果只用两种情况,也就是0或者是1,也就是红色和蓝色,介绍完了数据集我们就开始正式的编写代码了
编写代码
定义神经网络的结构
定义神经网络层数
首先就是确定它的层数,我们将X和Y传入进来,先来确定它的层数
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)虽然有400个数据集,但是设定神经网络的层数,我们还是要以单一样本为准,以一个小点点为例子,它的特征就是x和y,这里的x和y表示的是它的坐标,宏观的想一下,我们输入它的坐标,经过神经网络的处理,就可以知道它是红还是蓝了,所以它的输入特征有两个,也就是第0层是2,第2层是0,其中隐藏层我们定义的是4,然后我们将这三层返回,测试一下,看一下测试代码
#测试layer_sizes
print("=========================测试layer_sizes=========================")
X_asses , Y_asses = layer_sizes_test_case()
(n_x,n_h,n_y) = layer_sizes(X_asses,Y_asses)
print("输入层的节点数量为: n_x = " + str(n_x))
print("隐藏层的节点数量为: n_h = " + str(n_h))
print("输出层的节点数量为: n_y = " + str(n_y))这里我们使用到了layer_sizes_test_case(),这个就是我们定义库给出的函数,我们看一下
def layer_sizes_test_case():
np.random.seed(1)
X_assess = np.random.randn(5, 3)
Y_assess = np.random.randn(2, 3)
return X_assess, Y_assess我们定义了两个随机矩阵,也分别对应了输出和输入,如同输入为5,样本是3,输出为2,样本也是3,而在开头我们已经说了,神经网络是针对单一样本的,只需要看它的行,所以结果分别如下
=========================测试layer_sizes========================= 输入层的节点数量为: n_x = 5 隐藏层的节点数量为: n_h = 4 输出层的节点数量为: n_y = 2
初始化参数
我们已经定义了神经网络的层数,既然有两层的话,那么就会用两对w和b,我们将上面用到的三层分别传入
def initialize_parameters( n_x , n_h ,n_y):
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 parametersn_x是第一层,n_h是第二层,n_y是第三层
分析我们可以知道w[1]的规模如下[第一层神经元的数量,上一层神经元的数量],b[1]的规模是[第一层神经元的数量,1],同理w[2]的规模如下[第二层神经元的数量,和上一层神经元的数量],b[2]的规模是[第二层神经元的数量,1]
其中w和b中的内容,都是随机定义的,否则的话,不同的神经元的运算结果会是相同的,在吴恩达老师的课程中解释过,而且我们都要乘一个0.01,防止梯度过缓,但时候迭代差距不大,总结一下三个注意的问题
w和b的维数问题 一定要是随机值 乘0.01
然后我们测试一下我们的代码
#测试initialize_parameters
print("=========================测试initialize_parameters=========================")
n_x , n_h , n_y = initialize_parameters_test_case()
parameters = initialize_parameters(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"]))再去自定义的库中去看一下initialize_parameters_test_case
def initialize_parameters_test_case():
n_x, n_h, n_y = 2, 4, 1
return n_x, n_h, n_y我们就是定义了矩阵的规模,没什么要注意的,最后测试的结果
=========================测试initialize_parameters========================= W1 = [[-0.00416758 -0.00056267] [-0.02136196 0.01640271] [-0.01793436 -0.00841747] [ 0.00502881 -0.01245288]] b1 = [[ 0.] [ 0.] [ 0.] [ 0.]] W2 = [[-0.01057952 -0.00909008 0.00551454 0.02292208]] b2 = [[ 0.]]
向前传播
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
A1 = np.tanh(Z1)
Z2 = np.dot(W2 , A1) + b2
A2 = sigmoid(Z2)
#使用断言确保我的数据格式是正确的
assert(A2.shape == (1,X.shape[1]))
cache = {"Z1": Z1,
"A1": A1,
"Z2": Z2,
"A2": A2}
return (A2, cache)我们传入两对w和b,然后传入我们的x(2,400)的数据集,向前传播的公式就不仔细解释了,我们来分析一下我们矩阵维度的变化,W1的规模是[这一层的神经元,上一层的神经元(或者是输入特征值)],X就是(特征属性,样本的数量),b的规模就是[当前层的神经元的数量,1],因此我们的Z1就是[当前层的神经元的数量,样本数量],同理A1[当前层的神经元的数量,样本数量],Z2和A2也出来了[当前神经元的数量,样本数量],返回的值就是Z1,A1,Z2,A2
我们给出我们的测试数据,也是从自定义的库中去取的
def forward_propagation_test_case():
np.random.seed(1)
X_assess = np.random.randn(2, 3)
parameters = {'W1': np.array([[-0.00416758, -0.00056267],
[-0.02136196, 0.01640271],
[-0.01793436, -0.00841747],
[ 0.00502881, -0.01245288]]),
'W2': np.array([[-0.01057952, -0.00909008, 0.00551454, 0.02292208]]),
'b1': np.array([[ 0.],
[ 0.],
[ 0.],
[ 0.]]),
'b2': np.array([[ 0.]])}
return X_assess, parameters#测试forward_propagation
print("=========================测试forward_propagation=========================")
X_assess, parameters = forward_propagation_test_case()
A2, cache = forward_propagation(X_assess, parameters)
print(cache["Z1"], cache["A1"], cache["Z2"], cache["A2"])运算结果如下
=========================测试forward_propagation========================= [[-0.00616586 0.0020626 0.0034962 ] [-0.05229879 0.02726335 -0.02646869] [-0.02009991 0.00368692 0.02884556] [ 0.02153007 -0.01385322 0.02600471]] [[-0.00616578 0.0020626 0.00349619] [-0.05225116 0.02725659 -0.02646251] [-0.02009721 0.0036869 0.02883756] [ 0.02152675 -0.01385234 0.02599885]] [[ 0.00092281 -0.00056678 0.00095853]] [[0.5002307 0.49985831 0.50023963]]
损失函数的计算
先看代码
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然后就是测试,这里的测试和上面道理是相同的
#测试compute_cost
print("=========================测试compute_cost=========================")
A2 , Y_assess , parameters = compute_cost_test_case()
print("cost = " + str(compute_cost(A2,Y_assess,parameters)))=========================测试compute_cost========================= cost = 0.6929198937761266
正向的传播搞定了,我们在来看看反向传播
反向传播
首先是两层神经网络传播的公式,然后继续
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"]
A2 = cache["A2"]
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)
grads = {"dW1": dW1,
"db1": db1,
"dW2": dW2,
"db2": db2 }
return grads然后我们逐层进行分析
dZ2= A2 - Y dW2 = (1 / m) * np.dot(dZ2, A1.T)
分析一下规模的问题,dz2是(1,样本数),A1是(第一层神经元个数,样本数),A1*T是(样本数,第一层神经元个数),然后dw2是(1,第一层神经元个数)
db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True)
db2是(1,1)
dZ1 = np.multiply(np.dot(W2.T, dZ2), 1 - np.power(A1, 2))
1-np.power(A1,2)这个就是tanh的导数,multiply前后两个值是相同的维度
dW1 = (1 / m) * np.dot(dZ1, X.T)
db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True)dW1(当前神经元,输入特征值)
db1(当前神经元个数,1)
然后就是测试
#测试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"]))修改参数
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根据上面的几个函数,我们已经知道了W1,W2,b1,b2,dW1,dW2,db1,db2,然后我们通过梯度下降的方法来更新参数来获取到更好的参数
代码方面没什么要讲的
然后就是给出测试值,然后进行测试,先来看给出测试值的函数,这个是自定义库带有的
def update_parameters_test_case():
parameters = {'W1': np.array([[-0.00615039, 0.0169021 ],
[-0.02311792, 0.03137121],
[-0.0169217 , -0.01752545],
[ 0.00935436, -0.05018221]]),
'W2': np.array([[-0.0104319 , -0.04019007, 0.01607211, 0.04440255]]),
'b1': np.array([[ -8.97523455e-07],
[ 8.15562092e-06],
[ 6.04810633e-07],
[ -2.54560700e-06]]),
'b2': np.array([[ 9.14954378e-05]])}
grads = {'dW1': np.array([[ 0.00023322, -0.00205423],
[ 0.00082222, -0.00700776],
[-0.00031831, 0.0028636 ],
[-0.00092857, 0.00809933]]),
'dW2': np.array([[ -1.75740039e-05, 3.70231337e-03, -1.25683095e-03,
-2.55715317e-03]]),
'db1': np.array([[ 1.05570087e-07],
[ -3.81814487e-06],
[ -1.90155145e-07],
[ 5.46467802e-07]]),
'db2': np.array([[ -1.08923140e-05]])}
return parameters, grads然后就是测试和测试结果
#测试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"]))
测试结果如下:
=========================测试update_parameters=========================
W1 = [[-0.00643025 0.01936718]
[-0.02410458 0.03978052]
[-0.01653973 -0.02096177]
[ 0.01046864 -0.05990141]]
b1 = [[-1.02420756e-06]
[ 1.27373948e-05]
[ 8.32996807e-07]
[-3.20136836e-06]]
W2 = [[-0.01041081 -0.04463285 0.01758031 0.04747113]]
b2 = [[0.00010457]]整合到一起
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_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 = 0.5)
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 = 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) + '%')
京公网安备 11010502036488号