深度学习【一】神经网络

人脑中有很多相互连接的神经元,当大脑处理信息时,这些神经元之间通过电信号和化学物质相互作用,在大脑的不同区域之间传递信息。神经网络使用人工神经元模仿这种生物现象,这些人工神经元由称为节点的软件模块构成,使用数值计算来进行通信和传递信息。


一 神经网络介绍

人工神经网络(Artificial Neural Network,ANN)简称 神经网络(NN),是一种模仿生物神经网络结构和功能的计算模型。大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自适应系统(adaptive system),通俗地讲就是具备学习功能。

人工神经网络中的神经元,一般可以对多个输入进行加权求和,再经过特定的"激活函数"转换后输出。

使用多个神经元就可以构建多层神经网络,最左边的一列神经元都表示输入,称为 输入层 ;最右边一列表示网络的输出,称为 输出层 ;输入层与输出层之间的层统称为 中间层(隐藏层)

相邻层的神经元相互连接(图中下一层每个神经元都与上一层所有神经元连接,称为 全连接 ),每个连接都会有一个 权重

神经元中的信息逐层传递(一般称为 前向传播 forward),上一层神经元的输出作为下一层神经元的输入。


二 激活函数

2.1 感知机

感知机(Perceptron)是二分类模型,接收多个信号,输出一个信号。感知机的信号只有0、1两种取值。

下图是一个接收两个输入信号的感知机的例子:

是输入信号,是输出信号,是权重,○ 称为神经元或节点。输入信号被送往神经元时,会分别乘以固定的权重。神经元会计算传送过来的信号的总和,只有当这个总和超过某个界限值时才会输出1,也称之为神经元被激活。

这里将界限的阈值设为0。除了权重之外,还可以增加一个参数,被称为 偏置

感知机的多个输入信号都有各自的权重,这些权重发挥着控制各个信号的重要性的作用,权重越大,对应信号的重要性越高。偏置则可以用来控制神经元被激活的容易程度。

2.2 激活函数

2.2.1 引入介绍

可以看出,式(2.1)中包含了两步处理操作:首先按照输入各自的权重及偏置,计算出一个加权总和;然后再根据这个总和与阈值的大小关系,决定输出0还是1。

如果定义一个函数:

那么式(2.1)就可以简化为:

为了更明显地表示出两个处理步骤,可以进一步写成:

这里,我们将输入信号和偏置的加权总和,记作a。可以将输入信号的加权总和转换为输出信号,起到"激活神经元"的作用,所以被称为 激活函数

激活函数是连接感知机和神经网络的桥梁,在神经网络中起着至关重要的作用。

如果没有激活函数,整个神经网络就等效于单层线性变换,不论如何加深层数,总是存在与之等效的"无隐藏层的神经网络"。激活函数必须是非线性函数,也正是激活函数的存在为神经网络引入了非线性,使得神经网络能够学习和表示复杂的非线性关系。

2.2.2 常用激活函数

(1)阶越函数

代码实现:

def step_function(x):

if x > 0:

return 1

else:

return 0

这里的x只能取一个数值(浮点数)。如果我们希望直接传入Numpy数组进行批量化的操作,可以改进如下:

def step_function(x):

return np.array(x > 0, dtype=int)

(2)Sigmoid函数

Sigmoid(也叫Logistic函数)是平滑的、可微的,能将任意输入映射到区间(0,1)。常用于二分类的输出层。但因其涉及指数运算,计算量相对较高。

Sigmoid的输入在[-6,6]之外时,其输出值变化很小,可能导致信息丢失。

Sigmoid的输出并非以0为中心,其输出值均>0,导致后续层的输入始终为正,可能影响后续梯度更新方向。

Sigmoid的导数范围为(0,0.25),梯度较小。当输入在[-6,6]之外时,导数接近0,此时网络参数的更新将会极其缓慢。使用Sigmoid作为激活函数,可能出现梯度消失(在逐层反向传播时,梯度会呈指数级衰减)。

Sigmoid函数可以用代码实现如下:

def sigmoid(x):

return 1 / (1 + np.exp(-x))

(3)Tanh函数

Tanh(双曲正切)将输入映射到区间(-1,1)。其关于原点中心对称。常用在隐藏层。

输入在[-3,3]之外时,Tanh的输出值变化很小,此时其导数接近0。

Tanh的输出以0为中心,且其梯度相较于Sigmoid更大,收敛速度相对更快。但同样也存在梯度消失现象。

