从零手写BP神经网络:NumPy实现MNIST手写数字识别
简介面向深度学习初学者的BP神经网络手写数字识别工程完整覆盖了从数据加载、模型构建到训练与预测的流程可帮助读者吃透反向传播算法中梯度下降、链式法则等细节。资源包共包含2000个文件其中以图片样本为主辅以配置文件、标记文件、权重数据以及Python源码整体体积69.36MB并按照数据、代码、配置等模块分目录存放便于按图索骥学习。该实现已提供训练好的权重和偏移量数据文件运行时只需输入类似“4/mnist_test_4.png”的路径后缀即可直接得到预测结果同时压缩包也附有完整训练集读者可以自行重新训练网络观察不同超参数对识别准确率的影响。目前已有8757人学习使用特别适合正在做神经网络入门实验的学生或想从零搭建完整模型代码的计算机视觉爱好者。1. 从零手写BP神经网络MNIST手写数字识别不过是几百行NumPy某个周五晚上我去围观一个入门项目用纯Python NumPy从零搭BP神经网络识别MNIST手写数字。没看之前我以为至少得调五六个框架API看完发现反向传播的核心代码也就几十行矩阵乘法。这个项目不依赖任何深度学习框架却能在测试集上稳定跑出97%以上的准确率——它把BP神经网络的“魔法”削掉外壳给你看。适合两类人一是初学深度学习、卡在反向传播公式推导的二是想在mnist手写数字识别基础上改造成批量图像分类管线的。项目包含完整的数据加载、网络定义、训练循环和评估脚本改一行代码就能调整网络宽度和深度二次开发非常顺手。2. 技术选型与整体架构为什么弃用深度学习框架手写BP反而真香2.1 从黑盒到透明手写BP神经网络到底在解决什么问题MNIST手写数字识别在计算机视觉界的地位相当于编程课的“Hello World”。数据集本身不复杂60000张28x28的8位灰度训练图外加10000张测试图每张图对应0到9中的一个标签。难点从来不在“怎么把准确率刷到99%”——那用现有深度学习框架几行就能做到——而在于把BP神经网络内部的每一处矩阵乘法、每一次梯度传递讲清楚。用TensorFlow或者PyTorch搭这个网络大概只需要定义三层nn.Linear加上一个交叉熵损失一个下午就能写完。但训练完成后你手里是个黑匣子损失为什么这样下降、梯度为什么不会爆炸、权重初始化的细微差别会导致什么后果全部被框架封在底层。这个项目选择纯Python实现反而更有价值前向传播你能手写出每一步的矩阵形状反向传播你能追到链式法则的每一个因子。很多读者卡在反向传播公式推导很久看了这份代码之后会有一种“原来就这么回事”的感觉。在实际动手之前我先花时间搞清楚了技术选型。网络结构是最经典的三层BP输入层784个神经元对应28x28像素隐藏层128个神经元输出层10个神经元对应10个数字分类。激活函数用sigmoid输出层配合softmax处理多分类概率损失函数用交叉熵优化器就是最朴素的随机梯度下降。这套组合不是拍脑袋定的每一项都有明确的动机sigmoid是BP神经网络教科书里的标准配置softmax能把输出向量变成合法的概率分布交叉熵配合softmax让反向传播的梯度表达式收敛成极其简洁的“预测减真实值”。2.2 四文件组织与一次完整的数据流向整个项目没有复杂工程结构四个Python文件一条链路走到底。我拆包后第一件事就是把目录结构画出来因为搞懂数据从哪里来、经过谁、最后到哪里去比读代码本身更有助于跑起来。bp-mnist/ ├── data_loader.py # 读取MNIST二进制文件返回归一化后的图像矩阵和one-hot标签 ├── network.py # BPNeuralNetwork类权重初始化、前向传播、反向传播、参数更新 ├── train.py # 训练主入口配置超参、循环迭代、保存模型参数 └── evaluate.py # 加载训练好的参数在测试集上算准确率并可视化部分预测结果数据流向也清晰data_loader拿到MNIST官方的IDX二进制文件先按大端字节序解析出魔法数、样本数、行列数再把图像字节流reshape成(样本数, 784)的二维矩阵并除以255做归一化同时把标签转成10维one-hot向量。train.py拿到这批矩阵后初始化网络进入epoch循环每次取一小批样本走一遍前向传播得到预测算交叉熵损失接着反向传播算出W1、b1、W2、b2各自的梯度用SGD规则更新参数。训练结束后把权重保存成.npy文件。evaluate.py加载权重把测试集整体过一遍argmax取预测类别和真实标签比对得出准确率。这套“数据加载→归一化→one-hot→前向传播→反向传播→参数更新→评估”的流水线是整个项目的主轴。后面所有章节的代码拆解都是围绕这根轴展开的。3. 核心模块拆解数据读取、前向传播与反向传播的完整链路3.1 data_loaderMNIST二进制格式的读取与归一化MNIST官方数据不是一张张PNG图片而是一个IDX格式的二进制文件这坑过不少人。IDX格式开头是4个32位大端整数魔法数、样本数、行数、列数之后才是纯粹的像素字节流。如果直接用普通的open和read读取不去理会字节序解析出来的图像会是一堆乱码。data_loader里用Python的struct模块专门处理了这个问题。import numpy as np import struct def load_mnist(images_path, labels_path): # 读取图像文件前16个字节是4个int32按大端字节序()解包 with open(images_path, rb) as f: magic, num, rows, cols struct.unpack(IIII, f.read(16)) # 剩余字节全部是像素每个像素是0-255的uint8 images np.frombuffer(f.read(), dtypenp.uint8) images images.reshape(num, rows * cols) # 归一化到0-1区间sigmoid激活函数在这个输入范围内梯度最友好 images images.astype(np.float32) / 255.0 # 读取标签文件前8个字节是2个int32之后每个字节是一个标签 with open(labels_path, rb) as f: magic, num struct.unpack(II, f.read(8)) labels np.frombuffer(f.read(), dtypenp.uint8) return images, labels def one_hot(labels, num_classes10): n labels.shape[0] y np.zeros((n, num_classes), dtypenp.float32) y[np.arange(n), labels.astype(int)] 1.0 return y这段代码的关键点有两个。第一个是struct.unpack(IIII, ...)里的它明确告诉Python按大端字节序解析。MNIST官方文件的魔法数是2051图像和2049标签如果你按小端读这两个数会变成很奇怪的值后续reshape直接出错。第二个是归一化把0-255的像素值压缩到0-1之间。sigmoid激活函数的输出范围是0到1输入如果保持原始灰度值加权求和后的结果很容易落到sigmoid的饱和区梯度趋近于0训练会变得非常慢。归一化之后输入均值在0附近梯度流动明显更顺畅。标签处理这里有一个容易被忽略的细节one_hot编码后y的形状是(样本数, 10)每一行只有对应数字的那一列是1其余是0。输出层有10个神经元softmax会把它变成一个10维概率分布交叉熵损失拿这个概率分布和one_hot向量做比对。这里不推荐用原始数字标签直接训练因为0-9的整数会让网络错误地学到“8比7大”这类不存在的序数关系。3.2 网络定义与前向传播784个像素如何被压缩成10个概率BPNeuralNetwork类是整套代码的心脏。初始化阶段要做的事情不多确定三层神经元数量、给权重和偏置分配内存、设定学习率。权重初始化这里有一条血泪经验不能直接np.random.randn完事需要乘一个小的缩放系数。class BPNeuralNetwork: def __init__(self, input_size784, hidden_size128, output_size10, lr0.1): # 使用固定随机种子保证每次训练结果可复现 np.random.seed(42) self.input_size input_size self.hidden_size hidden_size self.output_size output_size self.lr lr # 权重初始化randn是标准正态分布乘0.01让初始值集中在0附近 self.W1 np.random.randn(input_size, hidden_size) * 0.01 self.b1 np.zeros((1, hidden_size), dtypenp.float32) self.W2 np.random.randn(hidden_size, output_size) * 0.01 self.b2 np.zeros((1, output_size), dtypenp.float32) def sigmoid(self, x): # 经典sigmoid激活函数输出范围(0,1) return 1.0 / (1.0 np.exp(-x)) def softmax(self, x): # 数值稳定的softmax先减去每行最大值防止exp溢出 exp_x np.exp(x - np.max(x, axis1, keepdimsTrue)) return exp_x / np.sum(exp_x, axis1, keepdimsTrue) def forward(self, x): # 输入x形状: (batch_size, 784) self.z1 x self.W1 self.b1 self.a1 self.sigmoid(self.z1) # 隐藏层输出: (batch_size, 128) self.z2 self.a1 self.W2 self.b2 # 输出层线性组合: (batch_size, 10) self.a2 self.softmax(self.z2) # 10类概率分布 return self.a2权重初始化乘0.01这是我在前两个项目里反复踩坑才记住的。如果不乘任何系数直接np.random.randn生成的权重标准差是128x28784个输入加权求和后数值很容易跑到几十甚至上百经过sigmoid后输出饱和在0或1附近梯度直接消失。乘0.01之后初始权重很小加权和落在sigmoid的线性段反向传播能顺利把梯度传回去。前向传播的维度变化值得对照着看。输入x是(batch_size, 784)x self.W1得到(batch_size, 128)加上形状为(1, 128)的b1广播机制会把它加到每一行。隐藏层输出a1仍然是(batch_size, 128)继续和W2相乘得到(batch_size, 10)softmax归一化成每行元素之和等于1的合法概率分布。这10个数就是对“数字0到9”各自的预测概率。3.3 反向传播链式法则落成矩阵运算反向传播是全项目最让人头疼的部分但代码写出来其实只有几行。关键在于搞清楚每个变量的形状和对应的梯度公式。这个项目用了softmax加交叉熵一个很漂亮的数学性质是输出层梯度直接等于预测概率减one_hot标签中间的推导省了一大截。def backward(self, x, y_true): # 样本数用于梯度归一化 m x.shape[0] # 输出层梯度softmax交叉熵的梯度恰好是 预测值 - 真实值 dz2 self.a2 - y_true # 隐藏层到输出层的权重梯度 dW2 self.a1.T dz2 / m db2 np.sum(dz2, axis0, keepdimsTrue) / m # 梯度继续往隐藏层传播 da1 dz2 self.W2.T # sigmoid的导数: a1 * (1 - a1)注意这里用的是前向传播缓存的a1 dz1 da1 * self.a1 * (1 - self.a1) # 输入层到隐藏层的权重梯度 dW1 x.T dz1 / m db1 np.sum(dz1, axis0, keepdimsTrue) / m # 随机梯度下降更新参数 self.W1 - self.lr * dW1 self.b1 - self.lr * db1 self.W2 - self.lr * dW2 self.b2 - self.lr * db2这段代码的每一行都值得细读。dz2 self.a2 - y_true是反向传播的起点它把10个类别的预测误差具体化为概率差。dW2 self.a1.T dz2 / m里的/ m是取平均值这样梯度大小不受batch_size影响如果只记得公式不带这个归一化梯度会随着batch增大成倍放大学习率就需要重新调。da1 dz2 self.W2.T把误差从输出层映射回隐藏层链式法则在这里体现得淋漓尽致——误差沿着网络的反方向逐层“分配”。dz1中的self.a1 * (1 - self.a1)是sigmoid函数的导数这里之所以能用前向传播缓存下来的a1是因为sigmoid的导数恰好能用自己的输出表示。最后四行参数更新是最朴素的梯度下降新权重等于旧权重减学习率乘梯度。没有动量没有自适应学习率没有L2正则。这份代码故意保持原始就是想让读者看清BP最底层的数学形状。训练时batch_size取64整个60000张训练图会被分成938个小批次每处理完一批就更新一次参数这比全量梯度下降收敛快又比纯随机梯度下降稳定。4. 运行与复现从命令行训练到单张图片推理4.1 环境准备其实只需要一个NumPy很多项目光依赖就能劝退一大部分人但这个项目不会。依赖严格来说只有一个NumPyPython版本3.6以上都行因为代码里没有用到任何新版本语法特性。安装环境我一般这样做# 创建虚拟环境避免污染全局Python python -m venv bp-mnist-env source bp-mnist-env/bin/activate pip install numpy如果电脑上已经有了NumPy跳过虚拟环境直接跑也没问题。这个项目不依赖GPU、不依赖CUDA、不依赖深度学习框架纯CPU就能在几分钟内完成训练。我手边的笔记本是普通集成显卡的办公本训练40个epoch大概耗时3分钟完全在可接受范围内。训练前需要准备MNIST数据集。代码默认从当前目录下的data/文件夹读取四个文件train-images-idx3-ubyte、train-labels-idx1-ubyte、t10k-images-idx3-ubyte、t10k-labels-idx1-ubyte。如果本地没有数据集常见做法是写一个下载脚本从MNIST官网或者镜像站拉取也可以直接把官方提供的.gz文件解压后放到data/目录。注意文件名的后缀要保持一致data_loader里是按固定文件名去open的。4.2 训练超参详解学习率、隐藏层大小、批次与epoch怎么配训练脚本里有一组超参数它们共同决定了训练效果和收敛速度。我把这个项目的默认配置整理成了表格方便对照着改参数名默认值作用调整建议learning_rate0.1控制每次参数更新的步长太大容易震荡太小收敛慢0.05到0.3之间优先尝试hidden_size128隐藏层神经元数量64到256之间过大会过拟合batch_size64每次迭代使用的样本数32到128之间越大越平滑但越慢epochs40完整遍历训练集的次数30到50超过50准确率提升不明显input_size784由图片尺寸决定不要改28x28灰度图固定值实际上学习率是这里面最值得优先调的参数。初次跑通建议用默认的0.1观察前几个epoch的损失变化如果损失快速下降说明学习率还算合适如果损失忽高忽低上下横跳说明步长太大改到0.05或者0.03如果损失下降像蜗牛爬说明步长太小改到0.2试试。隐藏层大小决定网络的表达能力128是平衡点改太小准确率会明显下降改太大会在训练集上拟合得很好但测试集上表现变差。批次大小也很有意思。64这个值在工程上是安全选择它让每个epoch的参数更新次数在900次左右。如果改成1就是纯随机梯度下降收敛会非常颠簸如果改成60000就成了全量梯度下降每epoch只更新一次训练时间拉长对MNIST这种小数据集有点大材小用。4.3 效果验证从损失曲线到单张图片推理训练主循环的逻辑很直白外层套epoch内层把数据按batch_size切片每片调用一次forward和backward并记录损失值和准确率。代码骨架大致是这个样子def train(net, images, labels, epochs40, batch_size64): num_samples images.shape[0] for epoch in range(epochs): # 每个epoch重新打乱数据顺序避免网络记住固定序列 indices np.random.permutation(num_samples) epoch_loss 0.0 for start in range(0, num_samples, batch_size): batch_idx indices[start:start batch_size] x_batch images[batch_idx] y_batch labels[batch_idx] # 前向传播后计算交叉熵损失 probs net.forward(x_batch) loss -np.mean(np.sum(y_batch * np.log(probs 1e-8), axis1)) epoch_loss loss # 反向传播更新参数 net.backward(x_batch, y_batch) # 每个epoch结束打印一次平均损失便于观察收敛趋势 avg_loss epoch_loss / (num_samples // batch_size) print(fEpoch {epoch 1}/{epochs}, Loss: {avg_loss:.4f})交叉熵损失里加了一个1e-8的微小常数这是为了防止np.log(0)导致运算结果是负无穷。训练过程中每一轮的损失值很能说明问题正常情况第一个epoch损失在2.3左右然后逐步下降到20个epoch左右降到0.15以下准确率在95%到97%之间徘徊。如果损失曲线是平的或者反弹多半是前面说的学习率或初始化问题。评估阶段更直观。加载保存的模型参数后把测试集10000张图全部走一遍前向传播预测类别取概率最大的索引然后和真实标签比较def evaluate(net, images, labels): probs net.forward(images) predictions np.argmax(probs, axis1) # labels是one-hot格式还原成数字索引后再比较 true_labels np.argmax(labels, axis1) accuracy np.mean(predictions true_labels) return accuracy, predictions, true_labels这个项目的默认配置跑出来的准确率通常在97%左右手动调好超参后能到97.5%以上。想看到更直观的效果可以用matplotlib把测试集的图片和预测结果画在一起图片下方标明“预测: 7, 真实: 7”你会看到绝大多数图片都被正确识别。随机抽几张预测错误的样本看看会发现基本都是人眼也很难分清的手写体比如潦草的7和9、写得过窄的4和9——这时候你会觉得97%的准确率其实已经相当能打了。5. 避坑复盘BP网络训练MNIST最容易翻车的5个时刻5.1 损失从一个很大的值开始然后一路狂奔有个现象我见得太多了训练刚开始损失就在几百几千甚至上亿打印出来的都是科学计数法。这种爆炸式开局基本可以断定是权重初始化出了问题。如果np.random.randn没有乘0.01784维输入加权求和后落入sigmoid饱和区输出层的交叉熵很大梯度却因为饱和几乎为零参数更新不动损失就僵持在一个离谱的大数值上。解决方式是回到权重初始化那一步压低初始权重的标准差。常见做法是乘0.01也有人用Xavier初始化即除以sqrt(input_size)。对于三层这个小规模网络0.01的经验值已经足够。如果换用ReLU这类激活函数初始化策略又不一样到时候需要重新排查。5.2 损失卡在2.3附近纹丝不动2.3这个数字很有辨识度它正好是随机猜测10个类别时的交叉熵损失即-ln(0.1)。损失卡在这里意味着网络什么都没学到输出概率均匀分布在10个类别上。原因往往不是训练本身而是数据预处理环节断了链——可能图像没有归一化输入数值过大导致sigmoid全饱和可能标签没有做one-hot编码backward里self.a2 - y_true的形状对不上。排查时先在小批数据上验证数据加载打印images.shape和labels.shape确认一个是(batch, 784)一个是(batch, 10)再看images的数值范围应该全部落在[0, 1]之间。这两点确认无误后用单个样本跑一遍forward如果输出层10个值有差异说明前向传播是通的问题就在反向传播的梯度上。5.3 训练一切正常但测试集准确率只有85%上下再也上不去这种“差一口气”的局面大概率不是程序写错了而是网络表达能力有限。三层隐藏层只放128个神经元能学到的特征模式确实有限。MNIST手写识别里很多数字的区分需要中高层特征比如弧线、横竖笔画的组合隐藏层太少就消化不了这些模式。我的建议是先别急着加层数把隐藏层从128调到256跑一次观察测试集准确率是否跨过95%。如果提高了说明是容量问题如果没变化再看是不是学习率太大导致后期在最优点附近震荡。隐藏层调大后注意训练时间会变长epoch数也可以相应增加几个。5.4 同一份代码三次训练三次结果一行代码没改今天跑97.1%明天跑96.8%后天又变成97.3%。这种波动让很多人误以为代码有bug其实只是随机种子的问题。数据打乱是随机数权重初始化也是随机数不同运行之间的初始状态不同最终收敛到的局部最优自然不同。修起来很简单在network.py的__init__里显式设置np.random.seed(42)在训练循环每次打乱数据前也固定一下np.random.seed(epoch)。如果希望每个实验都能精确复现还可以记录下每次训练所用的seed值和超参数组合。5.5 打印损失时出现nan程序没报错但结果全废nan是最让人头大的症状因为程序不会崩溃只会安静地输出无效结果。根本原因是数值溢出softmax里np.exp(x)在x很大的时候会得到无穷大inf/inf就变成了nan。输出层的线性组合如果没有做数值稳定处理前向传播的结果很容易出现极端值。解决方式已经在前面展示过softmax里先减掉每行的最大值再求exp和归一化。这一步能保证最大的指数项变成exp(0)1不会被数值上限炸掉。交叉熵损失里的1e-8也是同类防御措施。遇到nan先检查这两处顺带看一下学习率是不是调到了0.5以上。6. 二次开发改造把MNIST手写数字扩展到自定义图像识别跑通这个项目之后我对它的定位已经不再是“交作业用的demo”而是一个可以持续改造的骨架。接下来分享一下我的改进路径核心思路是让数据加载接口和网络结构解耦。第一步是替换数据来源。MNIST的data_loader写死了28x28和IDX格式如果想让模型认自己手写的数字需要接一个图像预处理管道。常见做法是用OpenCV把任意尺寸的图片转成灰度、缩放成28x28、二值化然后拉平成784维向量。这样训练出来的模型可以直接吃摄像头拍的手写数字图片而不是只能跑MNIST官方数据。第二步是让网络结构可配置化。MNIST输入784固定无法改变但隐藏层数量和层数可以做成参数。我的做法是将hidden_size改成列表初始化时按列表长度循环创建权重矩阵前向传播和反向传播用逐层循环实现。这样从三层BP扩展到多层只需要改一行配置不需要动训练逻辑。改造后的__init__大概长这样def __init__(self, layer_sizes, lr0.1): self.lr lr self.layers len(layer_sizes) - 1 self.weights [] self.biases [] # 逐层创建权重layer_sizes定义每一层的神经元数量 for i in range(self.layers): w np.random.randn(layer_sizes[i], layer_sizes[i 1]) * 0.01 b np.zeros((1, layer_sizes[i 1]), dtypenp.float32) self.weights.append(w) self.biases.append(b)前向传播和反向传播也要同步改成循环结构但核心数学公式不变。这一步改造完成后替换layer_sizes[784, 128, 64, 10]就可以得到四层网络体验不同深度对准确率的影响。验证改造是否成功我的习惯是用同一组训练集跑一遍准确率不应该比原来低否则说明反向传播循环里某个维度算错了检查每一层的权重形状是否与设计一致。第三步是增加评估维度。原始项目只输出一个准确率数字我把评估部分扩展成了混淆矩阵和分类报告哪些数字经常被认错一目了然。实际看下来最常见的是4和9、3和8这类形状相近的数字互相混淆这也为下一步数据增强提供了方向对训练数据做轻微的旋转、平移和加噪能有效降低这类混淆。从那以后我每次改造完模型都强制走一遍完整流程数据预处理验证、网络结构检查、训练损失曲线观察、测试集多维评估。这个习惯帮我改出了从手写数字到印刷体数字、再到简单字符分类的多个版本每一版都能稳定复现当时的效果。这个项目最大的价值不是那97%的准确率而是让你亲手摸到BP神经网络的每一根线后面再去用深度学习框架你会更清楚框架里包装的是什么东西。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

蒙特卡罗遇上风电功率预测:Kmeans场景划分生成置信区间

蒙特卡罗遇上风电功率预测:Kmeans场景划分生成置信区间

蒙特卡罗遇上风电功率预测:从Kmeans场景划分到不确定性仿真风电功率预测这几年越来越不好做了。原因不复杂:风机越来越多、场站越来越大,电网调度那边对预测精度的要求已经从“大概估个数”升级到“你给我一个区间,告诉我最乐观和…

2026/10/16 2:20:51 阅读更多 →
C++乒乓球游戏:双缓存防闪屏与状态机驱动的图形学实战

C++乒乓球游戏:双缓存防闪屏与状态机驱动的图形学实战

简介:这是一款基于C开发的桌面级乒乓球对战游戏,面向编程初学者与图形界面实践者,聚焦双缓存绘图优化、人机AI逻辑与本地PVP交互等核心知识点,适用于课程设计、算法可视化练习及小型游戏开发入门。资源包共52个文件,含…

2026/10/16 2:20:51 阅读更多 →
Ryujinx Switch 模拟器入门指南:三步在 PC 上跑起 Nintendo Switch 游戏

Ryujinx Switch 模拟器入门指南:三步在 PC 上跑起 Nintendo Switch 游戏

Ryujinx Switch 模拟器入门指南:三步在 PC 上跑起 Nintendo Switch 游戏 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx 想在不开主机的情况下,用 PC 玩 Switch…

2026/10/16 2:19:51 阅读更多 →

最新新闻

零基础学网络爬虫:从HTTP请求到数据采集实战全攻略

零基础学网络爬虫:从HTTP请求到数据采集实战全攻略

最近总有朋友问我:“零基础学网络爬虫到底怎么入手?网上教程一大堆,不是上来就讲正则,就是直接甩框架,看完更懵。”这个提问我很能理解。爬虫确实是个“看起来门槛低、实际坑不少”的方向,如果只是照着代码…

2026/10/17 8:50:26 阅读更多 →
【Bug已解决】How to fine tune BERT on unlabeled data? 解决方案

【Bug已解决】How to fine tune BERT on unlabeled data? 解决方案

【Bug已解决】How to fine tune BERT on unlabeled data? 解决方案问题标签:BERT、无标签数据、微调、半监督学习、自监督学习、领域适应适用版本:Transformers 4.x、PyTorch 1.8难度等级:⭐⭐⭐⭐(较高)一、问题描述…

2026/10/17 8:50:26 阅读更多 →
OSPF区域外路由计算与FA转发地址机制深度解析

OSPF区域外路由计算与FA转发地址机制深度解析

1. 从一个"路由表里看不到下一跳"的怪现象说起做网络这行的朋友,估计都遇到过这种让人挠头的场景:明明区域边界路由器已经把外部路由引入进来了,show ip route也能看到这条路由,但下一跳那一栏显示的却是一个"看起…

