Python快速实现最简单的神经网络

这篇博客介绍了作者根据理解手工实现的神经网络,包括单个感知器和多层神经网络层。代码中定义了激活函数、感知器类和神经网络层类,并给出了两个测试用例。在训练过程中,使用了反向传播更新权重。测试结果显示,网络能够对简单的分类任务进行学习并得到接近预期的结果。
摘要由CSDN通过智能技术生成

前言

该代码为本人在闲暇时按照对神经网络算法的理解手撸的,没有参考任何的代码和完成后也没有double check,所以不保证步骤完全正确,只能从测试用例上看出代码还是能用的。

本着"Talk is cheap. Show me the code."的原则,后文直接放出代码。代码实现了一个简单的神经元Perceptron(不带threshold),一个简单的神经网络层,以及两个测试用例。

希望研究神经网络算法的朋友请参考其他材料,本文不含任何公示推导。

代码

import numpy as np
import matplotlib.pyplot as plt


# Activation Function
def sigmoid(x):
    return 1 / (1 + np.exp(-x))


# Neural Unit
class Perceptron(object):
    def __init__(self, input_size: int, initializer: list = None, activation_func: str = None):
        # W[0] is the bias
        self.input_size = input_size
        # W is parameters of Perceptron
        self.n_W = self.input_size + 1
        self.W = np.random.uniform(low=0.0, high=1.0, size=self.n_W)
        # X is the input vector of Perceptron
        self.X = None

        self.output = 0.0
        self.delta_W = np.array([0] * self.n_W, dtype=np.float)
        self.delta_X = np.array([0] * input_size, dtype=np.float)
        self.activation_func = activation_func
        if initializer:
            assert len(initializer) == self.n_W
            self.W = initializer

    def forward(self, X):
        assert len(X) == self.input_size
        self.X = np.array(X, dtype=float)
        y = np.sum(self.W[1:] * self.X) + self.W[0]
        if self.activation_func == 'sigmoid':
            self.output = sigmoid(y)
        else:
            self.output = y
        return self.output

    def update(self, lr):
        self.W = self.W + lr * self.delta_W
        self.delta_W = np.array([0] * self.n_W, dtype=np.float)

    def __call__(self, X):
        return self.forward(X)


# Neural Layer
class Layer(object):
    def __init__(self, input_size: int, output_size: int, activation_func='sigmoid', lr=0.1):
        self.input_size = input_size
        self.output_size = output_size
        self.net = np.array(
            [Perceptron(input_size=input_size, activation_func=activation_func) for _ in range(output_size)])
        self.activation_func = activation_func
        self.inputs = np.array([0] * input_size, dtype=np.float)
        self.lr = lr
        self.outputs = np.array([0] * output_size, dtype=np.float)

    def forward(self, X):
        self.inputs = np.array(X, dtype=np.float)
        self.outputs = np.array([p(X) for p in self.net])
        return self.outputs

    def __call__(self, X):
        return self.forward(X)

    def backward(self, delta_outputs):
        assert len(delta_outputs) == len(self.net)
        for idx in range(self.output_size):
            delta_output = delta_outputs[idx]
            p = self.net[idx]
            o = self.outputs[idx]
            if self.activation_func == 'sigmoid':
                # W0 is the bias
                p.delta_W = delta_output * o * (1 - o) * np.array([1] + list(p.X))  # expand X for W_0
                p.delta_X = delta_output * o * (1 - o) * p.W[1:]
            else:
                # linear
                p.delta_W = delta_output * np.array([1] + list(p.X))
                # W0 is the bias
                p.delta_X = delta_output * p.W[1:]

    def update(self):
        for p in self.net:
            p.update(self.lr)


if __name__ == '__main__':
    # standard version ============================
    samples = [[[-2, -1], 1],
               [[25, 6], 0],
               [[17, 4], 0],
               [[-15, -6], 1]]

    # training
    layer1 = Layer(2, 10)
    layer2 = Layer(10, 1, activation_func='')
    for i in range(1000):
        # iteration
        print(f'iteration {i}')
        text_X = samples[3][0]
        text_y_d = samples[3][1]
        test_y = layer2(layer1((samples[3][0])))[0]
        print(f'X:{text_X}, y_d:{text_y_d}, y:{test_y}')
        for X, y_d in samples:
            # forward
            y = layer2(layer1(X))[0]
            # backward layer2 -> layer1
            err = y_d - y   # delta_outputs of layer 2
            layer2.backward([err])

            delta_outputs = np.array([0.0] * layer2.input_size, dtype=np.float)  # delta_outputs of layer 1
            for p in layer2.net:
                delta_outputs += p.delta_X
            layer1.backward(delta_outputs)

            # update gradient
            layer2.update()
            layer1.update()

    # result
    for X, y_d in samples:
        y = layer2(layer1(X))[0]
        print(f'The final result: X:{X}, y_d:{y_d}, y:{y}')

    # # simple version ============================
    # samples = [[[0, 1], 1],
    #            [[1, 0], -1]]
    # # training
    # layer1 = Layer(2, 1, activation_func='')
    # for i in range(100):
    #     # iteration
    #     print(f'iteration {i}')
    #     for X, y_d in samples:
    #         # forward
    #         y = layer1(X)[0]
    #         # backward
    #         err = y_d - y
    #         layer1.backward([err])
    #         print(f'W:{layer1.net[0].W}')
    #         print(f'delta_W:{layer1.net[0].delta_W}')
    #         layer1.update()
    #         print(f'X:{X}, y_d:{y_d}, y:{y}')

测试情况

用例1

X: [0, 1]  -> y: 1

X: [1, 0]  -> y: -1

非常简单,使用1层仅1个神经元的神经网络即可

结果:

迭代100次后:

W:[-0.00408549 -0.99589394  1.00406116]
delta_W:[2.43257503e-05 0.00000000e+00 2.43257503e-05]
X:[0, 1], y_d:1, y:0.9999756742496532
W:[-0.00408305 -0.99589394  1.00406359]
delta_W:[-2.30078698e-05 -2.30078698e-05 -0.00000000e+00]
X:[1, 0], y_d:-1, y:-0.9999769921301684

W是神经网络单元的参数,delta_W是待更新的梯度。

y_d 是期望值, y是预测值。

用例2

 测试用例参考了一下下文:

Python实现简单的神经网络!

 # Define dataset
 data = np.array([
 [-2, -1], # Alice
 [25, 6], # Bob
 [17, 4], # Charlie
 [-15, -6] # diana
 ])
 all_y_trues = np.array([
 1, # Alice
 0, # Bob
 0, # Charlie
 1 # diana
 ])

这里用了两层神经网络。

结果:

y_d 是期望值, y是预测值。

The final result: X:[-2, -1], y_d:1, y:0.9999900485975061
The final result: X:[25, 6], y_d:0, y:0.0012005012989202424
The final result: X:[17, 4], y_d:0, y:0.0001167411498785853
The final result: X:[-15, -6], y_d:1, y:1.0000069102572744

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值