Caffe模型训练
这条信息说明Caffe正在将求解器的状态(包括学习率、动量等参数)保存到另一个二进制的协议缓冲文件中。编译期间会遇到非常多的问题,基本是依赖库没装,和opencv的版本可能有些关键字被舍弃了,需要更改。实际上是不能直接使用 --image=your_image.jpg来直接指定图片文件来进行识别。运行./data/example/mnist/get_mnist.sh会自动下载四个文件。运行./ex
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是你想要识别的图像文件。
- 运行识别:
- 执行上述命令后,Caffe将加载模型,处理图像,并输出识别结果。
- 识别结果通常包括每个类别的概率分数。
实际上是不能直接使用 --image=your_image.jpg来直接指定图片文件来进行识别
应该要先转换为LMDB格式
将测试图片转换为LMDB
写个Python或者C++程序实现
还是Python转换比较方便
使用caffe自带工具转换为 LMDB:
- 准备数据:
将所有需要转换的.jpg文件放在一个目录中,例如/path/to/images。 - 创建列表文件:
创建一个文本文件,列出所有图像文件的路径及其标签(如果适用)。例如,创建一个名为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 数据导出为图像文件。以下是使用方法:
- 确保 Caffe 已经编译:
确保你已经编译了 Caffe,并且build/tools目录下有dump_lmdb_data工具。 - 运行
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)
- 运行脚本:
保存上述脚本为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博客
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)