全连接神经网络(前馈神经网络)

一、全连接神经网络介绍

在多层神经网络中, 第 N 层的每个神经元都分别与第 N-1 层的神经元相互连接。

1、神经元

这个神经元接收的输入信号为向量(x_1,x_2,x_3,x_4,x_5) , 向量(w_1,w_2,w_3,w_4,w_5)为输入向量的组合权重, b 为偏置项, 是一个标量。

神经元的作用是对输入向量进行加权求和, 并加上偏置项 b, 最后经过激活函数f 变换产生输出y = f\left(\sum_{i=1}^{5} w_i x_i + b\right)   ===>  y = f(w^Tx+b)

2、网络结构

在分类问题中, 神经网络一般有多层:

  • 第一层为输入层, 对应输入向量, 神经元的数量等于特征向量的维数, 输入层不对数据进行处理, 只是将输入向量送入下一层中进行计算。
  • 中间层为隐含层, 可能有多个。
  • 最后是输出层, 神经元的数量等于要分类的类别数, 输出层的输出值被用来做分类预测

  • 第一层是输入层,对应的输入向量为(x_1,x_2,x_3) , 有 3 个神经元, 输入层不对数据做任何处理, 直接原样送入下一层。
  • 中间层有 4 个神经元, 接收的数据为 (x_1,x_2,x_3), 输出向量为 (y_1,y_2,y_3,y_4)
  • 第三层为输出层, 接收的数据为向量(y_1,y_2,y_3,y_4) , 输出向量为(z_1,z_2)

神经网络通过激活函数而具有非线性, 通过调整权重形成不同的映射函数。现实应用中要拟合的函数一般是非线性的, 线性函数无论怎样复合最终还是线性函数, 因此, 必须使用非线性激活函数。

3、正向传播算法

算法公式:u^{(l)} = W^{(l)} z^{(l-1)} + b^{(l)}z^{(l)} = f(u^{(l)})

假设神经网络有 m 层, 正向传播算法的流程为:

 二、反向传播算法

1、理解

假设神经网络有 n_l 层, 第 l 层的神经元个数为 s_l。 第 l 层从第 l - 1 层接收的输入向量为 z^{(l-1)}, 第 l 层的权重矩阵为 w^{(l)}, 偏置向量为b^{(l)}, 输出向量为 z^{(l)}。 该层的输出可以写成如下形式:

(已有推导结论: \nabla_{W} L = \left(\nabla_{u^{(l)}} L\right) {z^{(l-1)}}^T\nabla_{​{z^{(l-1)}}} L = W^T\left(\nabla_{u^{(l)}} L\right) )

损失函数对权重和偏置的梯度:

发现\nabla_{W} L\nabla_{b^{(l)}} L中都存在\nabla_{u^{(l)}} L,因此定义误差项为:

即得到\delta^{(l)}递推公式为:

通过递推公式知\delta^{(l)}\delta^{(l+1)}计算得到,因此需要从后往前计算,当\delta^{(l)}计算出来时\nabla_{W} L\nabla_{b^{(l)}} L也可计算出来,这就称为反向传播

2、迭代流程

均按照一个样本迭代,若有m个样本,公式要除以m,以求平均梯度

  1. 正向传播, 利用当前权重和偏置值, 计算每一层的输出值--确定z^{(l-1)}
  2. 计算输出层的误差项 \delta^{(n_l)}
  3. 反向传播, 计算隐藏层各层的误差项:

    ⊙ 表示元素乘法,前边表示矩阵乘法
  4. 计算损失函数对权重和偏置的梯度:
  5. 用梯度下降法更新权重和梯度:


    如果采用所有样本进行迭代, 根据单个样本损失函数的梯度计算总梯度, 然后求均值即可。

3、常见激活函数

根据上边公式:\delta^{(l)} = (W^{(l+1)})^T \cdot (\delta^{(l+1)}) \odot f'(u^{(l)})

f'(u^{(l)})是激活函数的导数,因此若激活函数倒数好计算,误差项计算就会容易。

下面是几种常用的激活函数及它们的导数:

三、构建神经网络模型的基本步骤

四、线性回归神经网络结构

线性回归模型可以认为是神经网络模型的一种极简特例, 是一个神经元,其只有加权和、没有非线性变换。

4.1 数据处理

1、数据导入

从本地文件或 URL 中读取数据。

data_list = pd.read_csv(filepath)

2、数据归一化处理

使用Z-Score 归一化

data_list = (data_list - data_list.mean()) / data_list.std()

3、数据集划分

将数据集划分成训练集和测试集, 其中训练集用于确定模型的参数, 测试集用于评判模型的效果。