2026/10/17 8:50:26 阅读更多 →
【Bug已解决】How to use groups parameter in PyTorch conv2d function 解决方案

【Bug已解决】How to use groups parameter in PyTorch conv2d function 解决方案

【Bug已解决】How to use groups parameter in PyTorch conv2d function 解决方案问题标签:PyTorch、Conv2d、groups参数、分组卷积、深度可分离卷积适用版本:PyTorch 1.0 ~ 2.x难度等级:⭐⭐⭐(中等)一、问题描述 在 …

2026/10/17 8:50:26 阅读更多 →
2026 AIGC平台实战测评:从文本、图像到视频的降本增效选型指南

2026 AIGC平台实战测评:从文本、图像到视频的降本增效选型指南

2026年聊AIGC平台测评,这个标题最近在我后台被问炸了——不少朋友发的其实是“降”,大概是输入法把“聊”吞了。不过我倒是觉得“降”这个字误打误撞点到了要害:眼下的AI工具测评,真正该聊的就是“降本增效”。市面上的AIGC平台已…

2026/10/17 8:50:26 阅读更多 →
C++ libcurl工业级实战:断点续传、进度回调与SSL证书校验

C++ libcurl工业级实战:断点续传、进度回调与SSL证书校验

简介:本资源是一套面向C中高级开发者与跨平台网络编程学习者的实战项目包,聚焦libcurl在HTTP文件上传与下载场景下的工程化应用,并配套Node.js服务端用于端到端功能验证。资源共872个文件,涵盖227个JavaScript服务端逻辑文件、128…

2026/10/17 8:49:26 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →