简介面向深度学习初学者的简单全连接神经网络Python实现聚焦多层感知器的底层搭建原理适合想避开TensorFlow/PyTorch、通过numpy理解前向传播、反向传播与梯度更新等核心机制的读者。资源为zip压缩包共7个Python文件、体积约4KB代码涵盖网络定义、权重初始化、激活函数、前向传播、损失计算、反向传播、训练入口与预测脚本模块划分清晰可独立运行。已有4211人学习/下载。实现从sigmoid激活、均方误差、链式法则梯度更新到多轮迭代训练逐层展开并演示了训练流程与预测调用读者可修改数据规模、隐藏层维度与学习率直观观察损失变化与预测效果进而快速构建一个可运行的简单全连接网络。同时提供数据生成与公共配置辅助脚本方便替换数据集、调整网络参数训练时打印损失值有助于理解收敛过程为后续深入学习深度学习框架打下基础。1. 全连接神经网络不借框架用手写代码搞懂每一行很多人在网上搜python神经网络搜到的全是PyTorch、Keras两三行搭完模型的教程。模型是跑通了但问一句反向传播到底在更新什么十个人里九个含糊。我这篇笔记要拆的这份资源是不借助任何深度学习框架、纯用Python和numpy手写全连接神经网络FNN的完整实现。它解决的是黑匣子问题——让你能逐行看懂前向传播、反向传播、梯度更新而不是调包调参。适合两类人一类是刚入门、想真正理解神经网络原理的初学者另一类是已经会用框架、但总觉得哪里差了点火候想把手底下的数学推导补扎实的从业者。阅读之前先确认你的环境Python 3.8以上、numpy、matplotlib即可不建议用更高版本Python个别语法会有兼容差异。2. 从零构建全连接层前向传播与激活函数选型2.1 全连接层的矩阵运算本质全连接层的计算规律可以用一句话概括输入向量与权重矩阵相乘加上偏置再过一个激活函数。用数学式子写就是h f(XW b)。但真正写代码时有两个细节特别容易绊倒新手——矩阵形状和批量数据的维度。这份资源的代码里输入 X 的shape是(batch_size, input_dim)权重 W 的shape是(input_dim, output_dim)偏置 b 是(output_dim,)。矩阵相乘结果是(batch_size, output_dim)。很多初学者把 W 的维度搞反写成(output_dim, input_dim)结果就是维度报错或者更隐蔽的问题是转置来转置去逻辑全乱。class FullyConnectedLayer: def __init__(self, input_dim, output_dim, activationrelu): # 权重初始化用He初始化适合ReLU家族激活函数 self.W np.random.randn(input_dim, output_dim) * np.sqrt(2.0 / input_dim) self.b np.zeros(output_dim) self.activation activation self.input None self.z None # 线性输出反向传播要用 def forward(self, x): self.input x self.z np.dot(x, self.W) self.b if self.activation relu: self.output np.maximum(0, self.z) elif self.activation sigmoid: self.output 1 / (1 np.exp(-self.z)) elif self.activation tanh: self.output np.tanh(self.z) elif self.activation linear: self.output self.z return self.output逻辑说明forward方法接收上一层传入的数据先做线性变换xW b然后把结果按激活函数类型做非线性映射。self.input和self.z必须存下来因为反向传播计算梯度时需要用到前向传播时的中间结果。参数说明input_dim是上一层神经元的个数output_dim是当前层神经元个数activation是激活函数的字符串标识。这里用np.random.randn生成标准正态分布随机数再乘上np.sqrt(2.0 / input_dim)——这个系数就是 He 初始化的关键目的是让每一层的输出方差保持稳定不会随层数加深而爆炸或消失。2.2 激活函数怎么选sigmoid、tanh还是ReLU激活函数的选择直接决定网络能不能训练起来。sigmoid 在二分类输出层几乎是必须的因为它输出在 (0, 1) 区间天然对应概率tanh 输出在 (-1, 1)均值接近 0比 sigmoid 更容易收敛ReLU 是隐藏层的主力计算简单、梯度不衰减。但这三个函数各有各的坑。sigmoid 在输入绝对值很大时梯度趋近于 0层数一深梯度就消失ReLU 在输入为负数时梯度完全为 0某个神经元一旦死掉后续永远不会被激活。这份资源里给的解决方案是隐藏层用 ReLU输出层按任务选——回归任务用 linear二分类用 sigmoid多分类用 softmax后面单独实现。def softmax(logits): # 减去最大值防止指数溢出 shifted logits - np.max(logits, axis1, keepdimsTrue) exp_logits np.exp(shifted) return exp_logits / np.sum(exp_logits, axis1, keepdimsTrue)逻辑说明softmax 先把每个样本的 logits 减去该样本的最大值这是数值稳定的关键——如果不减exp(1000)会直接溢出成inf。然后逐行做指数运算并归一化输出每一行的和为 1。参数说明logits的 shape 是(batch_size, num_classes)axis1表示按行每个样本计算。这个函数在反向传播里配合交叉熵损失有特别好的性质下文会讲到。2.3 初始化方法与参数说明权重初始化是新手最容易忽略却影响巨大的细节。全零初始化是绝对禁忌——所有神经元变成对称的梯度更新后依然对称网络退化成单神经元。固定值初始化也不行会导致某些神经元永远无法被激活。这份资源里实现了几种常见初始化我一般会按层数选择单隐藏层用np.random.randn * 0.01就能跑因为层数浅、梯度可以自然传导两层以上的网络必须用 He 或 Xavier。He 初始化方差是2/input_dim适合 ReLUXavier 初始化方差是1/input_dim适合 sigmoid和tanh。如果搞混了最直接的症状是深层网络的损失在前期下降极慢甚至完全不动。3. 反向传播手推梯度与代码对表3.1 损失函数与梯度推导反向传播的核心是链式法则。拿一个三层的全连接网络举例输入层784维→ 隐藏层128神经元ReLU→ 输出层10神经元softmax损失函数用交叉熵。交叉熵损失的梯度有一个非常漂亮的性质如果输出层是 softmax损失对 logits 的梯度恰好等于(softmax输出 - onehot标签)。这个结论可以直接用不需要手动对每个分量求导。代码里我会把这一步写成注释防止自己三个月后忘了为什么。class CrossEntropyLoss: def forward(self, logits, labels): self.probs softmax(logits) batch_size logits.shape[0] # 取每个样本真实类别对应的概率加对数 correct_log_probs -np.log(self.probs[range(batch_size), labels] 1e-9) return np.mean(correct_log_probs) def backward(self, labels): batch_size labels.shape[0] grad self.probs.copy() grad[range(batch_size), labels] - 1 return grad / batch_size逻辑说明forward计算交叉熵损失1e-9是个小常数防止log(0)出现负无穷。backward实现了上面说的那个漂亮性质——梯度是 softmax 输出减去 onehot 标签然后除以批量大小做平均。参数说明logits是模型原始的线性输出labels是整数形式的类别标签shape 是(batch_size,)。grad / batch_size这里用的是平均梯度对应损失里np.mean的求平均操作两端必须保持一致否则训练会不稳定。3.2 反向传播的实现细节链式法则逐层回传反向传播的实现核心就一句话从损失开始逐层反推。每一层需要做两件事——算出当前层的权重梯度dW和偏置梯度db再算出传给上一层的梯度dx。def backward(self, grad_output, learning_rate): batch_size grad_output.shape[0] # 先过激活函数的导数 if self.activation relu: mask (self.z 0).astype(float) grad_z grad_output * mask elif self.activation sigmoid: sig 1 / (1 np.exp(-self.z)) grad_z grad_output * sig * (1 - sig) elif self.activation tanh: tanh_out np.tanh(self.z) grad_z grad_output * (1 - tanh_out ** 2) else: # linear grad_z grad_output # 权重梯度、偏置梯度、传给上一层的梯度 self.dW np.dot(self.input.T, grad_z) / batch_size self.db np.mean(grad_z, axis0) dx np.dot(grad_z, self.W.T) # 原地更新参数 self.W - learning_rate * self.dW self.b - learning_rate * self.db return dx逻辑说明mask (self.z 0)是 ReLU 的导数——大于 0 的地方导数是 1小于等于 0 的地方导数是 0。sigmoid 和 tanh 的导数都可以用输出值本身表示所以要先算一遍输出。dx是把当前层的梯度投影回输入空间让上一层能继续回传。参数说明grad_output是从后面一层传回来的梯度learning_rate是学习率。注意这里self.dW np.dot(self.input.T, grad_z) / batch_size——input.T是(input_dim, batch_size)乘以(batch_size, output_dim)得到(input_dim, output_dim)正好对齐 W 的shape。除以batch_size是因为损失用了平均梯度也要跟着平均。3.3 梯度检查验证反向传播的代码没写错反向传播一旦写错训练时表现为损失先降后升、或者完全乱跳。最烦的是这种错误不像维度报错那么明显。所以在正式训练前我会做一次数值梯度检查——用数值差分近似梯度和反向传播算出来的梯度对比如果相对误差在1e-6量级说明代码是对的。def numerical_gradient(layer, x, y, eps1e-7): grad_W np.zeros_like(layer.W) # 对权重矩阵每个元素做数值差分 for i in range(layer.W.shape[0]): for j in range(layer.W.shape[1]): W_plus layer.W.copy() W_minus layer.W.copy() W_plus[i, j] eps W_minus[i, j] - eps layer.W W_plus out_plus forward_pass(x, y) layer.W W_minus out_minus forward_pass(x, y) grad_W[i, j] (out_plus - out_minus) / (2 * eps) layer.W original_W # 恢复原权重 return grad_W逻辑说明数值梯度用中心差分公式(f(xeps) - f(x-eps)) / (2*eps)比单侧差分更精确。每次改一个权重元素重新做完整前向传播比较两个损失值差。参数说明eps取1e-7比较合适——太小会有浮点精度问题太大则近似误差大。注意用完后必须恢复layer.W这是新手最容易漏的忘了恢复会把权重污染掉。4. 训练手写数字识别完整流程与超参数调优4.1 数据准备从哪里拿、怎么预处理这份资源用的是 MNIST 手写数字数据集每张图是 28×28 像素的灰度图拉平就是 784 维向量标签是 0 到 9 的整数。数据集自带训练集 60000 张、测试集 10000 张。数据预处理有两个关键动作归一化和 onehot 编码。归一化把像素值从 0~255 压缩到 0~1直接除以 255.0 即可这一步对收敛速度影响极大——不做的话输入范围太大梯度更新会非常不稳定。onehot 编码是把整数标签转成 10 维向量虽然交叉熵损失直接用整数标签计算但如果你换成均方误差损失就必须用 onehot。def load_mnist_data(): # 假设已经从官网下载好四个.gz文件并放在data目录 with open(data/train-images.idx3-ubyte, rb) as f: magic struct.unpack(I, f.read(4))[0] num_images struct.unpack(I, f.read(4))[0] rows struct.unpack(I, f.read(4))[0] cols struct.unpack(I, f.read(4))[0] images np.frombuffer(f.read(), dtypenp.uint8).reshape(num_images, rows*cols) images images / 255.0 # 归一化到[0,1] with open(data/train-labels.idx1-ubyte, rb) as f: magic struct.unpack(I, f.read(4))[0] num_labels struct.unpack(I, f.read(4))[0] labels np.frombuffer(f.read(), dtypenp.uint8) return images, labels逻辑说明MNIST 的 idx 文件是二进制格式有固定的头部结构。struct.unpack(I, ...)按大端序读取 4 字节无符号整数跳过头部后剩余内容就是像素数据用np.frombuffer读成数组再 reshape。参数说明rows*cols是 784归一化用255.0而不是255是为了保持浮点除法。这套解析代码在很多数据集上通用还有一个坑是文件头偏移——train-images 是 16 字节头train-labels 是 8 字节头搞错了读出来的数据全是乱的训练准确率会停留在 10% 左右纯随机水平。4.2 训练循环与超参数设置learning_rate、epochs、batch_size训练循环是最容易写得松散的部分。全批量梯度下降每轮更新一次权重mini-batch 每轮更新多次。这份资源里默认用全批量代码结构清晰先跑通再改 mini-batch。def train(model, X_train, y_train, epochs50, learning_rate0.1): losses [] accuracies [] for epoch in range(epochs): # 前向传播 output model.forward(X_train) # 计算损失和准确率 loss model.loss_fn.forward(output, y_train) preds np.argmax(output, axis1) accuracy np.mean(preds y_train) # 反向传播 grad model.loss_fn.backward(y_train) model.backward(grad, learning_rate) losses.append(loss) accuracies.append(accuracy) print(fEpoch {epoch1}/{epochs}, Loss: {loss:.4f}, Acc: {accuracy:.4f}) return losses, accuracies逻辑说明每轮循环做一次完整的前向传播、损失计算、反向传播、参数更新。np.argmax取最大概率的类别索引作为预测结果和真实标签算准确率。参数说明learning_rate0.1对 MNIST 单隐藏层网络是个比较稳的起点。如果损失震荡改成 0.01如果每轮下降很慢可以试 0.5。注意这里用的是全批量建议先用 5000 张子集试跑一遍5 分钟能跑完再上全量。模型结构我用三层网络784→128→10隐藏层 ReLU。这个配置下50 轮训练准确率可以到 96% 以上不用任何正则化。但如果想上 98%需要加正则化和调优这个在第六章讲。4.3 验证集与过拟合的判断方法过拟合在你持续训练时会越来越明显训练准确率不断攀升验证准确率却停滞甚至下降。MNIST 因为数据比较干净单隐藏层网络不太容易过拟合但这个判断习惯必须养成。def train_with_validation(model, X_train, y_train, X_val, y_val, epochs50, learning_rate0.1): for epoch in range(epochs): output model.forward(X_train) train_loss model.loss_fn.forward(output, y_train) grad model.loss_fn.backward(y_train) model.backward(grad, learning_rate) # 每个epoch结束在验证集上做一次评估 val_output model.forward(X_val) val_preds np.argmax(val_output, axis1) val_acc np.mean(val_preds y_val) print(fEpoch {epoch1}: train_loss{train_loss:.4f}, fval_acc{val_acc:.4f}) # 早停连续5轮验证准确率无提升就中断 if epoch 5 and val_acc best_val_acc: patience - 1 if patience 0: print(Early stopping triggered.) break else: best_val_acc val_acc patience 5逻辑说明验证集每轮评估一次不参与梯度更新超参数也不会根据验证集调那是测试集的职责。早停逻辑是连续 5 轮没有新纪录就中断训练避免浪费算力。参数说明patience是早停的容忍轮数best_val_acc记录历史最佳准确率。注意val_output model.forward(X_val)时模型内部会覆盖self.input等缓存所以验证集前向传播必须放在反向传播之后否则会把训练状态污染掉。5. 常见问题与排查神经网络训练中的五个典型坑5.1 损失变成 NaN 或 inf现象训练几轮后损失打印变成nan重跑一遍可能一开始就是nan。原因最常见的是np.exp溢出。softmax 里如果logits出现 100 以上的值exp(100)直接爆掉。其次可能是学习率太大导致权重更新幅度过大梯度爆炸。解决分两步排查。先确认 softmax 里做了最大值偏移——看了代码里shifted logits - np.max(logits, axis1, keepdimsTrue)这能解决大部分溢出的 case。如果还是 nan把学习率调小两个数量级试试从 0.1 改到 0.001。我的习惯是每个 epoch 结束后检查 loss 是否有限用np.isfinite判断一旦发现异常就停止训练并打印关键中间变量不要硬跑完整轮。5.2 准确率一直停在 10% 左右现象准确率始终在 0.1 附近相当于随机乱猜。原因数据读取错误是头号嫌疑。MNIST 文件读错了字节偏移得到的是随机噪声。第二个常见原因是标签和特征没对齐——读图用了 train-images 文件读标签却用了 test-labels 文件两张表的顺序不一致。解决先用print(X_train[0].shape)确认数据维度是(784,)再打印前 10 个标签的独特性应该有 0~9 全出现最后用plt.imshow(X_train[0].reshape(28,28))可视化第一张图确认看起来像一个数字。我自己就犯过这个错花了整整两天——某个数据读取脚本里复制粘贴错了文件名准确率卡在 10%最后画了图才发现数据全乱。5.3 损失震荡完全不平滑现象损失曲线像锯齿一样上下乱跳或者前几轮下降明显但后面越来越剧烈。原因学习率过大。梯度计算没问题的时候过大的学习率会让权重在最优解附近来回穿越甚至越跳越远。另一个常见原因是批量大小和损失函数的平均操作不匹配——损失用了np.mean而梯度用了np.sum梯度方向被放大了 batch_size 倍。解决先把学习率降到 0.001 试跑 10 轮观察损失是否变平滑。如果平滑了就说明是学习率问题可以逐步调大。如果还没用检查backward里self.db np.mean(grad_z, axis0)和self.dW np.dot(self.input.T, grad_z) / batch_size是否保持一致。这两个操作一个用了 mean 一个用了除 batch_size数值上等价但容易在复制代码时只改一半。5.4 深层网络训练不动梯度消失的典型表现现象三层以内网络正常加到五六层后前几层几乎学不到东西准确率一直上不去。原因sigmoid 或 tanh 激活函数在深层网络里梯度逐层递减——每层最多乘 0.25十层以后梯度直接是0.25^10前几层的权重根本收不到有效梯度。解决把隐藏层全部换成 ReLU。ReLU 在正半轴导数是 1不会衰减梯度。如果已经用了 ReLU 还是很慢检查初始化——确认用的是 He 初始化np.sqrt(2.0 / input_dim)这个细节直接决定深层网络能不能训练起来。还有一个注意点最后一层如果做二分类用 sigmoid倒数第二层不要用 sigmoid这会让梯度在最后两个全连接层之间被打两次折扣。5.5 验证集评估污染训练状态现象验证集准确率忽高忽低而且训练几轮后验证准确率反而比训练准确率还高。原因一个非常隐蔽的坑——模型类里缓存了self.input和self.z如果验证集前向传播发生在反向传播之前这些缓存会被验证集数据覆盖。那么下一轮训练的梯度计算就会用验证集的数据导致梯度方向完全错误。解决固定前向传播和反向传播的执行顺序先backward后val_forward或者干脆每次反向传播前无视缓存、直接从当前输入计算梯度把self.input和self.z做成局部变量而不是实例属性。这个 bug 的症状非常迷惑人我遇到过两三次最后都是一行一行打日志才发现是缓存被覆盖了。6. 进阶技巧把玩具网络改造成真正能用的工具6.1 从全批量到 mini-batch三步改完训练速度提升一个量级全批量训练的缺点是每次更新都要看全部数据数据量大时内存开销大、收敛慢。改成 mini-batch 只需要三步打乱数据 → 切分批次 → 逐批更新权重。def train_minibatch(model, X_train, y_train, batch_size64, epochs10, learning_rate0.01): num_samples X_train.shape[0] num_batches num_samples // batch_size for epoch in range(epochs): # 每个epoch随机打乱数据避免批次间的顺序偏差 shuffle_idx np.random.permutation(num_samples) X_shuffled X_train[shuffle_idx] y_shuffled y_train[shuffle_idx] epoch_loss 0.0 for batch_idx in range(num_batches): start batch_idx * batch_size end start batch_size X_batch X_shuffled[start:end] y_batch y_shuffled[start:end] output model.forward(X_batch) loss model.loss_fn.forward(output, y_batch) grad model.loss_fn.backward(y_batch) model.backward(grad, learning_rate) epoch_loss loss print(fEpoch {epoch1}/{epochs}, Avg Loss: {epoch_loss / num_batches:.4f})逻辑说明np.random.permutation返回一个打乱的下标数组用它同时重排特征和标签保持对应关系。每个 batch 独立做前向、反向、更新一个 epoch 内的权重会更新num_batches次。参数说明batch_size取 32 或 64 比较合适。太大则梯度方向接近全批量、更新慢太小则梯度噪声大、损失曲线抖动明显。mini-batch 配合稍小的学习率0.01 左右通常比全批量更快收敛到相同精度。6.2 学习率衰减前半程大步学后半程小步精调固定学习率有个两难问题设大了前期学得快但后期震荡设小了前期收敛太慢。最简单的解决方式是指数衰减每过固定轮数把学习率乘一个衰减系数。def train_lr_decay(model, X_train, y_train, epochs30, initial_lr0.1, decay_interval10, decay_rate0.5): lr initial_lr for epoch in range(epochs): output model.forward(X_train) loss model.loss_fn.forward(output, y_train) grad model.loss_fn.backward(y_train) model.backward(grad, lr) if (epoch 1) % decay_interval 0: lr * decay_rate print(fEpoch {epoch1}: learning rate decayed to {lr:.5f})逻辑说明每decay_interval轮学习率乘以decay_rate逐渐变小。这样前半程用大步长快速跨过损失曲面的平坦区后半程用小步长精调避免在最优解附近反复横跳。参数说明decay_interval10表示每 10 轮衰减一次decay_rate0.5表示每次减半。这两个参数按训练时长调整——如果总共只训练 30 轮这个配置大概衰减两次如果训练 100 轮每 20 轮衰减 0.5 更合适。有一个反直觉的经验学习率衰减比使用更复杂优化器比如 Adam在数据集上更平滑尤其对小型网络而言Adam 反而容易把最优解附近的步长调得过于精细导致泛化性不如简单的衰减策略。6.3 正则化L2 让权重更均匀Dropout 让神经元更独立过拟合的本质是模型记住了训练集的噪声。两种最常用的手段L2 正则化在损失函数里加一项权重的平方和惩罚权重过大的神经元Dropout 在训练时随机丢弃一部分神经元的输出让网络不能依赖单个神经元的输出。class L2Regularization: def __init__(self, lambda_value0.001): self.lambda_value lambda_value def cost(self, model): reg_loss 0.0 for layer in model.layers: reg_loss np.sum(layer.W ** 2) return 0.5 * self.lambda_value * reg_loss def gradient(self, model): # L2的梯度是λW直接叠加到权重梯度上 for layer in model.layers: layer.dW self.lambda_value * layer.W逻辑说明L2 正则化的梯度等于lambda * W把它加到dW上再更新权重。直观来看每次更新都会对权重做一点缩水权重越大缩得越多于是网络倾向使用更多小权重而非少数大权重泛化性随之提升。参数说明lambda_value太小等于没加太大则欠拟合——训练准确率和验证准确率都低。我通常从0.01开始试每次除以 10观察验证集准确率的变化。Dropout 实现时注意推理阶段要关闭随机丢弃否则验证集的输出会被随机性干扰结果不稳定。边界说明这份资源适合处理中小规模数据、理解神经网络原理、快速验证想法。如果是真实业务场景下的海量数据和复杂模型还是用成熟框架更稳妥——手写的价值在于你把基础打透了用框架时心里有底。从那以后我每次训练任何模型都会把损失曲线和验证集准确率打印成一个文件强制完整走一遍先验证数据读取→再调学习率→再调结构→最后加正则的顺序不会跳过任何一步。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
GEF 逆向实战:用 pattern 命令基于 De Bruijn 序列定位溢出偏移量 网络安全开发工具 【免费下载链接】gef GEF (GDB Enhanced Features) - a modern experience for GDB with advanced debugging capabilities for exploit devs & reverse engineers on Linux 项目地址: https://gitcode.com/gh_mirrors/gef/gef 点击查看 免费下… · 2026/9/25 7:32:55
【windows】安装抓包工具Burp Suite 2024_10激活汉化 【windows】安装抓包工具Burp Suite 2024&激活&汉化
前言
在项目即将上线阶段,迈入生产环境之际,确保其安全性成为我们不可忽视的首要任务。为筑起一道坚不可摧的安全防线,我们借助业界公认的网络安全利器——Burp Suite,… · 2026/9/25 7:32:55
AI Agent工具链实战:CLI、MCP与OpenRouter集成指南 1. 从"treg"这个模糊词说起:它到底指什么第一次看到"treg"这个词,很多人会一头雾水。它不像"codex cli"或者"openrouter"那样有明确的指向,更像是一个被截断的缩写或者内部代号。结合热搜词里高频出… · 2026/9/25 7:32:49
SVM检测恶意URL:37维手工特征与线性核工程实践 简介:本资源是一套基于机器学习的恶意URL检测实战项目,面向计算机、人工智能、大数据等专业的本科生及初阶开发者,适用于课程设计、毕业设计与安全算法入门实践。项目完整实现从URL特征提取、模型训练(含SVM等经典算法)… · 2026/9/25 7:53:39
Atlas 300V 24G推理加速卡上高效部署YOLOv5全流程指南 先来说个真实经历。入职第二年接手了一个园区安防项目,甲方丢过来一批盒子,点名要跑YOLOv5做实时检测,厂家给的资料就一行字:Atlas 300V 24G推理卡。当时团队里没人碰过昇腾,第一反应是这卡到底能不能用来训练… · 2026/9/25 7:53:39
SQL注入绕过登录原理与防御:从拼接逻辑到实战靶场 第一次在 PortSwigger Academy 上做 SQL 注入绕过登录(Login Bypass)这个实验的时候,我其实有点不以为然。万能密码这东西听起来像十几年前的考古内容,总觉得在参数化查询、ORM 普及的今天,早就没什么实战价值了。但真… · 2026/9/25 7:53:39
Atlas 300V 24G NPU上部署YOLO:从环境配置到性能优化 最近有人问我“Atlas”是什么,说实话第一反应是数据库中间件那头大象,结果他后面跟了一句“部署YOLO”,又补了个“300V 24G”,我立马就明白他说的其实是昇腾Atlas系列的AI加速卡。这名字在AI领域有点被说烂了,因为它既… · 2026/9/25 7:53:33
昇腾Atlas 300V 24G加速卡部署YOLO全流程实战 1. 先搞清楚Atlas 300V 24G的定位:是加速卡,但不是你以为的那种加速卡1.1 一张卡解决什么问题看到热搜里连续出现“atlas部署yolo”和“atlas 300v 24g 是运算加速卡吗”这两条,我就知道又有一批做边缘AI或服务器推理的同学被这张卡吸引过来了… · 2026/9/25 7:53:27
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37