(4)ReLU函数

ReLU(Rectified Linear Unit,修正线性单元)会将小于0的输入转换为0,大于等于0的输入则保持不变。ReLU定义简单,计算量小。常用于隐藏层。

ReLU作为激活函数不存在梯度消失。当输入小于0时,ReLU的输出为0,这意味着在神经网络中,ReLU激活的节点只有部分是"活跃"的,这种稀疏性有助于减少计算量和提高模型的效率。

当神经元的输入持续为负数时,ReLU的输出始终为0。这意味着神经元可能永远不会被激活,从而导致"神经元死亡"问题。这会影响模型的学习能力,特别是如果大量的神经元都变成了"死神经元"。为解决此问题,可使用Leaky ReLU来代替ReLU作为激活函数。

在负数区域引入一个小的斜率来解决"神经元死亡"问题。

ReLU函数可以用代码实现如下:

def relu(x):

return np.maximum(0, x)

(5)Softmax函数

Softmax将一个任意的实数向量转换为一个概率分布,确保输出值的总和为1,是二分类激活函数Sigmoid在多分类上的推广。Softmax常用于多分类问题的输出层,用来表示类别的预测概率。

Softmax会放大输入中较大的值,使得最大输入值对应的输出概率较大,其他较小的值会被压缩。即在类别之间起到了一定的区分作用。

Softmax函数可以用代码实现如下:

def softmax(x):
return np.exp(x) / np.sum(np.exp(x))

考虑到x较大时,指数函数的值会非常大,容易溢出,可以改进为:

def softmax(x):

x = x - np.max(x) # 溢出对策
return np.exp(x) / np.sum(np.exp(x))

考虑到x为二维数组(矩阵)的情况,可以进一步写为:

def softmax(x):

if x.ndim == 2:

x = x.T

x = x - np.max(x, axis=0)

y = np.exp(x) / np.sum(np.exp(x), axis=0)

return y.T

x = x - np.max(x) # 溢出对策
return np.exp(x) / np.sum(np.exp(x))

(6)其他函数

① Identity(恒等函数)

②Leaky ReLU(Leaky Rectified Linear Unit)

③PReLU(Parametric Rectified Linear Unit)

④RReLU(Randomized Leaky ReLU)

⑤ELU(Exponential Linear Unit)

⑥Swish(也称Sigmoid Linear Unit,SiLU)

⑦softplus

2.2.3 如何选择

隐藏层

首选ReLU,如果效果不好可尝试Leaky ReLU等。

Sigmoid在隐藏层易导致梯度消失,应尽量避免。

Tanh的输出均值为0,对中心化数据更友好,但仍可能引发梯度消失,仅适用于浅层网络。

输出层

二分类选择Sigmoid。

多分类选择Softmax。

回归默认选择Identity。


三 信息传递过程

上面只是三层网络的示意图,实际上每层还应该有偏置,各输入信号加权总和还要经过激活函数的处理。接下来逐层进行分析,考察信号在各层之间传递的过程。

(1)输入层(第0层)→ 第1层

权重和神经元的上标(1)表示网络层号。而下标对于神经元来说,就是这一层内的"索引号";对于权重来说则包含两个数字,分别代表前一层和后一层神经元的索引号。所以,

就表示这是第1层的权重(输入层到第1层),并且是从第2个输入节点到第1层第1个节点。偏置的下标只有1个,因为前一层的偏置节点只有一个。

所以,可以利用式(2.4)(2.5)得到:

同样,对于第1层的第2个、第3个神经元,有:

我们可以直接写成矩阵乘法的形式:

其中:

可以看到,由于有2个输入节点、3个第1层节点,所以全连接层的权重W就应该是一个2×3的矩阵。这样,我们就可以很容易地利用Numpy中的矩阵乘法计算出输出信号值了。

(2)第1层 → 第2层

第1层到第2层的处理类似,权重参数应该是一个3×2的矩阵。

(3)第2层 → 输出层(第3层)

第2层到输出层的处理也类似,权重参数为2×2的矩阵;不过输出层的激活函数一般与隐藏层是不同的,这里用 表示。


四 应用实战

简单神经网络实现

python 复制代码
import numpy as np
from common.functions import sigmoid, identity_function

def init_network():
    network = {}
    network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
    network['b1'] = np.array([0.1, 0.2, 0.3])
    network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
    network['b2'] = np.array([0.1, 0.2])
    network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
    network['b3'] = np.array([0.1, 0.2])

    return network

def forward(network, x):
    w1, w2, w3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, w1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, w2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, w3) + b3
    y = identity_function(a3)

    return y

network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)

print(y)

手写数字识别

我们依然使用Digit Recognizer数据集来进行手写数字识别:https://www.kaggle.com/competitions/digit-recognizer

文件train.csv中包含手绘数字(从0到9)的灰度图像,每张图像为28×28像素,共784像素。每个像素有一个0到255的值表示该像素的亮度。

文件第1列为标签,之后784列分别为784个像素的亮度值。

我们的任务,就是要搭建一个神经网络,实现它的前向传播;也就是要根据输入的数据(28×28 = 784数据点表示的图像),推断出它到底是哪个数字,这个过程也被称为"推理"。

这里,我们构建的也是一个三层神经网络,输入层应该有784个神经元,输出层有10个神经元(表示0~9的分类结果);中间设置2个隐藏层,第一个隐藏层有50个神经元,第二个隐藏层有100个神经元。这里的参数是需要 学习 得到的。

train.py

python 复制代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from model import NeuralNetwork

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

def load_and_preprocess_data(csv_path):
    """
    加载和预处理训练数据
    """
    print("正在加载训练数据...")
    data = pd.read_csv(csv_path)

    # 分离特征和标签
    labels = data['label'].values
    pixels = data.drop('label', axis=1).values

    # 数据归一化
    pixels = pixels.astype('float32') / 255.0

    print(f"数据加载完成: {len(labels)}个样本")
    return pixels, labels


def split_data(X, y, test_size=0.2, random_state=42):
    """
    分割训练集和验证集
    """
    np.random.seed(random_state)
    indices = np.random.permutation(len(X))

    split_point = int(len(X) * (1 - test_size))

    train_indices = indices[:split_point]
    val_indices = indices[split_point:]

    X_train, X_val = X[train_indices], X[val_indices]
    y_train, y_val = y[train_indices], y[val_indices]

    return X_train, X_val, y_train, y_val


def plot_training_history(history):
    """绘制训练历史"""
    plt.figure(figsize=(12, 4))

    plt.subplot(1, 2, 1)
    plt.plot(history['train_loss'], label='训练损失')
    plt.plot(history['val_loss'], label='验证损失')
    plt.title('训练和验证损失')
    plt.xlabel('迭代次数')
    plt.ylabel('损失')
    plt.legend()

    plt.subplot(1, 2, 2)
    plt.plot(history['train_accuracy'], label='训练准确率')
    plt.plot(history['val_accuracy'], label='验证准确率')
    plt.title('训练和验证准确率')
    plt.xlabel('迭代次数')
    plt.ylabel('准确率')
    plt.legend()

    plt.tight_layout()
    plt.savefig('training_history.png')
    plt.show()