train_size = int(len(data_list) * ratio)
# 生成一个随机排列的整数数组
random_indices = np.random.permutation(len(data_list))
# 使用随机排列的索引列表重新设定 DataFrame 的行顺序
data_list = data_list.iloc[random_indices]
trainset = data_list[:train_size]

4、数据形状变换

如果读取的数据维数与我们的模型输入的输入维数不一致, 我们就需要进行数据形状的变换

X_train = X_train.values
y_train = trainset["MEDV"]
y_train = y_train.values.reshape(-1, 1) #1维变2维,-1表示自动计算行数,1表示列数
X_test = testset.drop("MEDV", axis=1)
X_test = X_test.values
y_test = testset["MEDV"]
y_test = y_test.values.reshape(-1, 1)

4.2 模型设计

如果将输入特征和输出预测值均以向量表示, 输入特征x有 13 个分量,y有 1 个分量, 那么参数权重的形状是 13× 1:W = \begin{bmatrix} w_1 & w_2 & w_3 & \cdots & w_{13} \end{bmatrix}

class Network(object):
    def __init__(self, num_of_weights):
        # 随机产生 w 的初始值
        # 为了保持程序每次运行结果的一致性, 此处设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights, 1) #数学中向量数据是按列存储的
        self.b = 0

    def forward(self, x):
        z = np.dot(x, self.w) + self.b
        return z

4.3 训练配置

通过损失函数平衡模型的好坏

    def loss(self, z, y):
            error = z - y
            cost = error * error
            cost = np.mean(cost)
            return cost

    4.4 训练过程

    使用梯度下降法寻找最优W和b

    def gradient(self, x, y):
        z = self.forward(x)
        gradient_w = (z - y) * x #(z - y)是m*1  x是m*n
        gradient_w = np.mean(gradient_w, axis=0) #批量梯度下降,对各个样本取得权重求均值
        gradient_w = gradient_w[:, np.newaxis] #变成列向量
        gradient_b = (z - y)
        gradient_b = np.mean(gradient_b)
        return gradient_w, gradient_b
    
    def update(self, gradient_w, gradient_b, alpha=0.01):
        self.w = self.w - alpha * gradient_w
        self.b = self.b - alpha * gradient_b

    训练模型

    def train(self, x, y, iterations=100, alpha=0.01):
        losses = []
        for i in range(iterations):
            z = self.forward(x)
            L = self.loss(z, y)
            gradient_w, gradient_b = self.gradient(x, y)
            self.update(gradient_w, gradient_b, alpha)
            losses.append(L)
            if (i + 1) % 10 == 0:
                print('iter {}, loss {}'.format(i, L))
        return losses

    调用函数进行模型训练

    # 创建网络
    net = Network(13)
    num_iterations = 1000
    # 启动训练
    losses = net.train(X_train, y_train, iterations=num_iterations,
    alpha=0.01)
    # 画出损失函数的变化趋势
    plot_x = np.arange(num_iterations)
    plot_y = np.array(losses)
    plt.plot(plot_x, plot_y)
    plt.show()

    4.5 模型保存

    将训练好的模型保存用于后续的预测过程。 当需要进行预测时使用训练好的模型进行预测。

    # 模型保存
    params = {
        "w": net.w.tolist(),
        "b": net.b
    } 
    
    #将参数保存到文件
    with open('params.json', 'w') as f:
        json.dump(params, f, indent=4)
    
    # 使用模型进行预测
    net = Network(13)
    with open('params.json', 'r') as f:
        params = json.load(f)
    
    net.w = np.array(params["w"])
    net.b = params["b"]
    
    z = net.forward(X_test)

    绘制图像

    # 绘制预测值与真实值的关系图
    x = np.arange(len(y_test))
    fig, ax = plt.subplots()
    ax.plot(x, z, label='predic', color='red')
    ax.plot(x, y_test, label='label', color='blue')
    ax.legend()
    plt.show()

     完整代码

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import json
    import random
    # pip install pandas matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
    
    
    # 1.数据处理
    # 载入数据集
    def loadData(filepath):
        """
        :param filepath: csv
        :return: X, y
        """
        data_list = pd.read_csv(filepath)
        # 使用Z-score对数据进行归一化处理
        data_list = (data_list - data_list.mean()) / data_list.std()
        return data_list
    
    
    data_list = loadData('housing.csv')
    
    
    # 划分训练集与测试集
    def splitData(data_list, ratio):
        train_size = int(len(data_list) * ratio)
        # 生成一个随机排列的整数数组
        random_indices = np.random.permutation(len(data_list))
        # 使用随机排列的索引列表重新设定 DataFrame 的行顺序
        data_list = data_list.iloc[random_indices]
        trainset = data_list[:train_size]
        testset = data_list[train_size:]
        X_train = trainset.drop("MEDV", axis=1)
        X_train = X_train.values
        y_train = trainset["MEDV"]
        y_train = y_train.values.reshape(-1, 1)
        X_test = testset.drop("MEDV", axis=1)
        X_test = X_test.values
        y_test = testset["MEDV"]
        y_test = y_test.values.reshape(-1, 1)
        return X_train, X_test, y_train, y_test
    
    
    X_train, X_test, y_train, y_test = splitData(data_list, 0.8)
    
    
    class Network(object):
        def __init__(self, num_of_weights):
            # 随机产生w的初始值
            # 为了保持程序每次运行结果的一致性,此处设置固定的随机数种子
            np.random.seed(0)
            self.w = np.random.randn(num_of_weights, 1)
            self.b = 0.
    
        def forward(self, x):
            z = np.dot(x, self.w) + self.b
            return z
    
        def loss(self, z, y):
            error = z - y
            num_samples = error.shape[0]
            cost = error * error
            cost = np.sum(cost) / num_samples
            return cost
    
        def gradient(self, x, y):
            z = self.forward(x)
            gradient_w = (z - y) * x
            gradient_w = np.mean(gradient_w, axis=0)
            gradient_w = gradient_w[:, np.newaxis]
            gradient_b = (z - y)
            gradient_b = np.mean(gradient_b)
            return gradient_w, gradient_b
    
        def update(self, gradient_w, gradient_b, alpha=0.01):
            self.w = self.w - alpha * gradient_w
            self.b = self.b - alpha * gradient_b
    
        def train(self, x, y, iterations=100, alpha=0.01):
            losses = []
            for i in range(iterations):
                z = self.forward(x)
                L = self.loss(z, y)
                gradient_w, gradient_b = self.gradient(x, y)
                self.update(gradient_w, gradient_b, alpha)
                losses.append(L)
                if (i + 1) % 10 == 0:
                    print('iter {}, loss {}'.format(i, L))
            return losses
    
        def SGD_train(self, x, y, num_epochs, batch_size=64, alpha=0.01):
            indices = list(range(len(x)))
            random.shuffle(indices)  # 打乱数据索引,以便随机选择样本
            losses = []
            for epoch_id in range(num_epochs):
                # 在每轮迭代开始之前,将训练数据的顺序随机打乱
                random.shuffle(indices)  # 打乱数据索引,以便随机选择样本
                # 将训练数据进行拆分,每个mini_batch包含batch_size条的数据
                loss = 0
                for start in range(0, len(x), batch_size):
                    end = min(start + batch_size, len(x))
                    batch_indices = indices[start:end]
                    X_batch = x[batch_indices]
                    y_batch = y[batch_indices]
                    a = self.forward(X_batch)
                    L = self.loss(a, y_batch)
                    loss += L
                    gradient_w, gradient_b = self.gradient(X_batch, y_batch)
                    self.update(gradient_w, gradient_b, alpha)
                    print('Epoch {} / iter {}, loss = {}'.
                          format(epoch_id, start+1, L))
                losses.append(loss)
    
            return losses
    
    
    # 创建网络
    net = Network(13)
    num_iterations = 1000
    # 启动训练
    losses = net.train(X_train, y_train, iterations=num_iterations, alpha=0.01)
    # losses = net.SGD_train(X_train, y_train, num_epochs=num_iterations, batch_size=64, alpha=0.01)
    
    # 画出损失函数的变化趋势
    plot_x = np.arange(num_iterations)
    plot_y = np.array(losses)
    plt.plot(plot_x, plot_y)
    plt.show()
    
    
    # 模型保存
    params = {
        "w": net.w.tolist(),
        "b": net.b
    }
    
    # 将参数保存到文件
    with open('params.json', 'w') as f:
        json.dump(params, f, indent=4)
    
    
    # 使用模型进行预测
    net = Network(13)
    with open('params.json', 'r') as f:
        params = json.load(f)
    
    net.w = np.array(params["w"])
    net.b = params["b"]
    
    z = net.forward(X_test)
    
    # 绘制预测值与真实值的关系图
    x = np.arange(len(y_test))
    fig, ax = plt.subplots()
    ax.plot(x, z, label='predic', color='red')
    ax.plot(x, y_test, label='label', color='blue')
    ax.legend()
    plt.show()

     五、全连接神经网络实现手写数字识别代码

    1、手写数字识别原理

    计算机存储数据的最小单位是 bit, 它只有 0 和 1 两种二进制状态, 所以任何数据在计算机中都是以 0 和 1 组成的一堆数字。 那如何让计算机认出图片中的数字是几呢? 灰度图像是一种只包含灰度级别而不包含彩色信息的图像, 灰度级别通常被分为 256 个等级, 即从全黑(灰度值 0) 到全白(灰度值 255), 使用 8位二进制数可以表示从 0 到 255 的 256 个不同的数值, 因此用一个 8 位二进制数表示一个像素。 如下图所示把 0 以外的点连接起来就能看出图片中的是数字 4。

    2、MINST 数据集

    MNIST 数据集是美国国家标准与技术研究院收集整理的大型手写数字数据库,包含 60,000 个示例的训练集以及 10,000 个示例的测试集。 其中的图像的尺寸为 28*28。 采样数据显示如下:

    MNIST 图像文件有一个特定的文件头, 其中包含一些元数据, 这个文件头由 16 字节组成, 它们分别表示:

    • magic_number: 一个魔数, 用于标识文件格式。 对于图像文件, 这个值通常是 0x00000803;
    • num_images: 文件中的图像数量;
    • num_rows: 每张图像的行数(即高度);
    • num_cols: 每张图像的列数(即宽度)。

    3、代码实现

    3.1 方法概括

    使用一个 3 层的全连接神经网络实现手写数字识别, 输入层神经原个数为 784(也就是图片的像素数 28× 28), 隐藏层的神经元个数为 128, 输出层的神经元个数为 10(数字从 0~9 的个数)。 隐藏层的激活函数使用 ReLU, 输出层的激活函数为 softmax。 分别使用批量梯度下降法和随机梯度下降法进行参数优化。

    3.2 数据处理

    # 读取MNIST数据集
    train_images = idx2numpy.convert_from_file('./MNIST/raw/train-images-idx3-ubyte')
    train_labels = idx2numpy.convert_from_file('./MNIST/raw/train-labels-idx1-ubyte')
    test_images = idx2numpy.convert_from_file('./MNIST/raw/t10k-images-idx3-ubyte')
    test_labels = idx2numpy.convert_from_file('./MNIST/raw/t10k-labels-idx1-ubyte')
    
    # 将图像数据转换为一维向量,并归一化
    X_train = train_images.reshape(train_images.shape[0], -1) / 255.0
    X_test = test_images.reshape(test_images.shape[0], -1) / 255.0
    
    # 将标签进行one-hot编码
    num_classes = 10
    y_train = np.eye(num_classes)[train_labels]
    y_test = np.eye(num_classes)[test_labels]

    one-hot讲解

     3.3 模型设计

    反向传播初始误差项跟所选损失函数有关

    • 使用均方误差(MSE)损失函数对应初始误差项 :

      a^{(L)} 是输出层的激活值,y 是真实标签
    • 使用交叉熵损失函数对应初始误差项 :

      y 是真实标签的one-hot编码
    class NeuralNetwork:
        def __init__(self, input_size, hidden_size, output_size):
            self.input_size = input_size
            self.hidden_size = hidden_size
            self.output_size = output_size
    
            # 初始化权重
            self.W1 = np.random.randn(self.input_size, self.hidden_size)
            self.b1 = np.zeros((1, self.hidden_size))
            self.W2 = np.random.randn(self.hidden_size, self.output_size)
            self.b2 = np.zeros((1, self.output_size))
    
        def forward(self, X):
            # 前向传播
            self.z1 = np.dot(X, self.W1) + self.b1
            self.a1 = np.maximum(0, self.z1)  # ReLU激活函数
            self.z2 = np.dot(self.a1, self.W2) + self.b2
            exp_scores = np.exp(self.z2)    # softmax
            self.probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
            return self.probs
    
        def backward(self, X, y, learning_rate=0.01):
            # 反向传播
            m = X.shape[0]
            delta3 = self.probs - y
            dW2 = np.dot(self.a1.T, delta3) / m
            db2 = np.sum(delta3, axis=0, keepdims=True) / m
            delta2 = np.dot(delta3, self.W2.T) * (self.a1 > 0) #(self.a1 > 0)表示Relu激活函数求导结果,要么是0要么是1
            dW1 = np.dot(X.T, delta2) / m
            db1 = np.sum(delta2, axis=0, keepdims=True) / m
    
            # 更新权重
            self.W1 -= learning_rate * dW1
            self.b1 -= learning_rate * db1
            self.W2 -= learning_rate * dW2
            self.b2 -= learning_rate * db2

    3.4 定义损失函数

    • 在回归问题中常用均方误差作为损失函数;
    • 在分类问题中常用采用交叉熵(Cross-Entropy) 作为损失函数。
        def calculate_loss(self, X, y):
            # 计算softmax交叉熵损失
            log_probs = -np.log(self.probs[range(X.shape[0]), np.argmax(y, axis=1)])
            return np.mean(log_probs)
    常用损失函数

    3.5 训练过程

    # 梯度下降法训练
        def train(self, X, y, num_epochs=1000, learning_rate=0.01):
            for epoch in range(num_epochs):
                # 前向传播和反向传播
                probs = self.forward(X)
                self.backward(X, y, learning_rate)
                loss = self.calculate_loss(X, y)
                print(f"Epoch {epoch}, Loss: {loss}")
    
        # 随机梯度下降法训练
        def train_SGD(self, X, y, num_epochs=1000, learning_rate=0.01, batch_size=64):  # 增加了batch_size参数
            indices = list(range(len(X)))
            random.shuffle(indices)  # 打乱数据索引,以便随机选择样本
            for epoch in range(num_epochs):
                # 使用随机顺序遍历数据集
                for start in range(0, len(X), batch_size):
                    end = min(start + batch_size, len(X))
                    batch_indices = indices[start:end]
                    X_batch = X[batch_indices]
                    y_batch = y[batch_indices]
                    # 前向传播和反向传播
                    probs = self.forward(X_batch)
                    self.backward(X_batch, y_batch, learning_rate)
                    if (start // batch_size) % 100 == 0:
                        loss = self.calculate_loss(X_batch, y_batch)
                        print(f"Epoch [{epoch + 1}/{num_epochs}], step {start + 1}/{len(X)}, Loss: {loss}")
    随机梯度下降算法
    • 梯度下降法计算一步梯度需要对所有样本进行计算
    • 随机梯度下降法(SGD)每次迭代都不使用整个数据集, 而是只选择小批量随机训练样本来计算梯度并更新模型参数。核心概念如下:
      1)minibatch: 每次迭代时抽取出来的一批数据被称为一个 minibatch。
      2)batch_size: 每个 minibatch 所包含的样本数目称为 batch_size。
      3)Epoch: 当程序迭代的时候, 按 minibatch 逐渐抽取出样本, 当把整个数据集都遍历到了的时候, 则完成了一轮训练, 也叫一个 Epoch(轮次)。 启动训练时,可以将训练的轮数 num_epochs 和 batch_size 作为参数传入。

    随机梯度下降算法的过程如下: 



     

    3.6 模型保存

    # 模型保存
    params = {
        "W1": nn.W1.tolist(),
        "b1": nn.b1.tolist(),
        "W2": nn.W2.tolist(),
        "b2": nn.b2.tolist()
    }
    # 将参数保存到文件
    with open('fcn_params.json', 'w') as f:
        json.dump(params, f, indent=4)

    4、手动实现代码(完整版)

    import numpy as np
    import idx2numpy  # 用于读取MNIST数据集
    from sklearn.metrics import accuracy_score
    import random
    import json
    import matplotlib.pyplot as plt
    
    
    # 读取MNIST数据集
    train_images = idx2numpy.convert_from_file('./MNIST/raw/train-images-idx3-ubyte')
    train_labels = idx2numpy.convert_from_file('./MNIST/raw/train-labels-idx1-ubyte')
    test_images = idx2numpy.convert_from_file('./MNIST/raw/t10k-images-idx3-ubyte')
    test_labels = idx2numpy.convert_from_file('./MNIST/raw/t10k-labels-idx1-ubyte')
    
    # num_images_to_display = 5
    # # for i in range(num_images_to_display):
    # #     plt.imshow(train_images[i], cmap='gray')
    # #     plt.title(f"Label: {train_labels[i]}")
    # #     plt.axis('off')
    # #     plt.show()
    
    # 将图像数据转换为一维向量,并归一化
    #第二个维度 -1 表示将所有剩余的维度(即 height 和 width)展平成一维。因此,每个图像将被转换为一个长度为 height * width 的一维数组。
    X_train = train_images.reshape(train_images.shape[0], -1) / 255.0
    X_test = test_images.reshape(test_images.shape[0], -1) / 255.0
    
    # 将标签进行one-hot编码
    num_classes = 10
    y_train = np.eye(num_classes)[train_labels]
    y_test = np.eye(num_classes)[test_labels]
    
    class NeuralNetwork:
        def __init__(self, input_size, hidden_size, output_size):
            self.input_size = input_size
            self.hidden_size = hidden_size
            self.output_size = output_size
    
            # 初始化权重
            self.W1 = np.random.randn(self.input_size, self.hidden_size)
            self.b1 = np.zeros((1, self.hidden_size))
            self.W2 = np.random.randn(self.hidden_size, self.output_size)
            self.b2 = np.zeros((1, self.output_size))
    
        def forward(self, X):
            # 前向传播
            self.z1 = np.dot(X, self.W1) + self.b1
            self.a1 = np.maximum(0, self.z1)  # ReLU激活函数
            self.z2 = np.dot(self.a1, self.W2) + self.b2
            exp_scores = np.exp(self.z2)    # softmax
            self.probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
            return self.probs
    
        def backward(self, X, y, learning_rate=0.01):
            # 反向传播
            m = X.shape[0]
            delta3 = self.probs - y
            dW2 = np.dot(self.a1.T, delta3) / m
            db2 = np.sum(delta3, axis=0, keepdims=True) / m
            delta2 = np.dot(delta3, self.W2.T) * (self.a1 > 0) #(self.a1 > 0)表示Relu激活函数求导结果,要么是0要么是1
            dW1 = np.dot(X.T, delta2) / m
            db1 = np.sum(delta2, axis=0, keepdims=True) / m
    
            # 更新权重
            self.W1 -= learning_rate * dW1
            self.b1 -= learning_rate * db1
            self.W2 -= learning_rate * dW2
            self.b2 -= learning_rate * db2
    
        def calculate_loss(self, X, y):
            # 计算softmax交叉熵损失
            log_probs = -np.log(self.probs[range(X.shape[0]), np.argmax(y, axis=1)])
            return np.mean(log_probs)
    
        # 梯度下降法训练
        def train(self, X, y, num_epochs=1000, learning_rate=0.01):
            for epoch in range(num_epochs):
                # 前向传播和反向传播
                probs = self.forward(X)
                self.backward(X, y, learning_rate)
                loss = self.calculate_loss(X, y)
                print(f"Epoch {epoch}, Loss: {loss}")
    
        # 随机梯度下降法训练
        def train_SGD(self, X, y, num_epochs=1000, learning_rate=0.01, batch_size=64):  # 增加了batch_size参数
            indices = list(range(len(X)))
            random.shuffle(indices)  # 打乱数据索引,以便随机选择样本
            for epoch in range(num_epochs):
                # 使用随机顺序遍历数据集
                for start in range(0, len(X), batch_size):
                    end = min(start + batch_size, len(X))
                    batch_indices = indices[start:end]
                    X_batch = X[batch_indices]
                    y_batch = y[batch_indices]
                    # 前向传播和反向传播
                    probs = self.forward(X_batch)
                    self.backward(X_batch, y_batch, learning_rate)
                    if (start // batch_size) % 100 == 0:
                        loss = self.calculate_loss(X_batch, y_batch)
                        print(f"Epoch [{epoch + 1}/{num_epochs}], step {start + 1}/{len(X)}, Loss: {loss}")
    
    # 定义网络结构和参数
    input_size = 784  # 28*28像素
    hidden_size = 128
    output_size = 10
    batch_size = 64
    
    # 创建神经网络实例
    nn = NeuralNetwork(input_size, hidden_size, output_size)
    
    # 训练模型
    nn.train_SGD(X_train, y_train, num_epochs=1, learning_rate=0.01, batch_size=batch_size)
    # nn.train(X_train, y_train, num_epochs=1, learning_rate=0.01)
    
    # 模型保存
    params = {
        "W1": nn.W1.tolist(),
        "b1": nn.b1.tolist(),
        "W2": nn.W2.tolist(),
        "b2": nn.b2.tolist()
    }
    # 将参数保存到文件
    with open('fcn_params.json', 'w') as f:
        json.dump(params, f, indent=4)
    
    
    # 使用模型进行预测
    nn = NeuralNetwork(input_size, hidden_size, output_size)
    with open('fcn_params.json', 'r') as f:
        params = json.load(f)
    
    nn.W1 = np.array(params["W1"])
    nn.b1 = np.array(params["b1"])
    nn.W2 = np.array(params["W2"])
    nn.b2 = np.array(params["b2"])
    
    # 预测并评估模型
    test_predictions = np.argmax(nn.forward(X_test), axis=1)
    
    test_accuracy = accuracy_score(np.argmax(y_test, axis=1), test_predictions)
    
    print(f"Test Accuracy: {test_accuracy:.4f}")
    
    

    5、pytorch实现代码(完整版)

    # python --version     3.8.10
    # PyTorch --version   2.3.1
    # torchvision --version 0.18.1
    import torch
    import torch.nn as nn
    import torch.optim as optim
    from torchvision import datasets, transforms
    
    # 定义超参数
    input_size = 784  # 28x28
    hidden_size = 128
    num_classes = 10
    num_epochs = 1
    batch_size = 64
    learning_rate = 0.001
    
    
    # 加载和预处理数据
    train_dataset = datasets.MNIST(root='./', train=True, transform=transforms.ToTensor(), download=False)
    test_dataset = datasets.MNIST(root='./', train=False, transform=transforms.ToTensor(), download=False)
    train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
    test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)
    
    
    # 定义全连接神经网络
    class NeuralNet(nn.Module):
        def __init__(self, input_size, hidden_size, num_classes):
            super(NeuralNet, self).__init__()
            self.fc1 = nn.Linear(input_size, hidden_size)
            self.relu = nn.ReLU()
            self.fc2 = nn.Linear(hidden_size, num_classes)
    
        def forward(self, x):
            out = self.fc1(x)
            out = self.relu(out)
            out = self.fc2(out)
            return out
    
    
    # 训练模型
    def train(model, train_loader, optimizer, criterion, num_epochs):
        for epoch in range(num_epochs):
            for i, (images, labels) in enumerate(train_loader):
                # 前向传播
                outputs = model.forward(images.reshape(-1, 28 * 28))
                loss = criterion(outputs, labels)
                # 反向传播和优化
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
                if i % 100 == 0:
                    print(f'Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{len(train_loader)}], Loss: {loss.item()}')
    
    
    # 测试模型
    def predict(model, test_loader):
        model.eval()  # 设置为评估模式
        correct = 0
        total = 0
        with torch.no_grad():
            for images, labels in test_loader:
                outputs = model.forward(images.reshape(-1, 28 * 28))
                _, predicted = torch.max(outputs.data, 1)
                total += labels.size(0)
                correct += (predicted == labels).sum().item()
        print(f'Accuracy of the network on the 10000 test images: {100 * correct / total} %')
    
    
    model = NeuralNet(input_size, hidden_size, num_classes)
    # 定义损失函数和优化器
    criterion = nn.CrossEntropyLoss()
    # optim.Adam
    optimizer = optim.Adam(model.parameters(), lr=learning_rate)  # SGD
    # 训练模型
    train(model, train_loader, optimizer, criterion, num_epochs=num_epochs)
    
    # 保存模型
    torch.save(model.state_dict(), 'fcn_state_dict.pth')
    
    # 加载模型
    model.load_state_dict(torch.load('fcn_state_dict.pth'))
    
    # 测试模型
    predict(model, test_loader)

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/960541.html

    如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

    相关文章

    Linux:多线程[2] 线程控制

    了解: Linux底层提供创建轻量级进程/进程的接口clone,通过选择是否共享资源创建。 vfork和fork都调用的clone进行实现,vfork和父进程共享地址空间-轻量级进程。 库函数pthread_create调用的也是底层的clone。 POSIX线程库 与线程有关的函数构…

    DeepSeek崛起:中国AI新星如何撼动全球资本市场格局

    引言 近期,中国人工智能实验室DeepSeek发布的两款开源模型——DeepSeek V3和DeepSeek R1——以其优异的性能和低廉的成本迅速爆火,引发了全球资本市场的震动,尤其对美国资本市场产生了显著影响。DeepSeek R1更是能够在数学、代码和推理任务上…

    【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR、流水线及伪指令

    文章目录 指令格式(重点)1. 立即数2. 寄存器位移 一、数据传送指令1. MOV指令2. MVN指令3. LDR指令 二、数据计算指令1. ADD指令1. SUB指令1. MUL指令 三、位运算指令1. AND指令2. ORR指令3. EOR指令4. BIC指令 四、比较指令五、跳转指令1. B/BL指令2. l…

    图像处理算法研究的程序框架

    目录 1 程序框架简介 2 C#图像读取、显示、保存模块 3 C动态库图像算法模块 4 C#调用C动态库 5 演示Demo 5.1 开发环境 5.2 功能介绍 5.3 下载地址 参考 1 程序框架简介 一个图像处理算法研究的常用程序逻辑框架,如下图所示 在该框架中,将图像处…

    病理AI领域基础模型及多实例学习方法的性能评估|顶刊精析·25-01-27

    小罗碎碎念 这篇论文聚焦于组织学全切片图像分析,旨在探究多实例学习(MIL)与基础模型(FMs)结合的效果。 由于全切片图像(WSI)分析面临标注有限和模型直接处理困难等问题,MIL成为常用…

    Tensor 基本操作2 理解 tensor.max 操作,沿着给定的 dim 是什么意思 | PyTorch 深度学习实战

    前一篇文章,Tensor 基本操作1 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started 目录 Tensor 基本操作torch.max默认指定维度 Tensor 基本操作 torch.max torch.max 实现降维运算,基于指定的 d…

    以太网详解(六)OSI 七层模型

    文章目录 OSI : Open System Interconnect(Reference Model)第七层:应用层(Application)第六层:表示层(Presentation)第五层:会话层(Session)第四…

    Spring MVC异常处理机制

    文章目录 1. 异常处理的思路2. 异常处理两种方式3. 简单异常处理器SimpleMappingExceptionResolver 1. 异常处理的思路 系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发…

    本地大模型编程实战(03)语义检索(2)

    文章目录 准备按批次嵌入加载csv文件,分割文档并嵌入测试嵌入效果总结代码 上一篇文章: 本地大模型编程实战(02)语义检索(1) 详细介绍了如何使用 langchain 实现语义检索,为了演示方便,使用的是 langchain 提供的内存数据库。 在实…

    [Dialog屏幕开发] 设置方式对话框

    阅读该篇文章之前,可先阅读下述资料 [Dialog屏幕开发] 设置搜索帮助https://blog.csdn.net/Hudas/article/details/145381433?spm1001.2014.3001.5501https://blog.csdn.net/Hudas/article/details/145381433?spm1001.2014.3001.5501上篇文章我们的屏幕已实现了如…

    【JavaEE进阶】Spring留言板实现

    目录 🎍预期结果 🍀前端代码 🎄约定前后端交互接口 🚩需求分析 🚩接口定义 🌳实现服务器端代码 🚩lombok介绍 🚩代码实现 🌴运行测试 🎄前端代码实…

    1.23学习

    misc buuctf-小明的保险箱 打开附件是一个在线图片首先将其另存为,然后仅仅只是一个图片,而无其他信息,那么我们再进行binwalk或者foremost文件分离,得到了一个文件夹,其中含有一个压缩包但是是一个加密的&#xff0…

    【Python】第五弹---深入理解函数:从基础到进阶的全面解析

    ✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】【Python】 目录 1、函数 1.1、函数是什么 1.2、语法格式 1.3、函数参数 1.4、函数返回值 1.5、变量作用域 1.6、函数…

    【数据结构】(1)集合类的认识

    一、什么是数据结构 1、数据结构的定义 数据结构就是存储、组织数据的方式,即相互之间存在一种或多种关系的数据元素的集合。 2、学习数据结构的目的 在实际开发中,我们需要使用大量的数据。为了高效地管理这些数据,实现增删改查等操作&…

    大数据Hadoop入门2

    第三部分(Hadoop MapReduce和Hadoop YARN) 1.课程内容-大纲-学习目标 2.理解先分再合、分而治之的思想 3.hadoop团队针对MapReduce的设计构思 map这里不能翻译成地图,翻译为mapping比较好一点 4.Hadoop MapReduce介绍、阶级划分和进程组成 5…

    什么是BFF?他有什么用?

    BFF(Backend for Frontend) 是一种架构模式,专门为前端应用提供定制化的后端服务。它的核心思想是为不同的前端客户端(如 Web、移动端、桌面端等)提供专门的后端服务,而不是让所有客户端共享同一个通用的后…

    【深度之眼cs231n第七期】笔记(三十一)

    目录 强化学习什么是强化学习?马尔可夫决策过程(MDP)Q-learning策略梯度SOTA深度强化学习 还剩一点小尾巴,还是把它写完吧。(距离我写下前面那行字又过了好几个月了【咸鱼本鱼】)(汗颜&#xff…

    K8S极简教程(4小时快速学会)

    1. K8S 概览 1.1 K8S 是什么 K8S官网文档:https://kubernetes.io/zh/docs/home/ 1.2 K8S核心特性 服务发现与负载均衡:无需修改你的应用程序即可使用陌生的服务发现机制。存储编排:自动挂载所选存储系统,包括本地存储。Secret和…

    SPDK vhost介绍

    目录 1. vhost技术的背景与动机Virtio 介绍virtio-blk数据路径为例 2. vhost技术的核心原理2.1 vhost-kernel2.2 vhost-user举例 2.3 SPDK vhostvhost的优势IO请求处理数据传输控制链路调整 3. SPDK vhost的实现与配置3.1 环境准备3.2 启动SPDK vhost服务3.3 创建虚拟块设备3.4…

    【C++数论】880. 索引处的解码字符串|2010

    本文涉及知识点 数论:质数、最大公约数、菲蜀定理 LeetCode880. 索引处的解码字符串 给定一个编码字符串 s 。请你找出 解码字符串 并将其写入磁带。解码时,从编码字符串中 每次读取一个字符 ,并采取以下步骤: 如果所读的字符是…