Caffe安装

克隆 Caffe 仓库

git clone https://github.com/BVLC/caffe.git

cd caffe

配置 Caffe:

cp Makefile.config.example Makefile.config

修改Makefile.config

取消注释CPU_ONLY := 1

开始编译

make all -j4

编译期间会遇到非常多的问题,基本是依赖库没装,和opencv的版本可能有些关键字被舍弃了,需要更改

安装需要的依赖库

sudo apt-get install -y cmake git g++ wget protobuf-compiler libatlas-base-dev libboost-all-dev libgflags-dev libgoogle-glog-dev libhdf5-serial-dev libleveldb-dev liblmdb-dev libopencv-dev libopenblas-dev libprotobuf-dev libsnappy-dev python-dev python-numpy python-scipy

找不到hdf5.h文件

打开 Makefile.config 文件,找到 INCLUDE_DIRS 和 LIBRARY_DIRS 部分,确保包含 HDF5 的路径

添加

INCLUDE_DIRS := $(INCLUDE_DIRS) /usr/include/hdf5/serial
LIBRARY_DIRS := $(LIBRARY_DIRS) /usr/lib/x86_64-linux-gnu

安装到系统

mkdir build

cmake ..

make all -j10

sudo make install

模型训练

测试mnist手写识别训练集过程

运行./data/example/mnist/get_mnist.sh会自动下载四个文件

分别为

train-images-idx3-ubyte.gz:训练图像

train-labels-idx1-ubyte.gz:训练标签

t10k-images-idx3-ubyte.gz:测试图像

t10k-labels-idx1-ubyte.gz:测试标签

这四个数据集因为网络问题没有下载成功

运行./examples/mnist/create_mnist.sh

使用Caffe提供的脚本来将数据集转换为LMDB格式

确保数据集已经正确转换为LMDB格式。你应该在/caffe/examples/mnist目录下看到以下文件:

mnist_train_lmdb

mnist_test_lmdb

定义网络文件和求解器文件

运行./examples/mnist/train_lenet.sh开始模型训练

学习如何写网络文件和求解器文件

开始训练

前提得先在example/mnist定义lenet文件夹

确保lenet_sovler.prototxt文件夹中对其他文件的路径使用绝对路径

caffe train --solver=lenet_solver.prototxt

训练好的模型和权重文件为:

模型文件是lenet_train_test.prototxt,权重文件是lenet_iter_10000.caffemodel

I0117 10:16:31.496856 25788 sgd_solver.cpp:273] Snapshotting solver state to binary proto file /home/work/caffe/caffe/examples/mnist/lenet_iter_10000.solverstate

这条信息说明Caffe正在将求解器的状态(包括学习率、动量等参数)保存到另一个二进制的协议缓冲文件中。文件名是lenet_iter_10000.solverstate,同样表示这是第10000次迭代的求解器状态快照。

如何调用该模型来进行识别

  • 使用Caffe的caffe命令行工具加载你的模型和权重文件。
  • 例如,如果你的模型文件是lenet_train_test.prototxt,权重文件是lenet_iter_10000.caffemodel,你可以使用以下命令:
caffe test --model=lenet_train_test.prototxt --weights=lenet_iter_10000.caffemodel --image=your_image.jpg
  • 在这个命令中,your_image.jpg是你想要识别的图像文件。
  1. 运行识别
  • 执行上述命令后,Caffe将加载模型,处理图像,并输出识别结果。
  • 识别结果通常包括每个类别的概率分数。

实际上是不能直接使用 --image=your_image.jpg来直接指定图片文件来进行识别

应该要先转换为LMDB格式

将测试图片转换为LMDB

写个Python或者C++程序实现

还是Python转换比较方便

使用caffe自带工具转换为 LMDB:

  1. 准备数据
    将所有需要转换的 .jpg 文件放在一个目录中,例如 /path/to/images
  2. 创建列表文件
    创建一个文本文件,列出所有图像文件的路径及其标签(如果适用)。例如,创建一个名为 train.txt 的文件,内容如下:
/path/to/images/image1.jpg 0
/path/to/images/image2.jpg 1
/path/to/images/image3.jpg 2