def train_model():
    """主训练函数"""
    print("=" * 60)
    print("开始训练手写数字识别模型")
    print("=" * 60)

    # 1. 加载数据
    csv_path = "E:\\实验报告\\深度学习\\课程内容\\data\\D_number\\D_train.csv"
    try:
        X, y = load_and_preprocess_data(csv_path)
    except FileNotFoundError:
        print(" 数据文件未找到,请检查路径")
        return

    # 2. 分割数据集
    X_train, X_val, y_train, y_val = split_data(X, y, test_size=0.2)
    print(f"训练集: {X_train.shape[0]} 个样本")
    print(f"验证集: {X_val.shape[0]} 个样本")

    # 3. 创建模型
    model = NeuralNetwork(input_size=784, hidden1_size=50, hidden2_size=100, output_size=10)

    # 4. 训练参数
    epochs = 300
    learning_rate = 0.01
    batch_size = 32

    # 存储训练历史
    history = {
        'train_loss': [], 'val_loss': [],
        'train_accuracy': [], 'val_accuracy': []
    }

    print(f"\n 训练参数:")
    print(f"迭代次数: {epochs}")
    print(f"学习率: {learning_rate}")
    print(f"批次大小: {batch_size}")
    print("\n 开始训练...")

    # 5. 训练循环
    for epoch in range(epochs):
        # 随机打乱训练数据
        indices = np.random.permutation(len(X_train))
        X_train_shuffled = X_train[indices]
        y_train_shuffled = y_train[indices]

        total_loss = 0
        total_batches = 0

        # 批次训练
        for i in range(0, len(X_train), batch_size):
            # 获取当前批次
            X_batch = X_train_shuffled[i:i + batch_size]
            y_batch = y_train_shuffled[i:i + batch_size]

            # 前向传播
            model.forward_propagation(X_batch)

            # 计算损失
            batch_loss = model.compute_loss(y_batch)
            total_loss += batch_loss
            total_batches += 1

            # 反向传播
            model.backward_propagation(X_batch, y_batch, learning_rate)

        # 计算平均训练损失
        avg_train_loss = total_loss / total_batches

        # 验证集评估
        model.forward_propagation(X_val)
        val_loss = model.compute_loss(y_val)
        train_accuracy = np.mean(model.predict(X_train) == y_train)
        val_accuracy = np.mean(model.predict(X_val) == y_val)

        # 记录历史
        history['train_loss'].append(avg_train_loss)
        history['val_loss'].append(val_loss)
        history['train_accuracy'].append(train_accuracy)
        history['val_accuracy'].append(val_accuracy)

        # 每10个epoch打印一次进度
        if (epoch + 1) % 10 == 0:
            print(f"Epoch {epoch + 1}/{epochs} | "
                  f"训练损失: {avg_train_loss:.4f} | "
                  f"验证损失: {val_loss:.4f} | "
                  f"验证准确率: {val_accuracy:.4f}")

    # 6. 绘制训练历史
    plot_training_history(history)

    # 7. 保存模型参数
    model.save_parameters("nn_sample.npz")

    # 8. 最终评估
    final_train_accuracy = np.mean(model.predict(X_train) == y_train)
    final_val_accuracy = np.mean(model.predict(X_val) == y_val)

    print(f"\n 最终结果:")
    print(f"训练集准确率: {final_train_accuracy:.4f} ({final_train_accuracy * 100:.2f}%)")
    print(f"验证集准确率: {final_val_accuracy:.4f} ({final_val_accuracy * 100:.2f}%)")
    print(f"训练完成!模型参数已保存为 'nn_sample.npz'")


if __name__ == "__main__":
    train_model()

predict.py

python 复制代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from model import NeuralNetwork

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

def load_and_preprocess_data(csv_path):
    """加载和预处理数据"""
    print("正在加载数据...")
    data = pd.read_csv(csv_path)

    if 'label' in data.columns:
        labels = data['label'].values
        pixels = data.drop('label', axis=1).values
    else:
        # 测试集可能没有标签
        labels = None
        pixels = data.values

    # 数据归一化
    pixels = pixels.astype('float32') / 255.0

    print(f"数据加载完成: {len(pixels)}个样本")
    return pixels, labels


def visualize_sample(pixels, labels=None, index=0, prediction=None, confidence=None):
    """可视化手写数字样本"""
    image = pixels[index].reshape(28, 28)

    plt.figure(figsize=(6, 6))
    plt.imshow(image, cmap='gray')

    if labels is not None and prediction is not None:
        title = f'真实: {labels[index]} | 预测: {prediction} | 置信度: {confidence:.3f}'
        color = 'green' if labels[index] == prediction else 'red'
    elif prediction is not None:
        title = f'预测: {prediction} | 置信度: {confidence:.3f}'
        color = 'blue'
    else:
        title = f'样本 {index}'
        color = 'black'

    plt.title(title, color=color, fontsize=12)
    plt.axis('off')
    plt.tight_layout()
    plt.show()


def evaluate_model(model, X, y, sample_size=1000):
    """评估模型性能"""
    if len(X) > sample_size:
        indices = np.random.choice(len(X), sample_size, replace=False)
        X_sample = X[indices]
        y_sample = y[indices]
    else:
        X_sample = X
        y_sample = y

    predictions = model.predict(X_sample)
    accuracy = np.mean(predictions == y_sample)

    print(f"模型评估结果:")
    print(f"样本数量: {len(X_sample)}")
    print(f"准确率: {accuracy:.4f} ({accuracy * 100:.2f}%)")

    # 显示混淆矩阵
    print(f"\ 详细预测结果:")
    print("索引 | 真实 | 预测 | 置信度 | 状态")
    print("-" * 45)

    for i in range(min(10, len(X_sample))):
        pred, conf = model.predict_with_confidence(X_sample[i:i + 1])
        status = "Right" if pred[0] == y_sample[i] else "Error"
        print(f"{i:4d} | {y_sample[i]:4d} | {pred[0]:4d} | {conf[0]:.3f}   | {status}")

    return accuracy


def predict_single_digit(model, pixel_data):
    """预测单个数字"""
    if len(pixel_data) == 784:
        # 如果是扁平化的数据,重塑为(1, 784)
        pixel_data = pixel_data.reshape(1, -1)

    prediction, confidence = model.predict_with_confidence(pixel_data)
    return prediction[0], confidence[0]


def main():
    """主推理函数"""
    print("=" * 60)
    print("手写数字识别 - 推理模式")
    print("=" * 60)

    # 1. 创建模型
    model = NeuralNetwork()

    # 2. 加载训练好的参数
    param_file = "nn_sample.npz"
    if not model.load_parameters(param_file):
        print("无法加载模型参数,请先运行 train.py 进行训练")
        return

    # 3. 加载数据
    csv_path = "E:\\实验报告\\深度学习\\课程内容\\data\\D_number\\D_test.csv"
    try:
        X, y = load_and_preprocess_data(csv_path)
    except FileNotFoundError:
        print("数据文件未找到")
        # 可以在这里添加手动输入数字的功能
        return

    # 4. 交互式推理演示
    while True:
        print(f"\n 选择操作:")
        print("1. 评估模型整体性能")
        print("2. 查看单个样本预测")
        print("3. 批量预测演示")
        print("4. 退出")

        choice = input("请输入选择 (1-4): ").strip()

        if choice == '1':
            # 整体评估
            evaluate_model(model, X, y, sample_size=1000)

        elif choice == '2':
            # 单个样本预测
            try:
                index = int(input("请输入样本索引 (0-{}): ".format(len(X) - 1)))
                if 0 <= index < len(X):
                    prediction, confidence = model.predict_with_confidence(X[index:index + 1])
                    visualize_sample(X, y, index, prediction[0], confidence[0])

                    print(f"\n 预测详情:")
                    print(f"样本索引: {index}")
                    print(f"预测数字: {prediction[0]}")
                    print(f"置信度: {confidence[0]:.3f}")
                else:
                    print("索引超出范围")
            except ValueError:
                print("请输入有效数字")

        elif choice == '3':
            # 批量演示
            print(f"\n 批量预测演示 :")
            for i in range(5):
                prediction, confidence = model.predict_with_confidence(X[i:i + 1])
                print(f"样本 {i}: 预测={prediction[0]}, 置信度={confidence[0]:.3f}")

        elif choice == '4':
            print("再见!")
            break
        else:
            print("无效选择,请重新输入")


if __name__ == "__main__":
    main()

model.py

python 复制代码
import numpy as np

