前言
不仅仅需要知道这个图片的内容是什么, 也需要让机器标注出图片的各部分内容,比如猫,狗,人,车等用框框标注出来,需要返回各个内容的坐标 在做人脸识别时就需要知道人脸的位置以及各个特征点的位置,这都是必要的
新的工具——无代码神经网络
caffe!!!你没看错,不需要写代码,只需要定义一些配置文件,真正做到了设计神经网络就能让他跑起来的地步 四个步骤: 转换数据(设置数据位置以及数据格式) 定义网络(编辑prototxt) 设计卷积层 定义求解器(编辑prototxt) 即设置学习率等超参数 训练(使用预先训练过的权重)(运行脚本)
需要定义四个配置文件: 数据源存储的地方BLOB,数据块配置文件 层配置文件,即数据层,卷积层等等,都是一层层支配的 神经图层配置文件 求解器配置文件 图片配置文件案例,先了解流程: 定义神经图层,直接从第二步开始了 包含数据层,卷积层,全连接层 定义求解器,第三步: 超参数 第四步,训练… 有win和linux两个版本,我用win,无他,方便尔
也就是说定义两个配置文件就能迅速的看到设计的神经网络的表现了,只是一些参数配置操作 事实上有很多预置的配置文件都是非常有名的,有时并不需要你亲自设计神经网络层… 如果样本合适,抓一个跑一跑,正确率也能达到95%以上左右,呵呵哒 模型动物园
caffe从准备数据到模型预测流程
1·准备数据
使用keras做数据增强,比opencv简便,数据增强就是图片平移,旋转,反转啥的,能把原数数据扩容几倍 需要安装两个依赖
keras在tensorflow上做了一层封装,使用会更加简便一些。这里是图像增强的api
2·接下来说如何将图片文件打包成lmdb格式
格式转换的4个必要条件: (1)编译好Caffe,而且convert_imageset.exe存在; (2)需要被转换的图像和目录 (3)在将图像转换为lmdb格式之前,首先生成两个标签文件train.txt和val.txt(具体格式见下文); (4)运行编辑修改好的create_imagenet.sh(最好将其制到你的项目文件夹下,不修改原始文件)生成lmdb文件。 开始吧! 原始数据如图 train是训练用的,val是测试用的 train内部结构 0里面是各种人脸图片,1里面则是不包含人脸的图片 val内部结构 仔细看就会发现,里面的图片用名字标注了是否包含人脸,因为是测试数据,就不需要分成01文件夹了,根据名字直接判别即可 然而如果想要打包成lmdb格式,仅仅有train和val训练和测试文件数据还不行,完整的原始数据应该是 多出来train.txt,和val.txt,这两个是从图片数据中提取出来的标签文件 train.txt内容结构如下: 每一行代表着,这是0文件夹下名字为xxxx的图片文件,标签是0
val.txt文件如图: 因为没有文件夹区分,所以直接是文件名,标签格式 train.txt和val.txt需要我们从图片数据中生成,这是python代码,你只需要修改实际路径即可
import os
import random
root_data_path = r"C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\data_set"
traintxt_path = root_data_path + "\\" + "train.txt"
valtxt_path = root_data_path + "\\" + "val.txt"
if os.path.exists(traintxt_path):
os.remove(traintxt_path)
if os.path.exists(valtxt_path):
os.remove(valtxt_path)
filenames = os.listdir(root_data_path)
file_train = open(traintxt_path, "w")
file_val = open(valtxt_path, "w")
train_list=[]
val_list=[]
if len(filenames) > 0:
for fn in filenames:
full_filename = os.path.join(root_data_path, fn)
if fn == "train":
file = os.listdir(full_filename)
for name in file:
temp = os.path.join(full_filename, name)
for img in os.listdir(temp):
train_list.append(name + "/" + img + " " + name + "\n")
elif fn == "val":
for img in os.listdir(full_filename):
category = img.split('_')
if category[1] == "nonface":
val_list.append(img + " " + "1" + "\n")
else:
val_list.append(img + " " + "0" + "\n")
def random_order(mylist):
newlist = []
all_len=len(mylist)
for i in range(all_len):
if mylist:
len1 = len(mylist)
myindex = random.randint(0, len1 - 1)
myval = mylist[myindex]
newlist.append(myval)
mylist.remove(mylist[myindex])
mynewcontent = ""
for i in newlist:
mynewcontent = mynewcontent + i
return mynewcontent
file_train.write(random_order(train_list))
file_val.write(random_order(val_list))
file_train.close()
file_val.close()
文件生成后,编辑sh即可 caffe-windows\examples\imagenet下有示例文件create_imagenet.sh 含义如下:
EXAMPLE=C:/Users/Administrator/Desktop/data_set
DATA=C:/Users/Administrator/Desktop/data_set
TOOLS=D:/program_files/caffe/caffe-windows/Build/x64/Release
TRAIN_DATA_ROOT=C:/Users/Administrator/Desktop/data_set/train/
VAL_DATA_ROOT=C:/Users/Administrator/Desktop/data_set/val/
RESIZE=true
if $RESIZE; then
RESIZE_HEIGHT=256
RESIZE_WIDTH=256
else
RESIZE_HEIGHT=0
RESIZE_WIDTH=0
fi
if [ ! -d "$TRAIN_DATA_ROOT" ]; then
echo "Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT"
echo "Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path" \
"where the ImageNet training data is stored."
exit 1
fi
if [ ! -d "$VAL_DATA_ROOT" ]; then
echo "Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT"
echo "Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path" \
"where the ImageNet validation data is stored."
exit 1
fi
echo "if train.txt and val.txt are exist in EXAMPLE dir. clear all"
rm -rf $EXAMPLE/train_lmdb
rm -rf $EXAMPLE/val_lmdb
echo "Creating train lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \
--resize_height=$RESIZE_HEIGHT \
--resize_width=$RESIZE_WIDTH \
--shuffle \
$TRAIN_DATA_ROOT \
$DATA/train.txt \
$EXAMPLE/train_lmdb
echo "Creating val lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \
--resize_height=$RESIZE_HEIGHT \
--resize_width=$RESIZE_WIDTH \
--shuffle \
$VAL_DATA_ROOT \
$DATA/val.txt \
$EXAMPLE/val_lmdb
echo "Done." read
复制自带的create_imagenet.sh文件到你的工程目录下,根据需要修改内容,比如我的
EXAMPLE=C:/Users/Administrator.DESKTOP-KMH7HN6/Downloads/li_test_net/my_face_detect/data_set
DATA=C:/Users/Administrator.DESKTOP-KMH7HN6/Downloads/li_test_net/my_face_detect/data_set
TOOLS=C:/Users/Administrator.DESKTOP-KMH7HN6/Downloads/Compressed/caffer_data/caffe-windows/scripts/build/tools/Release
TRAIN_DATA_ROOT=C:/Users/Administrator.DESKTOP-KMH7HN6/Downloads/li_test_net/my_face_detect/data_set/train/
VAL_DATA_ROOT=C:/Users/Administrator.DESKTOP-KMH7HN6/Downloads/li_test_net/my_face_detect/data_set/val/
RESIZE=true
if $RESIZE; then
RESIZE_HEIGHT=227
RESIZE_WIDTH=227
else
RESIZE_HEIGHT=0
RESIZE_WIDTH=0
fi
if [ ! -d "$TRAIN_DATA_ROOT" ]; then
echo "Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT"
echo "Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path" \
"where the ImageNet training data is stored."
exit 1
fi
if [ ! -d "$VAL_DATA_ROOT" ]; then
echo "Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT"
echo "Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path" \
"where the ImageNet validation data is stored."
exit 1
fi
echo "Creating train lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \
--resize_height=$RESIZE_HEIGHT \
--resize_width=$RESIZE_WIDTH \
--shuffle \
$TRAIN_DATA_ROOT \
$DATA/train.txt \
$EXAMPLE/face_train_lmdb
echo "Creating val lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \
--resize_height=$RESIZE_HEIGHT \
--resize_width=$RESIZE_WIDTH \
--shuffle \
$VAL_DATA_ROOT \
$DATA/val.txt \
$EXAMPLE/face_val_lmdb
echo "Done."
然而windows不能运行sh文件,安装一下git就行了,将他加入系统变量,双击sh文件就默认git运行了 注意事项 (1)注意windows系统中路径问题,复制的路径是“\”,在程序中需要的是“/”,注意对其修改,生成的train.txt和val.txt中的路径也是“/”。 (2)必须保证你的caffe编译成功 (3)修改convert_imageset.sh时,一定要注意路径问题,这里就是配置路径的很容易出错(具体请看上面的详细过程)。 (4)图像的名字中不要出现空格,否则会有问题(深刻的痛)。 (5)生成的train.txt文件中的图像路径问题:路径从train文件夹之后开始写,不包括train文件夹名称。 (6)如果之前生成过文件(不管是train.txt和val.txt,还是train_lmdb和val_lmdb),想重新生成,最好先删除之前的,有可能会有错误。
至此,lmdb数据已经生成完毕
3·制作hdf5数据格式
lmdb可以解决图像分类问题,0或者1 然而如果想要实现图像检测问题,比如在一张人脸图像上找到眼睛的位置,嘴的位置,返回坐标,就需要多目标检测 数据格式如下 一张图片共标记了五个位置,每两组数据作为一个x和y坐标 11 78左眼 62 71右眼 84 65 鼻子 26 51 左嘴角 46 89右嘴角 三张图片
最终的目标如图 将数据打包成h5格式,h5文件里包含了数据的内容和标签 之所以分为多个h5文件,是因为日常使用时不希望将所有的数据打包在一起,否则单个数据文件过大不是一件好事 trainlist.txt和testlist.txt里记录的是h5的文件名,相当于将h5文件分为了训练集和验证集
只要有图片文件和标签文件,即可使用如下代码生成 每行代码含义已经注明,根据需要修改
import h5py
import os
import cv2
import math
import numpy as np
import random
import re
root_path = r"C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\hdf5\image"
with open(r"C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\hdf5\hdf5.txt", 'r') as f:
lines = f.readlines()
num = len(lines)
random.shuffle(lines)
base_path_h5=r"C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\hdf5\h5"
imgAccu = 0
imgs = np.zeros([num, 3, 224, 224])
labels = np.zeros([num, 10])
for i in range(num):
line = lines[i]
segments = re.split('\s+', line)[:-1]
print(segments[0])
img = cv2.imread(os.path.join(root_path, segments[0]))
img = cv2.resize(img, (224, 224))
img = img.transpose(2,0,1)
imgs[i,:,:,:] = img.astype(np.float32)
for j in range(10):
labels[i,j] = float(segments[j+1])*224/256
batchSize = 1
batchNum = int(math.ceil(1.0*num/batchSize))
imgsMean = np.mean(imgs, axis=0)
labelsMean = np.mean(labels, axis=0)
labels = (labels - labelsMean)/10
if os.path.exists('trainlist.txt'):
os.remove('trainlist.txt')
if os.path.exists('testlist.txt'):
os.remove('testlist.txt')
comp_kwargs = {'compression': 'gzip', 'compression_opts': 1}
for i in range(batchNum):
start = i*batchSize
end = min((i+1)*batchSize, num)
if i < batchNum-1:
filename = r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\hdf5\h5\train{0}.h5'.format(i)
else:
filename = r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\hdf5\h5\test{0}.h5'.format(i-batchNum+1)
print(filename)
with h5py.File(filename, 'w') as f:
f.create_dataset('data', data = np.array((imgs[start:end]-imgsMean)/255.0).astype(np.float32), **comp_kwargs)
f.create_dataset('label', data = np.array(labels[start:end]).astype(np.float32), **comp_kwargs)
if i < batchNum-1:
with open(r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\hdf5\h5\trainlist.txt', 'a') as f:
f.write(os.path.join(base_path_h5, 'train{0}.h5').format(i) + '\n')
else:
with open(r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\hdf5\h5\testlist.txt', 'a') as f:
f.write(os.path.join(base_path_h5, 'test{0}.h5').format(i-batchNum+1) + '\n')
imgsMean = np.mean(imgsMean, axis=(1,2))
with open('mean.txt', 'w') as f:
f.write(str(imgsMean[0]) + '\n' + str(imgsMean[1]) + '\n' + str(imgsMean[2]))
4·设置网络图层参数
无代码开始了,只需要配置神经网络图层的参数即可 即使用脚本编写神经图层参数控制即可,然而如果脚本都不想写,想让脚本自动生成呢? 设计好参数,自动生成脚本即可
import sys
from caffe import layers as L
from caffe import params as P
import caffe
def lenet(lmdb, batch_size):
n = caffe.NetSpec()
n.data, n.label = L.Data(batch_size=batch_size, backend=P.Data.LMDB, source=lmdb,
transform_param=dict(scale=1./255), ntop=2)
n.conv1 = L.Convolution(n.data, kernel_size=5, num_output=20, weight_filler=dict(type='xavier'))
n.pool1 = L.Pooling(n.conv1, kernel_size=2, stride=2, pool=P.Pooling.MAX)
n.conv2 = L.Convolution(n.pool1, kernel_size=5, num_output=50, weight_filler=dict(type='xavier'))
n.pool2 = L.Pooling(n.conv2, kernel_size=2, stride=2, pool=P.Pooling.MAX)
n.ip1 = L.InnerProduct(n.pool2, num_output=500, weight_filler=dict(type='xavier'))
n.relu1 = L.ReLU(n.ip1, in_place=True)
n.ip2 = L.InnerProduct(n.relu1, num_output=10, weight_filler=dict(type='xavier'))
n.loss = L.SoftmaxWithLoss(n.ip2, n.label)
return n.to_proto()
with open('lenet_auto_train.prototxt', 'w') as f:
f.write(str(lenet(r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\minist_lmdb\mnist_train_lmdb', 64)))
with open('lenet_auto_test.prototxt', 'w') as f:
f.write(str(lenet(r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\minist_lmdb\mnist_test_lmdb', 100)))
我们可以使用caffe预设的神经网络,根据已有的结构进行设置参数 也可以设置自己的神经网络图层 如果暂时不需要设计自己的caffe神经图层,这里可以暂略
4·1定义自己的层
确认打开配置文件注释,这里允许我们自定义神经图层
如果之前没有打开,重新编译caffe,才能重新拥有此功能 自己设计的图层如
name: "convolution"
input: "data"
input_dim: 1
input_dim: 3
input_dim: 100
input_dim: 100
layer {
name: "conv"
type: "Convolution"
bottom: "data"
top: "conv"
convolution_param {
num_output: 3
kernel_size: 5
stride: 1
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: 'MyPythonLayer'
type: 'Python'
top: 'output'
bottom: 'conv'
python_param {
module: 'mypythonlayer'
layer: 'MyLayer'
param_str: "'num': 21"
}
}
设计图层的代码
import sys
caffe_root='/home/tyd/caffe/'
sys.path.insert(0, caffe_root+'python')
import caffe
import numpy as np
import yaml
import cv2
class MyLayer(caffe.Layer):
def setup(self, bottom, top):
self.num = yaml.load(self.param_str)["num"]
print "Parameter num : ", self.num
def reshape(self, bottom, top):
pass
def forward(self, bottom, top):
top[0].reshape(*bottom[0].shape)
print bottom[0].data.shape
print bottom[0].data
top[0].data[...] = bottom[0].data + self.num
print top[0].data[...]
def backward(self, top, propagate_down, bottom):
pass
net = caffe.Net('conv.prototxt',caffe.TEST)
im = np.array(cv2.imread('timg.jpeg'))
print im.shape
im_input = im[np.newaxis, :, :]
print im_input.shape
im_input2 = im_input.transpose((0,3,1,2))
print im_input2.shape
net.blobs['data'].reshape(*im_input2.shape)
net.blobs['data'].data[...] = im_input2
net.forward()
4·2绘制网络结构图
计算均值,caffe的一个命令即可 指定数据源,指定均值输出位置
sudo caffe/build/tools/compute_image_mean /caffe/examples/mnist/mnist_train_lmdb /caffe_case/mean.binaryproto
将protxt神经网络脚本文件变成图片形式展现出来,更加直观 1、安装graphViz sudo apt-get install graphviz 2 、安装pydot sudo pip install pydot
sudo python /caffe/python/draw_net.py /caffe/examples/mnist/lenet_train_test.prototxt /caffe_case/lenet.png --rankdir=BT
第一个参数:网络模型的prototxt文件
第二个参数:保存的图片路径及名字
第二个参数:–rankdir=x , x 有四种选项,分别是LR, RL, TB, BT 。用来表示网络的方向,分别是从左到右,从右到左,从上到小,从下到上。默认为LR。 可能会报错:ImportError: cannot import name ‘_validate_lengths’ 这是numpy版本不对造成的,卸载原来的numpy 重新安装
pip install numpy==1.15.0
仿照命令即可得到神经网络的结构图,拿minist测试后得到
5·开始训练
-solver:必选参数。一个protocol buffer类型的文件,即模型的配置文件。如:
-gpu: 可选参数。该参数用来指定用哪一块gpu运行,根据gpu的id进行选择,如果设置为'-gpu all'则使用所有的gpu运行。如使用第三块gpu运行:
-weights:可选参数。用预先训练好的权重来fine-tuning模型,需要一个caffemodel如:
time参数用来在屏幕上显示程序运行时间。显示网络图层,究竟消耗了多少时间,如:
关于gpu
6·生成loss曲线
绘制训练minist的loss曲线 真是滑稽,必须将loss.py放入如图其中 因为caffe的案例文件里有很多都是相对路径写的,很容易找不到上级路径,运行caffe案例需要如此,然而自己的项目可以随意 代码如图,取代了脚本
import numpy as np
import matplotlib.pyplot as plt
import sys,os
import caffe
caffe.set_mode_cpu()
solver = caffe.SGDSolver(r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\Compressed\caffer_data\caffe-windows\examples\mnist\lenet_solver.prototxt')
niter =1000
test_interval = 200
train_loss = np.zeros(niter)
test_acc = np.zeros(int(np.ceil(niter / test_interval)))
for it in range(niter):
solver.step(1)
train_loss[it] = solver.net.blobs['loss'].data
solver.test_nets[0].forward(start='conv1')
if it % test_interval == 0:
acc=solver.test_nets[0].blobs['accuracy'].data
print ('Iteration', it, 'testing...','accuracy:',acc)
test_acc[it // test_interval] = acc
print (test_acc)
_, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(np.arange(niter), train_loss)
ax2.plot(test_interval * np.arange(len(test_acc)), test_acc, 'r')
ax1.set_xlabel('iteration')
ax1.set_ylabel('train loss')
ax2.set_ylabel('test accuracy')
plt.show()
7·根据模型跑文件
图中文件是模型训练好的网络结构 而model文件加上超参数则可以对新的数据进行预测 训练时需要数据和标签,得到caffe的model,然后使用新的数据对model前向传播一次得到结果 比如猫狗分类,传入猫的图片 已经有的模型是有名的cifar数据集训练出来的
import numpy as np
import sys
import caffe
caffe.set_mode_cpu()
model_def = r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\Compressed\caffer_data\caffe-windows\models\bvlc_reference_caffenet\deploy.prototxt'
model_weights = r'C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\li_test_net\my_face_detect\temp\bvlc_reference_caffenet.caffemodel'
net = caffe.Net(model_def,
model_weights,
caffe.TEST)
mu = np.load(r"C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\Compressed\caffer_data\caffe-windows\python\caffe\imagenet\ilsvrc_2012_mean.npy")
mu = mu.mean(1).mean(1)
print ('mean-subtracted values:', zip('BGR', mu))
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2,0,1))
transformer.set_mean('data', mu)
transformer.set_raw_scale('data', 255)
transformer.set_channel_swap('data', (2,1,0))
net.blobs['data'].reshape(1,
3,
227, 227)
image = caffe.io.load_image(r"C:\Users\Administrator.DESKTOP-KMH7HN6\Downloads\Compressed\caffer_data\caffe-windows\examples\images\cat.jpg")
transformed_image = transformer.preprocess('data', image)
net.blobs['data'].data[...] = transformed_image
output = net.forward()
output_prob = output['prob'][0]
print ('predicted class is:', output_prob.argmax())
想知道类别281是什么吗? 流程已经清楚了,那么接下来就开始做人脸检测,标出人的眼睛鼻子等特征点
|