从零手搓AI工程:深入理解张量运算与自动微分
1. 从零手搓AI工程为什么我不建议你直接调包第一次看到“ai-engineering-from-scratch”这个标题我脑子里蹦出来的画面是一个刚入行的算法工程师打开Jupyter Notebookimport torch然后开始调nn.Transformer。这当然没错但如果你真的想搞明白AI工程到底是怎么回事从零开始造一遍轮子比调一百次包都管用。我做了十多年一线开发带过不少新人发现一个很普遍的现象很多人能跑通BERT微调但问他“注意力机制里那个除以根号d_k是干嘛的”他答不上来能部署一个推荐模型上线但问他“特征哈希冲突了怎么排查”他一脸茫然。这就是典型的“调包侠困境”——工具用得很溜底层逻辑一知半解一旦遇到线上诡异问题排查效率极低。“ai-engineering-from-scratch”这个项目核心思路就是用最朴素的方式把AI工程链路里的关键组件一个个手写出来。它不追求性能极致也不追求功能完备追求的是让你真正理解每一行代码背后的数学原理和工程取舍。适合谁看三类人一是刚转行AI、只会调库的新手想补底层功底二是工作两三年、遇到瓶颈的中级工程师想系统梳理知识体系三是对AI好奇的后端或数据工程师想搞清楚模型服务到底怎么跑起来的。这篇文章我会从整体设计思路、核心模块拆解、实操落地过程、常见坑排查四个维度把“从零构建AI工程”这件事讲透。每个部分都会给出可复现的代码片段和参数计算过程你跟着走一遍基本能自己搭出一个迷你版的AI工程框架。2. 整体设计思路为什么选择“手搓”而不是“调包”2.1 手搓的核心价值建立工程直觉很多人不理解明明有现成的PyTorch、TensorFlow、ONNX Runtime为什么还要费劲手写我举个例子你就明白了。假设你在线上服务里遇到一个诡异问题同一个模型离线评估AUC 0.85上线后AUC掉到0.72。你查了特征、查了数据分布、查了模型版本都没问题。这时候如果你对手写过的前向传播、梯度计算、数值稳定性有直觉你可能会想到是不是线上推理时某个归一化层的epsilon设置和训练时不一致或者浮点精度在批量推理时累积误差了这种直觉调包是调不出来的。只有你亲手实现过一遍知道每个算子在数值上是怎么处理的才能在出问题时快速定位。“ai-engineering-from-scratch”的设计哲学就是用最小依赖实现最大理解。整个项目只依赖NumPy和基础Python标准库连PyTorch都不用。为什么因为一旦引入深度学习框架很多底层细节就被封装了你就失去了观察数值流动的机会。2.2 模块划分与依赖关系整个项目我把它拆成五个核心模块依赖关系是单向的从下往上依次是模块层级模块名称核心职责依赖L1张量运算层实现基础张量操作、广播、矩阵乘法NumPyL2自动微分层实现计算图、反向传播、梯度累积L1L3神经网络层实现全连接、卷积、注意力等基础层L2L4训练循环层实现优化器、损失函数、学习率调度L3L5推理服务层实现模型序列化、批处理、HTTP服务L4这个分层不是拍脑袋定的而是按照依赖倒置原则上层依赖下层的抽象接口下层不知道上层的存在。这样做的好处是你可以在任意一层做替换或扩展而不影响其他层。比如你想把L1的张量运算换成CuPy来加速只要接口一致上层代码一行不用改。2.3 为什么不用现成框架做对比有人可能会问你手搓的这些东西性能肯定不如PyTorch那有什么意义意义在于理解成本和调试透明度。PyTorch的一个nn.Linear背后有几千行C和CUDA代码你出问题了只能靠猜。而你手写的Linear层只有几十行Python每一行你都能打断点、打日志、看数值。我实测过用NumPy手写一个两层MLP在MNIST上训练到98%准确率代码量大概300行训练时间比PyTorch慢5到8倍。但这300行代码里包含了前向传播、反向传播、梯度更新、损失计算的全部细节。你把这300行吃透再去看PyTorch的源码会有一种“原来如此”的通透感。注意手搓的目的是学习不是替代生产框架。实际项目中该用PyTorch还是用PyTorch但你要知道它背后发生了什么。3. 核心细节解析从张量到梯度的完整链路3.1 张量运算层广播机制与内存布局张量是AI工程的原子单元。很多人用NumPy的广播机制用得很顺手但问他“为什么(3,1)和(1,4)能广播成(3,4)”他可能说不清楚。广播的本质是维度对齐从右往左逐维比较要么相等要么其中一个为1要么其中一个不存在。手写张量层时我建议你先实现一个Tensor类内部持有一个NumPy数组然后实现__add__、__mul__、__matmul__等魔术方法。关键点在于梯度传播时的维度还原。举个例子import numpy as np class Tensor: def __init__(self, data, requires_gradFalse): self.data np.asarray(data, dtypenp.float32) self.requires_grad requires_grad self.grad None self._backward lambda: None def __add__(self, other): other other if isinstance(other, Tensor) else Tensor(other) out Tensor(self.data other.data, self.requires_grad or other.requires_grad) def _backward(): if self.requires_grad: grad out.grad # 处理广播如果self的维度比out少需要对grad求和 while grad.ndim self.data.ndim: grad grad.sum(axis0) for i, dim in enumerate(self.data.shape): if dim 1: grad grad.sum(axisi, keepdimsTrue) self.grad grad if self.grad is None else self.grad grad if other.requires_grad: grad out.grad while grad.ndim other.data.ndim: grad grad.sum(axis0) for i, dim in enumerate(other.data.shape): if dim 1: grad grad.sum(axisi, keepdimsTrue) other.grad grad if other.grad is None else other.grad grad out._backward _backward return out这段代码里最容易被忽略的是广播还原。当你做(3,1) (1,4)时输出是(3,4)反向传播时梯度要分别还原成(3,1)和(1,4)。还原规则是先把多出来的维度求和掉再把大小为1的维度保持住求和。这个逻辑不写对训练时梯度形状对不上直接报错。内存布局方面NumPy默认是C顺序行优先但矩阵乘法时如果转成Fortran顺序列优先可能更快。我实测下来对于小矩阵小于1000x1000C顺序和Fortran顺序差异不大对于大矩阵Fortran顺序在BLAS里可能有优势。但手搓阶段不用太纠结先保证正确性。3.2 自动微分层计算图与拓扑排序自动微分是深度学习框架的灵魂。手写自动微分核心是计算图和反向拓扑排序。每个Tensor节点记录它是怎么来的_backward函数反向传播时从损失节点开始按拓扑逆序依次调用_backward。拓扑排序的实现方式有两种一种是显式建图用DFS或BFS排序另一种是隐式递归利用Python的调用栈。我推荐显式建图因为调试更方便。具体做法是def backward(self): # 拓扑排序 topo [] visited set() def build_topo(v): if v not in visited: visited.add(v) for child in v._prev: build_topo(child) topo.append(v) build_topo(self) # 反向传播 self.grad np.ones_like(self.data) for v in reversed(topo): v._backward()这里有个坑梯度累积。如果你不做梯度清零多次反向传播会累加梯度。所以在训练循环里每次loss.backward()之前要手动把所有参数的grad置零。PyTorch的optimizer.zero_grad()就是干这个的。另一个坑是原地操作。如果你在计算图中用了或*会破坏计算图的历史记录导致梯度计算错误。手搓阶段所有操作都返回新Tensor不要原地修改。3.3 神经网络层全连接与注意力的手写实现全连接层是最简单的y xW b反向传播就是矩阵乘法。但要注意初始化。很多人随便用np.random.randn初始化结果训练不动。正确的做法是Xavier初始化或He初始化Xavierstd sqrt(2 / (fan_in fan_out))Hestd sqrt(2 / fan_in)为什么因为如果初始化方差太大前向传播时激活值会爆炸方差太小激活值会消失。Xavier和He是根据输入输出维度自动调整方差保证每层输出的方差大致一致。注意力机制稍微复杂一点。核心公式是Attention(Q,K,V) softmax(QK^T / sqrt(d_k))V。手写时要注意缩放因子除以sqrt(d_k)是为了防止点积结果过大导致softmax梯度消失。你可以做个实验不加缩放当d_k512时点积结果可能在几百到几千softmax后几乎变成one-hot梯度接近零。Mask处理如果是自回归生成需要上三角Mask把未来位置的注意力分数设为负无穷。注意是负无穷不是零因为softmax后零会变成均匀分布。数值稳定性softmax实现时要先减去最大值防止指数溢出。这个技巧在手写时一定要加上。def softmax(x, axis-1): x_max np.max(x, axisaxis, keepdimsTrue) exp_x np.exp(x - x_max) return exp_x / np.sum(exp_x, axisaxis, keepdimsTrue)3.4 训练循环层优化器与学习率调度优化器我建议从SGD开始手写然后扩展到Momentum、RMSProp、Adam。SGD的更新公式是param - lr * grad。Momentum引入速度项v beta * v grad; param - lr * v。Adam结合了Momentum和RMSProp还要做偏差修正。手写Adam时偏差修正是最容易漏的。因为初始时一阶矩和二阶矩都是零直接除会导致更新量过大。修正公式是m_hat m / (1 - beta1 ** t) v_hat v / (1 - beta2 ** t) param - lr * m_hat / (np.sqrt(v_hat) eps)学习率调度我常用的是余弦退火和阶梯下降。余弦退火适合长时间训练阶梯下降适合快速实验。手写时注意记录当前step然后按公式计算lr。4. 实操过程从零搭建一个迷你AI工程框架4.1 环境准备与依赖安装环境很简单只需要Python 3.8和NumPy。我建议用虚拟环境避免污染全局包python -m venv ai_from_scratch source ai_from_scratch/bin/activate # Linux/Mac # 或 ai_from_scratch\Scripts\activate # Windows pip install numpy matplotlibMatplotlib用来画损失曲线不是必须的但调试时很有用。整个项目不需要GPUCPU就能跑。我实测在MacBook Pro M1上训练一个两层MLP跑MNIST10个epoch大概3分钟。4.2 张量类的完整实现我把张量类的核心代码整理如下包含了加法、乘法、矩阵乘法、ReLU、Softmax等常用操作class Tensor: def __init__(self, data, requires_gradFalse, _children(), _op): self.data np.asarray(data, dtypenp.float32) self.requires_grad requires_grad self.grad None self._backward lambda: None self._prev set(_children) self._op _op def __add__(self, other): other other if isinstance(other, Tensor) else Tensor(other) out Tensor(self.data other.data, self.requires_grad or other.requires_grad, (self, other), ) def _backward(): if self.requires_grad: grad out.grad while grad.ndim self.data.ndim: grad grad.sum(axis0) for i, dim in enumerate(self.data.shape): if dim 1: grad grad.sum(axisi, keepdimsTrue) self.grad grad if self.grad is None else self.grad grad if other.requires_grad: grad out.grad while grad.ndim other.data.ndim: grad grad.sum(axis0) for i, dim in enumerate(other.data.shape): if dim 1: grad grad.sum(axisi, keepdimsTrue) other.grad grad if other.grad is None else other.grad grad out._backward _backward return out def __matmul__(self, other): other other if isinstance(other, Tensor) else Tensor(other) out Tensor(self.data other.data, self.requires_grad or other.requires_grad, (self, other), ) def _backward(): if self.requires_grad: grad out.grad other.data.T self.grad grad if self.grad is None else self.grad grad if other.requires_grad: grad self.data.T out.grad other.grad grad if other.grad is None else other.grad grad out._backward _backward return out def relu(self): out Tensor(np.maximum(0, self.data), self.requires_grad, (self,), relu) def _backward(): if self.requires_grad: grad out.grad * (self.data 0).astype(np.float32) self.grad grad if self.grad is None else self.grad grad out._backward _backward return out def backward(self): topo [] visited set() def build_topo(v): if v not in visited: visited.add(v) for child in v._prev: build_topo(child) topo.append(v) build_topo(self) self.grad np.ones_like(self.data) for v in reversed(topo): v._backward()这段代码里_prev记录了计算图的父节点_backward是反向传播函数。backward()方法先做拓扑排序然后从输出节点开始反向调用。4.3 全连接层与MLP的搭建基于Tensor类全连接层可以这样实现class Linear: def __init__(self, in_features, out_features): # He初始化 std np.sqrt(2.0 / in_features) self.weight Tensor(np.random.randn(in_features, out_features) * std, requires_gradTrue) self.bias Tensor(np.zeros(out_features), requires_gradTrue) def __call__(self, x): return x self.weight self.bias def parameters(self): return [self.weight, self.bias] class MLP: def __init__(self, sizes): self.layers [] for i in range(len(sizes) - 1): self.layers.append(Linear(sizes[i], sizes[i1])) def __call__(self, x): for i, layer in enumerate(self.layers): x layer(x) if i len(self.layers) - 1: x x.relu() return x def parameters(self): params [] for layer in self.layers: params.extend(layer.parameters()) return params这里注意最后一层不加ReLU因为要做分类需要原始logits。损失函数用交叉熵手写时要注意数值稳定性def cross_entropy(logits, labels): # logits: (batch, num_classes) # labels: (batch,) logits_max np.max(logits.data, axis1, keepdimsTrue) exp_logits np.exp(logits.data - logits_max) probs exp_logits / np.sum(exp_logits, axis1, keepdimsTrue) loss -np.log(probs[np.arange(len(labels)), labels] 1e-8).mean() return Tensor(loss)4.4 训练循环与参数更新训练循环的骨架如下def train(model, X, y, epochs10, lr0.01, batch_size32): params model.parameters() losses [] for epoch in range(epochs): # 打乱数据 indices np.random.permutation(len(X)) X_shuffled X[indices] y_shuffled y[indices] epoch_loss 0 for i in range(0, len(X), batch_size): X_batch Tensor(X_shuffled[i:ibatch_size]) y_batch y_shuffled[i:ibatch_size] # 前向传播 logits model(X_batch) loss cross_entropy(logits, y_batch) # 反向传播 for p in params: p.grad None loss.backward() # 参数更新 for p in params: p.data - lr * p.grad epoch_loss loss.data avg_loss epoch_loss / (len(X) // batch_size) losses.append(avg_loss) print(fEpoch {epoch1}, Loss: {avg_loss:.4f}) return losses这里有几个关键点一是梯度清零每次反向传播前要把所有参数的grad置None二是数据打乱防止模型学到样本顺序三是批处理小批量梯度下降比全批量收敛更快。4.5 模型保存与推理服务训练完成后模型保存很简单把参数存成NumPy的.npz文件def save_model(model, path): params {} for i, layer in enumerate(model.layers): params[fweight_{i}] layer.weight.data params[fbias_{i}] layer.bias.data np.savez(path, **params) def load_model(model, path): data np.load(path) for i, layer in enumerate(model.layers): layer.weight.data data[fweight_{i}] layer.bias.data data[fbias_{i}]推理服务可以用Flask或FastAPI搭一个简单的HTTP接口。我习惯用FastAPI因为自带文档和类型校验from fastapi import FastAPI from pydantic import BaseModel import numpy as np app FastAPI() model MLP([784, 128, 10]) load_model(model, model.npz) class InputData(BaseModel): features: list app.post(/predict) def predict(data: InputData): x Tensor(np.array(data.features).reshape(1, -1)) logits model(x) pred int(np.argmax(logits.data, axis1)[0]) return {prediction: pred}这个服务每秒能处理几百个请求对于学习项目足够了。生产环境要考虑批处理、异步、模型热更新等那是另一个话题。5. 常见问题与排查技巧实录5.1 梯度爆炸与消失的排查思路梯度爆炸和消失是手搓框架时最常见的问题。表现是损失变成NaN或者损失完全不下降。排查步骤检查初始化打印每层权重的均值和方差如果方差远大于1或远小于1说明初始化有问题。用He或Xavier初始化。检查激活函数ReLU在负半轴梯度为零如果大部分神经元输出为零梯度就消失了。可以换成LeakyReLU或ELU。检查学习率学习率太大会导致梯度爆炸太小会导致不收敛。我一般从0.001开始试如果损失震荡就调小如果损失下降太慢就调大。梯度裁剪如果梯度范数超过阈值就按比例缩放。这个技巧在RNN和Transformer里很常用。def clip_gradients(params, max_norm1.0): total_norm np.sqrt(sum(np.sum(p.grad ** 2) for p in params)) if total_norm max_norm: scale max_norm / (total_norm 1e-6) for p in params: p.grad * scale5.2 数值稳定性问题速查表问题现象可能原因解决方法损失变成NaN指数溢出、除零、log(0)softmax减最大值、加epsilon损失不下降学习率太小、初始化太差调大学习率、换初始化损失震荡学习率太大、批大小太小调小学习率、增大批大小梯度为零ReLU死亡、梯度消失换激活函数、用残差连接梯度形状不匹配广播还原错误检查反向传播的sum维度训练集准确率高但测试集低过拟合加正则化、Dropout、早停5.3 手搓框架的独家避坑技巧第一个坑NumPy的默认浮点类型是float64而深度学习通常用float32。如果你不显式指定dtype内存占用会翻倍计算速度也会慢。我在Tensor类的__init__里强制dtypenp.float32这个习惯帮我省了不少内存。第二个坑Python的循环比NumPy的向量化慢几十倍。手搓时尽量用向量化操作避免逐元素循环。比如计算准确率不要用for循环遍历每个样本直接用np.mean(np.argmax(logits, axis1) labels)。第三个坑随机种子。调试时一定要固定随机种子否则每次运行结果不一样你没法判断是代码改了还是随机性导致的。np.random.seed(42)放在所有随机操作之前。第四个坑内存泄漏。如果你在训练循环里不断创建Tensor但不释放内存会持续增长。Python的垃圾回收机制通常能处理但计算图如果形成循环引用就需要手动断开。我的做法是每次反向传播后把中间变量的_prev置空。提示手搓框架的代码量不大但调试时间可能占70%。建议每实现一个模块就写单元测试用数值梯度检验反向传播的正确性。5.4 数值梯度检验的实操方法数值梯度检验是验证反向传播正确性的黄金标准。原理很简单用有限差分近似梯度和反向传播算出的梯度对比。如果相对误差小于1e-4说明反向传播实现正确。def numerical_gradient(f, x, eps1e-4): grad np.zeros_like(x) it np.nditer(x, flags[multi_index]) while not it.finished: idx it.multi_index old_val x[idx] x[idx] old_val eps f_plus f(x) x[idx] old_val - eps f_minus f(x) grad[idx] (f_plus - f_minus) / (2 * eps) x[idx] old_val it.iternext() return grad这个函数对每个元素做中心差分精度比前向差分高。检验时先算数值梯度再算反向传播梯度然后计算相对误差rel_error np.abs(grad_numerical - grad_backward) / (np.abs(grad_numerical) np.abs(grad_backward) 1e-8) print(f最大相对误差: {np.max(rel_error):.2e})如果误差在1e-6到1e-4之间基本没问题如果大于1e-2说明反向传播有bug。6. 从手搓到生产下一步可以怎么扩展手搓完这个迷你框架你对AI工程的理解会上一个台阶。但学习不止于此接下来可以往几个方向扩展第一个方向是性能优化。用NumPy的einsum替代手动矩阵乘法用numexpr加速逐元素运算用numba的JIT编译热点函数。我实测过用numba加速后的矩阵乘法速度能提升3到5倍。第二个方向是算子扩展。手写卷积、池化、BatchNorm、LayerNorm、Dropout。每个算子都有独特的反向传播逻辑写一遍收获很大。比如BatchNorm的反向传播涉及均值和方差的梯度推导过程很锻炼人。第三个方向是分布式训练。手写数据并行、梯度累积、AllReduce。这个需要多进程或多线程Python的GIL是个障碍可以用multiprocessing或mpi4py。第四个方向是模型压缩与量化。手写剪枝、量化、知识蒸馏。量化就是把float32转成int8核心是找到合适的缩放因子。这个在边缘设备部署时非常有用。我个人在实际操作中的体会是手搓框架最大的价值不是让你成为“造轮子专家”而是让你在调包时心里有底。你知道每一行API背后发生了什么遇到问题能快速定位做技术选型时能权衡利弊。这种工程直觉是看多少篇论文都换不来的。最后再分享一个小技巧如果你觉得从零写一个完整框架太耗时可以只手写你当前项目最核心的那个模块。比如你做推荐系统就手写Embedding层和FM层你做NLP就手写Attention和LayerNorm。聚焦一个点深挖到底比泛泛地过一遍所有模块效果好得多。

相关新闻

【一个人的SOC】(06) 资产对账——影子/离线/不匹配三类差异稽核

【一个人的SOC】(06) 资产对账——影子/离线/不匹配三类差异稽核

上一篇 (05) Nmap 攻击面扫描我们把"三路数据源"拼齐了——Wazuh 看到装了 agent 的主机、TP-Link 看到 DHCP 设备、Nmap 看到影子和暴露面。但拼齐之后的下一步是什么? 是把数据丢在那里让它自己增长吗?不。下一个必然的问题是: …

2026/10/15 8:53:04 阅读更多 →
Panda3D 贡献者指南:从构建引擎到提交可合并 PR 的完整工作流

Panda3D 贡献者指南:从构建引擎到提交可合并 PR 的完整工作流

游戏开发图形学 【免费下载链接】panda3d Powerful, mature open-source cross-platform game engine for Python and C, developed by Disney and CMU 项目地址: https://gitcode.com/gh_mirrors/pa/panda3d 点击查看 免费下载 Panda3D 是一个完全依赖社区志愿者维…

2026/10/15 8:53:04 阅读更多 →
AVP-Loc地库定位实战:PCA加速ICP与ESKF融合

AVP-Loc地库定位实战:PCA加速ICP与ESKF融合

1. 从泊车痛点说起:AVP-Loc到底在解决什么问题如果你开过带自动泊车功能的车,大概率经历过这种场景:地库信号弱、GPS基本废掉,车机屏幕上那个代表你爱车的小蓝点开始飘,明明车头朝东它显示朝西,明明离柱子还…

2026/10/15 8:53:04 阅读更多 →

最新新闻

HDFS面试避坑指南:从NameNode内存到DataNode心跳的实战解析

HDFS面试避坑指南:从NameNode内存到DataNode心跳的实战解析

1. 为什么“HDFS面试基础知识点”这个标题背后藏着一场隐性淘汰赛刚带完一批某高校实验室的分布式系统实训,有个细节让我印象深刻:A同学在模拟面试中被问到“HDFS写入流程里,Client和NameNode交互几次?DataNode之间要不要通信&…

2026/10/15 10:13:13 阅读更多 →
近视防控的科学原理与实用方案详解

近视防控的科学原理与实用方案详解

1. 近视防控的底层逻辑与现状分析近视的本质是眼轴过长或角膜曲率过陡导致光线无法准确聚焦在视网膜上。根据2020年国家卫健委发布的数据,我国儿童青少年总体近视率已达52.7%,其中小学生35.6%、初中生71.1%、高中生80.5%。这个数字背后反映的是现代生活方…

2026/10/15 10:13:13 阅读更多 →
H5 Canvas粒子爆炸特效:从零实现到二次开发指南

H5 Canvas粒子爆炸特效:从零实现到二次开发指南

简介:面向网页特效开发者的Canvas粒子爆炸动画示例,基于HTML5 Canvas API实现彩色粒子文字背景,通过JavaScript控制粒子生成、运动与消散,让文字在爆炸与重组中呈现强烈的视觉冲击,适用于个人主页、活动专题页等场景的…

2026/10/15 10:13:13 阅读更多 →
预测市场技术架构与流动性引擎解析

预测市场技术架构与流动性引擎解析

1. 道琼斯视角下的预测市场新范式当传统金融数据巨头开始关注预测市场领域时,这个细分赛道正在发生质的变化。Polymarket作为去中心化预测市场的典型代表,其构建的170工具生态不仅重新定义了信息聚合方式,更在主流化进程中展现出独特的价值捕…

2026/10/15 10:13:13 阅读更多 →
基于机器视觉的智慧阅卷系统测控技术与仪器计算机毕业设计选题机器学习项目

基于机器视觉的智慧阅卷系统测控技术与仪器计算机毕业设计选题机器学习项目

✅源码获取: 🍅--------------------【左上方头像,置顶文章的最上方】联系我们------------------🍅✌网站介绍:✌10年项目辅导经验、专注于计算机技术领域学生项目实战辅导。✌服务范围:大数据、机器学习、…

2026/10/15 10:13:13 阅读更多 →
计算机网络第一章核心:分层抽象与自顶向下设计思维

计算机网络第一章核心:分层抽象与自顶向下设计思维

1. 这不是背诵清单,而是网络思维的起点:为什么第一章必须“自顶向下”重学“计算机网络自顶向下复习重点-第一章”——看到这个标题,很多正在备考的同学第一反应是翻书、划线、默写五层模型。但我在带过十几届网络课程助教、参与过多个中小型…

2026/10/15 10:12:13 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →