首页 / 资讯中心 / 文章详情

Python+Keras+TensorFlow环境搭建与手写数字识别实战

Python+Keras+TensorFlow环境搭建与手写数字识别实战 ★ FEATURED ARTICLE
这几年大大小小带过不少人入门神经网络问得最多的往往不是「反向传播到底怎么推」而是「我这环境到底能不能跑起来」。今天这篇系列开篇就把 Python、Keras、TensorFlow 这条最经典的技术栈从头捋一遍从环境搭建到第一个能跑通的模型把那些文档里写了但没人细讲的坑也一并填上。目标读者是准备入门 AI 的开发者或者已经搭过环境但没真正理解全流程的朋友。看完这一篇你能亲手跑通一个手写数字识别模型并且对「图像变成高维向量」这件事有个踏实的直觉。一句话概括全文用 Keras 作为前端接口、TensorFlow 作为后端引擎在 Python 生态里快速构建并训练一个神经网络同时把整个过程背后的原理和实操细节讲透。1. 为什么是这套技术栈Python、Keras 与 TensorFlow 的关系1.1 三个组件各自干了什么活很多人第一次接触这套东西会陷入一个误区以为 Keras 和 TensorFlow 是两个互相竞争的东西或者觉得 TensorFlow 必须写一堆底层 API。实际上它们的关系更像「点外卖」——Keras 是你手里的点单 AppTensorFlow 是后厨的炉灶和厨师。你在 Keras 界面里选好菜品、设置口味订单就交给后厨去完成你不需要关心火候怎么控制、锅铲怎么拿。Keras高层神经网络 API负责定义模型结构、编译、训练、评估。两三行代码就能堆出一个网络。TensorFlow底层计算引擎负责数学运算、自动微分、GPU 调度、分布式训练。Keras 会把你的模型描述翻译成 TensorFlow 的计算图来执行。Python胶水语言把开箱即用的数据工具NumPy、Pandas、训练好的模型和业务逻辑串起来。Keras 在 2019 年之后正式成为 TensorFlow 的官方高级 API所以在 TensorFlow 2.x 时代import tensorflow as tf之后直接用tf.keras就可以不用再单独装一个keras包。这也是新手最常见的疑惑之一网上教程一会儿from keras import ...一会儿from tensorflow.keras import ...其实后者才是当前推荐写法。1.2 为什么是这三件套组合选择这套组合的核心理由是「上手门槛和原理深度之间的平衡」。PyTorch 在科研圈确实流行2024 年的趋势也朝着动态图和灵活调试倾斜但对很多做工程落地、业务集成的开发者来说TensorFlow 的生态更完整从模型训练到SavedModel导出、再到移动端TFLite和浏览器里TF.js部署一条链路走到底。而 Keras 的抽象层级又足够高让新手不必在第一天就去啃计算图和自动微分的细节。我自己的体会是先通过 Keras 跑通模型再回头去理解底层概念这条路对普通开发者最友好。如果一开始就面对 TensorFlow 风格的底层代码比如tf.GradientTape、手写训练循环很容易被劝退。至于「为什么不用纯 Python 手写神经网络」那是为了理解原理做的练习不适合拿来搭建真实系统。这三件套恰好让入门曲线平滑又不至于和底层机制完全脱节。2. 环境搭建实操从 Python 到 GPU 加速的完整落地2.1 安装前的三个决定版本、虚拟环境、显卡驱动环境搭建是大部分新手的第一道坎而且坑往往不是模型代码写错而是版本之间互相不认。动手之前先把三件事定下来。第一Python 版本。TensorFlow 对 Python 版本的兼容是滞后且挑剔的。保险起见TensorFlow 2.10 附近用 Python 3.8 到 3.10 都没问题不要太追新Python 3.12 以上容易遇到一些依赖包还没适配的情况。我平时推荐 3.9 或 3.10稳定且生态兼容性最好。第二虚拟环境。强烈建议用 virtualenv 或 Conda 单独建一个环境不要直接装在系统 Python 里。原因很现实你机器上可能有其他项目依赖numpy1.x而 TensorFlow 要求numpy某个版本不用虚拟环境的话几个月后必出事。Conda 用户执行conda create -n tf python3.10virtualenv 用户执行python -m venv tf-env都是一个道理。第三显卡驱动和 CUDA 的关系。这里有个关键认知NVIDIA 驱动是向下兼容的驱动版本更高通常没问题但 TensorFlow 依赖的 CUDA 和 cuDNN 是它编译时就固定的版本。网上搜到那些「驱动版本 550.144.03、CUDA 版本失败」的报错帖多数是搞混了「系统驱动需要的最低要求」和「TensorFlow 要求的 CUDA 运行时版本」。2.2 TensorFlow 与 CUDA、cuDNN 的版本匹配看一眼就懂直接给一张我踩过坑之后整理的对照表对应 TensorFlow 2.5 到 2.16 的主要版本组合TensorFlow 版本Python 建议CUDA 版本配套cuDNN 版本Windows 支持 GPU 的方式2.5.03.7~3.911.28.1原生支持2.10.03.8~3.1011.28.1原生支持Windows 最后一代2.12.03.8~3.1111.8 或 12.08.6仅 WSL2 / Linux2.15.03.9~3.1212.28.9仅 WSL2 / Linux2.16.x3.9~3.1212.38.9仅 WSL2 / Linux注意一个很多人忽略的重点TensorFlow 2.11 开始Windows 上的 GPU 支持不再通过 pip 原生包提供官方推荐用 WSL2。所以如果你在 Windows 上想省心就停留在 2.10如果必须用最新版建议装一个 WSL2 Ubuntu 环境。驱动版本 550.144.03 这种只要你满足对应 CUDA 的最低驱动要求就能跑不要求系统预装「相同版本号」的 CUDA——pip 安装的 TensorFlow 自带运算库真正用的是显卡驱动。2.3 安装实操与版本锁定下面是最朴素的安装流程适合 Linux 和 Windows WSL2 场景。先建环境再安装# 创建并激活虚拟环境 python -m venv tf-env source tf-env/bin/activate # Windows 下是 tf-env\Scripts\activate # 安装 TensorFlow 2.10含 Keras pip install tensorflow2.10.0如果你的机器没有 NVIDIA 显卡或者只是想先在 CPU 上把流程跑通那就直接安装最新版 CPU 包pip install tensorflow-cpu装完之后用一段最简单的代码验证 GPU 是否可用import tensorflow as tf print(TensorFlow 版本:, tf.__version__) print(GPU 设备:, tf.config.list_physical_devices(GPU))如果能看到一个物理 GPU 设备列表说明你的环境没问题。看不到也不用慌CPU 也能跑通后面这个手写数字识别项目只是慢一点。初次运行 TensorFlow 会在启动时输出一大串日志这是正常的不要以为报错想安静一点可以在代码开头加一行tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR)。2.4 GPU 是否真的在工作怎么判断加速生效身边不少朋友装完环境训练时也不知道到底有没有用到 GPU。教你一个最简单的观察方法训练循环开始后打开任务管理器Windows或者nvidia-smi命令Linux/WSL看显卡的利用率。如果显存占用飙升、利用率时高时低说明模型正在 GPU 上计算如果一直是 0%那就是在 CPU 上硬跑。另一种方式是给模型加一个计时器对比 CPU 和 GPU 每个 epoch 的时间差异import time start time.time() model.fit(x_train, y_train, epochs5, batch_size32) print(训练耗时:, time.time() - start)同样的模型GPU 可能只要十几秒CPU 则要几十秒甚至几分钟。这里有一个很容易误导新手的现象小模型、小数据集上 GPU 不一定比 CPU 快因为数据在内存和显存之间搬运也需要时间。等模型变大、数据变多GPU 的优势才会体现出来。3. 神经网络到底在算什么从图像到高维向量的完整路径3.1 神经元与前馈神经网络的基本逻辑环境只是热身真正的核心是理解网络在工作时到底做了什么。神经网络的基本单元是「神经元」可以把它看成一个带权重的小计算器输入每个值先乘一个权重然后求和再加上偏置最后塞进一个激活函数。输出 激活函数( 输入1*权重1 输入2*权重2 ... 偏置 )一堆神经元按层排列数据只能从前向后传这就是前馈神经网络。之所以叫「前馈」是因为数据流动方向单一输入层 → 隐藏层 → 输出层没有回头路。很多文档里会提到「多层感知机」其实说的就是这种结构。那激活函数是干嘛用的一句话给网络引入非线性。如果没有激活函数无论堆多少层本质上都等价于一次线性变换那再深的网络也解不了稍微复杂一点的问题。就像搭积木如果全是方方正正没有凹凸的积木拼来拼去还是个长方体激活函数是那些卡扣让积木能搭出复杂形状。3.2 一张图像怎么变成高维向量拿手写数字识别来举例这个例子最能直观体现「图像变向量」的过程。一张 28×28 像素的灰度图有 784 个像素值网络做的第一件事就是把这 784 个数「拉平」成一个长度为 784 的一维向量。有人会问这不就是把图片拆开铺平了吗是的但这正是神经网络处理图像的起点。接下来这个 784 维向量和第一层隐藏层的权重矩阵相乘。如果隐藏层有 128 个神经元那权重矩阵的形状就是128×784一次矩阵乘法就把 784 维输入变成了 128 维特征向量。隐藏层再往后最终输出层如果是 10 个神经元对应数字 0~9权重矩阵形状就是10×128最后得到 10 个实数值。这 10 个原始分数再经过 Softmax 激活函数被压成 10 个和为 1 的概率值比如数字 7 的概率是 0.91、数字 1 是 0.05。那个「输出高维度向量」的说法其实可以概括为图像在前向传播中不断被压缩、提炼从像素空间映射到特征空间。人脸识别系统里输出向量往往有 128、256 甚至 512 维——同一张脸在不同照片下的向量距离很近不同人的脸向量距离很远。这就是深度学习做识别的最基础思想找一个好的高维向量表示。3.3 反向传播与梯度下降网络是如何学习的前向传播解决「给一个输入得出什么输出」但网络一开始权重都是随机的输出肯定不对。那怎么让权重变好答案是反向传播 梯度下降。简单说训练的过程就是猜答案然后根据错误修正的过程。先定义损失函数比如交叉熵它衡量「预测分布」和「真实标签分布」之间的差距然后通过链式法则从输出层向输入层反推每个权重对损失的影响有多大这叫作梯度。最后按梯度的反方向调整权重损失就会变小。反复迭代预测越来越准。我经常用一个「买菜砍价」的类比去解释梯度下降你心里有一个理想价格每报一次价看老板表情损失函数判断方向如果老板皱眉损失变大你就往反方向调整如果老板笑了损失变小说明方向对了继续走。学习率就是每次砍价的幅度——太大了容易砍过头太小了半天聊不成一单。4. 第一个实战5 分钟用 Keras 训练一个手写数字识别模型4.1 数据准备与归一化理论讲再多不如亲手跑一个模型。这里我们用 Keras 自带的 MNIST 数据集它是深度学习界的「Hello World」6 万张训练图、1 万张测试图全是 28×28 的手写数字灰度图。Keras 里直接下载就能用import tensorflow as tf from tensorflow import keras # 加载数据 (x_train, y_train), (x_test, y_test) keras.datasets.mnist.load_data() # 归一化把像素值从 0~255 压缩到 0~1 x_train x_train.astype(float32) / 255.0 x_test x_test.astype(float32) / 255.0 # 拉平把 28x28 变成 784 维向量 x_train x_train.reshape(-1, 784) x_test x_test.reshape(-1, 784)归一化那一步很多新手会漏掉但它是训练能否收敛的重要一环。原因不复杂如果不归一化输入数值范围在 0~255权重乘以大数值后加权求和结果动辄上百激活函数很容易进入饱和区梯度变得极小训练进展极慢。把数据缩放到 0~1 之后数值稳定训练又快又稳。这一步也直接对应着前面说的「图像变成向量」的预处理阶段。4.2 构建模型与编译接下来用 Keras 的Sequential逐层堆叠模型这是最直观的搭建方式model keras.Sequential([ keras.layers.Dense(128, activationrelu, input_shape(784,)), keras.layers.Dense(64, activationrelu), keras.layers.Dense(10, activationsoftmax) ]) model.compile( optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy] )三个Dense层分别表示第一层 128 个神经元、激活函数 ReLU第二层 64 个神经元、ReLU最后一层 10 个神经元、Softmax 输出概率。adam优化器是目前最常用的自适应学习率优化器对新手非常友好——它自动帮你调节学习率不用手工去调衰减策略。sparse_categorical_crossentropy是「整数标签」配对的损失函数因为我们这里的y_train是数字 0~9 的整数不是 one-hot 编码。我在这个模型的选择上故意没有加 Dropout也没做太多正则化因为手写数字识别本身任务简单目的是让你看清网络最朴素的骨架。后面遇到更复杂的任务时再加 Dropout、BN 这些「防御措施」也不迟。4.3 训练过程看什么训练只调一行fithistory model.fit( x_train, y_train, epochs10, batch_size32, validation_split0.2, verbose1 )epochs10表示整个训练集过 10 遍batch_size32表示每 32 张图算一次梯度更新validation_split0.2表示从训练集里留 20% 当验证集边训练边检查有没有过拟合。训练时你会看到类似这样的控制台输出Epoch 1/10 1500/1500 [] - 3s 2ms/step - loss: 0.2874 - accuracy: 0.9153 - val_loss: 0.1492 - val_accuracy: 0.9560这里每行信息都值得看loss是训练集上的损失accuracy是训练准确率val_loss和val_accuracy是验证集上的表现。理想情况是训练集准确率一路上升、损失逐步下降。如果发现train_loss还在降但val_loss开始抬头说明过拟合了得加正则化或减少 epochs。4.4 模型评估与预测训练完成之后用测试集做最终评估再挑一张真实图来看预测结果# 测试集评估 test_loss, test_acc model.evaluate(x_test, y_test, verbose0) print(测试集准确率:, test_acc) # 预测前 5 张图 import numpy as np predictions model.predict(x_test[:5]) for i in range(5): predicted_label np.argmax(predictions[i]) print(f第 {i1} 张图预测为: {predicted_label})model.predict返回的是每个类别的概率分布也就是一个 10 维向量np.argmax取出概率最高的位置。看到这里你已经走通了「图像输入 → 高维向量输出 → 类别判断」的完整链路。我强烈建议你把预测的向量打印出来看一眼比如某个数字 7 的预测向量可能是[0.01, 0.02, ..., 0.91, ...]比纯看准确率有感觉得多。5. 新手最容易栽的跟头安装与训练问题速查5.1 安装相关的典型问题与解法很多读者私信给我的报错翻来覆去就那么几类。我把它们整理成一个速查表遇到问题直接对照处理现象可能原因处理方法提示Could not load dynamic library cudnn64_8.dllTensorFlow 2.10 需要 cuDNN 8.1 配套下载对应版本 cuDNN将bin目录加入系统 PATH或改用 WSL2提示No module named tensorflow但是明明 pip 显示已安装装进了别的 Python 环境确认pip list和python指到同一个环境which python查看路径训练日志里说CPUGPU 列表为空驱动不匹配或没有 GPU 版检查nvidia-smi是否正常输出确认 TensorFlow 版本不是 CPU 包pip install tensorflow提示找不到匹配版本Python 版本过高或网络源问题降低 Python 版本到 3.10使用-i https://pypi.tuna.tsinghua.edu.cn/simple镜像源驱动版本 550.144.03 但报 CUDA 错误驱动版本本身没问题缺的是 TensorFlow 配套运行时正确安装对应版本的 CUDA/cuDNN不能用「系统显卡驱动版本」替代5.2 训练过程中的常见问题和排查思路安装问题大多版本对照表能解决训练阶段的坑每一个都值得留个心眼问题一训练 loss 不降还往上走。先检查学习率是不是太大adam的默认学习率大多数时候没问题但如果你手动设置了learning_rate0.1就可能震荡。其次检查输入有没有归一化我见过几次x_train忘记除以 255训练曲线像心电图一样上下跳。再不行就减小模型规模新手一上来把隐藏层设成 1024 个神经元小数据集上反而容易训练不稳。问题二显存不足 Out of Memory。这是最常见的硬件类问题。解决办法按优先级排序调小batch_size比如 64 改成 16减小模型规模减少数据加载时占用不要一次性把全量数据堆到内存里做预处理。如果本身显存就 4G 以下建议直接考虑云端 GPU。问题三验证集准确率比训练集低一大截。基本上就是过拟合。轻量处理是加 Dropout 层、加 L2 正则化或者把validation_split调小一点保证验证集统计稳定。另外提醒一点MNIST 这个数据集本身干净真实业务数据往往有标签噪声那种情况优先清洗数据不要急着调模型。问题四预测一张自定义图片结果总是不对。先检查输入格式是不是 784 维向量像素值有没有除以 255model.predict的输入要求是(batch, 784)哪怕只有一张图也要变成(1, 784)很多人在这一步卡住。5.3 一套更省心的开发姿势最后分享一个我自己的习惯训练之前先打印model.summary()看清楚每一层的输出形状和参数量。Keras 里model.summary()会显示类似下面这样的信息Layer (type) Output Shape Param # dense (Dense) (None, 128) 100480 dense_1 (Dense) (None, 64) 8256 dense_2 (Dense) (None, 10) 650 看到(None, 128)里的None了吗那个维度是 batch size训练时自动填充。每次模型行为异常我都会先确认这里输出 shape 是不是 10参数数量是不是合理100480这个数怎么来的就是784*128 128权重的乘积加上偏置。搞清楚这张表你对网络结构的掌控力会立刻上一个台阶。跑完这个手写数字识别也就意味着你正式迈入了神经网络的门槛。后面真正有意思的部分才刚刚开始卷积神经网络怎么处理彩色图片、LSTM 如何理解时间序列、模型怎么导出部署成真实服务。我个人建议你接下来做两件事一是把上面的模型动手敲一遍哪怕准确率已经很高也要亲手改一改隐藏层数、神经元数量体会参数变化带来的差异二是在本地保存好 Jupyter Notebook后续系列会在此基础上扩展。等你在实际运行中再遇到什么奇怪的问题大概率都能在今天这份问题清单里找到影子。我们下一篇见。
阅读完成 · 觉得有帮助?
咨询建站