手搓BP神经网络

神经网络是目前发展最好的人工智能算法,BP神经网络是所有神经网络的基础和基本框架。

废话不多说,直接coding下BP,就能理解神经网络的本质,真的非常简单

神经网络其实按我理解就是超级多的线性函数拟合成一个大的函数,用曲线,曲面对训练集进行拟合,一维就是一根线,二维就是面,三维就是拟合一个体,通常是非常高维度的,就是拟合训练集而已。再通俗点就是训练非常多的函数,权重强行记住训练集的点,让其带入到式子中永远正确。

那么训练好参数之后,新的数据代入进去自然而然就获得了结果,如果训练集和测试集相差不大,基本就是正确的。

针对训练过程,核心是神经网络的梯度下降和反向传播,你可以将这两个理解成一个东西,利用梯度下降算法进行求导,达到了反向传播的目的。

为什么要进行梯度下降呢,梯度下降是用来更新权重参数的,调整模型参数找到最为正确的拟合函数。梯度下降的本质是链式求导,找到函数拟合的正确方向。进行如下过程:

利用一个损失函数计算预测点和目标点之间的距离,这个距离可以输出出来看下函数拟合的效果,收敛说明拟合很好。最简单的损失函数是真实值直接减去预测值,注意,这就相当于一个向量,从真实值指向预测值,将这个损失函数式子对权重参数进行链式求导,就是先对激活函数求一次,激活函数再对节点的输入求一次,就将这个式子的对权重的梯度求出来了。类似于一种求切线,在这里梯度下降所求的导数更应该类似于高维空间中拟合函数到真实函数的方向,让拟合的函数逼近真实的函数,这里假设真实函数存在,是由训练集构成的。

不断地迭代,链式求导,权重参数在这个过程中就不断更新,所拟合的函数就无限逼近真实的情况,然后损失函数越来越小,收敛到0,不需要找到一个方向(梯度)调整了,以至于求解出了一个完全拟合训练集的超平面空间。

所以神经网络本质就是一个庞大的抽象函数拟合训练集的过程,怎么拟合,就看你的算法了。CNN,RNN,GNN等等,基本道理是一样的。

接下来提供一个自己coding的BP算法:

这个任务是实现身高体重分类性别的,准确率100%

import math
import random

# Sigmoid激活函数及其导数
def sigmoid(x):
    return 1 / (1 + math.exp(-x))

def sigmoid_derivative(x):
    return x * (1 - x)

# 数据标准化
def normalize_data(data):
    min_val = min([min(row) for row in data])
    max_val = max([max(row) for row in data])
    return [[(x - min_val) / (max_val - min_val) for x in row] for row in data]

# 神经网络类
class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        self.weights_input_hidden, self.weights_hidden_output = self.initialize_weights(input_size, hidden_size, output_size)
        self.hidden_size = hidden_size
        self.input_size = input_size
        self.output_size = output_size

    # 初始化权重(Xavier初始化法)
    def initialize_weights(self, input_size, hidden_size, output_size):
        weights_input_hidden = [[random.uniform(-1, 1) / math.sqrt(input_size) for _ in range(hidden_size)] for _ in range(input_size)]
        weights_hidden_output = [random.uniform(-1, 1) / math.sqrt(hidden_size) for _ in range(hidden_size)]
        return weights_input_hidden, weights_hidden_output

    # 前向传播
    def forward_propagate(self, inputs):
        hidden_layer_input = [sum(i * w for i, w in zip(inputs, weights_input_hidden_col)) for weights_input_hidden_col in zip(*self.weights_input_hidden)]
        hidden_layer_output = [sigmoid(x) for x in hidden_layer_input]

        output_layer_input = sum(h * w for h, w in zip(hidden_layer_output, self.weights_hidden_output))
        output = sigmoid(output_layer_input)

        return hidden_layer_output, output

    # 反向传播和权重更新
    def backpropagate(self, inputs, hidden_output, output, target, learning_rate):
        # 输出层误差
        output_error = output - target
        output_delta = output_error * sigmoid_derivative(output)

        # 隐藏层误差
        hidden_errors = [output_delta * w for w in self.weights_hidden_output]
        hidden_deltas = [error * sigmoid_derivative(h) for error, h in zip(hidden_errors, hidden_output)]

        # 更新权重(隐藏层到输出层)
        for i in range(len(self.weights_hidden_output)):
            self.weights_hidden_output[i] -= learning_rate * output_delta * hidden_output[i]

        # 更新权重(输入层到隐藏层)
        for i in range(len(self.weights_input_hidden)):
            for j in range(len(self.weights_input_hidden[i])):
                self.weights_input_hidden[i][j] -= learning_rate * hidden_deltas[j] * inputs[i]

    # 训练模型
    def train(self, training_data, labels, epochs, learning_rate):
        for epoch in range(epochs):
            total_loss = 0
            for inputs, target in zip(training_data, labels):
                hidden_output, output = self.forward_propagate(inputs)
                total_loss += cross_entropy_loss(target, output)
                self.backpropagate(inputs, hidden_output, output, target, learning_rate)
            if epoch % 100 == 0:  # 每100个周期输出一次损失
                print(f'Epoch {epoch}, Loss: {total_loss / len(training_data)}')

    # 预测
    def predict(self, inputs):
        _, output = self.forward_propagate(inputs)
        return 1 if output >= 0.5 else 0

# 计算交叉熵损失
def cross_entropy_loss(y_true, y_pred):
    return -(y_true * math.log(y_pred) + (1 - y_true) * math.log(1 - y_pred))

# 计算准确率并输出预测结果
def calculate_accuracy(test_data, test_labels, model):
    correct_predictions = 0
    for inputs, label in zip(test_data, test_labels):
        prediction = model.predict(inputs)
        print(f"Input: {inputs}, Predicted: {prediction}, Actual: {label}")
        if prediction == label:
            correct_predictions += 1
    accuracy = correct_predictions / len(test_data)
    print(f'Accuracy: {accuracy * 100:.2f}%')

# 主程序
def main():
    # 训练数据
    training_data = [
        [170, 65], [160, 50], [175, 75], [180, 80], [155, 45], [165, 55],
        [190, 90], [175, 70], [185, 85], [150, 48], [165, 58], [172, 66],
        [178, 77], [182, 78], [160, 53], [157, 50], [168, 60], [180, 85]
    ]  # 身高和体重
    labels = [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1]  # 性别(1表示男,0表示女)

    # 标准化训练数据
    training_data = normalize_data(training_data)

    # 初始化模型
    input_size = len(training_data[0])
    hidden_size = 6  # 隐藏层神经元数量
    output_size = 1  # 二分类
    learning_rate = 0.05  # 学习率
    epochs = 5000  # 训练轮次
    model = NeuralNetwork(input_size, hidden_size, output_size)

    # 训练模型
    model.train(training_data, labels, epochs, learning_rate)

    # 测试集数据
    test_data = [
        [165, 55], [185, 90], [170, 68], [155, 50], [180, 88], [160, 52]
    ]  # 测试数据
    test_labels = [0, 1, 1, 0, 1, 0]  # 真实标签

    # 标准化测试数据
    test_data = normalize_data(test_data)

    # 测试并输出预测结果和准确率
    calculate_accuracy(test_data, test_labels, model)

if __name__ == "__main__":
    main()

我只导入了两个包:math和radom,表明我的算法是基于数学原理实现的,由于时间久远,具体细节我也忘了,我只记得输入是2个节点,中间层,输出都是全连接。

这有利于我们了解神经网络的基础和本质!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值