用一个简单的神经网络实现异或功能

前言

前面学到,单层的逻辑回归模型无法解决线性不可分的问题,因此可以使用多层神经网络模型,其中神经网络的隐藏层可以被理解为用来将线性不可分的数据变为线性可分。
李弘毅老师的机器学习课程在讲完反向传播后就直接开始讲keras的使用了,而我则迫切想自己从零开始实现一个简单的神经网络,因此便有了这篇文章。
我设计的神经网络为全连接网络,如下:
在这里插入图片描述
由于存在问题,这里只重点记录存在的问题。

问题

  1. 初始化权重的问题:
    测试发现,权重初始化的值对于模型准确率的影响非常大,例如最初我使用numpy.random.rand()对权重初始化,这会使权重固定在 [ 0   1 ) [0\ 1) [0 1),在这种情况下,模型表现出对异或的分辨能力时好时坏(总体来说很差劲,即使分对了,可能性也只在0.6左右);后来,我使用numpy.random.randn()进行权重初始化,模型似乎准确了一些(0.7);由此我想到是否是随机权重的权值太低的问题,在上面的基础上乘以了一个系数(反复尝试得出)2,此时模型才表现出了相对令人置信的结果(仍然不是特别好):
    在这里插入图片描述
  2. 使用adagrad优化出现的问题:
    使用adagrad进行优化,但一直报错说除零错误,看了一下如果加上adagrad,迭代没几次权重和偏置就变成了0,百思不得其解…

代码

# -*- coding:utf-8 -*-
import numpy as np

x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
# 含有一个隐藏层的神经网络实现逻辑异或运算
y = np.array([[0], [1], [1], [0]])
# 如果实现的是与运算,将上面改为 y = np.array([[0], [0], [0], [1]])
w1 = 2 * np.random.randn(2, 3)
b1 = np.zeros((3, 1))
w2 = 2 * np.random.randn(3, 1)
b2 = 0.0
hidden = np.zeros((3, 1))
output = 0.0
dc_dz1 = np.zeros((3, 1))
dc_dz2 = 0.0


def sigmoid(z):
	# sigmoid
    return 1.0 / (1.0 + np.exp(-z))
    # tanh
    # return (np.exp(z)-np.exp(-z))/(np.exp(z)+np.exp(-z))
    # leaky relu
    # z = np.array(z)
    # z[z <= 0] *= 0.01
    # z = np.matrix(z)
    # return z


def dsigmoid(z):
	# sigmoid
    return sigmoid(z) * (1-sigmoid(z))
	# tanh
	# return 1 - sigmoid(z)**2
    # leaky relu
    # z[z <= 0] = 0.01
    # z[z > 1] = 1
    # return z


def forward_pass(index):
    global z1, z2
    z1 = w1.T.dot(np.matrix(x[index]).T) + b1
    hidden = sigmoid(z1)  # 3x1 计算的是中间层的值
    z2 = w2.T.dot(hidden) + b2
    output = sigmoid(z2)  # 输出为单个值,表示[x1, x2]属于类别一的概率


def back_pass(index):
    global  dc_dz1, dc_dz2
    # 先计算output的C对z2的偏导数
    # dc_dz2 = dsigmoid(z2) * (-y[index]/sigmoid(z2) + (1-y[index])/(1-sigmoid(z2)))
    # 下面这是上面化简的结果,因为 d_sig(x) = sig(x) * (1 - sig(x))
    # 而如果是上式的话,可能在分母部分出现除零错误
    dc_dz2 = (sigmoid(z2) - y[index])
    # 再计算隐藏层的C对z1的偏导数
    for i in range(len(dc_dz1)):
        dc_dz1[i] = dc_dz2 * w2[i] * dsigmoid(z1[i])


def gradient_descent():
    global w1, w2, b1, b2
    epochs = 1000
    learning_rate = 1.0
    sum_b1, sum_b2, sum_w1, sum_w2 = 0., 0., 0., 0.
    for i in range(epochs):
        # 初始化梯度为0
        grad_w1 = np.zeros((2, 3))
        grad_w2 = np.zeros((3, 1))
        grad_b1 = np.zeros((3, 1))
        grad_b2 = 0.0
        length = len(x)
        # 计算所有样本梯度之和
        for j in range(length):
            forward_pass(j)  # 前向传播计算所有神经元的值
            back_pass(j)  # 反向传播计算C对z的梯度
            # print(dc_dz2)
            grad_w2 += hidden * dc_dz2
            # t = np.matrix(x[j])
            grad_w1 += dc_dz1.dot(np.matrix(x[j])).T
            grad_b1 += dc_dz1
            grad_b2 += dc_dz2
            # print(dc_dz2)
        # 取样本平均梯度,防止计算溢出
        #grad_w1 /= length
        #grad_w2 /= length
        #grad_b1 /= length
        #grad_b2 /= length
        # adagrad
        #sum_b1 += grad_b1**2
        #sum_b2 += grad_b2**2
        #sum_w1 += grad_w1**2
        #um_w2 += grad_w2**2
        w1 -= learning_rate * grad_w1
        w2 -= learning_rate * grad_w2
        b1 -= learning_rate * grad_b1
        b2 -= learning_rate * grad_b2


def test_network():
    for i in range(len(x)):
        t = np.matrix(x[i])
        hid = sigmoid(w1.T.dot(t.T)+b1)
        out = sigmoid(hid.T.dot(w2)+b2)
        flag = 0
        if out >= 0.5:
            flag = 1
        print('input:', x[i], 'output:', flag, 'probability:%.6f' % out)


def show_variable():
    print(w1)
    print(w2)
    print(b1)
    print(b2)


if __name__ == '__main__':

    # 前向传播与反向传播的计算结果相乘即为C对w[i]的偏导数
    gradient_descent()
    test_network()
    # show_variable()

模型在与或门的表现

1.或门
在这里插入图片描述
2.与门
在这里插入图片描述

使用tanh作为激活函数区分异或的效果

其中一次的效果出奇的好:
在这里插入图片描述

使用ReLU的效果

(并不是每次都这么好,和上面一样取决于初始化的权重,这两个相比sigmoid似乎更不稳定)
在这里插入图片描述

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
构建一个单层的神经网络实现异或功能,可以按照以下步骤采用Python来实现: 1. 导入所需的库:首先需要导入`numpy`库,用于进行矩阵运算。 2. 定义网络结构:单层神经网络由输入层和输出层构成。输入层有2个神经元,输出层有1个神经元。 3. 初始化权重和偏置:通过随机初始化权重和偏置来使网络具有一定的初始参数。可以使用`np.random.randn()`来生成服从标准正态分布的随机数。 4. 定义激活函数:为了引入非线性因素,可以选择一种合适的激活函数。在这里,可以使用Sigmoid函数作为激活函数。 5. 定义前向传播函数:将输入数据通过权重和偏置传播,得到输出结果。可以使用矩阵乘法运算和激活函数来实现。 6. 定义损失函数:通过比较网络输出和实际标签的差异,定义一个损失函数来度量输出的准确性。可以选择均方差作为损失函数。 7. 定义反向传播函数:通过计算损失函数对权重和偏置的偏导数来更新网络参数。可以使用梯度下降法来进行参数更新。 8. 训练模型:使用训练数据进行多次迭代,不断更新网络参数,使得损失函数逐渐减小。可以选择合适的学习率和迭代次数。 9. 预测结果:使用训练好的模型对新数据进行预测。将新数据输入网络,通过前向传播函数得到输出结果。 以上是实现一个单层神经网络来解决异或问题的基本步骤。可以根据具体需求对其的细节进行调整和优化。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值