Caffe训练自己的数据集

标签(空格分隔): caffe Linux 深度学习


之前,我们已经完成了faster-rcnn的训练和测试工作,得到了person所在的区域。接下来我们将训练一个简单分类神经网络来对person进行简单地分类。

数据集准备

这里我们需要做类似于行人检测的工作,因此将INRIA Person Dataset中的某一部分行人样本图像进行训练。需要生成train.txt和val.txt,大家可以直接调用我的函数(注意,只是函数,不是完整的脚本哦),两者图像数量之比依旧是经典的1:1

def WriteText(trainfilename, valfilename, fileset, count):
    rate = [0.5, 0.5]
    trainset = []
    valset = []
    trainposset = set(random.sample(xrange(len(fileset)), int(len(fileset) * rate[0])))
    valposset = set(xrange(len(fileset))) - trainposset
    for pos in trainposset:
        trainset.append(fileset[pos])
    for pos in valposset:
        valset.append(fileset[pos])
    with open(trainfilename, 'a') as f:
        for file in trainset:
            writetext = file + " " + str(count)
            writetext = writetext + "\n"
            f.write(writetext)
    with open(valfilename, 'a') as f:
        for file in valset:
            writetext = file + " " + str(count)
            writetext = writetext + "\n"
            f.write(writetext)


def ReadJpeg(srcdir, ext=".jpg"):
    fileset = []
    for file in os.listdir(srcdir):
        absfilename = os.path.join(srcdir, file)
        if os.path.isfile(absfilename) and os.path.splitext(absfilename)[1] == ext:
            fileset.append(file)
    return sorted(fileset)


def CreateTrainValTxt(imsetpath, fileset):
    trainfilename = os.path.join(imsetpath, 'train.txt')
    valfilename = os.path.join(imsetpath, 'val.txt')
    if os.path.exists(trainfilename):
        os.remove(trainfilename)
    if os.path.exists(valfilename):
        os.remove(valfilename)
    clsset = []
    count = -1
    previousname = ''
    for file in fileset:
        personname = file[:-5]
        if previousname == personname:
            clsset.append(file)
        else:
            if 0 != len(clsset):
                WriteText(trainfilename, valfilename, clsset, count)
            clsset = []
            clsset.append(file)
            previousname = personname
            count = count + 1

记住,为了后面训练的顺利进行,我们用脚本将图像规模统一调整为256*256,利用PIL的Image模块进行resize:

def ConvertImagesize(imsetpath, fileset):
    for file in fileset:
        print "Convert:" + file
        filepath = os.path.join(imsetpath, file)
        im = Image.open(filepath)
        out = im.resize((256, 256), Image.ANTIALIAS)
        out.save(filepath)

官方脚本的修改

这些脚本都是用来进行预处理的脚本,我们可以根据自身需求更改路径,或者重新写一个脚本即可。这些脚本都保存在examples/imagenet目录下。
1.create_imagenet.sh
该脚本主要利用build/tools目录下的convert_imageset工具生成lmdb,某些路径可以自己修改。这里我直接将其改写成了一个Python脚本函数:

def CreateImagenet(tooldir='build/tools', datadir='data/ilsvrc12/Persons', exampledir='examples/imagenet'):
    imdbdir = exampledir + "/ilsvrc12_train_lmdb"
    if os.path.exists(imdbdir):
        shutil.rmtree(imdbdir)
    cmd = tooldir + "/convert_imageset --shuffle " + datadir + "/ " + datadir + "/train.txt " + imdbdir
    print cmd
    os.system(cmd)
    imdbdir = exampledir + "/ilsvrc12_val_lmdb"
    if os.path.exists(imdbdir):
        shutil.rmtree(imdbdir)
    cmd = tooldir + "/convert_imageset --shuffle " + datadir + "/ " + datadir + "/val.txt " + imdbdir
    print cmd
    os.system(cmd)

2.make_imagenet.mean.sh
该脚本主要生成所有训练图像的平均值,利用到的是compute_image_mean工具,生成imagenet_mean.binaryproto。在这我也同样改成了一个Python脚本函数:

def MakeImagenetMean(tooldir='build/tools', datadir='data/ilsvrc12/Persons', exampledir='examples/imagenet'):
    cmd = tooldir + "/compute_image_mean " + exampledir + "/ilsvrc12_train_lmdb " + datadir + "/imagenet_mean.binaryproto"
    print cmd
    os.system(cmd)

网络结构的修改

网络结构文件存放在models下,这里我使用的是bvlc_reference_caffenet,修改其train_val.prototxt文件:

layer {
  name: "fc8"
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 1000 #修改为需要输出的类别数
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

执行训练脚本

训练脚本路径为examples/imagenet/train_caffenet.sh目录下,这里为了方便同样的将shell脚本改写成Python脚本,为了方便起见,finetuning脚本也写在一起。

def TrainCaffeNet(mode=1):
    tooldir = 'build/tools'
    abspath = os.getcwd()
    imsetpath = os.path.join(abspath, 'data/ilsvrc12/Persons')
    fileset = ReadJpeg(imsetpath)
    CreateTrainValTxt(imsetpath, fileset)
    ConvertImagesize(imsetpath, fileset)
    CreateImagenet()
    MakeImagenetMean()
    ChangePrototxt()
    if 1==mode:
        cmd = tooldir + "/caffe train --solver=models/bvlc_reference_caffenet/solver.prototxt"
    else:
        cmd = tooldir + "/caffe train --solver=examples/finetune_pascal_detection/pascal_finetune_trainval_test.prototxt"\
        +" --weights models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel"
    print cmd
    os.system(cmd)

finetuning网络

按照官方教程介绍,finetuning的工作在网络上的修改只是最后一层,以AlexNet为例则是fc8,目录examples/finetune_pascal_detection下有以下用到的文件。
1.修改pascal_finetune_trainval_test.prototxt网络全连接层fc8

layer {
  name: "fc8-finetune" # 要将本层改名,这样网络才会随机初始化权值
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8-finetune" #改名
  param {
    lr_mult: 10 # 本层学习速率为以前的10倍
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 246 # 类别数,按需修改
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "accuracy"
  type: "Accuracy"
  bottom: "fc8-finetune" # 本层学习速率为以前的10倍
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "fc8-finetune" # 本层学习速率为以前的10倍
  bottom: "label"
  top: "loss"
}

2.修改pascal_finetune_solver.prototxt学习速率

net: "models/bvlc_reference_caffenet/fintune-train_val.prototxt"
test_iter: 100 #减少测试迭代次数
test_interval: 1000
base_lr: 0.001 #学习速率减小
lr_policy: "step"
gamma: 0.1
stepsize: 20000 #学习步长增加
display: 20
max_iter: 100000 #减少最大迭代次数
momentum: 0.9
weight_decay: 0.0005
snapshot: 10000
snapshot_prefix: "models/bvlc_reference_caffenet/caffenet_train"
solver_mode: GPU

3.执行训练脚本(见上文)

你可能感兴趣的:(Caffe训练自己的数据集)