题目:
有一个数据集X,数据集是二维的。
有一个标签集Y,Y = 0或Y = 1。
要求构建单层神经网络进行数据的分类。
数据集:
数据集加载函数
X :训练集数据,维度:(2,400)
Y : 训练集标签,维度:(1,400)
#数据加载函数
def load_planar_dataset():
m = 400 # 样本数量
N = int(m/2) # 每类的样本数
D = 2 # feature维度
X = np.zeros((m,D)) # 数据矩阵,其中每行是一个示例
Y = np.zeros((m,1), dtype='uint8') # 标签向量(0表示红色,1表示蓝色)
a = 4 # maximum ray of the flower
for j in range(2):
ix = range(N*j,N*(j+1))#生成(0~N-1)和(N-1~2N)的序列
#下面是样本的两个feature
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)]#按列合并 维度:(m,D)
Y[ix] = j #label 维度:(m,1)
X = X.T
Y = Y.T
return X, Y
数据可视化:
X,Y = load_planar_dataset()
print('X的维度:',X.shape,',Y的维度:',Y.shape)#X的维度: (2, 400) ,Y的维度: (1, 400)
#绘制图像
plt.scatter(X[0,:],X[1,:],c=Y,s=40,cmap=plt.cm.Spectral,edgecolors="black")
输出:X的维度: (2, 400) ,Y的维度: (1, 400)
可以看出,数据的分布不是线性可分的。先使用线性分类器LR来实验一下
首先使用逻辑回归进行数据的分类
一、分界线绘制函数
#涉及等高线的绘画方法
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
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,edgecolors="black")
二、调用LR函数
#先尝试使用逻辑回归进行分类
lr_clf = sklearn.linear_model.LogisticRegression()
lr_clf = lr_clf.fit(X.T,Y.T)
#绘制分界线
plot_decision_boundary(lambda x:lr_clf.predict(x),X,Y)
#正确率只有47%,数据集线性不可分
print(lr_clf.score(X.T,Y.T))
实验结果:
正确率:0.4625
实验准确率只有46%,分类效果较差,并不能很好的进行分类。
搭建单层神经网络
一、初始化参数
注意事项:
W不能初始化为0
,否则梯度下降会无效,应初始化为一个接近0的小数,在0附件激活函数梯度较大。
#初始化参数w和b
'''
n_x:输入层的节点数
n_h:隐藏层的节点数
n_y:输出层的节点数
'''
def init_parameter(n_x,n_h,n_y):
#初始化,W初始化成接近0的数,b可以初始化为0
W_1 = np.random.randn(n_h,n_x)*0.01
b_1 = np.zeros((n_h,1))
W_2 = np.random.randn(n_y,n_h)*0.01
b_2 = np.zeros((n_y,1))
#打包成字典返回
parameter = {'W_1':W_1,'b_1':b_1,'W_2':W_2,'b_2':b_2}
return parameter
二、编写激活函数及其导数
-
sigmoid函数的导数:
g’(z) = g(z)(1-g(z)) -
tanh函数的导数:
g’(z) = 1- ( g(z) )2
#激活函数sigmoid
def sigmoid(Z):
return 1/(1+np.exp(-Z))
def back_sigmoid(Z):
return sigmoid(Z)*(1-sigmoi(Z))
#激活函数tanh
def tanh(Z):
return (np.exp(Z)-np.exp(-Z))/(np.exp(Z)+np.exp(-Z))
def back_tanh(Z):
return 1-np.square(tanh(Z))
三、代价函数
采用交叉熵形式
#计算代价函数(交叉熵)
def costfunction(A_2,Y):
cost = Y*np.log(A_2) +(1-Y)*np.log(1-A_2)
return (-1/m)*np.sum(cost,axis=1)
四、前向传播
前向传播特征,反向传播误差
#前向传播
def forward_propagation(X,parameter):
W_1,b_1,W_2,b_2 = parameter['W_1'],parameter['b_1'],parameter['W_2'],parameter['b_2']
#前向传播第0层-第一层
Z_1 = W_1@X + b_1
A_1 = tanh(Z_1)
#前向传播第1层-第二层
Z_2 = W_2@A_1 + b_2
A_2 = sigmoid(Z_2)
cache = {'Z_1':Z_1,'A_1':A_1,'Z_2':Z_2,'A_2':A_2}
return cache
五、反向传播
#反向传播
'''
parameter:W_1,b_1,W_2,b_2
'''
def back_propagation(X,Y,parameter):
#获取正向传播的输出参数
cache = forward_propagation(X,parameter)
Z_1,A_1,Z_2,A_2 = cache['Z_1'],cache['A_1'],cache['Z_2'],cache['A_2']
#第2层-第1层的反向传播
dZ_2 = A_2 - Y
dW_2 = (1/m)*dZ_2@A_1.T
db_2 = (1/m)*np.sum(dZ_2,axis=1,keepdims=True)
#第1层-第0层的反向传播
W_2 = parameter['W_2']
dZ_1 = W_2.T@dZ_2*back_tanh(Z_1)
dW_1 = (1/m)*dZ_1@X.T
db_1 = (1/m)*np.sum(dZ_1,axis=1,keepdims=True)
cache = {'dW_2':dW_2,'db_2':db_2,'dW_1':dW_1,'db_1':db_1}
return cache
六、梯度下降更新参数
#进行梯度下降更新参数
'''
parameter:W_1,b_1,W_2,b_2
grads:dW_1,db_1,dW_2,db_2
'''
def gradient_descent(parameter,grads,alpha):
#获取参数W和b
W_1,b_1,W_2,b_2 = parameter['W_1'],parameter['b_1'],parameter['W_2'],parameter['b_2']
#获取梯度
dW_1,db_1,dW_2,db_2 = grads['dW_1'],grads['db_1'],grads['dW_2'],grads['db_2']
#参数更新
W_1 = W_1 - alpha*dW_1
b_1 = b_1 - alpha*db_1
W_2 = W_2 - alpha*dW_2
b_2 = b_2 - alpha*db_2
#打包成字典返回
parameter = {'W_1':W_1,'b_1':b_1,'W_2':W_2,'b_2':b_2}
return parameter
七、模型整合
初始化参数–>迭代【前向传播+后向传播+更新参数】–>返回参数
#模型整合
def neural_network(X,Y,n_h,iterations,alpha,print_label):
#获取样本维度和样本数量
n_x,m = np.shape(X)[0],np.shape(X)[1]
n_y = np.shape(Y)[0]
#获取初始化参数
parameter = init_parameter(n_x,n_h,n_y)
W_1,b_1,W_2,b_2 = parameter['W_1'],parameter['b_1'],parameter['W_2'],parameter['b_2']
costs = []
#迭代更新
for i in range(iterations):
#正向传播
cache = forward_propagation(X,parameter)
Z_1,A_1,Z_2,A_2 = cache['Z_1'],cache['A_1'],cache['Z_2'],cache['A_2']
#记录代价函数值
if (i%1000 == 0) & (print_label):
cost = costfunction(A_2,Y)
costs.append(cost)
print("第",i,"次迭代,erro:",cost)
#获取梯度
grads = back_propagation(X,Y,parameter)
#更新参数
parameter = gradient_descent(parameter,grads,alpha)
return (parameter,costs)
八、预测
预测就是前向传播的过程
#预测函数
def predict(parameter,X):
cache = forward_propagation(X,parameter)
A_2 = cache["A_2"]
return np.round(A_2)
九、调用
#训练模型,获取参数
parameter,costs = neural_network(X,Y,4,10000,0.5,True)
#绘制分界线
plot_decision_boundary(lambda x:predict(parameter,x.T),X,Y)
#模型预测
y_pred = predict(parameter,X)
print('准确率为:',100*(1-np.mean(np.abs(Y-y_pred))),'%')
实验结果:
十、尝试不同的隐藏层数量
#更改隐藏节点数量
n_h_number=np.array([1,4,8,16])
for i in range(len(n_h_number)):
parameter,costs = neural_network(X,Y,n_h_number[i],10000,0.5,False)
#绘制分界线
plt.subplot(2,2,i+1)
plot_decision_boundary(lambda x:predict(parameter,x.T),X,Y)
#模型预测
y_pred = predict(parameter,X)
print(n_h_number[i],'个隐藏节点的准确率为:',100*(1-np.mean(np.abs(Y-y_pred))),'%')
实验结果:
当隐藏层节点变成1,就变成了一个线性分类器,存在欠拟合问题。
当隐藏层节点变多,可以捕获的特征变多,更好的拟合训练集数据。
完整代码
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import planar_utils
import sklearn
from sklearn import linear_model
np.random.seed(1)#设置固定的随机种子,保证实验可重复性
#数据加载函数
def load_planar_dataset():
m = 400 # 样本数量
N = int(m/2) # 每类的样本数
D = 2 # feature维度
X = np.zeros((m,D)) # 数据矩阵,其中每行是一个示例
Y = np.zeros((m,1), dtype='uint8') # 标签向量(0表示红色,1表示蓝色)
a = 4 # maximum ray of the flower
for j in range(2):
ix = range(N*j,N*(j+1))#生成(0~N-1)和(N-1~2N)的序列
#下面是样本的两个feature
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)]#按列合并 维度:(m,D)
Y[ix] = j #label 维度:(m,1)
X = X.T
Y = Y.T
return X, Y
#涉及等高线的绘画方法
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
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,edgecolors="black")
X,Y = load_planar_dataset()
print('X的维度:',X.shape,',Y的维度:',Y.shape)#X的维度: (2, 400) ,Y的维度: (1, 400)
#绘制图像
plt.scatter(X[0,:],X[1,:],c=Y,s=40,cmap=plt.cm.Spectral,edgecolors="black")
#样本数
m = np.shape(X)[1]
#维度
n = np.shape(X)[0]
m,n
#先尝试使用逻辑回归进行分类
lr_clf = sklearn.linear_model.LogisticRegression()
lr_clf = lr_clf.fit(X.T,Y.T)
#绘制分界线
plot_decision_boundary(lambda x:lr_clf.predict(x),X,Y)
#正确率只有47%,数据集线性不可分
print(lr_clf.score(X.T,Y.T))
#搭建单层神经网络
#初始化参数w和b
'''
n_x:输入层的节点数
n_h:隐藏层的节点数
n_y:输出层的节点数
'''
def init_parameter(n_x,n_h,n_y):
#初始化,W初始化成接近0的数,b可以初始化为0
W_1 = np.random.randn(n_h,n_x)*0.01
b_1 = np.zeros((n_h,1))
W_2 = np.random.randn(n_y,n_h)*0.01
b_2 = np.zeros((n_y,1))
#打包成字典返回
parameter = {'W_1':W_1,'b_1':b_1,'W_2':W_2,'b_2':b_2}
return parameter
#激活函数sigmoid
def sigmoid(Z):
return 1/(1+np.exp(-Z))
def back_sigmoid(Z):
return sigmoid(Z)*(1-sigmoi(Z))
#激活函数tanh
def tanh(Z):
return (np.exp(Z)-np.exp(-Z))/(np.exp(Z)+np.exp(-Z))
def back_tanh(Z):
return 1-np.square(tanh(Z))
#计算代价函数(交叉熵)
def costfunction(A_2,Y):
cost = Y*np.log(A_2) +(1-Y)*np.log(1-A_2)
return (-1/m)*np.sum(cost,axis=1)
#前向传播
def forward_propagation(X,parameter):
W_1,b_1,W_2,b_2 = parameter['W_1'],parameter['b_1'],parameter['W_2'],parameter['b_2']
#前向传播第0层-第一层
Z_1 = W_1@X + b_1
A_1 = tanh(Z_1)
#前向传播第1层-第二层
Z_2 = W_2@A_1 + b_2
A_2 = sigmoid(Z_2)
cache = {'Z_1':Z_1,'A_1':A_1,'Z_2':Z_2,'A_2':A_2}
return cache
#反向传播
def back_propagation(X,Y,parameter):
#获取正向传播的输出参数
cache = forward_propagation(X,parameter)
Z_1,A_1,Z_2,A_2 = cache['Z_1'],cache['A_1'],cache['Z_2'],cache['A_2']
#第2层-第1层的反向传播
dZ_2 = A_2 - Y
dW_2 = (1/m)*dZ_2@A_1.T
db_2 = (1/m)*np.sum(dZ_2,axis=1,keepdims=True)
#第1层-第0层的反向传播
W_2 = parameter['W_2']
dZ_1 = W_2.T@dZ_2*back_tanh(Z_1)
dW_1 = (1/m)*dZ_1@X.T
db_1 = (1/m)*np.sum(dZ_1,axis=1,keepdims=True)
cache = {'dW_2':dW_2,'db_2':db_2,'dW_1':dW_1,'db_1':db_1}
return cache
#进行梯度下降更新参数
'''
parameter:W_1,b_1,W_2,b_2
grads:dW_1,db_1,dW_2,db_2
'''
def gradient_descent(parameter,grads,alpha):
#获取参数W和b
W_1,b_1,W_2,b_2 = parameter['W_1'],parameter['b_1'],parameter['W_2'],parameter['b_2']
#获取梯度
dW_1,db_1,dW_2,db_2 = grads['dW_1'],grads['db_1'],grads['dW_2'],grads['db_2']
#参数更新
W_1 = W_1 - alpha*dW_1
b_1 = b_1 - alpha*db_1
W_2 = W_2 - alpha*dW_2
b_2 = b_2 - alpha*db_2
#打包成字典返回
parameter = {'W_1':W_1,'b_1':b_1,'W_2':W_2,'b_2':b_2}
return parameter
#模型整合
def neural_network(X,Y,n_h,iterations,alpha,print_label):
#获取样本维度和样本数量
n_x,m = np.shape(X)[0],np.shape(X)[1]
n_y = np.shape(Y)[0]
#获取初始化参数
parameter = init_parameter(n_x,n_h,n_y)
W_1,b_1,W_2,b_2 = parameter['W_1'],parameter['b_1'],parameter['W_2'],parameter['b_2']
costs = []
#迭代更新
for i in range(iterations):
#正向传播
cache = forward_propagation(X,parameter)
Z_1,A_1,Z_2,A_2 = cache['Z_1'],cache['A_1'],cache['Z_2'],cache['A_2']
#记录代价函数值
if (i%1000 == 0) & (print_label):
cost = costfunction(A_2,Y)
costs.append(cost)
print("第",i,"次迭代,erro:",cost)
#获取梯度
grads = back_propagation(X,Y,parameter)
#更新参数
parameter = gradient_descent(parameter,grads,alpha)
return (parameter,costs)
#预测函数
def predict(parameter,X):
cache = forward_propagation(X,parameter)
A_2 = cache["A_2"]
return np.round(A_2)
parameter,costs = neural_network(X,Y,4,10000,0.5,True)
#绘制分界线
plot_decision_boundary(lambda x:predict(parameter,x.T),X,Y)
#模型预测
y_pred = predict(parameter,X)
print('准确率为:',100*(1-np.mean(np.abs(Y-y_pred))),'%')
#更改隐藏节点数量
n_h_number=np.array([1,4,8,16])
for i in range(len(n_h_number)):
parameter,costs = neural_network(X,Y,n_h_number[i],10000,0.5,False)
#绘制分界线
plt.subplot(2,2,i+1)
plot_decision_boundary(lambda x:predict(parameter,x.T),X,Y)
#模型预测
y_pred = predict(parameter,X)
print(n_h_number[i],'个隐藏节点的准确率为:',100*(1-np.mean(np.abs(Y-y_pred))),'%')