Ubuntu 20.04 纯CPU安装Caffe完整指南:从编译踩坑到MNIST跑通
如果你手上是一台没有独立显卡的服务器或者只是想快速跑通一个 Caffe 老模型Ubuntu 20.04 上装 Caffe-CPU 版绝对算得上“看起来简单、真做起来到处碰壁”的项目。我这次从零开始记录下每一步操作和编译期间遇到的所有问题包括hdf5.h找不到、boost_python3链接失败、protobuf 版本对不上这一系列典型坑。这篇文章适合想用纯 CPU 环境跑 Caffe、又刚好在 Ubuntu 20.04 上折腾的同学全部过程都经过实测照着做基本能一次过。很多教程直接让你make clean make all实际上在 20.04 上会连续栽在头文件路径、旧构建系统和 OpenCV 4 兼容性上。我下面的流程刻意避开了花哨的方案走的是最稳的路径官方源码 系统 apt 依赖 Python 3.8 虚拟环境编译选项只开 CPU_ONLY。1. 踩坑前的准备环境基线、仓库选择与整体思路1.1 为什么在 20.04 上装“纯 CPU 版”反而更麻烦Caffe 本身是一个很老的框架它的最后一次大版本迭代停留在几年前而 Ubuntu 20.04 默认自带的是 Python 3.8、GCC 9、OpenCV 4.x、protobuf 3.6。这几个版本对老 Caffe 来说都不是最舒适的搭配尤其是编译时用到的头文件路径和动态库命名跟老教程里的 Ubuntu 16.04 时代差别很大。这事就像把一个老游戏装在新系统上光有安装包没用得先把系统里的“兼容层”一个个对齐。我之前的机器是 Ubuntu 20.04.6默认 GCC 9.4没有独立显卡。需要注意如果系统里已经有 GCC 10 并且被设为默认编译器建议装一下gcc-9、g-9并把默认版本切回去因为 GCC 10 更严格的检查会让 Caffe 的旧代码冒出很多“新问题”。1.2 源码仓库选择用 master 而不是旧 tag我直接拉取的是 BVLC 官方仓库的 master 分支不是某个1.0老 tag。原因很简单master 分支里包含了对 OpenCV 4 和 Python 3 的兼容性修正老 tag 里的 Makefile 默认还在找 Python 2 的头文件编译出来的东西在 20.04 上基本没法用。cd ~ git clone https://github.com/BVLC/caffe.git cd caffe如果网络慢也可以先下载压缩包解压。拉完源码之后不要急着make先静下心把依赖装齐否则后续报错会把排查成本无限拉高。2. 依赖安装一条 apt 命令和 Python 环境的取舍2.1 apt 包清单与每个包的作用Ubuntu 20.04 上安装 Caffe-CPU 需要的系统级依赖比想象中多但一条 apt 命令就能覆盖绝大部分。我实际执行的是sudo apt update sudo apt install -y build-essential cmake git pkgconf \ libprotobuf-dev protobuf-compiler \ libleveldb-dev libsnappy-dev libhdf5-dev \ libboost-system-dev libboost-filesystem-dev libboost-python-dev \ libgflags-dev libgoogle-glog-dev liblmdb-dev \ libopenblas-dev liblapack-dev \ libopencv-dev python3-dev python3-pip python3-venv这里有几个包值得单独解释一下libprotobuf-dev和protobuf-compilerCaffe 运行时要加载 protobuf 生成的序列化代码编译阶段需要用protoc生成caffe.pb.h。Ubuntu 20.04 仓库里的 protobuf 是 3.6.1这个版本对 Caffe 是安全的不要为了追新去装更新的 protobuf。libleveldb-dev、libsnappy-dev、liblmdb-devCaffe 读取训练数据的 LMDB / LevelDB 格式依赖这几个库。少装了任何一 个make all都会直接报头文件找不到。libboost-python-dev这是 Python 接口的关键后面我遇到的最大链接问题就出在它身上。libopenblas-devCPU 版 Caffe 的矩阵运算走 OpenBLAS比 ATLAS 快不少。后面Makefile.config里BLAS : open会用到它。libopencv-devCaffe 处理图像时要读取 OpenCV 的 Mat 结构20.04 自带的是 OpenCV 4.2足够用了。如果你不确定某个包是否装上可以用dpkg -l | grep opencv这种命令确认。我建议直接一次性装齐避免在编译中途停下来补装因为每次补装都可能改变库文件搜索路径再一次触发头文件找不到的问题。2.2 Python 3.8 虚拟环境把 protobuf 隔离起来系统 Python 3.8 和 Caffe 源码配合没问题但我强烈建议创建一个独立的 Python 虚拟环境把protobuf、numpy、scikit-image这些 Python 依赖放进去。原因后面细说核心是 Caffe 的 Python 模块和它的 C 库必须共享同一套 protobuf 版本如果 Python 全局环境里被其他框架换成了新版 protobuf导入 Caffe 时会出现各种诡异的运行时错误。python3 -m venv ~/caffe-env source ~/caffe-env/bin/activate pip install -U pip setuptools wheel pip install numpy scikit-image protobuf3.6.1这里把protobuf锁在3.6.1是因为系统libprotobuf-dev也是 3.6.1。Python 侧的google.protobuf和 C 侧的libprotobuf版本一旦不一致轻则警告重则直接报Unknown solver type或Unknown layer type。2.3 克隆源码与 Makefile.config 的生成依赖装完后回到~/caffe目录先复制一份配置文件cd ~/caffe cp Makefile.config.example Makefile.config这个Makefile.config是整个安装过程的核心之后的报错有一半都跟它没配好有关。下一节我会把每一行关键配置拆开讲不光是贴配置还会说明为什么这么改。3. Makefile.config 关键配置CPU_ONLY 与大坑预防3.1 必须改的行用编辑器打开Makefile.config我这次做了以下几处修改# 开启 CPU-only 模式跳过 CUDA 相关编译 CPU_ONLY : 1 # 没有 GPUCUDNN 必须关闭 USE_CUDNN : 0 # 用 OpenBLAS BLAS : open BLAS_INCLUDE : /usr/include/x86_64-linux-gnu BLAS_LIB : /usr/lib/x86_64-linux-gnu # Python 3.8 配置 PYTHON_LIBRARIES : boost_python3 python3.8 PYTHON_INCLUDE : /usr/include/python3.8 # 追加 HDF5 头文件和库的搜索路径 INCLUDE_DIRS : $(PYTHON_INCLUDE) /usr/local/include /usr/include/hdf5/serial LIBRARY_DIRS : $(PYTHON_LIB) /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/hdf5/serial上面这几行里CPU_ONLY : 1是最关键的开关。千万要注意如果之前复制过旧版本的Makefile.config很可能默认是注释掉的不取消注释的话即使没有显卡Makefile 仍然会尝试找 CUDA 工具链然后报一堆nvcc not found之类的错误。PYTHON_LIBRARIES里的python3.8对应的是libpython3.8.soPYTHON_INCLUDE对应/usr/include/python3.8。这两个地方如果写成python3.6m或/usr/include/python3.6m在 20.04 上必然找不到头文件。3.2 HDF5 头文件路径为什么老是找不到Caffe 的数据层需要 HDF5但 Ubuntu 20.04 把 HDF5 的头文件放在了一个子目录/usr/include/hdf5/serial/而不是常规的/usr/include/hdf5/。如果Makefile.config里不加INCLUDE_DIRS /usr/include/hdf5/serial编译到hdf5.hpp时就会报fatal error: hdf5.h: No such file or directory很多人在这里卡住以为是自己 HDF5 没装好其实只是路径没配。同理库文件在/usr/lib/x86_64-linux-gnu/hdf5/serial/所以LIBRARY_DIRS里也要把这一项加上。3.3 Boost Python 链接从 py38 到 py3 的符号链接这一节可能是最有价值的避坑点。Ubuntu 20.04 的libboost-python-dev装好后系统里的动态库文件名是libboost_python-py38.so但 Caffe 的 Makefile 在链接 Python 接口时会去找-lboost_python3。正常情况下-lboost_python3会被解析成libboost_python3.so而 20.04 不一定会提供这个软链接。第一次跑make all时我看到的报错是/usr/bin/ld: cannot find -lboost_python3解决办法有两种一是用参数指定二是直接建一个符号链接。我选的是符号链接方式sudo ln -s /usr/lib/x86_64-linux-gnu/libboost_python-py38.so /usr/lib/x86_64-linux-gnu/libboost_python3.so建立软链接后再编译就不会卡在这一步。如果你的系统是其他 Python 版本比如 3.10就把py38替换成对应的数字。这个坑几乎是 Ubuntu 20.04 上 Caffe-CPU 安装的重灾区因为老教程里都是直接说“安装libboost-python-dev就好”没人提醒你库文件命名已经变了。4. 编译阶段报错全记录make all / make test / make pycaffe4.1 第一次 make all 的报错清单与修复环境变量和配置文件都确认无误后执行编译make -j4这里我建议不要一上来就用-j$(nproc)尤其是内存只有 4G 左右的机器。先用-j4或直接make -j1遇到报错时日志完整度更高不容易被多线程输出冲散。我这次踩到的报错按出现顺序整理如下报错关键字根因修复方式fatal error: hdf5.hHDF5 头文件路径未加入INCLUDE_DIRS在Makefile.config的INCLUDE_DIRS中加入/usr/include/hdf5/serialcannot find -lboost_python3系统库名为libboost_python-py38.soMakefile 找的是libboost_python3.so建立符号链接sudo ln -s ...fatal error: leveldb/db.h没有安装libleveldb-dev执行sudo apt install libleveldb-devfatal error: caffe.pb.h没有生成 protobuf 的 C 代码或者没有先执行make proto先执行make proto再重新make allcannot find -lcblas或cannot find -lopenblasBLAS 库未装或BLAS : atlas但缺少 ATLAS 库安装libopenblas-dev liblapack-dev并保持BLAS : open其中caffe.pb.h这个坑最容易迷惑人。如果你执行make pycaffe而不是make all编译 Python 接口时就会直接找include/caffe/proto/caffe.pb.h但该文件是由protoc编译.proto文件生成的得先执行make proto执行完make proto后再回到顶层目录重新编译。整个make all过程大约需要几分钟取决于机器性能。如果编译过程中弹出一堆没见过的警告不用慌Caffe 的代码是老的GCC 9 给出一堆-Wunused-but-set-variable之类的提示很正常只要最后能看到生成build/lib/libcaffe.so就算过了。4.2 make runtest 的验证与合理预期make all通过后我建议顺手跑一下官方单元测试make test -j4 make runtestCPU 模式下的make runtest会跑一大批层测试和网络测试绝大多数都能通过。个别跟 GPU memory 相关的测试会被跳过或报ERROR这属于预期行为因为CPU_ONLY : 1本身就是把 CUDA 路径排除了。如果你看到测试里出现几十个FAILED先别急着怀疑安装有误多数情况下是 protobuf 版本不匹配导致的Unknown layer type。这时候回到 Python 环境检查pip show protobuf的输出把它退回 3.6.1然后make clean make all再测一遍。4.3 make pycaffe 与导入验证核心库编译完成后编译 Python 接口make pycaffe -j4这一步结束后~/caffe/python目录下会生成一个caffe模块。验证方式很简单source ~/caffe-env/bin/activate export CAFFE_ROOT$HOME/caffe export PYTHONPATH$CAFFE_ROOT/python:$PYTHONPATH export LD_LIBRARY_PATH$CAFFE_ROOT/build/lib:$LD_LIBRARY_PATH python -c import caffe; print(caffe.__version__)能打印版本号就代表 Python 接口可用了。如果在这一步报libcaffe.so.1.0.0: cannot open shared object file说明动态库搜索路径没配执行上面的export LD_LIBRARY_PATH就能解决。我实际遇到过一次导入时直接报AttributeError: module google.protobuf.internal has no attribute ...检查后发现是虚拟环境里的 protobuf 被升级到了更晚版本跟系统libprotobuf不一致。重新执行pip install protobuf3.6.1后就好了。5. 用 MNIST 打通 CPU 训练验证5.1 下载数据并生成 LMDB环境装好不代表真能用我习惯用 Caffe 自带的 MNIST 例子做最终验证。第一步是下载数据cd ~/caffe ./data/mnist/get_mnist.sh脚本会从 MNIST 官网下载四个 gzip 压缩包然后自动解压到data/mnist/目录。如果机器上没装wget要先装一下sudo apt install -y wget接着生成 LMDB 格式的训练集和测试集./examples/mnist/create_mnist.sh这一步如果报错说找不到convert_mnist_data.bin说明前面make all时工具链没编译完全回到make all再把编译结果补齐。如果报文件大小不符或解压失败通常是下载不完整删掉data/mnist下压缩包重新执行脚本即可。5.2 修改 solver 为 CPU 模式并开始训练Caffe 自带的 MNIST 示例默认使用 GPU 模式纯 CPU 环境必须改。最简单的方式是用sed把 solver 里的solver_mode改成 CPUsed -i s/solver_mode: GPU/solver_mode: CPU/ examples/mnist/lenet_solver.prototxt修改后执行训练脚本./examples/mnist/train_lenet.sh第一次跑的时候控制台会先打印一堆网络结构信息接着出现进度条。CPU 模式下 LeNet 这种小网络跑得并不慢一万次迭代大概也就是几分钟的事。训练结束时能看到类似accuracy 0.9906的输出说明整条链路已经打通。如果训练过程中报Check failed: registry.count(type) 1 (0 vs. 1) Unknown layer type: ...几乎可以确定是 protobuf 版本错乱。解决方案和前面一样确认虚拟环境的 protobuf 版本和系统库一致必要时make clean make all重新编译。5.3 训练结果与常见运行期问题MNIST 跑通后caffe这个 Python 模块也能正常加载网络结构做前向推理。我这里补充两个运行期容易遇到的问题如果训练时数据集路径不对会报Check failed: blob.count() 0检查examples/mnist/mnist_train_lmdb目录是否存在以及lenet_train_test.prototxt里的数据源路径是否写对。如果控制台一直不输出迭代日志只有空白卡住多半是 CPU 线程数配置问题。可以在Makefile.config里打开OPENMP : 1或者把 batch size 调小一些。6. 收尾环境变量、protobuf 版本冲突和我的建议装到这里Caffe-CPU 已经可以正常使用了。我最后必须提醒一句这台机器上如果未来还要装其他深度学习框架protobuf会是你第一个需要提防的冲突源。Caffe 依赖的是很老的 protobuf 接口而 PyTorch、TensorFlow 这类框架的依赖管理器很容易把 Python 侧的 protobuf 升级到 3.19 甚至更高到时候再导入 Caffe 就会出现各种“灵异事件”。我的做法是把 Caffe 相关的环境变量统一放在一个单独的脚本里每次使用前手动 source而不是一股脑写进.bashrcexport CAFFE_ROOT$HOME/caffe export PYTHONPATH$CAFFE_ROOT/python:$PYTHONPATH export LD_LIBRARY_PATH$CAFFE_ROOT/build/lib:$LD_LIBRARY_PATH写进.bashrc也没问题但会让其他项目在导入 Python 模块时无端多一条搜索路径容易引发隐藏依赖冲突。如果想彻底省心就把 Caffe 留在虚拟环境里用的时候激活环境不用就退出。最后再分享一个小技巧如果后续跑旧模型时遇到Unknown solver type: SGD或Unknown layer type先别怀疑模型文件写错了绝大多数是 Caffe 二进制库和 Python protobuf 之间版本错位导致的。这时候把~/caffe/python下生成的__pycache__删除重新make pycaffe再对齐 protobuf 版本问题基本都会消失。Caffe 的确很老但在纯 CPU 环境下它依旧是一个非常轻量、能让你理解卷积网络底层数据流的好工具。