numpy搭建浅层神经网络

numpy 搭建一个浅层神经网络来对数据进行简单分类

一个浅层神经网络一般只有两层,输入层并不算在内
数据集: sklearn.datasets 里面的 make_moons 构造数据
numpy 实现

目录:

1、构建数据

这里我们用的是 sklearn.datases 里面的 make_moons 来构造一个月牙形的数据

1.1、引入相应的库


import numpy as np
import matplotlib.pyplot as plt
import sklearn
import sklearn.datasets

%matplotlib inline
np.random.seed(1)

后面的代码都用这里引入的库

1.2、把数据以散点图的方式展现出来

这里我们产生的数据的维度是 X: (400, 2),我们通过转置给它转换成 (2, 400)。Y: (400,),先把它转化成标准的 (400, 1),再把它转换成 (1, 400) 的维度
make_moons 函数参数:
n_samples: 样本数量,400
shuffle: 是否打乱,类似于将数据集 random 一下
noise: 默认是false,数据集是否加入高斯噪声
random_state:生成随机种子,给定一个int型数据,能够保证每次生成数据相同。


X, Y = sklearn.datasets.make_moons(400, shuffle=True, noise=0.2, random_state=50)
X = X.T
Y = Y.reshape(Y.shape[0], 1).T
print (X.shape)
print (Y.shape)

# 画图
# X[0, :] 代表 x 轴
# X[1, :] 代表 y 轴
# Y.rabel 代表这些点的颜色,1 代表的是蓝色,0 代表红色,前提 cmap = plt.cm.Spectral
# c 参数接受的是一个 (m,) 的数据,而 Y 被我们转换成 (1, m) 型的,所以用 Y.ravel()
# s = 40 代表的是点的大小
plt.scatter(X[0, :], X[1, :], c=Y.ravel(), s=40, cmap=plt.cm.Spectral)

输出:
(2, 400)
(1, 400)

2、构建网络

1.1、初始化参数