class NeuralNetwork:
    """
    三层神经网络模型类
    结构: 输入层(784) → 隐藏层1(50) → 隐藏层2(100) → 输出层(10)
    """

    def __init__(self, input_size=784, hidden1_size=50, hidden2_size=100, output_size=10):
        """
        初始化神经网络
        参数:
        - input_size: 输入层大小 (28x28=784)
        - hidden1_size: 第一隐藏层大小
        - hidden2_size: 第二隐藏层大小
        - output_size: 输出层大小 (10个数字类别)
        """
        self.input_size = input_size
        self.hidden1_size = hidden1_size
        self.hidden2_size = hidden2_size
        self.output_size = output_size

        # 参数字典
        self.params = {}
        self._initialize_parameters()

    def _initialize_parameters(self, initialization='xavier'):
        """
        初始化网络参数
        参数:
        - initialization: 初始化方法 ('xavier' 或 'random')
        """
        if initialization == 'xavier':
            # Xavier初始化,有助于训练稳定性
            self.params['W1'] = np.random.randn(self.input_size, self.hidden1_size) * np.sqrt(1. / self.input_size)
            self.params['W2'] = np.random.randn(self.hidden1_size, self.hidden2_size) * np.sqrt(1. / self.hidden1_size)
            self.params['W3'] = np.random.randn(self.hidden2_size, self.output_size) * np.sqrt(1. / self.hidden2_size)
        else:
            # 随机初始化
            self.params['W1'] = np.random.randn(self.input_size, self.hidden1_size) * 0.01
            self.params['W2'] = np.random.randn(self.hidden1_size, self.hidden2_size) * 0.01
            self.params['W3'] = np.random.randn(self.hidden2_size, self.output_size) * 0.01

        # 偏置初始化为0
        self.params['b1'] = np.zeros((1, self.hidden1_size))
        self.params['b2'] = np.zeros((1, self.hidden2_size))
        self.params['b3'] = np.zeros((1, self.output_size))

    def relu(self, x):
        """ReLU激活函数"""
        return np.maximum(0, x)

    def relu_derivative(self, x):
        """ReLU导数,用于反向传播"""
        return (x > 0).astype(float)

    def softmax(self, x):
        """Softmax函数"""
        # 数值稳定性处理
        exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
        return exp_x / np.sum(exp_x, axis=1, keepdims=True)

    def forward_propagation(self, X):
        """
        前向传播
        参数:
        - X: 输入数据 (n_samples, 784)
        返回:
        - 输出层概率分布 (n_samples, 10)
        """
        # 输入层 → 隐藏层1
        self.z1 = np.dot(X, self.params['W1']) + self.params['b1']
        self.a1 = self.relu(self.z1)

        # 隐藏层1 → 隐藏层2
        self.z2 = np.dot(self.a1, self.params['W2']) + self.params['b2']
        self.a2 = self.relu(self.z2)

        # 隐藏层2 → 输出层
        self.z3 = np.dot(self.a2, self.params['W3']) + self.params['b3']
        self.a3 = self.softmax(self.z3)

        return self.a3

    def backward_propagation(self, X, y, learning_rate=0.01):
        """
        反向传播(用于训练)
        参数:
        - X: 输入数据
        - y: 真实标签
        - learning_rate: 学习率
        """
        m = X.shape[0]  # 样本数量

        # 将标签转换为one-hot编码
        y_one_hot = np.eye(self.output_size)[y]

        # 计算输出层误差
        dz3 = self.a3 - y_one_hot
        dw3 = np.dot(self.a2.T, dz3) / m
        db3 = np.sum(dz3, axis=0, keepdims=True) / m

        # 计算隐藏层2误差
        dz2 = np.dot(dz3, self.params['W3'].T) * self.relu_derivative(self.z2)
        dw2 = np.dot(self.a1.T, dz2) / m
        db2 = np.sum(dz2, axis=0, keepdims=True) / m

        # 计算隐藏层1误差
        dz1 = np.dot(dz2, self.params['W2'].T) * self.relu_derivative(self.z1)
        dw1 = np.dot(X.T, dz1) / m
        db1 = np.sum(dz1, axis=0, keepdims=True) / m

        # 更新参数
        self.params['W3'] -= learning_rate * dw3
        self.params['b3'] -= learning_rate * db3
        self.params['W2'] -= learning_rate * dw2
        self.params['b2'] -= learning_rate * db2
        self.params['W1'] -= learning_rate * dw1
        self.params['b1'] -= learning_rate * db1

    def compute_loss(self, y_true):
        """计算交叉熵损失"""
        y_one_hot = np.eye(self.output_size)[y_true]
        # 避免log(0)的情况
        epsilon = 1e-15
        a3_clipped = np.clip(self.a3, epsilon, 1. - epsilon)
        loss = -np.mean(np.sum(y_one_hot * np.log(a3_clipped), axis=1))
        return loss

    def predict(self, X):
        """预测数字类别"""
        probabilities = self.forward_propagation(X)
        return np.argmax(probabilities, axis=1)

    def predict_with_confidence(self, X):
        """预测数字类别及置信度"""
        probabilities = self.forward_propagation(X)
        predictions = np.argmax(probabilities, axis=1)
        confidences = np.max(probabilities, axis=1)
        return predictions, confidences

    def save_parameters(self, file_path):
        """保存模型参数到文件"""
        np.savez(file_path,
                 W1=self.params['W1'], b1=self.params['b1'],
                 W2=self.params['W2'], b2=self.params['b2'],
                 W3=self.params['W3'], b3=self.params['b3'])
        print(f"模型参数已保存到: {file_path}")

    def load_parameters(self, file_path):
        """从文件加载模型参数"""
        try:
            loaded_params = np.load(file_path)
            self.params = {
                'W1': loaded_params['W1'],
                'b1': loaded_params['b1'],
                'W2': loaded_params['W2'],
                'b2': loaded_params['b2'],
                'W3': loaded_params['W3'],
                'b3': loaded_params['b3']
            }
            print(f"模型参数已从 {file_path} 加载")
            return True
        except FileNotFoundError:
            print(f"参数文件 {file_path} 未找到")
            return False
        except Exception as e:
            print(f"加载参数时出错: {e}")
            return False

训练与预测结果


2026.01.23

相关推荐
NAGNIP22 分钟前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab2 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab2 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP5 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年5 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼6 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS6 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区7 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈7 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang8 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx