核心内容摘要
快速部署Qwen3-4B Instruct-2507:一份详细的依赖清单与脚本分享
深度学习基础核心工具库functions.py深度学习基础核心工具库的纯 NumPy 实现完整封装了神经网络训练 / 预测所需的5 类激活函数和2 类损失函数所有函数均适配标量、向量、矩阵多维度输入解决了数值溢出、维度不兼容等工程问题是手写数字识别等神经网络任务的基础依赖组件。
代码整体分为激活函数、损失函数、测试代码三部分以下逐模块详细解析包含核心作用、
实现原理、关键技巧和使用场景
激活函数
阶跃函数step_function# x传入标量 def step_function0(x): if x 0: return 1 else: return 0 # x传入向量或矩阵 def step_function(x): return np.array(x 0, dtypeint)核心作用最基础的二值激活函数将输入划分为「0/1」两类是感知机的核心组件用于理论讲解双版本实现step_function0仅支持标量输入通过条件判断实现二值输出step_function支持向量 / 矩阵输入利用np.array(x 0, dtypeint)实现向量化计算返回与输入同形状的 0/1 数组缺点函数不连续、导数恒为 0无法通过梯度下降训练实际神经网络中几乎不使用仅作理论参考。
Sigmoid 函数sigmoiddef sigmoid(x): return 1/(1 np.exp(-x))核心作用经典 S 型激活函数将任意输入映射到 **(0,
** 区间是早期神经网络隐藏层的主流选择
实现原理基于公式 sigmoid(x)1e−x1利用 NumPy 广播机制直接支持多维度输入关键特点连续可导支持梯度下降训练但存在梯度消失问题输入绝对值过大时导数趋近于 0导致深层网络训练困难使用场景浅层神经网络的隐藏层如本次手写数字识别的 2 层隐藏层。
ReLU 函数reludef relu(x): return np.maximum(0, x)核心作用目前最主流的隐藏层激活函数彻底解决了 Sigmoid 的梯度消失问题大幅提升深层网络的训练效率
实现原理基于公式 ReLU(x)max(0,x)利用np.maximum(0, x)取「0 和输入值的较大值」计算极快且适配任意维度关键特点单侧激活x0 时直接输出 x导数为 1x≤0 时输出 0导数为 0保留有效梯度训练收敛速度远快于 Sigmoid使用场景浅层 / 深层神经网络的隐藏层可直接替换本次手写数字识别的 Sigmoid提升精度。
Softmax 函数softmax# 一维版单样本向量输入 def softmax0(x): x x - np.max(x) # 溢出对策核心技巧避免指数爆炸 return np.exp(x) / np.sum(np.exp(x)) # 通用版支持一维向量/二维矩阵N个样本×C个类别 def softmax(x): if x.ndim 2: # 处理批量样本 x x.T # 转置为C×N方便按列每个样本计算 x x - np.max(x, axis
# 按列减每个样本的最大值 y np.exp(x) / np.sum(np.exp(x), axis
# 按列求和保证每列和为1 return y.T # 转置回N×C恢复原始样本维度 # 处理单样本向量 x x - np.max(x) return np.exp(x) / np.sum(np.exp(x))核心作用专门用于分类任务的输出层将网络输出的原始数值logits可正可负映射到 **(0,
** 区间且所有输出值求和为 1使其具备概率分布的含义如输出 [
1,
8,
1] 表示 3 类的预测概率分别为 10%、80%、10%核心解决问题数值溢出—— 直接计算np.exp(x)时若 x 值较大如 x100指数结果会超出浮点数范围导致报错因此先执行x x - np.max(x)输入值减去自身最大值保证最大值为 0np.exp(
1从根本避免溢出双版本实现softmax0仅支持一维向量输入单样本预测softmax通用版同时支持一维向量单样本和二维矩阵N×CN 个样本、C 个类别矩阵输入时按样本维度行计算保证每个样本的输出概率和为 1实现细节矩阵输入时通过转置x.T将「N×C」转为「C×N」方便按列每个样本执行减最大值、求和操作最后再转置回原形状保证输出与输入维度一致使用场景所有分类任务的输出层如本次手写数字识别的 10 分类输出层输出
的概率分布。
恒等函数identitydef identity(x): return x核心作用输入与输出完全一致yx专门用于回归任务的输出层
实现原理极简实现无任何计算开销支持任意维度输入使用场景回归任务如房价预测、温度预测需输出连续数值无需做概率映射与本次手写数字识别的分类任务无关仅作完整工具库补充。
损失函数
均方误差Mean Squared Error, MSEdef mean_squared_error(y, t): return
5 * np.sum((y - t) **
核心作用专门用于回归任务的损失函数计算预测值与真实值的「平方误差的平均值」
实现原理基于公式 MSE(y,t)21∑i1n(yi−ti)2利用 NumPy 向量化计算适配任意维度输入关键技巧乘以
5 是为了简化后续梯度计算—— 对 MSE 求导时平方的系数 2 会与
5 抵消最终导数为(y−t)无需额外计算系数特点误差越大损失值增长越快连续可导梯度下降训练稳定使用场景回归任务如房价预测与本次手写数字识别的分类任务无关。
交叉熵误差Cross Entropy Error, CEEdef cross_entropy_error(y, t): # 一维→二维单样本转换为批量格式统一计算逻辑 if y.ndim 1: y y.reshape(1, y.size) t t.reshape(1, t.size) # 独热编码→类别索引兼容两种标签格式无需额外预处理 if t.size y.size: t t.argmax(axis
n y.shape[0] # 样本数量 # 高级索引取真实类别概率加1e-7避免log(
求和后归一化 return -np.sum(np.log(y[np.arange(n), t] 1e-
) / n核心作用专门用于分类任务的损失函数是分类任务的首选损失函数—— 对「预测错误的类别」惩罚更显著训练收敛速度远快于 MSE核心解决问题对数无意义—— 当模型预测概率yi0时log(yi)会出现无穷大错误因此在预测值后加1e−7极小值不影响概率大小保证对数计算有定义关键特性维度兼容 标签格式兼容无需额外预处理即可直接使用适配「一维向量单样本」和「二维矩阵N 个样本」自动将一维输入转换为二维统一计算逻辑兼容独热编码标签t 为 one-hot如 [0,1,0]表示第 2 类为真实标签和类别索引标签t 为整数如 1若检测到 t 为独热编码t.size y.size会自动通过argmax(axis
转换为类别索引
实现原理基于公式 CEE(y,t)−n1∑i1nlog(yi,ti1e−
其中n为样本数ti为第i个样本的真实类别索引yi,ti为对应类别的预测概率关键细节通过y[np.arange(n), t]快速取每个样本「真实类别对应的预测概率」NumPy 高级索引避免循环计算高效最后除以样本数n归一化保证损失值大小与样本数无关适配批量训练使用场景所有分类任务如本次手写数字识别的 10 分类任务搭配 Softmax 输出层使用。
测试代码if __name__ __main__: # 测试输入4×3矩阵4个样本每个样本3个类别 x np.array([[0, 1, 2], [3, 4, 5], [-1, -2, -3], [-6, -4, -5]]) print(softmax(x)) # 输出4×3矩阵每行和为1这部分是Softmax 函数的专项测试代码作用是验证通用版softmax对二维矩阵输入批量样本的处理能力确保满足分类任务的核心要求。
完整functions.pyimport numpy as np #
激活函数 #
阶跃函数 # x传入标量 def step_function0(x): if x 0: return 1 else: return 0 # x传入向量或矩阵 def step_function(x): return np.array(x 0, dtypeint) #
Sigmoid 函数 def sigmoid(x): return 1/(1 np.exp(-x)) #
ReLU 函数 def relu(x): return np.maximum(0, x) #
Softmax函数 # 输入x为向量 def softmax0(x): # 溢出对策 x x - np.max(x) return np.exp(x) / np.sum(np.exp(x)) # 输入x为矩阵 N×C def softmax(x): if x.ndim 2: x x.T x x - np.max(x, axis
# 减去每条数据中xi的最大值 y np.exp(x) / np.sum(np.exp(x), axis
return y.T # 溢出对策 x x - np.max(x) return np.exp(x) / np.sum(np.exp(x)) #
恒等函数 def identity(x): return x #
损失函数 #
MSE def mean_squared_error(y, t): return
5 * np.sum((y - t) **
#
交叉熵误差 def cross_entropy_error(y, t): # 对于一维情况直接转换为二维 if y.ndim 1: y y.reshape(1, y.size) t t.reshape(1, t.size) # t 是独热编码表示转换为正确类别标签的索引 if t.size y.size: t t.argmax(axis
n y.shape[0] return -np.sum(np.log(y[np.arange(n), t] 1e-
) / n # 测试 if __name__ __main__: x np.array([[0, 1, 2], [3, 4, 5], [-1, -2, -3], [-6, -4, -5]]) print(softmax(x))3 层全连接神经网络前向传播深度学习入门经典的 3 层全连接前馈神经网络实现基于 NumPy 完成从输入层→隐藏层→输出层的前向传播计算无训练逻辑仅实现固定参数下的 “数据正向流动计算”是理解神经网络核心原理的基础。
代码整体分为网络参数初始化、前向传播计算、实际调用执行三部分先明确核心概念再逐行拆解逻辑、补充计算细节和原理。
定义并初始化神经网络的所有权重W和偏置b用字典存储键为参数名值为 NumPy 数组保证参数按层索引返回完整的网络参数字典。
def init_network(): # 新建空字典用于存储各层权重和偏置 network {} # 输入层→隐藏层1的权重2行输入层2神经元×3列隐藏层1 3神经元符合「前层×后层」规则 network[W1] np.array([[
1,
3,
5], [
2,
4,
6]]) # 隐藏层1的偏置长度3与隐藏层1神经元数一致 network[b1] np.array([
1,
2,
3]) # 隐藏层1→隐藏层2的权重3行隐藏层1 3神经元×2列隐藏层2 2神经元 network[W2] np.array([[
1,
4], [
2,
5], [
3,
6]]) # 隐藏层2的偏置长度2与隐藏层2神经元数一致 network[b2] np.array([
1,
2]) # 隐藏层2→输出层的权重2行隐藏层2 2神经元×2列输出层2神经元 network[W3] np.array([[
1,
3], [
2,
4]]) # 输出层的偏置长度2与输出层神经元数一致 network[b3] np.array([
1,
2]) # 返回包含所有参数的网络字典 return network实现数据从输入层到输出层的逐层计算是神经网络的执行核心。
参数network为init_network()返回的参数字典x为输入数据NumPy 数组返回最终输出结果y。
def forward(network, x): # 从网络字典中解包各层权重和偏置简化后续代码书写无需重复写network[W1] w1, w2, w3 network[W1], network[W2], network[W3] b1, b2, b3 network[b1], network[b2], network[b3] # 第1层计算输入层 → 隐藏层1 a1 np.dot(x, w
b1 # 线性变换输入x与权重w1点积 偏置b1NumPy广播自动适配形状 z1 sigmoid(a
# 非线性激活sigmoid处理线性结果a1引入非线性得到隐藏层1输出 # 第2层计算隐藏层1 → 隐藏层2 a2 np.dot(z1, w
b2 # 线性变换上一层激活输出z1作为输入与w2点积 b2 z2 sigmoid(a
# 非线性激活隐藏层统一用sigmoid得到隐藏层2输出 # 第3层计算隐藏层2 → 输出层 a3 np.dot(z2, w
b3 # 线性变换最后一次线性计算无后续隐藏层 y identity(a
# 输出层激活恒等函数直接输出a3回归任务无需映射 return y完整代码import numpy as np from functions import sigmoid, identity def init_network(): network {} network[W1] np.array([[
1,
3,
5], [
2,
4,
6]]) network[b1] np.array([
1,
2,
3]) network[W2] np.array([[
1,
4], [
2,
5], [
3,
6]]) network[b2] np.array([
1,
2]) network[W3] np.array([[
1,
3], [
2,
4]]) network[b3] np.array([
1,
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, w
b1 z1 sigmoid(a
a2 np.dot(z1, w
b2 z2 sigmoid(a
a3 np.dot(z2, w
b3 y identity(a
return y network init_network() x np.array([
0,
5]) y forward(network, x) print(y)运行结果应用案例手写数字识别基于预训练模型的手写数字识别预测程序核心实现MNIST 数据集加载预处理预训练神经网络模型加载批量预测识别精度评估的全流程基于 NumPy、Pandas 和 Scikit-learn 实现依赖自定义的激活函数工具库functions.py。
任务要求
任务与数据集说明任务手写数字识别
共 10 个类别属于多分类任务数据集MNIST 的train.csvKaggle 经典格式每行是 1 个手写数字样本第一列label是真实数字
剩余 784 列是 28×28 像素的灰度值
即784 个输入特征→10 个输出类别核心依赖functions.py中的sigmoid隐藏层激活、softmax输出层激活将结果转为概率分布。
网络与模型说明神经网络结构784 输入→N 隐藏 1→M 隐藏 2→10 输出的 3 层全连接网络权重 / 偏置形状匹配 784 特征 10 分类预训练模型nn_sample是提前训练好的网络参数文件由joblib保存包含W1/W2/W3权重矩阵和b1/b2/b3偏置向量本代码仅做加载和预测无训练逻辑。
思路批量预测避免单样本循环计算提升效率每次处理 100 个样本归一化将像素值缩放到
区间适配神经网络输入要求概率最大化预测通过softmax将网络输出转为
的概率分布取概率最大的索引为预测数字精度评估通过「预测值与真实标签匹配数 / 总样本数」计算识别准确率。
代码编写完成 MNIST 数据集的「加载→特征 / 标签拆分→训练 / 测试集划分→归一化」全预处理流程返回预处理后的测试集特征和测试集真实标签为后续预测做数据准备。
def get_data(): #
加载MNIST数据集train.csv放在代码同级目录 data pd.read_csv(train.csv) #
拆分特征矩阵X和真实标签y X data.drop(label, axis
# 特征删除label列剩余784列28×28像素DataFrame格式 y data[label] # 标签仅保留label列值为
的整数Series格式 #
划分训练集70%和测试集30%test_size
3表示测试集占30% # 注本代码无训练逻辑训练集实际未使用仅为保持预处理流程完整性 x_train, x_test, y_train, y_test train_test_split(X, y, test_size
0.
#
初始化归一化器MinMaxScaler将数据缩放到[0,1]区间公式X_scaled (X - X_min) / (X_max - X_min) preprocessor MinMaxScaler() #
训练集拟合转换拟合训练集的最大值/最小值仅用训练集的统计信息做归一化避免数据泄露 x_train preprocessor.fit_transform(x_train) #
测试集仅转换直接使用训练集的最大值/最小值保证训练/测试集归一化标准一致 x_test preprocessor.transform(x_test) # 修正后返回预处理后的测试集特征、测试集真实标签原代码t_test改为y_test return x_test, y_test关键细节说明数据泄露避免fit_transform仅用在训练集transform用在测试集是机器学习预处理的黄金准则防止测试集的统计信息影响模型泛化能力数据格式转换Pandas 的 DataFrame/Series 经fit_transform后会自动转为NumPy 数组适配后续神经网络的矩阵计算归一化的必要性MNIST 像素值为
若不归一化大数值会导致sigmoid激活函数进入饱和区导数趋近于 0即使是预测阶段也会导致网络输出异常精度骤降。
实现 3 层全连接神经网络的前向传播计算接收预训练模型参数和输入数据返回每个样本对应 10 个数字的概率分布是手写数字识别的「预测核心」。
def predict(network, x): #
从网络参数字典中解包权重和偏置简化后续计算 w1, w2, w3 network[W1], network[W2], network[W3] b1, b2, b3 network[b1], network[b2], network[b3] #
第一层计算输入层 → 隐藏层1线性变换非线性激活 a1 np.dot(x, w
b1 # 线性变换输入x与权重w1矩阵点积 偏置b1NumPy广播自动适配形状 z1 sigmoid(a
# 非线性激活sigmoid将线性结果映射到(0,
引入非线性特征提取 #
第二层计算隐藏层1 → 隐藏层2重复线性激活流程 a2 np.dot(z1, w
b2 # 上一层激活输出z1作为当前层输入 z2 sigmoid(a
# 隐藏层统一使用sigmoid激活 #
第三层计算隐藏层2 → 输出层线性变换softmax概率化 a3 np.dot(z2, w
b3 # 最后一次线性变换得到10个原始输出值logits y softmax(a
# softmax将原始值转为(0,
的概率分布且10个概率和为1 return y关键细节说明批量输入适配函数的输入x可以是单样本向量1×784或批量样本矩阵N×784NumPy 的np.dot和广播机制会自动适配返回对应形状的概率分布1×10 或 N×10这是向量化计算的核心优势无需循环单样本softmax 的核心作用将网络输出的原始值可正可负、无固定范围转为概率分布例如输出[
01,
95,
04, ..., 0]表示该样本是数字 1 的概率为 95%是其他数字的概率极低让输出结果具备直观的概率含义与回归任务的区别手写数字识别是分类任务因此输出层用softmax而非恒等函数后续可通过np.argmax快速取概率最大的类别作为预测结果。
完整代码import numpy as np import pandas as pd import joblib from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler from functions import sigmoid, softmax def get_data(): # 加载数据集 data pd.read_csv(train.csv) # 划分训练集和测试集 X data.drop(label, axis
y data[label] x_train, x_test, y_train, y_test train_test_split(X, y, test_size
0.