8月8日Pytorch笔记——Numpy 实现 Backpropagation

该博客介绍了如何使用Numpy从头实现一个多层感知机(MLP),包括前向传播和反向传播算法。在前向传播中,作者定义了sigmoid激活函数,并通过权重和偏置计算输出。在反向传播部分,计算了损失函数,然后逐层反向传播误差,更新权重和偏置。最后,训练函数展示了如何在MNIST数据集上进行训练,并提供了评估功能。
摘要由CSDN通过智能技术生成


前言

本文为8月8日Pytorch笔记, 用 Numpy 实现 Backpropagation。


一、Numpy 实现 Backpropagation

1
代码如下:

import numpy as np
import random

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

def sigmoid_prime(z):
    return sigmoid(z) * (1 - sigmoid(z))

class MLP_np:
    def __init__(self, sizes):
        '''

        :param sizes: [784, 30, 10]
        '''
        self.sizes = sizes
        self.num_layers = len(sizes) - 1

        # sizes: [784, 30, 10]
        # w: [ch_out, ch_in]
        # b: [ch_out]
        self.weights = [np.random.randn(ch2, ch1) for ch1, ch2 in zip(sizes[:-1], sizes[1:])] # [784, 30] [30, 10]
        # z = wx + b [30, 1]
        self.biases = [np.random.randn(ch, 1) for ch in sizes[1:]]

    def forward(self, x):
        '''

        :param x: [784, 1]
        :return: [10, 1]
        '''
        for b, w in zip(self.biases, self.weights):
            # [30, 784] @ [784, 1] ==> [30, 1] + [30, 1] ==> [30, 1]
            z = np.dot(w, x) + b
            # [30, 1]
            x = sigmoid(z)

        return x

    def backward(self, x, y):
        '''

        :param x: [784, 1]
        :param y: [10, 1] ont_hot encoding
        :return:
        '''
        # 1. forward
        # 生成列表保存梯度信息
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        nabla_b = [np.zeros(b.shape) for b in self.biases]

        # 保存每层的激活函数
        activations = [x]
        # 保存每层的 z
        zs = []
        activation = x

        for b, w in zip(self.biases, self.weights):
            z = np.dot(w, activation) + b
            activation = sigmoid(z)

            zs.append(z)
            activations.append(activation)

        loss = np.power(activations[-1] - y, 2).sum()
        # 2. backward
        # 2.1 计算输出层的梯度
        # [10, 1] * [10, 1] ==> [10, 1]
        delta = activations[-1] * (1 - activations[-1]) * (activations[-1] - y)
        nabla_b[-1] = delta
        # [10, 1] @ [1, 30] ==> [10, 30]
        # acativation: [30, 1]
        nabla_w[-1] = np.dot(delta,  activations[-2].T)

        # 2.2 计算隐藏层的梯度
        for l in range(2, self.num_layers+1):
            l = -l
            z = zs[l]
            a = activations[l]

            # dlta_j
            # [10, 30].T @ [10, 1] ==> [30, 1] * [30, 1]
            delta = np.dot(self.weights[l+1].T, delta) * a * (1-a)

            nabla_b[l] = delta
            # [30, 1] @ [784, 1].T ==> [30, 784]
            nabla_w[l] = np.dot(delta, activations[l-1].T)

        return nabla_w, nabla_b, loss

    def train(self, training_data, epochs, batchsz, lr, test_data):
        '''

        :param training_data: list of (x, y)
        :param epochs: 1000
        :param batchsz: 10
        :param lr: 0.01
        :param test_data: list of (x, y)
        :return:
        '''
        if test_data:
            n_test = len(test_data)

        n = len(training_data)
        for j in range(epochs):
            random.shuffle(training_data)
            mini_batches = [
                training_data[k: k+batchsz]
                for k in range(0, n, batchsz)
            ]
            # for every batch in current batch
            for mini_batch in mini_batches:
                loss = self.update_mini_batch(mini_batch, lr)


            if test_data:
                print("Epoch {0}: {1} / {2}".format(
                    j, self.evaluate(test_data), n_test
                ), loss)
            else:
                print("Epoch {0} complete".format(j))

    def update_mini_batch(self, batch, lr):
        '''

        :param batch: list of (x, y)
        :param lr: 0.01
        :return:
        '''
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        loss = 0

        # for every sample in current batch
        for x, y in batch:
            # list of every w/b gradient
            # [w1, w2, w3]
            nabla_w_, nabla_b_, loss_ = self.backward(x, y)
            nabla_w =[accu+cur for accu, cur in zip(nabla_w, nabla_w_)]
            nabla_b = [accu + cur for accu, cur in zip(nabla_b, nabla_b_)]
            loss += loss_

        nabla_w = [w / len(batch) for w in nabla_w]
        nabla_b = [b / len(batch) for b in nabla_b]
        loss = loss / len(batch)

        # w = w - lr * nabla_w
        self.weights = [w - lr * nabla for w, nabla in zip(self.weights, nabla_w)]
        self.biases = [b - lr * nabla for b, nabla in zip(self.biases, nabla_b)]

        return loss


    def evaluate(self, test_data):
        '''

        :param test_data: list of (x, y)
        :return:
        '''
        result = [(np.argmax(self.forward(x)), y) for x, y in test_data]
        correct = sum(int(pred==y) for pred, y in result)

        return correct


