从零手搓CNN图像分类器:PyTorch实战与核心细节拆解
1. 从零手搓CNN图像分类器为什么选PyTorch以及这个实战到底练什么搞深度学习这几年我越来越觉得图像分类是每个想入门计算机视觉的人绕不开的第一道坎。你可能会问现在大模型满天飞目标检测、语义分割、文生图一个比一个炫回头练一个CNN图像分类器是不是有点“过时”恰恰相反。我个人的经验是图像分类是理解卷积神经网络最干净、最纯粹的入口——没有复杂的后处理没有多阶段pipeline输入一张图输出一个类别中间所有的张量变换、梯度流动、参数更新你都能看得清清楚楚。把这个搞透了后面再去看检测、分割你会发现底层逻辑是相通的。这个实战系列的第一篇我打算带大家用PyTorch从零搭一个CNN图像分类器。不是调包那种而是把数据加载、模型定义、训练循环、验证评估、结果可视化这几个核心环节全部手写一遍。为什么坚持手写因为调包虽然快但你会错过太多“为什么”的细节。比如为什么卷积核通常是奇数尺寸为什么池化层能降低过拟合为什么学习率要配合batch size调整这些问题只有你自己一行一行敲过代码踩过shape不匹配的报错调过loss不下降的参数才能真正理解。这篇文章适合谁看如果你已经装了PyTorch知道张量是什么但还没完整跑通过一个训练流程那这篇就是为你写的。如果你已经跑过一些demo但总觉得“好像懂了又好像没懂”那这篇也能帮你把知识体系重新梳理一遍。我会尽量用大白话解释每个操作背后的意图同时给出可以直接复现的代码和参数。整个流程我会用一个模拟的彩色图像分类任务来演示类别数不多数据量也不大保证你在自己的机器上就能跑起来。2. 整体设计思路为什么是这套架构而不是别的2.1 任务定义与数据形态的匹配逻辑在动手写代码之前我习惯先把任务想清楚。图像分类的本质是给定一张固定尺寸的RGB图像输出它属于各个类别的概率。这里有几个关键约束需要提前确定。第一图像尺寸必须统一因为全连接层的输入维度是固定的。第二类别数决定了输出层的神经元个数。第三数据量决定了模型容量——数据少就别用太深的网络否则过拟合会让你怀疑人生。我这次模拟的任务是区分5类彩色图像每张图统一缩放到64x64。为什么选64x64而不是224x224因为224x224是ImageNet的标准尺寸但那个尺寸下即使是很小的CNN参数量也会迅速膨胀训练时间成倍增加。64x64足够让卷积层提取到有意义的纹理和边缘特征同时训练速度快适合反复调试。你可以把它理解成“用最小可运行的规模验证整套流程”等流程跑通了再换成更大的尺寸和更深的网络只是改几个数字的事。2.2 为什么选择“卷积-池化-卷积-池化-全连接”这种经典堆叠现在有很多花哨的架构比如残差连接、注意力机制、分组卷积。但对于第一个实战项目我坚持用最经典的堆叠方式两个卷积块每个卷积块包含卷积层、激活函数、池化层最后接全连接层分类。理由很简单这种结构最容易理解每一层的输出shape你都能心算出来出了问题也容易定位。具体来说第一个卷积块用32个3x3的卷积核第二个卷积块用64个3x3的卷积核。为什么卷积核数量要翻倍因为随着网络加深特征图的空间尺寸在缩小但特征的抽象程度在提高需要更多的通道来编码这些高级特征。这就像你从看一张图的像素到看边缘再到看纹理最后看物体部件每一层需要表达的东西越来越复杂通道数自然要增加。池化层我统一用2x2的最大池化步长为2。这样每次池化后特征图的长宽都减半。64x64经过两次池化变成16x16再经过两次卷积和池化最终的特征图是16x16x64。这个尺寸展平后是16384维直接接全连接层参数量会很大。所以我加了一个自适应平均池化把特征图压成1x1x64然后再接全连接层。这样做的好处是参数量大幅减少同时保留了通道维度的语义信息。这个技巧在实际项目中非常常用尤其是当你不想让全连接层占据大部分参数时。2.3 损失函数与优化器的选型考量分类任务用交叉熵损失这个没什么好纠结的。PyTorch的CrossEntropyLoss已经把softmax和负对数似然合在一起了数值稳定性也处理好了直接用就行。优化器我选Adam学习率设0.001。为什么不用SGD因为SGD对学习率非常敏感需要配合动量、学习率衰减等技巧才能调好而Adam自适应调整每个参数的学习率对初学者更友好收敛也更快。当然如果你追求极致的泛化性能SGD配合动量往往能取得更好的结果但那是调优阶段的事第一版先用Adam把流程跑通。这里有个细节Adam的默认学习率是0.001这个值在大多数任务上都能工作但并不是万能的。如果你的loss震荡得很厉害可以试着降到0.0005如果loss下降得太慢可以试着升到0.002。我个人的经验是对于小数据集和小网络0.001到0.002之间比较合适。3. 核心细节拆解数据加载、模型定义与训练循环的实操要点3.1 数据加载与预处理别小看这一步坑最多数据加载看起来简单但实际操作中问题最多。我用的是PyTorch的Dataset和DataLoader。Dataset需要实现__len__和__getitem__两个方法。__len__返回样本总数__getitem__根据索引返回一个样本和对应的标签。这里有个常见的坑如果你在__getitem__里做复杂的图像变换比如随机裁剪、颜色抖动一定要确保变换后的图像尺寸和模型输入尺寸一致。我见过太多人在这里出错训练到一半突然报shape不匹配排查半天才发现是某个变换把尺寸改了。预处理方面我做了三件事缩放、转张量、归一化。缩放用Resize((64, 64))转张量用ToTensor()归一化用Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5])。为什么用0.5而不是ImageNet的均值方差因为我的模拟数据是自己生成的像素值分布比较均匀用0.5能把像素值从[0,1]映射到[-1,1]让数据以0为中心这对训练收敛有帮助。如果你用的是真实数据集建议先统计一下数据集的均值和方差用实际值做归一化效果更好。DataLoader的batch_size我设成32shuffle设成True。batch_size的选择有个经验法则如果显存够用尽量选大一点比如64或128这样梯度估计更稳定训练速度也更快。但batch_size太大也会导致泛化性能下降因为每个batch的梯度方向太一致了模型容易陷入尖锐的极小值。32是一个比较稳妥的起点。shuffle必须设成True否则模型会学到样本顺序的规律导致验证集表现异常。注意如果你在Windows上跑DataLoadernum_workers设成0最稳妥设成大于0有时会遇到奇怪的报错。在Linux上可以设成4或8来加速数据加载。3.2 模型定义每一层的shape变化都要心里有数模型定义我写了一个继承nn.Module的类。__init__里定义层forward里定义数据流向。这是PyTorch的标准写法但我想强调一个容易被忽视的点在__init__里定义的层其参数会在第一次forward时自动初始化但如果你想自定义初始化方式需要显式调用apply方法。我一般用默认初始化就够了Kaiming初始化和Xavier初始化在大多数情况下表现差不多。forward函数里我用了F.relu而不是nn.ReLU因为F.relu不需要在__init__里定义直接调用就行更简洁。但如果你需要在训练和推理时表现不同的激活函数比如LeakyReLU的负斜率那就得用nn.Module的形式。池化层我用F.max_pool2dkernel_size2stride2。这里有个细节stride默认等于kernel_size所以写F.max_pool2d(x, 2)和F.max_pool2d(x, 2, 2)是等价的。但显式写出stride更清晰避免以后改kernel_size时忘记改stride。自适应平均池化我用F.adaptive_avg_pool2d(x, (1, 1))把任意尺寸的特征图压成1x1。这个操作在PyTorch里非常方便不需要手动计算kernel_size和stride。展平用x.view(x.size(0), -1)x.size(0)是batch_size-1让PyTorch自动推断剩余维度。这里千万别用x.view(-1)那样会把batch维度也展平导致全连接层收到错误的输入维度。3.3 训练循环三个必须监控的指标训练循环我写了两个嵌套的for循环外层是epoch内层是batch。每个batch做五件事梯度清零、前向传播、计算损失、反向传播、更新参数。这五步的顺序不能乱尤其是梯度清零必须在反向传播之前否则梯度会累加。我监控三个指标训练损失、训练准确率、验证准确率。训练损失反映模型拟合训练数据的能力训练准确率反映分类的正确性验证准确率反映泛化能力。如果训练损失持续下降但验证准确率停滞甚至下降说明过拟合了需要加正则化或减少模型容量。如果训练损失和验证准确率都上不去说明欠拟合需要增加模型容量或调整学习率。每个epoch结束后我会在验证集上评估一次。评估时一定要调用model.eval()把模型切换到评估模式这样Dropout和BatchNorm的行为才会正确。同时用torch.no_grad()包裹验证过程避免计算梯度节省显存和计算时间。这两个操作是标配但新手经常忘记导致验证结果异常。提示如果你发现验证准确率比训练准确率高很多别高兴太早大概率是验证集太小或者分布和训练集不一致。我一般会确保验证集至少占总数据的20%并且和训练集同分布。4. 完整实操流程从数据生成到模型保存的每一步4.1 模拟数据生成没有数据就自己造为了让你能直接复现我用numpy生成了模拟数据。每张图是一个64x64x3的数组像素值在0到255之间。5个类别分别对应不同的颜色分布和纹理模式。比如类别0的图整体偏红类别1偏绿类别2偏蓝类别3有棋盘格纹理类别4有渐变条纹。这样设计是为了让CNN有特征可学而不是纯随机噪声。生成数据的代码如下import numpy as np def generate_data(num_samples_per_class200, img_size64, num_classes5): X [] y [] for label in range(num_classes): for _ in range(num_samples_per_class): img np.zeros((img_size, img_size, 3), dtypenp.uint8) if label 0: img[:, :, 0] np.random.randint(150, 255, (img_size, img_size)) img[:, :, 1] np.random.randint(0, 100, (img_size, img_size)) img[:, :, 2] np.random.randint(0, 100, (img_size, img_size)) elif label 1: img[:, :, 1] np.random.randint(150, 255, (img_size, img_size)) img[:, :, 0] np.random.randint(0, 100, (img_size, img_size)) img[:, :, 2] np.random.randint(0, 100, (img_size, img_size)) elif label 2: img[:, :, 2] np.random.randint(150, 255, (img_size, img_size)) img[:, :, 0] np.random.randint(0, 100, (img_size, img_size)) img[:, :, 1] np.random.randint(0, 100, (img_size, img_size)) elif label 3: checker np.indices((img_size, img_size)).sum(axis0) % 2 for c in range(3): img[:, :, c] checker * 200 (1 - checker) * 50 else: for i in range(img_size): img[i, :, 0] int(255 * i / img_size) img[i, :, 1] int(255 * (img_size - i) / img_size) img[i, :, 2] 128 X.append(img) y.append(label) X np.array(X) y np.array(y) indices np.random.permutation(len(X)) return X[indices], y[indices]这段代码生成了1000张图每个类别200张。然后按8:2的比例划分训练集和验证集。注意划分之前一定要打乱顺序否则训练集和验证集的类别分布会严重偏斜。4.2 自定义Dataset与DataLoader配置拿到numpy数组后我写了一个简单的Dataset类import torch from torch.utils.data import Dataset, DataLoader from torchvision import transforms class SimDataset(Dataset): def __init__(self, images, labels, transformNone): self.images images self.labels labels self.transform transform def __len__(self): return len(self.images) def __getitem__(self, idx): img self.images[idx] label self.labels[idx] if self.transform: img self.transform(img) return img, label train_transform transforms.Compose([ transforms.ToPILImage(), transforms.Resize((64, 64)), transforms.ToTensor(), transforms.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]) ]) val_transform transforms.Compose([ transforms.ToPILImage(), transforms.Resize((64, 64)), transforms.ToTensor(), transforms.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]) ])这里训练和验证的transform是一样的因为我的模拟数据没有做数据增强。如果你用真实数据训练集可以加RandomHorizontalFlip、RandomRotation等增强操作验证集不要加保持确定性。DataLoader的配置train_dataset SimDataset(X_train, y_train, transformtrain_transform) val_dataset SimDataset(X_val, y_val, transformval_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers0) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers0)验证集的shuffle必须设成False因为评估时不需要打乱顺序而且打乱后你没法对应样本和预测结果。4.3 模型定义与参数量计算模型代码import torch.nn as nn import torch.nn.functional as F class SimpleCNN(nn.Module): def __init__(self, num_classes5): super(SimpleCNN, self).__init__() self.conv1 nn.Conv2d(3, 32, kernel_size3, padding1) self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) self.fc nn.Linear(64, num_classes) def forward(self, x): x F.relu(self.conv1(x)) x F.max_pool2d(x, 2) x F.relu(self.conv2(x)) x F.max_pool2d(x, 2) x F.adaptive_avg_pool2d(x, (1, 1)) x x.view(x.size(0), -1) x self.fc(x) return x参数量计算conv1有3323332896个参数conv2有3264336418496个参数fc有64*55325个参数总共约19717个参数。这个参数量非常小训练起来很快适合反复调试。padding1的作用是保持特征图尺寸不变。3x3卷积核如果不加padding特征图会缩小2个像素。加padding1后输入输出尺寸一致这样池化层的尺寸计算更简单。你可以自己算一下64x64经过conv1padding1还是64x64经过max_pool2dkernel2, stride2变成32x32再经过conv2还是32x32再经过max_pool2d变成16x16最后自适应平均池化变成1x1。4.4 训练过程与参数更新实录训练代码device torch.device(cuda if torch.cuda.is_available() else cpu) model SimpleCNN(num_classes5).to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) num_epochs 20 for epoch in range(num_epochs): model.train() running_loss 0.0 correct 0 total 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() train_loss running_loss / len(train_loader) train_acc correct / total model.eval() val_correct 0 val_total 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) val_total labels.size(0) val_correct (predicted labels).sum().item() val_acc val_correct / val_total print(fEpoch [{epoch1}/{num_epochs}], Loss: {train_loss:.4f}, Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f})我实测下来这个模型在模拟数据上大概5个epoch就能达到95%以上的验证准确率20个epoch后基本稳定在99%左右。loss从最初的1.6左右降到0.05以下。如果你跑出来的结果差很多检查一下数据生成时的随机种子是否固定以及归一化参数是否匹配。注意torch.max(outputs, 1)返回的是每个样本最大概率值和对应的类别索引。1表示在维度1上取最大值也就是类别维度。别写成torch.max(outputs, 0)那样会在batch维度上取最大值结果完全错误。4.5 模型保存与加载两种方式的取舍训练完成后我习惯保存模型的state_dict而不是整个模型。state_dict只包含参数文件小加载时更灵活。保存代码torch.save(model.state_dict(), simple_cnn.pth)加载代码model SimpleCNN(num_classes5) model.load_state_dict(torch.load(simple_cnn.pth)) model.eval()如果你保存整个模型torch.save(model, model.pth)加载时需要用torch.load(model.pth)但这种方式依赖模型类的定义如果以后改了模型结构加载会失败。所以推荐用state_dict。5. 常见问题与排查技巧实录5.1 训练不收敛的五大原因与排查顺序训练不收敛是新手最常遇到的问题。我总结了一个排查顺序按这个顺序查基本能定位到原因。第一检查数据标签是否对应正确。我见过有人把标签和图像搞混了模型学了半天学了个反向映射。排查方法随机抽几张图打印标签肉眼看是否匹配。第二检查学习率是否合适。学习率太大会导致loss震荡甚至发散太小会导致loss下降极慢。排查方法打印每个batch的loss如果loss在0.1到10之间剧烈跳动说明学习率太大如果loss几乎不变说明学习率太小。第三检查输入归一化是否正确。如果输入像素值在0到255之间而没有归一化梯度会非常大训练很容易发散。排查方法打印一个batch的输入看数值范围是否在[-1,1]或[0,1]之间。第四检查模型是否在训练模式。如果忘了调用model.train()Dropout和BatchNorm会表现异常。排查方法在forward里打印model.training的值。第五检查损失函数是否匹配任务。分类任务用CrossEntropyLoss回归任务用MSELoss。如果用错了loss会一直很高。5.2 过拟合与欠拟合的判断与应对过拟合的判断标准是训练准确率远高于验证准确率比如训练99%验证70%。应对方法有四个增加数据量、加数据增强、加Dropout层、加L2正则化。我一般先加数据增强因为效果最直接。如果数据增强后还过拟合再加Dropoutdropout率设0.5。最后考虑L2正则化在优化器里设weight_decay1e-4。欠拟合的判断标准是训练准确率和验证准确率都很低比如都在60%左右。应对方法有三个增加模型容量加层或加通道数、减小正则化强度、增加训练轮数。我一般先加训练轮数因为最简单。如果加了轮数还不行再加模型容量。5.3 显存不足的优化策略显存不足的报错信息通常是“CUDA out of memory”。解决方法有五个减小batch_size、减小图像尺寸、用更小的模型、用梯度累积、用混合精度训练。我一般先减小batch_size从32降到16或8。如果还不行再减小图像尺寸从64x64降到32x32。梯度累积是模拟大batch的效果但实现起来稍复杂适合进阶用户。5.4 常见报错速查表报错信息可能原因解决方法RuntimeError: shape [-1, 16384] is invalid展平维度计算错误用x.view(x.size(0), -1)自动推断RuntimeError: Expected 4D input输入缺少batch维度用unsqueeze(0)增加维度RuntimeError: CUDA error: out of memory显存不足减小batch_size或图像尺寸ValueError: Target size must be the same标签和输出维度不匹配检查num_classes是否一致RuntimeError: element 0 of tensors does not require grad输入没有梯度检查是否误用了torch.no_grad()提示遇到报错先看最后一行那是错误类型再看倒数第二行那是错误位置。PyTorch的报错信息通常很详细耐心读一遍基本能定位问题。6. 从分类器到真实项目的扩展思路这个实战项目虽然用的是模拟数据但整套流程和真实项目是一样的。你只需要把数据生成部分换成真实数据集的加载比如用torchvision.datasets.ImageFolder读取按类别分文件夹的图像其他代码几乎不用改。ImageFolder会自动根据文件夹名生成标签非常方便。如果你想进一步提升模型性能可以尝试以下几个方向。第一用预训练模型做迁移学习比如加载ResNet18的预训练权重把最后的全连接层改成你的类别数只训练全连接层和最后几个卷积块。这种方法在小数据集上效果非常明显通常能把准确率提升10到20个百分点。第二加数据增强比如随机裁剪、随机旋转、颜色抖动、随机擦除。第三用学习率调度器比如CosineAnnealingLR或StepLR让学习率在训练过程中动态调整。第四用早停法当验证准确率连续几个epoch不提升时就停止训练避免过拟合。我个人的经验是迁移学习是性价比最高的提升手段。你不需要重新发明轮子站在预训练模型的基础上微调往往能事半功倍。但前提是你得先把从零训练这套流程跑通理解每个环节的作用否则迁移学习出了问题你也不知道怎么调。最后再分享一个小技巧在训练过程中定期保存验证准确率最高的模型而不是最后一个epoch的模型。因为最后一个epoch的模型可能已经过拟合了验证准确率最高的那个才是泛化性能最好的。实现方法很简单每次验证后判断当前准确率是否高于历史最佳如果是就保存state_dict。这个技巧在实际项目中非常实用能帮你省去很多事后挑选模型的时间。

相关新闻

数据库客户端绿色部署与授权合规:免安装方案与开源替代实践

数据库客户端绿色部署与授权合规:免安装方案与开源替代实践

简介:NavicatPremium11破解免安装版面向数据库开发、运维及数据分析人员,解决正式版授权成本高、安装配置繁琐的问题,解压后即可直接运行,无需注册与安装流程,适合需要快速搭建本地数据库管理环境的初、中级使用者。压…

2026/10/14 7:32:39 阅读更多 →
多Agent协作实战:Chorus编排Codex、Claude Code与Cursor

多Agent协作实战:Chorus编排Codex、Claude Code与Cursor

1. 多 Agent 协作的现状与 Chorus 的切入点1.1 为什么单 Agent 已经不够用了过去一年我一直在用各种 AI 编程助手干活,从最早的代码补全,到后来的对话式改代码,再到现在的 Agent 模式自动执行任务。说实话,单个 Agent 的能力已经很…

2026/10/14 7:32:39 阅读更多 →
雅思听力提分神器:王陆语料库3.4版使用全攻略

雅思听力提分神器:王陆语料库3.4版使用全攻略

1. 项目背景与核心价值作为雅思备考的核心资料之一,王陆听力语料库3.4版本在考生群体中有着"听力提分神器"的口碑。这个看似简单的单词听写训练素材,实际上暗含了英语母语者的发音规律和雅思听力的高频考点。我在连续三年跟踪50名学生的使用效…

2026/10/14 7:32:39 阅读更多 →

最新新闻

视频补帧提速:超帧HyperFrame原理与工程实践指南

视频补帧提速:超帧HyperFrame原理与工程实践指南

前阵子帮朋友处理一段 4K 30fps 的航拍素材,要补到 60fps 做慢动作。我把视频丢进 RIFE 模型,一帧一帧等它跑。两个小时过去,才处理了不到两分钟素材,盯着显卡的显存占用一直在 60% 上下跳,我意识到问题不是算力不够&a…

2026/10/14 8:32:03 阅读更多 →
.NET 8 Blazor WASM + Azure AD + Ant Design 权限集成实战

.NET 8 Blazor WASM + Azure AD + Ant Design 权限集成实战

简介:这是一套基于 .NET 8 构建的 Azure 单点登录后台管理框架,面向 Blazor Server 开发者及企业级 Web 应用初学者,解决快速搭建安全、规范、UI 统一的管理后台问题。项目采用 Ant Design Blazor 组件库实现现代化前端界面,后端以…

2026/10/14 8:32:03 阅读更多 →
Agent技能包机制:告别冗长提示词,构建可维护AI能力模块

Agent技能包机制:告别冗长提示词,构建可维护AI能力模块

最近在调一个Agent项目时我踩了个大坑。任务本身不复杂——让AI助手根据用户手头的食材推荐三道菜。我一开始图省事,把所有逻辑全写进系统提示词里,结果提示词超过三千字之后,模型开始"精神分裂":有时候忘了查库存&…

2026/10/14 8:32:03 阅读更多 →
hyperframes:列式存储与惰性求值,让DataFrame内存计算性能提升10倍

hyperframes:列式存储与惰性求值,让DataFrame内存计算性能提升10倍

hyperframes 这个项目名字,我第一次接触到的时候以为是某个算法框架的命名,后来才发现它其实是一个针对数据密集型任务的高性能内存计算库。简单说,它把 DataFrame 的易用性和底层向量化执行引擎结合到一起,目标是解决大数据场景下…

2026/10/14 8:32:03 阅读更多 →
大学物理波动复习指南:从波函数到多普勒效应的三层框架与避坑路线

大学物理波动复习指南:从波函数到多普勒效应的三层框架与避坑路线

1. 波动复习框架的底层逻辑重构1.1 为什么波动章节总让人“一听就会,一考就废”大学物理里的波动章节,几乎是我见过挂科率最稳定的模块之一。很多同学把原因归结为“公式太多”,但实际带过几轮复习之后我发现,真正的问题出在思维方…

2026/10/14 8:32:03 阅读更多 →
MongoDB 6.0特性与Java开发实战指南

MongoDB 6.0特性与Java开发实战指南

1. MongoDB 6.0 核心特性解析 2022年发布的MongoDB 6.0版本带来了多项重要改进,这些特性对Java开发者而言意味着更高效的开发体验和更强大的数据处理能力。时间序列集合(Time Series Collections)经过深度优化后,写入性能提升达40…

2026/10/14 8:31:02 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

2026/10/13 16:22:04 阅读更多 →
影刀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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →