吴恩达作业(2)-带有一个隐藏层的平面数据分类

题目:
有一个数据集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

二、编写激活函数及其导数

  1. sigmoid函数的导数:
    g’(z) = g(z)(1-g(z))

  2. 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))),'%')
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值