新闻详情

新闻详情

首页 / 资讯中心 / 详情

CNN手写数字识别实战:从cnn_mnist.zip到模型训练与部署

发布时间:2026/9/28 1:50:05来源:尧图网络
CNN手写数字识别实战:从cnn_mnist.zip到模型训练与部署
简介面向手写数字识别入门学习者的CNN项目压缩包聚焦利用卷积神经网络解决MNIST数据集的分类任务适合希望动手实践图像识别、掌握深度学习基础流程的初学者。资源共20个文件包括Python脚本、PNG测试图片、MNIST原始数据压缩包与说明文档压缩包整体约11.08MB轻量易用。其中cnn_mnist.py是核心训练与测试脚本input_data.py负责图像归一化与训练/测试集划分mnist_demo.py可直观展示预测效果readme.txt提供运行指南自带data目录使数据加载与结果可视化完整闭环。由于内置完整MNIST数据集无需额外下载即可直接运行并可依据准确率输出调整网络结构帮助理解卷积层、池化层、全连接层的作用以及数据预处理、模型评估等关键环节。已有315人学习下载是深度学习入门阶段值得实操复现与改写的轻量示例。1. 收到一个 cnn_mnist.zip包里到底装了什么以及为什么 MNIST 到现在还有人在跑做深度学习的同学几乎都在某个阶段接触过cnn_mnist.zip这类压缩包——名字里把 CNN、MNIST、手写数据集、mnist.py全写齐了解压之后无非是训练脚本、数据集文件或者别人整理好的代码骨架。很多人第一反应是“MNIST 不是入门玩具吗有什么好折腾的”但实际接手时会发现数据下载 404、显存不够、训练不收敛、模型评估指标虚高每一个都能卡住半天。这篇文章的目标很直接拿到这类包之后怎么把里面的 CNN 手写数字识别跑通、跑稳并且能迁移到自己的小数据集上。适合两类人——刚入门想照着代码复现的以及要在 CPU 或低显存环境快速验证 CNN 方案的。2. 拆包与数据准备MNIST 的三种获取姿势以及 torchvision 下载 404 的应急方案2.1 压缩包里通常有哪几类文件先别急着跑代码解压cnn_mnist.zip后常见的文件布局是一个或几个.py脚本、一个README、可能带requirements.txt数据集要么已经放在data/目录里要么靠代码运行时自动下载。建议先按文件类型分类而不是直接双击运行mnist.py。我一般会先做三件事用tree或文件管理器看目录结构打开README确认 Python 版本和依赖再检查代码里数据加载部分用的是torchvision.datasets.MNIST还是keras.datasets.mnist。unzip cnn_mnist.zip -d cnn_mnist cd cnn_mnist find . -type f | sort # 最常见的结果 # ./mnist.py # ./README.md # ./requirements.txt # ./data/ 或 ./MNIST/ 目录unzip是 Linux 下的标准解压命令Windows 上可以用tar -xf或者直接右键解压。find这一步的价值在于先确认数据集是否已经内置——如果data/目录存在且文件体积在几十 MB 以上说明作者已经把 MNIST 的四个核心文件train-images-idx3-ubyte、train-labels-idx1-ubyte、t10k-images-idx3-ubyte、t10k-labels-idx1-ubyte打包进来了离线也能跑如果只有脚本没有数据运行时大概率会触发网络下载。接下来打开requirements.txt重点确认torch或tensorflow的版本约束因为 MNIST 相关的代码大多在 PyTorch 和 Keras 两套 API 之间二选一混着看会非常难受。2.2 torchvision 下载 MNIST 遇到 404用本地源文件绕过网络依赖很多人在跑带数据自动下载的mnist.py时会在torchvision.datasets.MNIST(root./data, downloadTrue)这一步卡住报HTTP 404 Not Found。这不是代码写错而是torchvision.datasets里的 MNIST 下载地址指向了 Yann LeCun 的原始站点该站点的文件路径调整过导致新版本 torchvision 按旧 URL 请求时直接 404。遇到这个情况最快的办法是手动下载四个.gz文件放进root目录再让downloadTrue跳过网络请求。# 在项目根目录执行创建 data 目录并放入 MNIST 原始文件 mkdir -p data/MNIST/raw cd data/MNIST/raw # 假设已经手动下载好四个 gz 文件train-images-idx3-ubyte.gz 等 ls -lh *.gz # 预期看到 train-images-idx3-ubyte.gz 等四个文件from torchvision import datasets, transforms transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_set datasets.MNIST(root./data, trainTrue, downloadTrue, transformtransform) test_set datasets.MNIST(root./data, trainFalse, downloadTrue, transformtransform) print(f训练集大小: {len(train_set)}) print(f测试集大小: {len(test_set)})这里的关键是torchvision.datasets.MNIST在downloadTrue时会先检查root/MNIST/raw/目录下是否存在对应的.gz文件如果文件在就直接解压读取不再发起网络请求。所以只要四个原始文件已经就位即使代码里写的是downloadTrue也不会触发下载。Normalize((0.1307,), (0.3081,))是 MNIST 全数据集的均值和标准差这两个值是固定先验别改成 0.5 或别的会影响收敛速度。TensorFlow/Keras 侧对应的是tf.keras.datasets.mnist.load_data()它走的是 Google 存储一般不会 404但国内网络可能超时可以用keras.utils.get_file手动指定镜像或本地缓存路径。2.3 DataLoader 参数怎么设batch_size 和 num_workers 的取舍拿到数据后mnist.py里通常会用DataLoader包装数据集。这块的参数并不是照抄默认值就行尤其当你跑在 Windows 或低配 Mac 上时num_workers设置不当会导致训练启动极慢甚至直接卡死。from torch.utils.data import DataLoader train_loader DataLoader(train_set, batch_size64, shuffleTrue, num_workers2, pin_memoryTrue) test_loader DataLoader(test_set, batch_size256, shuffleFalse, num_workers2, pin_memoryTrue) print(f训练批次数: {len(train_loader)}) print(f测试批次数: {len(test_loader)})batch_size64在 MNIST 这种单通道 28x28 小图上显存占用可以忽略不计所以可以放心加大到 128 或 256。num_workers是数据加载的子进程数Linux 上设为 CPU 核心数的一半即可Windows 上如果设为大于 0 且主脚本没有if __name__ __main__:保护会直接报RuntimeError。pin_memoryTrue在 GPU 训练时能把数据加载到锁页内存提高传输效率但如果用的是 Apple Silicon这个参数有时反而会拖慢速度建议保留默认 False。如果你在别人的mnist.py里看到DataLoader和torchvision.datasets混用注意确认数据集对象是从torchvision来的而不是从keras来的两者的张量格式、通道顺序都不一样。3. CNN 手写识别模型选型为什么是两层卷积而不是全连接以及各层参数怎么定3.1 从全连接到卷积MNIST 这个任务到底需要多深的网络很多第一次接触cnn_mnist的人会疑惑手写数字只有 10 类图像又那么小为什么不用全连接网络硬怼答案是全连接网络在 28x28 的输入上也能跑到 97% 左右的准确率但参数量巨大、训练极慢而且对像素位置的微小偏移极度敏感泛化能力差。CNN 通过卷积核的权值共享和局部连接把参数量降了几个数量级同时天然具备平移不变性——手写数字的笔画稍微偏几个像素卷积特征图仍然能捕获到同样的边缘和纹理模式。MNIST 上常见的 CNN 结构并不深两层卷积加一层全连接就够用。相比 ImageNet 动辄 ResNet-50、EfficientNetMNIST 的图太小、语义太简单堆到很深的网络只会带来过拟合和训练速度下降没有收益。这也是为什么这类压缩包里的mnist.py基本都是小而美的结构——不是为了炫技是任务本身决定的。3.2 卷积核、池化、Dropout、全连接每一层存在的理由下面这段代码是 PyTorch 版mnist.py里最常见的 CNN 骨架几乎每个参数都能讲出理由建议直接抄走再按需微调。import torch.nn as nn class MNISTCNN(nn.Module): def __init__(self): super(MNISTCNN, self).__init__() self.features nn.Sequential( nn.Conv2d(1, 32, kernel_size3, stride1, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), nn.Conv2d(32, 64, kernel_size3, stride1, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), ) self.classifier nn.Sequential( nn.Dropout(p0.25), nn.Flatten(), nn.Linear(64 * 7 * 7, 128), nn.ReLU(inplaceTrue), nn.Dropout(p0.5), nn.Linear(128, 10), ) def forward(self, x): x self.features(x) x self.classifier(x) return x第一层Conv2d(1, 32, kernel_size3, padding1)把单通道灰度图映射到 32 通道padding1保证 28x28 输入经过卷积后尺寸不变这样MaxPool2d(2, 2)会把特征图缩小到 14x14。第二层卷积把通道扩展到 64再一次池化后变成 7x7这也是后面64 * 7 * 7这个数的由来。Dropout放在全连接部分第一处p0.25作用于展平后的特征向量第二处p0.5放在隐层输出上两处 Dropout 的强度不同是因为前者输入维度大、过拟合风险相对可控后者接近分类层、需要更强正则。这里最容易算错的是7 * 7这个尺寸。如果你的输入不是 28x28或者卷积层padding/stride改过nn.Linear的输入维度就必须跟着改否则会报维度不匹配的RuntimeError。我习惯在 forward 里加一行print(x.shape)先跑一次确认尺寸后再写死Linear的输入维度。3.3 损失函数和优化器交叉熵与 Adam 的默认参数够用但要注意收敛曲线模型输出层有 10 个神经元训练时目标是让正确类别的得分最高。PyTorch 里nn.CrossEntropyLoss已经内置了 Softmax 和对数损失所以输出层不要自己加 Softmax否则会双重缩放、损失曲线异常。import torch.optim as optim model MNISTCNN() criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) # 一个典型的训练步 for images, labels in train_loader: optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() break # 仅示意跑完整训练请放到后面的循环里CrossEntropyLoss的输入是未经 Softmax 的原始 logitslabels是整数索引而不是 one-hot这两个都是 PyTorch 的约定写错不会报错但准确率会崩。Adam 的默认学习率0.001在 MNIST 上普遍够用但如果你发现 loss 在前几个 epoch 下降极快、之后停滞可以尝试把学习率降到0.0005或者换用 SGD momentum后者在 MNIST 上往往能刷到更高准确率只是需要多调几个 epoch。如果你拿到的是 TensorFlow 版的mnist.py对应的是tf.keras.losses.SparseCategoricalCrossentropy(from_logitsTrue)和optimizers.Adam()概念完全一致。4. 训练与评估完整训练循环、CPU/GPU 设备切换以及准确率到底该看哪个数4.1 训练循环模板先定义设备再组织 epoch 循环mnist.py里的训练部分通常是最容易翻车的地方——有人直接跳过设备判断把模型和输入都扔到 GPU 上结果换台机器就报CUDA out of memory。我一般会先写一个get_device()的工具函数再把训练循环包成标准结构方便换数据集时直接复用。import torch from tqdm import tqdm def get_device(): if torch.cuda.is_available(): return torch.device(cuda) elif torch.backends.mps.is_available(): return torch.device(mps) return torch.device(cpu) device get_device() print(f使用设备: {device}) model MNISTCNN().to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) epochs 10 train_losses [] val_accuracies [] for epoch in range(epochs): model.train() running_loss 0.0 for images, labels in tqdm(train_loader, descfEpoch {epoch1}/{epochs}): 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() * images.size(0) epoch_loss running_loss / len(train_loader.dataset) train_losses.append(epoch_loss) # 每个 epoch 结束后评估一次验证集 model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() acc 100.0 * correct / total val_accuracies.append(acc) print(fEpoch {epoch1}: loss{epoch_loss:.4f}, val_acc{acc:.2f}%)model.train()与model.eval()的切换是 Dropout 和 BatchNorm 生效的关键——训练时 Dropout 随机失活评估时必须关闭否则验证集准确率会随机波动。torch.no_grad()告诉 PyTorch 评估阶段不需要计算梯度省内存也提速。loss.item() * images.size(0)是为了按样本数加权累加避免最后一个 batch 太小导致平均 loss 失真。tqdm只是显示进度条没有也不影响训练但没了它你会觉得训练像黑匣子还是建议装上。4.2 训练时到底该看 train loss 还是 val acc一个被问烂但值得说清的问题很多新手盯着 train loss 不放看到它降到 0.01 就兴奋结果验证集准确率只有 90%反过来又怀疑模型写错。实际上train loss低只能说明模型在训练集上拟合得好不能说明泛化能力val acc才是你真正关心的指标。在 MNIST 上当val_acc已经到 99% 左右时继续训练对提升没有意义反而可能让 train loss 继续下降而 val acc 不再变化这就是过拟合的信号。训练过程中建议做三件事第一打印每个 epoch 的 train loss 和 val acc而不是只打印最终结果第二保存验证集上表现最好的模型权重而不是最后一个 epoch 的权重第三如果发现 train loss 下降但 val acc 停滞考虑加 Dropout 或数据增强。MNIST 本身太简单过拟合通常不严重但如果你把这个模板迁移到自己的小数据集上这三个习惯能救命。4.3 保存与加载模型.pth 文件里到底存什么加载时有哪些坑训练完的模型需要保存但保存的方式有讲究。常见的.pth文件有两种保存策略只存state_dict或者连整个模型结构一起存。前者是推荐做法因为只存参数、不依赖模型类代码的位置换机器或重构模型类后仍能加载。# 保存最佳模型 torch.save(model.state_dict(), mnist_cnn_best.pth) # 加载模型 model MNISTCNN() model.load_state_dict(torch.load(mnist_cnn_best.pth, map_locationdevice)) model.to(device) model.eval()state_dict只包含每层的权重和偏置不包含模型结构所以加载前必须先实例化MNISTCNN()。map_locationdevice解决的是设备迁移问题——如果模型在 GPU 上训练保存加载到 CPU 机器时不加这个参数会报错。model.eval()在推理前必须调用原因前面已经说过。如果是 TensorFlow 版对应的是model.save_weights(mnist_cnn_weights.h5)和model.load_weights思路完全一致。5. 手写数字识别避坑指南5 个高频报错与训练玄学问题排查5.1 torchvision 下载 MNIST 报 404根因是上游 URL 变化不是你的网络问题现象运行datasets.MNIST(root./data, downloadTrue)时控制台报HTTP Error 404: Not Found。原因torchvision.datasets.MNIST内部把下载地址写死在源码里指向的是 Yann LeCun 的个人站点yann.lecun.com/exdb/mnist该站点的文件目录或反爬策略调整过导致部分版本的 torchvision 按旧路径请求返回 404。这跟你的网络环境无关换 Wi-Fi、开代理都解决不了。解决最稳妥的方案是手动下载四个.gz文件放到data/MNIST/raw/目录下然后让downloadTrue跳过下载。具体步骤先创建目录再从镜像站下载 MNIST 原始文件最后重新运行代码。如果你用的是 TensorFlowtf.keras.datasets.mnist走的是另一个存储路径一般不会 404但国内网络可能超时建议用keras.utils.get_file手动指定缓存路径。5.2 训练时 loss 变成 nan学习率过大或输入未归一化现象训练刚开始时 loss 正常到某个 epoch 突然变成nan然后准确率归零。原因最常见的是学习率偏大导致梯度更新震荡发散其次是输入图像没有归一化像素值在 0~255 之间直接喂进网络激活值溢出。MNIST 的ToTensor()会把像素缩放到 0~1但如果你自己写数据加载、或者复用了别人的预处理代码很容易漏掉归一化那一步。解决把optimizer的学习率从0.01降到0.001或0.0005并检查数据预处理里是否有transforms.ToTensor()。另外可以用torch.isnan(loss)在训练循环里加一个判断一旦出现 nan 就打印当前 batch 的图像和标签快速定位问题来源。如果用的是 Keras把loss改成sparse_categorical_crossentropy时要注意from_logits的设置设错同样会导致训练异常。5.3 DataLoader 在 Windows 上报 RuntimeErrornum_workers 与主进程保护的纠缠现象Windows 上运行mnist.pyDataLoader初始化时报RuntimeError: DataLoader worker (pid xxx) is killed by signal或者直接报An attempt has been made to start a new process before the current process has finished its bootstrapping phase。原因Windows 使用spawn方式创建子进程如果你的脚本没有if __name__ __main__:保护多进程数据加载时子进程会重新执行整个脚本导致递归启动。解决把训练逻辑全部包在if __name__ __main__:里或者把num_workers设为 0。在 Linux 和 Mac 上num_workers0不会报错只是数据加载慢一些所以很多跨平台复用的代码会直接写 0。如果你在 Windows 上还想用多进程提速务必确认脚本主入口有__main__保护再尝试num_workers2。5.4 验证集准确率卡在 98% 上不去换优化器或调 Dropout现象模型训练到某个程度后验证集准确率一直卡在 98% 附近再增加 epoch 也没有明显变化。原因MNIST 本身手写数字存在书写歧义98% 以上已经是比较正常的水平再往上刷需要更精细的调优。常见原因是 Dropout 过强、模型容量不足、或者优化器陷入局部最优。解决先把 Dropout 从0.5降到0.3或者把第二层卷积的64通道数提到128看验证集准确率是否松动。如果还是卡住把Adam换成SGD(momentum0.9)并配合学习率衰减通常能在 3~5 个 epoch 内突破 98.5%。如果你用的是 Keras对应调整model.compile(optimizersgd, loss..., metrics[accuracy])即可。还有个容易忽略的点数据增强。MNIST 官方测试集和训练集分布基本一致但如果你给训练集加了随机噪声或旋转验证集效果可能不升反降慎用。5.5 换机器跑脚本报 CUDA out of memory显存不够但代码没有降级逻辑现象在本地 GPU 上能跑的mnist.py拿到只有 2GB 显存的机器上报CUDA out of memory。原因代码里把torch.device(cuda)写死了没有根据实际显存调整batch_size。MNIST 单张图虽然很小但DataLoader的num_workers和pin_memory都可能放大显存占用尤其是在模型和数据都压在 GPU 上时。解决先把batch_size从 64 降到 32 或 16再看是否还报错。如果还不行检查代码里有没有同时加载多个模型或特征图的冗余操作。最稳妥的写法是在代码开头统一device并且用torch.cuda.get_device_properties(0).total_memory打印显存大小做到心中有数。另外with torch.no_grad()在评估阶段必开否则评估过程也可能触发显存增长。6. 进阶验证技巧用混淆矩阵定位误判样本并把模型导出为可部署的脚本训练收敛到 99% 以上之后很多人以为事情就结束了但实际业务里更关键的是看模型错在哪里。MNIST 的 10 个类别里4和9、3和8在潦草书写下极难区分这类混淆在测试集整体准确率上看不出来。所以我会在训练结束后做两件事绘制混淆矩阵并随机抽取一批预测错误的图片看看模型到底是被笔迹骗了还是被预处理坑了。下面是一个可直接运行的诊断脚本适用于 PyTorch 版模型。import matplotlib.pyplot as plt import numpy as np import torch from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay # 假设 model 和 test_loader 已经定义 model.eval() all_preds [] all_labels [] with torch.no_grad(): for images, labels in test_loader: images images.to(device) outputs model(images) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.numpy()) cm confusion_matrix(all_labels, all_preds) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labels[str(i) for i in range(10)]) disp.plot() plt.savefig(mnist_confusion_matrix.png, dpi150, bbox_inchestight)confusion_matrix的输入必须是 NumPy 数组或列表不能直接传张量所以先.cpu().numpy()转换。对角线上的数字是正确分类的样本数非对角线上的亮点就是容易混淆的类别对。如果你发现某个类别的召回率明显偏低比如9经常被误判为4可以先看原始图片里有没有训练集本身标注错误的情况——MNIST 原始标签也有少量噪声这是公开数据集自带的问题不是你模型的锅。诊断完之后如果想把这个模型接进自己的推理脚本建议做一次导出。PyTorch 可以把模型转成 TorchScript 或 ONNX 格式这样脱离 Python 训练环境也能用。下面是一个 ONNX 导出的示例导出的模型可以用onnxruntime在 CPU 上快速推理也可以部署到支持 ONNX 的服务端。dummy_input torch.randn(1, 1, 28, 28, devicedevice) torch.onnx.export(model, dummy_input, mnist_cnn.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch_size}, output: {0: batch_size}}, opset_version11)dummy_input的维度必须和模型输入一致batch_size1、channel1、height28、width28。dynamic_axes允许推理时动态指定 batch 大小不设的话导出模型只能固定单样本输入。opset_version11是兼容性较好的选档新版 onnxruntime 都支持。这一步做完cnn_mnist.zip里的东西就不再只是练手代码而是可以嵌入到实际项目里的识别模块了。我自己的血泪经验是每次拿到这类压缩包先花 10 分钟确认数据加载和模型结构再动手训练省下的时间不止一小时。训练时把设备判断、.eval()切换、模型保存这三件事做成模板换任何数据集都不会翻车。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

从 CHANGELOG.md 到插件指纹:WPScan 如何用变更日志精准识别 WordCamp Dashboard Widget 版本 2026/9/28 2:46:03

从 CHANGELOG.md 到插件指纹:WPScan 如何用变更日志精准识别 WordCamp Dashboard Widget 版本

网络安全漏洞扫描渗透测试应用安全CLI 【免费下载链接】wpscan WPScan WordPress security scanner. Written for security professionals and blog maintainers to test the security of their WordPress websites. Contact us via contactwpscan.com 项目地址: ht…

阅读更多 →
区块链做网站避坑指南:3步省下50%冤枉钱 2026/9/28 2:46:03

区块链做网站避坑指南:3步省下50%冤枉钱

区块链做网站避坑指南:3步省下50%冤枉钱 找建站公司报价时,你是不是也心里直打鼓?对方张口就是“区块链概念”、“去中心化架构”,报价单上全是看不懂的术语,总价轻松破万甚至破十万。你明明只是想要个展示项目或者落地页,却担心自己不懂行被当成“…

阅读更多 →
Webiny React 依赖审计与现代化迁移指南:基于 dependencies/react.md 的完整解读 2026/9/28 2:46:03

Webiny React 依赖审计与现代化迁移指南:基于 dependencies/react.md 的完整解读

CMS后端前端 【免费下载链接】webiny-js Open-source, self-hosted CMS platform on AWS serverless (Lambda, DynamoDB, S3). TypeScript framework with multi-tenancy, lifecycle hooks, GraphQL API, and AI-assisted development via MCP server. Built for developers at…

阅读更多 →
NoneBot2 中的 aiohttp 驱动适配器:纯客户端 HTTP/WebSocket 连接的实现与使用 2026/9/28 2:46:03

NoneBot2 中的 aiohttp 驱动适配器:纯客户端 HTTP/WebSocket 连接的实现与使用

后端即时通讯 【免费下载链接】nonebot2 跨平台 Python 异步聊天机器人框架 / Asynchronous multi-platform chatbot framework written in Python 项目地址: https://gitcode.com/gh_mirrors/no/nonebot2 点击查看 免费下载 NoneBot2 的 nonebot.drivers.aiohttp …

阅读更多 →
动物图像数据集清洗实战:从28K原始图到生产级训练数据 2026/9/28 2:46:03

动物图像数据集清洗实战:从28K原始图到生产级训练数据

简介:本资源是一个面向计算机视觉初学者与AI实践者的动物图像分类数据集,适用于图像识别、数据增强、模型训练与迁移学习等典型CV任务。数据集涵盖狗、猫、马、蜘蛛、蝴蝶、鸡、羊、牛、松鼠、大象共10类常见动物,总计约28,000张中等质量JPG/…

阅读更多 →
mGBA 贡献指南:从 Issue 提报到编码规范与 MPL 2.0 许可合规的完整实践 2026/9/28 2:45:56

mGBA 贡献指南:从 Issue 提报到编码规范与 MPL 2.0 许可合规的完整实践

游戏开发 【免费下载链接】mgba mGBA Game Boy Advance Emulator 项目地址: https://gitcode.com/gh_mirrors/mg/mgba 点击查看 免费下载 mGBA 是一个以 C 和 C 编写的 Game Boy Advance 模拟器,同时支持 Game Boy / Game Boy Color 与 Super Game Boy&…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