news 2026/10/26 7:33:57

MNIST手写数字识别实战:CNN模型构建与TensorFlow代码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MNIST手写数字识别实战:CNN模型构建与TensorFlow代码解析

简介:一套基于Python实现的手写数字识别系统源码与配套数据集,主要面向需要完成课程设计或期末大作业的Python学习者,也适合深度学习、计算机视觉初学者将其作为第一个综合实践项目。项目已通过导师指导并获得97分高分评价,代码完整且可运行,下载后无需修改即可直接复现结果,主要流程包括数据读取、预处理、模型训练与数字识别测试,能够作为课程项目模板或答辩演示材料直接使用。资源包共3个文件,分别是Python主脚本、rar压缩格式的数据集和Markdown说明文档,整体约11.06MB;三种文件承担不同角色:脚本实现核心识别逻辑,数据包提供手写数字样本,说明文档介绍项目结构与运行方式,层次清晰、便于按需查看。目前已有260人学习下载,项目完整性与实用性得到多数下载者的认可。借助这份压缩包,读者可以快速跑通一个可演示的数字识别Demo,并根据文档理解特征处理与参数调整的基本思路,还能在此基础上替换数据集、优化网络结构,完成扩展到更高阶的模型实验。无论是用于课程考核、毕业设计前练手,还是丰富个人项目经历,都能提供扎实的起点,同时省去大量环境搭建与排错时间。

1. 手写数字识别系统:一份能跑通的期末作业源码里到底有什么

“基于Python实现的手写数字识别系统源码+数据集”,这份期末大作业包我刚拆完就跑通了。它不是那种需要自己补半天环境、还得猜数据格式的半成品,而是把数据集(data.rar)、训练主程序(test2.py)、说明文档(README.md)打包在一起,按 README 的步骤走就能完成从数据加载到模型评估的完整闭环,README 里标注了导师评分 97 分,项目结构确实对得起这个分数。手写数字识别(MNIST 是最常见的入门数据集)这个任务看起来小,但涵盖了图像分类的全部关键环节:数据读取、归一化、网络构建、训练、评估、误差分析。你把这套流程走通,后面换成其他图片分类任务都是同一套骨架。这份资源适合两类人:课程设计或期末大作业急需交付的学生,以及想通过 MNIST 入门深度学习图像识别的开发者。

2. 资源包结构与技术选型:为什么是MNIST+CNN这套组合

2.1 解压后你会看到什么:数据、脚本和文档的三层分工

拿到 zip 包之后,我的习惯是先看目录结构,而不是急着跑代码。这份资源的核心内容就三块:data.rar存放训练数据,test2.py是训练与评估主程序,README.md是使用说明。按照 README 的描述,先解压数据,再运行主程序,顺序别搞反,否则脚本找不到数据文件会直接抛异常。

文件类型职责使用时机
data.rar压缩数据集存放手写数字训练集/测试集第 1 步解压到./data/
test2.pyPython 脚本数据加载、模型构建、训练、评估解压完成后执行
README.md说明文档环境要求、运行步骤、依赖清单动手之前先读一遍

我一般会先看一眼 data.rar 里的内容再决定后续怎么处理,避免解压后发现目录层级和代码对不上。Linux 环境用unrar,Windows 上直接用 WinRAR 或者 7-Zip 解压到data文件夹即可。

unrar e data.rar ./data/ ls -lh ./data/

unrar e会把压缩包内所有文件解压到指定目录并去掉原有目录层级。执行完ls后重点看两件事:文件数量是否和 README 描述一致,以及每个文件的行数或图片数量。手写数字识别最经典的 MNIST 数据集是 6 万张训练图加 1 万张测试图,课程设计包为了便于查看,通常会转成 CSV 格式,每一行是 784 个灰度像素值加上一个标签列。如果解压出来是图片目录结构(比如train/0/、train/1/),那 test2.py 里对应的数据读取方式就会换成flow_from_directory,这点在读取之前最好确认一下。

2.2 为什么选 CNN 而不是 SVM 或逻辑回归

理论上手写数字识别用传统机器学习也能做,比如 HOG 特征加 SVM 可以把准确率做到 95% 左右,逻辑回归大概 92%。但问题在于,这类方案需要先做特征工程,对书写风格变化、笔画粗细非常敏感,换一套测试数据效果就可能明显下滑。对于课程设计来说,这不算一个“稳”的方案。

CNN 解决的是特征提取的自动化问题。卷积核在 28x28 的灰度图上滑动,自动学习边缘、笔画端点、弧线这些局部特征,再逐层组合成数字的高层语义。对一个两层卷积的浅层网络来说,MNIST 准确率可以轻松到 99% 以上,而且结构足够简单,答辩时每一层的输入输出形状、参数量都能讲得明明白白。

test2.py 里搭建的就是这类经典结构:

from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout model = Sequential([ Conv2D(32, (3, 3), activation="relu", input_shape=(28, 28, 1)), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activation="relu"), MaxPooling2D((2, 2)), Flatten(), Dense(128, activation="relu"), Dropout(0.3), Dense(10, activation="softmax"), ])

这个结构的参数量分布很值得算一遍。第一层卷积的权重是 33132 再加 32 个偏置,共 320 个参数;经过 2x2 最大池化后,特征图从 28x28 降到 14x14。第二个卷积层输入是 32 通道,参数变成 333264 再加 64 个偏置,共 18496 个。再池化一次降到 7x7,Flatten 之后是 7764=3136 个特征,映射到 128 个神经元,最后输出层是 128*10+10=1290 个参数。整个模型加起来约 31 万个参数,CPU 训练完全跑得动。

层输出形状参数量作用
Conv2D(32, 3x3)28x28x32320提取边缘与笔画特征
MaxPooling2D14x14x320降维,保留主要特征
Conv2D(64, 3x3)14x14x6418496提取更高层特征
MaxPooling2D7x7x640二次降维
Flatten31360展平接入全连接
Dense(128)128401664特征组合
Dropout(0.3)1280随机丢弃防过拟合
Dense(10)101290输出 0-9 概率

激活函数的选择也有讲究。卷积层和全连接层用 relu 而不是 sigmoid,是因为 relu 在正区间梯度恒为 1,能有效缓解深层网络梯度消失的问题。输出层用 softmax,把 10 个神经元的输出变成概率分布,概率最大的索引就是识别结果。

数据集为什么单独压成 rar 而不是直接放原目录?通常有两个原因:一是平台上传有文件数限制,压缩后方便传输;二是老师要求提交原始数据文件,保证可复现。这其实是个好习惯,下载解压后内容和 README 对得上,才说明资源完整。

3. 从依赖安装到模型训练:把test2.py完整跑通

3.1 环境准备与依赖安装

跑通这个项目的关键不是模型代码,而是 Python 和 TensorFlow 的版本匹配。这个坑几乎每个人都会踩一次:Python 3.11 环境里装 TensorFlow 2.5,结果import tensorflow直接 DLL load failed。我建议用 Python 3.8 到 3.10 之间的版本,搭配 TensorFlow 2.10,这是兼容性最稳的组合。

python --version pip install numpy==1.24.3 matplotlib opencv-python pip install tensorflow-cpu==2.10.0

tensorflow-cpu是 CPU 版本,不需要额外装 CUDA 和 cuDNN。MNIST 的数据量不大,CPU 上训练 10 个 epoch 也就几分钟,完全没必要为了这个项目去配 GPU 环境。numpy锁版本是为了避免部分老代码用到np.float这类在新版本中移除的别名时报错。opencv-python就是常用的cv2库,后面做单张图片预测和实时识别都会用到。

如果安装速度慢,可以换国内镜像源:

pip install tensorflow-cpu==2.10.0 -i https://pypi.tuna.tsinghua.edu.cn/simple

装完依赖之后,建议先跑一行import tensorflow as tf; print(tf.__version__)验证安装成功,再往下走。这一步花十秒钟,能省掉后面排查半天环境问题的时间。

3.2 数据加载与预处理:还原 28x28 的图片结构

手写数字识别的数据预处理,最核心的一点是把一维的 784 个像素还原成 28x28 的二维图片,并且做归一化。test2.py 里默认读取./data/train.csv,这是课程设计最常见的组织方式,直接把 MNIST 转成带表头的 CSV。

import numpy as np # 从 CSV 读取数据,第一行是表头 train_data = np.loadtxt("./data/train.csv", delimiter=",", skiprows=1) # 第一列是标签,其余 784 列是 28x28 的灰度像素 X = train_data[:, 1:].reshape(-1, 28, 28, 1).astype("float32") / 255.0 y = train_data[:, 0].astype("int")

skiprows=1跳过了 CSV 里的表头行。train_data[:, 1:]取出从第二列开始的像素数据,reshape(-1, 28, 28, 1)把每一行的 784 个值还原成 28 行 28 列的单通道图片,最后一个维度 1 表示灰度通道。如果这里忘了加通道维度,后面喂给卷积层时会直接报维度不匹配的错误。除以 255 是归一化操作,原始像素范围是 0 到 255,网络训练时更希望输入落在 0 到 1 之间,这样梯度更新更稳定,收敛速度也更快。我经常看到有人在归一化这一步偷懒,不除或者除错,结果准确率卡在 80% 上不去。

如果解压出来的数据不是 CSV,而是按类别存放的图片目录,那就改用flow_from_directory方式加载:

from tensorflow.keras.preprocessing.image import ImageDataGenerator datagen = ImageDataGenerator(rescale=1.0 / 255.0, validation_split=0.2) train_gen = datagen.flow_from_directory( "./data/train", target_size=(28, 28), color_mode="grayscale", batch_size=128, class_mode="categorical", subset="training", )

这种方式要求目录结构是train/0/xxx.png、train/1/xxx.png这样按数字类别分文件夹组织。两者路径不同,但核心逻辑一样:把任意来源的手写数字图片统一成 28x28 的单通道灰度图,再做归一化。下面先按 CSV 流程往下走,因为这是 test2.py 验证过的主链路。

3.3 模型构建与训练:输入和标签的形状必须对齐

模型构建部分核心是搞清楚两个维度:图片输入形状和标签编码方式。图片是 28x28x1,标签如果是整数形式 0 到 9,就要转成 one-hot 编码,也就是每个标签变成一个长度为 10 的向量,比如数字 3 变成[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]。

from tensorflow.keras.utils import to_categorical # 将整数标签转成 one-hot 编码 y_cat = to_categorical(y, 10) model.compile( optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"], ) history = model.fit( X, y_cat, batch_size=128, epochs=10, validation_split=0.2, verbose=1, )

build逻辑在这里,模型结构在 2.2 节已经定义了,compile阶段指定优化器、损失函数和评估指标。adam是自适应学习率优化器,对新手最友好,不需要手动调节学习率。categorical_crossentropy配合 one-hot 标签使用,如果你不想做 one-hot 编码,可以把损失函数换成sparse_categorical_crossentropy,效果等价。

validation_split=0.2会在训练数据里切出 20% 作为验证集,用于观察模型在没见过数据上的表现,这是判断过拟合的唯一途径。batch_size=128表示每轮训练取 128 张图片计算一次梯度,batch 太大内存压力大,太小训练不稳定。epochs=10对 MNIST 这个任务来说已经足够,训练过程中可以直观看到损失下降趋势。

训练完成后保存模型,这一步非常关键,否则每次评估都要重新训练:

model.save("mnist_model.h5")

保存为 h5 文件后,后续可以直接加载模型做预测和评估,不需要再跑训练流程。下面是 10 个 epoch 过程中典型的指标变化:

epochlossaccuracyval_lossval_accuracy
10.3520.9020.0890.973
30.1080.9670.0620.982
50.0710.9790.0460.986
80.0410.9870.0360.990
100.0280.9910.0300.992

从表格可以看出,训练损失持续下降,验证准确率同步上升,到第 10 轮时训练集准确率 99.1%,验证集 99.2%,两者差距很小,说明模型没有明显过拟合。如果 val_loss 在第 5 轮之后开始反弹而训练 loss 还在降,那就要考虑减小 epochs 或者加大 Dropout 比例了。

4. 评估指标与可视化:准确率之外还要看什么

只报告一个准确率数字在答辩时很难撑住场面,老师通常会追问:模型在哪些数字上容易犯错?有没有证据证明模型真的学到了特征,还是只是记住了训练集?这一章把评估环节做扎实,报告会更有说服力。

4.1 损失曲线:直观判断过拟合

训练过程返回的history对象里保存了每个 epoch 的损失和准确率,把它画出来是最直观的评估方式。

import matplotlib.pyplot as plt plt.plot(history.history["loss"], label="train_loss") plt.plot(history.history["val_loss"], label="val_loss") plt.xlabel("epoch") plt.ylabel("loss") plt.legend() plt.title("Loss Curve") plt.savefig("loss_curve.png")

判断方法很简单:两条曲线都在下降并逐渐平缓,说明训练正常;训练损失持续下降但验证损失在第 6 轮开始反弹,就是过拟合的典型信号,此时应减少 epochs、增加 Dropout 比例,或者引入数据增强。把这张曲线图贴进课程设计报告,比单纯写“模型准确率 99%”更有说服力,因为它展示了调优过程。

4.2 混淆矩阵:找出最容易认错的数字

准确率是一个整体指标,混淆矩阵才能告诉你错误具体发生在哪里。

from sklearn.metrics import confusion_matrix, classification_report # 加载测试集并预测 test_data = np.loadtxt("./data/test.csv", delimiter=",", skiprows=1) X_test = test_data[:, 1:].reshape(-1, 28, 28, 1).astype("float32") / 255.0 y_test = test_data[:, 0].astype("int") pred = np.argmax(model.predict(X_test), axis=1) cm = confusion_matrix(y_test, pred) print(classification_report(y_test, pred, digits=3))

confusion_matrix生成一个 10x10 的矩阵,cm[i][j]表示真实数字 i 被预测成了 j 的次数。对角线上的值是正确预测,越大越好。classification_report会输出每个类别的精确率、召回率、F1 值。

从多次经验来看,最容易混淆的组合是 3 和 5、4 和 9、7 和 2,原因在于手写风格潦草时局部笔画特征相似,加上 28x28 分辨率较低,部分细节在缩放过程中丢失。如果对角线之外有数值特别大的项,说明模型对某对数字的区分能力不足,这时候优先检查数据集中是否存在大量书写不清晰的样本,而不是急着改模型结构。毕业论文往错误分析部分贴一张混淆矩阵热力图,配上这类分析,导师会认为你做了完整的误差分析闭环。

4.3 单张图片预测:模型导出后的标准化验证

训练完成的模型要验证它真的能用,最好的方式是拿一张新写的手写数字图片走完整预测流程。

import cv2 import numpy as np img = cv2.imread("handwritten_5.png", cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, (28, 28)) # 如果图片是黑字白底,需要翻转成白字黑底,与训练数据一致 img = 255 - img img = img.reshape(1, 28, 28, 1).astype("float32") / 255.0 pred = model.predict(img) print("预测结果:", np.argmax(pred), "置信度:", np.max(pred))

cv2.imread以灰度模式读取图片,resize把任意尺寸统一到 28x28。翻转操作很关键:MNIST 训练数据是白字黑底,而我们用手机拍的照片通常是黑字白底,如果不翻转,模型会把背景当成笔画来识别,表现是准确率骤降。翻转之后再做归一化,然后增加 batch 维度变成(1, 28, 28, 1),因为模型训练时接受的输入是四维张量,单张图片必须补上第一个维度。最后np.argmax从 10 个概率值里取最大的索引作为识别结果。这一步走通了,才算真正完成了从数据解压到模型部署的完整验证。

5. 常见问题与避坑:运行报错和精度上不去的六个场景

5.1 import tensorflow 直接报错:DLL load failed 或 ModuleNotFoundError

现象:执行import tensorflow as tf时,在 Python 3.11 或 3.12 环境里直接报ModuleNotFoundError,或者报 DLL 加载失败。

原因:Python 版本与 TensorFlow 版本不匹配。TensorFlow 2.10 以及更早的版本在 Python 3.10 之后兼容性很差,新版 Python 只支持新版本 TensorFlow,而课程设计包里的代码是按老版本 API 写的。

解决:不要跟系统 Python 硬杠,建一个虚拟环境再装。

conda create -n digit python=3.9 conda activate digit pip install tensorflow-cpu==2.10.0

这里 Python 3.9 配 TensorFlow 2.10 是经过验证的稳定组合。

5.2 数据解压后形状对不上:找不到 784 个像素

现象:执行np.loadtxt后报错,提示字符串无法转成浮点数,或者train_data.shape不是预期的二维数组。

原因:CSV 带表头时没有加skiprows=1,字符串列混进了数值矩阵;也可能是data.rar里放的根本不是 CSV,而是图片目录,脚本按错误格式解析了。

解决:先检查数据形态,不要跳过这一步直接跑训练。

train_data = np.loadtxt("./data/train.csv", delimiter=",") print(train_data.shape) print(train_data[0])

输出第一行看一眼是数字还是字母,立刻就能定位问题。解压出来是图片目录的话,换flow_from_directory方式加载,路径指向./data/train即可。

5.3 训练 Loss 变成 NaN

现象:训练到第几个 epoch 时 loss 突然变成 nan,准确率卡在一个值不再变化。

原因:最常见的是输入数据里有 NaN 值,或者没有做归一化,像素值范围过大导致梯度爆炸。

解决:先检查数据。

print(np.isnan(train_data).sum())

如果结果为 0,确认归一化操作已执行。还可以把优化器改成显式指定学习率:

from tensorflow.keras.optimizers import Adam model.compile( optimizer=Adam(learning_rate=1e-3), loss="categorical_crossentropy", metrics=["accuracy"], )

1e-3 的学习率对 MNIST 是安全范围。如果还出现 NaN,用np.clip把输入限制在 0 到 1 之间之后再喂给模型。

5.4 准确率卡在 80% 左右上不去

现象:模型训练完成,准确率在 80% 附近波动,上不了 90%。

原因:网络结构太浅,或者输入没有归一化,也可能是 epochs 只有 3 到 5 轮,模型还没收敛。更隐蔽的原因是把 28x28 的图片 reshape 成 784 维后直接输入全连接层,丢掉了空间结构信息。

解决:确认归一化已执行,把网络改成卷积结构。如果已经是卷积结构但准确率低,把第一层卷积核从 16 提到 32,epochs 从 5 提到 10。如果数据集是自采的小规模样本,80% 可能就是数据量的上限,此时应该做数据增强而不是盲目加深网络。

5.5 预测单张图片时维度报错

现象:ValueError: Input 0 of layer sequential is incompatible with the layer: expected axis -1 of input shape to have value 1 but received input with shape (28, 28)。

原因:模型训练时输入是(batch, 28, 28, 1),单张图片如果不加 batch 维度,形状不匹配直接报错。

解决:补一个维度即可。

img = img.reshape(1, 28, 28, 1).astype("float32") / 255.0

如果你不想用reshape,也可以用img[None, ...],效果一样。

5.6 显示手写图片时颜色反了或者全黑

现象:用plt.imshow(img)显示手写图片时,背景是黑色、数字是白色,跟平时看到的黑字白底不一样。更严重的是,模型预测手机里拍的数字时准确率骤降。

原因:MNIST 训练数据是白字黑底,很多课程设计包在保存图片时也是白字黑底。而我们日常见到的扫描件、手机拍的照片是黑字白底,两者颜色正好相反。如果直接把黑字白底的图片喂给模型,所有像素值的关系都反了,模型看到的完全不是训练时的分布。

解决:显示时加cmap="gray",预测时按实际图片做颜色翻转。

plt.imshow(img, cmap="gray") # 黑字白底时翻转 img = 255 - img

这个坑很隐蔽,我见过有人把手机拍的数字全部预测成 1,查了好久才发现是颜色翻转问题。现在看到新图片,第一反应就是判断它和训练数据的背景关系。

6. 把识别系统接到摄像头:实时手写数字识别的改造思路

模型训完别急着收工,可以做一个鼠标画板实时识别的小工具,把 test2.py 的能力延伸到一个可交互的场景。核心思路是:用 OpenCV 建一个 280x280 的画布,鼠标在白底上画数字,按下任意键后对画布做预处理,扔给模型预测。

import cv2 import numpy as np from tensorflow.keras.models import load_model model = load_model("mnist_model.h5") canvas = np.zeros((280, 280), dtype=np.uint8) drawing = False ix, iy = -1, -1 def draw(event, x, y, flags, param): global drawing, ix, iy if event == cv2.EVENT_LBUTTONDOWN: drawing = True ix, iy = x, y elif event == cv2.EVENT_MOUSEMOVE and drawing: cv2.line(canvas, (ix, iy), (x, y), 255, 12) ix, iy = x, y elif event == cv2.EVENT_LBUTTONUP: drawing = False cv2.namedWindow("draw") cv2.setMouseCallback("draw", draw) while True: cv2.imshow("draw", canvas) key = cv2.waitKey(1) & 0xFF if key == ord("q"): break img = cv2.resize(canvas, (28, 28)) img = img.reshape(1, 28, 28, 1).astype("float32") / 255.0 pred = model.predict(img) print("识别结果:", np.argmax(pred), "置信度:", np.max(pred))

dtype=np.uint8保证画布是 8 位灰度图,cv2.line用 255 画白线,正好是黑底白字,和 MNIST 训练数据一致,不用额外做翻转。setMouseCallback把鼠标事件绑定到画布窗口,按 q 退出循环。退出后把 280x280 画布缩放到 28x28,归一化后直接预测。

实时识别和离线预测的预处理链路完全一致:灰度化、缩放、归一化、加 batch 维度。模型本身不需要做任何改动,改的只是图片来源。如果要把这套流程接到摄像头画面,替代方案是把摄像头 ROI 区域截下来,预处理之后再预测,逻辑完全复用。从那以后,我每次拿这类项目做图像分类改造,都会强制走一遍“形状检查、颜色翻转、归一化、加 batch 维”这条链路,确认输入和训练时完全一致再动模型。这个习惯帮我省了很多次调参的冤枉时间,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 7:33:29

AI论文降重与去痕迹技术解析

1. 论文降重与去AI痕迹的双重挑战去年帮导师审阅研究生论文时,有个现象让我印象深刻:超过60%的论文在查重系统里都会触发"AI生成内容"的警告标记。更棘手的是,这些论文即使用传统降重方法处理过后,仍然会留下明显的机器…

作者头像 李华
网站建设 2026/10/26 7:33:09

text-to-cad 实战:用 CadQuery 将自然语言生成 STEP/GLB/STL 模型

1. 从一段文字到三维模型:text-to-cad 到底在解决什么问题第一次听到 “text-to-cad” 这个说法,我脑子里蹦出来的画面是:对着屏幕敲一句“给我画一个 806010 的法兰盘,中心开 30 的通孔,四角打 M6 沉头孔”&#xff0…

作者头像 李华
网站建设 2026/10/26 7:33:07

MSCKF视觉惯性里程计MATLAB实现:原理、参数调优与工程坑

简介:状态估计是机器人定位与自动驾驶感知的核心环节,当轮式里程计打滑或纯视觉在弱纹理环境中失效时,融合IMU与相机的视觉惯性里程计(VIO)成为工程上最稳健的位姿估计方案之一。MSCKF作为VIO中的经典算法,…

作者头像 李华
网站建设 2026/10/26 7:31:38

端到端测试本质:验证用户真实旅程而非UI流程

1. 什么是真正的端到端测试:不是跑通页面,而是验证用户真实旅程“e2e”这个词在工程师日常对话里出现频率极高,但很多人一听到就下意识想到“写个脚本点点按钮、填填表单、截图看看”,甚至把它和UI自动化划等号。这其实是个危险的…

作者头像 李华
网站建设 2026/10/26 7:30:04

机器学习中的微积分原理与实践指南

1. 机器学习与微积分的核心关联微积分作为机器学习的数学基石,其重要性怎么强调都不为过。在实际建模过程中,我们每天都在与导数和积分打交道——从最简单的线性回归到复杂的神经网络,微积分的身影无处不在。以最常见的梯度下降算法为例&…

作者头像 李华
网站建设 2026/10/26 7:29:55

Qwen与DeepSeek代码生成模型深度对比评测

1. 项目概述最近在AI编程助手领域,千问(Qwen)和DeepSeek两大模型引起了开发者社区的广泛关注。作为长期关注AI编程辅助工具的技术博主,我花了三周时间对这两个模型进行了深度测试,从代码生成、问题解答到复杂算法实现等多个维度进行了全面对比…

作者头像 李华