tensorflow版本的faster rcnn在windows系統下用cpu訓練
原始碼地址:https://github.com/endernewton/tf-faster-rcnn
首先在cpu下完成訓練就要把程式中用gpu的部分註釋掉,具體需要註釋的部分如下:
(1)tf-faster-rcnn-master/lib/model/config.py路徑下USE_GPU_NMS 改為false:
(2)lib/model/nms_wrapper.py路徑下,註釋掉以下部分:
(3)lib/setup.py整個檔案重新寫一份,其中相應的gpu部分已經註釋:
# -------------------------------------------------------- # Fast R-CNN # Copyright (c) 2015 Microsoft # Licensed under The MIT License [see LICENSE for details] # Written by Ross Girshick # -------------------------------------------------------- import os from os.path import join as pjoin import numpy as np from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext def find_in_path(name, path): "Find a file in a search path" #adapted fom http://code.activestate.com/recipes/52224-find-a-file-given-a-search-path/ for dir in path.split(os.pathsep): binpath = pjoin(dir, name) if os.path.exists(binpath): return os.path.abspath(binpath) return None def locate_cuda(): """Locate the CUDA environment on the system Returns a dict with keys 'home', 'nvcc', 'include', and 'lib64' and values giving the absolute path to each directory. Starts by looking for the CUDAHOME env variable. If not found, everything is based on finding 'nvcc' in the PATH. """ # first check if the CUDAHOME env variable is in use if 'CUDAHOME' in os.environ: home = os.environ['CUDAHOME'] nvcc = pjoin(home, 'bin', 'nvcc') else: # otherwise, search the PATH for NVCC nvcc = find_in_path('nvcc.exe', os.environ['PATH']) if nvcc is None: # raise EnvironmentError('The nvcc binary could not be ' # 'located in your $PATH. Either add it to your path, or set $CUDAHOME') return None home = os.path.dirname(os.path.dirname(nvcc)) cudaconfig = {'home':home, 'nvcc':nvcc, 'include': pjoin(home, 'include'), 'lib64': pjoin(home, 'lib', 'x64')} for k, v in iter(cudaconfig.items()): if not os.path.exists(v): # raise EnvironmentError('The CUDA %s path could not be located in %s' % (k, v)) return None return cudaconfig CUDA = locate_cuda() # Obtain the numpy include directory. This logic works across numpy versions. try: numpy_include = np.get_include() except AttributeError: numpy_include = np.get_numpy_include() def customize_compiler_for_nvcc(self): # _msvccompiler.py imports: import os import shutil import stat import subprocess import winreg from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log from distutils.util import get_platform from itertools import count super = self.compile self.src_extensions.append('.cu') # find python include import sys py_dir = sys.executable.replace('\\', '/').split('/')[:-1] py_include = pjoin('/'.join(py_dir), 'include') # override method in _msvccompiler.py, starts from line 340 def compile(sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): if not self.initialized: self.initialize() compile_info = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) macros, objects, extra_postargs, pp_opts, build = compile_info compile_opts = extra_preargs or [] compile_opts.append('/c') if debug: compile_opts.extend(self.compile_options_debug) else: compile_opts.extend(self.compile_options) add_cpp_opts = False for obj in objects: try: src, ext = build[obj] except KeyError: continue if debug: # pass the full pathname to MSVC in debug mode, # this allows the debugger to find the source file # without asking the user to browse for it src = os.path.abspath(src) if ext in self._c_extensions: input_opt = "/Tc" + src elif ext in self._cpp_extensions: input_opt = "/Tp" + src add_cpp_opts = True elif ext in self._rc_extensions: # compile .RC to .RES file input_opt = src output_opt = "/fo" + obj try: self.spawn([self.rc] + pp_opts + [output_opt, input_opt]) except DistutilsExecError as msg: raise CompileError(msg) continue elif ext in self._mc_extensions: # Compile .MC to .RC file to .RES file. # * '-h dir' specifies the directory for the # generated include file # * '-r dir' specifies the target directory of the # generated RC file and the binary message resource # it includes # # For now (since there are no options to change this), # we use the source-directory for the include file and # the build directory for the RC file and message # resources. This works at least for win32all. h_dir = os.path.dirname(src) rc_dir = os.path.dirname(obj) try: # first compile .MC to .RC and .H file self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src]) base, _ = os.path.splitext(os.path.basename(src)) rc_file = os.path.join(rc_dir, base + '.rc') # then compile .RC to .RES file self.spawn([self.rc, "/fo" + obj, rc_file]) except DistutilsExecError as msg: raise CompileError(msg) continue elif ext == '.cu': # a trigger for cu compile try: # use the cuda for .cu files # self.set_executable('compiler_so', CUDA['nvcc']) # use only a subset of the extra_postargs, which are 1-1 translated # from the extra_compile_args in the Extension class postargs = extra_postargs['nvcc'] arg = [CUDA['nvcc']] + sources + ['-odir', pjoin(output_dir, 'nms')] for include_dir in include_dirs: arg.append('-I') arg.append(include_dir) arg += ['-I', py_include] # arg += ['-lib', CUDA['lib64']] arg += ['-Xcompiler', '/EHsc,/W3,/nologo,/Ox,/MD'] arg += postargs self.spawn(arg) continue except DistutilsExecError as msg: # raise CompileError(msg) continue else: # how to handle this file? raise CompileError("Don't know how to compile {} to {}" .format(src, obj)) args = [self.cc] + compile_opts + pp_opts if add_cpp_opts: args.append('/EHsc') args.append(input_opt) args.append("/Fo" + obj) args.extend(extra_postargs) try: self.spawn(args) except DistutilsExecError as msg: raise CompileError(msg) return objects self.compile = compile # run the customize_compiler class custom_build_ext(build_ext): def build_extensions(self): customize_compiler_for_nvcc(self.compiler) build_ext.build_extensions(self) ext_modules = [ Extension( "utils.cython_bbox", ["utils/bbox.pyx"], extra_compile_args={'gcc': ["-Wno-cpp", "-Wno-unused-function"]}, include_dirs = [numpy_include] ), Extension( "nms.cpu_nms", ["nms/cpu_nms.pyx"], include_dirs = [numpy_include] ), # Extension('nms.gpu_nms', # ['nms/nms_kernel.cu', 'nms/gpu_nms.pyx'], # library_dirs=[CUDA['lib64']], # libraries=['cudart'], # language='c++', # # this syntax is specific to this build system # # we're only going to use certain compiler args with nvcc and not with gcc # # the implementation of this trick is in customize_compiler() below # extra_compile_args={'gcc': ["-Wno-unused-function"], # 'nvcc': ['-arch=sm_52', # '--ptxas-options=-v', # '-c']}, # include_dirs = [numpy_include, CUDA['include']] # ) ] setup( name='tf_faster_rcnn', ext_modules=ext_modules, # inject our custom trigger cmdclass={'build_ext': custom_build_ext}, )
(4)
(a)檔案lib/nms/cpu_nms.pyx
:
改變第25行:cdef np.ndarray[np.int_t, ndim=1] order = scores.argsort()[::-1]
為:cdef np.ndarray[np.int64_t, ndim=1] order = scores.argsort()[::-1]
(b)檔案lib/nms/gpu_nms.pyx
:
改變25行: cdef np.ndarray[np.int_t, ndim=1] \
為:cdef np.ndarray[np.int64_t, ndim=1] \
(c)檔案lib/datasets/pascal_voc.py
改變226行:'{:s}.xml')
為: '{0:s}.xml')
(d)檔案 lib/datasets/voc_eval.py:
改變121行:with open(cachefile, 'w') as f:
為:with open(cachefile, 'wb') as f:
(5)轉到 lib
目錄下, 終端執行 python setup.py build_ext
.
這一步如果遇到一些錯誤,建議刪除所有的.c和.cpp檔案,(lib/nms/cpu_nms.c
, lib/nms/gpu_nms.cpp
, lib/utils/bbox.c
)
(6)訓練模型:因為GitHub原作者是在ubuntu系統下進行訓練的,所以用./experiments/scripts/train_faster_rcnn.sh 0 pascal_voc vgg16就可以進行,而.sh檔案不能用在windows平臺上(本人對ubuntu一概不知,所以也不會弄),但是訓練的過程它是呼叫的trainval_net.py開始訓練的,所以我就把trainval_net.py進行修改,程式碼如下:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import _init_paths
from model.train_val import get_training_roidb, train_net
from model.config import cfg, cfg_from_file, cfg_from_list, get_output_dir, get_output_tb_dir
from datasets.factory import get_imdb
import datasets.imdb
import argparse
import pprint
import numpy as np
import sys
import tensorflow as tf
from nets.vgg16 import vgg16
from nets.resnet_v1 import resnetv1
class args:
"""
Parse input arguments
"""
cfg_file = '../experiments/cfgs/vgg16.yml'
weight = '../data/imagenet_weights/vgg16.ckpt'
imdb_name = 'voc_2007_trainval'
imdbval_name = 'voc_2007_test'
max_iters = 100000
tag = None
net = 'vgg16'
set_cfgs = ['ANCHOR_SCALES', '[8,16,32]', 'ANCHOR_RATIOS', '[0.5,1,2]', 'TRAIN.STEPSIZE', '[50000]']
def combined_roidb(imdb_names):
"""
Combine multiple roidbs
"""
def get_roidb(imdb_name):
imdb = get_imdb(imdb_name)
print('Loaded dataset `{:s}` for training'.format(imdb.name))
imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD)
print('Set proposal method: {:s}'.format(cfg.TRAIN.PROPOSAL_METHOD))
roidb = get_training_roidb(imdb)
return roidb
roidbs = [get_roidb(s) for s in imdb_names.split('+')]
roidb = roidbs[0]
if len(roidbs) > 1:
for r in roidbs[1:]:
roidb.extend(r)
tmp = get_imdb(imdb_names.split('+')[1])
imdb = datasets.imdb.imdb(imdb_names, tmp.classes)
else:
imdb = get_imdb(imdb_names)
return imdb, roidb
if __name__ == '__main__':
# args = parse_args()
print('Called with args:')
if args.cfg_file is not None:
cfg_from_file(args.cfg_file)
if args.set_cfgs is not None:
cfg_from_list(args.set_cfgs)
print('Using config:')
pprint.pprint(cfg)
np.random.seed(cfg.RNG_SEED)
# train set
imdb, roidb = combined_roidb(args.imdb_name)
print('{:d} roidb entries'.format(len(roidb)))
# output directory where the models are saved
output_dir = get_output_dir(imdb, args.tag)
print('Output will be saved to `{:s}`'.format(output_dir))
# tensorboard directory where the summaries are saved during training
tb_dir = get_output_tb_dir(imdb, args.tag)
print('TensorFlow summaries will be saved to `{:s}`'.format(tb_dir))
# also add the validation set, but with no flipping images
orgflip = cfg.TRAIN.USE_FLIPPED
cfg.TRAIN.USE_FLIPPED = False
_, valroidb = combined_roidb(args.imdbval_name)
print('{:d} validation roidb entries'.format(len(valroidb)))
cfg.TRAIN.USE_FLIPPED = orgflip
# load network
if args.net == 'vgg16':
net = vgg16()
elif args.net == 'res50':
net = resnetv1(num_layers=50)
elif args.net == 'res101':
net = resnetv1(num_layers=101)
elif args.net == 'res152':
net = resnetv1(num_layers=152)
elif args.net == 'mobile':
net = mobilenetv1()
else:
raise NotImplementedError
train_net(net, imdb, roidb, valroidb, output_dir, tb_dir,
pretrained_model=args.weight,
max_iters=args.max_iters)
原來的trainval_net.py檔案中的args是通過命令列來傳參的,這裡我設定了一個args類對所需的變數進行賦值。