# 随机初始化参数
def init_params(x_dim, h_dim, y_dim):
    '''
    参数
    x_dim: 输入单元数,即特征数
    h_dim: 隐藏单元个数
    y_dim: 输出单元个数
    
    返回值
    params: 初始化后的参数字典
    '''
    np.random.seed(3)

    # W1 的维度是 (h_dim, x_dim), b: shape = (h_dim, 1)
    W1 = np.random.randn(h_dim, x_dim) / np.sqrt(x_dim)
    b1 = np.zeros((h_dim, 1))
    
    # W2 的维度是 (y_dim, h_dim), b: shape = (y_dim, 1)
    W2 = np.random.randn(y_dim, h_dim) / np.sqrt(h_dim)
    b2 = np.zeros((y_dim, 1))
    
    params = {'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}
    
    return params

这里我们可以输出看一下它的维度


# 检验维度
params = init_params(2, 4, 1)
print ('W1', params['W1'].shape)
print ('b1', params['b1'].shape)
print ('W2', params['W2'].shape)
print ('b2', params['b2'].shape)

输出:
W1 (4, 2)
b1 (4, 1)
W2 (1, 4)
b2 (1, 1)

1.2、前向传播

这里第一层用的激活函数为 tanh,第二层激活函数用的是 sigmoid

Z 1 = W 1 ⋅ X + b 1 Z1 = W1 · X + b1 Z1=W1X+b1
A 1 = g ( Z 1 ) A1 = g(Z1) A1=g(Z1)

Z 2 = W 2 ⋅ A 1 + b 2 Z2 = W2 · A1 + b2 Z2=W2A1+b2
A 2 = g ( Z 2 ) A2 = g(Z2) A2=g(Z2)


# sigmoid
def sigmoid(Z):
    return 1 / (1 + np.exp(-Z))

# 前向传播
def forward_propagation(X, params):
    '''
    参数
    X: shape=(2, m)
    params: 存放 W, b 的字典
    
    返回值
    A2: 前向传播结束之后得到的预测值
    caches: 存放 Z1,A1 的缓存
    '''
    W1 = params['W1']
    b1 = params['b1']
    W2 = params['W2']
    b2 = params['b2']
    
    Z1 = np.dot(W1, X) + b1
    A1 = np.tanh(Z1)
    
    Z2 = np.dot(W2, A1) + b2
    A2 = sigmoid(Z2)
    
    caches = {'Z1': Z1, 'A1': A1, 'Z2': Z2, 'A2': A2}
    
    return A2, caches

1.3、计算损失

J ( c o s t ) = − 1 m [ Y ⋅ l o g ( Y _ h a t ) . T + ( 1 − Y ) ⋅ l o g ( 1 − Y _ h a t ) . T ] J(cost) = -\frac{1}{m} [Y·log(Y\_hat).T + (1-Y)·log(1-Y\_hat).T] J(cost)=m1[Ylog(Y_hat).T+(1Y)log(1Y_hat).T]


def get_cost(Y_hat, Y):
    m = Y_hat.shape[1]
    cost = -1 / m * (np.dot(Y, np.log(Y_hat).T) + np.dot(1 - Y, np.log(1 - Y_hat).T))
    # 把计算后的 cost 转换成一个数字
    cost = np.squeeze(cost)
    return cost

我们可以检验一下它的维度


cost = get_cost(Y_hat, Y)
print (cost)

输出:
0.7582110023564143

1.4、反向传播

d Z [ 2 ] = A [ 2 ] − Y dZ^{[2]} = A^{[2]} - Y dZ[2]=A[2]Y d W [ 2 ] = d Z [ 2 ] ⋅ A [ 1 ] m dW^{[2]} = \frac{dZ^{[2]} · A^{[1]}}{m} dW[2]=mdZ[2]A[1] d b [ 2 ] = ∑ i = 0 m d Z [ 2 ] m db^{[2]} = \frac{\sum_{i=0}^mdZ^{[2]}}{m} db[2]=mi=0mdZ[2]
d A [ 1 ] = W [ 2 ] ⋅ d Z [ 2 ] dA^{[1]} = W^{[2]} · dZ^{[2]} dA[1]=W[2]dZ[2] d W [ 1 ] = d Z [ 1 ] ⋅ A [ 1 ] m dW^{[1]} = \frac{dZ^{[1]} · A^{[1]}}{m} dW[1]=mdZ[1]A[1] d b [ 1 ] = ∑ i = 0 m d Z [ 1 ] m db^{[1]} = \frac{\sum_{i=0}^mdZ^{[1]}}{m} db[1]=mi=0mdZ[1]


# 反向传播
def backward_propagation(X, Y_hat, Y, params, caches):
    '''
    参数
    Y_hat: 预测,也就是前面的 A2
    Y: 真实值
    params: W, b
    caches; Z, A
    
    返回值
    grads: 梯度值,存放计算后的 dW, db
    '''
    m = X.shape[1]
    W1 = params['W1']
    W2 = params['W2']
    
    Z1 = caches['Z1']
    A1 = caches['A1']
    Z2 = caches['Z2']
    
    dZ2 = Y_hat - Y
    dW2 = np.dot(dZ2, A1.T) / m
    db2 = np.sum(dZ2, axis=1, keepdims=True) / m
    dA1 = np.dot(W2.T, dZ2)
    
    dZ1 = dA1 * (1 - np.power(A1, 2))
    dW1 = np.dot(dZ1, X.T) / m
    db1 = np.sum(dZ1, axis=1, keepdims=True) / m
    
    grads = {'dW1': dW1, 'db1': db1, 'dW2': dW2, 'db2': db2}
    
    return grads

1.5、更新参数

α \alpha α为学习率,代码中的 learning_rate

W [ 1 ] = W [ 1 ] − α ⋅ d W [ 1 ] W^{[1]} = W^{[1]} - \alpha · dW^{[1]} W[1]=W[1]αdW[1]
b [ 1 ] = b [ 1 ] − α ⋅ d b [ 1 ] b^{[1]} = b^{[1]} - \alpha · db^{[1]} b[1]=b[1]αdb[1]

W [ 2 ] = W [ 2 ] − α ⋅ d W [ 2 ] W^{[2]} = W^{[2]} - \alpha · dW^{[2]} W[2]=W[2]αdW[2]
b [ 2 ] = b [ 2 ] − α ⋅ d b [ 2 ] b^{[2]} = b^{[2]} - \alpha · db^{[2]} b[2]=b[2]αdb[2]


def update_params(X, params, grads, learning_rate):
    '''
    参数
    X: shape = (2, m)
    params: 参数
    grads: 梯度值
    learning_rate: 学习率

    返回值
    params: 新的参数
    '''
    params['W1'] -= learning_rate * grads['dW1']
    params['b1'] -= learning_rate * grads['db1']    
    params['W2'] -= learning_rate * grads['dW2']    
    params['b2'] -= learning_rate * grads['db2']    
    
    return params

1.6、构建完整模型


def nn_model(X, Y, h_dim=4, epochs=1000, learning_rate=1.2):
    '''
    参数
    X: shape = (2, m)
    Y: shape = (1, m)
    h_dim: 隐藏层的单元数
    epochs: 迭代次数
    learning_rate: 学习率

    返回值
    params: 训练后的参数,也就是训练后的模型
    '''
    # 输入维度是 2, 输出维度是 1
    x_dim, y_dim = 2, 1

    # 初始化参数
    params = init_params(x_dim, h_dim, y_dim)
    
    for i in range(1, epochs + 1):
        # 前向传播
        Y_hat, caches = forward_propagation(X, params)
        
        # 计算损失
        cost = get_cost(Y_hat, Y)
        
        # 反向传播
        grads = backward_propagation(X, Y_hat, Y, params, caches)
        
        # 更新参数
        params = update_params(X, params, grads, learning_rate)
        
        # 迭代 1000 就打印 cost
        if i % 1000 == 0:
            print ('Iter: {:04}, cost: {:.5f}'.format(i, cost))
    
    return params

3、预测

预测即利用训练好的参数进行一次前向传播后的 Y_hat


# 预测
def predict(params, X):
    Y_hat, caches = forward_propagation(X, params)
    # 解释一下 np.round() 函数,有点像四舍五入函数,小于等于 0.5 的就归为 0,反之为 1
    preds = np.round(Y_hat)
    return preds

4、开始训练

先定义一个函数用来描出边界


# 画出边界
# 画出决策边界
def plot_decision_boundary(model, X, y):
    # 指定范围
    # 
    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
    
    # 利用 np.meshgrid 生成一个坐标矩阵
    # np.arange(x_min, x_max, h) 生成 x 的坐标,返回的是一个数组,范围为 x_min - x_max,步长为 0.01
    # np.arange(y_min, y_max, h) 生成 y 的坐标,返回的是一个数组
    # 假如这里输出 xx: (435, 581),yy: (435, 581)
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h ))

    # xx.ravel() 方法是使高维变为一维,即从 (435, 581) -> (252735, )
    # xx.ravel() 变成了一维的数组(一列),那么 yy.ravel() 同理
    # np.c_[xx.ravel(), yy.ravel()]方法把两列组成一个两列的矩阵 (252735, 2)
    # 这就和我们的输入数据 X 差不多了,2 个特征的数据
    # 然后我们又借助 model 方法来进行这些点的预测,就是这些点该是 0,还是 1。
    # 因为我们需要的输入维度是 (2, m) 的,所以 lambda x: predict(params, x.T) 我们要加上个转置
    Z = model(np.c_[xx.ravel(), yy.ravel()])
    
    # 预测好了之后,我们又把它归为,转为坐标矩阵,只是现在它们都有了一个身份,也就是下面的图内是红色还是蓝色                                                                                                                       
    Z = Z.reshape(xx.shape)
    
    # 下面的参数中,xx, yy 组成坐标矩阵,Z 则是预测值,即标志它们是 0 还是 1,cmap 则指定配色版 
    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)
# 训练
params = nn_model(X, Y, h_dim=4, epochs=10000)
predictions = predict(params, X)
accuracy = float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size))              
print ('\n预测准确率是: {:.2f}'.format(accuracy))
# 画出决策边界
plot_decision_boundary(lambda x: predict(params, x.T), X, Y.ravel())

输出:

Iter: 1000, cost: 0.06048
Iter: 2000, cost: 0.05541
Iter: 3000, cost: 0.05331
Iter: 4000, cost: 0.05229
Iter: 5000, cost: 0.05167
Iter: 6000, cost: 0.05122
Iter: 7000, cost: 0.05073
Iter: 8000, cost: 0.04988
Iter: 9000, cost: 0.04914
Iter: 10000, cost: 0.04845

预测准确率是: 0.98

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值