吴恩达深度学习专项课程的所有实验均采用Jupyter Notebooks实现,不熟悉的朋友可以提前使用一下Notebooks。本周的实验主要是训练一个单隐层神经网络,进行2分类任务。采用的数据集都是平面数据集(2维数据集,输入特征数=2)。
目录
1.实验综述
2.导入必要的包
import numpy as np
import matplotlib.pyplot as plt
from testCases import * #提供一些测试用例 定义在testCases.py文件中
import sklearn #提供简单高效的数据挖掘和分析工具
import sklearn.datasets
import sklearn.linear_model
#提供本实验中可以用到的各种有用的函数
#这些函数定义在 planar_utils.py文件中
from planar_utils import plot_decision_boundary,sigmoid,load_planar_dataset,load_extra_datasets
3.数据集
首先看一下待分类数据集,下面的代码将会加载一个2分类数据集。
- 可视化数据集
X,Y=load_planar_dataset() #加载2维数据集 X:特征矩阵(n_x,m)(每一列是样本的特征向量(包含两个特征)) Y:(1,m)样本标签
#可视化该数据集 形状是一朵“花” 红色代表y=0,蓝色y=1.实验任务是构建模型分类这些数据
#Y是一个用二维数组表示的行向量(1,m) 使用np.squeeze(Y) 把Y变为包含m个元素的一维数组
plt.scatter(X[0,:],X[1,:],c=np.squeeze(Y),s=40,cmap=plt.cm.Spectral)
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 #每列表示一个样本 (D,m)
Y = Y.T #(1,m)
return X, Y
- 查看数据集大小
#查看样本数 X,Y的形状
shape_X=X.shape
shape_Y=Y.shape
m=X.shape[1] #样本数为矩阵X的列数 X:nx*m (nx=2)
print ('The shape of X is: ' + str(shape_X))
print ('The shape of Y is: ' + str(shape_Y))
print ('I have m = %d training examples!' % (m))
4.简单逻辑回归
再使用神经网络进行分类之前,先使用逻辑回归分类器分类上述数据集。
直接调用sklearn封装好的逻辑回归模型
- 训练逻辑回归分类器
#训练逻辑回归分类器
clf = sklearn.linear_model.LogisticRegressionCV(); #使用默认的超参数
clf.fit(X.T,Y.T) #X.T (m,nx) Y.T(m,1) 每一行代表一个样本
- 绘制决策边界,计算在训练集上的准确率
#绘制逻辑回归的决策边界
plot_decision_boundary(lambda x: clf.predict(x),X,np.squeeze(Y))
plt.title('Logistic Regression')
#打印准确率
LR_predictions=clf.predict(X.T) #预测的label
print ('Accuracy of logistic regression: %d ' % float((np.dot(Y,LR_predictions) + np.dot(1-Y,1-LR_predictions))/float(Y.size)*100) +
'% ' + "(percentage of correctly labelled datapoints)")
def plot_decision_boundary(model, X, y):
# Set min and max values and give it some padding
#确定网格的边界
x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1
y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1
h = 0.01
# Generate a grid of points with distance h between them
#生成网格点
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
# Predict the function value for the whole grid
#预测每个网格点的label
Z = model(np.c_[xx.ravel(), yy.ravel()])
#转为网格的形状
Z = Z.reshape(xx.shape)
# Plot the contour and training examples
#绘制等高线图 渲染决策区域和决策边界
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
plt.ylabel('x2')
plt.xlabel('x1')
#画出数据集的样本点
plt.scatter(X[0, :], X[1, :], c=y, cmap=plt.cm.Spectral)
- 总结
上图中的数据集是线性不可分的,所以逻辑回归分类器(线性分类器)的效果不是很好。接下来试一下神经网络分类器。
5.神经网络模型
训练单隐层神经网络模型。
- 网络结构
- 数学表达式
- 构建神经网络的一般方法
- 定义神经网络结构
def layer_sizes(X,Y):
'''
参数:
X:数据集样本特征矩阵 (n_x,m)
Y: 数据集样本对于标签 (1,m)
返回:
n_x:输入层单元数
n_h:隐层单元数
n_y:输出层单元数
'''
n_x=X.shape[0] #样本特征向量维数(特征数量)
n_h=4
n_y=Y.shape[0]
return (n_x,n_h,n_y)
X_assess, Y_assess = layer_sizes_test_case()
(n_x, n_h, n_y) = layer_sizes(X_assess, Y_assess)
print("The size of the input layer is: n_x = " + str(n_x))
print("The size of the hidden layer is: n_h = " + str(n_h))
print("The size of the output layer is: n_y = " + str(n_y))
- 随机初始化模型参数
确保参数的维度是正确的。
随机初始化权重参数,偏置参数初始化为0.
def initialize_parameters(n_x,n_h,n_y):
'''
参数:
n_x:输入层单元数
n_h:隐层单元数
n_y:输出层单元数
返回:
初始化的参数,字典形式(若每一列是一个样本的特征向量话,W的维度是后一层单元数*前一层单元数)
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((n_h,1))
W2=np.random.randn(n_y,n_h)*0.01
b2=np.zeros((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
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"]))
- 迭代
1)前向传播:
def forward_propagation(X,parameters):
'''
X:数据集的输入特征矩阵 每一列是一个样本的输入特征向量 (n_x,m)
parameters:模型参数 字典形式
返回:
A2:神经网络的输出
cache:存储中间结果Z1,A1,Z2,A2 字典形式
'''
W1=parameters['W1'] #(n_h,n_x)
b1=parameters['b1'] #(n_h,1)
W2=parameters['W2'] #(n_y=1,n_h)
b2=parameters['b2'] #(n_y=1,1)
Z1=np.dot(W1,X)+b1 #(n_h,m)
A1=np.tanh(Z1) #(n_h,m) 隐层使用tanh激活函数
Z2=np.dot(W2,A1)+b2 #(n_y=1,m)
A2=sigmoid(Z2) #2分类 输出层用sigmoid激活函数(planar_utils.py中定义) (n_y=1,m)
assert(A2.shape == (1, X.shape[1])) #(1,m)
#缓存中间结果 便于反向传播计算梯度
cache = {"Z1": Z1,
"A1": A1,
"Z2": Z2,
"A2": A2}
return A2,cache
X_assess, parameters = forward_propagation_test_case()
A2, cache = forward_propagation(X_assess, parameters)
# Note: we use the mean here just to make sure that your output matches ours.
print(np.mean(cache['Z1']) ,np.mean(cache['A1']),np.mean(cache['Z2']),np.mean(cache['A2']))
2)计算代价函数
def compute_cost(A2,Y,parameters):
'''
计算交叉熵损失 上式(13)
参数:
A2: 神经网络输出 (1,m) 预测概率
Y: 样本真实标签 (1,m)
parameters:模型参数
返回:
cost:交叉熵损失 上式(13)
'''
cost=-np.mean(Y*np.log(A2)+(1-Y)*np.log(1-A2)) #*等价于np.multiply 对应位置元素相乘
assert(isinstance(cost,float))
return cost
A2, Y_assess, parameters = compute_cost_test_case()
print("cost = " + str(compute_cost(A2, Y_assess, parameters)))
3)反向传播(向量化)
def backward_propagation(parameters,cache,X,Y):
'''
实现神经网络的反向传播
参数:
parameters:模型参数
cache:存储前向传播的中间结果 Z1,A1,Z2,A2
X:数据集特征矩阵 (n_x,m)
Y:数据集样本对应标签 (1,m)
返回:
grads:模型参数的梯度 字典形式
'''
m=X.shape[1]
A1=cache['A1'] #(n_h,m)
A2=cache['A2'] #(1,m)
W1=parameters['W1']
W2=parameters['W2']
dZ2=A2-Y #(1,m)
dW2=np.dot(dZ2,A1.T)/m #(1,n_h)
db2=np.mean(dZ2,axis=1,keepdims=True) #标量
dZ1=np.dot(W2.T,dZ2)*(1-np.power(A1,2)) #(n_h,m) a=g(z)=tanz求导 1-a^2
dW1=np.dot(dZ1,X.T)/m #(n_h,n_x)
db1=np.mean(dZ1,axis=1,keepdims=True) #(n_h,1)
#keepdims=True 保持维度,用2维数组表示向量
grads = {"dW1": dW1,
"db1": db1,
"dW2": dW2,
"db2": db2}
return grads
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"]))
4)使用梯度下降法更新参数
def update_parameters(parameters,grads,learning_rate=1.2):
'''
使用梯度下降法更新参数
参数:
parameters:模型参数
grads:模型参数的梯度
learning_rate:学习率
返回:
parameters:更新后的参数
'''
W1=parameters['W1']
b1=parameters['b1']
W2=parameters['W2']
b2=parameters['b2']
dW1=grads['dW1']
db1=grads['db1']
dW2=grads['dW2']
db2=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
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"]))
- 把以上三部分汇总到nn_model()中
def nn_model(X,Y,n_h,num_iterations=10000,print_cost=False):
'''
参数:
X:数据集特征矩阵 (n_x,m)
Y:数据集样本标签 (1,m)
n_h:隐层单元数量
num_iterations:梯度下降迭代次数
print_cost:若为True,没1000次迭代 打印一次cost
返回:
parameters:学习好的参数
costs:每1000次迭代后的cost
'''
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']
costs=[]
#梯度下降法迭代过程
for i in range(num_iterations): #使用batch GD,每次使用全部的样本计算梯度,做一次梯度下降
A2,cache=forward_propagation(X,parameters) #前向传播
cost=compute_cost(A2,Y,parameters) #计算cost
grads=backward_propagation(parameters,cache,X,Y) #反向传播
parameters=update_parameters(parameters,grads)#更新参数
if print_cost and i%1000==0:
costs.append(cost)
print ("Cost after iteration %i: %f" %(i, cost))
return parameters,costs
X_assess, Y_assess = nn_model_test_case()
parameters,_= nn_model(X_assess, Y_assess, 4, num_iterations=10000, print_cost=True)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
- 预测
def predict(parameters,X):
'''
参数:
parameters:训练好的参数 字典形式
X:数据集样本特征矩阵 (n_x,m)
返回:
predictions:模型在数据集X上的预测结果 red:0/blue:1
'''
A2,cache=forward_propagation(X,parameters)
predictions=np.around(A2)
'''
predictions[A2>0.5]=1
predictions[A2<=0.5]=0
'''
return predictions
parameters, X_assess = predict_test_case()
predictions = predict(parameters, X_assess)
print("predictions mean = " + str(np.mean(predictions)))
1)查看神经网络模型在planar数据集上的分类效果
#训练一个有n_h个隐藏单元的单隐层神经网络
parameters,_=nn_model(X,Y,n_h=4,num_iterations=10000,print_cost=True)
#绘制决策边界
plot_decision_boundary(lambda x:predict(parameters,x.T),X,np.squeeze(Y))
plt.title("Decision Boundary for hidden layer size " + str(4))
2)打印模型在训练集上的准确率
#打印模型在训练上的准确率
predictions = predict(parameters, X)
print ('Accuracy: %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 = 5000)
plot_decision_boundary(lambda x: predict(parameters, x.T), X, np.squeeze(Y))
predictions = predict(parameters, X)
accuracy = float((np.dot(Y,predictions.T) + np.dot(1-Y,1-predictions.T))/float(Y.size)*100)
print ("Accuracy for {} hidden units: {} %".format(n_h, accuracy))
- 其他尝试
- 实验总结
6.在其他数据集上进行尝试
# 加载数据集(4个)
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" #可以更改为不同的数据集
#选择noisy_moons数据集
X, Y = datasets[dataset]
X, Y = X.T, Y.reshape(1, Y.shape[0]) #X转型为特征矩阵(n_x,m) 每一列为一个样本的特征向量
#Y转型为行向量 (1,m)
# 对标签Y进行处理 进行2分类 0/1
if dataset == "noisy_circles":
Y = Y%2
#可视化数据集
plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral);
- 可视化数据集
- 训练单隐层神经网络 尝试不同隐层单元数
#训练单隐层神经网络 尝试不同隐层单元数
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 = 5000)
plot_decision_boundary(lambda x: predict(parameters, x.T), X,np.squeeze(Y))
predictions = predict(parameters, X)
accuracy = float((np.dot(Y,predictions.T) + np.dot(1-Y,1-predictions.T))/float(Y.size)*100)
print ("Accuracy for {} hidden units: {} %".format(n_h, accuracy))
- 训练集准确率
- 决策边界