def main():
    import mnist_loader
    # loading MNIST data
    training_data, validation_data, test_data = mnist_loader.load_data_wrapper()
    print(len(training_data), training_data[0][0].shape, training_data[0][1].shape)
    print(len(test_data), test_data[0][0].shape, test_data[0][1].shape)

    # Set up a network with 30 hidden neurons
    net = MLP_np([784, 30, 10])
    # Use SGD to learn from the MNIST training data
    # 1000 epochs, with a mini-batch size of 10, lr=0.1
    net.train(training_data, 1000, 10, 0.1, test_data=test_data)


if __name__ == '__main__':
    main()

>>> Epoch 999: 9436 / 10000 0.0002412281437943844

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyTorchNumPy 都是在 Python 中广泛使用的科学计算库。它们各自有着特定的功能集,主要用于处理数据、构建模型以及执行数学运算等任务。 ### PyTorch 版本: PyTorch 是由 Facebook AI 研究院 (FAIR) 开发的一个开源机器学习框架。它提供了一个强大的张量 (tensor) 计算引擎,并内置了自动求导功能,非常适合进行深度学习的研究和应用。PyTorch 的灵活性和动态性的设计使其成为研究人员和开发者的重要工具之一。PyTorch 的最新稳定版通常会通过 PyPI (Python 包索引)提供安装包。例如,`torch==1.9.0+cu111` 是在 CUDA 11.1 上支持的 PyTorch 版本的一个示例。用户可以通过 pip 或者 conda 安装相应的 PyTorch 版本。 ### NumPy 版本: NumPyPython 数据分析的基础库,专注于高性能数值数组操作和通用函数的支持。它是几乎所有数据分析和科学计算库的基础,包括 Pandas 和 SciPy。对于 NumPy,版本选择更多关注于性能优化、新功能引入和 bug 修复。用户可以从 Anaconda 或者直接从其 GitHub 页面下载最新的源码并手动编译安装,也可以通过 pip 直接安装预编译的二进制文件,如 `numpy==1.20.3`。 ### 对应关系: 虽然 PyTorchNumPy 都是 Python 生态系统的一部分,但在实际应用中,它们很少直接交互,因为它们服务于不同的目的。PyTorch 主要是用于深度学习模型训练和推理,而 NumPy 则侧重于通用数值计算和数据结构处理。但是,在使用深度学习框架时,可能会使用到 NumPy 创建初始数据或者在特定场景下进行辅助计算,尤其是当需要与 TensorFlow 或其他非深度学习库集成时。此外,有时为了提高性能,深度学习模型的某些部分可能需要使用更底层的数据处理功能,这时可能会将 NumPy 数组传递给 PyTorch 张量或其他库。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值