转换为 LMDB:

使用 Caffe 提供的 convert_imageset 工具将图像文件转换为 LMDB 格式。假设 train.txt 文件已经创建好,可以使用以下命令:

# 转换为训练集
build/tools/convert_imageset --shuffle --resize_height=28 --resize_width=28 /home/work/caffe/caffe/examples/mnist/images/train.txt /home/work/caffe/caffe/examples/mnist/image/train_lmdb

修改lenet_train_test.prototxt模型中的测试集的路径,即可开始测试

caffe test --model=lenet_train_test.prototxt --weights=lenet_iter_10000.caffemodel

caffe工具包:

Caffe 提供了一系列的命令行工具,用于数据处理、模型训练、模型评估等任务。以下是 build/tools 目录下的一些常用工具及其功能:

caffe

  • 功能:这是 Caffe 的主要命令行工具,用于训练模型、测试模型、提取特征等。
  • 用法
./caffe train --solver=solver.prototxt
./caffe test --model=test.prototxt --weights=model.caffemodel

compute_image_mean

  • 功能:计算图像数据集的均值文件,通常用于数据预处理。
  • 用法
./compute_image_mean --backend=lmdb train_lmdb mean.binaryproto

convert_imageset

  • 功能:将图像文件转换为 LMDB 或 LevelDB 格式,用于数据集的高效存储和读取。
  • 用法
./convert_imageset --shuffle --resize_height=256 --resize_width=256 /path/to/images/ train.txt /path/to/output/train_lmdb

extract_features

  • 功能:从训练好的模型中提取特征。
  • 用法
./extract_features --gpu 0 --model=model.prototxt --weights=model.caffemodel --blobs=fc7 --output_dir=output_dir

upgrade_net_proto_text

  • 功能:将旧版本的网络定义文件(.prototxt)升级到新版本。
  • 用法
./upgrade_net_proto_text old_net.prototxt new_net.prototxt

upgrade_net_proto_binary

  • 功能:将旧版本的网络权重文件(.caffemodel)升级到新版本。
  • 用法
./upgrade_net_proto_binary old_model.caffemodel new_model.caffemodel

upgrade_solver_proto_text

  • 功能:将旧版本的求解器配置文件(.prototxt)升级到新版本。
  • 用法
./upgrade_solver_proto_text old_solver.prototxt new_solver.prototxt

cmake_install.cmake

  • 功能:CMake 生成的安装脚本,用于安装 Caffe。
  • 用法:通常不需要手动调用,由 CMake 自动处理。

CMakeFiles

  • 功能:CMake 生成的文件目录,包含编译过程中生成的文件。
  • 用法:通常不需要手动操作。

Makefile

  • 功能:CMake 生成的 Makefile,用于编译 Caffe。
  • 用法
make

将LMDB格式转换为JPG格式

使用 Caffe 的 dump_lmdb_data 工具

Caffe 还提供了一个命令行工具 dump_lmdb_data,可以用来将 LMDB 数据导出为图像文件。以下是使用方法:

  1. 确保 Caffe 已经编译
    确保你已经编译了 Caffe,并且 build/tools 目录下有 dump_lmdb_data 工具。
  2. 运行 dump_lmdb_data 工具
    使用以下命令将 LMDB 数据导出为图像文件:
build/tools/dump_lmdb_data --db=/path/to/your/lmdb --output=/path/to/output/images

创建转换脚本//不行

使用以下 Python 脚本 convert_lmdb_to_images.py

import lmdb
import cv2
import numpy as np
import os

def convert_lmdb_to_images(lmdb_path, output_dir):
    env = lmdb.open(lmdb_path, readonly=True)
    with env.begin() as txn:
        cursor = txn.cursor()
        for key, value in cursor:
            datum = caffe.proto.caffe_pb2.Datum()
            datum.ParseFromString(value)
            data = caffe.io.datum_to_array(datum)
            image = data.transpose(1, 2, 0)
            image = (image * 255).astype(np.uint8)
            image_path = os.path.join(output_dir, f"{key.decode()}.jpg")
            cv2.imwrite(image_path, image)
            print(f"Saved {image_path}")

if __name__ == "__main__":
    lmdb_path = "/path/to/your/lmdb"
    output_dir = "/path/to/output/images"
    os.makedirs(output_dir, exist_ok=True)
    convert_lmdb_to_images(lmdb_path, output_dir)
  1. 运行脚本
    保存上述脚本为 convert_lmdb_to_images.py,然后在终端中运行:
python3 convert_lmdb_to_images.py

将数据集转换为照片

python脚本程序

import idx2numpy
from PIL import Image
import os

# 文件路径
file_path = '/home/work/caffe/caffe/data/mnist/train-images-idx3-ubyte'
output_dir = '/home/work/caffe/caffe/examples/mnist/image/image'

# 创建输出目录
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# 读取IDX文件
images = idx2numpy.convert_from_file(file_path)

# 将每个图像保存为JPG文件
for i, image in enumerate(images):
    img = Image.fromarray(image)
    img.save(os.path.join(output_dir, f'image_{i}.jpg'))

print(f'所有图像已保存到 {output_dir} 目录下。')

编写C++程序进行手写数字识别

需要指定caffe的include和lib路径

g++ -std=c++11 -I/home/work/caffe/caffe/include -L/home/work/caffe/caffe/build/lib -lcaffe -o test test.cpp

./test_caffe

C++程序

#include <iostream>
#include <opencv2/opencv.hpp>
#include <caffe/caffe.hpp>

using namespace std;
using namespace cv;
using namespace caffe;

int main(int argc, char** argv) {
    // 初始化Caffe
    Caffe::set_mode(Caffe::CPU);

    // 加载模型
    string model_file = "/home/work/caffe/caffe/examples/mnist/lenet_train_test.prototxt";
    string trained_file = "lenet_iter_10000.caffemodel";
    string mean_file = ""; // 如果有均值文件,可以在这里指定

    Net<float> net(model_file, TEST);
    net.CopyTrainedLayersFrom(trained_file);

    // 读取图片
    Mat image = imread("path_to_your_image.png", IMREAD_GRAYSCALE);
    if (image.empty()) {
        cerr << "无法读取图片" << endl;
        return -1;
    }

    // 预处理图片
    resize(image, image, Size(28, 28));
    image.convertTo(image, CV_32F, 1.0 / 255.0);

    // 创建Blob
    Blob<float> blob;
    blob.Reshape(vector<int>{1, 1, 28, 28});
    float* blob_data = blob.mutable_cpu_data();
    for (int i = 0; i < 28; ++i) {
        for (int j = 0; j < 28; ++j) {
            blob_data[i * 28 + j] = image.at<float>(i, j);
        }
    }

    // 设置输入
    net.set_input_blobs(&blob);

    // 前向传播
    net.ForwardPrefilled();

    // 获取预测结果
    Blob<float>* output_layer = net.output_blobs()[0];
    const float* begin = output_layer->cpu_data();
    const float* end = begin + output_layer->count();
    int predicted_class = max_element(begin, end) - begin;

    cout << "预测的数字是: " << predicted_class << endl;

    return 0;
}

如何编译caffe的时候仅CPU模式编译,并且提供给C++的接口也是不会报缺CUDA库的错,该在编译前做哪些操作

使用-DCPU_ONLY指令过滤:

g++ -std=c++11 -DCPU_ONLY -I/home/work/caffe/caffe/include -L/home/work/caffe/caffe/build/lib -lcaffe -o test test.cpp

出现库没找到

g++ -std=c++11 -DCPU_ONLY -I/home/work/caffe/caffe/build/include -I/home/work/caffe/caffe/include -L/home/work/caffe/caffe/build/lib -lcaffe -o test test.cpp

完整的编译命令(包括 Google Logging 库)

g++ -std=c++11 -DCPU_ONLY -I/home/work/caffe/caffe/build/include -I/home/work/caffe/caffe/include -L/home/work/caffe/caffe/build/lib -lcaffe -o test test.cpp $(pkg-config --libs opencv) -lboost_system -lboost_thread -lglog

调整顺序后编译通过:

g++ -std=c++11 -DCPU_ONLY -I/home/work/caffe/caffe/build/include -I/home/work/caffe/caffe/include -L/home/work/caffe/caffe/build/lib -o test test.cpp $(pkg-config --libs opencv) -lboost_system -lboost_thread -lglog -lcaffe

使用Opencv4.x+dnn实现mnistC++

下载安装OpenCV4.x

下载 OpenCV 4.x 源码

wget -O opencv.zip https://github.com/opencv/opencv/archive/4.x.zip
unzip opencv.zip
cd opencv-4.x

编译和安装

mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make -j4
sudo make install

确认 OpenCV 4 的安装路径

ls /usr/local/include/opencv4

设置 LD_LIBRARY_PATH 环境变量

export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
为了使这个设置永久生效,可以将上述命令添加到你的 ~/.bashrc 文件中:

echo 'export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
3. 更新 ldconfig 配置
确保 ldconfig 能够找到 OpenCV 4 的库文件:

sudo ldconfig

编译和执行代码

g++ test02.cpp -o test02 -I/usr/local/include/opencv4 -L/usr/local/lib -lopencv_dnn -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_imgcodecs

./test02

代码

#include <iostream>  
#include <opencv2/opencv.hpp>  
#include <opencv2/dnn.hpp>  

using namespace std;
using namespace cv;
using namespace cv::dnn;

/* 
 * 找到概率最高的类别(即概率最大的类别)
 * @param probBlob 概率矩阵
 * @param classId 返回的类别ID
 * @param classProb 返回的类别概率
 */
static void getMaxClass(const Mat &probBlob, int *classId, double *classProb)
{
    // 将概率矩阵重塑为1行
    Mat probMat = probBlob.reshape(1, 1);
    // 找到概率矩阵中的最大值及其位置
    Point classNumber;
    minMaxLoc(probMat, NULL, classProb, NULL, &classNumber);
    // 将类别ID设置为最大值的位置
    *classId = classNumber.x;
}

int main()
{
    // 模型配置文件路径
    string modelTxt = "/home/work/caffe/caffe/examples/mnist/deploy.prototxt";
    // 模型权重文件路径
    string modelBin = "/home/work/caffe/caffe/examples/mnist/lenet_iter_10000.caffemodel";

    // 读取图像
    Mat img = imread("/home/work/caffe/caffe/examples/mnist/image/image4.jpg", 0);

    // 将图像转换为blob,用于输入到神经网络
    Mat inputBlob = dnn::blobFromImage(img, 0.00390625f, Size(28, 28), Scalar(), false);

    // 创建神经网络对象
    dnn::Net net;
    try {
        // 从Caffe模型文件中读取网络
        net = dnn::readNetFromCaffe(modelTxt, modelBin);
    } catch (cv::Exception &ee) {
        // 捕获异常并输出错误信息
        cerr << "Exception: " << ee.what() << endl;
        if (net.empty()) {
            // 如果网络为空,输出错误信息并退出程序
            cout << "Can't load the network by using the flowing files:" << endl;
            cout << "modelTxt: " << modelTxt << endl;
            cout << "modelBin: " << modelBin << endl;
            exit(-1);
        }
    }

    // 设置网络的输入,"data" 是输入层的名称
    net.setInput(inputBlob, "data");

    // 计算输出,"prob" 是输出层的名称
    Mat pred = net.forward("prob");

    // 输出预测结果
    cout << pred << endl;

    // 获取最佳类别和概率
    int classId;
    double classProb;
    getMaxClass(pred, &classId, &classProb);

    // 输出最佳类别
    cout << "Best Class: " << classId << endl;

    // 输出概率
    cout << "Probability: " << classProb * 100 << "%" << endl;

    return 0;
}

deploy.prototxt文件

name: "LeNet"
input: "data"
input_shape {
  dim: 1
  dim: 1
  dim: 28
  dim: 28
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 20
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "conv2"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 50
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "conv2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 500
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}
layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}


layer {  
  name: "prob"  
  type: "Softmax"  
  bottom: "ip2"  
  top: "prob"  
}  

引用文章

opencv3.3调用 caffe mnist 模型进行手写数字的分类_在c++程序中调用mnist模型进行分类-CSDN博客

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