diff --git a/.gitignore b/.gitignore index fa279f9071b..73bba6cb364 100644 --- a/.gitignore +++ b/.gitignore @@ -26,21 +26,26 @@ # Compiled MATLAB *.mex* -# build, distribute, and bins -build -.build_debug/* -.build_release/* -distribute/* -*.testbin -*.bin -python/caffe/proto/ +# IPython notebook checkpoints +.ipynb_checkpoints # Editor temporaries *.swp *~ -# IPython notebook checkpoints -.ipynb_checkpoints +# Sublime Text settings +*.sublime-workspace +*.sublime-project + +# Eclipse Project settings +*.*project +.settings + +# QtCreator files +*.user + +# OSX dir files +.DS_Store ## Caffe @@ -58,13 +63,16 @@ models/* *leveldb *lmdb -# LevelDB files -*.sst -*.ldb -LOCK -LOG* -CURRENT -MANIFEST-* +# build, distribute, and bins (+ python proto bindings) +build +.build_debug/* +.build_release/* +distribute/* +*.testbin +*.bin +python/caffe/proto/ +cmake_build +.cmake_build # Generated documentation docs/_site @@ -73,12 +81,10 @@ _site doxygen docs/dev -# Sublime Text settings -*.sublime-workspace -*.sublime-project - -# Eclipse Project settings -*.*project - -# CMake generated files -*.gen.cmake +# LevelDB files +*.sst +*.ldb +LOCK +LOG* +CURRENT +MANIFEST-* diff --git a/.travis.yml b/.travis.yml index 3deb45a2f0c..955aa8c3ba2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,8 @@ install: - sudo -E $SCRIPTS/travis_install.sh before_script: - - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib + - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/cuda/lib64 + - export PATH=/home/travis/miniconda/bin:$PATH - if ! $WITH_CMAKE; then $SCRIPTS/travis_setup_makefile_config.sh; fi script: $SCRIPTS/travis_build_and_test.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cb7d583504..adea37be565 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,90 +1,65 @@ -cmake_minimum_required(VERSION 2.8.8) -project( Caffe ) - -### Build Options ########################################################################## - -option(CPU_ONLY "Build Caffe without GPU support" OFF) -option(BUILD_PYTHON "Build Python wrapper" OFF) -option(BUILD_MATLAB "Build Matlab wrapper" OFF) -option(BUILD_EXAMPLES "Build examples" ON) -option(BUILD_SHARED_LIBS "Build SHARED libs if ON and STATIC otherwise" OFF) - -if(NOT BLAS) - set(BLAS atlas) -endif() - -if(NOT CUDA_TEST_DEVICE) - set(CUDA_TEST_DEVICE -1) +cmake_minimum_required(VERSION 2.8.7) + +# ---[ Caffe project +project(Caffe C CXX) + +# ---[ Using cmake scripts and modules +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules) + +include(cmake/Utils.cmake) +include(cmake/Targets.cmake) +include(cmake/Misc.cmake) +include(cmake/Summary.cmake) +include(cmake/ConfigGen.cmake) + +# ---[ Options +caffe_option(CPU_ONLY "Build Caffe wihtout CUDA support" OFF) # TODO: rename to USE_CUDA +caffe_option(USE_CUDNN "Build Caffe with cuDNN libary support" ON IF NOT CPU_ONLY) +caffe_option(BUILD_SHARED_LIBS "Build shared libraries" ON) +caffe_option(BUILD_python "Build Python wrapper" ON) +caffe_option(BUILD_matlab "Build Matlab wrapper" OFF IF UNIX OR APPLE) +caffe_option(BUILD_docs "Build documentation" ON IF UNIX OR APPLE) + +# ---[ Dependencies +include(cmake/Dependencies.cmake) + +# ---[ Flags +if(UNIX OR APLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall") endif() -# Install Prefix -if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set (CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Default install path" FORCE ) +if(USE_libstdcpp) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++") + message("-- Warning: forcing libstdc++ (controlled by USE_libstdcpp option in cmake)") endif() -### Configuration ########################################################################### -# Compiler Flags -set(CMAKE_CXX_COMPILER_FLAGS ${CMAKE_CXX_COMPILER_FLAGS} -Wall) -set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -fPIC) # set global flags -set(CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) # set debug flags -set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) # set release flags +add_definitions(-DGTEST_USE_OWN_TR1_TUPLE) -# Global Definitions -if(CPU_ONLY) - add_definitions(-DCPU_ONLY) -endif() - -# Include Directories -set(${PROJECT_NAME}_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/include) -include_directories(${${PROJECT_NAME}_INCLUDE_DIRS}) -include_directories(${CMAKE_SOURCE_DIR}/src) - -# CMake Scripts dir -set(CMAKE_SCRIPT_DIR ${CMAKE_SOURCE_DIR}/CMakeScripts) +# ---[ Warnings +caffe_warnings_disable(CMAKE_CXX_FLAGS -Wno-sign-compare -Wno-uninitialized) -# CMake module path for custom module finding -set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SCRIPT_DIR}) - -# CUDA is required globally -if(NOT CPU_ONLY) - find_package(CUDA 5.5 REQUIRED) - include_directories(${CUDA_INCLUDE_DIRS}) -endif() +# ---[ Config generation +configure_file(cmake/Templates/caffe_config.h.in "${PROJECT_BINARY_DIR}/caffe_config.h") -### Subdirectories ########################################################################## +# ---[ Includes +set(Caffe_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) +include_directories(${Caffe_INCLUDE_DIR} ${PROJECT_BINARY_DIR}) +include_directories(BEFORE src) # This is needed for gtest. +# ---[ Subdirectories add_subdirectory(src/gtest) add_subdirectory(src/caffe) add_subdirectory(tools) +add_subdirectory(examples) +add_subdirectory(python) +add_subdirectory(matlab) +add_subdirectory(docs) -if(BUILD_EXAMPLES) - message(STATUS "Examples enabled") - add_subdirectory(examples) -endif() - -if(BUILD_PYTHON) - message(STATUS "Python enabled") - add_subdirectory(python) -endif() - -if(BUILD_MATLAB) - message(STATUS "Matlab enabled") - add_subdirectory(matlab) -endif() - -### Lint Target Setup ########################################################################## - -set(LINT_TARGET lint) -set(LINT_SCRIPT ${CMAKE_SCRIPT_DIR}/lint.cmake) -add_custom_target( - ${LINT_TARGET} - COMMAND ${CMAKE_COMMAND} -P ${LINT_SCRIPT} -) - -### Install ################################################################################# - -# Install Includes -file(GLOB folders ${${PROJECT_NAME}_INCLUDE_DIRS}/*) -install(DIRECTORY ${folders} DESTINATION include) +# ---[ Linter target +add_custom_target(lint COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/lint.cmake) +# ---[ Configuration summary +caffe_print_configuration_summary() +# ---[ Export configs generation +caffe_generate_export_configs() diff --git a/CMakeScripts/FindLMDB.cmake b/CMakeScripts/FindLMDB.cmake deleted file mode 100644 index e615f542335..00000000000 --- a/CMakeScripts/FindLMDB.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# Try to find the LMBD libraries and headers -# LMDB_FOUND - system has LMDB lib -# LMDB_INCLUDE_DIR - the LMDB include directory -# LMDB_LIBRARIES - Libraries needed to use LMDB - -# FindCWD based on FindGMP by: -# Copyright (c) 2006, Laurent Montel, -# -# Redistribution and use is allowed according to the terms of the BSD license. - -# Adapted from FindCWD by: -# Copyright 2013 Conrad Steenberg -# Aug 31, 2013 - -if (LMDB_INCLUDE_DIR AND LMDB_LIBRARIES) - # Already in cache, be silent - set(LMDB_FIND_QUIETLY TRUE) -endif (LMDB_INCLUDE_DIR AND LMDB_LIBRARIES) - -find_path(LMDB_INCLUDE_DIR NAMES "lmdb.h" HINTS "$ENV{LMDB_DIR}/include") -find_library(LMDB_LIBRARIES NAMES lmdb HINTS $ENV{LMDB_DIR}/lib ) -MESSAGE(STATUS "LMDB lib: " ${LMDB_LIBRARIES} ) -MESSAGE(STATUS "LMDB include: " ${LMDB_INCLUDE} ) - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LMDB DEFAULT_MSG LMDB_INCLUDE_DIR LMDB_LIBRARIES) - -mark_as_advanced(LMDB_INCLUDE_DIR LMDB_LIBRARIES) diff --git a/CMakeScripts/FindLevelDB.cmake b/CMakeScripts/FindLevelDB.cmake deleted file mode 100644 index f3386f26dbf..00000000000 --- a/CMakeScripts/FindLevelDB.cmake +++ /dev/null @@ -1,37 +0,0 @@ -# - Find LevelDB -# -# LEVELDB_INCLUDE - Where to find leveldb/db.h -# LEVELDB_LIBS - List of libraries when using LevelDB. -# LEVELDB_FOUND - True if LevelDB found. - -get_filename_component(module_file_path ${CMAKE_CURRENT_LIST_FILE} PATH) - -# Look for the header file. -find_path(LEVELDB_INCLUDE NAMES leveldb/db.h PATHS $ENV{LEVELDB_ROOT}/include /opt/local/include /usr/local/include /usr/include DOC "Path in which the file leveldb/db.h is located." ) -mark_as_advanced(LEVELDB_INCLUDE) - -# Look for the library. -# Does this work on UNIX systems? (LINUX) -find_library(LEVELDB_LIBS NAMES leveldb PATHS /usr/lib $ENV{LEVELDB_ROOT}/lib DOC "Path to leveldb library." ) -mark_as_advanced(LEVELDB_LIBS) - -# Copy the results to the output variables. -if (LEVELDB_INCLUDE AND LEVELDB_LIBS) - message(STATUS "Found leveldb in ${LEVELDB_INCLUDE} ${LEVELDB_LIBS}") - set(LEVELDB_FOUND 1) - include(CheckCXXSourceCompiles) - set(CMAKE_REQUIRED_LIBRARY ${LEVELDB_LIBS} pthread) - set(CMAKE_REQUIRED_INCLUDES ${LEVELDB_INCLUDE}) - else () - set(LEVELDB_FOUND 0) - endif () - - # Report the results. - if (NOT LEVELDB_FOUND) - set(LEVELDB_DIR_MESSAGE "LEVELDB was not found. Make sure LEVELDB_LIBS and LEVELDB_INCLUDE are set.") - if (LEVELDB_FIND_REQUIRED) - message(FATAL_ERROR "${LEVELDB_DIR_MESSAGE}") - elseif (NOT LEVELDB_FIND_QUIETLY) - message(STATUS "${LEVELDB_DIR_MESSAGE}") - endif () - endif () \ No newline at end of file diff --git a/CMakeScripts/FindMKL.cmake b/CMakeScripts/FindMKL.cmake deleted file mode 100644 index eb2d9f8868b..00000000000 --- a/CMakeScripts/FindMKL.cmake +++ /dev/null @@ -1,113 +0,0 @@ -# - Find Intel MKL -# Find the MKL libraries -# -# Options: -# -# MKL_STATAIC : use static linking -# MKL_MULTI_THREADED: use multi-threading -# MKL_SDL : Single Dynamic Library interface -# -# This module defines the following variables: -# -# MKL_FOUND : True if MKL_INCLUDE_DIR are found -# MKL_INCLUDE_DIR : where to find mkl.h, etc. -# MKL_INCLUDE_DIRS : set when MKL_INCLUDE_DIR found -# MKL_LIBRARIES : the library to link against. - - -include(FindPackageHandleStandardArgs) - -set(INTEL_ROOT "/opt/intel" CACHE PATH "Folder contains intel libs") -set(MKL_ROOT ${INTEL_ROOT}/mkl CACHE PATH "Folder contains MKL") - -# Find include dir -find_path(MKL_INCLUDE_DIR mkl.h - PATHS ${MKL_ROOT}/include) - -# Find include directory -# There is no include folder under linux -if(WIN32) - find_path(INTEL_INCLUDE_DIR omp.h - PATHS ${INTEL_ROOT}/include) - set(MKL_INCLUDE_DIR ${MKL_INCLUDE_DIR} ${INTEL_INCLUDE_DIR}) -endif() - -# Find libraries - -# Handle suffix -set(_MKL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - -if(WIN32) - if(MKL_STATAIC) - set(CMAKE_FIND_LIBRARY_SUFFIXES .lib) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES _dll.lib) - endif() -else() - if(MKL_STATAIC) - set(CMAKE_FIND_LIBRARY_SUFFIXES .a) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES .so) - endif() -endif() - - -# MKL is composed by four layers: Interface, Threading, Computational and RTL - -if(MKL_SDL) - find_library(MKL_LIBRARY mkl_rt - PATHS ${MKL_ROOT}/lib/ia32/) - - set(MKL_MINIMAL_LIBRARY ${MKL_LIBRARY}) -else() - ######################### Interface layer ####################### - if(WIN32) - set(MKL_INTERFACE_LIBNAME mkl_intel_c) - else() - set(MKL_INTERFACE_LIBNAME mkl_intel) - endif() - - find_library(MKL_INTERFACE_LIBRARY ${MKL_INTERFACE_LIBNAME} - PATHS ${MKL_ROOT}/lib/ia32/) - - ######################## Threading layer ######################## - if(MKL_MULTI_THREADED) - set(MKL_THREADING_LIBNAME mkl_intel_thread) - else() - set(MKL_THREADING_LIBNAME mkl_sequential) - endif() - - find_library(MKL_THREADING_LIBRARY ${MKL_THREADING_LIBNAME} - PATHS ${MKL_ROOT}/lib/ia32/) - - ####################### Computational layer ##################### - find_library(MKL_CORE_LIBRARY mkl_core - PATHS ${MKL_ROOT}/lib/ia32/) - find_library(MKL_FFT_LIBRARY mkl_cdft_core - PATHS ${MKL_ROOT}/lib/ia32/) - find_library(MKL_SCALAPACK_LIBRARY mkl_scalapack_core - PATHS ${MKL_ROOT}/lib/ia32/) - - ############################ RTL layer ########################## - if(WIN32) - set(MKL_RTL_LIBNAME libiomp5md) - else() - set(MKL_RTL_LIBNAME libiomp5) - endif() - find_library(MKL_RTL_LIBRARY ${MKL_RTL_LIBNAME} - PATHS ${INTEL_RTL_ROOT}/lib) - - set(MKL_LIBRARY ${MKL_INTERFACE_LIBRARY} ${MKL_THREADING_LIBRARY} ${MKL_CORE_LIBRARY} ${MKL_FFT_LIBRARY} ${MKL_SCALAPACK_LIBRARY} ${MKL_RTL_LIBRARY}) - set(MKL_MINIMAL_LIBRARY ${MKL_INTERFACE_LIBRARY} ${MKL_THREADING_LIBRARY} ${MKL_CORE_LIBRARY} ${MKL_RTL_LIBRARY}) -endif() - -set(CMAKE_FIND_LIBRARY_SUFFIXES ${_MKL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) - -find_package_handle_standard_args(MKL DEFAULT_MSG - MKL_INCLUDE_DIR MKL_LIBRARY MKL_MINIMAL_LIBRARY) - -if(MKL_FOUND) - set(MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR}) - set(MKL_LIBRARIES ${MKL_LIBRARY}) - set(MKL_MINIMAL_LIBRARIES ${MKL_LIBRARY}) -endif() diff --git a/CMakeScripts/FindSnappy.cmake b/CMakeScripts/FindSnappy.cmake deleted file mode 100644 index d769b442812..00000000000 --- a/CMakeScripts/FindSnappy.cmake +++ /dev/null @@ -1,33 +0,0 @@ -# Find the Snappy libraries -# -# The following variables are optionally searched for defaults -# Snappy_ROOT_DIR: Base directory where all Snappy components are found -# -# The following are set after configuration is done: -# Snappy_FOUND -# Snappy_INCLUDE_DIRS -# Snappy_LIBS - -find_path(SNAPPY_INCLUDE_DIR - NAMES snappy.h - HINTS ${SNAPPY_ROOT_DIR} - ${SNAPPY_ROOT_DIR}/include -) - -find_library(SNAPPY_LIBS - NAMES snappy - HINTS ${SNAPPY_ROOT_DIR} - ${SNAPPY_ROOT_DIR}/lib -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Snappy - DEFAULT_MSG - SNAPPY_LIBS - SNAPPY_INCLUDE_DIR -) - -mark_as_advanced( - SNAPPY_LIBS - SNAPPY_INCLUDE_DIR -) diff --git a/Makefile b/Makefile index 6a660f5e0f2..29827270baf 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,11 @@ -# The makefile for caffe. Pretty hacky. PROJECT := caffe CONFIG_FILE := Makefile.config include $(CONFIG_FILE) BUILD_DIR_LINK := $(BUILD_DIR) -RELEASE_BUILD_DIR := .$(BUILD_DIR)_release -DEBUG_BUILD_DIR := .$(BUILD_DIR)_debug +RELEASE_BUILD_DIR ?= .$(BUILD_DIR)_release +DEBUG_BUILD_DIR ?= .$(BUILD_DIR)_debug DEBUG ?= 0 ifeq ($(DEBUG), 1) @@ -17,18 +16,20 @@ else OTHER_BUILD_DIR := $(DEBUG_BUILD_DIR) endif -# The target shared library and static library name +# All of the directories containing code. +SRC_DIRS := $(shell find * -type d -exec bash -c "find {} -maxdepth 1 \ + \( -name '*.cpp' -o -name '*.proto' \) | grep -q ." \; -print) + +# The target shared library name LIB_BUILD_DIR := $(BUILD_DIR)/lib -NAME := $(LIB_BUILD_DIR)/lib$(PROJECT).so STATIC_NAME := $(LIB_BUILD_DIR)/lib$(PROJECT).a +DYNAMIC_NAME := $(LIB_BUILD_DIR)/lib$(PROJECT).so ############################## # Get all source files ############################## # CXX_SRCS are the source files excluding the test ones. CXX_SRCS := $(shell find src/$(PROJECT) ! -name "test_*.cpp" -name "*.cpp") -# HXX_SRCS are the header files -HXX_SRCS := $(shell find include/$(PROJECT) ! -name "test_*.hpp" -name "*.hpp") # CU_SRCS are the cuda source files CU_SRCS := $(shell find src/$(PROJECT) ! -name "test_*.cu" -name "*.cu") # TEST_SRCS are the test source files @@ -37,8 +38,6 @@ TEST_SRCS := $(shell find src/$(PROJECT) -name "test_*.cpp") TEST_SRCS := $(filter-out $(TEST_MAIN_SRC), $(TEST_SRCS)) TEST_CU_SRCS := $(shell find src/$(PROJECT) -name "test_*.cu") GTEST_SRC := src/gtest/gtest-all.cpp -# TEST_HXX_SRCS are the test header files -TEST_HXX_SRCS := $(shell find include/$(PROJECT) -name "test_*.hpp") # TOOL_SRCS are the source files for the tool binaries TOOL_SRCS := $(shell find tools -name "*.cpp") # EXAMPLE_SRCS are the source files for the example binaries @@ -70,8 +69,8 @@ EMPTY_LINT_REPORT := $(BUILD_DIR)/.$(LINT_EXT) NONEMPTY_LINT_REPORT := $(BUILD_DIR)/$(LINT_EXT) # PY$(PROJECT)_SRC is the python wrapper for $(PROJECT) PY$(PROJECT)_SRC := python/$(PROJECT)/_$(PROJECT).cpp -PY$(PROJECT)_HXX_SRC := python/$(PROJECT)/_$(PROJECT).hpp PY$(PROJECT)_SO := python/$(PROJECT)/_$(PROJECT).so +PY$(PROJECT)_HXX := include/$(PROJECT)/python_layer.hpp # MAT$(PROJECT)_SRC is the matlab wrapper for $(PROJECT) MAT$(PROJECT)_SRC := matlab/$(PROJECT)/mat$(PROJECT).cpp ifneq ($(MATLAB_DIR),) @@ -87,7 +86,6 @@ PROTO_GEN_HEADER_SRCS := $(addprefix $(PROTO_BUILD_DIR)/, \ $(notdir ${PROTO_SRCS:.proto=.pb.h})) PROTO_GEN_HEADER := $(addprefix $(PROTO_BUILD_INCLUDE_DIR)/, \ $(notdir ${PROTO_SRCS:.proto=.pb.h})) -HXX_SRCS += $(PROTO_GEN_HEADER) PROTO_GEN_CC := $(addprefix $(BUILD_DIR)/, ${PROTO_SRCS:.proto=.pb.cc}) PY_PROTO_BUILD_DIR := python/$(PROJECT)/proto PY_PROTO_INIT := python/$(PROJECT)/proto/__init__.py @@ -97,25 +95,22 @@ PROTO_GEN_PY := $(foreach file,${PROTO_SRCS:.proto=_pb2.py}, \ # These objects will be linked into the final shared library, so we # exclude the tool, example, and test objects. CXX_OBJS := $(addprefix $(BUILD_DIR)/, ${CXX_SRCS:.cpp=.o}) -CU_OBJS := $(addprefix $(BUILD_DIR)/, ${CU_SRCS:.cu=.cuo}) +CU_OBJS := $(addprefix $(BUILD_DIR)/cuda/, ${CU_SRCS:.cu=.o}) PROTO_OBJS := ${PROTO_GEN_CC:.cc=.o} -OBJ_BUILD_DIR := $(BUILD_DIR)/src/$(PROJECT) -LAYER_BUILD_DIR := $(OBJ_BUILD_DIR)/layers -UTIL_BUILD_DIR := $(OBJ_BUILD_DIR)/util OBJS := $(PROTO_OBJS) $(CXX_OBJS) $(CU_OBJS) # tool, example, and test objects TOOL_OBJS := $(addprefix $(BUILD_DIR)/, ${TOOL_SRCS:.cpp=.o}) TOOL_BUILD_DIR := $(BUILD_DIR)/tools -TEST_BUILD_DIR := $(BUILD_DIR)/src/$(PROJECT)/test +TEST_CXX_BUILD_DIR := $(BUILD_DIR)/src/$(PROJECT)/test +TEST_CU_BUILD_DIR := $(BUILD_DIR)/cuda/src/$(PROJECT)/test TEST_CXX_OBJS := $(addprefix $(BUILD_DIR)/, ${TEST_SRCS:.cpp=.o}) -TEST_CU_OBJS := $(addprefix $(BUILD_DIR)/, ${TEST_CU_SRCS:.cu=.cuo}) +TEST_CU_OBJS := $(addprefix $(BUILD_DIR)/cuda/, ${TEST_CU_SRCS:.cu=.o}) TEST_OBJS := $(TEST_CXX_OBJS) $(TEST_CU_OBJS) GTEST_OBJ := $(addprefix $(BUILD_DIR)/, ${GTEST_SRC:.cpp=.o}) -GTEST_BUILD_DIR := $(dir $(GTEST_OBJ)) EXAMPLE_OBJS := $(addprefix $(BUILD_DIR)/, ${EXAMPLE_SRCS:.cpp=.o}) -EXAMPLE_BUILD_DIR := $(BUILD_DIR)/examples -EXAMPLE_BUILD_DIRS := $(EXAMPLE_BUILD_DIR) -EXAMPLE_BUILD_DIRS += $(foreach obj,$(EXAMPLE_OBJS),$(dir $(obj))) +# Output files for automatic dependency generation +DEPS := ${CXX_OBJS:.o=.d} ${CU_OBJS:.o=.d} ${TEST_CXX_OBJS:.o=.d} \ + ${TEST_CU_OBJS:.o=.d} # tool, example, and test bins TOOL_BINS := ${TOOL_OBJS:.o=.bin} EXAMPLE_BINS := ${EXAMPLE_OBJS:.o=.bin} @@ -128,6 +123,7 @@ TEST_CU_BINS := $(addsuffix .testbin,$(addprefix $(TEST_BIN_DIR)/, \ TEST_CXX_BINS := $(addsuffix .testbin,$(addprefix $(TEST_BIN_DIR)/, \ $(foreach obj,$(TEST_CXX_OBJS),$(basename $(notdir $(obj)))))) TEST_BINS := $(TEST_CXX_BINS) $(TEST_CU_BINS) +# TEST_ALL_BIN is the test binary that links caffe dynamically. TEST_ALL_BIN := $(TEST_BIN_DIR)/test_all.testbin ############################## @@ -135,11 +131,11 @@ TEST_ALL_BIN := $(TEST_BIN_DIR)/test_all.testbin ############################## WARNS_EXT := warnings.txt CXX_WARNS := $(addprefix $(BUILD_DIR)/, ${CXX_SRCS:.cpp=.o.$(WARNS_EXT)}) -CU_WARNS := $(addprefix $(BUILD_DIR)/, ${CU_SRCS:.cu=.cuo.$(WARNS_EXT)}) +CU_WARNS := $(addprefix $(BUILD_DIR)/cuda/, ${CU_SRCS:.cu=.o.$(WARNS_EXT)}) TOOL_WARNS := $(addprefix $(BUILD_DIR)/, ${TOOL_SRCS:.cpp=.o.$(WARNS_EXT)}) EXAMPLE_WARNS := $(addprefix $(BUILD_DIR)/, ${EXAMPLE_SRCS:.cpp=.o.$(WARNS_EXT)}) TEST_WARNS := $(addprefix $(BUILD_DIR)/, ${TEST_SRCS:.cpp=.o.$(WARNS_EXT)}) -TEST_CU_WARNS := $(addprefix $(BUILD_DIR)/, ${TEST_CU_SRCS:.cu=.cuo.$(WARNS_EXT)}) +TEST_CU_WARNS := $(addprefix $(BUILD_DIR)/cuda/, ${TEST_CU_SRCS:.cu=.o.$(WARNS_EXT)}) ALL_CXX_WARNS := $(CXX_WARNS) $(TOOL_WARNS) $(EXAMPLE_WARNS) $(TEST_WARNS) ALL_CU_WARNS := $(CU_WARNS) $(TEST_CU_WARNS) ALL_WARNS := $(ALL_CXX_WARNS) $(ALL_CU_WARNS) @@ -166,10 +162,8 @@ ifneq ($(CPU_ONLY), 1) LIBRARIES := cudart cublas curand endif LIBRARIES += glog gflags protobuf leveldb snappy \ - lmdb \ - boost_system \ - hdf5_hl hdf5 \ - opencv_core opencv_highgui opencv_imgproc pthread + lmdb boost_system hdf5_hl hdf5 m \ + opencv_core opencv_highgui opencv_imgproc PYTHON_LIBRARIES := boost_python python2.7 WARNINGS := -Wall -Wno-sign-compare @@ -183,14 +177,10 @@ ifneq ($(strip $(DISTRIBUTE_DIR)),distribute) DIST_ALIASES += distribute endif -ALL_BUILD_DIRS := $(sort \ - $(BUILD_DIR) $(LIB_BUILD_DIR) $(OBJ_BUILD_DIR) \ - $(LAYER_BUILD_DIR) $(UTIL_BUILD_DIR) $(TOOL_BUILD_DIR) \ - $(TEST_BUILD_DIR) $(TEST_BIN_DIR) $(GTEST_BUILD_DIR) \ - $(EXAMPLE_BUILD_DIRS) \ - $(LINT_OUTPUT_DIR) \ - $(PROTO_BUILD_DIR) $(PROTO_BUILD_INCLUDE_DIR) $(PY_PROTO_BUILD_DIR) \ - $(DISTRIBUTE_SUBDIRS)) +ALL_BUILD_DIRS := $(sort $(BUILD_DIR) $(addprefix $(BUILD_DIR)/, $(SRC_DIRS)) \ + $(addprefix $(BUILD_DIR)/cuda/, $(SRC_DIRS)) \ + $(LIB_BUILD_DIR) $(TEST_BIN_DIR) $(PY_PROTO_BUILD_DIR) $(LINT_OUTPUT_DIR) \ + $(DISTRIBUTE_SUBDIRS) $(PROTO_BUILD_INCLUDE_DIR)) ############################## # Set directory for Doxygen-generated documentation @@ -224,6 +214,7 @@ else ifeq ($(UNAME), Darwin) OSX := 1 endif +# Linux ifeq ($(LINUX), 1) CXX ?= /usr/bin/g++ GCCVERSION := $(shell $(CXX) -dumpversion | cut -f1,2 -d.) @@ -232,22 +223,31 @@ ifeq ($(LINUX), 1) WARNINGS += -Wno-uninitialized endif # boost::thread is reasonably called boost_thread (compare OS X) - LIBRARIES += boost_thread + # We will also explicitly add stdc++ to the link target. + LIBRARIES += boost_thread stdc++ endif # OS X: # clang++ instead of g++ -# libstdc++ instead of libc++ for CUDA compatibility on 10.9 +# libstdc++ for NVCC compatibility on OS X >= 10.9 with CUDA < 7.0 ifeq ($(OSX), 1) CXX := /usr/bin/clang++ - # clang throws this warning for cuda headers - WARNINGS += -Wno-unneeded-internal-declaration - ifneq ($(findstring 10.9, $(shell sw_vers -productVersion)),) + CUDA_VERSION := $(shell $(CUDA_DIR)/bin/nvcc -V | grep -o 'release \d' | grep -o '\d') + ifeq ($(shell echo $(CUDA_VERSION) \< 7.0 | bc), 1) CXXFLAGS += -stdlib=libstdc++ LINKFLAGS += -stdlib=libstdc++ endif + # clang throws this warning for cuda headers + WARNINGS += -Wno-unneeded-internal-declaration + # gtest needs to use its own tuple to not conflict with clang + COMMON_FLAGS += -DGTEST_USE_OWN_TR1_TUPLE=1 # boost::thread is called boost_thread-mt to mark multithreading on OS X LIBRARIES += boost_thread-mt + # we need to explicitly ask for the rpath to be obeyed + DYNAMIC_FLAGS := -install_name @rpath/libcaffe.so + ORIGIN := @loader_path +else + ORIGIN := \$$ORIGIN endif # Custom compiler @@ -255,6 +255,15 @@ ifdef CUSTOM_CXX CXX := $(CUSTOM_CXX) endif +# Static linking +ifneq (,$(findstring clang++,$(CXX))) + STATIC_LINK_COMMAND := -Wl,-force_load $(STATIC_NAME) +else ifneq (,$(findstring g++,$(CXX))) + STATIC_LINK_COMMAND := -Wl,--whole-archive $(STATIC_NAME) -Wl,--no-whole-archive +else + $(error Cannot static link with the $(CXX) compiler.) +endif + # Debugging ifeq ($(DEBUG), 1) COMMON_FLAGS += -DDEBUG -g -O0 @@ -279,6 +288,12 @@ ifeq ($(CPU_ONLY), 1) COMMON_FLAGS += -DCPU_ONLY endif +# Python layer support +ifeq ($(WITH_PYTHON_LAYER), 1) + COMMON_FLAGS += -DWITH_PYTHON_LAYER + LIBRARIES += $(PYTHON_LIBRARIES) +endif + # BLAS configuration (default = ATLAS) BLAS ?= atlas ifeq ($(BLAS), mkl) @@ -300,22 +315,41 @@ else endif else ifeq ($(OSX), 1) # OS X packages atlas as the vecLib framework - BLAS_INCLUDE ?= /System/Library/Frameworks/vecLib.framework/Versions/Current/Headers/ LIBRARIES += cblas - LDFLAGS += -framework vecLib + # 10.10 has accelerate while 10.9 has veclib + XCODE_CLT_VER := $(shell pkgutil --pkg-info=com.apple.pkg.CLTools_Executables | grep -o 'version: 6') + ifneq (,$(findstring version: 6,$(XCODE_CLT_VER))) + BLAS_INCLUDE ?= /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/Headers/ + LDFLAGS += -framework Accelerate + else + BLAS_INCLUDE ?= /System/Library/Frameworks/vecLib.framework/Versions/Current/Headers/ + LDFLAGS += -framework vecLib + endif endif endif INCLUDE_DIRS += $(BLAS_INCLUDE) LIBRARY_DIRS += $(BLAS_LIB) +LIBRARY_DIRS += $(LIB_BUILD_DIR) + +# Automatic dependency generation (nvcc is handled separately) +CXXFLAGS += -MMD -MP + # Complete build flags. COMMON_FLAGS += $(foreach includedir,$(INCLUDE_DIRS),-I$(includedir)) CXXFLAGS += -pthread -fPIC $(COMMON_FLAGS) $(WARNINGS) NVCCFLAGS += -ccbin=$(CXX) -Xcompiler -fPIC $(COMMON_FLAGS) # mex may invoke an older gcc that is too liberal with -Wuninitalized MATLAB_CXXFLAGS := $(CXXFLAGS) -Wno-uninitialized -LINKFLAGS += -fPIC $(COMMON_FLAGS) $(WARNINGS) -LDFLAGS += $(foreach librarydir,$(LIBRARY_DIRS),-L$(librarydir)) \ +LINKFLAGS += -pthread -fPIC $(COMMON_FLAGS) $(WARNINGS) + +USE_PKG_CONFIG ?= 0 +ifeq ($(USE_PKG_CONFIG), 1) + PKG_CONFIG := $(shell pkg-config opencv --libs) +else + PKG_CONFIG := +endif +LDFLAGS += $(foreach librarydir,$(LIBRARY_DIRS),-L$(librarydir)) $(PKG_CONFIG) \ $(foreach library,$(LIBRARIES),-l$(library)) PYTHON_LDFLAGS := $(LDFLAGS) $(foreach library,$(PYTHON_LIBRARIES),-l$(library)) @@ -330,6 +364,13 @@ PYTHON_LDFLAGS := $(LDFLAGS) $(foreach library,$(PYTHON_LIBRARIES),-l$(library)) # default behavior of 'find'. SUPERCLEAN_EXTS := .so .a .o .bin .testbin .pb.cc .pb.h _pb2.py .cuo +# Set the sub-targets of the 'everything' target. +EVERYTHING_TARGETS := all py$(PROJECT) test warn lint +# Only build matcaffe as part of "everything" if MATLAB_DIR is specified. +ifneq ($(MATLAB_DIR),) + EVERYTHING_TARGETS += mat$(PROJECT) +endif + ############################## # Define build targets ############################## @@ -337,9 +378,9 @@ SUPERCLEAN_EXTS := .so .a .o .bin .testbin .pb.cc .pb.h _pb2.py .cuo py mat py$(PROJECT) mat$(PROJECT) proto runtest \ superclean supercleanlist supercleanfiles warn everything -all: $(NAME) $(STATIC_NAME) tools examples +all: $(STATIC_NAME) $(DYNAMIC_NAME) tools examples -everything: all py$(PROJECT) mat$(PROJECT) test warn lint runtest +everything: $(EVERYTHING_TARGETS) linecount: cloc --read-lang-def=$(PROJECT).cloc \ @@ -376,7 +417,7 @@ $(LINT_OUTPUTS): $(LINT_OUTPUT_DIR)/%.lint.txt : % $(LINT_SCRIPT) | $(LINT_OUTPU > $@ \ || true -test: $(TEST_ALL_BIN) $(TEST_BINS) +test: $(TEST_ALL_BIN) $(TEST_ALL_DYNLINK_BIN) $(TEST_BINS) tools: $(TOOL_BINS) $(TOOL_BIN_LINKS) @@ -386,10 +427,11 @@ py$(PROJECT): py py: $(PY$(PROJECT)_SO) $(PROTO_GEN_PY) -$(PY$(PROJECT)_SO): $(STATIC_NAME) $(PY$(PROJECT)_SRC) $(PY$(PROJECT)_HXX_SRC) - $(CXX) -shared -o $@ $(PY$(PROJECT)_SRC) \ - $(STATIC_NAME) $(LINKFLAGS) $(PYTHON_LDFLAGS) - @ echo +$(PY$(PROJECT)_SO): $(PY$(PROJECT)_SRC) $(PY$(PROJECT)_HXX) | $(DYNAMIC_NAME) + @ echo CXX/LD -o $@ $< + $(Q)$(CXX) -shared -o $@ $(PY$(PROJECT)_SRC) \ + -o $@ $(LINKFLAGS) -l$(PROJECT) $(PYTHON_LDFLAGS) \ + -Wl,-rpath,$(ORIGIN)/../../build/lib mat$(PROJECT): mat @@ -401,15 +443,18 @@ $(MAT$(PROJECT)_SO): $(MAT$(PROJECT)_SRC) $(STATIC_NAME) "to build mat$(PROJECT)."; \ exit 1; \ fi - $(MATLAB_DIR)/bin/mex $(MAT$(PROJECT)_SRC) \ + @ echo MEX $< + $(Q)$(MATLAB_DIR)/bin/mex $(MAT$(PROJECT)_SRC) \ CXX="$(CXX)" \ CXXFLAGS="\$$CXXFLAGS $(MATLAB_CXXFLAGS)" \ - CXXLIBS="\$$CXXLIBS $(STATIC_NAME) $(LDFLAGS)" -output $@ - @ echo + CXXLIBS="\$$CXXLIBS $(STATIC_LINK_COMMAND) $(LDFLAGS)" -output $@ runtest: $(TEST_ALL_BIN) $(TEST_ALL_BIN) $(TEST_GPUID) --gtest_shuffle $(TEST_FILTER) +pytest: py + cd python; python -m unittest discover -s caffe/test + warn: $(EMPTY_WARN_REPORT) $(EMPTY_WARN_REPORT): $(ALL_WARNS) | $(BUILD_DIR) @@ -423,9 +468,7 @@ $(EMPTY_WARN_REPORT): $(ALL_WARNS) | $(BUILD_DIR) $(RM) $(NONEMPTY_WARN_REPORT); \ echo "No compiler warnings!"; -$(ALL_CXX_WARNS): %.o.$(WARNS_EXT) : %.o - -$(ALL_CU_WARNS): %.cuo.$(WARNS_EXT) : %.cuo +$(ALL_WARNS): %.o.$(WARNS_EXT) : %.o $(BUILD_DIR_LINK): $(BUILD_DIR)/.linked @@ -442,128 +485,74 @@ $(BUILD_DIR)/.linked: $(ALL_BUILD_DIRS): | $(BUILD_DIR_LINK) @ mkdir -p $@ -$(NAME): $(PROTO_OBJS) $(OBJS) | $(LIB_BUILD_DIR) - $(CXX) -shared -o $@ $(OBJS) $(LINKFLAGS) $(LDFLAGS) - @ echo - -$(STATIC_NAME): $(PROTO_OBJS) $(OBJS) | $(LIB_BUILD_DIR) - ar rcs $@ $(PROTO_OBJS) $(OBJS) - @ echo +$(DYNAMIC_NAME): $(OBJS) | $(LIB_BUILD_DIR) + @ echo LD -o $@ + $(Q)$(CXX) -shared -o $@ $(OBJS) $(LINKFLAGS) $(LDFLAGS) $(DYNAMIC_FLAGS) -$(TEST_BUILD_DIR)/%.o: src/$(PROJECT)/test/%.cpp $(HXX_SRCS) $(TEST_HXX_SRCS) \ - | $(TEST_BUILD_DIR) - $(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ - || (cat $@.$(WARNS_EXT); exit 1) - @ cat $@.$(WARNS_EXT) - @ echo +$(STATIC_NAME): $(OBJS) | $(LIB_BUILD_DIR) + @ echo AR -o $@ + $(Q)ar rcs $@ $(OBJS) -$(TEST_BUILD_DIR)/%.cuo: src/$(PROJECT)/test/%.cu $(HXX_SRCS) $(TEST_HXX_SRCS) \ - | $(TEST_BUILD_DIR) - $(CUDA_DIR)/bin/nvcc $(NVCCFLAGS) $(CUDA_ARCH) -c $< -o $@ 2> $@.$(WARNS_EXT) \ +$(BUILD_DIR)/%.o: %.cpp | $(ALL_BUILD_DIRS) + @ echo CXX $< + $(Q)$(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ || (cat $@.$(WARNS_EXT); exit 1) @ cat $@.$(WARNS_EXT) - @ echo - -$(TEST_ALL_BIN): $(TEST_MAIN_SRC) $(TEST_OBJS) $(GTEST_OBJ) $(STATIC_NAME) \ - | $(TEST_BIN_DIR) - $(CXX) $(TEST_MAIN_SRC) $(TEST_OBJS) $(GTEST_OBJ) $(STATIC_NAME) \ - -o $@ $(LINKFLAGS) $(LDFLAGS) - @ echo - -$(TEST_CU_BINS): $(TEST_BIN_DIR)/%.testbin: $(TEST_BUILD_DIR)/%.cuo $(GTEST_OBJ) $(STATIC_NAME) \ - | $(TEST_BIN_DIR) - $(CXX) $(TEST_MAIN_SRC) $< $(GTEST_OBJ) $(STATIC_NAME) \ - -o $@ $(LINKFLAGS) $(LDFLAGS) - @ echo - -$(TEST_CXX_BINS): $(TEST_BIN_DIR)/%.testbin: $(TEST_BUILD_DIR)/%.o $(GTEST_OBJ) $(STATIC_NAME) \ - | $(TEST_BIN_DIR) - $(CXX) $(TEST_MAIN_SRC) $< $(GTEST_OBJ) $(STATIC_NAME) \ - -o $@ $(LINKFLAGS) $(LDFLAGS) - @ echo - -# Target for extension-less symlinks to tool binaries with extension '*.bin'. -$(TOOL_BUILD_DIR)/%: $(TOOL_BUILD_DIR)/%.bin | $(TOOL_BUILD_DIR) - @ $(RM) $@ - @ ln -s $(abspath $<) $@ - -$(TOOL_BINS): %.bin : %.o $(STATIC_NAME) - $(CXX) $< $(STATIC_NAME) -o $@ $(LINKFLAGS) $(LDFLAGS) - @ echo - -$(EXAMPLE_BINS): %.bin : %.o $(STATIC_NAME) - $(CXX) $< $(STATIC_NAME) -o $@ $(LINKFLAGS) $(LDFLAGS) - @ echo - -$(LAYER_BUILD_DIR)/%.o: src/$(PROJECT)/layers/%.cpp $(HXX_SRCS) \ - | $(LAYER_BUILD_DIR) - $(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ - || (cat $@.$(WARNS_EXT); exit 1) - @ cat $@.$(WARNS_EXT) - @ echo $(PROTO_BUILD_DIR)/%.pb.o: $(PROTO_BUILD_DIR)/%.pb.cc $(PROTO_GEN_HEADER) \ | $(PROTO_BUILD_DIR) - $(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ - || (cat $@.$(WARNS_EXT); exit 1) - @ cat $@.$(WARNS_EXT) - @ echo - -$(UTIL_BUILD_DIR)/%.o: src/$(PROJECT)/util/%.cpp $(HXX_SRCS) | $(UTIL_BUILD_DIR) - $(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ + @ echo CXX $< + $(Q)$(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ || (cat $@.$(WARNS_EXT); exit 1) @ cat $@.$(WARNS_EXT) - @ echo -$(GTEST_OBJ): $(GTEST_SRC) | $(GTEST_BUILD_DIR) - $(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ +$(BUILD_DIR)/cuda/%.o: %.cu | $(ALL_BUILD_DIRS) + @ echo NVCC $< + $(Q)$(CUDA_DIR)/bin/nvcc $(NVCCFLAGS) $(CUDA_ARCH) -M $< -o ${@:.o=.d} \ + -odir $(@D) + $(Q)$(CUDA_DIR)/bin/nvcc $(NVCCFLAGS) $(CUDA_ARCH) -c $< -o $@ 2> $@.$(WARNS_EXT) \ || (cat $@.$(WARNS_EXT); exit 1) @ cat $@.$(WARNS_EXT) - @ echo -$(LAYER_BUILD_DIR)/%.cuo: src/$(PROJECT)/layers/%.cu $(HXX_SRCS) \ - | $(LAYER_BUILD_DIR) - $(CUDA_DIR)/bin/nvcc $(NVCCFLAGS) $(CUDA_ARCH) -c $< -o $@ 2> $@.$(WARNS_EXT) \ - || (cat $@.$(WARNS_EXT); exit 1) - @ cat $@.$(WARNS_EXT) - @ echo +$(TEST_ALL_BIN): $(TEST_MAIN_SRC) $(TEST_OBJS) $(GTEST_OBJ) \ + | $(DYNAMIC_NAME) $(TEST_BIN_DIR) + @ echo CXX/LD -o $@ $< + $(Q)$(CXX) $(TEST_MAIN_SRC) $(TEST_OBJS) $(GTEST_OBJ) \ + -o $@ $(LINKFLAGS) $(LDFLAGS) -l$(PROJECT) -Wl,-rpath,$(ORIGIN)/../lib -$(UTIL_BUILD_DIR)/%.cuo: src/$(PROJECT)/util/%.cu | $(UTIL_BUILD_DIR) - $(CUDA_DIR)/bin/nvcc $(NVCCFLAGS) $(CUDA_ARCH) -c $< -o $@ 2> $@.$(WARNS_EXT) \ - || (cat $@.$(WARNS_EXT); exit 1) - @ cat $@.$(WARNS_EXT) - @ echo +$(TEST_CU_BINS): $(TEST_BIN_DIR)/%.testbin: $(TEST_CU_BUILD_DIR)/%.o \ + $(GTEST_OBJ) | $(DYNAMIC_NAME) $(TEST_BIN_DIR) + @ echo LD $< + $(Q)$(CXX) $(TEST_MAIN_SRC) $< $(GTEST_OBJ) \ + -o $@ $(LINKFLAGS) $(LDFLAGS) -l$(PROJECT) -Wl,-rpath,$(ORIGIN)/../lib -$(TOOL_BUILD_DIR)/%.o: tools/%.cpp $(PROTO_GEN_HEADER) | $(TOOL_BUILD_DIR) - $(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ - || (cat $@.$(WARNS_EXT); exit 1) - @ cat $@.$(WARNS_EXT) - @ echo +$(TEST_CXX_BINS): $(TEST_BIN_DIR)/%.testbin: $(TEST_CXX_BUILD_DIR)/%.o \ + $(GTEST_OBJ) | $(DYNAMIC_NAME) $(TEST_BIN_DIR) + @ echo LD $< + $(Q)$(CXX) $(TEST_MAIN_SRC) $< $(GTEST_OBJ) \ + -o $@ $(LINKFLAGS) $(LDFLAGS) -l$(PROJECT) -Wl,-rpath,$(ORIGIN)/../lib -$(EXAMPLE_BUILD_DIR)/%.o: examples/%.cpp $(PROTO_GEN_HEADER) \ - | $(EXAMPLE_BUILD_DIRS) - $(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ - || (cat $@.$(WARNS_EXT); exit 1) - @ cat $@.$(WARNS_EXT) - @ echo +# Target for extension-less symlinks to tool binaries with extension '*.bin'. +$(TOOL_BUILD_DIR)/%: $(TOOL_BUILD_DIR)/%.bin | $(TOOL_BUILD_DIR) + @ $(RM) $@ + @ ln -s $(abspath $<) $@ -$(BUILD_DIR)/src/$(PROJECT)/%.o: src/$(PROJECT)/%.cpp $(HXX_SRCS) - $(CXX) $< $(CXXFLAGS) -c -o $@ 2> $@.$(WARNS_EXT) \ - || (cat $@.$(WARNS_EXT); exit 1) - @ cat $@.$(WARNS_EXT) - @ echo +$(TOOL_BINS) $(EXAMPLE_BINS): %.bin : %.o | $(DYNAMIC_NAME) + @ echo CXX/LD -o $@ + $(Q)$(CXX) $< -o $@ $(LINKFLAGS) -l$(PROJECT) $(LDFLAGS) \ + -Wl,-rpath,$(ORIGIN)/../../lib proto: $(PROTO_GEN_CC) $(PROTO_GEN_HEADER) $(PROTO_BUILD_DIR)/%.pb.cc $(PROTO_BUILD_DIR)/%.pb.h : \ $(PROTO_SRC_DIR)/%.proto | $(PROTO_BUILD_DIR) - protoc --proto_path=$(PROTO_SRC_DIR) --cpp_out=$(PROTO_BUILD_DIR) $< - @ echo + @ echo PROTOC $< + $(Q)protoc --proto_path=$(PROTO_SRC_DIR) --cpp_out=$(PROTO_BUILD_DIR) $< $(PY_PROTO_BUILD_DIR)/%_pb2.py : $(PROTO_SRC_DIR)/%.proto \ $(PY_PROTO_INIT) | $(PY_PROTO_BUILD_DIR) - protoc --proto_path=$(PROTO_SRC_DIR) --python_out=$(PY_PROTO_BUILD_DIR) $< - @ echo + @ echo PROTOC \(python\) $< + $(Q)protoc --proto_path=$(PROTO_SRC_DIR) --python_out=$(PY_PROTO_BUILD_DIR) $< $(PY_PROTO_INIT): | $(PY_PROTO_BUILD_DIR) touch $(PY_PROTO_INIT) @@ -601,7 +590,7 @@ superclean: clean supercleanfiles $(DIST_ALIASES): $(DISTRIBUTE_DIR) -$(DISTRIBUTE_DIR): all py $(HXX_SRCS) | $(DISTRIBUTE_SUBDIRS) +$(DISTRIBUTE_DIR): all py | $(DISTRIBUTE_SUBDIRS) # add include cp -r include $(DISTRIBUTE_DIR)/ mkdir -p $(DISTRIBUTE_DIR)/include/caffe/proto @@ -610,7 +599,9 @@ $(DISTRIBUTE_DIR): all py $(HXX_SRCS) | $(DISTRIBUTE_SUBDIRS) cp $(TOOL_BINS) $(DISTRIBUTE_DIR)/bin cp $(EXAMPLE_BINS) $(DISTRIBUTE_DIR)/bin # add libraries - cp $(NAME) $(DISTRIBUTE_DIR)/lib cp $(STATIC_NAME) $(DISTRIBUTE_DIR)/lib + cp $(DYNAMIC_NAME) $(DISTRIBUTE_DIR)/lib # add python - it's not the standard way, indeed... cp -r python $(DISTRIBUTE_DIR)/python + +-include $(DEPS) diff --git a/Makefile.config.example b/Makefile.config.example index 18798ed2d06..7a8aafd7c9f 100644 --- a/Makefile.config.example +++ b/Makefile.config.example @@ -47,18 +47,27 @@ BLAS := atlas PYTHON_INCLUDE := /usr/include/python2.7 \ /usr/lib/python2.7/dist-packages/numpy/core/include # Anaconda Python distribution is quite popular. Include path: -# PYTHON_INCLUDE := $(HOME)/anaconda/include \ - # $(HOME)/anaconda/include/python2.7 \ - # $(HOME)/anaconda/lib/python2.7/site-packages/numpy/core/include +# Verify anaconda location, sometimes it's in root. +# ANACONDA_HOME := $(HOME)/anaconda +# PYTHON_INCLUDE := $(ANACONDA_HOME)/include \ + # $(ANACONDA_HOME)/include/python2.7 \ + # $(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include \ # We need to be able to find libpythonX.X.so or .dylib. PYTHON_LIB := /usr/lib -# PYTHON_LIB := $(HOME)/anaconda/lib +# PYTHON_LIB := $(ANACONDA_HOME)/lib + +# Uncomment to support layers written in Python (will link against Python libs) +# WITH_PYTHON_LAYER := 1 # Whatever else you find you need goes here. INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib +# Uncomment to use `pkg-config` to specify OpenCV library paths. +# (Usually not necessary -- OpenCV libraries are normally installed in one of the above $LIBRARY_DIRS.) +# USE_PKG_CONFIG := 1 + BUILD_DIR := build DISTRIBUTE_DIR := distribute @@ -67,3 +76,6 @@ DISTRIBUTE_DIR := distribute # The ID of the GPU that 'make runtest' will use to run unit tests. TEST_GPUID := 0 + +# enable pretty build (comment to see full commands) +Q ?= @ diff --git a/README.md b/README.md index 9a9b3ee36eb..ebec286d550 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,34 @@ # Caffe -Caffe is a deep learning framework developed with cleanliness, readability, and speed in mind.
-Consult the [project website](http://caffe.berkeleyvision.org) for all documentation. +Caffe is a deep learning framework made with expression, speed, and modularity in mind. +It is developed by the Berkeley Vision and Learning Center ([BVLC](http://bvlc.eecs.berkeley.edu)) and community contributors. +Check out the [project site](http://caffe.berkeleyvision.org) for all the details like -Please ask usage questions and how to model different tasks on the [caffe-users mailing list](https://groups.google.com/forum/#!forum/caffe-users). +- [DIY Deep Learning for Vision with Caffe](https://docs.google.com/presentation/d/1UeKXVgRvvxg9OUdh_UiC5G71UMscNPlvArsWER41PsU/edit#slide=id.p) +- [Tutorial Documentation](http://caffe.berkeleyvision.org/tutorial/) +- [BVLC reference models](http://caffe.berkeleyvision.org/model_zoo.html) and the [community model zoo](https://github.com/BVLC/caffe/wiki/Model-Zoo) +- [Installation instructions](http://caffe.berkeleyvision.org/installation.html) +and step-by-step examples. + +[![Join the chat at https://gitter.im/BVLC/caffe](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/BVLC/caffe?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +Please join the [caffe-users group](https://groups.google.com/forum/#!forum/caffe-users) or [gitter chat](https://gitter.im/BVLC/caffe) to ask questions and talk about methods and models. +Framework development discussions and thorough bug reports are collected on [Issues](https://github.com/BVLC/caffe/issues). + +Happy brewing! + +## License and Citation + +Caffe is released under the [BSD 2-Clause license](https://github.com/BVLC/caffe/blob/master/LICENSE). +The BVLC reference models are released for unrestricted use. + +Please cite Caffe in your publications if it helps your research: + + @article{jia2014caffe, + Author = {Jia, Yangqing and Shelhamer, Evan and Donahue, Jeff and Karayev, Sergey and Long, Jonathan and Girshick, Ross and Guadarrama, Sergio and Darrell, Trevor}, + Journal = {arXiv preprint arXiv:1408.5093}, + Title = {Caffe: Convolutional Architecture for Fast Feature Embedding}, + Year = {2014} + } diff --git a/cmake/ConfigGen.cmake b/cmake/ConfigGen.cmake new file mode 100644 index 00000000000..c82047dcc5f --- /dev/null +++ b/cmake/ConfigGen.cmake @@ -0,0 +1,93 @@ + +################################################################################################ +# Helper function to fetch caffe includes which will be passed to dependent projects +# Usage: +# caffe_get_current_includes() +function(caffe_get_current_includes includes_variable) + get_property(current_includes DIRECTORY PROPERTY INCLUDE_DIRECTORIES) + caffe_convert_absolute_paths(current_includes) + + # remove at most one ${PROJECT_BINARY_DIR} include added for caffe_config.h + list(FIND current_includes ${PROJECT_BINARY_DIR} __index) + list(REMOVE_AT current_includes ${__index}) + + caffe_list_unique(current_includes) + set(${includes_variable} ${current_includes} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Helper function to get all list items that begin with given prefix +# Usage: +# caffe_get_items_with_prefix( ) +function(caffe_get_items_with_prefix prefix list_variable output_variable) + set(__result "") + foreach(__e ${${list_variable}}) + if(__e MATCHES "^${prefix}.*") + list(APPEND __result ${__e}) + endif() + endforeach() + set(${output_variable} ${__result} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Function for generation Caffe build- and install- tree export config files +# Usage: +# caffe_generate_export_configs() +function(caffe_generate_export_configs) + set(install_cmake_suffix "share/Caffe") + + # ---[ Configure build-tree CaffeConfig.cmake file ]--- + caffe_get_current_includes(Caffe_INCLUDE_DIRS) + + set(Caffe_DEFINITIONS "") + if(NOT HAVE_CUDA) + set(HAVE_CUDA FALSE) + list(APPEND Caffe_DEFINITIONS -DCPU_ONLY) + endif() + + if(NOT HAVE_CUDNN) + set(HAVE_CUDNN FALSE) + else() + list(APPEND DEFINITIONS -DUSE_CUDNN) + endif() + + if(BLAS STREQUAL "MKL" OR BLAS STREQUAL "mkl") + list(APPEND Caffe_DEFINITIONS -DUSE_MKL) + endif() + + configure_file("cmake/Templates/CaffeConfig.cmake.in" "${PROJECT_BINARY_DIR}/CaffeConfig.cmake" @ONLY) + + # Add targets to the build-tree export set + export(TARGETS caffe proto FILE "${PROJECT_BINARY_DIR}/CaffeTargets.cmake") + export(PACKAGE Caffe) + + # ---[ Configure install-tree CaffeConfig.cmake file ]--- + + # remove source and build dir includes + caffe_get_items_with_prefix(${PROJECT_SOURCE_DIR} Caffe_INCLUDE_DIRS __insource) + caffe_get_items_with_prefix(${PROJECT_BINARY_DIR} Caffe_INCLUDE_DIRS __inbinary) + list(REMOVE_ITEM Caffe_INCLUDE_DIRS ${__insource} ${__inbinary}) + + # add `install` include folder + set(lines + "get_filename_component(__caffe_include \"\${Caffe_CMAKE_DIR}/../../include\" ABSOLUTE)\n" + "list(APPEND Caffe_INCLUDE_DIRS \${__caffe_include})\n" + "unset(__caffe_include)\n") + string(REPLACE ";" "" Caffe_INSTALL_INCLUDE_DIR_APPEND_COMMAND ${lines}) + + configure_file("cmake/Templates/CaffeConfig.cmake.in" "${PROJECT_BINARY_DIR}/cmake/CaffeConfig.cmake" @ONLY) + + # Install the CaffeConfig.cmake and export set to use wuth install-tree + install(FILES "${PROJECT_BINARY_DIR}/cmake/CaffeConfig.cmake" DESTINATION ${install_cmake_suffix}) + install(EXPORT CaffeTargets DESTINATION ${install_cmake_suffix}) + + # ---[ Configure and install version file ]--- + + # TODO: Lines below are commented because Caffe does't declare its version in headers. + # When the declarations are added, modify `caffe_extract_caffe_version()` macro and uncomment + + # configure_file(cmake/Templates/CaffeConfigVersion.cmake.in "${PROJECT_BINARY_DIR}/CaffeConfigVersion.cmake" @ONLY) + # install(FILES "${PROJECT_BINARY_DIR}/CaffeConfigVersion.cmake" DESTINATION ${install_cmake_suffix}) +endfunction() + + diff --git a/cmake/Cuda.cmake b/cmake/Cuda.cmake new file mode 100644 index 00000000000..ff58d31c166 --- /dev/null +++ b/cmake/Cuda.cmake @@ -0,0 +1,254 @@ +if(CPU_ONLY) + return() +endif() + +# Known NVIDIA GPU achitectures Caffe can be compiled for. +# This list will be used for CUDA_ARCH_NAME = All option +set(Caffe_known_gpu_archs "20 21(20) 30 35 50") + +################################################################################################ +# A function for automatic detection of GPUs installed (if autodetection is enabled) +# Usage: +# caffe_detect_installed_gpus(out_variable) +function(caffe_detect_installed_gpus out_variable) + if(NOT CUDA_gpu_detect_output) + set(__cufile ${PROJECT_BINARY_DIR}/detect_cuda_archs.cu) + + file(WRITE ${__cufile} "" + "#include \n" + "int main()\n" + "{\n" + " int count = 0;\n" + " if (cudaSuccess != cudaGetDeviceCount(&count)) return -1;\n" + " if (count == 0) return -1;\n" + " for (int device = 0; device < count; ++device)\n" + " {\n" + " cudaDeviceProp prop;\n" + " if (cudaSuccess == cudaGetDeviceProperties(&prop, device))\n" + " std::printf(\"%d.%d \", prop.major, prop.minor);\n" + " }\n" + " return 0;\n" + "}\n") + + execute_process(COMMAND "${CUDA_NVCC_EXECUTABLE}" "--run" "${__cufile}" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/CMakeFiles/" + RESULT_VARIABLE __nvcc_res OUTPUT_VARIABLE __nvcc_out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(__nvcc_res EQUAL 0) + string(REPLACE "2.1" "2.1(2.0)" __nvcc_out "${__nvcc_out}") + set(CUDA_gpu_detect_output ${__nvcc_out} CACHE INTERNAL "Returned GPU architetures from caffe_detect_gpus tool" FORCE) + endif() + endif() + + if(NOT CUDA_gpu_detect_output) + message(STATUS "Automatic GPU detection failed. Building for all known architectures.") + set(${out_variable} ${Caffe_known_gpu_archs} PARENT_SCOPE) + else() + set(${out_variable} ${CUDA_gpu_detect_output} PARENT_SCOPE) + endif() +endfunction() + + +################################################################################################ +# Function for selecting GPU arch flags for nvcc based on CUDA_ARCH_NAME +# Usage: +# caffe_select_nvcc_arch_flags(out_variable) +function(caffe_select_nvcc_arch_flags out_variable) + # List of arch names + set(__archs_names "Fermi" "Kepler" "Maxwell" "All" "Manual") + set(__archs_name_default "All") + if(NOT CMAKE_CROSSCOMPILING) + list(APPEND __archs_names "Auto") + set(__archs_name_default "Auto") + endif() + + # set CUDA_ARCH_NAME strings (so it will be seen as dropbox in CMake-Gui) + set(CUDA_ARCH_NAME ${__archs_name_default} CACHE STRING "Select target NVIDIA GPU achitecture.") + set_property( CACHE CUDA_ARCH_NAME PROPERTY STRINGS "" ${__archs_names} ) + mark_as_advanced(CUDA_ARCH_NAME) + + # verify CUDA_ARCH_NAME value + if(NOT ";${__archs_names};" MATCHES ";${CUDA_ARCH_NAME};") + string(REPLACE ";" ", " __archs_names "${__archs_names}") + message(FATAL_ERROR "Only ${__archs_names} architeture names are supported.") + endif() + + if(${CUDA_ARCH_NAME} STREQUAL "Manual") + set(CUDA_ARCH_BIN ${Caffe_known_gpu_archs} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported") + set(CUDA_ARCH_PTX "50" CACHE STRING "Specify 'virtual' PTX architectures to build PTX intermediate code for") + mark_as_advanced(CUDA_ARCH_BIN CUDA_ARCH_PTX) + else() + unset(CUDA_ARCH_BIN CACHE) + unset(CUDA_ARCH_PTX CACHE) + endif() + + if(${CUDA_ARCH_NAME} STREQUAL "Fermi") + set(__cuda_arch_bin "20 21(20)") + elseif(${CUDA_ARCH_NAME} STREQUAL "Kepler") + set(__cuda_arch_bin "30 35") + elseif(${CUDA_ARCH_NAME} STREQUAL "Maxwell") + set(__cuda_arch_bin "50") + elseif(${CUDA_ARCH_NAME} STREQUAL "All") + set(__cuda_arch_bin ${Caffe_known_gpu_archs}) + elseif(${CUDA_ARCH_NAME} STREQUAL "Auto") + caffe_detect_installed_gpus(__cuda_arch_bin) + else() # (${CUDA_ARCH_NAME} STREQUAL "Manual") + set(__cuda_arch_bin ${CUDA_ARCH_BIN}) + endif() + + # remove dots and convert to lists + string(REGEX REPLACE "\\." "" __cuda_arch_bin "${__cuda_arch_bin}") + string(REGEX REPLACE "\\." "" __cuda_arch_ptx "${CUDA_ARCH_PTX}") + string(REGEX MATCHALL "[0-9()]+" __cuda_arch_bin "${__cuda_arch_bin}") + string(REGEX MATCHALL "[0-9]+" __cuda_arch_ptx "${__cuda_arch_ptx}") + caffe_list_unique(__cuda_arch_bin __cuda_arch_ptx) + + set(__nvcc_flags "") + set(__nvcc_archs_readable "") + + # Tell NVCC to add binaries for the specified GPUs + foreach(__arch ${__cuda_arch_bin}) + if(__arch MATCHES "([0-9]+)\\(([0-9]+)\\)") + # User explicitly specified PTX for the concrete BIN + list(APPEND __nvcc_flags -gencode arch=compute_${CMAKE_MATCH_2},code=sm_${CMAKE_MATCH_1}) + list(APPEND __nvcc_archs_readable sm_${CMAKE_MATCH_1}) + else() + # User didn't explicitly specify PTX for the concrete BIN, we assume PTX=BIN + list(APPEND __nvcc_flags -gencode arch=compute_${__arch},code=sm_${__arch}) + list(APPEND __nvcc_archs_readable sm_${__arch}) + endif() + endforeach() + + # Tell NVCC to add PTX intermediate code for the specified architectures + foreach(__arch ${__cuda_arch_ptx}) + list(APPEND __nvcc_flags -gencode arch=compute_${__arch},code=compute_${__arch}) + list(APPEND __nvcc_archs_readable compute_${__arch}) + endforeach() + + string(REPLACE ";" " " __nvcc_archs_readable "${__nvcc_archs_readable}") + set(${out_variable} ${__nvcc_flags} PARENT_SCOPE) + set(${out_variable}_readable ${__nvcc_archs_readable} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Short command for cuda comnpilation +# Usage: +# caffe_cuda_compile( ) +macro(caffe_cuda_compile objlist_variable) + foreach(var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG) + set(${var}_backup_in_cuda_compile_ "${${var}}") + + # we remove /EHa as it generates warnings under windows + string(REPLACE "/EHa" "" ${var} "${${var}}") + + endforeach() + + if(UNIX OR APPLE) + list(APPEND CUDA_NVCC_FLAGS -Xcompiler -fPIC) + endif() + + if(APPLE) + list(APPEND CUDA_NVCC_FLAGS -Xcompiler -Wno-unused-function) + endif() + + cuda_compile(cuda_objcs ${ARGN}) + + foreach(var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG) + set(${var} "${${var}_backup_in_cuda_compile_}") + unset(${var}_backup_in_cuda_compile_) + endforeach() + + set(${objlist_variable} ${cuda_objcs}) +endmacro() + +################################################################################################ +# Short command for cuDNN detection. Believe it soon will be a part of CUDA toolkit distribution. +# That's why not FindcuDNN.cmake file, but just the macro +# Usage: +# detect_cuDNN() +function(detect_cuDNN) + set(CUDNN_ROOT "" CACHE PATH "CUDNN root folder") + + find_path(CUDNN_INCLUDE cudnn.h + PATHS ${CUDNN_ROOT} $ENV{CUDNN_ROOT} ${CUDA_TOOLKIT_INCLUDE} + DOC "Path to cuDNN include directory." ) + + get_filename_component(__libpath_hist ${CUDA_CUDART_LIBRARY} PATH) + find_library(CUDNN_LIBRARY NAMES libcudnn.so # libcudnn_static.a + PATHS ${CUDNN_ROOT} $ENV{CUDNN_ROOT} ${CUDNN_INCLUDE} ${__libpath_hist} + DOC "Path to cuDNN library.") + + if(CUDNN_INCLUDE AND CUDNN_LIBRARY) + set(HAVE_CUDNN TRUE PARENT_SCOPE) + set(CUDNN_FOUND TRUE PARENT_SCOPE) + + mark_as_advanced(CUDNN_INCLUDE CUDNN_LIBRARY CUDNN_ROOT) + message(STATUS "Found cuDNN (include: ${CUDNN_INCLUDE}, library: ${CUDNN_LIBRARY})") + endif() +endfunction() + + +################################################################################################ +### Non macro section +################################################################################################ + +find_package(CUDA 5.5 QUIET) +find_cuda_helper_libs(curand) # cmake 2.8.7 compartibility which doesn't search for curand + +if(NOT CUDA_FOUND) + return() +endif() + +set(HAVE_CUDA TRUE) +message(STATUS "CUDA detected: " ${CUDA_VERSION}) +include_directories(SYSTEM ${CUDA_INCLUDE_DIRS}) +list(APPEND Caffe_LINKER_LIBS ${CUDA_CUDART_LIBRARY} + ${CUDA_curand_LIBRARY} ${CUDA_CUBLAS_LIBRARIES}) + +# cudnn detection +if(USE_CUDNN) + detect_cuDNN() + if(HAVE_CUDNN) + add_definitions(-DUSE_CUDNN) + include_directories(SYSTEM ${CUDNN_INCLUDE}) + list(APPEND Caffe_LINKER_LIBS ${CUDNN_LIBRARY}) + endif() +endif() + +# setting nvcc arch flags +caffe_select_nvcc_arch_flags(NVCC_FLAGS_EXTRA) +list(APPEND CUDA_NVCC_FLAGS ${NVCC_FLAGS_EXTRA}) +message(STATUS "Added CUDA NVCC flags for: ${NVCC_FLAGS_EXTRA_readable}") + +# Boost 1.55 workaround, see https://svn.boost.org/trac/boost/ticket/9392 or +# https://github.com/ComputationalRadiationPhysics/picongpu/blob/master/src/picongpu/CMakeLists.txt +if(Boost_VERSION EQUAL 105500) + message(STATUS "Cuda + Boost 1.55: Applying noinline work around") + # avoid warning for CMake >= 2.8.12 + set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} \"-DBOOST_NOINLINE=__attribute__((noinline))\" ") +endif() + +# disable some nvcc diagnostic that apears in boost, glog, glags, opencv, etc. +foreach(diag cc_clobber_ignored integer_sign_change useless_using_declaration set_but_not_used) + list(APPEND CUDA_NVCC_FLAGS -Xcudafe --diag_suppress=${diag}) +endforeach() + +# setting default testing device +if(NOT CUDA_TEST_DEVICE) + set(CUDA_TEST_DEVICE -1) +endif() + +mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_VERBOSE_BUILD) +mark_as_advanced(CUDA_SDK_ROOT_DIR CUDA_SEPARABLE_COMPILATION) + +# Handle clang/libc++ issue +if(APPLE) + caffe_detect_darwin_version(OSX_VERSION) + + # OSX 10.9 and higher uses clang/libc++ by default which is incompartible with old CUDA toolkits + if(OSX_VERSION VERSION_GREATER 10.8) + # enabled by default if and only if CUDA version is less than 7.0 + caffe_option(USE_libstdcpp "Use libstdc++ instead of libc++" (CUDA_VERSION VERSION_LESS 7.0)) + endif() +endif() diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake new file mode 100644 index 00000000000..aa2dcbe1d0d --- /dev/null +++ b/cmake/Dependencies.cmake @@ -0,0 +1,125 @@ +# This list is required for static linking and exported to CaffeConfig.cmake +set(Caffe_LINKER_LIBS "") + +# ---[ Boost +find_package(Boost 1.46 REQUIRED COMPONENTS system thread) +include_directories(SYSTEM ${Boost_INCLUDE_DIR}) +list(APPEND Caffe_LINKER_LIBS ${Boost_LIBRARIES}) + +# ---[ Threads +find_package(Threads REQUIRED) +list(APPEND Caffe_LINKER_LIBS ${CMAKE_THREAD_LIBS_INIT}) + +# ---[ Google-glog +find_package(Glog REQUIRED) +include_directories(SYSTEM ${GLOG_INCLUDE_DIRS}) +list(APPEND Caffe_LINKER_LIBS ${GLOG_LIBRARIES}) + +# ---[ Google-gflags +find_package(GFlags REQUIRED) +include_directories(SYSTEM ${GFLAGS_INCLUDE_DIRS}) +list(APPEND Caffe_LINKER_LIBS ${GFLAGS_LIBRARIES}) + +# ---[ Google-protobuf +include(cmake/ProtoBuf.cmake) + +# ---[ HDF5 +find_package(HDF5 COMPONENTS HL REQUIRED) +include_directories(SYSTEM ${HDF5_INCLUDE_DIRS}) +list(APPEND Caffe_LINKER_LIBS ${HDF5_LIBRARIES}) + +# ---[ LMDB +find_package(LMDB REQUIRED) +include_directories(SYSTEM ${LMDB_INCLUDE_DIR}) +list(APPEND Caffe_LINKER_LIBS ${LMDB_LIBRARIES}) + +# ---[ LevelDB +find_package(LevelDB REQUIRED) +include_directories(SYSTEM ${LEVELDB_INCLUDE}) +list(APPEND Caffe_LINKER_LIBS ${LevelDB_LIBRARIES}) + +# ---[ Snappy +find_package(Snappy REQUIRED) +include_directories(SYSTEM ${Snappy_INCLUDE_DIR}) +list(APPEND Caffe_LINKER_LIBS ${Snappy_LIBRARIES}) + +# ---[ CUDA +include(cmake/Cuda.cmake) +if(NOT HAVE_CUDA) + if(CPU_ONLY) + message("-- CUDA is disabled. Building without it...") + else() + message("-- CUDA is not detected by cmake. Building without it...") + endif() + + # TODO: remove this not cross platform define in future. Use caffe_config.h instead. + add_definitions(-DCPU_ONLY) +endif() + +# ---[ OpenCV +find_package(OpenCV QUIET COMPONENTS core highgui imgproc imgcodecs) +if(NOT OpenCV_FOUND) # if not OpenCV 3.x, then imgcodecs are not found + find_package(OpenCV REQUIRED COMPONENTS core highgui imgproc) +endif() +include_directories(SYSTEM ${OpenCV_INCLUDE_DIRS}) +list(APPEND Caffe_LINKER_LIBS ${OpenCV_LIBS}) +message(STATUS "OpenCV found (${OpenCV_CONFIG_PATH})") + +# ---[ BLAS +if(NOT APPLE) + set(BLAS "Atlas" CACHE STRING "Selected BLAS library") + set_property(CACHE BLAS PROPERTY STRINGS "Atlas;Open;MKL") + + if(BLAS STREQUAL "Atlas" OR BLAS STREQUAL "atlas") + find_package(Atlas REQUIRED) + include_directories(SYSTEM ${Atlas_INCLUDE_DIR}) + list(APPEND Caffe_LINKER_LIBS ${Atlas_LIBRARIES}) + elseif(BLAS STREQUAL "Open" OR BLAS STREQUAL "open") + find_package(OpenBLAS REQUIRED) + include_directories(SYSTEM ${OpenBLAS_INCLUDE_DIR}) + list(APPEND Caffe_LINKER_LIBS ${OpenBLAS_LIB}) + elseif(BLAS STREQUAL "MKL" OR BLAS STREQUAL "mkl") + find_package(MKL REQUIRED) + include_directories(SYSTEM ${MKL_INCLUDE_DIR}) + list(APPEND Caffe_LINKER_LIBS ${MKL_LIBRARIES}) + add_definitions(-DUSE_MKL) + endif() +elseif(APPLE) + find_package(vecLib REQUIRED) + include_directories(SYSTEM ${vecLib_INCLUDE_DIR}) + list(APPEND Caffe_LINKER_LIBS ${vecLib_LINKER_LIBS}) +endif() + +# ---[ Python +if(BUILD_python) + # disable Python 3 search + find_package(PythonInterp 2.7) + find_package(PythonLibs 2.7) + find_package(NumPy 1.7.1) + find_package(Boost 1.46 COMPONENTS python) + + if(PYTHONLIBS_FOUND AND NUMPY_FOUND AND Boost_PYTHON_FOUND) + set(HAVE_PYTHON TRUE) + endif() +endif() + +# ---[ Matlab +if(BUILD_matlab) + find_package(MatlabMex) + if(MATLABMEX_FOUND) + set(HAVE_MATLAB TRUE) + endif() + + # sudo apt-get install liboctave-dev + find_program(Octave_compiler NAMES mkoctfile DOC "Octave C++ compiler") + + if(HAVE_MATLAB AND Octave_compiler) + set(Matlab_build_mex_using "Matlab" CACHE STRING "Select Matlab or Octave if both detected") + set_property(CACHE Matlab_build_mex_using PROPERTY STRINGS "Matlab;Octave") + endif() +endif() + +# ---[ Doxygen +if(BUILD_docs) + find_package(Doxygen) +endif() diff --git a/cmake/Misc.cmake b/cmake/Misc.cmake new file mode 100644 index 00000000000..608a5f13a79 --- /dev/null +++ b/cmake/Misc.cmake @@ -0,0 +1,47 @@ +# ---[ Configurations types +set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Possible configurations" FORCE) +mark_as_advanced(CMAKE_CONFIGURATION_TYPES) + +if(DEFINED CMAKE_BUILD_TYPE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${CMAKE_CONFIGURATION_TYPES}) +endif() + +# --[ If user doesn't specify build type then assume release +if("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE Release) +endif() + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_COMPILER_IS_CLANGXX TRUE) +endif() + +# ---[ Solution folders +caffe_option(USE_PROJECT_FOLDERS "IDE Solution folders" (MSVC_IDE OR CMAKE_GENERATOR MATCHES Xcode) ) + +if(USE_PROJECT_FOLDERS) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMakeTargets") +endif() + +# ---[ Install options +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/install" CACHE PATH "Default install path" FORCE) +endif() + +# ---[ RPATH settings +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE BOOLEAN "Use link paths for shared library rpath") +set(CMAKE_MACOSX_RPATH TRUE) + +# ---[ Funny target +if(UNIX OR APPLE) + add_custom_target(symlink_to_build COMMAND "ln" "-sf" "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/build" + COMMENT "Adding symlink: /build -> ${PROJECT_BINARY_DIR}" ) +endif() + +# ---[ Set debug postfix +set(Caffe_DEBUG_POSTFIX "-d") + +set(CAffe_POSTFIX "") +if(CMAKE_BUILD_TYPE MATCHES "Debug") + set(CAffe_POSTFIX ${Caffe_DEBUG_POSTFIX}) +endif() diff --git a/CMakeScripts/FindAtlas.cmake b/cmake/Modules/FindAtlas.cmake similarity index 63% rename from CMakeScripts/FindAtlas.cmake rename to cmake/Modules/FindAtlas.cmake index 27657a6c7d7..6e1564351c7 100644 --- a/CMakeScripts/FindAtlas.cmake +++ b/cmake/Modules/FindAtlas.cmake @@ -23,14 +23,14 @@ set(Atlas_LIB_SEARCH_PATHS $ENV{Atlas_ROOT_DIR}/lib ) -find_path(Atlas_CBLAS_INCLUDE_DIR NAMES cblas.h PATHS ${Atlas_INCLUDE_SEARCH_PATHS}) +find_path(Atlas_CBLAS_INCLUDE_DIR NAMES cblas.h PATHS ${Atlas_INCLUDE_SEARCH_PATHS}) find_path(Atlas_CLAPACK_INCLUDE_DIR NAMES clapack.h PATHS ${Atlas_INCLUDE_SEARCH_PATHS}) -find_library(Atlas_CBLAS_LIBRARY NAMES ptcblas_r ptcblas cblas_r cblas PATHS ${Atlas_LIB_SEARCH_PATHS}) -find_library(Atlas_BLAS_LIBRARY NAMES atlas_r atlas PATHS ${Atlas_LIB_SEARCH_PATHS}) -find_library(Atlas_LAPACK_LIBRARY NAMES alapack_r alapack lapack_atlas PATHS ${Atlas_LIB_SEARCH_PATHS}) -set(LOOKED_FOR +find_library(Atlas_CBLAS_LIBRARY NAMES ptcblas_r ptcblas cblas_r cblas PATHS ${Atlas_LIB_SEARCH_PATHS}) +find_library(Atlas_BLAS_LIBRARY NAMES atlas_r atlas PATHS ${Atlas_LIB_SEARCH_PATHS}) +find_library(Atlas_LAPACK_LIBRARY NAMES alapack_r alapack lapack_atlas PATHS ${Atlas_LIB_SEARCH_PATHS}) +set(LOOKED_FOR Atlas_CBLAS_INCLUDE_DIR Atlas_CLAPACK_INCLUDE_DIR @@ -43,19 +43,10 @@ include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Atlas DEFAULT_MSG ${LOOKED_FOR}) if(ATLAS_FOUND) - + set(Atlas_INCLUDE_DIR ${Atlas_CBLAS_INCLUDE_DIR} ${Atlas_CLAPACK_INCLUDE_DIR}) + set(Atlas_LIBRARIES ${Atlas_LAPACK_LIBRARY} ${Atlas_CBLAS_LIBRARY} ${Atlas_BLAS_LIBRARY}) mark_as_advanced(${LOOKED_FOR}) - set(Atlas_INCLUDE_DIR - ${Atlas_CBLAS_INCLUDE_DIR} - ${Atlas_CLAPACK_INCLUDE_DIR} - ) - - set(Atlas_LIBRARIES - ${Atlas_LAPACK_LIBRARY} - ${Atlas_CBLAS_LIBRARY} - ${Atlas_BLAS_LIBRARY} - ) - + message(STATUS "Found Atlas (include: ${Atlas_CBLAS_INCLUDE_DIR}, library: ${Atlas_BLAS_LIBRARY})") endif(ATLAS_FOUND) diff --git a/CMakeScripts/FindGFlags.cmake b/cmake/Modules/FindGFlags.cmake similarity index 79% rename from CMakeScripts/FindGFlags.cmake rename to cmake/Modules/FindGFlags.cmake index f93c57136a1..146e8455a50 100644 --- a/CMakeScripts/FindGFlags.cmake +++ b/cmake/Modules/FindGFlags.cmake @@ -38,11 +38,13 @@ else() find_library(GFLAGS_LIBRARY gflags) endif() -find_package_handle_standard_args(GFLAGS DEFAULT_MSG - GFLAGS_INCLUDE_DIR GFLAGS_LIBRARY) +find_package_handle_standard_args(GFLAGS DEFAULT_MSG GFLAGS_INCLUDE_DIR GFLAGS_LIBRARY) if(GFLAGS_FOUND) set(GFLAGS_INCLUDE_DIRS ${GFLAGS_INCLUDE_DIR}) set(GFLAGS_LIBRARIES ${GFLAGS_LIBRARY}) + message(STATUS "Found gflags (include: ${GFLAGS_INCLUDE_DIR}, library: ${GFLAGS_LIBRARY})") + mark_as_advanced(GFLAGS_LIBRARY_DEBUG GFLAGS_LIBRARY_RELEASE + GFLAGS_LIBRARY GFLAGS_INCLUDE_DIR GFLAGS_ROOT_DIR) endif() diff --git a/CMakeScripts/FindGlog.cmake b/cmake/Modules/FindGlog.cmake similarity index 70% rename from CMakeScripts/FindGlog.cmake rename to cmake/Modules/FindGlog.cmake index 0dc30abdbf5..56c76434897 100644 --- a/CMakeScripts/FindGlog.cmake +++ b/cmake/Modules/FindGlog.cmake @@ -34,15 +34,15 @@ if(MSVC) else() find_library(GLOG_LIBRARY glog PATHS ${GLOG_ROOT_DIR} - PATH_SUFFIXES - lib - lib64) + PATH_SUFFIXES lib lib64) endif() -find_package_handle_standard_args(GLOG DEFAULT_MSG - GLOG_INCLUDE_DIR GLOG_LIBRARY) +find_package_handle_standard_args(GLOG DEFAULT_MSG GLOG_INCLUDE_DIR GLOG_LIBRARY) if(GLOG_FOUND) - set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR}) - set(GLOG_LIBRARIES ${GLOG_LIBRARY}) + set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR}) + set(GLOG_LIBRARIES ${GLOG_LIBRARY}) + message(STATUS "Found glog (include: ${GLOG_INCLUDE_DIR}, library: ${GLOG_LIBRARY})") + mark_as_advanced(GLOG_ROOT_DIR GLOG_LIBRARY_RELEASE GLOG_LIBRARY_DEBUG + GLOG_LIBRARY GLOG_INCLUDE_DIR) endif() diff --git a/CMakeScripts/FindLAPACK.cmake b/cmake/Modules/FindLAPACK.cmake similarity index 100% rename from CMakeScripts/FindLAPACK.cmake rename to cmake/Modules/FindLAPACK.cmake diff --git a/cmake/Modules/FindLMDB.cmake b/cmake/Modules/FindLMDB.cmake new file mode 100644 index 00000000000..8a817fd6f10 --- /dev/null +++ b/cmake/Modules/FindLMDB.cmake @@ -0,0 +1,28 @@ +# Try to find the LMBD libraries and headers +# LMDB_FOUND - system has LMDB lib +# LMDB_INCLUDE_DIR - the LMDB include directory +# LMDB_LIBRARIES - Libraries needed to use LMDB + +# FindCWD based on FindGMP by: +# Copyright (c) 2006, Laurent Montel, +# +# Redistribution and use is allowed according to the terms of the BSD license. + +# Adapted from FindCWD by: +# Copyright 2013 Conrad Steenberg +# Aug 31, 2013 + +find_path(LMDB_INCLUDE_DIR NAMES lmdb.h PATHS "$ENV{LMDB_DIR}/include") +find_library(LMDB_LIBRARIES NAMES lmdb PATHS "$ENV{LMDB_DIR}/lib" ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LMDB DEFAULT_MSG LMDB_INCLUDE_DIR LMDB_LIBRARIES) + +if(LMDB_FOUND) + message(STATUS "Found lmdb (include: ${LMDB_INCLUDE_DIR}, library: ${LMDB_LIBRARIES})") + mark_as_advanced(LMDB_INCLUDE_DIR LMDB_LIBRARIES) + + caffe_parse_header(${LMDB_INCLUDE_DIR}/lmdb.h + LMDB_VERSION_LINES MDB_VERSION_MAJOR MDB_VERSION_MINOR MDB_VERSION_PATCH) + set(LMDB_VERSION "${MDB_VERSION_MAJOR}.${MDB_VERSION_MINOR}.${MDB_VERSION_PATCH}") +endif() diff --git a/cmake/Modules/FindLevelDB.cmake b/cmake/Modules/FindLevelDB.cmake new file mode 100644 index 00000000000..97f08ac9349 --- /dev/null +++ b/cmake/Modules/FindLevelDB.cmake @@ -0,0 +1,44 @@ +# - Find LevelDB +# +# LevelDB_INCLUDES - List of LevelDB includes +# LevelDB_LIBRARIES - List of libraries when using LevelDB. +# LevelDB_FOUND - True if LevelDB found. + +# Look for the header file. +find_path(LevelDB_INCLUDE NAMES leveldb/db.h + PATHS $ENV{LEVELDB_ROOT}/include /opt/local/include /usr/local/include /usr/include + DOC "Path in which the file leveldb/db.h is located." ) + +# Look for the library. +find_library(LevelDB_LIBRARY NAMES leveldb + PATHS /usr/lib $ENV{LEVELDB_ROOT}/lib + DOC "Path to leveldb library." ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LevelDB DEFAULT_MSG LevelDB_INCLUDE LevelDB_LIBRARY) + +if(LEVELDB_FOUND) + message(STATUS "Found LevelDB (include: ${LevelDB_INCLUDE}, library: ${LevelDB_LIBRARY})") + set(LevelDB_INCLUDES ${LevelDB_INCLUDE}) + set(LevelDB_LIBRARIES ${LevelDB_LIBRARY}) + mark_as_advanced(LevelDB_INCLUDE LevelDB_LIBRARY) + + if(EXISTS "${LevelDB_INCLUDE}/leveldb/db.h") + file(STRINGS "${LevelDB_INCLUDE}/leveldb/db.h" __version_lines + REGEX "static const int k[^V]+Version[ \t]+=[ \t]+[0-9]+;") + + foreach(__line ${__version_lines}) + if(__line MATCHES "[^k]+kMajorVersion[ \t]+=[ \t]+([0-9]+);") + set(LEVELDB_VERSION_MAJOR ${CMAKE_MATCH_1}) + elseif(__line MATCHES "[^k]+kMinorVersion[ \t]+=[ \t]+([0-9]+);") + set(LEVELDB_VERSION_MINOR ${CMAKE_MATCH_1}) + endif() + endforeach() + + if(LEVELDB_VERSION_MAJOR AND LEVELDB_VERSION_MINOR) + set(LEVELDB_VERSION "${LEVELDB_VERSION_MAJOR}.${LEVELDB_VERSION_MINOR}") + endif() + + caffe_clear_vars(__line __version_lines) + endif() +endif() diff --git a/cmake/Modules/FindMKL.cmake b/cmake/Modules/FindMKL.cmake new file mode 100644 index 00000000000..d2012db579a --- /dev/null +++ b/cmake/Modules/FindMKL.cmake @@ -0,0 +1,110 @@ +# Find the MKL libraries +# +# Options: +# +# MKL_USE_SINGLE_DYNAMIC_LIBRARY : use single dynamic library interface +# MKL_USE_STATIC_LIBS : use static libraries +# MKL_MULTI_THREADED : use multi-threading +# +# This module defines the following variables: +# +# MKL_FOUND : True mkl is found +# MKL_INCLUDE_DIR : unclude directory +# MKL_LIBRARIES : the libraries to link against. + + +# ---[ Options +caffe_option(MKL_USE_SINGLE_DYNAMIC_LIBRARY "Use single dynamic library interface" ON) +caffe_option(MKL_USE_STATIC_LIBS "Use static libraries" OFF IF NOT MKL_USE_SINGLE_DYNAMIC_LIBRARY) +caffe_option(MKL_MULTI_THREADED "Use multi-threading" ON IF NOT MKL_USE_SINGLE_DYNAMIC_LIBRARY) + +# ---[ Root folders +set(INTEL_ROOT "/opt/intel" CACHE PATH "Folder contains intel libs") +find_path(MKL_ROOT include/mkl.h PATHS $ENV{MKL_ROOT} ${INTEL_ROOT}/mkl + DOC "Folder contains MKL") + +# ---[ Find include dir +find_path(MKL_INCLUDE_DIR mkl.h PATHS ${MKL_ROOT} PATH_SUFFIXES include) +set(__looked_for MKL_INCLUDE_DIR) + +# ---[ Find libraries +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(__path_suffixes lib lib/ia32) +else() + set(__path_suffixes lib lib/intel64) +endif() + +set(__mkl_libs "") +if(MKL_USE_SINGLE_DYNAMIC_LIBRARY) + list(APPEND __mkl_libs rt) +else() + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + if(WIN32) + list(APPEND __mkl_libs intel_c) + else() + list(APPEND __mkl_libs intel gf) + endif() + else() + list(APPEND __mkl_libs intel_lp64 gf_lp64) + endif() + + if(MKL_MULTI_THREADED) + list(APPEND __mkl_libs intel_thread) + else() + list(APPEND __mkl_libs sequential) + endif() + + list(APPEND __mkl_libs core cdft_core) +endif() + + +foreach (__lib ${__mkl_libs}) + set(__mkl_lib "mkl_${__lib}") + string(TOUPPER ${__mkl_lib} __mkl_lib_upper) + + if(MKL_USE_STATIC_LIBS) + set(__mkl_lib "lib${__mkl_lib}.a") + endif() + + find_library(${__mkl_lib_upper}_LIBRARY + NAMES ${__mkl_lib} + PATHS ${MKL_ROOT} "${MKL_INCLUDE_DIR}/.." + PATH_SUFFIXES ${__path_suffixes} + DOC "The path to Intel(R) MKL ${__mkl_lib} library") + mark_as_advanced(${__mkl_lib_upper}_LIBRARY) + + list(APPEND __looked_for ${__mkl_lib_upper}_LIBRARY) + list(APPEND MKL_LIBRARIES ${${__mkl_lib_upper}_LIBRARY}) +endforeach() + + +if(NOT MKL_USE_SINGLE_DYNAMIC_LIBRARY) + if (MKL_USE_STATIC_LIBS) + set(__iomp5_libs iomp5 libiomp5mt.lib) + else() + set(__iomp5_libs iomp5 libiomp5md.lib) + endif() + + if(WIN32) + find_path(INTEL_INCLUDE_DIR omp.h PATHS ${INTEL_ROOT} PATH_SUFFIXES include) + list(APPEND __looked_for INTEL_INCLUDE_DIR) + endif() + + find_library(MKL_RTL_LIBRARY ${__iomp5_libs} + PATHS ${INTEL_RTL_ROOT} ${INTEL_ROOT}/compiler ${MKL_ROOT}/.. ${MKL_ROOT}/../compiler + PATH_SUFFIXES ${__path_suffixes} + DOC "Path to Path to OpenMP runtime library") + + list(APPEND __looked_for MKL_RTL_LIBRARY) + list(APPEND MKL_LIBRARIES ${MKL_RTL_LIBRARY}) +endif() + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MKL DEFAULT_MSG ${__looked_for}) + +if(MKL_FOUND) + message(STATUS "Found MKL (include: ${MKL_INCLUDE_DIR}, lib: ${MKL_LIBRARIES}") +endif() + +caffe_clear_vars(__looked_for __mkl_libs __path_suffixes __lib_suffix __iomp5_libs) diff --git a/cmake/Modules/FindMatlabMex.cmake b/cmake/Modules/FindMatlabMex.cmake new file mode 100644 index 00000000000..28ae65e7cbb --- /dev/null +++ b/cmake/Modules/FindMatlabMex.cmake @@ -0,0 +1,48 @@ +# This module looks for MatlabMex compiler +# Defines variables: +# Matlab_DIR - Matlab root dir +# Matlab_mex - path to mex compiler +# Matlab_mexext - path to mexext + +if(MSVC) + foreach(__ver "9.30" "7.14" "7.11" "7.10" "7.9" "7.8" "7.7") + get_filename_component(__matlab_root "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${__ver};MATLABROOT]" ABSOLUTE) + if(__matlab_root) + break() + endif() + endforeach() +endif() + +if(APPLE) + foreach(__ver "R2014b" "R2014a" "R2013b" "R2013a" "R2012b" "R2012a" "R2011b" "R2011a" "R2010b" "R2010a") + if(EXISTS /Applications/MATLAB_${__ver}.app) + set(__matlab_root /Applications/MATLAB_${__ver}.app) + break() + endif() + endforeach() +endif() + +if(UNIX) + execute_process(COMMAND which matlab OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE __out RESULT_VARIABLE __res) + + if(__res MATCHES 0) # Suppress `readlink` warning if `which` returned nothing + execute_process(COMMAND which matlab COMMAND xargs readlink + COMMAND xargs dirname COMMAND xargs dirname COMMAND xargs echo -n + OUTPUT_VARIABLE __matlab_root OUTPUT_STRIP_TRAILING_WHITESPACE) + endif() +endif() + + +find_path(Matlab_DIR NAMES bin/mex bin/mexext PATHS ${__matlab_root} + DOC "Matlab directory" NO_DEFAULT_PATH) + +find_program(Matlab_mex NAMES mex mex.bat HINTS ${Matlab_DIR} PATH_SUFFIXES bin NO_DEFAULT_PATH) +find_program(Matlab_mexext NAMES mexext mexext.bat HINTS ${Matlab_DIR} PATH_SUFFIXES bin NO_DEFAULT_PATH) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MatlabMex DEFAULT_MSG Matlab_mex Matlab_mexext) + +if(MATLABMEX_FOUND) + mark_as_advanced(Matlab_mex Matlab_mexext) +endif() diff --git a/cmake/Modules/FindNumPy.cmake b/cmake/Modules/FindNumPy.cmake new file mode 100644 index 00000000000..a671494caba --- /dev/null +++ b/cmake/Modules/FindNumPy.cmake @@ -0,0 +1,58 @@ +# - Find the NumPy libraries +# This module finds if NumPy is installed, and sets the following variables +# indicating where it is. +# +# TODO: Update to provide the libraries and paths for linking npymath lib. +# +# NUMPY_FOUND - was NumPy found +# NUMPY_VERSION - the version of NumPy found as a string +# NUMPY_VERSION_MAJOR - the major version number of NumPy +# NUMPY_VERSION_MINOR - the minor version number of NumPy +# NUMPY_VERSION_PATCH - the patch version number of NumPy +# NUMPY_VERSION_DECIMAL - e.g. version 1.6.1 is 10601 +# NUMPY_INCLUDE_DIR - path to the NumPy include files + +unset(NUMPY_VERSION) +unset(NUMPY_INCLUDE_DIR) + +if(PYTHONINTERP_FOUND) + execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "import numpy as n; print(n.__version__); print(n.get_include());" + RESULT_VARIABLE __result + OUTPUT_VARIABLE __output + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(__result MATCHES 0) + string(REGEX REPLACE ";" "\\\\;" __values ${__output}) + string(REGEX REPLACE "\r?\n" ";" __values ${__values}) + list(GET __values 0 NUMPY_VERSION) + list(GET __values 1 NUMPY_INCLUDE_DIR) + + string(REGEX MATCH "^([0-9])+\\.([0-9])+\\.([0-9])+" __ver_check "${NUMPY_VERSION}") + if(NOT "${__ver_check}" STREQUAL "") + set(NUMPY_VERSION_MAJOR ${CMAKE_MATCH_1}) + set(NUMPY_VERSION_MINOR ${CMAKE_MATCH_2}) + set(NUMPY_VERSION_PATCH ${CMAKE_MATCH_3}) + math(EXPR NUMPY_VERSION_DECIMAL + "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}") + string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIR ${NUMPY_INCLUDE_DIR}) + else() + unset(NUMPY_VERSION) + unset(NUMPY_INCLUDE_DIR) + message(STATUS "Requested NumPy version and include path, but got instead:\n${__output}\n") + endif() + endif() +else() + message(STATUS "To find NumPy Python interpretator is required to be found.") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NumPy REQUIRED_VARS NUMPY_INCLUDE_DIR NUMPY_VERSION + VERSION_VAR NUMPY_VERSION) + +if(NUMPY_FOUND) + message(STATUS "NumPy ver. ${NUMPY_VERSION} found (include: ${NUMPY_INCLUDE_DIR})") +endif() + +caffe_clear_vars(__result __output __error_value __values __ver_check __error_value) + diff --git a/CMakeScripts/FindOpenBLAS.cmake b/cmake/Modules/FindOpenBLAS.cmake similarity index 100% rename from CMakeScripts/FindOpenBLAS.cmake rename to cmake/Modules/FindOpenBLAS.cmake diff --git a/cmake/Modules/FindSnappy.cmake b/cmake/Modules/FindSnappy.cmake new file mode 100644 index 00000000000..eff2a864a7b --- /dev/null +++ b/cmake/Modules/FindSnappy.cmake @@ -0,0 +1,28 @@ +# Find the Snappy libraries +# +# The following variables are optionally searched for defaults +# Snappy_ROOT_DIR: Base directory where all Snappy components are found +# +# The following are set after configuration is done: +# SNAPPY_FOUND +# Snappy_INCLUDE_DIR +# Snappy_LIBRARIES + +find_path(Snappy_INCLUDE_DIR NAMES snappy.h + PATHS ${SNAPPY_ROOT_DIR} ${SNAPPY_ROOT_DIR}/include) + +find_library(Snappy_LIBRARIES NAMES snappy + PATHS ${SNAPPY_ROOT_DIR} ${SNAPPY_ROOT_DIR}/lib) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Snappy DEFAULT_MSG Snappy_INCLUDE_DIR Snappy_LIBRARIES) + +if(SNAPPY_FOUND) + message(STATUS "Found Snappy (include: ${Snappy_INCLUDE_DIR}, library: ${Snappy_LIBRARIES})") + mark_as_advanced(Snappy_INCLUDE_DIR Snappy_LIBRARIES) + + caffe_parse_header(${Snappy_INCLUDE_DIR}/snappy-stubs-public.h + SNAPPY_VERION_LINES SNAPPY_MAJOR SNAPPY_MINOR SNAPPY_PATCHLEVEL) + set(Snappy_VERSION "${SNAPPY_MAJOR}.${SNAPPY_MINOR}.${SNAPPY_PATCHLEVEL}") +endif() + diff --git a/cmake/Modules/FindvecLib.cmake b/cmake/Modules/FindvecLib.cmake new file mode 100644 index 00000000000..9600da43647 --- /dev/null +++ b/cmake/Modules/FindvecLib.cmake @@ -0,0 +1,34 @@ +# Find the vecLib libraries as part of Accelerate.framework or as standalon framework +# +# The following are set after configuration is done: +# VECLIB_FOUND +# vecLib_INCLUDE_DIR +# vecLib_LINKER_LIBS + + +if(NOT APPLE) + return() +endif() + +set(__veclib_include_suffix "Frameworks/vecLib.framework/Versions/Current/Headers") + +find_path(vecLib_INCLUDE_DIR vecLib.h + DOC "vecLib include directory" + PATHS /System/Library/${__veclib_include_suffix} + /System/Library/Frameworks/Accelerate.framework/Versions/Current/${__veclib_include_suffix} + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/Headers/) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(vecLib DEFAULT_MSG vecLib_INCLUDE_DIR) + +if(VECLIB_FOUND) + if(vecLib_INCLUDE_DIR MATCHES "^/System/Library/Frameworks/vecLib.framework.*") + set(vecLib_LINKER_LIBS -lcblas "-framework vecLib") + message(STATUS "Found standalone vecLib.framework") + else() + set(vecLib_LINKER_LIBS -lcblas "-framework Accelerate") + message(STATUS "Found vecLib as part of Accelerate.framework") + endif() + + mark_as_advanced(vecLib_INCLUDE_DIR) +endif() diff --git a/cmake/ProtoBuf.cmake b/cmake/ProtoBuf.cmake new file mode 100644 index 00000000000..8946d66c57b --- /dev/null +++ b/cmake/ProtoBuf.cmake @@ -0,0 +1,90 @@ +# Finds Google Protocol Buffers library and compilers and extends +# the standart cmake script with version and python generation support + +find_package( Protobuf REQUIRED ) +include_directories(SYSTEM ${PROTOBUF_INCLUDE_DIR}) +list(APPEND Caffe_LINKER_LIBS ${PROTOBUF_LIBRARIES}) + +# As of Ubuntu 14.04 protoc is no longer a part of libprotobuf-dev package +# and should be installed separately as in: sudo apt-get install protobuf-compiler +if(EXISTS ${PROTOBUF_PROTOC_EXECUTABLE}) + message(STATUS "Found PROTOBUF Compiler: ${PROTOBUF_PROTOC_EXECUTABLE}") +else() + message(FATAL_ERROR "Could not find PROTOBUF Compiler") +endif() + +if(PROTOBUF_FOUND) + # fetches protobuf version + caffe_parse_header(${PROTOBUF_INCLUDE_DIR}/google/protobuf/stubs/common.h VERION_LINE GOOGLE_PROTOBUF_VERSION) + string(REGEX MATCH "([0-9])00([0-9])00([0-9])" PROTOBUF_VERSION ${GOOGLE_PROTOBUF_VERSION}) + set(PROTOBUF_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") + unset(GOOGLE_PROTOBUF_VERSION) +endif() + +# place where to generate protobuf sources +set(proto_gen_folder "${PROJECT_BINARY_DIR}/include/caffe/proto") +include_directories(SYSTEM "${PROJECT_BINARY_DIR}/include") + +set(PROTOBUF_GENERATE_CPP_APPEND_PATH TRUE) + +################################################################################################ +# Modification of standard 'protobuf_generate_cpp()' with output dir parameter and python support +# Usage: +# caffe_protobuf_generate_cpp_py( ) +function(caffe_protobuf_generate_cpp_py output_dir srcs_var hdrs_var python_var) + if(NOT ARGN) + message(SEND_ERROR "Error: caffe_protobuf_generate_cpp_py() called without any proto files") + return() + endif() + + if(PROTOBUF_GENERATE_CPP_APPEND_PATH) + # Create an include path for each file specified + foreach(fil ${ARGN}) + get_filename_component(abs_fil ${fil} ABSOLUTE) + get_filename_component(abs_path ${abs_fil} PATH) + list(FIND _protoc_include ${abs_path} _contains_already) + if(${_contains_already} EQUAL -1) + list(APPEND _protoc_include -I ${abs_path}) + endif() + endforeach() + else() + set(_protoc_include -I ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + if(DEFINED PROTOBUF_IMPORT_DIRS) + foreach(dir ${PROTOBUF_IMPORT_DIRS}) + get_filename_component(abs_path ${dir} ABSOLUTE) + list(FIND _protoc_include ${abs_path} _contains_already) + if(${_contains_already} EQUAL -1) + list(APPEND _protoc_include -I ${abs_path}) + endif() + endforeach() + endif() + + set(${srcs_var}) + set(${hdrs_var}) + set(${python_var}) + foreach(fil ${ARGN}) + get_filename_component(abs_fil ${fil} ABSOLUTE) + get_filename_component(fil_we ${fil} NAME_WE) + + list(APPEND ${srcs_var} "${output_dir}/${fil_we}.pb.cc") + list(APPEND ${hdrs_var} "${output_dir}/${fil_we}.pb.h") + list(APPEND ${python_var} "${output_dir}/${fil_we}_pb2.py") + + add_custom_command( + OUTPUT "${output_dir}/${fil_we}.pb.cc" + "${output_dir}/${fil_we}.pb.h" + "${output_dir}/${fil_we}_pb2.py" + COMMAND ${CMAKE_COMMAND} -E make_directory "${output_dir}" + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --cpp_out ${output_dir} ${_protoc_include} ${abs_fil} + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --python_out ${output_dir} ${_protoc_include} ${abs_fil} + DEPENDS ${abs_fil} + COMMENT "Running C++/Python protocol buffer compiler on ${fil}" VERBATIM ) + endforeach() + + set_source_files_properties(${${srcs_var}} ${${hdrs_var}} ${${python_var}} PROPERTIES GENERATED TRUE) + set(${srcs_var} ${${srcs_var}} PARENT_SCOPE) + set(${hdrs_var} ${${hdrs_var}} PARENT_SCOPE) + set(${python_var} ${${python_var}} PARENT_SCOPE) +endfunction() diff --git a/cmake/Summary.cmake b/cmake/Summary.cmake new file mode 100644 index 00000000000..3f7dff6b6e0 --- /dev/null +++ b/cmake/Summary.cmake @@ -0,0 +1,166 @@ +################################################################################################ +# Caffe status report function. +# Automatically align right column and selects text based on condition. +# Usage: +# caffe_status() +# caffe_status( [ ...]) +# caffe_status( THEN ELSE ) +function(caffe_status text) + set(status_cond) + set(status_then) + set(status_else) + + set(status_current_name "cond") + foreach(arg ${ARGN}) + if(arg STREQUAL "THEN") + set(status_current_name "then") + elseif(arg STREQUAL "ELSE") + set(status_current_name "else") + else() + list(APPEND status_${status_current_name} ${arg}) + endif() + endforeach() + + if(DEFINED status_cond) + set(status_placeholder_length 23) + string(RANDOM LENGTH ${status_placeholder_length} ALPHABET " " status_placeholder) + string(LENGTH "${text}" status_text_length) + if(status_text_length LESS status_placeholder_length) + string(SUBSTRING "${text}${status_placeholder}" 0 ${status_placeholder_length} status_text) + elseif(DEFINED status_then OR DEFINED status_else) + message(STATUS "${text}") + set(status_text "${status_placeholder}") + else() + set(status_text "${text}") + endif() + + if(DEFINED status_then OR DEFINED status_else) + if(${status_cond}) + string(REPLACE ";" " " status_then "${status_then}") + string(REGEX REPLACE "^[ \t]+" "" status_then "${status_then}") + message(STATUS "${status_text} ${status_then}") + else() + string(REPLACE ";" " " status_else "${status_else}") + string(REGEX REPLACE "^[ \t]+" "" status_else "${status_else}") + message(STATUS "${status_text} ${status_else}") + endif() + else() + string(REPLACE ";" " " status_cond "${status_cond}") + string(REGEX REPLACE "^[ \t]+" "" status_cond "${status_cond}") + message(STATUS "${status_text} ${status_cond}") + endif() + else() + message(STATUS "${text}") + endif() +endfunction() + + +################################################################################################ +# Function for fetching Caffe version from git and headers +# Usage: +# caffe_extract_caffe_version() +function(caffe_extract_caffe_version) + set(Caffe_GIT_VERSION "unknown") + find_package(Git) + if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + OUTPUT_VARIABLE Caffe_GIT_VERSION + RESULT_VARIABLE __git_result) + if(NOT ${__git_result} EQUAL 0) + set(Caffe_GIT_VERSION "unknown") + endif() + endif() + + set(Caffe_GIT_VERSION ${Caffe_GIT_VERSION} PARENT_SCOPE) + set(Caffe_VERSION " (Caffe doesn't declare its version in headers)" PARENT_SCOPE) + + # caffe_parse_header(${Caffe_INCLUDE_DIR}/caffe/version.hpp Caffe_VERSION_LINES CAFFE_MAJOR CAFFE_MINOR CAFFE_PATCH) + # set(Caffe_VERSION "${CAFFE_MAJOR}.${CAFFE_MINOR}.${CAFFE_PATCH}" PARENT_SCOPE) + + # or for #define Caffe_VERSION "x.x.x" + # caffe_parse_header_single_define(Caffe ${Caffe_INCLUDE_DIR}/caffe/version.hpp Caffe_VERSION) + # set(Caffe_VERSION ${Caffe_VERSION_STRING} PARENT_SCOPE) + +endfunction() + + +################################################################################################ +# Prints accumulatd caffe configuration summary +# Usage: +# caffe_print_configuration_summary() + +function(caffe_print_configuration_summary) + caffe_extract_caffe_version() + set(Caffe_VERSION ${Caffe_VERSION} PARENT_SCOPE) + + caffe_merge_flag_lists(__flags_rel CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS) + caffe_merge_flag_lists(__flags_deb CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS) + + caffe_status("") + caffe_status("******************* Caffe Configuration Summary *******************") + caffe_status("General:") + caffe_status(" Version : ${Caffe_VERSION}") + caffe_status(" Git : ${Caffe_GIT_VERSION}") + caffe_status(" System : ${CMAKE_SYSTEM_NAME}") + caffe_status(" C++ compiler : ${CMAKE_CXX_COMPILER}") + caffe_status(" Release CXX flags : ${__flags_rel}") + caffe_status(" Debug CXX flags : ${__flags_deb}") + caffe_status(" BUILD_SHARED_LIBS : ${BUILD_SHARED_LIBS}") + caffe_status(" Build type : ${CMAKE_BUILD_TYPE}") + caffe_status(" BUILD_python : ${BUILD_python}") + caffe_status(" BUILD_matlab : ${BUILD_matlab}") + caffe_status(" BUILD_docs : ${BUILD_docs}") + caffe_status(" CPU_ONLY : ${CPU_ONLY}") + caffe_status("") + caffe_status("Dependencies:") + caffe_status(" BLAS : " APPLE THEN "Yes (vecLib)" ELSE "Yes (${BLAS})") + caffe_status(" glog : Yes") + caffe_status(" gflags : Yes") + caffe_status(" protobuf : " PROTOBUF_FOUND THEN "Yes (ver. ${PROTOBUF_VERSION})" ELSE "No" ) + caffe_status(" lmdb : " LMDB_FOUND THEN "Yes (ver. ${LMDB_VERSION})" ELSE "No") + caffe_status(" Snappy : " SNAPPY_FOUND THEN "Yes (ver. ${Snappy_VERSION})" ELSE "No" ) + caffe_status(" LevelDB : " LEVELDB_FOUND THEN "Yes (ver. ${LEVELDB_VERSION})" ELSE "No") + caffe_status(" OpenCV : Yes (ver. ${OpenCV_VERSION})") + caffe_status(" CUDA : " HAVE_CUDA THEN "Yes (ver. ${CUDA_VERSION})" ELSE "No" ) + caffe_status("") + if(HAVE_CUDA) + caffe_status("NVIDIA CUDA:") + caffe_status(" Target GPU(s) : ${CUDA_ARCH_NAME}" ) + caffe_status(" GPU arch(s) : ${NVCC_FLAGS_EXTRA_readable}") + if(USE_CUDNN) + caffe_status(" cuDNN : " HAVE_CUDNN THEN "Yes" ELSE "Not found") + else() + caffe_status(" cuDNN : Disabled") + endif() + caffe_status("") + endif() + if(HAVE_PYTHON) + caffe_status("Python:") + caffe_status(" Interpreter :" PYTHON_EXECUTABLE THEN "${PYTHON_EXECUTABLE} (ver. ${PYTHON_VERSION_STRING})" ELSE "No") + caffe_status(" Libraries :" PYTHONLIBS_FOUND THEN "${PYTHON_LIBRARIES} (ver ${PYTHONLIBS_VERSION_STRING})" ELSE "No") + caffe_status(" NumPy :" NUMPY_FOUND THEN "${NUMPY_INCLUDE_DIR} (ver ${NUMPY_VERSION})" ELSE "No") + caffe_status("") + endif() + if(BUILD_matlab) + caffe_status("Matlab:") + caffe_status(" Matlab :" HAVE_MATLAB THEN "Yes (${Matlab_mex}, ${Matlab_mexext}" ELSE "No") + caffe_status(" Octave :" Octave_compiler THEN "Yes (${Octave_compiler})" ELSE "No") + if(HAVE_MATLAB AND Octave_compiler) + caffe_status(" Build mex using : ${Matlab_build_mex_using}") + endif() + caffe_status("") + endif() + if(BUILD_docs) + caffe_status("Documentaion:") + caffe_status(" Doxygen :" DOXYGEN_FOUND THEN "${DOXYGEN_EXECUTABLE} (${DOXYGEN_VERSION})" ELSE "No") + caffe_status(" config_file : ${DOXYGEN_config_file}") + + caffe_status("") + endif() + caffe_status("Install:") + caffe_status(" Install path : ${CMAKE_INSTALL_PREFIX}") + caffe_status("") +endfunction() + diff --git a/cmake/Targets.cmake b/cmake/Targets.cmake new file mode 100644 index 00000000000..e3ad872313b --- /dev/null +++ b/cmake/Targets.cmake @@ -0,0 +1,169 @@ +################################################################################################ +# Defines global Caffe_LINK flag, This flag is required to prevent linker from excluding +# some objects which are not addressed directly but are registered via static constructors +if(BUILD_SHARED_LIBS) + set(Caffe_LINK caffe) +else() + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(Caffe_LINK -Wl,-force_load caffe) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(Caffe_LINK -Wl,--whole-archive caffe -Wl,--no-whole-archive) + endif() +endif() + +################################################################################################ +# Convenient command to setup source group for IDEs that support this feature (VS, XCode) +# Usage: +# caffe_source_group( GLOB[_RECURSE] ) +function(caffe_source_group group) + cmake_parse_arguments(CAFFE_SOURCE_GROUP "" "" "GLOB;GLOB_RECURSE" ${ARGN}) + if(CAFFE_SOURCE_GROUP_GLOB) + file(GLOB srcs1 ${CAFFE_SOURCE_GROUP_GLOB}) + source_group(${group} FILES ${srcs1}) + endif() + + if(CAFFE_SOURCE_GROUP_GLOB_RECURSE) + file(GLOB_RECURSE srcs2 ${CAFFE_SOURCE_GROUP_GLOB_RECURSE}) + source_group(${group} FILES ${srcs2}) + endif() +endfunction() + +################################################################################################ +# Collecting sources from globbing and appending to output list variable +# Usage: +# caffe_source_group( GLOB[_RECURSE] ) +function(caffe_collect_sources variable) + cmake_parse_arguments(CAFFE_COLLECT_SOURCES "" "" "GLOB;GLOB_RECURSE" ${ARGN}) + if(CAFFE_COLLECT_SOURCES_GLOB) + file(GLOB srcs1 ${CAFFE_COLLECT_SOURCES_GLOB}) + set(${variable} ${variable} ${srcs1}) + endif() + + if(CAFFE_COLLECT_SOURCES_GLOB_RECURSE) + file(GLOB_RECURSE srcs2 ${CAFFE_COLLECT_SOURCES_GLOB_RECURSE}) + set(${variable} ${variable} ${srcs2}) + endif() +endfunction() + +################################################################################################ +# Short command getting caffe sources (assuming standard Caffe code tree) +# Usage: +# caffe_pickup_caffe_sources() +function(caffe_pickup_caffe_sources root) + # put all files in source groups (visible as subfolder in many IDEs) + caffe_source_group("Include" GLOB "${root}/include/caffe/*.h*") + caffe_source_group("Include\\Util" GLOB "${root}/include/caffe/util/*.h*") + caffe_source_group("Include" GLOB "${PROJECT_BINARY_DIR}/caffe_config.h*") + caffe_source_group("Source" GLOB "${root}/src/caffe/*.cpp") + caffe_source_group("Source\\Util" GLOB "${root}/src/caffe/util/*.cpp") + caffe_source_group("Source\\Layers" GLOB "${root}/src/caffe/layers/*.cpp") + caffe_source_group("Source\\Cuda" GLOB "${root}/src/caffe/layers/*.cu") + caffe_source_group("Source\\Cuda" GLOB "${root}/src/caffe/util/*.cu") + caffe_source_group("Source\\Proto" GLOB "${root}/src/caffe/proto/*.proto") + + # source groups for test target + caffe_source_group("Include" GLOB "${root}/include/caffe/test/test_*.h*") + caffe_source_group("Source" GLOB "${root}/src/caffe/test/test_*.cpp") + caffe_source_group("Source\\Cuda" GLOB "${root}/src/caffe/test/test_*.cu") + + # collect files + file(GLOB test_hdrs ${root}/include/caffe/test/test_*.h*) + file(GLOB test_srcs ${root}/src/caffe/test/test_*.cpp) + file(GLOB_RECURSE hdrs ${root}/include/caffe/*.h*) + file(GLOB_RECURSE srcs ${root}/src/caffe/*.cpp) + list(REMOVE_ITEM hdrs ${test_hdrs}) + list(REMOVE_ITEM srcs ${test_srcs}) + + # adding headers to make the visible in some IDEs (Qt, VS, Xcode) + list(APPEND srcs ${hdrs} ${PROJECT_BINARY_DIR}/caffe_config.h) + list(APPEND test_srcs ${test_hdrs}) + + # collect cuda files + file(GLOB test_cuda ${root}/src/caffe/test/test_*.cu) + file(GLOB_RECURSE cuda ${root}/src/caffe/*.cu) + list(REMOVE_ITEM cuda ${test_cuda}) + + # add proto to make them editable in IDEs too + file(GLOB_RECURSE proto_files ${root}/src/caffe/*.proto) + list(APPEND srcs ${proto_files}) + + # convet to absolute paths + caffe_convert_absolute_paths(srcs) + caffe_convert_absolute_paths(cuda) + caffe_convert_absolute_paths(test_srcs) + caffe_convert_absolute_paths(test_cuda) + + # propogate to parent scope + set(srcs ${srcs} PARENT_SCOPE) + set(cuda ${cuda} PARENT_SCOPE) + set(test_srcs ${test_srcs} PARENT_SCOPE) + set(test_cuda ${test_cuda} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Short command for setting defeault target properties +# Usage: +# caffe_default_properties() +function(caffe_default_properties target) + set_target_properties(${target} PROPERTIES + DEBUG_POSTFIX ${Caffe_DEBUG_POSTFIX} + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") +endfunction() + +################################################################################################ +# Short command for setting runtime directory for build target +# Usage: +# caffe_set_runtime_directory( ) +function(caffe_set_runtime_directory target dir) + set_target_properties(${target} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${dir}") +endfunction() + +################################################################################################ +# Short command for setting solution folder property for target +# Usage: +# caffe_set_solution_folder( ) +function(caffe_set_solution_folder target folder) + if(USE_PROJECT_FOLDERS) + set_target_properties(${target} PROPERTIES FOLDER "${folder}") + endif() +endfunction() + +################################################################################################ +# Reads lines from input file, prepends source directory to each line and writes to output file +# Usage: +# caffe_configure_testdatafile() +function(caffe_configure_testdatafile file) + file(STRINGS ${file} __lines) + set(result "") + foreach(line ${__lines}) + set(result "${result}${PROJECT_SOURCE_DIR}/${line}\n") + endforeach() + file(WRITE ${file}.gen.cmake ${result}) +endfunction() + +################################################################################################ +# Filter outs all files that are not inlcuded in selected list +# Usage: +# caffe_leave_only_selected_tests( ) +function(caffe_leave_only_selected_tests file_list) + if(NOT ARGN) + return() # blank list means leave all + endif() + string(REPLACE "," ";" __selected ${ARGN}) + list(APPEND __selected caffe_main) + + set(result "") + foreach(f ${${file_list}}) + get_filename_component(name ${f} NAME_WE) + string(REGEX REPLACE "^test_" "" name ${name}) + list(FIND __selected ${name} __index) + if(NOT __index EQUAL -1) + list(APPEND result ${f}) + endif() + endforeach() + set(${file_list} ${result} PARENT_SCOPE) +endfunction() + diff --git a/cmake/Templates/CaffeConfig.cmake.in b/cmake/Templates/CaffeConfig.cmake.in new file mode 100644 index 00000000000..a4b03d961e0 --- /dev/null +++ b/cmake/Templates/CaffeConfig.cmake.in @@ -0,0 +1,58 @@ +# Config file for the Caffe package. +# +# Note: +# Caffe and this config file depends on opencv, +# so put `find_package(OpenCV)` before searching Caffe +# via `find_package(Caffe)`. All other lib/includes +# dependencies are hard coded int the file +# +# After successful configuration the following variables +# will be defined: +# +# Caffe_INCLUDE_DIRS - Caffe include directories +# Caffe_LIBRARIES - libraries to link against +# Caffe_DEFINITIONS - a list of definitions to pass to compiler +# +# Caffe_HAVE_CUDA - signals about CUDA support +# Caffe_HAVE_CUDNN - signals about cuDNN support + + +# OpenCV dependency + +if(NOT OpenCV_FOUND) + set(Caffe_OpenCV_CONFIG_PATH "@OpenCV_CONFIG_PATH@") + if(Caffe_OpenCV_CONFIG_PATH) + get_filename_component(Caffe_OpenCV_CONFIG_PATH ${Caffe_OpenCV_CONFIG_PATH} ABSOLUTE) + + if(EXISTS ${Caffe_OpenCV_CONFIG_PATH} AND NOT TARGET opencv_core) + message(STATUS "Caffe: using OpenCV config from ${Caffe_OpenCV_CONFIG_PATH}") + include(${Caffe_OpenCV_CONFIG_PATH}/OpenCVModules.cmake) + endif() + + else() + find_package(OpenCV REQUIRED) + endif() + unset(Caffe_OpenCV_CONFIG_PATH) +endif() + +# Compute paths +get_filename_component(Caffe_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +set(Caffe_INCLUDE_DIRS "@Caffe_INCLUDE_DIRS@") + +@Caffe_INSTALL_INCLUDE_DIR_APPEND_COMMAND@ + +# Our library dependencies +if(NOT TARGET caffe AND NOT caffe_BINARY_DIR) + include("${Caffe_CMAKE_DIR}/CaffeTargets.cmake") +endif() + +# List of IMPORTED libs created by CaffeTargets.cmake +set(Caffe_LIBRARIES caffe) + +# Definitions +set(Caffe_DEFINITIONS "@Caffe_DEFINITIONS@") + +# Cuda support variables +set(Caffe_CPU_ONLY @CPU_ONLY@) +set(Caffe_HAVE_CUDA @HAVE_CUDA@) +set(Caffe_HAVE_CUDNN @HAVE_CUDNN@) diff --git a/cmake/Templates/CaffeConfigVersion.cmake.in b/cmake/Templates/CaffeConfigVersion.cmake.in new file mode 100644 index 00000000000..cbfa514f1a6 --- /dev/null +++ b/cmake/Templates/CaffeConfigVersion.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION "@Caffe_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/cmake/Templates/caffe_config.h.in b/cmake/Templates/caffe_config.h.in new file mode 100644 index 00000000000..6039e8f6b21 --- /dev/null +++ b/cmake/Templates/caffe_config.h.in @@ -0,0 +1,32 @@ +/* Sources directory */ +#define SOURCE_FOLDER "${PROJECT_SOURCE_DIR}" + +/* Binaries directory */ +#define BINARY_FOLDER "${PROJECT_BINARY_DIR}" + +/* NVIDA Cuda */ +#cmakedefine HAVE_CUDA + +/* NVIDA cuDNN */ +#cmakedefine HAVE_CUDNN +#cmakedefine USE_CUDNN + +/* NVIDA cuDNN */ +#cmakedefine CPU_ONLY + +/* Test device */ +#define CUDA_TEST_DEVICE ${CUDA_TEST_DEVICE} + +/* Temporary (TODO: remove) */ +#if 1 + #define CMAKE_SOURCE_DIR SOURCE_FOLDER "/src/" + #define EXAMPLES_SOURCE_DIR BINARY_FOLDER "/examples/" + #define CMAKE_EXT ".gen.cmake" +#else + #define CMAKE_SOURCE_DIR "src/" + #define EXAMPLES_SOURCE_DIR "examples/" + #define CMAKE_EXT "" +#endif + +/* Matlab */ +#cmakedefine HAVE_MATLAB diff --git a/cmake/Utils.cmake b/cmake/Utils.cmake new file mode 100644 index 00000000000..a56c7c300c0 --- /dev/null +++ b/cmake/Utils.cmake @@ -0,0 +1,381 @@ +################################################################################################ +# Command alias for debugging messages +# Usage: +# dmgs() +function(dmsg) + message(STATUS ${ARGN}) +endfunction() + +################################################################################################ +# Removes duplicates from list(s) +# Usage: +# caffe_list_unique( [] [...]) +macro(caffe_list_unique) + foreach(__lst ${ARGN}) + if(${__lst}) + list(REMOVE_DUPLICATES ${__lst}) + endif() + endforeach() +endmacro() + +################################################################################################ +# Clears variables from lsit +# Usage: +# caffe_list_unique() +macro(caffe_clear_vars) + foreach(_var ${ARGN}) + unset(${_var}) + endforeach() +endmacro() + +################################################################################################ +# Removes duplicates from string +# Usage: +# caffe_string_unique() +function(caffe_string_unique __string) + if(${__string}) + set(__list ${${__string}}) + separate_arguments(__list) + list(REMOVE_DUPLICATES __list) + foreach(__e ${__list}) + set(__str "${__str} ${__e}") + endforeach() + set(${__string} ${__str} PARENT_SCOPE) + endif() +endfunction() + +################################################################################################ +# Prints list element per line +# Usage: +# caffe_print_list() +function(caffe_print_list) + foreach(e ${ARGN}) + message(STATUS ${e}) + endforeach() +endfunction() + +################################################################################################ +# Function merging lists of compiler flags to single string. +# Usage: +# caffe_merge_flag_lists(out_variable [] [] ...) +function(caffe_merge_flag_lists out_var) + set(__result "") + foreach(__list ${ARGN}) + foreach(__flag ${${__list}}) + string(STRIP ${__flag} __flag) + set(__result "${__result} ${__flag}") + endforeach() + endforeach() + string(STRIP ${__result} __result) + set(${out_var} ${__result} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Converts all paths in list to absolute +# Usage: +# caffe_convert_absolute_paths() +function(caffe_convert_absolute_paths variable) + set(__dlist "") + foreach(__s ${${variable}}) + get_filename_component(__abspath ${__s} ABSOLUTE) + list(APPEND __list ${__abspath}) + endforeach() + set(${variable} ${__list} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Reads set of version defines from the header file +# Usage: +# caffe_parse_header( ..) +macro(caffe_parse_header FILENAME FILE_VAR) + set(vars_regex "") + set(__parnet_scope OFF) + set(__add_cache OFF) + foreach(name ${ARGN}) + if("${name}" STREQUAL "PARENT_SCOPE") + set(__parnet_scope ON) + elseif("${name}" STREQUAL "CACHE") + set(__add_cache ON) + elseif(vars_regex) + set(vars_regex "${vars_regex}|${name}") + else() + set(vars_regex "${name}") + endif() + endforeach() + if(EXISTS "${FILENAME}") + file(STRINGS "${FILENAME}" ${FILE_VAR} REGEX "#define[ \t]+(${vars_regex})[ \t]+[0-9]+" ) + else() + unset(${FILE_VAR}) + endif() + foreach(name ${ARGN}) + if(NOT "${name}" STREQUAL "PARENT_SCOPE" AND NOT "${name}" STREQUAL "CACHE") + if(${FILE_VAR}) + if(${FILE_VAR} MATCHES ".+[ \t]${name}[ \t]+([0-9]+).*") + string(REGEX REPLACE ".+[ \t]${name}[ \t]+([0-9]+).*" "\\1" ${name} "${${FILE_VAR}}") + else() + set(${name} "") + endif() + if(__add_cache) + set(${name} ${${name}} CACHE INTERNAL "${name} parsed from ${FILENAME}" FORCE) + elseif(__parnet_scope) + set(${name} "${${name}}" PARENT_SCOPE) + endif() + else() + unset(${name} CACHE) + endif() + endif() + endforeach() +endmacro() + +################################################################################################ +# Reads single version define from the header file and parses it +# Usage: +# caffe_parse_header_single_define( ) +function(caffe_parse_header_single_define LIBNAME HDR_PATH VARNAME) + set(${LIBNAME}_H "") + if(EXISTS "${HDR_PATH}") + file(STRINGS "${HDR_PATH}" ${LIBNAME}_H REGEX "^#define[ \t]+${VARNAME}[ \t]+\"[^\"]*\".*$" LIMIT_COUNT 1) + endif() + + if(${LIBNAME}_H) + string(REGEX REPLACE "^.*[ \t]${VARNAME}[ \t]+\"([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_MAJOR "${${LIBNAME}_H}") + string(REGEX REPLACE "^.*[ \t]${VARNAME}[ \t]+\"[0-9]+\\.([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_MINOR "${${LIBNAME}_H}") + string(REGEX REPLACE "^.*[ \t]${VARNAME}[ \t]+\"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" ${LIBNAME}_VERSION_PATCH "${${LIBNAME}_H}") + set(${LIBNAME}_VERSION_MAJOR ${${LIBNAME}_VERSION_MAJOR} ${ARGN} PARENT_SCOPE) + set(${LIBNAME}_VERSION_MINOR ${${LIBNAME}_VERSION_MINOR} ${ARGN} PARENT_SCOPE) + set(${LIBNAME}_VERSION_PATCH ${${LIBNAME}_VERSION_PATCH} ${ARGN} PARENT_SCOPE) + set(${LIBNAME}_VERSION_STRING "${${LIBNAME}_VERSION_MAJOR}.${${LIBNAME}_VERSION_MINOR}.${${LIBNAME}_VERSION_PATCH}" PARENT_SCOPE) + + # append a TWEAK version if it exists: + set(${LIBNAME}_VERSION_TWEAK "") + if("${${LIBNAME}_H}" MATCHES "^.*[ \t]${VARNAME}[ \t]+\"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+).*$") + set(${LIBNAME}_VERSION_TWEAK "${CMAKE_MATCH_1}" ${ARGN} PARENT_SCOPE) + endif() + if(${LIBNAME}_VERSION_TWEAK) + set(${LIBNAME}_VERSION_STRING "${${LIBNAME}_VERSION_STRING}.${${LIBNAME}_VERSION_TWEAK}" ${ARGN} PARENT_SCOPE) + else() + set(${LIBNAME}_VERSION_STRING "${${LIBNAME}_VERSION_STRING}" ${ARGN} PARENT_SCOPE) + endif() + endif() +endfunction() + +######################################################################################################## +# An option that the user can select. Can accept condition to control when option is available for user. +# Usage: +# caffe_option( "doc string" [IF ]) +function(caffe_option variable description value) + set(__value ${value}) + set(__condition "") + set(__varname "__value") + foreach(arg ${ARGN}) + if(arg STREQUAL "IF" OR arg STREQUAL "if") + set(__varname "__condition") + else() + list(APPEND ${__varname} ${arg}) + endif() + endforeach() + unset(__varname) + if("${__condition}" STREQUAL "") + set(__condition 2 GREATER 1) + endif() + + if(${__condition}) + if("${__value}" MATCHES ";") + if(${__value}) + option(${variable} "${description}" ON) + else() + option(${variable} "${description}" OFF) + endif() + elseif(DEFINED ${__value}) + if(${__value}) + option(${variable} "${description}" ON) + else() + option(${variable} "${description}" OFF) + endif() + else() + option(${variable} "${description}" ${__value}) + endif() + else() + unset(${variable} CACHE) + endif() +endfunction() + +################################################################################################ +# Utility macro for comparing two lists. Used for CMake debugging purposes +# Usage: +# caffe_compare_lists( [description]) +function(caffe_compare_lists list1 list2 desc) + set(__list1 ${${list1}}) + set(__list2 ${${list2}}) + list(SORT __list1) + list(SORT __list2) + list(LENGTH __list1 __len1) + list(LENGTH __list2 __len2) + + if(NOT ${__len1} EQUAL ${__len2}) + message(FATAL_ERROR "Lists are not equal. ${__len1} != ${__len2}. ${desc}") + endif() + + foreach(__i RANGE 1 ${__len1}) + math(EXPR __index "${__i}- 1") + list(GET __list1 ${__index} __item1) + list(GET __list2 ${__index} __item2) + if(NOT ${__item1} STREQUAL ${__item2}) + message(FATAL_ERROR "Lists are not equal. Differ at element ${__index}. ${desc}") + endif() + endforeach() +endfunction() + +################################################################################################ +# Command for disabling warnings for different platforms (see below for gcc and VisualStudio) +# Usage: +# caffe_warnings_disable( -Wshadow /wd4996 ..,) +macro(caffe_warnings_disable) + set(_flag_vars "") + set(_msvc_warnings "") + set(_gxx_warnings "") + + foreach(arg ${ARGN}) + if(arg MATCHES "^CMAKE_") + list(APPEND _flag_vars ${arg}) + elseif(arg MATCHES "^/wd") + list(APPEND _msvc_warnings ${arg}) + elseif(arg MATCHES "^-W") + list(APPEND _gxx_warnings ${arg}) + endif() + endforeach() + + if(NOT _flag_vars) + set(_flag_vars CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + + if(MSVC AND _msvc_warnings) + foreach(var ${_flag_vars}) + foreach(warning ${_msvc_warnings}) + set(${var} "${${var}} ${warning}") + endforeach() + endforeach() + elseif((CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX) AND _gxx_warnings) + foreach(var ${_flag_vars}) + foreach(warning ${_gxx_warnings}) + if(NOT warning MATCHES "^-Wno-") + string(REPLACE "${warning}" "" ${var} "${${var}}") + string(REPLACE "-W" "-Wno-" warning "${warning}") + endif() + set(${var} "${${var}} ${warning}") + endforeach() + endforeach() + endif() + caffe_clear_vars(_flag_vars _msvc_warnings _gxx_warnings) +endmacro() + +################################################################################################ +# Helper function get current definitions +# Usage: +# caffe_get_current_definitions() +function(caffe_get_current_definitions definitions_var) + get_property(current_definitions DIRECTORY PROPERTY COMPILE_DEFINITIONS) + set(result "") + + foreach(d ${current_definitions}) + list(APPEND result -D${d}) + endforeach() + + caffe_list_unique(result) + set(${definitions_var} ${result} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Helper function get current includes/definitions +# Usage: +# caffe_get_current_cflags() +function(caffe_get_current_cflags cflags_var) + get_property(current_includes DIRECTORY PROPERTY INCLUDE_DIRECTORIES) + caffe_convert_absolute_paths(current_includes) + caffe_get_current_definitions(cflags) + + foreach(i ${current_includes}) + list(APPEND cflags "-I${i}") + endforeach() + + caffe_list_unique(cflags) + set(${cflags_var} ${cflags} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Helper function to parse current linker libs into link directoris, libflags and osx frameworks +# Usage: +# caffe_parse_linker_libs( ) +function(caffe_parse_linker_libs Caffe_LINKER_LIBS_variable folders_var flags_var frameworks_var) + + set(__unspec "") + set(__debug "") + set(__optimized "") + set(__framework "") + set(__varname "__unspec") + + # split libs into debug, optimized, unspecified and frameworks + foreach(list_elem ${${Caffe_LINKER_LIBS_variable}}) + if(list_elem STREQUAL "debug") + set(__varname "__debug") + elseif(list_elem STREQUAL "optimized") + set(__varname "__optimized") + elseif(list_elem MATCHES "^-framework[ \t]+([^ \t].*)") + list(APPEND __framework -framework ${CMAKE_MATCH_1}) + else() + list(APPEND ${__varname} ${list_elem}) + set(__varname "__unspec") + endif() + endforeach() + + # attach debug or optimized libs to unspecified according to current configuration + if(CMAKE_BUILD_TYPE MATCHES "Debug") + set(__libs ${__unspec} ${__debug}) + else() + set(__libs ${__unspec} ${__optimized}) + endif() + + set(libflags "") + set(folders "") + + # convert linker libraries list to link flags + foreach(lib ${__libs}) + if(TARGET ${lib}) + list(APPEND folders $) + list(APPEND libflags -l${lib}) + elseif(lib MATCHES "^-l.*") + list(APPEND libflags ${lib}) + elseif(IS_ABSOLUTE ${lib}) + get_filename_component(name_we ${lib} NAME_WE) + get_filename_component(folder ${lib} PATH) + + string(REGEX MATCH "^lib(.*)" __match ${name_we}) + list(APPEND libflags -l${CMAKE_MATCH_1}) + list(APPEND folders ${folder}) + else() + message(FATAL_ERROR "Logic error. Need to update cmake script") + endif() + endforeach() + + caffe_list_unique(libflags folders) + + set(${folders_var} ${folders} PARENT_SCOPE) + set(${flags_var} ${libflags} PARENT_SCOPE) + set(${frameworks_var} ${__framework} PARENT_SCOPE) +endfunction() + +################################################################################################ +# Helper function to detect Darwin version, i.e. 10.8, 10.9, 10.10, .... +# Usage: +# caffe_detect_darwin_version() +function(caffe_detect_darwin_version output_var) + if(APPLE) + execute_process(COMMAND /usr/bin/sw_vers -productVersion + RESULT_VARIABLE __sw_vers OUTPUT_VARIABLE __sw_vers_out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(${output_var} ${__sw_vers_out} PARENT_SCOPE) + else() + set(${output_var} "" PARENT_SCOPE) + endif() +endfunction() diff --git a/CMakeScripts/lint.cmake b/cmake/lint.cmake similarity index 94% rename from CMakeScripts/lint.cmake rename to cmake/lint.cmake index 04df3409e84..585babb3587 100644 --- a/CMakeScripts/lint.cmake +++ b/cmake/lint.cmake @@ -1,10 +1,12 @@ -set(CMAKE_SOURCE_DIR ../) +set(CMAKE_SOURCE_DIR ..) set(LINT_COMMAND ${CMAKE_SOURCE_DIR}/scripts/cpp_lint.py) set(SRC_FILE_EXTENSIONS h hpp hu c cpp cu cc) set(EXCLUDE_FILE_EXTENSTIONS pb.h pb.cc) set(LINT_DIRS include src/caffe examples tools python matlab) +cmake_policy(SET CMP0009 NEW) # supress cmake warning + # find all files of interest foreach(ext ${SRC_FILE_EXTENSIONS}) foreach(dir ${LINT_DIRS}) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 00000000000..ae47e461736 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,106 @@ +# Building docs script +# Requirements: +# sudo apt-get install doxygen texlive ruby-dev +# sudo gem install jekyll execjs therubyracer + +if(NOT BUILD_docs OR NOT DOXYGEN_FOUND) + return() +endif() + +################################################################################################# +# Gather docs from /examples/**/readme.md +function(gather_readmes_as_prebuild_cmd target gathered_dir root) + set(full_gathered_dir ${root}/${gathered_dir}) + + file(GLOB_RECURSE readmes ${root}/examples/readme.md ${root}/examples/README.md) + foreach(file ${readmes}) + # Only use file if it is to be included in docs. + file(STRINGS ${file} file_lines REGEX "include_in_docs: true") + + if(file_lines) + # Since everything is called readme.md, rename it by its dirname. + file(RELATIVE_PATH file ${root} ${file}) + get_filename_component(folder ${file} PATH) + set(new_filename ${full_gathered_dir}/${folder}.md) + + # folder value might be like /readme.md. That's why make directory. + get_filename_component(new_folder ${new_filename} PATH) + add_custom_command(TARGET ${target} PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${new_folder} + COMMAND ln -sf ${root}/${file} ${new_filename} + COMMENT "Creating symlink ${new_filename} -> ${root}/${file}" + WORKING_DIRECTORY ${root} VERBATIM) + endif() + endforeach() +endfunction() + +################################################################################################ +# Gather docs from examples/*.ipynb and add YAML front-matter. +function(gather_notebooks_as_prebuild_cmd target gathered_dir root) + set(full_gathered_dir ${root}/${gathered_dir}) + + if(NOT PYTHON_EXECUTABLE) + message(STATUS "Python interpeter is not found. Can't include *.ipynb files in docs. Skipping...") + return() + endif() + + file(GLOB_RECURSE notebooks ${root}/examples/*.ipynb) + foreach(file ${notebooks}) + file(RELATIVE_PATH file ${root} ${file}) + set(new_filename ${full_gathered_dir}/${file}) + + get_filename_component(new_folder ${new_filename} PATH) + add_custom_command(TARGET ${target} PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${new_folder} + COMMAND ${PYTHON_EXECUTABLE} scripts/copy_notebook.py ${file} ${new_filename} + COMMENT "Copying notebook ${file} to ${new_filename}" + WORKING_DIRECTORY ${root} VERBATIM) + endforeach() + + set(${outputs_var} ${outputs} PARENT_SCOPE) +endfunction() + +################################################################################################ +########################## [ Non macro part ] ################################################## + +# Gathering is done at each 'make doc' +file(REMOVE_RECURSE ${PROJECT_SOURCE_DIR}/docs/gathered) + +# Doxygen config file path +set(DOXYGEN_config_file ${PROJECT_SOURCE_DIR}/.Doxyfile CACHE FILEPATH "Doxygen config file") + +# Adding docs target +add_custom_target(docs COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_config_file} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Launching doxygen..." VERBATIM) + +# Gathering examples into docs subfolder +gather_notebooks_as_prebuild_cmd(docs docs/gathered ${PROJECT_SOURCE_DIR}) +gather_readmes_as_prebuild_cmd(docs docs/gathered ${PROJECT_SOURCE_DIR}) + +# Auto detect output directory +file(STRINGS ${DOXYGEN_config_file} config_line REGEX "OUTPUT_DIRECTORY[ \t]+=[^=].*") +if(config_line) + string(REGEX MATCH "OUTPUT_DIRECTORY[ \t]+=([^=].*)" __ver_check "${config_line}") + string(STRIP ${CMAKE_MATCH_1} output_dir) + message(STATUS "Detected Doxygen OUTPUT_DIRECTORY: ${output_dir}") +else() + set(output_dir ./doxygen/) + message(STATUS "Can't find OUTPUT_DIRECTORY in doxygen config file. Try to use default: ${output_dir}") +endif() + +if(NOT IS_ABSOLUTE ${output_dir}) + set(output_dir ${PROJECT_SOURCE_DIR}/${output_dir}) + get_filename_component(output_dir ${output_dir} ABSOLUTE) +endif() + +# creates symlink in docs subfolder to code documentation built by doxygen +add_custom_command(TARGET docs POST_BUILD VERBATIM + COMMAND ln -sfn "${output_dir}/html" doxygen + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/docs + COMMENT "Creating symlink ${PROJECT_SOURCE_DIR}/docs/doxygen -> ${output_dir}/html") + +# for quick launch of jekyll +add_custom_target(jekyll COMMAND jekyll serve -w -s . -d _site --port=4000 + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/docs + COMMENT "Launching jekyll..." VERBATIM) diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index 73c6d5873d3..b8efe60bc3b 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -11,6 +11,8 @@ Caffe {% if page contains 'title' %}| {{ page.title }}{% endif %} + + @@ -34,8 +36,16 @@

Caffe

- Deep learning framework developed by Yangqing Jia / BVLC + Deep learning framework by the BVLC

+

+ Created by +
+ Yangqing Jia +
+ Lead Developer +
+ Evan Shelhamer

  • View On GitHub diff --git a/docs/development.md b/docs/development.md index dfed3308eeb..fe54864bd35 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,10 +1,10 @@ --- title: Developing and Contributing --- -# Development +# Development and Contributing Caffe is developed with active participation of the community.
    -The [BVLC](http://bvlc.eecs.berkeley.edu/) maintainers welcome all contributions! +The [BVLC](http://bvlc.eecs.berkeley.edu/) brewers welcome all contributions! The exact details of contributions are recorded by versioning and cited in our [acknowledgements](http://caffe.berkeleyvision.org/#acknowledgements). This method is impartial and always up-to-date. @@ -21,7 +21,7 @@ If a contributor wants to further mark their specific copyright on a particular ### Documentation -This website, written with [Jekyll](http://jekyllrb.com/), functions as the official Caffe documentation -- simply run `scripts/build_docs.sh` and view the website at `http://0.0.0.0:4000`. +This website, written with [Jekyll](http://jekyllrb.com/), acts as the official Caffe documentation -- simply run `scripts/build_docs.sh` and view the website at `http://0.0.0.0:4000`. We prefer tutorials and examples to be documented close to where they live, in `readme.md` files. The `build_docs.sh` script gathers all `examples/**/readme.md` and `examples/*.ipynb` files, and makes a table of contents. @@ -33,79 +33,77 @@ Other docs, such as installation guides, are written in the `docs` directory and We strive to provide provide lots of usage examples, and to document all code in docstrings. We absolutely appreciate any contribution to this effort! -### The release cycle +### Versioning -- The `dev` branch receives all new development, including community contributions. -We aim to keep it in a functional state, but large changes do occur, and things do get broken every now and then. -Use only if you want the "bleeding edge". -- BVLC maintainers will periodically update the `master` branch with changes from `dev`, giving it a release tag ([releases so far](https://github.com/BVLC/caffe/releases)). -Use this if you want more stability. +The `master` branch receives all new development including community contributions. +We try to keep it in a reliable state, but it is the bleeding edge, and things do get broken every now and then. +BVLC maintainers will periodically make releases by marking stable checkpoints as tags and maintenance branches. [Past releases](https://github.com/BVLC/caffe/releases) are catalogued online. -### Issues & Pull Request Protocol +#### Issues & Pull Request Protocol -Use Github Issues to report [bugs], propose features, and ask development [questions]. -Large-scale development work is guided by [milestones], which are sets of Issues selected for concurrent release (integration from `dev` to `master`). +Post [Issues](https://github.com/BVLC/caffe/issues) to propose features, report [bugs], and discuss framework code. +Large-scale development work is guided by [milestones], which are sets of Issues selected for bundling as releases. Please note that since the core developers are largely researchers, we may work on a feature in isolation for some time before releasing it to the community, so as to claim honest academic contribution. We do release things as soon as a reasonable technical report may be written, and we still aim to inform the community of ongoing development through Github Issues. -When you are ready to start developing your feature or fixing a bug, follow this protocol: +**When you are ready to develop a feature or fixing a bug, follow this protocol**: -- Develop in [feature branches] with descriptive names. - - For new development branch off `dev`. - - For documentation and fixes for `master` branch off `master`. -- Bring your work up-to-date by [rebasing] onto the latest `dev` / `master`. -(Polish your changes by [interactive rebase], if you'd like.) -- [Pull request] your contribution to `BVLC/caffe`'s `dev` / `master` branch for discussion and review. +- Develop in [feature branches] with descriptive names. Branch off of the latest `master`. +- Bring your work up-to-date by [rebasing] onto the latest `master` when done. +(Groom your changes by [interactive rebase], if you'd like.) +- [Pull request] your contribution to `BVLC/caffe`'s `master` branch for discussion and review. - Make PRs *as soon as development begins*, to let discussion guide development. - A PR is only ready for merge review when it is a fast-forward merge, and all code is documented, linted, and tested -- that means your PR must include tests! - When the PR satisfies the above properties, use comments to request maintainer review. -Below is a poetic presentation of the protocol in code form. +The following is a poetic presentation of the protocol in code form. #### [Shelhamer's](https://github.com/shelhamer) “life of a branch in four acts” -Make the `feature` branch off of the latest `bvlc/dev` +Make the `feature` branch off of the latest `bvlc/master` ``` -git checkout dev -git pull upstream dev +git checkout master +git pull upstream master git checkout -b feature # do your work, make commits ``` -Prepare to merge by rebasing your branch on the latest `bvlc/dev` +Prepare to merge by rebasing your branch on the latest `bvlc/master` ``` -# make sure dev is fresh -git checkout dev -git pull upstream dev -# rebase your branch on the tip of dev +# make sure master is fresh +git checkout master +git pull upstream master +# rebase your branch on the tip of master git checkout feature -git rebase dev +git rebase master ``` -Push your branch to pull request it into `dev` +Push your branch to pull request it into `BVLC/caffe:master` ``` git push origin feature -# ...make pull request to dev... +# ...make pull request to master... ``` -Now make a pull request! You can do this from the command line (`git pull-request -b dev`) if you install [hub](https://github.com/github/hub). +Now make a pull request! You can do this from the command line (`git pull-request -b master`) if you install [hub](https://github.com/github/hub). Hub has many other magical uses. -The pull request of `feature` into `dev` will be a clean merge. Applause. +The pull request of `feature` into `master` will be a clean merge. Applause. [bugs]: https://github.com/BVLC/caffe/issues?labels=bug&page=1&state=open -[questions]: https://github.com/BVLC/caffe/issues?labels=question&page=1&state=open [milestones]: https://github.com/BVLC/caffe/issues?milestone=1 [Pull request]: https://help.github.com/articles/using-pull-requests [interactive rebase]: https://help.github.com/articles/interactive-rebase [rebasing]: http://git-scm.com/book/en/Git-Branching-Rebasing [feature branches]: https://www.atlassian.com/git/workflows#!workflow-feature-branch +**Historical note**: Caffe once relied on a two branch `master` and `dev` workflow. +PRs from this time are still open but these will be merged into `master` or closed. + ### Testing Run `make runtest` to check the project tests. New code requires new tests. Pull requests that fail tests will not be accepted. -The `googletest` framework we use provides many additional options, which you can access by running the test binaries directly. One of the more useful options is `--gtest_filter`, which allows you to filter tests by name: +The `gtest` framework we use provides many additional options, which you can access by running the test binaries directly. One of the more useful options is `--gtest_filter`, which allows you to filter tests by name: # run all tests with CPU in the name build/test/test_all.testbin --gtest_filter='*CPU*' @@ -119,7 +117,7 @@ To get a list of all options `googletest` provides, simply pass the `--help` fla ### Style -- Follow [Google C++ style](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml) and [Google python style](http://google-styleguide.googlecode.com/svn/trunk/pyguide.html) + [PEP 8](http://legacy.python.org/dev/peps/pep-0008/). +- **Run `make lint` to check C++ code.** - Wrap lines at 80 chars. +- Follow [Google C++ style](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml) and [Google python style](http://google-styleguide.googlecode.com/svn/trunk/pyguide.html) + [PEP 8](http://legacy.python.org/dev/peps/pep-0008/). - Remember that “a foolish consistency is the hobgoblin of little minds,” so use your best judgement to write the clearest code for your particular case. -- **Run `make lint` to check C++ code.** diff --git a/docs/images/caffeine-icon.png b/docs/images/caffeine-icon.png new file mode 100644 index 00000000000..88b4a002bb0 Binary files /dev/null and b/docs/images/caffeine-icon.png differ diff --git a/docs/index.md b/docs/index.md index bf1d9c3c78b..932b3b58d1d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,31 +4,33 @@ title: Deep Learning Framework # Caffe -Caffe is a deep learning framework developed with cleanliness, readability, and speed in mind. -It was created by [Yangqing Jia](http://daggerfs.com) during his PhD at UC Berkeley, and is in active development by the Berkeley Vision and Learning Center ([BVLC](http://bvlc.eecs.berkeley.edu)) and by community contributors. +Caffe is a deep learning framework made with expression, speed, and modularity in mind. +It is developed by the Berkeley Vision and Learning Center ([BVLC](http://bvlc.eecs.berkeley.edu)) and by community contributors. +[Yangqing Jia](http://daggerfs.com) created the project during his PhD at UC Berkeley. Caffe is released under the [BSD 2-Clause license](https://github.com/BVLC/caffe/blob/master/LICENSE). Check out our web image classification [demo](http://demo.caffe.berkeleyvision.org)! -## Why use Caffe? +## Why Caffe? -**Clean architecture** enables rapid deployment. -Networks are specified in simple config files, with no hard-coded parameters in the code. -Switching between CPU and GPU is as simple as setting a flag -- so models can be trained on a GPU machine, and then used on commodity clusters. +**Expressive architecture** encourages application and innovation. +Models and optimization are defined by configuration without hard-coding. +Switch between CPU and GPU by setting a single flag to train on a GPU machine then deploy to commodity clusters or mobile devices. -**Readable & modifiable implementation** fosters active development. -In Caffe's first year, it has been forked by over 600 developers on Github, and many have pushed significant changes. +**Extensible code** fosters active development. +In Caffe's first year, it has been forked by over 1,000 developers and had many significant changes contributed back. +Thanks to these contributors the framework tracks the state-of-the-art in both code and models. -**Speed** makes Caffe perfect for industry use. -Caffe can process over **40M images per day** with a single NVIDIA K40 or Titan GPU\*. -That's 5 ms/image in training, and 2 ms/image in test. -We believe that Caffe is the fastest CNN implementation available. +**Speed** makes Caffe perfect for research experiments and industry deployment. +Caffe can process **over 60M images per day** with a single NVIDIA K40 GPU\*. +That's 1 ms/image for inference and 4 ms/image for learning. +We believe that Caffe is the fastest convnet implementation available. **Community**: Caffe already powers academic research projects, startup prototypes, and even large-scale industrial applications in vision, speech, and multimedia. -There is an active discussion and support community on [Github](https://github.com/BVLC/caffe/issues). +Join our community of brewers on the [caffe-users group](https://groups.google.com/forum/#!forum/caffe-users) and [Github](https://github.com/BVLC/caffe/).

    -\* When files are properly cached, and using the ILSVRC2012-winning [SuperVision](http://www.image-net.org/challenges/LSVRC/2012/supervision.pdf) model. +\* With the ILSVRC2012-winning [SuperVision](http://www.image-net.org/challenges/LSVRC/2012/supervision.pdf) model and caching IO. Consult performance [details](/performance_hardware.html).

    @@ -56,7 +58,7 @@ Developer documentation automagically generated from code comments. -
    {{page.title}}
    {{page.description}}
    {% endfor %} -### Notebook examples +### Notebook Examples {% assign notebooks = site.pages | where:'category','notebook' | sort: 'priority' %} {% for page in notebooks %} @@ -77,6 +79,17 @@ Please cite Caffe in your publications if it helps your research: If you do publish a paper where Caffe helped your research, we encourage you to update the [publications wiki](https://github.com/BVLC/caffe/wiki/Publications). Citations are also tracked automatically by [Google Scholar](http://scholar.google.com/scholar?oi=bibs&hl=en&cites=17333247995453974016). +## Contacting Us + +Join the [caffe-users group](https://groups.google.com/forum/#!forum/caffe-users) to ask questions and discuss methods and models. This is where we talk about usage, installation, and applications. + +Framework development discussions and thorough bug reports are collected on [Issues](https://github.com/BVLC/caffe/issues). + +Contact [caffe-dev](mailto:caffe-dev@googlegroups.com) if you have a confidential proposal for the framework *and the ability to act on it*. +Requests for features, explanations, or personal help will be ignored; post to [caffe-users](https://groups.google.com/forum/#!forum/caffe-users) instead. + +The core Caffe developers offer [consulting services](mailto:caffe-coldpress@googlegroups.com) for appropriate projects. + ## Acknowledgements The BVLC Caffe developers would like to thank NVIDIA for GPU donation, A9 and Amazon Web Services for a research grant in support of Caffe development and reproducible research in deep learning, and BVLC PI [Trevor Darrell](http://www.eecs.berkeley.edu/~trevor/) for guidance. @@ -85,20 +98,9 @@ The BVLC members who have contributed to Caffe are (alphabetical by first name): [Eric Tzeng](https://github.com/erictzeng), [Evan Shelhamer](http://imaginarynumber.net/), [Jeff Donahue](http://jeffdonahue.com/), [Jon Long](https://github.com/longjon), [Ross Girshick](http://www.cs.berkeley.edu/~rbg/), [Sergey Karayev](http://sergeykarayev.com/), [Sergio Guadarrama](http://www.eecs.berkeley.edu/~sguada/), and [Yangqing Jia](http://daggerfs.com/). The open-source community plays an important and growing role in Caffe's development. -Check out the Github [project pulse](https://github.com/BVLC/caffe/pulse) for recent activity, and the [contributors](https://github.com/BVLC/caffe/graphs/contributors) for a sorted list. +Check out the Github [project pulse](https://github.com/BVLC/caffe/pulse) for recent activity and the [contributors](https://github.com/BVLC/caffe/graphs/contributors) for the full list. We sincerely appreciate your interest and contributions! If you'd like to contribute, please read the [developing & contributing](development.html) guide. Yangqing would like to give a personal thanks to the NVIDIA Academic program for providing GPUs, [Oriol Vinyals](http://www1.icsi.berkeley.edu/~vinyals/) for discussions along the journey, and BVLC PI [Trevor Darrell](http://www.eecs.berkeley.edu/~trevor/) for advice. - -## Contacting us - -All questions about usage, installation, code, and applications should be searched for and asked on the [caffe-users mailing list](https://groups.google.com/forum/#!forum/caffe-users). - -All development discussion should be carried out at [GitHub Issues](https://github.com/BVLC/caffe/issues). - -If you have a proposal that may not be suited for public discussion *and an ability to act on it*, please email us [directly](mailto:caffe-dev@googlegroups.com). -Requests for features, explanations, or personal help will be ignored; post such matters publicly as issues. - -The core Caffe developers may be able to provide [consulting services](mailto:caffe-coldpress@googlegroups.com) for appropriate projects. diff --git a/docs/install_apt.md b/docs/install_apt.md new file mode 100644 index 00000000000..89bc9a00aef --- /dev/null +++ b/docs/install_apt.md @@ -0,0 +1,44 @@ +--- +title: Installation: Ubuntu +--- + +# Ubuntu Installation + +**General dependencies** + + sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libboost-all-dev libhdf5-serial-dev + +**Remaining dependencies, 14.04** + + sudo apt-get install libgflags-dev libgoogle-glog-dev liblmdb-dev protobuf-compiler + +**Remaining dependencies, 12.04** + + # glog + wget https://google-glog.googlecode.com/files/glog-0.3.3.tar.gz + tar zxvf glog-0.3.3.tar.gz + cd glog-0.3.3 + ./configure + make && make install + # gflags + wget https://github.com/schuhschuh/gflags/archive/master.zip + unzip master.zip + cd gflags-master + mkdir build && cd build + export CXXFLAGS="-fPIC" && cmake .. && make VERBOSE=1 + make && make install + # lmdb + git clone git://gitorious.org/mdb/mdb.git + cd mdb/libraries/liblmdb + make && make install + +Note that glog does not compile with the most recent gflags version (2.1), so before that is resolved you will need to build with glog first. + +**CUDA**: Install via the NVIDIA package instead of `apt-get` to be certain of the library and driver versions. +Install the library and latest driver separately; the driver bundled with the library is usually out-of-date. + +**BLAS**: install ATLAS by `sudo apt-get install libatlas-base-dev` or install OpenBLAS or MKL for better CPU performance. + +**Python** (optional): if you use the default Python you will need to `sudo apt-get install` the `python-dev` package to have the Python headers for building the pycaffe interface. + +Continue with [compilation](installation.html#compilation). diff --git a/docs/install_osx.md b/docs/install_osx.md new file mode 100644 index 00000000000..55b098731fc --- /dev/null +++ b/docs/install_osx.md @@ -0,0 +1,128 @@ +--- +title: Installation: OS X +--- + +# OS X Installation + +We highly recommend using the [Homebrew](http://brew.sh/) package manager. +Ideally you could start from a clean `/usr/local` to avoid conflicts. +In the following, we assume that you're using Anaconda Python and Homebrew. + +**CUDA**: Install via the NVIDIA package that includes both CUDA and the bundled driver. **CUDA 7 is strongly suggested.** Older CUDA require `libstdc++` while clang++ is the default compiler and `libc++` the default standard library on OS X 10.9+. This disagreement makes it necessary to change the compilation settings for each of the dependencies. This is prone to error. + +**Library Path**: We find that everything compiles successfully if `$LD_LIBRARY_PATH` is not set at all, and `$DYLD_FALLBACK_LIBRARY_PATH` is set to to provide CUDA, Python, and other relevant libraries (e.g. `/usr/local/cuda/lib:$HOME/anaconda/lib:/usr/local/lib:/usr/lib`). +In other `ENV` settings, things may not work as expected. + +**General dependencies** + + brew install --fresh -vd snappy leveldb gflags glog szip lmdb + # need the homebrew science source for OpenCV and hdf5 + brew tap homebrew/science + hdf5 opencv + +If using Anaconda Python, a modification to the OpenCV formula might be needed +Do `brew edit opencv` and change the lines that look like the two lines below to exactly the two lines below. + + -DPYTHON_LIBRARY=#{py_prefix}/lib/libpython2.7.dylib + -DPYTHON_INCLUDE_DIR=#{py_prefix}/include/python2.7 + +If using Anaconda Python, HDF5 is bundled and the `hdf5` formula can be skipped. + +**Remaining dependencies, with / without Python** + + # with Python pycaffe needs dependencies built from source + brew install --build-from-source --with-python --fresh -vd protobuf + brew install --build-from-source --fresh -vd boost + # without Python the usual installation suffices + brew install protobuf boost + +**BLAS**: already installed as the [Accelerate / vecLib Framework](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man7/Accelerate.7.html). OpenBLAS and MKL are alternatives for faster CPU computation. + +**Python** (optional): Anaconda is the preferred Python. +If you decide against it, please use Homebrew. +Check that Caffe and dependencies are linking against the same, desired Python. + +Continue with [compilation](installation.html#compilation). + +## libstdc++ installation + +This route is not for the faint of heart. +For OS X 10.10 and 10.9 you should install CUDA 7 and follow the instructions above. +If that is not an option, take a deep breath and carry on. + +In OS X 10.9+, clang++ is the default C++ compiler and uses `libc++` as the standard library. +However, NVIDIA CUDA (even version 6.0) currently links only with `libstdc++`. +This makes it necessary to change the compilation settings for each of the dependencies. + +We do this by modifying the Homebrew formulae before installing any packages. +Make sure that Homebrew doesn't install any software dependencies in the background; all packages must be linked to `libstdc++`. + +The prerequisite Homebrew formulae are + + boost snappy leveldb protobuf gflags glog szip lmdb homebrew/science/opencv + +For each of these formulas, `brew edit FORMULA`, and add the ENV definitions as shown: + + def install + # ADD THE FOLLOWING: + ENV.append "CXXFLAGS", "-stdlib=libstdc++" + ENV.append "CFLAGS", "-stdlib=libstdc++" + ENV.append "LDFLAGS", "-stdlib=libstdc++ -lstdc++" + # The following is necessary because libtool likes to strip LDFLAGS: + ENV["CXX"] = "/usr/bin/clang++ -stdlib=libstdc++" + ... + +To edit the formulae in turn, run + + for x in snappy leveldb protobuf gflags glog szip boost boost-python lmdb homebrew/science/opencv; do brew edit $x; done + +After this, run + + for x in snappy leveldb gflags glog szip lmdb homebrew/science/opencv; do brew uninstall $x; brew install --build-from-source --fresh -vd $x; done + brew uninstall protobuf; brew install --build-from-source --with-python --fresh -vd protobuf + brew install --build-from-source --fresh -vd boost boost-python + +If this is not done exactly right then linking errors will trouble you. + +**Homebrew versioning** that Homebrew maintains itself as a separate git repository and making the above `brew edit FORMULA` changes will change files in your local copy of homebrew's master branch. By default, this will prevent you from updating Homebrew using `brew update`, as you will get an error message like the following: + + $ brew update + error: Your local changes to the following files would be overwritten by merge: + Library/Formula/lmdb.rb + Please, commit your changes or stash them before you can merge. + Aborting + Error: Failure while executing: git pull -q origin refs/heads/master:refs/remotes/origin/master + +One solution is to commit your changes to a separate Homebrew branch, run `brew update`, and rebase your changes onto the updated master. You'll have to do this both for the main Homebrew repository in `/usr/local/` and the Homebrew science repository that contains OpenCV in `/usr/local/Library/Taps/homebrew/homebrew-science`, as follows: + + cd /usr/local + git checkout -b caffe + git add . + git commit -m "Update Caffe dependencies to use libstdc++" + cd /usr/local/Library/Taps/homebrew/homebrew-science + git checkout -b caffe + git add . + git commit -m "Update Caffe dependencies" + +Then, whenever you want to update homebrew, switch back to the master branches, do the update, rebase the caffe branches onto master and fix any conflicts: + + # Switch batch to homebrew master branches + cd /usr/local + git checkout master + cd /usr/local/Library/Taps/homebrew/homebrew-science + git checkout master + + # Update homebrew; hopefully this works without errors! + brew update + + # Switch back to the caffe branches with the forumlae that you modified earlier + cd /usr/local + git rebase master caffe + # Fix any merge conflicts and commit to caffe branch + cd /usr/local/Library/Taps/homebrew/homebrew-science + git rebase master caffe + # Fix any merge conflicts and commit to caffe branch + + # Done! + +At this point, you should be running the latest Homebrew packages and your Caffe-related modifications will remain in place. diff --git a/docs/install_yum.md b/docs/install_yum.md new file mode 100644 index 00000000000..478e7d952cc --- /dev/null +++ b/docs/install_yum.md @@ -0,0 +1,45 @@ +--- +title: Installation: RHEL / Fedora / CentOS +--- + +# RHEL / Fedora / CentOS Installation + +**General dependencies** + + sudo yum install protobuf-devel leveldb-devel snappy-devel opencv-devel boost-devel hdf5-devel + +**Remaining dependencies, recent OS** + + sudo yum install gflags-devel glog-devel lmdb-devel + +**Remaining dependencies, if not found** + + # glog + wget https://google-glog.googlecode.com/files/glog-0.3.3.tar.gz + tar zxvf glog-0.3.3.tar.gz + cd glog-0.3.3 + ./configure + make && make install + # gflags + wget https://github.com/schuhschuh/gflags/archive/master.zip + unzip master.zip + cd gflags-master + mkdir build && cd build + export CXXFLAGS="-fPIC" && cmake .. && make VERBOSE=1 + make && make install + # lmdb + git clone git://gitorious.org/mdb/mdb.git + cd mdb/libraries/liblmdb + make && make install + +Note that glog does not compile with the most recent gflags version (2.1), so before that is resolved you will need to build with glog first. + +**CUDA**: Install via the NVIDIA package instead of `yum` to be certain of the library and driver versions. +Install the library and latest driver separately; the driver bundled with the library is usually out-of-date. + + CentOS/RHEL/Fedora: + +**BLAS**: install ATLAS by `sudo yum install atlas-devel` or install OpenBLAS or MKL for better CPU performance. For the Makefile build, uncomment and set `BLAS_LIB` accordingly as ATLAS is usually installed under `/usr/lib[64]/atlas`). + +**Python** (optional): if you use the default Python you will need to `sudo yum install` the `python-devel` package to have the Python headers for building the pycaffe wrapper. + +Continue with [compilation](installation.html#compilation). diff --git a/docs/installation.md b/docs/installation.md index 16e319b4392..16575b54029 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -4,30 +4,36 @@ title: Installation # Installation -Prior to installing, it is best to read through this guide and take note of the details for your platform. -We have installed Caffe on Ubuntu 14.04, Ubuntu 12.04, OS X 10.9, and OS X 10.8. +Prior to installing, have a glance through this guide and take note of the details for your platform. +We install and run Caffe on Ubuntu 14.04 and 12.04, OS X 10.10 / 10.9 / 10.8, and AWS. +The official Makefile and `Makefile.config` build are complemented by an automatic CMake build from the community. - [Prerequisites](#prerequisites) - [Compilation](#compilation) -- [Hardware questions](#hardware_questions) +- [Hardware](#hardware) +- Platforms: [Ubuntu guide](install_apt.html), [OS X guide](install_osx.html), and [RHEL / CentOS / Fedora guide](install_yum.html) -Ask installation questions on the [caffe-users](https://groups.google.com/forum/#!forum/caffe-users) mailing list. +When updating Caffe, it's best to `make clean` before re-compiling. ## Prerequisites -Caffe depends on several software packages. +Caffe has several dependencies. -* [CUDA](https://developer.nvidia.com/cuda-zone) library version 6.5 (recommended), 6.0, 5.5, or 5.0 and the latest driver version for CUDA 6 or 319.* for CUDA 5 (and NOT 331.*) -* [BLAS](http://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms) (provided via ATLAS, MKL, or OpenBLAS). -* [OpenCV](http://opencv.org/) (>= 2.4) -* [Boost](http://www.boost.org/) (>= 1.55, although only 1.55 and 1.56 are tested) -* `glog`, `gflags`, `protobuf`, `leveldb`, `snappy`, `hdf5`, `lmdb` -* For the Python wrapper - * `Python 2.7`, `numpy (>= 1.7)`, boost-provided `boost.python` -* For the MATLAB wrapper - * MATLAB with the `mex` compiler. +* [CUDA](https://developer.nvidia.com/cuda-zone) is required for GPU mode. + * library version 7.0 and the latest driver version are recommended, but 6.* is fine too + * 5.5, and 5.0 are compatible but considered legacy +* [BLAS](http://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms) via ATLAS, MKL, or OpenBLAS. +* [Boost](http://www.boost.org/) >= 1.55 +* [OpenCV](http://opencv.org/) >= 2.4 including 3.0 +* `protobuf`, `glog`, `gflags` +* IO libraries `hdf5`, `leveldb`, `snappy`, `lmdb` -**cuDNN Caffe**: for fastest operation Caffe is accelerated by drop-in integration of [NVIDIA cuDNN](https://developer.nvidia.com/cudnn). To speed up your Caffe models, install cuDNN then uncomment the `USE_CUDNN := 1` flag in `Makefile.config` when installing Caffe. Acceleration is automatic. +Pycaffe and Matcaffe interfaces have their own natural needs. + +* For Python Caffe: `Python 2.7`, `numpy (>= 1.7)`, boost-provided `boost.python` +* For MATLAB Caffe: MATLAB with the `mex` compiler. + +**cuDNN Caffe**: for fastest operation Caffe is accelerated by drop-in integration of [NVIDIA cuDNN](https://developer.nvidia.com/cudnn). To speed up your Caffe models, install cuDNN then uncomment the `USE_CUDNN := 1` flag in `Makefile.config` when installing Caffe. Acceleration is automatic. For now cuDNN v1 is integrated but see [PR #1731](https://github.com/BVLC/caffe/pull/1731) for v2. **CPU-only Caffe**: for cold-brewed CPU-only Caffe uncomment the `CPU_ONLY := 1` flag in `Makefile.config` to configure and build Caffe without CUDA. This is helpful for cloud or cluster deployment. @@ -39,13 +45,9 @@ To install CUDA, go to the [NVIDIA CUDA website](https://developer.nvidia.com/cu For best performance, Caffe can be accelerated by [NVIDIA cuDNN](https://developer.nvidia.com/cudnn). Register for free at the cuDNN site, install it, then continue with these installation instructions. To compile with cuDNN set the `USE_CUDNN := 1` flag set in your `Makefile.config`. Caffe requires BLAS as the backend of its matrix and vector computations. -There are several implementations of this library. -The choice is yours: +There are several implementations of this library. The choice is yours: * [ATLAS](http://math-atlas.sourceforge.net/): free, open source, and so the default for Caffe. - + Ubuntu: `sudo apt-get install libatlas-base-dev` - + CentOS/RHEL/Fedora: `sudo yum install atlas-devel` - + OS X: already installed as the [Accelerate / vecLib Framework](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man7/Accelerate.7.html). * [Intel MKL](http://software.intel.com/en-us/intel-mkl): commercial and optimized for Intel CPUs, with a free trial and [student](http://software.intel.com/en-us/intel-education-offerings) licenses. 1. Install MKL. 2. Set `BLAS := mkl` in `Makefile.config` @@ -53,7 +55,7 @@ The choice is yours: 1. Install OpenBLAS 2. Set `BLAS := open` in `Makefile.config` -### Python and/or MATLAB wrappers (optional) +### Python and/or MATLAB Caffe (optional) #### Python @@ -61,15 +63,9 @@ The main requirements are `numpy` and `boost.python` (provided by boost). `panda You can install the dependencies with - for req in $(cat requirements.txt); do sudo pip install $req; done - -but we highly recommend first installing the [Anaconda](https://store.continuum.io/cshop/anaconda/) Python distribution, which provides most of the necessary packages, as well as the `hdf5` library dependency. - -For **Ubuntu**, if you use the default Python you will need to `sudo apt-get install` the `python-dev` package to have the Python headers for building the wrapper. + for req in $(cat requirements.txt); do pip install $req; done -For **Fedora**, if you use the default Python you will need to `sudo yum install` the `python-devel` package to have the Python headers for building the wrapper. - -For **OS X**, Anaconda is the preferred Python. If you decide against it, please use Homebrew -- but beware of potential linking errors! +but we suggest first installing the [Anaconda](https://store.continuum.io/cshop/anaconda/) Python distribution, which provides most of the necessary packages, as well as the `hdf5` library dependency. To import the `caffe` Python module after completing the installation, add the module directory to your `$PYTHONPATH` by `export PYTHONPATH=/path/to/caffe/python:$PYTHONPATH` or the like. You should not import the module in the `caffe/python/caffe` directory! @@ -79,170 +75,7 @@ To import the `caffe` Python module after completing the installation, add the m Install MATLAB, and make sure that its `mex` is in your `$PATH`. -*Caffe's MATLAB interface works with versions 2012b, 2013a/b, and 2014a.* - -### The rest of the dependencies - -#### Linux - -On **Ubuntu**, most of the dependencies can be installed with - - sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libboost-all-dev libhdf5-serial-dev - -and for **Ubuntu 14.04** the rest of the dependencies can be installed with - - sudo apt-get install libgflags-dev libgoogle-glog-dev liblmdb-dev protobuf-compiler - -Keep reading to find out how to manually build and install the Google flags library, Google logging library and LMDB on **Ubuntu 12.04**. - -On **CentOS / RHEL / Fedora**, most of the dependencies can be installed with - - sudo yum install protobuf-devel leveldb-devel snappy-devel opencv-devel boost-devel hdf5-devel - -The Google flags library, Google logging library and LMDB already made their ways into newer versions of **CentOS / RHEL / Fedora** so it is better to first attempt to install them using `yum` - - sudo yum install gflags-devel glog-devel lmdb-devel - -**Finally** in case you couldn't find those extra libraries mentioned above in your distribution's repositories, here are the instructions to follow for manually building and installing them on **Ubuntu 12.04 / CentOS / RHEL / Fedora** (or practically on any Linux distribution) - - # glog - wget https://google-glog.googlecode.com/files/glog-0.3.3.tar.gz - tar zxvf glog-0.3.3.tar.gz - cd glog-0.3.3 - ./configure - make && make install - # gflags - wget https://github.com/schuhschuh/gflags/archive/master.zip - unzip master.zip - cd gflags-master - mkdir build && cd build - export CXXFLAGS="-fPIC" && cmake .. && make VERBOSE=1 - make && make install - # lmdb - git clone git://gitorious.org/mdb/mdb.git - cd mdb/libraries/liblmdb - make && make install - -Note that glog does not compile with the most recent gflags version (2.1), so before that is resolved you will need to build with glog first. - -#### OS X - -On **OS X**, we highly recommend using the [Homebrew](http://brew.sh/) package manager, and ideally starting from a clean install of the OS (or from a wiped `/usr/local`) to avoid conflicts. -In the following, we assume that you're using Anaconda Python and Homebrew. - -To install the OpenCV dependency, we'll need to provide an additional source for Homebrew: - - brew tap homebrew/science - -If using Anaconda Python, a modification is required to the OpenCV formula. -Do `brew edit opencv` and change the lines that look like the two lines below to exactly the two lines below. - - -DPYTHON_LIBRARY=#{py_prefix}/lib/libpython2.7.dylib - -DPYTHON_INCLUDE_DIR=#{py_prefix}/include/python2.7 - -**NOTE**: We find that everything compiles successfully if `$LD_LIBRARY_PATH` is not set at all, and `$DYLD_FALLBACK_LIBRARY_PATH` is set to to provide CUDA, Python, and other relevant libraries (e.g. `/usr/local/cuda/lib:$HOME/anaconda/lib:/usr/local/lib:/usr/lib`). -In other `ENV` settings, things may not work as expected. - -**NOTE**: There is currently a conflict between boost 1.56 and CUDA in some configurations. Check the [conflict description](https://github.com/BVLC/caffe/issues/1193#issuecomment-57491906) and try downgrading to 1.55. - -#### 10.8-specific Instructions - -Simply run the following: - - brew install --build-from-source boost boost-python - brew install --with-python protobuf - for x in snappy leveldb gflags glog szip lmdb homebrew/science/opencv; do brew install $x; done - -Building boost from source is needed to link against your local Python (exceptions might be raised during some OS X installs, but **ignore** these and continue). If you do not need the Python wrapper, simply doing `brew install boost` is fine. - -**Note** that the HDF5 dependency is provided by Anaconda Python in this case. -If you're not using Anaconda, include `hdf5` in the list above. - -#### 10.9-specific Instructions - -In OS X 10.9, clang++ is the default C++ compiler and uses `libc++` as the standard library. -However, NVIDIA CUDA (even version 6.0) currently links only with `libstdc++`. -This makes it necessary to change the compilation settings for each of the dependencies. - -We do this by modifying the Homebrew formulae before installing any packages. -Make sure that Homebrew doesn't install any software dependencies in the background; all packages must be linked to `libstdc++`. - -The prerequisite Homebrew formulae are - - boost snappy leveldb protobuf gflags glog szip lmdb homebrew/science/opencv - -For each of these formulas, `brew edit FORMULA`, and add the ENV definitions as shown: - - def install - # ADD THE FOLLOWING: - ENV.append "CXXFLAGS", "-stdlib=libstdc++" - ENV.append "CFLAGS", "-stdlib=libstdc++" - ENV.append "LDFLAGS", "-stdlib=libstdc++ -lstdc++" - # The following is necessary because libtool likes to strip LDFLAGS: - ENV["CXX"] = "/usr/bin/clang++ -stdlib=libstdc++" - ... - -To edit the formulae in turn, run - - for x in snappy leveldb protobuf gflags glog szip boost boost-python lmdb homebrew/science/opencv; do brew edit $x; done - -After this, run - - for x in snappy leveldb gflags glog szip lmdb homebrew/science/opencv; do brew uninstall $x; brew install --build-from-source --fresh -vd $x; done - brew uninstall protobuf; brew install --build-from-source --with-python --fresh -vd protobuf - brew install --build-from-source --fresh -vd boost boost-python - -**Note** that `brew install --build-from-source --fresh -vd boost` is fine if you do not need the Caffe Python wrapper. - -**Note** that the HDF5 dependency is provided by Anaconda Python in this case. -If you're not using Anaconda, include `hdf5` in the list above. - -**Note** that in order to build the Caffe Python wrappers you must install `boost` and `boost-python`: - - brew install --build-from-source --fresh -vd boost boost-python - -**Note** that Homebrew maintains itself as a separate git repository and making the above `brew edit FORMULA` changes will change files in your local copy of homebrew's master branch. By default, this will prevent you from updating Homebrew using `brew update`, as you will get an error message like the following: - - $ brew update - error: Your local changes to the following files would be overwritten by merge: - Library/Formula/lmdb.rb - Please, commit your changes or stash them before you can merge. - Aborting - Error: Failure while executing: git pull -q origin refs/heads/master:refs/remotes/origin/master - -One solution is to commit your changes to a separate Homebrew branch, run `brew update`, and rebase your changes onto the updated master. You'll have to do this both for the main Homebrew repository in `/usr/local/` and the Homebrew science repository that contains OpenCV in `/usr/local/Library/Taps/homebrew/homebrew-science`, as follows: - - cd /usr/local - git checkout -b caffe - git add . - git commit -m "Update Caffe dependencies to use libstdc++" - cd /usr/local/Library/Taps/homebrew/homebrew-science - git checkout -b caffe - git add . - git commit -m "Update Caffe dependencies" - -Then, whenever you want to update homebrew, switch back to the master branches, do the update, rebase the caffe branches onto master and fix any conflicts: - - # Switch batch to homebrew master branches - cd /usr/local - git checkout master - cd /usr/local/Library/Taps/homebrew/homebrew-science - git checkout master - - # Update homebrew; hopefully this works without errors! - brew update - - # Switch back to the caffe branches with the forumlae that you modified earlier - cd /usr/local - git rebase master caffe - # Fix any merge conflicts and commit to caffe branch - cd /usr/local/Library/Taps/homebrew/homebrew-science - git rebase master caffe - # Fix any merge conflicts and commit to caffe branch - - # Done! - -At this point, you should be running the latest Homebrew packages and your Caffe-related modifications will remain in place. +*Caffe's MATLAB interface works with versions 2014a/b, 2013a/b, and 2012b.* #### Windows @@ -250,8 +83,7 @@ There is an unofficial Windows port of Caffe at [niuzhiheng/caffe:windows](https ## Compilation -Now that you have the prerequisites, edit your `Makefile.config` to change the paths for your setup (you should especially uncomment and set `BLAS_LIB` accordingly on distributions like **CentOS / RHEL / Fedora** where ATLAS is installed under `/usr/lib[64]/atlas`) -The defaults should work, but uncomment the relevant lines if using Anaconda Python. +Now that you have the prerequisites, edit your `Makefile.config` to change the paths for your setup The defaults should work, but uncomment the relevant lines if using Anaconda Python. cp Makefile.config.example Makefile.config # Adjust Makefile.config (for example, if using Anaconda Python) @@ -259,24 +91,22 @@ The defaults should work, but uncomment the relevant lines if using Anaconda Pyt make test make runtest -To compile with cuDNN acceleration, you should uncomment the `USE_CUDNN := 1` switch in `Makefile.config`. - -If there is no GPU in your machine, you should switch to CPU-only Caffe by uncommenting `CPU_ONLY := 1` in `Makefile.config`. +- For cuDNN acceleration, you should uncomment the `USE_CUDNN := 1` switch in `Makefile.config`. +- For CPU-only Caffe, uncomment `CPU_ONLY := 1` in `Makefile.config`. To compile the Python and MATLAB wrappers do `make pycaffe` and `make matcaffe` respectively. Be sure to set your MATLAB and Python paths in `Makefile.config` first! -*Distribution*: run `make distribute` to create a `distribute` directory with all the Caffe headers, compiled libraries, binaries, etc. needed for distribution to other machines. +**Distribution**: run `make distribute` to create a `distribute` directory with all the Caffe headers, compiled libraries, binaries, etc. needed for distribution to other machines. -*Speed*: for a faster build, compile in parallel by doing `make all -j8` where 8 is the number of parallel threads for compilation (a good choice for the number of threads is the number of cores in your machine). +**Speed**: for a faster build, compile in parallel by doing `make all -j8` where 8 is the number of parallel threads for compilation (a good choice for the number of threads is the number of cores in your machine). Now that you have installed Caffe, check out the [MNIST tutorial](gathered/examples/mnist.html) and the [reference ImageNet model tutorial](gathered/examples/imagenet.html). -### Compilation using CMake (beta) +### CMake Compilation -In lieu of manually editing `Makefile.config` to tell Caffe where dependencies are located, Caffe also provides a CMake-based build system (currently in "beta"). -It requires CMake version >= 2.8.8. -The basic installation steps are as follows: +In lieu of manually editing `Makefile.config` to configure the build, Caffe offers an unofficial CMake build thanks to @Nerei, @akosiorek, and other members of the community. It requires CMake version >= 2.8.7. +The basic steps are as follows: mkdir build cd build @@ -284,21 +114,14 @@ The basic installation steps are as follows: make all make runtest -#### Ubuntu 12.04 - -Note that in Ubuntu 12.04, Aptitude will install version CMake 2.8.7 by default, which is not supported by Caffe's CMake build (requires at least 2.8.8). -As a workaround, if you are using Ubuntu 12.04 you can try the following steps to install (or upgrade to) CMake 2.8.9: - - sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y - sudo apt-get -y update - sudo apt-get install cmake +See [PR #1667](https://github.com/BVLC/caffe/pull/1667) for options and details. -## Hardware Questions +## Hardware -**Laboratory Tested Hardware**: Berkeley Vision runs Caffe with K40s, K20s, and Titans including models at ImageNet/ILSVRC scale. We also run on GTX series cards and GPU-equipped MacBook Pros. We have not encountered any trouble in-house with devices with CUDA capability >= 3.0. All reported hardware issues thus-far have been due to GPU configuration, overheating, and the like. +**Laboratory Tested Hardware**: Berkeley Vision runs Caffe with K40s, K20s, and Titans including models at ImageNet/ILSVRC scale. We also run on GTX series cards (980s and 770s) and GPU-equipped MacBook Pros. We have not encountered any trouble in-house with devices with CUDA capability >= 3.0. All reported hardware issues thus-far have been due to GPU configuration, overheating, and the like. **CUDA compute capability**: devices with compute capability <= 2.0 may have to reduce CUDA thread numbers and batch sizes due to hardware constraints. Your mileage may vary. Once installed, check your times against our [reference performance numbers](performance_hardware.html) to make sure everything is configured properly. -Ask hardware questions on the [caffe-users](https://groups.google.com/forum/#!forum/caffe-users) mailing list. +Ask hardware questions on the [caffe-users group](https://groups.google.com/forum/#!forum/caffe-users). diff --git a/docs/model_zoo.md b/docs/model_zoo.md index 06dc0a49ec7..ad30d0acd55 100644 --- a/docs/model_zoo.md +++ b/docs/model_zoo.md @@ -3,30 +3,28 @@ title: Model Zoo --- # Caffe Model Zoo -Lots of researchers and engineers have made Caffe models for different tasks with all kinds of architectures and data. -These models are learned and applied for problems ranging from simple regression, to large-scale visual classification, to Siamese networks for image similarity, to speech and robotics applications. - -To help share these models, we introduce the model zoo framework: +Lots of people have used Caffe to train models of different architectures and applied to different problems, ranging from simple regression to AlexNet-alikes to Siamese networks for image similarity to speech applications. +To lower the friction of sharing these models, we introduce the model zoo framework: - A standard format for packaging Caffe model info. -- Tools to upload/download model info to/from Github Gists, and to download trained `.caffemodel` binaries. +- Tools to upload/download model info to/from Github Gists, and to download trained `.caffemodel` parameters. - A central wiki page for sharing model info Gists. -## Where to get trained models +## BVLC Reference Models -First of all, we bundle BVLC-trained models for unrestricted, out of the box use. -
    -See the [BVLC model license](#bvlc-model-license) for details. +First of all, we provide some trained models out of the box. Each one of these can be downloaded by running `scripts/download_model_binary.py ` where `` is specified below: -- **BVLC Reference CaffeNet** in `models/bvlc_reference_caffenet`: AlexNet trained on ILSVRC 2012, with a minor variation from the version as described in [ImageNet classification with deep convolutional neural networks](http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks) by Krizhevsky et al. in NIPS 2012. (Trained by Jeff Donahue @jeffdonahue) -- **BVLC AlexNet** in `models/bvlc_alexnet`: AlexNet trained on ILSVRC 2012, almost exactly as described in [ImageNet classification with deep convolutional neural networks](http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks) by Krizhevsky et al. in NIPS 2012. (Trained by Evan Shelhamer @shelhamer) -- **BVLC Reference R-CNN ILSVRC-2013** in `models/bvlc_reference_rcnn_ilsvrc13`: pure Caffe implementation of [R-CNN](https://github.com/rbgirshick/rcnn) as described by Girshick et al. in CVPR 2014. (Trained by Ross Girshick @rbgirshick) -- **BVLC GoogLeNet** in `models/bvlc_googlenet`: GoogLeNet trained on ILSVRC 2012, almost exactly as described in [Going Deeper with Convolutions](http://arxiv.org/abs/1409.4842) by Szegedy et al. in ILSVRC 2014. (Trained by Sergio Guadarrama @sguada) +- **BVLC Reference CaffeNet** in `models/bvlc_reference_caffenet`: AlexNet trained on ILSVRC 2012, with a minor variation from the version as described in the NIPS 2012 paper. (Trained by Jeff Donahue @jeffdonahue) +- **BVLC AlexNet** in `models/bvlc_alexnet`: AlexNet trained on ILSVRC 2012, almost exactly as described in NIPS 2012. (Trained by Evan Shelhamer @shelhamer) +- **BVLC Reference R-CNN ILSVRC-2013** in `models/bvlc_reference_rcnn_ilsvrc13`: pure Caffe implementation of [R-CNN](https://github.com/rbgirshick/rcnn). (Trained by Ross Girshick @rbgirshick) +- **BVLC GoogleNet** in `models/bvlc_googlenet`: GoogleNet trained on ILSVRC 2012, almost exactly as described in [GoogleNet](http://arxiv.org/abs/1409.4842). (Trained by Sergio Guadarrama @sguada) + -**Community models** made by Caffe users are posted to a publicly editable [wiki page](https://github.com/BVLC/caffe/wiki/Model-Zoo). -These models are subject to conditions of their respective authors such as citation and license. -Thank you for sharing your models! +## Community Models + +The publicly-editable [Caffe Model Zoo wiki](https://github.com/BVLC/caffe/wiki/Model-Zoo) catalogues user-made models. +Refer to the model details for authorship and conditions -- please respect licenses and citations. ## Model info format @@ -46,7 +44,7 @@ A caffe model is distributed as a directory containing: Github Gist is a good format for model info distribution because it can contain multiple files, is versionable, and has in-browser syntax highlighting and markdown rendering. -`scripts/upload_model_to_gist.sh ` uploads non-binary files in the model directory as a Github Gist and prints the Gist ID. If `gist_id` is already part of the `/readme.md` frontmatter, then updates existing Gist. +- `scripts/upload_model_to_gist.sh `: uploads non-binary files in the model directory as a Github Gist and prints the Gist ID. If `gist_id` is already part of the `/readme.md` frontmatter, then updates existing Gist. Try doing `scripts/upload_model_to_gist.sh models/bvlc_alexnet` to test the uploading (don't forget to delete the uploaded gist afterward). @@ -58,13 +56,4 @@ It is up to the user where to host the `.caffemodel` file. We host our BVLC-provided models on our own server. Dropbox also works fine (tip: make sure that `?dl=1` is appended to the end of the URL). -`scripts/download_model_binary.py ` downloads the `.caffemodel` from the URL specified in the `/readme.md` frontmatter and confirms SHA1. - -## BVLC model license - -The Caffe models bundled by the BVLC are released for unrestricted use. - -These models are trained on data from the [ImageNet project](http://www.image-net.org/) and training data includes internet photos that may be subject to copyright. - -Our present understanding as researchers is that there is no restriction placed on the open release of these learned model weights, since none of the original images are distributed in whole or in part. -To the extent that the interpretation arises that weights are derivative works of the original copyright holder and they assert such a copyright, UC Berkeley makes no representations as to what use is allowed other than to consider our present release in the spirit of fair use in the academic mission of the university to disseminate knowledge and tools as broadly as possible without restriction. +- `scripts/download_model_binary.py `: downloads the `.caffemodel` from the URL specified in the `/readme.md` frontmatter and confirms SHA1. diff --git a/docs/tutorial/interfaces.md b/docs/tutorial/interfaces.md index 6b0ec347dfe..17430b35c57 100644 --- a/docs/tutorial/interfaces.md +++ b/docs/tutorial/interfaces.md @@ -9,7 +9,13 @@ Caffe has command line, Python, and MATLAB interfaces for day-to-day usage, inte The command line interface -- cmdcaffe -- is the `caffe` tool for model training, scoring, and diagnostics. Run `caffe` without any arguments for help. This tool and others are found in caffe/build/tools. (The following example calls require completing the LeNet / MNIST example first.) -**Training**: `caffe train` learns models from scratch, resumes learning from saved snapshots, and fine-tunes models to new data and tasks. All training requires a solver configuration through the `-solver solver.prototxt` argument. Resuming requires the `-snapshot model_iter_1000.solverstate` argument to load the solver snapshot. Fine-tuning requires the `-weights model.caffemodel` argument for the model initialization. +**Training**: `caffe train` learns models from scratch, resumes learning from saved snapshots, and fine-tunes models to new data and tasks: + +* All training requires a solver configuration through the `-solver solver.prototxt` argument. +* Resuming requires the `-snapshot model_iter_1000.solverstate` argument to load the solver snapshot. +* Fine-tuning requires the `-weights model.caffemodel` argument for the model initialization. + +For example, you can run: # train LeNet caffe train -solver examples/mnist/lenet_solver.prototxt @@ -26,17 +32,19 @@ For a full example of fine-tuning, see examples/finetuning_on_flickr_style, but **Testing**: `caffe test` scores models by running them in the test phase and reports the net output as its score. The net architecture must be properly defined to output an accuracy measure or loss as its output. The per-batch score is reported and then the grand average is reported last. # - # score the learned LeNet model on the validation set as defined in the model architeture lenet_train_test.prototxt - caffe test -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000 -gpu 0 -iterations 100 + # score the learned LeNet model on the validation set as defined in the + # model architeture lenet_train_test.prototxt + caffe test -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000.caffemodel -gpu 0 -iterations 100 **Benchmarking**: `caffe time` benchmarks model execution layer-by-layer through timing and synchronization. This is useful to check system performance and measure relative execution times for models. # (These example calls require you complete the LeNet / MNIST example first.) # time LeNet training on CPU for 10 iterations caffe time -model examples/mnist/lenet_train_test.prototxt -iterations 10 - # time a model architecture with the given weights on the first GPU for 10 iterations # time LeNet training on GPU for the default 50 iterations caffe time -model examples/mnist/lenet_train_test.prototxt -gpu 0 + # time a model architecture with the given weights on the first GPU for 10 iterations + caffe time -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000.caffemodel -gpu 0 -iterations 10 **Diagnostics**: `caffe device_query` reports GPU details for reference and checking device ordinals for running on a given device in multi-GPU machines. diff --git a/docs/tutorial/solver.md b/docs/tutorial/solver.md index 8884ea0e1e8..17f793ef778 100644 --- a/docs/tutorial/solver.md +++ b/docs/tutorial/solver.md @@ -6,7 +6,7 @@ title: Solver / Model Optimization The solver orchestrates model optimization by coordinating the network's forward inference and backward gradients to form parameter updates that attempt to improve the loss. The responsibilities of learning are divided between the Solver for overseeing the optimization and generating parameter updates and the Net for yielding loss and gradients. -The Caffe solvers are Stochastic Gradient Descent (SGD), Adaptive Gradient (ADAGRAD), and Nesterov's Accelerated Gradient (NAG). +The Caffe solvers are Stochastic Gradient Descent (SGD), Adaptive Gradient (ADAGRAD), and Nesterov's Accelerated Gradient (NESTEROV). The solver @@ -126,7 +126,7 @@ Note that in practice, for weights $$ W \in \mathcal{R}^d $$, AdaGrad implementa ### NAG -**Nesterov's accelerated gradient** (`solver_type: NAG`) was proposed by Nesterov [1] as an "optimal" method of convex optimization, achieving a convergence rate of $$ \mathcal{O}(1/t^2) $$ rather than the $$ \mathcal{O}(1/t) $$. +**Nesterov's accelerated gradient** (`solver_type: NESTEROV`) was proposed by Nesterov [1] as an "optimal" method of convex optimization, achieving a convergence rate of $$ \mathcal{O}(1/t^2) $$ rather than the $$ \mathcal{O}(1/t) $$. Though the required assumptions to achieve the $$ \mathcal{O}(1/t^2) $$ convergence typically will not hold for deep networks trained with Caffe (e.g., due to non-smoothness and non-convexity), in practice NAG can be a very effective method for optimizing certain types of deep learning architectures, as demonstrated for deep MNIST autoencoders by Sutskever et al. [2]. The weight update formulas look very similar to the SGD updates given above: diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 055f4ef0d35..f29fc7e5522 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,16 +1,31 @@ -project( Examples ) +file(GLOB_RECURSE examples_srcs "${PROJECT_SOURCE_DIR}/examples/*.cpp") -file(GLOB_RECURSE EXAMPLES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) - -foreach(source ${EXAMPLES_SOURCES}) - # get file name - get_filename_component(name ${source} NAME_WE) +foreach(source_file ${examples_srcs}) + # get file name + get_filename_component(name ${source_file} NAME_WE) - #get folder name - get_filename_component(path ${source} PATH) - get_filename_component(folder ${path} NAME_WE) + # get folder name + get_filename_component(path ${source_file} PATH) + get_filename_component(folder ${path} NAME_WE) - add_executable(${name} ${source}) - target_link_libraries(${name} caffe) - set_target_properties(${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${folder}) -endforeach(source) + add_executable(${name} ${source_file}) + target_link_libraries(${name} ${Caffe_LINK}) + caffe_default_properties(${name}) + + # set back RUNTIME_OUTPUT_DIRECTORY + set_target_properties(${name} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/examples/${folder}") + + caffe_set_solution_folder(${name} examples) + + # install + install(TARGETS ${name} DESTINATION bin) + + if(UNIX OR APPLE) + # Funny command to make tutorials work + # TODO: remove in future as soon as naming is standartaized everywhere + set(__outname ${PROJECT_BINARY_DIR}/examples/${folder}/${name}${CAffe_POSTFIX}) + add_custom_command(TARGET ${name} POST_BUILD + COMMAND ln -sf "${__outname}" "${__outname}.bin") + endif() +endforeach() diff --git a/examples/cifar10/cifar10_full.prototxt b/examples/cifar10/cifar10_full.prototxt index 8bbd30004fd..c16f7dca49f 100644 --- a/examples/cifar10/cifar10_full.prototxt +++ b/examples/cifar10/cifar10_full.prototxt @@ -6,13 +6,17 @@ input_dim: 1 input_dim: 3 input_dim: 32 input_dim: 32 -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 32 pad: 2 @@ -20,9 +24,9 @@ layers { stride: 1 } } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -31,31 +35,35 @@ layers { stride: 2 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "pool1" top: "pool1" } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { - norm_region: WITHIN_CHANNEL local_size: 3 alpha: 5e-05 beta: 0.75 + norm_region: WITHIN_CHANNEL } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 32 pad: 2 @@ -63,15 +71,15 @@ layers { stride: 1 } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -80,21 +88,21 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { - norm_region: WITHIN_CHANNEL local_size: 3 alpha: 5e-05 beta: 0.75 + norm_region: WITHIN_CHANNEL } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" convolution_param { @@ -104,15 +112,15 @@ layers { stride: 1 } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "pool3" - type: POOLING + type: "Pooling" bottom: "conv3" top: "pool3" pooling_param { @@ -121,22 +129,26 @@ layers { stride: 2 } } -layers { +layer { name: "ip1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool3" top: "ip1" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 250 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 250 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 10 } } -layers { +layer { name: "prob" - type: SOFTMAX + type: "Softmax" bottom: "ip1" top: "prob" } diff --git a/examples/cifar10/cifar10_full_train_test.prototxt b/examples/cifar10/cifar10_full_train_test.prototxt index 8fde19f046e..d45fc61e120 100644 --- a/examples/cifar10/cifar10_full_train_test.prototxt +++ b/examples/cifar10/cifar10_full_train_test.prototxt @@ -1,39 +1,49 @@ name: "CIFAR10_full" -layers { +layer { name: "cifar" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/cifar10/cifar10_train_leveldb" - batch_size: 100 + include { + phase: TRAIN } transform_param { mean_file: "examples/cifar10/mean.binaryproto" } - include: { phase: TRAIN } + data_param { + source: "examples/cifar10/cifar10_train_lmdb" + batch_size: 100 + backend: LMDB + } } -layers { +layer { name: "cifar" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/cifar10/cifar10_test_leveldb" - batch_size: 100 + include { + phase: TEST } transform_param { mean_file: "examples/cifar10/mean.binaryproto" } - include: { phase: TEST } + data_param { + source: "examples/cifar10/cifar10_test_lmdb" + batch_size: 100 + backend: LMDB + } } -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 32 pad: 2 @@ -48,9 +58,9 @@ layers { } } } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -59,31 +69,35 @@ layers { stride: 2 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "pool1" top: "pool1" } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { - norm_region: WITHIN_CHANNEL local_size: 3 alpha: 5e-05 beta: 0.75 + norm_region: WITHIN_CHANNEL } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 32 pad: 2 @@ -98,15 +112,15 @@ layers { } } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -115,21 +129,21 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { - norm_region: WITHIN_CHANNEL local_size: 3 alpha: 5e-05 beta: 0.75 + norm_region: WITHIN_CHANNEL } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" convolution_param { @@ -146,15 +160,15 @@ layers { } } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "pool3" - type: POOLING + type: "Pooling" bottom: "conv3" top: "pool3" pooling_param { @@ -163,15 +177,19 @@ layers { stride: 2 } } -layers { +layer { name: "ip1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool3" top: "ip1" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 250 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 250 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 10 weight_filler { @@ -183,17 +201,19 @@ layers { } } } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "ip1" bottom: "label" top: "accuracy" - include: { phase: TEST } + include { + phase: TEST + } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "ip1" bottom: "label" top: "loss" diff --git a/examples/cifar10/cifar10_quick.prototxt b/examples/cifar10/cifar10_quick.prototxt index 505158f7a34..1ad190e185f 100644 --- a/examples/cifar10/cifar10_quick.prototxt +++ b/examples/cifar10/cifar10_quick.prototxt @@ -4,13 +4,17 @@ input_dim: 1 input_dim: 3 input_dim: 32 input_dim: 32 -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 32 pad: 2 @@ -18,9 +22,9 @@ layers { stride: 1 } } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -29,19 +33,23 @@ layers { stride: 2 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "pool1" top: "pool1" } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "pool1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 32 pad: 2 @@ -49,15 +57,15 @@ layers { stride: 1 } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -66,13 +74,17 @@ layers { stride: 2 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "pool2" top: "conv3" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 64 pad: 2 @@ -80,15 +92,15 @@ layers { stride: 1 } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "pool3" - type: POOLING + type: "Pooling" bottom: "conv3" top: "pool3" pooling_param { @@ -97,31 +109,39 @@ layers { stride: 2 } } -layers { +layer { name: "ip1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool3" top: "ip1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 64 } } -layers { +layer { name: "ip2" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip1" top: "ip2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 10 } } -layers { +layer { name: "prob" - type: SOFTMAX + type: "Softmax" bottom: "ip2" top: "prob" } diff --git a/examples/cifar10/cifar10_quick_train_test.prototxt b/examples/cifar10/cifar10_quick_train_test.prototxt index 409cfe809f4..2317739353e 100644 --- a/examples/cifar10/cifar10_quick_train_test.prototxt +++ b/examples/cifar10/cifar10_quick_train_test.prototxt @@ -1,39 +1,49 @@ name: "CIFAR10_quick" -layers { +layer { name: "cifar" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/cifar10/cifar10_train_leveldb" - batch_size: 100 + include { + phase: TRAIN } transform_param { mean_file: "examples/cifar10/mean.binaryproto" } - include: { phase: TRAIN } + data_param { + source: "examples/cifar10/cifar10_train_lmdb" + batch_size: 100 + backend: LMDB + } } -layers { +layer { name: "cifar" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/cifar10/cifar10_test_leveldb" - batch_size: 100 + include { + phase: TEST } transform_param { mean_file: "examples/cifar10/mean.binaryproto" } - include: { phase: TEST } + data_param { + source: "examples/cifar10/cifar10_test_lmdb" + batch_size: 100 + backend: LMDB + } } -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 32 pad: 2 @@ -48,9 +58,9 @@ layers { } } } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -59,19 +69,23 @@ layers { stride: 2 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "pool1" top: "pool1" } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "pool1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 32 pad: 2 @@ -86,15 +100,15 @@ layers { } } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -103,13 +117,17 @@ layers { stride: 2 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "pool2" top: "conv3" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 64 pad: 2 @@ -124,15 +142,15 @@ layers { } } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "pool3" - type: POOLING + type: "Pooling" bottom: "conv3" top: "pool3" pooling_param { @@ -141,13 +159,17 @@ layers { stride: 2 } } -layers { +layer { name: "ip1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool3" top: "ip1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 64 weight_filler { @@ -159,13 +181,17 @@ layers { } } } -layers { +layer { name: "ip2" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip1" top: "ip2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 10 weight_filler { @@ -177,17 +203,19 @@ layers { } } } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" - include: { phase: TEST } + include { + phase: TEST + } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss" diff --git a/examples/cifar10/convert_cifar_data.cpp b/examples/cifar10/convert_cifar_data.cpp index 90ecb6d9a88..f4c42e4d2e7 100644 --- a/examples/cifar10/convert_cifar_data.cpp +++ b/examples/cifar10/convert_cifar_data.cpp @@ -9,14 +9,18 @@ #include // NOLINT(readability/streams) #include +#include "boost/scoped_ptr.hpp" #include "glog/logging.h" #include "google/protobuf/text_format.h" -#include "leveldb/db.h" #include "stdint.h" #include "caffe/proto/caffe.pb.h" +#include "caffe/util/db.hpp" +using caffe::Datum; +using boost::scoped_ptr; using std::string; +namespace db = caffe::db; const int kCIFARSize = 32; const int kCIFARImageNBytes = 3072; @@ -31,26 +35,20 @@ void read_image(std::ifstream* file, int* label, char* buffer) { return; } -void convert_dataset(const string& input_folder, const string& output_folder) { - // Leveldb options - leveldb::Options options; - options.create_if_missing = true; - options.error_if_exists = true; +void convert_dataset(const string& input_folder, const string& output_folder, + const string& db_type) { + scoped_ptr train_db(db::GetDB(db_type)); + train_db->Open(output_folder + "/cifar10_train_" + db_type, db::NEW); + scoped_ptr txn(train_db->NewTransaction()); // Data buffer int label; char str_buffer[kCIFARImageNBytes]; - string value; - caffe::Datum datum; + Datum datum; datum.set_channels(3); datum.set_height(kCIFARSize); datum.set_width(kCIFARSize); LOG(INFO) << "Writing Training data"; - leveldb::DB* train_db; - leveldb::Status status; - status = leveldb::DB::Open(options, output_folder + "/cifar10_train_leveldb", - &train_db); - CHECK(status.ok()) << "Failed to open leveldb."; for (int fileid = 0; fileid < kCIFARTrainBatches; ++fileid) { // Open files LOG(INFO) << "Training Batch " << fileid + 1; @@ -62,17 +60,20 @@ void convert_dataset(const string& input_folder, const string& output_folder) { read_image(&data_file, &label, str_buffer); datum.set_label(label); datum.set_data(str_buffer, kCIFARImageNBytes); - datum.SerializeToString(&value); - snprintf(str_buffer, kCIFARImageNBytes, "%05d", + int length = snprintf(str_buffer, kCIFARImageNBytes, "%05d", fileid * kCIFARBatchSize + itemid); - train_db->Put(leveldb::WriteOptions(), string(str_buffer), value); + string out; + CHECK(datum.SerializeToString(&out)); + txn->Put(string(str_buffer, length), out); } } + txn->Commit(); + train_db->Close(); LOG(INFO) << "Writing Testing data"; - leveldb::DB* test_db; - CHECK(leveldb::DB::Open(options, output_folder + "/cifar10_test_leveldb", - &test_db).ok()) << "Failed to open leveldb."; + scoped_ptr test_db(db::GetDB(db_type)); + test_db->Open(output_folder + "/cifar10_test_" + db_type, db::NEW); + txn.reset(test_db->NewTransaction()); // Open files std::ifstream data_file((input_folder + "/test_batch.bin").c_str(), std::ios::in | std::ios::binary); @@ -81,28 +82,28 @@ void convert_dataset(const string& input_folder, const string& output_folder) { read_image(&data_file, &label, str_buffer); datum.set_label(label); datum.set_data(str_buffer, kCIFARImageNBytes); - datum.SerializeToString(&value); - snprintf(str_buffer, kCIFARImageNBytes, "%05d", itemid); - test_db->Put(leveldb::WriteOptions(), string(str_buffer), value); + int length = snprintf(str_buffer, kCIFARImageNBytes, "%05d", itemid); + string out; + CHECK(datum.SerializeToString(&out)); + txn->Put(string(str_buffer, length), out); } - - delete train_db; - delete test_db; + txn->Commit(); + test_db->Close(); } int main(int argc, char** argv) { - if (argc != 3) { + if (argc != 4) { printf("This script converts the CIFAR dataset to the leveldb format used\n" "by caffe to perform classification.\n" "Usage:\n" - " convert_cifar_data input_folder output_folder\n" + " convert_cifar_data input_folder output_folder db_type\n" "Where the input folder should contain the binary batch files.\n" "The CIFAR dataset could be downloaded at\n" " http://www.cs.toronto.edu/~kriz/cifar.html\n" "You should gunzip them after downloading.\n"); } else { google::InitGoogleLogging(argv[0]); - convert_dataset(string(argv[1]), string(argv[2])); + convert_dataset(string(argv[1]), string(argv[2]), string(argv[3])); } return 0; } diff --git a/examples/cifar10/create_cifar10.sh b/examples/cifar10/create_cifar10.sh index dfba7cca48a..a42725cb610 100755 --- a/examples/cifar10/create_cifar10.sh +++ b/examples/cifar10/create_cifar10.sh @@ -3,16 +3,17 @@ EXAMPLE=examples/cifar10 DATA=data/cifar10 +DBTYPE=lmdb -echo "Creating leveldb..." +echo "Creating $DBTYPE..." -rm -rf $EXAMPLE/cifar10_train_leveldb $EXAMPLE/cifar10_test_leveldb +rm -rf $EXAMPLE/cifar10_train_$DBTYPE $EXAMPLE/cifar10_test_$DBTYPE -./build/examples/cifar10/convert_cifar_data.bin $DATA $EXAMPLE +./build/examples/cifar10/convert_cifar_data.bin $DATA $EXAMPLE $DBTYPE echo "Computing image mean..." -./build/tools/compute_image_mean $EXAMPLE/cifar10_train_leveldb \ - $EXAMPLE/mean.binaryproto leveldb +./build/tools/compute_image_mean -backend=$DBTYPE \ + $EXAMPLE/cifar10_train_$DBTYPE $EXAMPLE/mean.binaryproto echo "Done." diff --git a/examples/classification.ipynb b/examples/classification.ipynb index 6b6c5488d36..6f8fa4252e6 100644 --- a/examples/classification.ipynb +++ b/examples/classification.ipynb @@ -4,7 +4,7 @@ "example_name": "ImageNet classification", "include_in_docs": true, "priority": 1, - "signature": "sha256:2caae2c1fe3e282b8f836d380a45622351c91db18a1591e4f2fa67faba9ab72c" + "signature": "sha256:918b797b1b7d78125c8f1e3c84756b0679120cbe1071ce7fee7aeafef0fbae55" }, "nbformat": 3, "nbformat_minor": 0, @@ -64,10 +64,9 @@ "cell_type": "code", "collapsed": false, "input": [ - "caffe.set_phase_test()\n", "caffe.set_mode_cpu()\n", "net = caffe.Classifier(MODEL_FILE, PRETRAINED,\n", - " mean=np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy'),\n", + " mean=np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1),\n", " channel_swap=(2,1,0),\n", " raw_scale=255,\n", " image_dims=(256, 256))" @@ -237,7 +236,7 @@ "# Resize the image to the standard (256, 256) and oversample net input sized crops.\n", "input_oversampled = caffe.io.oversample([caffe.io.resize_image(input_image, net.image_dims)], net.crop_dims)\n", "# 'data' is the input blob name in the model definition, so we preprocess for that input.\n", - "caffe_input = np.asarray([net.preprocess('data', in_) for in_ in input_oversampled])\n", + "caffe_input = np.asarray([net.transformer.preprocess('data', in_) for in_ in input_oversampled])\n", "# forward() takes keyword args for the input blobs with preprocessed input arrays.\n", "%timeit net.forward(data=caffe_input)" ], diff --git a/examples/detection.ipynb b/examples/detection.ipynb index d05c0c22052..2ccf21f09eb 100644 --- a/examples/detection.ipynb +++ b/examples/detection.ipynb @@ -3,7 +3,8 @@ "description": "Run a pretrained model as a detector in Python.", "example_name": "R-CNN detection", "include_in_docs": true, - "priority": 3 + "priority": 3, + "signature": "sha256:5d53dc49c9b6b93c1a2714c99043a763029ec98aebfb44acfa8d9e61781c9499" }, "nbformat": 3, "nbformat_minor": 0, @@ -37,7 +38,7 @@ "input": [ "!mkdir -p _temp\n", "!echo `pwd`/images/fish-bike.jpg > _temp/det_input.txt\n", - "!../python/detect.py --crop_mode=selective_search --pretrained_model=models/bvlc_reference_rcnn_ilsvrc13/bvlc_reference_rcnn_ilsvrc13.caffemodel --model_def=models/bvlc_reference_rcnn_ilsvrc13/deploy.prototxt --gpu --raw_scale=255 _temp/det_input.txt _temp/det_output.h5" + "!../python/detect.py --crop_mode=selective_search --pretrained_model=../models/bvlc_reference_rcnn_ilsvrc13/bvlc_reference_rcnn_ilsvrc13.caffemodel --model_def=../models/bvlc_reference_rcnn_ilsvrc13/deploy.prototxt --gpu --raw_scale=255 _temp/det_input.txt _temp/det_output.h5" ], "language": "python", "metadata": {}, @@ -47,52 +48,60 @@ "stream": "stdout", "text": [ "WARNING: Logging before InitGoogleLogging() is written to STDERR\r\n", - "I0610 10:12:49.299607 25530 net.cpp:36] Initializing net from parameters: \r\n", + "I0218 20:43:25.383932 2099749632 net.cpp:42] Initializing net from parameters: \r\n", "name: \"R-CNN-ilsvrc13\"\r\n", - "layers {\r\n", + "input: \"data\"\r\n", + "input_dim: 10\r\n", + "input_dim: 3\r\n", + "input_dim: 227\r\n", + "input_dim: 227\r\n", + "state {\r\n", + " phase: TEST\r\n", + "}\r\n", + "layer {\r\n", + " name: \"conv1\"\r\n", + " type: \"Convolution\"\r\n", " bottom: \"data\"\r\n", " top: \"conv1\"\r\n", - " name: \"conv1\"\r\n", - " type: CONVOLUTION\r\n", " convolution_param {\r\n", " num_output: 96\r\n", " kernel_size: 11\r\n", " stride: 4\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"relu1\"\r\n", + " type: \"ReLU\"\r\n", " bottom: \"conv1\"\r\n", " top: \"conv1\"\r\n", - " name: \"relu1\"\r\n", - " type: RELU\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"pool1\"\r\n", + " type: \"Pooling\"\r\n", " bottom: \"conv1\"\r\n", " top: \"pool1\"\r\n", - " name: \"pool1\"\r\n", - " type: POOLING\r\n", " pooling_param {\r\n", " pool: MAX\r\n", " kernel_size: 3\r\n", " stride: 2\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"norm1\"\r\n", + " type: \"LRN\"\r\n", " bottom: \"pool1\"\r\n", " top: \"norm1\"\r\n", - " name: \"norm1\"\r\n", - " type: LRN\r\n", " lrn_param {\r\n", " local_size: 5\r\n", " alpha: 0.0001\r\n", " beta: 0.75\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"conv2\"\r\n", + " type: \"Convolution\"\r\n", " bottom: \"norm1\"\r\n", " top: \"conv2\"\r\n", - " name: \"conv2\"\r\n", - " type: CONVOLUTION\r\n", " convolution_param {\r\n", " num_output: 256\r\n", " pad: 2\r\n", @@ -100,56 +109,57 @@ " group: 2\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r", + "\r\n", + " name: \"relu2\"\r\n", + " type: \"ReLU\"\r\n", " bottom: \"conv2\"\r\n", " top: \"conv2\"\r\n", - " name: \"relu2\"\r\n", - " type: RELU\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"pool2\"\r\n", + " type: \"Pooling\"\r\n", " bottom: \"conv2\"\r\n", " top: \"pool2\"\r\n", - " name: \"pool2\"\r\n", - " type: POOLING\r\n", " pooling_param {\r\n", " pool: MAX\r\n", " kernel_size: 3\r\n", " stride: 2\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"norm2\"\r\n", + " type: \"LRN\"\r\n", " bottom: \"pool2\"\r\n", " top: \"norm2\"\r\n", - " name: \"norm2\"\r\n", - " type: LRN\r\n", " lrn_param {\r\n", " local_size: 5\r\n", " alpha: 0.0001\r\n", " beta: 0.75\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"conv3\"\r\n", + " type: \"Convolution\"\r\n", " bottom: \"norm2\"\r\n", " top: \"conv3\"\r\n", - " name: \"conv3\"\r\n", - " type: CONVOLUTION\r\n", " convolution_param {\r\n", " num_output: 384\r\n", " pad: 1\r\n", " kernel_size: 3\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"relu3\"\r\n", + " type: \"ReLU\"\r\n", " bottom: \"conv3\"\r\n", " top: \"conv3\"\r\n", - " name: \"relu3\"\r\n", - " type: RELU\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"conv4\"\r\n", + " type: \"Convolution\"\r\n", " bottom: \"conv3\"\r\n", " top: \"conv4\"\r\n", - " name: \"conv4\"\r\n", - " type: CONVOLUTION\r\n", " convolution_param {\r\n", " num_output: 384\r\n", " pad: 1\r\n", @@ -157,17 +167,17 @@ " group: 2\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"relu4\"\r\n", + " type: \"ReLU\"\r\n", " bottom: \"conv4\"\r\n", " top: \"conv4\"\r\n", - " name: \"relu4\"\r\n", - " type: RELU\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"conv5\"\r\n", + " type: \"Convolution\"\r\n", " bottom: \"conv4\"\r\n", " top: \"conv5\"\r\n", - " name: \"conv5\"\r\n", - " type: CONVOLUTION\r\n", " convolution_param {\r\n", " num_output: 256\r\n", " pad: 1\r\n", @@ -175,205 +185,265 @@ " group: 2\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"relu5\"\r\n", + " type: \"ReLU\"\r\n", " bottom: \"conv5\"\r\n", " top: \"conv5\"\r\n", - " name: \"relu5\"\r\n", - " type: RELU\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"pool5\"\r\n", + " type: \"Pooling\"\r\n", " bottom: \"conv5\"\r\n", " top: \"pool5\"\r\n", - " name: \"pool5\"\r\n", - " type: POOLING\r\n", " pooling_param {\r\n", " pool: MAX\r\n", " kernel_size: 3\r\n", " stride: 2\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"fc6\"\r\n", + " type: \"InnerProduct\"\r\n", " bottom: \"pool5\"\r\n", " top: \"fc6\"\r\n", - " name: \"fc6\"\r\n", - " type: INNER_PRODUCT\r\n", " inner_product_param {\r\n", " num_output: 4096\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"relu6\"\r\n", + " type: \"ReLU\"\r\n", " bottom: \"fc6\"\r\n", " top: \"fc6\"\r\n", - " name: \"relu6\"\r\n", - " type: RELU\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"drop6\"\r\n", + " type: \"Dropout\"\r\n", " bottom: \"fc6\"\r\n", " top: \"fc6\"\r\n", - " name: \"drop6\"\r\n", - " type: DROPOUT\r\n", " dropout_param {\r\n", " dropout_ratio: 0.5\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"fc7\"\r\n", + " type: \"InnerProduct\"\r\n", " bottom: \"fc6\"\r\n", " top: \"fc7\"\r\n", - " name: \"fc7\"\r\n", - " type: INNER_PRODUCT\r\n", " inner_product_param {\r\n", " num_output: 4096\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"relu7\"\r\n", + " type: \"ReLU\"\r\n", " bottom: \"fc7\"\r\n", " top: \"fc7\"\r\n", - " name: \"relu7\"\r\n", - " type: RELU\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"drop7\"\r\n", + " type: \"Dropout\"\r\n", " bottom: \"fc7\"\r\n", " top: \"fc7\"\r\n", - " name: \"drop7\"\r\n", - " type: DROPOUT\r\n", " dropout_param {\r\n", " dropout_ratio: 0.5\r\n", " }\r\n", "}\r\n", - "layers {\r\n", + "layer {\r\n", + " name: \"fc-rcnn\"\r\n", + " type: \"InnerProduct\"\r\n", " bottom: \"fc7\"\r\n", " top: \"fc-rcnn\"\r\n", - " name: \"fc-rcnn\"\r\n", - " type: INNER_PRODUCT\r\n", " inner_product_param {\r\n", " num_output: 200\r\n", " }\r\n", "}\r\n", - "input: \"data\"\r\n", - "input_dim: 10\r\n", - "input_dim: 3\r\n", - "input_dim: 227\r\n", - "input_dim: 227\r\n", - "I0610 10:12:49.300204 25530 net.cpp:77] Creating Layer conv1\r\n", - "I0610 10:12:49.300214 25530 net.cpp:87] conv1 <- data\r\n", - "I0610 10:12:49.300220 25530 net.cpp:113] conv1 -> conv1\r\n", - "I0610 10:12:49.300283 25530 net.cpp:128] Top shape: 10 96 55 55 (2904000)\r\n", - "I0610 10:12:49.300294 25530 net.cpp:154] conv1 needs backward computation.\r\n", - "I0610 10:12:49.300302 25530 net.cpp:77] Creating Layer relu1\r\n", - "I0610 10:12:49.300308 25530 net.cpp:87] relu1 <- conv1\r\n", - "I0610 10:12:49.300314 25530 net.cpp:101] relu1 -> conv1 (in-place)\r\n", - "I0610 10:12:49.300323 25530 net.cpp:128] Top shape: 10 96 55 55 (2904000)\r\n", - "I0610 10:12:49.300328 25530 net.cpp:154] relu1 needs backward computation.\r\n", - "I0610 10:12:49.300335 25530 net.cpp:77] Creating Layer pool1\r\n", - "I0610 10:12:49.300341 25530 net.cpp:87] pool1 <- conv1\r\n", - "I0610 10:12:49.300348 25530 net.cpp:113] pool1 -> pool1\r\n", - "I0610 10:12:49.300357 25530 net.cpp:128] Top shape: 10 96 27 27 (699840)\r\n", - "I0610 10:12:49.300365 25530 net.cpp:154] pool1 needs backward computation.\r\n", - "I0610 10:12:49.300372 25530 net.cpp:77] Creating Layer norm1\r\n", - "I0610 10:12:49.300379 25530 net.cpp:87] norm1 <- pool1\r\n", - "I0610 10:12:49.300384 25530 net.cpp:113] norm1 -> norm1\r\n", - "I0610 10:12:49.300393 25530 net.cpp:128] Top shape: 10 96 27 27 (699840)\r\n", - "I0610 10:12:49.300400 25530 net.cpp:154] norm1 needs backward computation.\r\n", - "I0610 10:12:49.300406 25530 net.cpp:77] Creating Layer conv2\r\n", - "I0610 10:12:49.300412 25530 net.cpp:87] conv2 <- norm1\r\n", - "I0610 10:12:49.300420 25530 net.cpp:113] conv2 -> conv2\r\n", - "I0610 10:12:49.300925 25530 net.cpp:128] Top shape: 10 256 27 27 (1866240)\r\n", - "I0610 10:12:49.300935 25530 net.cpp:154] conv2 needs backward computation.\r\n", - "I0610 10:12:49.300941 25530 net.cpp:77] Creating Layer relu2\r\n", - "I0610 10:12:49.300947 25530 net.cpp:87] relu2 <- conv2\r\n", - "I0610 10:12:49.300954 25530 net.cpp:101] relu2 -> conv2 (in-place)\r\n", - "I0610 10:12:49.300961 25530 net.cpp:128] Top shape: 10 256 27 27 (1866240)\r\n", - "I0610 10:12:49.300967 25530 net.cpp:154] relu2 needs backward computation.\r\n", - "I0610 10:12:49.300974 25530 net.cpp:77] Creating Layer pool2\r\n", - "I0610 10:12:49.300981 25530 net.cpp:87] pool2 <- conv2\r\n", - "I0610 10:12:49.300987 25530 net.cpp:113] pool2 -> pool2\r\n", - "I0610 10:12:49.300994 25530 net.cpp:128] Top shape: 10 256 13 13 (432640)\r\n", - "I0610 10:12:49.301000 25530 net.cpp:154] pool2 needs backward computation.\r\n", - "I0610 10:12:49.301007 25530 net.cpp:77] Creating Layer norm2\r\n", - "I0610 10:12:49.301013 25530 net.cpp:87] norm2 <- pool2\r\n", - "I0610 10:12:49.301019 25530 net.cpp:113] norm2 -> norm2\r\n", - "I0610 10:12:49.301026 25530 net.cpp:128] Top shape: 10 256 13 13 (432640)\r\n", - "I0610 10:12:49.301033 25530 net.cpp:154] norm2 needs backward computation.\r\n", - "I0610 10:12:49.301041 25530 net.cpp:77] Creating Layer conv3\r\n", - "I0610 10:12:49.301048 25530 net.cpp:87] conv3 <- norm2\r\n", - "I0610 10:12:49.301054 25530 net.cpp:113] conv3 -> conv3\r\n", - "I0610 10:12:49.302455 25530 net.cpp:128] Top shape: 10 384 13 13 (648960)\r\n", - "I0610 10:12:49.302467 25530 net.cpp:154] conv3 needs backward computation.\r\n", - "I0610 10:12:49.302477 25530 net.cpp:77] Creating Layer relu3\r\n", - "I0610 10:12:49.302484 25530 net.cpp:87] relu3 <- conv3\r\n", - "I0610 10:12:49.302490 25530 net.cpp:101] relu3 -> conv3 (in-place)\r\n", - "I0610 10:12:49.302496 25530 net.cpp:128] Top shape: 10 384 13 13 (648960)\r\n", - "I0610 10:12:49.302503 25530 net.cpp:154] relu3 needs backward computation.\r\n", - "I0610 10:12:49.302510 25530 net.cpp:77] Creating Layer conv4\r\n", - "I0610 10:12:49.302515 25530 net.cpp:87] conv4 <- conv3\r\n", - "I0610 10:12:49.302521 25530 net.cpp:113] conv4 -> conv4\r\n", - "I0610 10:12:49.303639 25530 net.cpp:128] Top shape: 10 384 13 13 (648960)\r\n", - "I0610 10:12:49.303650 25530 net.cpp:154] conv4 needs backward computation.\r\n", - "I0610 10:12:49.303658 25530 net.cpp:77] Creating Layer relu4\r\n", - "I0610 10:12:49.303663 25530 net.cpp:87] relu4 <- conv4\r\n", - "I0610 10:12:49.303670 25530 net.cpp:101] relu4 -> conv4 (in-place)\r\n", - "I0610 10:12:49.303676 25530 net.cpp:128] Top shape: 10 384 13 13 (648960)\r\n", - "I0610 10:12:49.303683 25530 net.cpp:154] relu4 needs backward computation.\r\n", - "I0610 10:12:49.303691 25530 net.cpp:77] Creating Layer conv5\r\n", - "I0610 10:12:49.303697 25530 net.cpp:87] conv5 <- conv4\r\n", - "I0610 10:12:49.303704 25530 net.cpp:113] conv5 -> conv5\r\n", - "I0610 10:12:49.304410 25530 net.cpp:128] Top shape: 10 256 13 13 (432640)\r\n", - "I0610 10:12:49.304420 25530 net.cpp:154] conv5 needs backward computation.\r\n", - "I0610 10:12:49.304427 25530 net.cpp:77] Creating Layer relu5\r\n", - "I0610 10:12:49.304433 25530 net.cpp:87] relu5 <- conv5\r\n", - "I0610 10:12:49.304440 25530 net.cpp:101] relu5 -> conv5 (in-place)\r\n", - "I0610 10:12:49.304446 25530 net.cpp:128] Top shape: 10 256 13 13 (432640)\r\n", - "I0610 10:12:49.304471 25530 net.cpp:154] relu5 needs backward computation.\r\n", - "I0610 10:12:49.304478 25530 net.cpp:77] Creating Layer pool5\r\n", - "I0610 10:12:49.304484 25530 net.cpp:87] pool5 <- conv5\r\n", - "I0610 10:12:49.304491 25530 net.cpp:113] pool5 -> pool5\r\n", - "I0610 10:12:49.304498 25530 net.cpp:128] Top shape: 10 256 6 6 (92160)\r\n", - "I0610 10:12:49.304504 25530 net.cpp:154] pool5 needs backward computation.\r\n", - "I0610 10:12:49.304512 25530 net.cpp:77] Creating Layer fc6\r\n", - "I0610 10:12:49.304517 25530 net.cpp:87] fc6 <- pool5\r\n", - "I0610 10:12:49.304523 25530 net.cpp:113] fc6 -> fc6\r\n" + "I0218 20:43:25.385720 2099749632 net.cpp:336] Input 0 -> data\r\n", + "I0218 20:43:25.385769 2099749632 layer_factory.hpp:74] Creating layer conv1\r\n", + "I0218 20:43:25.385783 2099749632 net.cpp:76] Creating Layer conv1\r\n", + "I0218 20:43:25.385790 2099749632 net.cpp:372] conv1 <- data\r\n", + "I0218 20:43:25.385802 2099749632 net.cpp:334] conv1 -> conv1\r\n", + "I0218 20:43:25.385815 2099749632 net.cpp:105] Setting up conv1\r\n", + "I0218 20:43:25.386574 2099749632 net.cpp:112] Top shape: 10 96 55 55 (2904000)\r\n", + "I0218 20:43:25.386610 2099749632 layer_factory.hpp:74] Creating layer relu1\r\n", + "I0218 20:43:25.386625 2099749632 net.cpp:76] Creating Layer relu1\r\n", + "I0218 20:43:25.386631 2099749632 net.cpp:372] relu1 <- conv1\r\n", + "I0218 20:43:25.386641 2099749632 net.cpp:323] relu1 -> conv1 (in-place)\r\n", + "I0218 20:43:25.386649 2099749632 net.cpp:105] Setting up relu1\r\n", + "I0218 20:43:25.386656 2099749632 net.cpp:112] Top shape: 10 96 55 55 (2904000)\r\n", + "I0218 20:43:25.386663 2099749632 layer_factory.hpp:74] Creating layer pool1\r\n", + "I0218 20:43:25.386675 2099749632 net.cpp:76] Creating Layer pool1\r\n", + "I0218 20:43:25.386682 2099749632 net.cpp:372] pool1 <- conv1\r\n", + "I0218 20:43:25.386690 2099749632 net.cpp:334] pool1 -> pool1\r\n", + "I0218 20:43:25.386699 2099749632 net.cpp:105] Setting up pool1\r\n", + "I0218 20:43:25.386716 2099749632 net.cpp:112] Top shape: 10 96 27 27 (699840)\r\n", + "I0218 20:43:25.386725 2099749632 layer_factory.hpp:74] Creating layer norm1\r\n", + "I0218 20:43:25.386736 2099749632 net.cpp:76] Creating Layer norm1\r\n", + "I0218 20:43:25.386744 2099749632 net.cpp:372] norm1 <- pool1\r\n", + "I0218 20:43:25.386803 2099749632 net.cpp:334] norm1 -> norm1\r\n", + "I0218 20:43:25.386819 2099749632 net.cpp:105] Setting up norm1\r\n", + "I0218 20:43:25.386832 2099749632 net.cpp:112] Top shape: 10 96 27 27 (699840)\r\n", + "I0218 20:43:25.386842 2099749632 layer_factory.hpp:74] Creating layer conv2\r\n", + "I0218 20:43:25.386852 2099749632 net.cpp:76] Creating Layer conv2\r\n", + "I0218 20:43:25.386865 2099749632 net.cpp:372] conv2 <- norm1\r\n", + "I0218 20:43:25.386878 2099749632 net.cpp:334] conv2 -> conv2\r\n", + "I0218 20:43:25.386899 2099749632 net.cpp:105] Setting up conv2\r\n", + "I0218 20:43:25.387024 2099749632 net.cpp:112] Top shape: 10 256 27 27 (1866240)\r\n", + "I0218 20:43:25.387042 2099749632 layer_factory.hpp:74] Creating layer relu2\r\n", + "I0218 20:43:25.387050 2099749632 net.cpp:76] Creating Layer relu2\r\n", + "I0218 20:43:25.387058 2099749632 net.cpp:372] relu2 <- conv2\r\n", + "I0218 20:43:25.387066 2099749632 net.cpp:323] relu2 -> conv2 (in-place)\r\n", + "I0218 20:43:25.387075 2099749632 net.cpp:105] Setting up relu2\r\n", + "I0218 20:43:25.387081 2099749632 net.cpp:112] Top shape: 10 256 27 27 (1866240)\r\n", + "I0218 20:43:25.387089 2099749632 layer_factory.hpp:74] Creating layer pool2\r\n", + "I0218 20:43:25.387097 2099749632 net.cpp:76] Creating Layer pool2\r\n", + "I0218 20:43:25.387104 2099749632 net.cpp:372] pool2 <- conv2\r\n", + "I0218 20:43:25.387112 2099749632 net.cpp:334] pool2 -> pool2\r\n", + "I0218 20:43:25.387121 2099749632 net.cpp:105] Setting up pool2\r\n", + "I0218 20:43:25.387130 2099749632 net.cpp:112] Top shape: 10 256 13 13 (432640)\r\n", + "I0218 20:43:25.387137 2099749632 layer_factory.hpp:74] Creating layer norm2\r\n", + "I0218 20:43:25.387145 2099749632 net.cpp:76] Creating Layer norm2\r\n", + "I0218 20:43:25.387152 2099749632 net.cpp:372] norm2 <- pool2\r\n", + "I0218 20:43:25.387161 2099749632 net.cpp:334] norm2 -> norm2\r\n", + "I0218 20:43:25.387168 2099749632 net.cpp:105] Setting up norm2\r\n", + "I0218 20:43:25.387176 2099749632 net.cpp:112] Top shape: 10 256 13 13 (432640)\r\n", + "I0218 20:43:25.387228 2099749632 layer_factory.hpp:74] Creating layer conv3\r\n", + "I0218 20:43:25.387249 2099749632 net.cpp:76] Creating Layer conv3\r\n", + "I0218 20:43:25.387258 2099749632 net.cpp:372] conv3 <- norm2\r\n", + "I0218 20:43:25.387266 2099749632 net.cpp:334] conv3 -> conv3\r\n", + "I0218 20:43:25.387276 2099749632 net.cpp:105] Setting up conv3\r\n", + "I0218 20:43:25.389375 2099749632 net.cpp:112] Top shape: 10 384 13 13 (648960)\r\n", + "I0218 20:43:25.389408 2099749632 layer_factory.hpp:74] Creating layer relu3\r\n", + "I0218 20:43:25.389421 2099749632 net.cpp:76] Creating Layer relu3\r\n", + "I0218 20:43:25.389430 2099749632 net.cpp:372] relu3 <- conv3\r\n", + "I0218 20:43:25.389438 2099749632 net.cpp:323] relu3 -> conv3 (in-place)\r\n", + "I0218 20:43:25.389447 2099749632 net.cpp:105] Setting up relu3\r\n", + "I0218 20:43:25.389456 2099749632 net.cpp:112] Top shape: 10 384 13 13 (648960)\r\n", + "I0218 20:43:25.389462 2099749632 layer_factory.hpp:74] Creating layer conv4\r\n", + "I0218 20:43:25.389472 2099749632 net.cpp:76] Creating Layer conv4\r\n", + "I0218 20:43:25.389478 2099749632 net.cpp:372] conv4 <- conv3\r\n", + "I0218 20:43:25.389487 2099749632 net.cpp:334] conv4 -> conv4\r\n", + "I0218 20:43:25.389497 2099749632 net.cpp:105] Setting up conv4\r\n", + "I0218 20:43:25.391810 2099749632 net.cpp:112] Top shape: 10 384 13 13 (648960)\r\n", + "I0218 20:43:25.391856 2099749632 layer_factory.hpp:74] Creating layer relu4\r\n", + "I0218 20:43:25.391871 2099749632 net.cpp:76] Creating Layer relu4\r\n", + "I0218 20:43:25.391880 2099749632 net.cpp:372] relu4 <- conv4\r\n", + "I0218 20:43:25.391888 2099749632 net.cpp:323] relu4 -> conv4 (in-place)\r\n", + "I0218 20:43:25.391898 2099749632 net.cpp:105] Setting up relu4\r\n", + "I0218 20:43:25.391906 2099749632 net.cpp:112] Top shape: 10 384 13 13 (648960)\r\n", + "I0218 20:43:25.391913 2099749632 layer_factory.hpp:74] Creating layer conv5\r\n", + "I0218 20:43:25.391923 2099749632 net.cpp:76] Creating Layer conv5\r\n", + "I0218 20:43:25.391929 2099749632 net.cpp:372] conv5 <- conv4\r\n", + "I0218 20:43:25.391937 2099749632 net.cpp:334] conv5 -> conv5\r\n", + "I0218 20:43:25.391947 2099749632 net.cpp:105] Setting up conv5\r\n", + "I0218 20:43:25.393072 2099749632 net.cpp:112] Top shape: 10 256 13 13 (432640)\r\n", + "I0218 20:43:25.393108 2099749632 layer_factory.hpp:74] Creating layer relu5\r\n", + "I0218 20:43:25.393122 2099749632 net.cpp:76] Creating Layer relu5\r\n", + "I0218 20:43:25.393129 2099749632 net.cpp:372] relu5 <- conv5\r\n", + "I0218 20:43:25.393138 2099749632 net.cpp:323] relu5 -> conv5 (in-place)\r\n", + "I0218 20:43:25.393148 2099749632 net.cpp:105] Setting up relu5\r\n", + "I0218 20:43:25.393157 2099749632 net.cpp:112] Top shape: 10 256 13 13 (432640)\r\n", + "I0218 20:43:25.393167 2099749632 layer_factory.hpp:74] Creating layer pool5\r\n", + "I0218 20:43:25.393175 2099749632 net.cpp:76] Creating Layer pool5\r\n", + "I0218 20:43:25.393182 2099749632 net.cpp:372] pool5 <- conv5\r\n", + "I0218 20:43:25.393190 2099749632 net.cpp:334] pool5 -> pool5\r\n", + "I0218 20:43:25.393199 2099749632 net.cpp:105] Setting up pool5\r\n", + "I0218 20:43:25.393209 2099749632 net.cpp:112] Top shape: 10 256 6 6 (92160)\r\n", + "I0218 20:43:25.393218 2099749632 layer_factory.hpp:74] Creating layer fc6\r\n", + "I0218 20:43:25.393226 2099749632 net.cpp:76] Creating Layer fc6\r\n", + "I0218 20:43:25.393232 2099749632 net.cpp:372] fc6 <- pool5\r\n", + "I0218 20:43:25.393240 2099749632 net.cpp:334] fc6 -> fc6\r\n", + "I0218 20:43:25.393249 2099749632 net.cpp:105] Setting up fc6\r\n" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "I0218 20:43:25.516396 2099749632 net.cpp:112] Top shape: 10 4096 1 1 (40960)\r\n", + "I0218 20:43:25.516445 2099749632 layer_factory.hpp:74] Creating layer relu6\r\n", + "I0218 20:43:25.516463 2099749632 net.cpp:76] Creating Layer relu6\r\n", + "I0218 20:43:25.516470 2099749632 net.cpp:372] relu6 <- fc6\r\n", + "I0218 20:43:25.516480 2099749632 net.cpp:323] relu6 -> fc6 (in-place)\r\n", + "I0218 20:43:25.516490 2099749632 net.cpp:105] Setting up relu6\r\n", + "I0218 20:43:25.516497 2099749632 net.cpp:112] Top shape: 10 4096 1 1 (40960)\r\n", + "I0218 20:43:25.516505 2099749632 layer_factory.hpp:74] Creating layer drop6\r\n", + "I0218 20:43:25.516515 2099749632 net.cpp:76] Creating Layer drop6\r\n", + "I0218 20:43:25.516521 2099749632 net.cpp:372] drop6 <- fc6\r\n", + "I0218 20:43:25.516530 2099749632 net.cpp:323] drop6 -> fc6 (in-place)\r\n", + "I0218 20:43:25.516538 2099749632 net.cpp:105] Setting up drop6\r\n", + "I0218 20:43:25.516557 2099749632 net.cpp:112] Top shape: 10 4096 1 1 (40960)\r\n", + "I0218 20:43:25.516566 2099749632 layer_factory.hpp:74] Creating layer fc7\r\n", + "I0218 20:43:25.516576 2099749632 net.cpp:76] Creating Layer fc7\r\n", + "I0218 20:43:25.516582 2099749632 net.cpp:372] fc7 <- fc6\r\n", + "I0218 20:43:25.516589 2099749632 net.cpp:334] fc7 -> fc7\r\n", + "I0218 20:43:25.516599 2099749632 net.cpp:105] Setting up fc7\r\n" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "I0218 20:43:25.604786 2099749632 net.cpp:112] Top shape: 10 4096 1 1 (40960)\r\n", + "I0218 20:43:25.604838 2099749632 layer_factory.hpp:74] Creating layer relu7\r\n", + "I0218 20:43:25.604852 2099749632 net.cpp:76] Creating Layer relu7\r\n", + "I0218 20:43:25.604859 2099749632 net.cpp:372] relu7 <- fc7\r\n", + "I0218 20:43:25.604868 2099749632 net.cpp:323] relu7 -> fc7 (in-place)\r\n", + "I0218 20:43:25.604878 2099749632 net.cpp:105] Setting up relu7\r\n", + "I0218 20:43:25.604885 2099749632 net.cpp:112] Top shape: 10 4096 1 1 (40960)\r\n", + "I0218 20:43:25.604893 2099749632 layer_factory.hpp:74] Creating layer drop7\r\n", + "I0218 20:43:25.604902 2099749632 net.cpp:76] Creating Layer drop7\r\n", + "I0218 20:43:25.604908 2099749632 net.cpp:372] drop7 <- fc7\r\n", + "I0218 20:43:25.604917 2099749632 net.cpp:323] drop7 -> fc7 (in-place)\r\n", + "I0218 20:43:25.604924 2099749632 net.cpp:105] Setting up drop7\r\n", + "I0218 20:43:25.604933 2099749632 net.cpp:112] Top shape: 10 4096 1 1 (40960)\r\n", + "I0218 20:43:25.604939 2099749632 layer_factory.hpp:74] Creating layer fc-rcnn\r\n", + "I0218 20:43:25.604948 2099749632 net.cpp:76] Creating Layer fc-rcnn\r\n", + "I0218 20:43:25.604954 2099749632 net.cpp:372] fc-rcnn <- fc7\r\n", + "I0218 20:43:25.604962 2099749632 net.cpp:334] fc-rcnn -> fc-rcnn\r\n", + "I0218 20:43:25.604971 2099749632 net.cpp:105] Setting up fc-rcnn\r\n", + "I0218 20:43:25.606878 2099749632 net.cpp:112] Top shape: 10 200 1 1 (2000)\r\n", + "I0218 20:43:25.606904 2099749632 net.cpp:165] fc-rcnn does not need backward computation.\r\n", + "I0218 20:43:25.606909 2099749632 net.cpp:165] drop7 does not need backward computation.\r\n", + "I0218 20:43:25.606916 2099749632 net.cpp:165] relu7 does not need backward computation.\r\n", + "I0218 20:43:25.606922 2099749632 net.cpp:165] fc7 does not need backward computation.\r\n", + "I0218 20:43:25.606928 2099749632 net.cpp:165] drop6 does not need backward computation.\r\n", + "I0218 20:43:25.606935 2099749632 net.cpp:165] relu6 does not need backward computation.\r\n", + "I0218 20:43:25.606940 2099749632 net.cpp:165] fc6 does not need backward computation.\r\n", + "I0218 20:43:25.606946 2099749632 net.cpp:165] pool5 does not need backward computation.\r\n", + "I0218 20:43:25.606952 2099749632 net.cpp:165] relu5 does not need backward computation.\r\n", + "I0218 20:43:25.606958 2099749632 net.cpp:165] conv5 does not need backward computation.\r\n", + "I0218 20:43:25.606964 2099749632 net.cpp:165] relu4 does not need backward computation.\r\n", + "I0218 20:43:25.606971 2099749632 net.cpp:165] conv4 does not need backward computation.\r\n", + "I0218 20:43:25.606976 2099749632 net.cpp:165] relu3 does not need backward computation.\r\n", + "I0218 20:43:25.606982 2099749632 net.cpp:165] conv3 does not need backward computation.\r\n", + "I0218 20:43:25.606988 2099749632 net.cpp:165] norm2 does not need backward computation.\r\n", + "I0218 20:43:25.606995 2099749632 net.cpp:165] pool2 does not need backward computation.\r\n", + "I0218 20:43:25.607002 2099749632 net.cpp:165] relu2 does not need backward computation.\r\n", + "I0218 20:43:25.607007 2099749632 net.cpp:165] conv2 does not need backward computation.\r\n", + "I0218 20:43:25.607013 2099749632 net.cpp:165] norm1 does not need backward computation.\r\n", + "I0218 20:43:25.607199 2099749632 net.cpp:165] pool1 does not need backward computation.\r\n", + "I0218 20:43:25.607213 2099749632 net.cpp:165] relu1 does not need backward computation.\r\n", + "I0218 20:43:25.607219 2099749632 net.cpp:165] conv1 does not need backward computation.\r\n", + "I0218 20:43:25.607225 2099749632 net.cpp:201] This network produces output fc-rcnn\r\n", + "I0218 20:43:25.607239 2099749632 net.cpp:446] Collecting Learning Rate and Weight Decay.\r\n", + "I0218 20:43:25.607255 2099749632 net.cpp:213] Network initialization done.\r\n", + "I0218 20:43:25.607262 2099749632 net.cpp:214] Memory required for data: 62425920\r\n" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "E0218 20:43:26.388214 2099749632 upgrade_proto.cpp:618] Attempting to upgrade input file specified using deprecated V1LayerParameter: ../models/bvlc_reference_rcnn_ilsvrc13/bvlc_reference_rcnn_ilsvrc13.caffemodel\r\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ - "I0610 10:12:49.364333 25530 net.cpp:128] Top shape: 10 4096 1 1 (40960)\r\n", - "I0610 10:12:49.364372 25530 net.cpp:154] fc6 needs backward computation.\r\n", - "I0610 10:12:49.364387 25530 net.cpp:77] Creating Layer relu6\r\n", - "I0610 10:12:49.364420 25530 net.cpp:87] relu6 <- fc6\r\n", - "I0610 10:12:49.364429 25530 net.cpp:101] relu6 -> fc6 (in-place)\r\n", - "I0610 10:12:49.364437 25530 net.cpp:128] Top shape: 10 4096 1 1 (40960)\r\n", - "I0610 10:12:49.364444 25530 net.cpp:154] relu6 needs backward computation.\r\n", - "I0610 10:12:49.364455 25530 net.cpp:77] Creating Layer drop6\r\n", - "I0610 10:12:49.364461 25530 net.cpp:87] drop6 <- fc6\r\n", - "I0610 10:12:49.364467 25530 net.cpp:101] drop6 -> fc6 (in-place)\r\n", - "I0610 10:12:49.364480 25530 net.cpp:128] Top shape: 10 4096 1 1 (40960)\r\n", - "I0610 10:12:49.364487 25530 net.cpp:154] drop6 needs backward computation.\r\n", - "I0610 10:12:49.364495 25530 net.cpp:77] Creating Layer fc7\r\n", - "I0610 10:12:49.364501 25530 net.cpp:87] fc7 <- fc6\r\n", - "I0610 10:12:49.364507 25530 net.cpp:113] fc7 -> fc7\r\n", - "I0610 10:12:49.391316 25530 net.cpp:128] Top shape: 10 4096 1 1 (40960)\r\n", - "I0610 10:12:49.391350 25530 net.cpp:154] fc7 needs backward computation.\r\n", - "I0610 10:12:49.391361 25530 net.cpp:77] Creating Layer relu7\r\n", - "I0610 10:12:49.391369 25530 net.cpp:87] relu7 <- fc7\r\n", - "I0610 10:12:49.391377 25530 net.cpp:101] relu7 -> fc7 (in-place)\r\n", - "I0610 10:12:49.391384 25530 net.cpp:128] Top shape: 10 4096 1 1 (40960)\r\n", - "I0610 10:12:49.391391 25530 net.cpp:154] relu7 needs backward computation.\r\n", - "I0610 10:12:49.391398 25530 net.cpp:77] Creating Layer drop7\r\n", - "I0610 10:12:49.391427 25530 net.cpp:87] drop7 <- fc7\r\n", - "I0610 10:12:49.391433 25530 net.cpp:101] drop7 -> fc7 (in-place)\r\n", - "I0610 10:12:49.391440 25530 net.cpp:128] Top shape: 10 4096 1 1 (40960)\r\n", - "I0610 10:12:49.391446 25530 net.cpp:154] drop7 needs backward computation.\r\n", - "I0610 10:12:49.391454 25530 net.cpp:77] Creating Layer fc-rcnn\r\n", - "I0610 10:12:49.391459 25530 net.cpp:87] fc-rcnn <- fc7\r\n", - "I0610 10:12:49.391466 25530 net.cpp:113] fc-rcnn -> fc-rcnn\r\n", - "I0610 10:12:49.392812 25530 net.cpp:128] Top shape: 10 200 1 1 (2000)\r\n", - "I0610 10:12:49.392823 25530 net.cpp:154] fc-rcnn needs backward computation.\r\n", - "I0610 10:12:49.392829 25530 net.cpp:165] This network produces output fc-rcnn\r\n", - "I0610 10:12:49.392850 25530 net.cpp:183] Collecting Learning Rate and Weight Decay.\r\n", - "I0610 10:12:49.392868 25530 net.cpp:176] Network initialization done.\r\n", - "I0610 10:12:49.392875 25530 net.cpp:177] Memory required for Data 41950840\r\n" + "I0218 20:43:27.089423 2099749632 upgrade_proto.cpp:626] Successfully upgraded file specified using deprecated V1LayerParameter\r\n" ] }, { @@ -381,21 +451,39 @@ "stream": "stdout", "text": [ "GPU mode\r\n", - "Loading input...\r\n", - "selective_search_rcnn({'/home/shelhamer/caffe/examples/images/fish-bike.jpg'}, '/tmp/tmpo7yOum.mat')\r\n" + "Loading input...\r\n" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "selective_search_rcnn({'/Users/shelhamer/h/desk/caffe/caffe-dev/examples/images/fish-bike.jpg'}, '/var/folders/bk/dtkn5qjd11bd17b2j36zplyw0000gp/T/tmpakaRLL.mat')\r\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ - "Processed 1570 windows in 35.012 s.\r\n", - "/home/shelhamer/anaconda/lib/python2.7/site-packages/pandas/io/pytables.py:2446: PerformanceWarning: \r\n", + "Processed 1570 windows in 102.895 s.\r\n" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "/Users/shelhamer/anaconda/lib/python2.7/site-packages/pandas/io/pytables.py:2453: PerformanceWarning: \r\n", "your performance may suffer as PyTables will pickle object types that it cannot\r\n", "map directly to c-types [inferred_type->mixed,key->block1_values] [items->['prediction']]\r\n", "\r\n", - " warnings.warn(ws, PerformanceWarning)\r\n", - "Saved to _temp/det_output.h5 in 0.035 s.\r\n" + " warnings.warn(ws, PerformanceWarning)\r\n" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Saved to _temp/det_output.h5 in 0.298 s.\r\n" ] } ], @@ -432,12 +520,12 @@ "stream": "stdout", "text": [ "(1570, 5)\n", - "prediction [-2.64547, -2.88455, -2.85903, -3.17038, -1.92...\n", + "prediction [-2.62247, -2.84579, -2.85122, -3.20838, -1.94...\n", "ymin 79.846\n", "xmin 9.62\n", "ymax 246.31\n", "xmax 339.624\n", - "Name: /home/shelhamer/caffe/examples/images/fish-bike.jpg, dtype: object\n" + "Name: /Users/shelhamer/h/desk/caffe/caffe-dev/examples/images/fish-bike.jpg, dtype: object\n" ] } ], @@ -481,37 +569,37 @@ "stream": "stdout", "text": [ "name\n", - "accordion -2.645470\n", - "airplane -2.884554\n", - "ant -2.859026\n", - "antelope -3.170383\n", - "apple -1.924201\n", - "armadillo -2.493925\n", - "artichoke -2.235427\n", - "axe -2.378177\n", - "baby bed -2.757855\n", - "backpack -2.160120\n", - "bagel -2.715738\n", - "balance beam -2.716172\n", - "banana -2.418939\n", - "band aid -1.604563\n", - "banjo -2.329196\n", + "accordion -2.622471\n", + "airplane -2.845788\n", + "ant -2.851219\n", + "antelope -3.208377\n", + "apple -1.949950\n", + "armadillo -2.472935\n", + "artichoke -2.201684\n", + "axe -2.327404\n", + "baby bed -2.737925\n", + "backpack -2.176763\n", + "bagel -2.681061\n", + "balance beam -2.722538\n", + "banana -2.390628\n", + "band aid -1.598909\n", + "banjo -2.298197\n", "...\n", - "trombone -2.531519\n", - "trumpet -2.382109\n", - "turtle -2.378510\n", - "tv or monitor -2.777433\n", - "unicycle -2.263807\n", - "vacuum -1.894700\n", - "violin -2.797967\n", - "volleyball -2.807812\n", - "waffle iron -2.418155\n", - "washer -2.429423\n", - "water bottle -2.163465\n", - "watercraft -2.803971\n", - "whale -3.094172\n", - "wine bottle -2.830827\n", - "zebra -2.791829\n", + "trombone -2.582361\n", + "trumpet -2.352853\n", + "turtle -2.360859\n", + "tv or monitor -2.761043\n", + "unicycle -2.218467\n", + "vacuum -1.907717\n", + "violin -2.757079\n", + "volleyball -2.723689\n", + "waffle iron -2.418540\n", + "washer -2.408994\n", + "water bottle -2.174899\n", + "watercraft -2.837425\n", + "whale -3.120338\n", + "wine bottle -2.772960\n", + "zebra -2.742913\n", "Name: 0, Length: 200, dtype: float32\n" ] } @@ -542,22 +630,22 @@ "output_type": "pyout", "prompt_number": 4, "text": [ - "" + "" ] }, { "metadata": {}, "output_type": "display_data", "text": [ - "" + "" ] }, { "metadata": {}, "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAALMAAAOoCAYAAACa7cU2AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsvUmMZOlVPX5ifjHPc2RG5FiZlVWVNXTbbptu1G0MeGNA\nFpK9MBaYBbCyZFnemgWDF5YYhYQFkjcMYgFGgK2WjWQ3zdBtV1XXkFVZOcc8zy8iXkzvv6j/uc7C\nwM9UFoZK1Se1XJXOihcR73v3u/fcc8416Lqu48V6sc7BMv5vv4EX68V6VuvFZn6xzs16sZlfrHOz\nXmzmF+vcrBeb+cU6N+vFZn6xzs06F5v5G9/4BjY2NrC2toYvfelL/yPXyGQyuHLlCq5du4b3ve99\nAIBms4mPfOQjWF9fx0/+5E+i3W4/9ev/0i/9EqLRKC5fviw/+69e/7d+67ewtraGjY0NvPnmm8/s\nml/84heRSqVw7do1XLt2DV//+tef2TVzuRxef/11bG1t4dKlS/i93/u9Z/s59ed8TadTfWVlRT86\nOtLH47G+vb2t7+zsPPPrZDIZvdFoPPGzz3/+8/qXvvQlXdd1/bd/+7f1L3zhC0/9+t/5znf0mzdv\n6pcuXfp/vv79+/f17e1tfTwe60dHR/rKyoo+m82eyTW/+MUv6l/+8pd/4HefxTVLpZJ+69YtXdd1\nvdfr6evr6/rOzs4z+5zPfWR+5513sLq6ikwmA4vFgk984hP42te+9j9yLf3f9Zf+9m//Fp/+9KcB\nAJ/+9KfxN3/zN0/92q+++ir8fv8P9fpf+9rX8MlPfhIWiwWZTAarq6t45513nsk1gR/8nM/qmrFY\nDFevXgUAuFwubG5uolAoPLPP+dxv5kKhgIWFBfl7KpVCoVB45tcxGAz4iZ/4Cbz00kv4yle+AgCo\nVCqIRqMAgGg0ikql8kyv+Z+9frFYRCqVkt971p/593//97G9vY3PfOYzcuQ/62seHx/j1q1beP/7\n3//MPudzv5kNBsOP5Dpvv/02bt26ha9//ev4wz/8Q7z11ls/8D7+J9/L/+v1n9W1f/VXfxVHR0e4\nffs24vE4Pve5zz3za/b7fXz84x/H7/7u78Ltdv/Aaz7t53zuN3MymUQul5O/53K5J57mZ7Xi8TgA\nIBwO4+d+7ufwzjvvIBqNolwuAwBKpRIikcgzveZ/9vr//jPn83kkk8lncs1IJCIb6pd/+ZflWH9W\n15xMJvj4xz+OT33qU/jZn/1ZAM/ucz73m/mll17C3t4ejo+PMR6P8Zd/+Zf42Mc+9kyvMRgM0Ov1\nAACqquLNN9/E5cuX8bGPfQxf/epXAQBf/epX5eY8q/Wfvf7HPvYx/MVf/AXG4zGOjo6wt7cnCMtZ\nV6lUkj//9V//tSAdz+Kauq7jM5/5DC5evIjPfvaz8vNn9jn/W+Xo/9H1D//wD/r6+rq+srKi/+Zv\n/uYzf/3Dw0N9e3tb397e1re2tuQajUZD//CHP6yvra3pH/nIR/RWq/XU1/jEJz6hx+Nx3WKx6KlU\nSv/TP/3T//L1f+M3fkNfWVnRL1y4oH/jG994Jtf8kz/5E/1Tn/qUfvnyZf3KlSv6z/zMz+jlcvmZ\nXfOtt97SDQaDvr29rV+9elW/evWq/vWvf/2ZfU6Drr+ggL5Y52M9N2nGj6Ix8mI93+u5iMyz2QwX\nLlzAN7/5TSSTSbz88sv48z//c2xubv5vv7UX6//Qei4i84+yMfJiPb/rudjMP6rGyIv1fK/nYjP/\nqBojL9bzvcz/22/gh1k/TGPE4XBgOBz+qN/ai/WMl9frfWr24XNRAE6nU1y4cAHf+ta3kEgk8L73\nve8HCkCDwYBf+IVfQDAYxGAwgKqqcLvdmE6nWFhYQL/fR6/Xw3A4RKVSQTqdxmw2Q6VSgcPhwNra\nGgaDAZrNJgBgPB7DZDJhMpnAaDRiMplga2sL2WwWdrsdmqbh29/+Nj784Q9jOp3C6/WiXC7DZDJh\nNpvB4XBgNBrB7/ej3+/DbDZD13XM53PE43H0ej1YrVa43W7kcjkYDAYMBgOk02moqopyuQyj0YiT\nkxNcvnwZ5XIZtVoNGxsb6Pf70HUdm5ub2N/fh8vlQr/fx2w2g8lkwmg0gsViQTgcxmAwQKfTwdLS\nEiaTCfx+P/b396FpGlKpFE5OTuByuWAwGBAIBHB8fIxQKIRAIIBvfetbePXVV1EoFBAKhTAej1Gr\n1ZBIJDCZTLCwsIDJZIKdnR1cvHgRJycnWFhYgMViwf379xEMBuF2u6HrOur1OgBgOBzCbrdjMpkg\nmUzi4OAAyWQSFosFqqrij/7oj/5DotMPs56LyGw2m/EHf/AH+Kmf+inMZjN85jOf+Q+RjHA4jPl8\njnQ6jXfffRfRaBSTyQSz2QyapsHn8yEYDMJiscBsNsNkMkHTNKytraHT6UBRFFitVnQ6HWQyGeRy\n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\nLADwHmTDz5IewE+8aGDMzMsI1YnsSqVS5sAOhUL2dzG7o4tAH82OCWaNiAe6GHiOgEUuh4Q7YvJl\nwUIySbqR5MSfgZGAymRgTVzwCJyg4iFeSDEF3aGFip/xz22nye/+7u8qmUzq4cOH9u++9rWv6d69\ne3rjjTf0m7/5m5aOI0lf//rXtbGxoc3NTX33u9+1f//ee+/p4cOH2tjY0B/90R/9f/6d/MDa7bb1\n/hUKBcOKyWujgBJGChE5DFexWFSv19P+/r7tItL1fFiv128IzyEunIuD4xKDJm4USIHxeGxySela\n7wAzhuQTPyL4LMwmqAGMHWgLR7p0jRdjFJVk9wGyQ5CoooYjpIURgRhfxFd0nUDoYDYIhUJGhVNI\nxB0F+SpGWu4IkiyQ5vj4WHNzc1a1zEj2qs//2mL+nd/5HX3nO9+58e9+5Vd+RU+fPtUHH3ygO3fu\n6Otf/7ok6dmzZ/r7v/97PXv2TN/5znf01a9+1UD53//939df/dVfaWtrS1tbW//lz3Q+dFhHo1HT\n3CKvZBfEvkN6JqUw7BoEsjCHsutxUUokEqbZZaxIp9MmNo/FYkYJx+NxE7VDM0Opo1E+PT1Vo9Gw\nUSMQCJhACGJGkqnfqPjF7eHUNCAHBRlBh+F0gYNz8zNBpLSzs2PqP4osgTFpiSUqjH4Vonfn5ubM\n3wipAurCBoOJls+DnRuZa7FY1LvvvnurNfe/tpi/+MUv/pe83S996Uu2y33+8583T9s//MM/6Mtf\n/rJcLpeWl5e1vr6ud955xySQn/vc5yRJv/3bv61vfetb/+3f6ewZuXPnjoWTSDIyIxQKWbxtIpHQ\n+vq6JRDBhqVSKSUSCZN1UsIDtUzdLk4Q0n24nLGrETbD1+DxeOxFAknw+Xx67bXXbJflzwmFQspk\nMgqHw5Z/HI/HrSmLeC0yo1GfYenCBhUOh027jV4b+hu9Cg4WcplzuZzZrMh3RhYQCATMTMBYwqxP\nzoezEg34kNRUskww+zICplIpra6u3mrN/T+D5r75zW/qy1/+sqTr4/jtt9+2/wbc5XK5bohPstms\nyuXyf/tn1ut12znL5bLtmIwbCG1wP7x48cLmRxqhOP6Bn2D1iMP1+/2q1+s2Q87Pz9tsKsmIEq/X\nq06no2AwaFAZlimOVZwrtVpNmUxG5XLZwl5Y2LCWlKfncjltb29bEQ4j1NTUlO22hUJBmUzGcjgu\n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\nfve7eu+99yRJd+/eNXfSs/Xft85O5s95/fnPf9bQ0JC+973v2dcWFxefojuurq7q5s2bun79uq5f\nv673339fkrSxsaGbN2/q+eef1+Liov7617/q+PhY3/nOd7S4uKgrV67oV7/6lSRpeXlZX/3qV3Xj\nxg3dvHnTYsh++9vfanFxUdeuXdPLL7/8T/zL/wXLOVuf6/r1r3/t/OhHP/pPX3/y5Ilz+fJlx3Ec\np9VqOZ1Ox3Ecx3n06JFz48YNx3Ec55e//KXzi1/8wnEcxzk+PnZ2dnacDz/80PnSl75k36fZbDqO\n4zivvPKKs7S05DiO4/ztb39zXnnlFcdxHGdxcdEpFotPvbZX11mZ8TkvpoD/1To4OND3v/99ffzx\nx+rv79fS0pIk6cUXX9Sbb76pw8NDvfbaa7p69arm5ua0srKiH/zgB3r11Vf15S9/Wbu7u3r//ff1\njW9846nvKUkvvfSSvv3tb+uNN97Q66+//vn8kf8u61/9NPX6+tOf/uTcvHnzP33dfTL/7Gc/c378\n4x87juM43W7XGRgYsNdtbGw477zzjnPt2jXn3XffdRzHcXZ3d53f//73zmuvvea8+eabzvb2tpNK\npf6/v8MHH3zg/PSnP3Wmp6edra2t/84/799qndXMn/N65ZVXtL+/r3feece+dufOHa2trdn/3t7e\nVjKZlCS9++675iCfz+cVi8X01ltv6a233tKtW7cswu3111/X22+/rY8++kjBYFAzMzP63e9+J+mk\n6bxz546kk1r6xRdf1M9//nPFYrFTk3n+rde/+mn637CKxaLzxhtvOHNzc86lS5ecr33ta87S0pKz\nuLjoOI7jLC0tOVeuXHGuXr3q/OQnP3GCwaDjOI7zm9/8xrl8+bLz/PPPOzdv3nRWV1edjz/+2Hnh\nhReca9euOdeuXXP++Mc/Oo5zctJ/5Stfca5eveo899xzzttvv+04juO8/vrrzuLionP58mXnhz/8\n4b/mDfgnrTNuxtnqmXVWZpytnllnm/ls9cw628xnq2fW2WY+Wz2zzjbz2eqZdbaZz1bPrLPNfLZ6\nZp1t5rPVM+s/AIIOk5coA2g1AAAAAElFTkSuQmCC\n", + "png": "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\n3bt35h2yt7dndrSgGJKMpsogCWsw/gzO9eXlpTY2NrSwsGA+z1ADSJslk5ByCCemSqViGHmv11Oj\n0bAHC6NxuNno/1qtlsF3d10jtZnhOUiygcJ7771nXT+CV2/edb1etw7+8PDQPIX9fr+ePXtmm4RJ\n2WAwsGlaJBJRp9OxzD7UGoPBQKVSydJK+b5+v1+1Ws2+DtYDnNBc3f1+XwsLC9rc3JQks5dFHOrz\n+VQul20a6fP5zC9udnZWtVrN+MuE9FDy4H46OTlpATnAdCARR0dHhjF7hxqcwGgmIQvBdb66utLK\nyop2d3dvZbPw0GHXAF4OTOo1bbzPGqnNjAH25eWlnj9/ru9///t6/vy5VlZWjMPw7t07u+5evnyp\nYDCozc1NTUxMaGZmxtCEYrFohoftdluZTMZO+/X1devOHz9+rO3tbVNykIf35s0bxWIxJRIJra2t\nyXVdQySGw6FqtZpKpZLa7bYZfOMsOhgM9PHHHxu3AfLQ6uqq3n//fYsuQwaFO//jx4+1u7urt2/f\namVlRVtbW5qamrplI0Y93el01Gq1rLlEZULjNzk5qY2NDSM7XV1dqVarGef75ORES0tLRmuFj7K5\nuWk6wEgkYhwNLzfEK5aNxWImaHj79u293v+R2sw+n89y+eLxuMmksKXiTYIY9PTpUyMQgXSUSiVD\nNpaXl1UoFPT5558rFovZaT4/P69ut2skdhKfOGXi8bgODg5MFpVIJGxYgkuQJAvxOT8/v5V7PTY2\nplwuZx50DF0ePXqk8fFxpdNpHR4e2mtqtVoqFAp2bZdKJQ2HQ/ue4Me9Xk+xWMxEtvl8XmNjY6b2\nzmQyevXqlfL5vAVXUmZgWQsSA1dbkj0IsVjMkrwqlYohHJlMxiRqZ2dnRvWMx+MWLBoIBPTkyZN7\n1cwjhWaAXEgygan35ICeCJOOcEu0dJQKdOetVsuuf6aJ0BjPzs6MTMP1OxwOb2XtDYdD4/iiViEu\ngaB0LGAJtOT/OcnxcPNa3cIRxugGWAvRKA8EPz+oCyNnb9Qxr5tJKfU4jSoNKp4YOHuSQoUaR/oy\n0J0bBUYcE0xvpDD2wZIMBcH7465rpDYzOjw2JJgyTdNgMNDi4qLxHiSZ/Ws0GrUOHGFqOBw2RTZ2\nA+jtcDDCoTMcDmthYcEoqKSasnGRWUky4g96PdTgkINQpcCdILfbi7rAicb5iDLULiPnAAAbvUlE\n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\nfu67Lue+cVW/KctxnNH4QR6WXNd17vL3RmYzP6yHNVJlxsP6/3s9bOaHNTLrYTM/rJFZD5v5V7Ac\nx8k6jvMfHcdZdxznI8dx/shxnCXHcVZ/3a9tlNZITQB/E5dzjeH9Z0n/1nXdv3bzsa9Lup9JxMP6\nhfVwMn/167clDV3X/T0+4LruqiRLPXccp+Q4zh87jvPxzT+/dfPx3M3HP3UcZ9VxnD/rOI7PcZw/\nuPn/zx3H+Qc3n/vIcZz/enPy/7HjOMs3H/8rN5/73HGc//2r/dF/tevhZP7q11NJH/8pn1OT9Odd\n1x04jrMk6T9I+rakvy7pv7mu+89vTviQpG9Iyruu+3VJchwncvM1fk/S33Vdd91xnD8j6Xcl/TlJ\n/0TSX3Bdt+r53JFcD5v5q1//L0D+hKR/7TjO+5IuJS3dfPxnkv6N4zh+Sf/Fdd3PHMfZkLTgOM6/\nkvRHkv674zhhSb8l6YdMJm++piT9RNK/cxznP0n6w1/KT/Qbuh7KjK9+vZD0zT/lc/6hpKrrus8k\nfUvSpCS5rvt/JH1PUkXSHziO8zdc1+1Iel/S/5L09yT9viRHUsd13W94/nly8zX+vqR/LGlW0seO\n4yR+2T/gb8p62Mxf8XJd939ImnQc5+/wMcdxnul6c7Eikg5u/vtvShq7+bw5SQ3XdX9f15v2Q8dx\nkpLGXNf9Q12XEN9wXbcnadNxnL988/ecm+8hx3Eeua77M9d1/6mkhqTiV/jj/lrXw2b+1ay/JOl3\nbqC5LyT9M0lVfVmC/K6kv+U4znNJy5II9/htSc8dx/lE0l+V9C8lFST9T8dxPpX07yX9o5vP/YGk\nv33zNb6Q9BdvPv4vbhrFVUk/cV3386/yB/11rgduxsMamfVwMj+skVkPm/lhjcx62MwPa2TWw2Z+\nWCOzHjbzwxqZ9bCZH9bIrIfN/LBGZj1s5oc1Muv/AuHZAPr9VeA9AAAAAElFTkSuQmCC\n", "text": [ - "" + "" ] } ], @@ -586,16 +674,16 @@ "stream": "stdout", "text": [ "name\n", - "person 1.883164\n", - "bicycle 0.936994\n", - "unicycle 0.016907\n", - "banjo 0.013019\n", - "motorcycle -0.024704\n", - "electric fan -0.193420\n", - "turtle -0.243857\n", - "cart -0.289637\n", - "lizard -0.307945\n", - "baby bed -0.582180\n", + "person 1.835771\n", + "bicycle 0.866110\n", + "unicycle 0.057080\n", + "motorcycle -0.006122\n", + "banjo -0.028209\n", + "turtle -0.189831\n", + "electric fan -0.206788\n", + "cart -0.214235\n", + "lizard -0.393519\n", + "helmet -0.477942\n", "dtype: float32\n" ] } @@ -651,20 +739,20 @@ "text": [ "Top detection:\n", "name\n", - "person 1.883164\n", - "swimming trunks -1.136701\n", - "rubber eraser -1.251888\n", - "plastic bag -1.286928\n", - "snowmobile -1.304962\n", + "person 1.835771\n", + "swimming trunks -1.150371\n", + "rubber eraser -1.231106\n", + "turtle -1.266037\n", + "plastic bag -1.303265\n", "dtype: float32\n", "\n", "Second-best detection:\n", "name\n", - "bicycle 0.936994\n", - "unicycle -0.372841\n", - "scorpion -0.812350\n", - "lobster -1.041506\n", - "lamp -1.118889\n", + "bicycle 0.866110\n", + "unicycle -0.359139\n", + "scorpion -0.811621\n", + "lobster -0.982891\n", + "lamp -1.096808\n", "dtype: float32\n" ] }, @@ -673,15 +761,15 @@ "output_type": "pyout", "prompt_number": 6, "text": [ - "" + "" ] }, { "metadata": {}, "output_type": "display_data", - "png": "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\n77fWWvu9733P7t6925ZlaQ8ePGi3b99u67q+JNf9dtrx48ftSy+9ZK21dnV11e7atcseOHDgsl+X\nwWBgrbW2qip7yy232GefffayXxNrrf385z9v77nnHvuxj33MWjvaP9Zae1Ej8Oeeew47duzAtm3b\nEIYh7rrrLjzxxBMX8xIumX3gAx8gRZOW7d+/H/fddx8A4L777sPjjz8OAHjiiSdw9913IwxDbNu2\nDTt27MBzzz130a/57baNGzdibm4OABGDvfe978X8/Pxlvy5MtcwzBlNTU5f9mhw9ehRPPvkkfv3X\nf91D6y73NQEucgllfn4eV1xxhf/31q1bMT8/fzEv4afKFhYWMDs7CwCYnZ3FwsICAODYsWPYunWr\n/73LYZ0OHTqEl156Cbfccstlvy7GGMzNzWF2dtaXmC73Nfmt3/otfO5zn/NYcWC0f4CL7MB/0lTR\n5WztwYgL/fz/V+v3+/jkJz+JRx55BGNjY2t+djmui5QSL7/8Mo4ePYpvfvObeOaZZ9b8/HJbky9/\n+cuYmZnBnj17LjjYcrmtCdtFdeBbtmzBkSNH/L+PHDmy5qS83Gx2dhYnTpwAABw/fhwzMzMAzl2n\no0ePYsuWLZfkGt9uq6oKn/zkJ3HvvffiE5/4BIDRurBNTEzgzjvvxAsvvHBZr8m3v/1t7N+/H+95\nz3tw991342tf+xruvffey3pN2C6qA7/xxhvx2muv4dChQyjLEo899hj27t17MS/hp8r27t2Lffv2\nAQD27dvnHdjevXvxpS99CWVZ4uDBg3jttddw8803X8pLfVvMWotf+7Vfw9VXX43f/M3f9N+/nNfl\n1KlTHk2RZRmefvpp7Nmz57JekwceeABHjhzBwYMH8aUvfQkf+tCH8MUvfvGyXhNvF7tr+uSTT9pd\nu3bZ7du32wceeOBiv/0ls7vuustu2rTJhmFot27dah999FF7+vRp++EPf9ju3LnT3n777XZpacn/\n/u/93u/Z7du32yuvvNI+9dRTl/DK3z579tlnrRDC7t69287Nzdm5uTn7la985bJel1dffdXu2bPH\n7t6921533XX293//96219rJek7Z9/etf9yiU0ZpYOxqlH9nIRjayd6iNJjFHNrKRjewdaiMHPrKR\njWxk71AbOfCRjWxkI3uH2siBj2xkIxvZO9RGDnxkIxvZyN6hNnLgIxvZyEb2DrWRAx/ZyEY2sneo\njRz4yEY2spG9Q+3/AULZeneOI2OUAAAAAElFTkSuQmCC\n", + "png": "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\n", "text": [ - "" + "" ] } ], @@ -797,19 +885,19 @@ "output_type": "stream", "stream": "stdout", "text": [ - "scores: [ 0.93699419 -0.65612102 -1.32907355]\n" + "scores: [ 0.86610985 -0.70051557 -1.34796357]\n" ] }, { "metadata": {}, "output_type": "display_data", - "png": "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\n", + "png": "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\n", "text": [ - "" + "" ] } ], - "prompt_number": 10 + "prompt_number": 9 }, { "cell_type": "markdown", @@ -842,4 +930,4 @@ "metadata": {} } ] -} +} \ No newline at end of file diff --git a/examples/feature_extraction/imagenet_val.prototxt b/examples/feature_extraction/imagenet_val.prototxt index 83fe8c1a08d..b0a1cefa00e 100644 --- a/examples/feature_extraction/imagenet_val.prototxt +++ b/examples/feature_extraction/imagenet_val.prototxt @@ -1,24 +1,24 @@ name: "CaffeNet" -layers { +layer { name: "data" - type: IMAGE_DATA + type: "ImageData" top: "data" top: "label" + transform_param { + mirror: false + crop_size: 227 + mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" + } image_data_param { source: "examples/_temp/file_list.txt" batch_size: 50 new_height: 256 new_width: 256 } - transform_param { - crop_size: 227 - mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - mirror: false - } } -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" convolution_param { @@ -27,15 +27,15 @@ layers { stride: 4 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -44,9 +44,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { @@ -55,9 +55,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" convolution_param { @@ -67,15 +67,15 @@ layers { group: 2 } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -84,9 +84,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { @@ -95,9 +95,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" convolution_param { @@ -106,15 +106,15 @@ layers { kernel_size: 3 } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION + type: "Convolution" bottom: "conv3" top: "conv4" convolution_param { @@ -124,15 +124,15 @@ layers { group: 2 } } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION + type: "Convolution" bottom: "conv4" top: "conv5" convolution_param { @@ -142,15 +142,15 @@ layers { group: 2 } } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" bottom: "conv5" top: "pool5" pooling_param { @@ -159,79 +159,79 @@ layers { stride: 2 } } -layers { +layer { name: "fc6" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool5" top: "fc6" inner_product_param { num_output: 4096 } } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6" top: "fc6" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" bottom: "fc6" top: "fc6" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc7" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc6" top: "fc7" inner_product_param { num_output: 4096 } } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7" top: "fc7" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" bottom: "fc7" top: "fc7" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc8" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc7" top: "fc8" inner_product_param { num_output: 1000 } } -layers { +layer { name: "prob" - type: SOFTMAX + type: "Softmax" bottom: "fc8" top: "prob" } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "prob" bottom: "label" top: "accuracy" } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "fc8" bottom: "label" top: "loss" diff --git a/examples/feature_extraction/readme.md b/examples/feature_extraction/readme.md index c325ed482e5..6c8917e27e1 100644 --- a/examples/feature_extraction/readme.md +++ b/examples/feature_extraction/readme.md @@ -51,7 +51,7 @@ Extract Features Now everything necessary is in place. - ./build/tools/extract_features.bin models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel examples/_temp/imagenet_val.prototxt fc7 examples/_temp/features 10 + ./build/tools/extract_features.bin models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel examples/_temp/imagenet_val.prototxt fc7 examples/_temp/features 10 lmdb The name of feature blob that you extract is `fc7`, which represents the highest level feature of the reference model. We can use any other layer, as well, such as `conv5` or `pool3`. diff --git a/examples/filter_visualization.ipynb b/examples/filter_visualization.ipynb index 8e068805d62..0bfdb5caf68 100644 --- a/examples/filter_visualization.ipynb +++ b/examples/filter_visualization.ipynb @@ -4,7 +4,7 @@ "example_name": "Filter visualization", "include_in_docs": true, "priority": 2, - "signature": "sha256:526501b358e0f60c489eaf5799e8603a75019cc65401533baa307b5603fdefa1" + "signature": "sha256:44536e4f82eb5748b6a3bb6fcfca01bc6c5815dad2641c994dab031f452b7606" }, "nbformat": 3, "nbformat_minor": 0, @@ -62,14 +62,13 @@ "cell_type": "code", "collapsed": false, "input": [ - "caffe.set_phase_test()\n", "caffe.set_mode_cpu()\n", "net = caffe.Classifier(caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt',\n", " caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel')\n", "# input preprocessing: 'data' is the name of the input blob == net.inputs[0]\n", - "net.set_mean('data', np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy')) # ImageNet mean\n", - "net.set_raw_scale('data', 255) # the reference model operates on images in [0,255] range instead of [0,1]\n", - "net.set_channel_swap('data', (2,1,0)) # the reference model has channels in BGR order instead of RGB" + "net.transformer.set_mean('data', np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1)) # ImageNet mean\n", + "net.transformer.set_raw_scale('data', 255) # the reference model operates on images in [0,255] range instead of [0,1]\n", + "net.transformer.set_channel_swap('data', (2,1,0)) # the reference model has channels in BGR order instead of RGB" ], "language": "python", "metadata": {}, @@ -214,7 +213,7 @@ "collapsed": false, "input": [ "# index four is the center crop\n", - "plt.imshow(net.deprocess('data', net.blobs['data'].data[4]))" + "plt.imshow(net.transformer.deprocess('data', net.blobs['data'].data[4]))" ], "language": "python", "metadata": {}, diff --git a/examples/finetune_flickr_style/style_names.txt b/examples/finetune_flickr_style/style_names.txt new file mode 100644 index 00000000000..73090c97821 --- /dev/null +++ b/examples/finetune_flickr_style/style_names.txt @@ -0,0 +1,20 @@ +Detailed +Pastel +Melancholy +Noir +HDR +Vintage +Long Exposure +Horror +Sunny +Bright +Hazy +Bokeh +Serene +Texture +Ethereal +Macro +Depth of Field +Geometric Composition +Minimal +Romantic diff --git a/examples/finetune_pascal_detection/pascal_finetune_trainval_test.prototxt b/examples/finetune_pascal_detection/pascal_finetune_trainval_test.prototxt index 5cd605bbf11..9dd2120acad 100644 --- a/examples/finetune_pascal_detection/pascal_finetune_trainval_test.prototxt +++ b/examples/finetune_pascal_detection/pascal_finetune_trainval_test.prototxt @@ -1,9 +1,17 @@ name: "CaffeNet" -layers { +layer { name: "data" - type: WINDOW_DATA + type: "WindowData" top: "data" top: "label" + include { + phase: TRAIN + } + transform_param { + mirror: true + crop_size: 227 + mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" + } window_data_param { source: "examples/finetune_pascal_detection/window_file_2007_trainval.txt" batch_size: 128 @@ -13,18 +21,20 @@ layers { context_pad: 16 crop_mode: "warp" } +} +layer { + name: "data" + type: "WindowData" + top: "data" + top: "label" + include { + phase: TEST + } transform_param { mirror: true crop_size: 227 mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" } - include: { phase: TRAIN } -} -layers { - name: "data" - type: WINDOW_DATA - top: "data" - top: "label" window_data_param { source: "examples/finetune_pascal_detection/window_file_2007_test.txt" batch_size: 128 @@ -34,22 +44,20 @@ layers { context_pad: 16 crop_mode: "warp" } - transform_param { - mirror: true - crop_size: 227 - mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - } - include: { phase: TEST } } -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 11 @@ -64,15 +72,15 @@ layers { } } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -81,9 +89,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { @@ -92,15 +100,19 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 2 @@ -116,15 +128,15 @@ layers { } } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -133,9 +145,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { @@ -144,15 +156,19 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -167,21 +183,25 @@ layers { } } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION + type: "Convolution" bottom: "conv3" top: "conv4" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -197,21 +217,25 @@ layers { } } } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION + type: "Convolution" bottom: "conv4" top: "conv5" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 1 @@ -227,15 +251,15 @@ layers { } } } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" bottom: "conv5" top: "pool5" pooling_param { @@ -244,15 +268,19 @@ layers { stride: 2 } } -layers { +layer { name: "fc6" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool5" top: "fc6" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 weight_filler { @@ -265,30 +293,34 @@ layers { } } } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6" top: "fc6" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" bottom: "fc6" top: "fc6" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc7" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc6" top: "fc7" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 weight_filler { @@ -301,30 +333,34 @@ layers { } } } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7" top: "fc7" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" bottom: "fc7" top: "fc7" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc8_pascal" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc7" top: "fc8_pascal" - blobs_lr: 10 - blobs_lr: 20 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 10 + decay_mult: 1 + } + param { + lr_mult: 20 + decay_mult: 0 + } inner_product_param { num_output: 21 weight_filler { @@ -337,17 +373,19 @@ layers { } } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "fc8_pascal" bottom: "label" } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "fc8_pascal" bottom: "label" top: "accuracy" - include { phase: TEST } + include { + phase: TEST + } } diff --git a/examples/hdf5_classification/train_val.prototxt b/examples/hdf5_classification/train_val.prototxt index b55b6644b17..b9ccc1a93ec 100644 --- a/examples/hdf5_classification/train_val.prototxt +++ b/examples/hdf5_classification/train_val.prototxt @@ -1,35 +1,43 @@ name: "LogisticRegressionNet" -layers { +layer { name: "data" - type: HDF5_DATA + type: "HDF5Data" top: "data" top: "label" + include { + phase: TRAIN + } hdf5_data_param { source: "examples/hdf5_classification/data/train.txt" batch_size: 10 } - include: { phase: TRAIN } } -layers { +layer { name: "data" - type: HDF5_DATA + type: "HDF5Data" top: "data" top: "label" + include { + phase: TEST + } hdf5_data_param { source: "examples/hdf5_classification/data/test.txt" batch_size: 10 } - include: { phase: TEST } } -layers { +layer { name: "fc1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "data" top: "fc1" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 2 weight_filler { @@ -42,18 +50,20 @@ layers { } } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "fc1" bottom: "label" top: "loss" } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "fc1" bottom: "label" top: "accuracy" - include: { phase: TEST } + include { + phase: TEST + } } diff --git a/examples/hdf5_classification/train_val2.prototxt b/examples/hdf5_classification/train_val2.prototxt index b6a75650ad3..f9ef731fff9 100644 --- a/examples/hdf5_classification/train_val2.prototxt +++ b/examples/hdf5_classification/train_val2.prototxt @@ -1,35 +1,43 @@ name: "LogisticRegressionNet" -layers { +layer { name: "data" - type: HDF5_DATA + type: "HDF5Data" top: "data" top: "label" + include { + phase: TRAIN + } hdf5_data_param { source: "examples/hdf5_classification/data/train.txt" batch_size: 10 } - include: { phase: TRAIN } } -layers { +layer { name: "data" - type: HDF5_DATA + type: "HDF5Data" top: "data" top: "label" + include { + phase: TEST + } hdf5_data_param { source: "examples/hdf5_classification/data/test.txt" batch_size: 10 } - include: { phase: TEST } } -layers { +layer { name: "fc1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "data" top: "fc1" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 40 weight_filler { @@ -42,21 +50,25 @@ layers { } } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "fc1" top: "fc1" } -layers { +layer { name: "fc2" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc1" top: "fc2" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 2 weight_filler { @@ -69,18 +81,20 @@ layers { } } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "fc2" bottom: "label" top: "loss" } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "fc2" bottom: "label" top: "accuracy" - include: { phase: TEST } + include { + phase: TEST + } } diff --git a/examples/imagenet/bvlc_caffenet_full_conv.prototxt b/examples/imagenet/bvlc_caffenet_full_conv.prototxt index 395b0f0162f..7b22bfa1404 100644 --- a/examples/imagenet/bvlc_caffenet_full_conv.prototxt +++ b/examples/imagenet/bvlc_caffenet_full_conv.prototxt @@ -5,9 +5,9 @@ input_dim: 1 input_dim: 3 input_dim: 451 input_dim: 451 -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" convolution_param { @@ -16,15 +16,15 @@ layers { stride: 4 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -33,9 +33,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { @@ -44,9 +44,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" convolution_param { @@ -56,15 +56,15 @@ layers { group: 2 } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -73,9 +73,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { @@ -84,9 +84,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" convolution_param { @@ -95,15 +95,15 @@ layers { kernel_size: 3 } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION + type: "Convolution" bottom: "conv3" top: "conv4" convolution_param { @@ -113,15 +113,15 @@ layers { group: 2 } } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION + type: "Convolution" bottom: "conv4" top: "conv5" convolution_param { @@ -131,15 +131,15 @@ layers { group: 2 } } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" bottom: "conv5" top: "pool5" pooling_param { @@ -148,9 +148,9 @@ layers { stride: 2 } } -layers { +layer { name: "fc6-conv" - type: CONVOLUTION + type: "Convolution" bottom: "pool5" top: "fc6-conv" convolution_param { @@ -158,24 +158,24 @@ layers { kernel_size: 6 } } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6-conv" top: "fc6-conv" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" bottom: "fc6-conv" top: "fc6-conv" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc7-conv" - type: CONVOLUTION + type: "Convolution" bottom: "fc6-conv" top: "fc7-conv" convolution_param { @@ -183,24 +183,24 @@ layers { kernel_size: 1 } } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7-conv" top: "fc7-conv" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" bottom: "fc7-conv" top: "fc7-conv" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc8-conv" - type: CONVOLUTION + type: "Convolution" bottom: "fc7-conv" top: "fc8-conv" convolution_param { @@ -208,9 +208,9 @@ layers { kernel_size: 1 } } -layers { +layer { name: "prob" - type: SOFTMAX + type: "Softmax" bottom: "fc8-conv" top: "prob" } diff --git a/examples/images/cat_gray.jpg b/examples/images/cat_gray.jpg new file mode 100644 index 00000000000..43c5ce37716 Binary files /dev/null and b/examples/images/cat_gray.jpg differ diff --git a/examples/mnist/lenet.prototxt b/examples/mnist/lenet.prototxt index 491fad1b1c0..cb42610fe1e 100644 --- a/examples/mnist/lenet.prototxt +++ b/examples/mnist/lenet.prototxt @@ -4,13 +4,17 @@ input_dim: 64 input_dim: 1 input_dim: 28 input_dim: 28 -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 20 kernel_size: 5 @@ -23,9 +27,9 @@ layers { } } } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -34,13 +38,17 @@ layers { stride: 2 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "pool1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 50 kernel_size: 5 @@ -53,9 +61,9 @@ layers { } } } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -64,13 +72,17 @@ layers { stride: 2 } } -layers { +layer { name: "ip1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool2" top: "ip1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 500 weight_filler { @@ -81,19 +93,23 @@ layers { } } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "ip1" top: "ip1" } -layers { +layer { name: "ip2" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip1" top: "ip2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 10 weight_filler { @@ -104,9 +120,9 @@ layers { } } } -layers { +layer { name: "prob" - type: SOFTMAX + type: "Softmax" bottom: "ip2" top: "prob" } diff --git a/examples/lenet/lenet_stepearly_solver.prototxt b/examples/mnist/lenet_stepearly_solver.prototxt similarity index 100% rename from examples/lenet/lenet_stepearly_solver.prototxt rename to examples/mnist/lenet_stepearly_solver.prototxt diff --git a/examples/mnist/lenet_train_test.prototxt b/examples/mnist/lenet_train_test.prototxt index 2bd960b56aa..b18fc26cfd8 100644 --- a/examples/mnist/lenet_train_test.prototxt +++ b/examples/mnist/lenet_train_test.prototxt @@ -1,42 +1,49 @@ name: "LeNet" -layers { +layer { name: "mnist" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/mnist/mnist_train_lmdb" - backend: LMDB - batch_size: 64 + include { + phase: TRAIN } transform_param { scale: 0.00390625 } - include: { phase: TRAIN } + data_param { + source: "examples/mnist/mnist_train_lmdb" + batch_size: 64 + backend: LMDB + } } -layers { +layer { name: "mnist" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/mnist/mnist_test_lmdb" - backend: LMDB - batch_size: 100 + include { + phase: TEST } transform_param { scale: 0.00390625 } - include: { phase: TEST } + data_param { + source: "examples/mnist/mnist_test_lmdb" + batch_size: 100 + backend: LMDB + } } - -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 20 kernel_size: 5 @@ -49,9 +56,9 @@ layers { } } } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -60,13 +67,17 @@ layers { stride: 2 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "pool1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 50 kernel_size: 5 @@ -79,9 +90,9 @@ layers { } } } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -90,13 +101,17 @@ layers { stride: 2 } } -layers { +layer { name: "ip1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool2" top: "ip1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 500 weight_filler { @@ -107,19 +122,23 @@ layers { } } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "ip1" top: "ip1" } -layers { +layer { name: "ip2" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip1" top: "ip2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 10 weight_filler { @@ -130,17 +149,19 @@ layers { } } } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" - include: { phase: TEST } + include { + phase: TEST + } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss" diff --git a/examples/mnist/mnist_autoencoder.prototxt b/examples/mnist/mnist_autoencoder.prototxt index 0b33781a16f..563c7c91e52 100644 --- a/examples/mnist/mnist_autoencoder.prototxt +++ b/examples/mnist/mnist_autoencoder.prototxt @@ -1,67 +1,73 @@ name: "MNISTAutoencoder" -layers { - top: "data" +layer { name: "data" - type: DATA - data_param { - source: "examples/mnist/mnist_train_lmdb" - backend: LMDB - batch_size: 100 + type: "Data" + top: "data" + include { + phase: TRAIN } transform_param { scale: 0.0039215684 } - include: { phase: TRAIN } -} -layers { - top: "data" - name: "data" - type: DATA data_param { source: "examples/mnist/mnist_train_lmdb" - backend: LMDB batch_size: 100 + backend: LMDB + } +} +layer { + name: "data" + type: "Data" + top: "data" + include { + phase: TEST + stage: "test-on-train" } transform_param { scale: 0.0039215684 } - include: { - phase: TEST - stage: 'test-on-train' + data_param { + source: "examples/mnist/mnist_train_lmdb" + batch_size: 100 + backend: LMDB } } -layers { - top: "data" +layer { name: "data" - type: DATA - data_param { - source: "examples/mnist/mnist_test_lmdb" - backend: LMDB - batch_size: 100 + type: "Data" + top: "data" + include { + phase: TEST + stage: "test-on-test" } transform_param { scale: 0.0039215684 } - include: { - phase: TEST - stage: 'test-on-test' + data_param { + source: "examples/mnist/mnist_test_lmdb" + batch_size: 100 + backend: LMDB } } -layers { +layer { + name: "flatdata" + type: "Flatten" bottom: "data" top: "flatdata" - name: "flatdata" - type: FLATTEN } -layers { +layer { + name: "encode1" + type: "InnerProduct" bottom: "data" top: "encode1" - name: "encode1" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 1 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 1 + decay_mult: 0 + } inner_product_param { num_output: 1000 weight_filler { @@ -75,21 +81,25 @@ layers { } } } -layers { +layer { + name: "encode1neuron" + type: "Sigmoid" bottom: "encode1" top: "encode1neuron" - name: "encode1neuron" - type: SIGMOID } -layers { +layer { + name: "encode2" + type: "InnerProduct" bottom: "encode1neuron" top: "encode2" - name: "encode2" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 1 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 1 + decay_mult: 0 + } inner_product_param { num_output: 500 weight_filler { @@ -103,21 +113,25 @@ layers { } } } -layers { +layer { + name: "encode2neuron" + type: "Sigmoid" bottom: "encode2" top: "encode2neuron" - name: "encode2neuron" - type: SIGMOID } -layers { +layer { + name: "encode3" + type: "InnerProduct" bottom: "encode2neuron" top: "encode3" - name: "encode3" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 1 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 1 + decay_mult: 0 + } inner_product_param { num_output: 250 weight_filler { @@ -131,21 +145,25 @@ layers { } } } -layers { +layer { + name: "encode3neuron" + type: "Sigmoid" bottom: "encode3" top: "encode3neuron" - name: "encode3neuron" - type: SIGMOID } -layers { +layer { + name: "encode4" + type: "InnerProduct" bottom: "encode3neuron" top: "encode4" - name: "encode4" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 1 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 1 + decay_mult: 0 + } inner_product_param { num_output: 30 weight_filler { @@ -159,15 +177,19 @@ layers { } } } -layers { +layer { + name: "decode4" + type: "InnerProduct" bottom: "encode4" top: "decode4" - name: "decode4" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 1 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 1 + decay_mult: 0 + } inner_product_param { num_output: 250 weight_filler { @@ -181,21 +203,25 @@ layers { } } } -layers { +layer { + name: "decode4neuron" + type: "Sigmoid" bottom: "decode4" top: "decode4neuron" - name: "decode4neuron" - type: SIGMOID } -layers { +layer { + name: "decode3" + type: "InnerProduct" bottom: "decode4neuron" top: "decode3" - name: "decode3" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 1 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 1 + decay_mult: 0 + } inner_product_param { num_output: 500 weight_filler { @@ -209,21 +235,25 @@ layers { } } } -layers { +layer { + name: "decode3neuron" + type: "Sigmoid" bottom: "decode3" top: "decode3neuron" - name: "decode3neuron" - type: SIGMOID } -layers { +layer { + name: "decode2" + type: "InnerProduct" bottom: "decode3neuron" top: "decode2" - name: "decode2" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 1 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 1 + decay_mult: 0 + } inner_product_param { num_output: 1000 weight_filler { @@ -237,21 +267,25 @@ layers { } } } -layers { +layer { + name: "decode2neuron" + type: "Sigmoid" bottom: "decode2" top: "decode2neuron" - name: "decode2neuron" - type: SIGMOID } -layers { +layer { + name: "decode1" + type: "InnerProduct" bottom: "decode2neuron" top: "decode1" - name: "decode1" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 1 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 1 + decay_mult: 0 + } inner_product_param { num_output: 784 weight_filler { @@ -265,25 +299,25 @@ layers { } } } -layers { +layer { + name: "loss" + type: "SigmoidCrossEntropyLoss" bottom: "decode1" bottom: "flatdata" top: "cross_entropy_loss" - name: "loss" - type: SIGMOID_CROSS_ENTROPY_LOSS loss_weight: 1 } -layers { +layer { + name: "decode1neuron" + type: "Sigmoid" bottom: "decode1" top: "decode1neuron" - name: "decode1neuron" - type: SIGMOID } -layers { +layer { + name: "loss" + type: "EuclideanLoss" bottom: "decode1neuron" bottom: "flatdata" top: "l2_error" - name: "loss" - type: EUCLIDEAN_LOSS loss_weight: 0 } diff --git a/examples/net_surgery.ipynb b/examples/net_surgery.ipynb index a4fb4a66065..2932687da6a 100644 --- a/examples/net_surgery.ipynb +++ b/examples/net_surgery.ipynb @@ -4,7 +4,7 @@ "example_name": "Editing model parameters", "include_in_docs": true, "priority": 5, - "signature": "sha256:bf84bcbd78fe007310f86d71c5969ba4205b6e06f408029860eec94821844bee" + "signature": "sha256:811097f2151652d2b630c016a5f1de23bd824df3dfcfc72aa0aeb23b2d9686c0" }, "nbformat": 3, "nbformat_minor": 0, @@ -28,7 +28,7 @@ "cell_type": "code", "collapsed": false, "input": [ - "!diff imagenet/imagenet_full_conv.prototxt ../models/bvlc_reference_caffenet/deploy.prototxt" + "!diff imagenet/bvlc_caffenet_full_conv.prototxt ../models/bvlc_reference_caffenet/deploy.prototxt" ], "language": "python", "metadata": {}, @@ -146,7 +146,9 @@ "import caffe\n", "\n", "# Load the original network and extract the fully-connected layers' parameters.\n", - "net = caffe.Net('../models/bvlc_reference_caffenet/deploy.prototxt', '../models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel')\n", + "net = caffe.Net('../models/bvlc_reference_caffenet/deploy.prototxt', \n", + " '../models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel', \n", + " caffe.TEST)\n", "params = ['fc6', 'fc7', 'fc8']\n", "# fc_params = {name: (weights, biases)}\n", "fc_params = {pr: (net.params[pr][0].data, net.params[pr][1].data) for pr in params}\n", @@ -181,7 +183,9 @@ "collapsed": false, "input": [ "# Load the fully-convolutional network to transplant the parameters.\n", - "net_full_conv = caffe.Net('imagenet/bvlc_caffenet_full_conv.prototxt', '../models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel')\n", + "net_full_conv = caffe.Net('imagenet/bvlc_caffenet_full_conv.prototxt', \n", + " '../models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel',\n", + " caffe.TEST)\n", "params_full_conv = ['fc6-conv', 'fc7-conv', 'fc8-conv']\n", "# conv_params = {name: (weights, biases)}\n", "conv_params = {pr: (net_full_conv.params[pr][0].data, net_full_conv.params[pr][1].data) for pr in params_full_conv}\n", @@ -279,19 +283,19 @@ "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "\n", - "caffe.set_phase_test()\n", - "\n", "# load input and configure preprocessing\n", "im = caffe.io.load_image('images/cat.jpg')\n", - "net_full_conv.set_mean('data', np.load('../python/caffe/imagenet/ilsvrc_2012_mean.npy'))\n", - "net_full_conv.set_channel_swap('data', (2,1,0))\n", - "net_full_conv.set_raw_scale('data', 255.0)\n", + "transformer = caffe.io.Transformer({'data': net_full_conv.blobs['data'].data.shape})\n", + "transformer.set_mean('data', np.load('../python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1))\n", + "transformer.set_transpose('data', (2,0,1))\n", + "transformer.set_channel_swap('data', (2,1,0))\n", + "transformer.set_raw_scale('data', 255.0)\n", "# make classification map by forward and print prediction indices at each location\n", - "out = net_full_conv.forward_all(data=np.asarray([net_full_conv.preprocess('data', im)]))\n", + "out = net_full_conv.forward_all(data=np.asarray([transformer.preprocess('data', im)]))\n", "print out['prob'][0].argmax(axis=0)\n", "# show net input and confidence map (probability of the top prediction at each location)\n", "plt.subplot(1, 2, 1)\n", - "plt.imshow(net_full_conv.deprocess('data', net_full_conv.blobs['data'].data[0]))\n", + "plt.imshow(transformer.deprocess('data', net_full_conv.blobs['data'].data[0]))\n", "plt.subplot(1, 2, 2)\n", "plt.imshow(out['prob'][0].max(axis=0))" ], diff --git a/examples/siamese/convert_mnist_siamese_data.cpp b/examples/siamese/convert_mnist_siamese_data.cpp index 400d15a2705..71c56a0ae61 100644 --- a/examples/siamese/convert_mnist_siamese_data.cpp +++ b/examples/siamese/convert_mnist_siamese_data.cpp @@ -36,7 +36,7 @@ void convert_dataset(const char* image_filename, const char* label_filename, std::ifstream image_file(image_filename, std::ios::in | std::ios::binary); std::ifstream label_file(label_filename, std::ios::in | std::ios::binary); CHECK(image_file) << "Unable to open file " << image_filename; - CHECK(label_file) << "Unable to open file " << label_file; + CHECK(label_file) << "Unable to open file " << label_filename; // Read the magic and the meta data uint32_t magic; uint32_t num_items; diff --git a/examples/siamese/mnist_siamese.prototxt b/examples/siamese/mnist_siamese.prototxt index 8dd42e9c1b5..0e903f85909 100644 --- a/examples/siamese/mnist_siamese.prototxt +++ b/examples/siamese/mnist_siamese.prototxt @@ -4,23 +4,26 @@ input_dim: 10000 input_dim: 1 input_dim: 28 input_dim: 28 - -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 20 kernel_size: 5 stride: 1 } } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -29,22 +32,26 @@ layers { stride: 2 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "pool1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } convolution_param { num_output: 50 kernel_size: 5 stride: 1 } } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -53,42 +60,53 @@ layers { stride: 2 } } -layers { +layer { name: "ip1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool2" top: "ip1" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 500 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "ip1" top: "ip1" } -layers { +layer { name: "ip2" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip1" top: "ip2" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 10 } } - -layers { +layer { name: "feat" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip2" top: "feat" - blobs_lr: 1 - blobs_lr: 2 + param { + lr_mult: 1 + } + param { + lr_mult: 2 + } inner_product_param { num_output: 2 } diff --git a/examples/siamese/mnist_siamese_train_test.prototxt b/examples/siamese/mnist_siamese_train_test.prototxt index 92361c31dc7..8ff864f556f 100644 --- a/examples/siamese/mnist_siamese_train_test.prototxt +++ b/examples/siamese/mnist_siamese_train_test.prototxt @@ -1,50 +1,60 @@ name: "mnist_siamese_train_test" -layers { +layer { name: "pair_data" - type: DATA + type: "Data" top: "pair_data" top: "sim" + include { + phase: TRAIN + } + transform_param { + scale: 0.00390625 + } data_param { source: "examples/siamese/mnist_siamese_train_leveldb" - scale: 0.00390625 batch_size: 64 } - include: { phase: TRAIN } } -layers { +layer { name: "pair_data" - type: DATA + type: "Data" top: "pair_data" top: "sim" + include { + phase: TEST + } + transform_param { + scale: 0.00390625 + } data_param { source: "examples/siamese/mnist_siamese_test_leveldb" - scale: 0.00390625 batch_size: 100 } - include: { phase: TEST } } -layers { - name: "slice_pair" - type: SLICE - bottom: "pair_data" - top: "data" - top: "data_p" - slice_param { - slice_dim: 1 - slice_point: 1 - } +layer { + name: "slice_pair" + type: "Slice" + bottom: "pair_data" + top: "data" + top: "data_p" + slice_param { + slice_dim: 1 + slice_point: 1 + } } - - - - -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "conv1_w" + lr_mult: 1 + } + param { + name: "conv1_b" + lr_mult: 2 + } convolution_param { num_output: 20 kernel_size: 5 @@ -56,12 +66,10 @@ layers { type: "constant" } } - param: "conv1_w" - param: "conv1_b" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -70,13 +78,19 @@ layers { stride: 2 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "pool1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "conv2_w" + lr_mult: 1 + } + param { + name: "conv2_b" + lr_mult: 2 + } convolution_param { num_output: 50 kernel_size: 5 @@ -88,12 +102,10 @@ layers { type: "constant" } } - param: "conv2_w" - param: "conv2_b" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -102,13 +114,19 @@ layers { stride: 2 } } -layers { +layer { name: "ip1" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool2" top: "ip1" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "ip1_w" + lr_mult: 1 + } + param { + name: "ip1_b" + lr_mult: 2 + } inner_product_param { num_output: 500 weight_filler { @@ -118,22 +136,26 @@ layers { type: "constant" } } - param: "ip1_w" - param: "ip1_b" } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "ip1" top: "ip1" } -layers { +layer { name: "ip2" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip1" top: "ip2" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "ip2_w" + lr_mult: 1 + } + param { + name: "ip2_b" + lr_mult: 2 + } inner_product_param { num_output: 10 weight_filler { @@ -143,17 +165,20 @@ layers { type: "constant" } } - param: "ip2_w" - param: "ip2_b" } - -layers { +layer { name: "feat" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip2" top: "feat" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "feat_w" + lr_mult: 1 + } + param { + name: "feat_b" + lr_mult: 2 + } inner_product_param { num_output: 2 weight_filler { @@ -163,19 +188,20 @@ layers { type: "constant" } } - param: "feat_w" - param: "feat_b" } - - - -layers { +layer { name: "conv1_p" - type: CONVOLUTION + type: "Convolution" bottom: "data_p" top: "conv1_p" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "conv1_w" + lr_mult: 1 + } + param { + name: "conv1_b" + lr_mult: 2 + } convolution_param { num_output: 20 kernel_size: 5 @@ -187,12 +213,10 @@ layers { type: "constant" } } - param: "conv1_w" - param: "conv1_b" } -layers { +layer { name: "pool1_p" - type: POOLING + type: "Pooling" bottom: "conv1_p" top: "pool1_p" pooling_param { @@ -201,13 +225,19 @@ layers { stride: 2 } } -layers { +layer { name: "conv2_p" - type: CONVOLUTION + type: "Convolution" bottom: "pool1_p" top: "conv2_p" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "conv2_w" + lr_mult: 1 + } + param { + name: "conv2_b" + lr_mult: 2 + } convolution_param { num_output: 50 kernel_size: 5 @@ -219,12 +249,10 @@ layers { type: "constant" } } - param: "conv2_w" - param: "conv2_b" } -layers { +layer { name: "pool2_p" - type: POOLING + type: "Pooling" bottom: "conv2_p" top: "pool2_p" pooling_param { @@ -233,13 +261,19 @@ layers { stride: 2 } } -layers { +layer { name: "ip1_p" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool2_p" top: "ip1_p" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "ip1_w" + lr_mult: 1 + } + param { + name: "ip1_b" + lr_mult: 2 + } inner_product_param { num_output: 500 weight_filler { @@ -249,22 +283,26 @@ layers { type: "constant" } } - param: "ip1_w" - param: "ip1_b" } -layers { +layer { name: "relu1_p" - type: RELU + type: "ReLU" bottom: "ip1_p" top: "ip1_p" } -layers { +layer { name: "ip2_p" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip1_p" top: "ip2_p" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "ip2_w" + lr_mult: 1 + } + param { + name: "ip2_b" + lr_mult: 2 + } inner_product_param { num_output: 10 weight_filler { @@ -274,17 +312,20 @@ layers { type: "constant" } } - param: "ip2_w" - param: "ip2_b" } - -layers { +layer { name: "feat_p" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "ip2_p" top: "feat_p" - blobs_lr: 1 - blobs_lr: 2 + param { + name: "feat_w" + lr_mult: 1 + } + param { + name: "feat_b" + lr_mult: 2 + } inner_product_param { num_output: 2 weight_filler { @@ -294,20 +335,15 @@ layers { type: "constant" } } - param: "feat_w" - param: "feat_b" } - - - -layers { - name: "loss" - type: CONTRASTIVE_LOSS - contrastive_loss_param { - margin: 1.0 - } - bottom: "feat" - bottom: "feat_p" - bottom: "sim" - top: "loss" +layer { + name: "loss" + type: "ContrastiveLoss" + bottom: "feat" + bottom: "feat_p" + bottom: "sim" + top: "loss" + contrastive_loss_param { + margin: 1 + } } diff --git a/examples/web_demo/app.py b/examples/web_demo/app.py index d33fc92f078..e456526fa55 100644 --- a/examples/web_demo/app.py +++ b/examples/web_demo/app.py @@ -112,7 +112,7 @@ class ImagenetClassifier(object): if not os.path.exists(val): raise Exception( "File for {} is missing. Should be at: {}".format(key, val)) - default_args['image_dim'] = 227 + default_args['image_dim'] = 256 default_args['raw_scale'] = 255. default_args['gpu_mode'] = False diff --git a/include/caffe/blob.hpp b/include/caffe/blob.hpp index ef10aea53f0..42e4420408c 100644 --- a/include/caffe/blob.hpp +++ b/include/caffe/blob.hpp @@ -106,6 +106,15 @@ class Blob { Dtype asum_data() const; /// @brief Compute the sum of absolute values (L1 norm) of the diff. Dtype asum_diff() const; + /// @brief Compute the sum of squares (L2 norm squared) of the data. + Dtype sumsq_data() const; + /// @brief Compute the sum of squares (L2 norm squared) of the diff. + Dtype sumsq_diff() const; + + /// @brief Scale the blob data by a constant factor. + void scale_data(Dtype scale_factor); + /// @brief Scale the blob diff by a constant factor. + void scale_diff(Dtype scale_factor); /** * @brief Set the data_ shared_ptr to point to the SyncedMemory holding the diff --git a/include/caffe/caffe.hpp b/include/caffe/caffe.hpp index 0af9ef04c43..3c829f2f9b0 100644 --- a/include/caffe/caffe.hpp +++ b/include/caffe/caffe.hpp @@ -8,6 +8,7 @@ #include "caffe/common.hpp" #include "caffe/filler.hpp" #include "caffe/layer.hpp" +#include "caffe/layer_factory.hpp" #include "caffe/net.hpp" #include "caffe/proto/caffe.pb.h" #include "caffe/solver.hpp" diff --git a/include/caffe/common.hpp b/include/caffe/common.hpp index 9c6eb4d6834..890673cd7e6 100644 --- a/include/caffe/common.hpp +++ b/include/caffe/common.hpp @@ -19,7 +19,7 @@ // gflags 2.1 issue: namespace google was changed to gflags without warning. // Luckily we will be able to use GFLAGS_GFAGS_H_ to detect if it is version -// 2.1. If yes , we will add a temporary solution to redirect the namespace. +// 2.1. If yes, we will add a temporary solution to redirect the namespace. // TODO(Yangqing): Once gflags solves the problem in a more elegant way, let's // remove the following hack. #ifndef GFLAGS_GFLAGS_H_ @@ -34,13 +34,39 @@ private:\ // Instantiate a class with float and double specifications. #define INSTANTIATE_CLASS(classname) \ + char gInstantiationGuard##classname; \ template class classname; \ template class classname +#define INSTANTIATE_LAYER_GPU_FORWARD(classname) \ + template void classname::Forward_gpu( \ + const std::vector*>& bottom, \ + const std::vector*>& top); \ + template void classname::Forward_gpu( \ + const std::vector*>& bottom, \ + const std::vector*>& top); + +#define INSTANTIATE_LAYER_GPU_BACKWARD(classname) \ + template void classname::Backward_gpu( \ + const std::vector*>& top, \ + const std::vector& propagate_down, \ + const std::vector*>& bottom); \ + template void classname::Backward_gpu( \ + const std::vector*>& top, \ + const std::vector& propagate_down, \ + const std::vector*>& bottom) + +#define INSTANTIATE_LAYER_GPU_FUNCS(classname) \ + INSTANTIATE_LAYER_GPU_FORWARD(classname); \ + INSTANTIATE_LAYER_GPU_BACKWARD(classname) + // A simple macro to mark codes that are not implemented, so that when the code // is executed we will see a fatal log. #define NOT_IMPLEMENTED LOG(FATAL) << "Not Implemented Yet" +// See PR #1236 +namespace cv {class Mat;} + namespace caffe { // We will use the boost shared_ptr instead of the new C++11 one mainly @@ -51,6 +77,7 @@ using boost::shared_ptr; using std::fstream; using std::ios; using std::isnan; +using std::isinf; using std::iterator; using std::make_pair; using std::map; @@ -77,8 +104,6 @@ class Caffe { return *singleton_; } enum Brew { CPU, GPU }; - enum Phase { TRAIN, TEST }; - // This random number generator facade hides boost and CUDA rng // implementation from one another (for cross-platform compatibility). @@ -110,16 +135,12 @@ class Caffe { // Returns the mode: running on CPU or GPU. inline static Brew mode() { return Get().mode_; } - // Returns the phase: TRAIN or TEST. - inline static Phase phase() { return Get().phase_; } // The setters for the variables // Sets the mode. It is recommended that you don't change the mode halfway // into the program since that may cause allocation of pinned memory being // freed in a non-pinned way, which may cause problems - I haven't verified // it personally but better to note it here in the header file. inline static void set_mode(Brew mode) { Get().mode_ = mode; } - // Sets the phase. - inline static void set_phase(Phase phase) { Get().phase_ = phase; } // Sets the random seed of both boost and curand static void set_random_seed(const unsigned int seed); // Sets the device. Since we have cublas and curand stuff, set device also @@ -136,7 +157,6 @@ class Caffe { shared_ptr random_generator_; Brew mode_; - Phase phase_; static shared_ptr singleton_; private: diff --git a/include/caffe/common_layers.hpp b/include/caffe/common_layers.hpp index 1f945ca34e9..c67822c3738 100644 --- a/include/caffe/common_layers.hpp +++ b/include/caffe/common_layers.hpp @@ -39,13 +39,11 @@ class ArgMaxLayer : public Layer { explicit ArgMaxLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_ARGMAX; - } + virtual inline const char* type() const { return "ArgMax"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } @@ -62,10 +60,10 @@ class ArgMaxLayer : public Layer { * @f$ (for @f$ K = 1 @f$). */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /// @brief Not implemented (non-differentiable function) virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { NOT_IMPLEMENTED; } bool out_max_val_; @@ -82,13 +80,11 @@ class ConcatLayer : public Layer { explicit ConcatLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_CONCAT; - } + virtual inline const char* type() const { return "Concat"; } virtual inline int MinBottomBlobs() const { return 2; } virtual inline int ExactNumTopBlobs() const { return 1; } @@ -110,9 +106,9 @@ class ConcatLayer : public Layer { * @f$ */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the error gradient w.r.t. the concatenate inputs. @@ -137,9 +133,9 @@ class ConcatLayer : public Layer { * @f$ */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); Blob col_bob_; int count_; @@ -162,25 +158,23 @@ class EltwiseLayer : public Layer { explicit EltwiseLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_ELTWISE; - } + virtual inline const char* type() const { return "Eltwise"; } virtual inline int MinBottomBlobs() const { return 2; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); EltwiseParameter_EltwiseOp op_; vector coeffs_; @@ -205,11 +199,9 @@ class FlattenLayer : public Layer { explicit FlattenLayer(const LayerParameter& param) : Layer(param) {} virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_FLATTEN; - } + virtual inline const char* type() const { return "Flatten"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } @@ -223,9 +215,9 @@ class FlattenLayer : public Layer { * the outputs -- i.e., the (virtually) copied, flattened inputs */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the error gradient w.r.t. the concatenate inputs. @@ -237,9 +229,9 @@ class FlattenLayer : public Layer { * gradient is (virtually) copied */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); int count_; }; @@ -256,25 +248,23 @@ class InnerProductLayer : public Layer { explicit InnerProductLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_INNER_PRODUCT; - } + virtual inline const char* type() const { return "InnerProduct"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); int M_; int K_; @@ -294,23 +284,21 @@ class MVNLayer : public Layer { explicit MVNLayer(const LayerParameter& param) : Layer(param) {} virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_MVN; - } + virtual inline const char* type() const { return "MVN"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); Blob mean_, variance_, temp_; @@ -328,25 +316,23 @@ class SilenceLayer : public Layer { explicit SilenceLayer(const LayerParameter& param) : Layer(param) {} virtual void Reshape(const vector*>& bottom, - vector*>* top) {} + const vector*>& top) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_SILENCE; - } + virtual inline const char* type() const { return "Silence"; } virtual inline int MinBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 0; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top) {} + const vector*>& top) {} // We can't define Forward_gpu here, since STUB_GPU will provide // its own definition for CPU_ONLY mode. virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); }; /** @@ -360,23 +346,21 @@ class SoftmaxLayer : public Layer { explicit SoftmaxLayer(const LayerParameter& param) : Layer(param) {} virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_SOFTMAX; - } + virtual inline const char* type() const { return "Softmax"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); /// sum_multiplier is used to carry out sum using BLAS Blob sum_multiplier_; @@ -393,19 +377,20 @@ template class CuDNNSoftmaxLayer : public SoftmaxLayer { public: explicit CuDNNSoftmaxLayer(const LayerParameter& param) - : SoftmaxLayer(param) {} + : SoftmaxLayer(param), handles_setup_(false) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual ~CuDNNSoftmaxLayer(); protected: virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); + bool handles_setup_; cudnnHandle_t handle_; cudnnTensor4dDescriptor_t bottom_desc_; cudnnTensor4dDescriptor_t top_desc_; @@ -424,23 +409,21 @@ class SplitLayer : public Layer { explicit SplitLayer(const LayerParameter& param) : Layer(param) {} virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_SPLIT; - } + virtual inline const char* type() const { return "Split"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int MinTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); int count_; }; @@ -457,25 +440,23 @@ class SliceLayer : public Layer { explicit SliceLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_SLICE; - } + virtual inline const char* type() const { return "Slice"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int MinTopBlobs() const { return 2; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); Blob col_bob_; int count_; diff --git a/include/caffe/data_layers.hpp b/include/caffe/data_layers.hpp index 8e2637b0658..1f154408c27 100644 --- a/include/caffe/data_layers.hpp +++ b/include/caffe/data_layers.hpp @@ -7,8 +7,6 @@ #include "boost/scoped_ptr.hpp" #include "hdf5.h" -#include "leveldb/db.h" -#include "lmdb.h" #include "caffe/blob.hpp" #include "caffe/common.hpp" @@ -16,13 +14,12 @@ #include "caffe/filler.hpp" #include "caffe/internal_thread.hpp" #include "caffe/layer.hpp" +#include "caffe/net.hpp" #include "caffe/proto/caffe.pb.h" +#include "caffe/util/db.hpp" namespace caffe { -#define HDF5_DATA_DATASET_NAME "data" -#define HDF5_DATA_LABEL_NAME "label" - /** * @brief Provides base for data layers that feed blobs to the Net. * @@ -37,33 +34,21 @@ class BaseDataLayer : public Layer { // DataLayerSetUp to do special data layer setup for individual layer types. // This method may not be overridden except by the BasePrefetchingDataLayer. virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void DataLayerSetUp(const vector*>& bottom, - vector*>* top) {} + const vector*>& top) {} // Data layers have no bottoms, so reshaping is trivial. virtual void Reshape(const vector*>& bottom, - vector*>* top) {} + const vector*>& top) {} virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) {} + const vector& propagate_down, const vector*>& bottom) {} virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) {} - - int datum_channels() const { return datum_channels_; } - int datum_height() const { return datum_height_; } - int datum_width() const { return datum_width_; } - int datum_size() const { return datum_size_; } + const vector& propagate_down, const vector*>& bottom) {} protected: TransformationParameter transform_param_; - DataTransformer data_transformer_; - int datum_channels_; - int datum_height_; - int datum_width_; - int datum_size_; - Blob data_mean_; - const Dtype* mean_; - Caffe::Phase phase_; + shared_ptr > data_transformer_; bool output_labels_; }; @@ -78,12 +63,12 @@ class BasePrefetchingDataLayer : // DataLayerSetUp to do special data layer setup for individual layer types. // This method may not be overridden. void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void CreatePrefetchThread(); virtual void JoinPrefetchThread(); @@ -93,6 +78,7 @@ class BasePrefetchingDataLayer : protected: Blob prefetch_data_; Blob prefetch_label_; + Blob transformed_data_; }; template @@ -102,11 +88,9 @@ class DataLayer : public BasePrefetchingDataLayer { : BasePrefetchingDataLayer(param) {} virtual ~DataLayer(); virtual void DataLayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_DATA; - } + virtual inline const char* type() const { return "Data"; } virtual inline int ExactNumBottomBlobs() const { return 0; } virtual inline int MinTopBlobs() const { return 1; } virtual inline int MaxTopBlobs() const { return 2; } @@ -114,15 +98,8 @@ class DataLayer : public BasePrefetchingDataLayer { protected: virtual void InternalThreadEntry(); - // LEVELDB - shared_ptr db_; - shared_ptr iter_; - // LMDB - MDB_env* mdb_env_; - MDB_dbi mdb_dbi_; - MDB_txn* mdb_txn_; - MDB_cursor* mdb_cursor_; - MDB_val mdb_key_, mdb_value_; + shared_ptr db_; + shared_ptr cursor_; }; /** @@ -136,24 +113,22 @@ class DummyDataLayer : public Layer { explicit DummyDataLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); // Data layers have no bottoms, so reshaping is trivial. virtual void Reshape(const vector*>& bottom, - vector*>* top) {} + const vector*>& top) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_DUMMY_DATA; - } + virtual inline const char* type() const { return "DummyData"; } virtual inline int ExactNumBottomBlobs() const { return 0; } virtual inline int MinTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) {} + const vector& propagate_down, const vector*>& bottom) {} virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) {} + const vector& propagate_down, const vector*>& bottom) {} vector > > fillers_; vector refill_; @@ -171,34 +146,31 @@ class HDF5DataLayer : public Layer { : Layer(param) {} virtual ~HDF5DataLayer(); virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); // Data layers have no bottoms, so reshaping is trivial. virtual void Reshape(const vector*>& bottom, - vector*>* top) {} + const vector*>& top) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_HDF5_DATA; - } + virtual inline const char* type() const { return "HDF5Data"; } virtual inline int ExactNumBottomBlobs() const { return 0; } - virtual inline int ExactNumTopBlobs() const { return 2; } + virtual inline int MinTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) {} + const vector& propagate_down, const vector*>& bottom) {} virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) {} + const vector& propagate_down, const vector*>& bottom) {} virtual void LoadHDF5FileData(const char* filename); std::vector hdf_filenames_; unsigned int num_files_; unsigned int current_file_; hsize_t current_row_; - Blob data_blob_; - Blob label_blob_; + std::vector > > hdf_blobs_; }; /** @@ -209,17 +181,16 @@ class HDF5DataLayer : public Layer { template class HDF5OutputLayer : public Layer { public: - explicit HDF5OutputLayer(const LayerParameter& param); + explicit HDF5OutputLayer(const LayerParameter& param) + : Layer(param), file_opened_(false) {} virtual ~HDF5OutputLayer(); virtual void LayerSetUp(const vector*>& bottom, - vector*>* top) {} + const vector*>& top); // Data layers have no bottoms, so reshaping is trivial. virtual void Reshape(const vector*>& bottom, - vector*>* top) {} + const vector*>& top) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_HDF5_OUTPUT; - } + virtual inline const char* type() const { return "HDF5Output"; } // TODO: no limit on the number of blobs virtual inline int ExactNumBottomBlobs() const { return 2; } virtual inline int ExactNumTopBlobs() const { return 0; } @@ -228,15 +199,16 @@ class HDF5OutputLayer : public Layer { protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void SaveBlobs(); + bool file_opened_; std::string file_name_; hid_t file_id_; Blob data_blob_; @@ -255,11 +227,9 @@ class ImageDataLayer : public BasePrefetchingDataLayer { : BasePrefetchingDataLayer(param) {} virtual ~ImageDataLayer(); virtual void DataLayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_IMAGE_DATA; - } + virtual inline const char* type() const { return "ImageData"; } virtual inline int ExactNumBottomBlobs() const { return 0; } virtual inline int ExactNumTopBlobs() const { return 2; } @@ -283,31 +253,35 @@ class MemoryDataLayer : public BaseDataLayer { explicit MemoryDataLayer(const LayerParameter& param) : BaseDataLayer(param), has_new_data_(false) {} virtual void DataLayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_MEMORY_DATA; - } + virtual inline const char* type() const { return "MemoryData"; } virtual inline int ExactNumBottomBlobs() const { return 0; } virtual inline int ExactNumTopBlobs() const { return 2; } virtual void AddDatumVector(const vector& datum_vector); + virtual void AddMatVector(const vector& mat_vector, + const vector& labels); // Reset should accept const pointers, but can't, because the memory // will be given to Blob, which is mutable void Reset(Dtype* data, Dtype* label, int n); + void set_batch_size(int new_size); int batch_size() { return batch_size_; } + int channels() { return channels_; } + int height() { return height_; } + int width() { return width_; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); - int batch_size_; + int batch_size_, channels_, height_, width_, size_; Dtype* data_; Dtype* labels_; int n_; - int pos_; + size_t pos_; Blob added_data_; Blob added_label_; bool has_new_data_; @@ -326,11 +300,9 @@ class WindowDataLayer : public BasePrefetchingDataLayer { : BasePrefetchingDataLayer(param) {} virtual ~WindowDataLayer(); virtual void DataLayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_WINDOW_DATA; - } + virtual inline const char* type() const { return "WindowData"; } virtual inline int ExactNumBottomBlobs() const { return 0; } virtual inline int ExactNumTopBlobs() const { return 2; } @@ -343,6 +315,12 @@ class WindowDataLayer : public BasePrefetchingDataLayer { enum WindowField { IMAGE_INDEX, LABEL, OVERLAP, X1, Y1, X2, Y2, NUM }; vector > fg_windows_; vector > bg_windows_; + Blob data_mean_; + vector mean_values_; + bool has_mean_file_; + bool has_mean_values_; + bool cache_images_; + vector > image_database_cache_; }; } // namespace caffe diff --git a/include/caffe/data_transformer.hpp b/include/caffe/data_transformer.hpp index 5d5134f5db5..880356601a4 100644 --- a/include/caffe/data_transformer.hpp +++ b/include/caffe/data_transformer.hpp @@ -1,6 +1,9 @@ #ifndef CAFFE_DATA_TRANSFORMER_HPP #define CAFFE_DATA_TRANSFORMER_HPP +#include + +#include "caffe/blob.hpp" #include "caffe/common.hpp" #include "caffe/proto/caffe.pb.h" @@ -13,40 +16,97 @@ namespace caffe { template class DataTransformer { public: - explicit DataTransformer(const TransformationParameter& param) - : param_(param) { - phase_ = Caffe::phase(); - } + explicit DataTransformer(const TransformationParameter& param, Phase phase); virtual ~DataTransformer() {} + /** + * @brief Initialize the Random number generations if needed by the + * transformation. + */ void InitRand(); /** * @brief Applies the transformation defined in the data layer's * transform_param block to the data. * - * @param batch_item_id - * Datum position within the batch. This is used to compute the - * writing position in the top blob's data * @param datum * Datum containing the data to be transformed. - * @param mean - * @param transformed_data - * This is meant to be the top blob's data. The transformed data will be - * written at the appropriate place within the blob's data. + * @param transformed_blob + * This is destination blob. It can be part of top blob's data if + * set_cpu_data() is used. See data_layer.cpp for an example. + */ + void Transform(const Datum& datum, Blob* transformed_blob); + + /** + * @brief Applies the transformation defined in the data layer's + * transform_param block to a vector of Datum. + * + * @param datum_vector + * A vector of Datum containing the data to be transformed. + * @param transformed_blob + * This is destination blob. It can be part of top blob's data if + * set_cpu_data() is used. See memory_layer.cpp for an example. */ - void Transform(const int batch_item_id, const Datum& datum, - const Dtype* mean, Dtype* transformed_data); + void Transform(const vector & datum_vector, + Blob* transformed_blob); + + /** + * @brief Applies the transformation defined in the data layer's + * transform_param block to a vector of Mat. + * + * @param mat_vector + * A vector of Mat containing the data to be transformed. + * @param transformed_blob + * This is destination blob. It can be part of top blob's data if + * set_cpu_data() is used. See memory_layer.cpp for an example. + */ + void Transform(const vector & mat_vector, + Blob* transformed_blob); + /** + * @brief Applies the transformation defined in the data layer's + * transform_param block to a cv::Mat + * + * @param cv_img + * cv::Mat containing the data to be transformed. + * @param transformed_blob + * This is destination blob. It can be part of top blob's data if + * set_cpu_data() is used. See image_data_layer.cpp for an example. + */ + void Transform(const cv::Mat& cv_img, Blob* transformed_blob); + + /** + * @brief Applies the same transformation defined in the data layer's + * transform_param block to all the num images in a input_blob. + * + * @param input_blob + * A Blob containing the data to be transformed. It applies the same + * transformation to all the num images in the blob. + * @param transformed_blob + * This is destination blob, it will contain as many images as the + * input blob. It can be part of top blob's data. + */ + void Transform(Blob* input_blob, Blob* transformed_blob); protected: - virtual unsigned int Rand(); + /** + * @brief Generates a random integer from Uniform({0, 1, ..., n-1}). + * + * @param n + * The upperbound (exclusive) value of the random number. + * @return + * A uniformly random integer value from ({0, 1, ..., n-1}). + */ + virtual int Rand(int n); + void Transform(const Datum& datum, Dtype* transformed_data); // Tranformation parameters TransformationParameter param_; shared_ptr rng_; - Caffe::Phase phase_; + Phase phase_; + Blob data_mean_; + vector mean_values_; }; } // namespace caffe diff --git a/include/caffe/filler.hpp b/include/caffe/filler.hpp index 136ce958aed..eebf565b1d5 100644 --- a/include/caffe/filler.hpp +++ b/include/caffe/filler.hpp @@ -76,13 +76,13 @@ class GaussianFiller : public Filler { CHECK_GE(sparse, -1); if (sparse >= 0) { // Sparse initialization is implemented for "weight" blobs; i.e. matrices. - // These have num == channels == 1; height is number of inputs; width is + // These have num == channels == 1; width is number of inputs; height is // number of outputs. The 'sparse' variable specifies the mean number // of non-zero input weights for a given output. CHECK_EQ(blob->num(), 1); CHECK_EQ(blob->channels(), 1); - int num_inputs = blob->height(); - Dtype non_zero_probability = Dtype(sparse) / Dtype(num_inputs); + int num_outputs = blob->height(); + Dtype non_zero_probability = Dtype(sparse) / Dtype(num_outputs); rand_vec_.reset(new SyncedMemory(blob->count() * sizeof(int))); int* mask = reinterpret_cast(rand_vec_->mutable_cpu_data()); caffe_rng_bernoulli(blob->count(), non_zero_probability, mask); diff --git a/include/caffe/layer.hpp b/include/caffe/layer.hpp index e160075b939..34e00d72c05 100644 --- a/include/caffe/layer.hpp +++ b/include/caffe/layer.hpp @@ -7,6 +7,7 @@ #include "caffe/blob.hpp" #include "caffe/common.hpp" +#include "caffe/layer_factory.hpp" #include "caffe/proto/caffe.pb.h" #include "caffe/util/device_alternate.hpp" @@ -32,7 +33,8 @@ class Layer { */ explicit Layer(const LayerParameter& param) : layer_param_(param) { - // The only thing we do is to copy blobs if there are any. + // Set phase and copy blobs (if there are any). + phase_ = param.phase(); if (layer_param_.blobs_size() > 0) { blobs_.resize(layer_param_.blobs_size()); for (int i = 0; i < layer_param_.blobs_size(); ++i) { @@ -56,8 +58,9 @@ class Layer { * Sets up the loss weight multiplier blobs for any non-zero loss weights. * This method may not be overridden. */ - void SetUp(const vector*>& bottom, vector*>* top) { - CheckBlobCounts(bottom, *top); + void SetUp(const vector*>& bottom, + const vector*>& top) { + CheckBlobCounts(bottom, top); LayerSetUp(bottom, top); Reshape(bottom, top); SetLossWeights(top); @@ -80,7 +83,7 @@ class Layer { * adjust the top blob sizes. */ virtual void LayerSetUp(const vector*>& bottom, - vector*>* top) {} + const vector*>& top) {} /** * @brief Adjust the shapes of top blobs and internal buffers to accomodate @@ -95,7 +98,7 @@ class Layer { * accomodate the bottom blobs. */ virtual void Reshape(const vector*>& bottom, - vector*>* top) = 0; + const vector*>& top) = 0; /** * @brief Given the bottom blobs, compute the top blobs and the loss. @@ -115,7 +118,7 @@ class Layer { * Your layer should implement Forward_cpu and (optionally) Forward_gpu. */ inline Dtype Forward(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Given the top blob error gradients, compute the bottom blob error @@ -140,7 +143,7 @@ class Layer { */ inline void Backward(const vector*>& top, const vector& propagate_down, - vector*>* bottom); + const vector*>& bottom); /** * @brief Returns the vector of learnable parameter blobs. @@ -177,18 +180,9 @@ class Layer { } /** - * @brief Returns the layer type as an enum value. + * @brief Returns the layer type. */ - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_NONE; - } - - /** - * @brief Returns the layer type name. - */ - virtual inline const string& type_name() const { - return LayerParameter_LayerType_Name(type()); - } + virtual inline const char* type() const { return ""; } /** * @brief Returns the exact number of bottom blobs required by the layer, @@ -295,6 +289,8 @@ class Layer { protected: /** The protobuf that stores the layer parameters */ LayerParameter layer_param_; + /** The phase: TRAIN or TEST */ + Phase phase_; /** The vector that stores the learnable parameters as a set of blobs. */ vector > > blobs_; /** Vector indicating whether to compute the diff of each param blob. */ @@ -306,13 +302,13 @@ class Layer { /** @brief Using the CPU device, compute the layer output. */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top) = 0; + const vector*>& top) = 0; /** * @brief Using the GPU device, compute the layer output. * Fall back to Forward_cpu() if unavailable. */ virtual void Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { // LOG(WARNING) << "Using CPU code as backup."; return Forward_cpu(bottom, top); } @@ -323,7 +319,7 @@ class Layer { */ virtual void Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) = 0; + const vector*>& bottom) = 0; /** * @brief Using the GPU device, compute the gradients for any parameters and * for the bottom blobs if propagate_down is true. @@ -331,7 +327,7 @@ class Layer { */ virtual void Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { // LOG(WARNING) << "Using CPU code as backup."; Backward_cpu(top, propagate_down, bottom); } @@ -345,37 +341,37 @@ class Layer { const vector*>& top) { if (ExactNumBottomBlobs() >= 0) { CHECK_EQ(ExactNumBottomBlobs(), bottom.size()) - << type_name() << " Layer takes " << ExactNumBottomBlobs() + << type() << " Layer takes " << ExactNumBottomBlobs() << " bottom blob(s) as input."; } if (MinBottomBlobs() >= 0) { CHECK_LE(MinBottomBlobs(), bottom.size()) - << type_name() << " Layer takes at least " << MinBottomBlobs() + << type() << " Layer takes at least " << MinBottomBlobs() << " bottom blob(s) as input."; } if (MaxBottomBlobs() >= 0) { CHECK_GE(MaxBottomBlobs(), bottom.size()) - << type_name() << " Layer takes at most " << MaxBottomBlobs() + << type() << " Layer takes at most " << MaxBottomBlobs() << " bottom blob(s) as input."; } if (ExactNumTopBlobs() >= 0) { CHECK_EQ(ExactNumTopBlobs(), top.size()) - << type_name() << " Layer produces " << ExactNumTopBlobs() + << type() << " Layer produces " << ExactNumTopBlobs() << " top blob(s) as output."; } if (MinTopBlobs() >= 0) { CHECK_LE(MinTopBlobs(), top.size()) - << type_name() << " Layer produces at least " << MinTopBlobs() + << type() << " Layer produces at least " << MinTopBlobs() << " top blob(s) as output."; } if (MaxTopBlobs() >= 0) { CHECK_GE(MaxTopBlobs(), top.size()) - << type_name() << " Layer produces at most " << MaxTopBlobs() + << type() << " Layer produces at most " << MaxTopBlobs() << " top blob(s) as output."; } if (EqualNumBottomTopBlobs()) { CHECK_EQ(bottom.size(), top.size()) - << type_name() << " Layer produces one top blob as output for each " + << type() << " Layer produces one top blob as output for each " << "bottom blob input."; } } @@ -384,17 +380,17 @@ class Layer { * Called by SetUp to initialize the weights associated with any top blobs in * the loss function. Store non-zero loss weights in the diff blob. */ - inline void SetLossWeights(vector*>* top) { + inline void SetLossWeights(const vector*>& top) { const int num_loss_weights = layer_param_.loss_weight_size(); if (num_loss_weights) { - CHECK_EQ(top->size(), num_loss_weights) << "loss_weight must be " + CHECK_EQ(top.size(), num_loss_weights) << "loss_weight must be " "unspecified or specified once per top blob."; - for (int top_id = 0; top_id < top->size(); ++top_id) { + for (int top_id = 0; top_id < top.size(); ++top_id) { const Dtype loss_weight = layer_param_.loss_weight(top_id); if (loss_weight == Dtype(0)) { continue; } this->set_loss(top_id, loss_weight); - const int count = (*top)[top_id]->count(); - Dtype* loss_multiplier = (*top)[top_id]->mutable_cpu_diff(); + const int count = top[top_id]->count(); + Dtype* loss_multiplier = top[top_id]->mutable_cpu_diff(); caffe_set(count, loss_weight, loss_multiplier); } } @@ -408,27 +404,27 @@ class Layer { // functions. template inline Dtype Layer::Forward(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { Dtype loss = 0; switch (Caffe::mode()) { case Caffe::CPU: Forward_cpu(bottom, top); - for (int top_id = 0; top_id < top->size(); ++top_id) { + for (int top_id = 0; top_id < top.size(); ++top_id) { if (!this->loss(top_id)) { continue; } - const int count = (*top)[top_id]->count(); - const Dtype* data = (*top)[top_id]->cpu_data(); - const Dtype* loss_weights = (*top)[top_id]->cpu_diff(); + const int count = top[top_id]->count(); + const Dtype* data = top[top_id]->cpu_data(); + const Dtype* loss_weights = top[top_id]->cpu_diff(); loss += caffe_cpu_dot(count, data, loss_weights); } break; case Caffe::GPU: Forward_gpu(bottom, top); #ifndef CPU_ONLY - for (int top_id = 0; top_id < top->size(); ++top_id) { + for (int top_id = 0; top_id < top.size(); ++top_id) { if (!this->loss(top_id)) { continue; } - const int count = (*top)[top_id]->count(); - const Dtype* data = (*top)[top_id]->gpu_data(); - const Dtype* loss_weights = (*top)[top_id]->gpu_diff(); + const int count = top[top_id]->count(); + const Dtype* data = top[top_id]->gpu_data(); + const Dtype* loss_weights = top[top_id]->gpu_diff(); Dtype blob_loss = 0; caffe_gpu_dot(count, data, loss_weights, &blob_loss); loss += blob_loss; @@ -444,7 +440,7 @@ inline Dtype Layer::Forward(const vector*>& bottom, template inline void Layer::Backward(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { switch (Caffe::mode()) { case Caffe::CPU: Backward_cpu(top, propagate_down, bottom); @@ -468,10 +464,6 @@ void Layer::ToProto(LayerParameter* param, bool write_diff) { } } -// The layer factory function -template -Layer* GetLayer(const LayerParameter& param); - } // namespace caffe #endif // CAFFE_LAYER_H_ diff --git a/include/caffe/layer_factory.hpp b/include/caffe/layer_factory.hpp new file mode 100644 index 00000000000..2fcd93869a0 --- /dev/null +++ b/include/caffe/layer_factory.hpp @@ -0,0 +1,127 @@ +/** + * @brief A layer factory that allows one to register layers. + * During runtime, registered layers could be called by passing a LayerParameter + * protobuffer to the CreateLayer function: + * + * LayerRegistry::CreateLayer(param); + * + * There are two ways to register a layer. Assuming that we have a layer like: + * + * template + * class MyAwesomeLayer : public Layer { + * // your implementations + * }; + * + * and its type is its C++ class name, but without the "Layer" at the end + * ("MyAwesomeLayer" -> "MyAwesome"). + * + * If the layer is going to be created simply by its constructor, in your c++ + * file, add the following line: + * + * REGISTER_LAYER_CLASS(MyAwesome); + * + * Or, if the layer is going to be created by another creator function, in the + * format of: + * + * template + * Layer GetMyAwesomeLayer(const LayerParameter& param) { + * // your implementation + * } + * + * (for example, when your layer has multiple backends, see GetConvolutionLayer + * for a use case), then you can register the creator function instead, like + * + * REGISTER_LAYER_CREATOR(MyAwesome, GetMyAwesomeLayer) + * + * Note that each layer type should only be registered once. + */ + +#ifndef CAFFE_LAYER_FACTORY_H_ +#define CAFFE_LAYER_FACTORY_H_ + +#include +#include + +#include "caffe/common.hpp" +#include "caffe/proto/caffe.pb.h" + +namespace caffe { + +template +class Layer; + +template +class LayerRegistry { + public: + typedef shared_ptr > (*Creator)(const LayerParameter&); + typedef std::map CreatorRegistry; + + static CreatorRegistry& Registry() { + static CreatorRegistry* g_registry_ = new CreatorRegistry(); + return *g_registry_; + } + + // Adds a creator. + static void AddCreator(const string& type, Creator creator) { + CreatorRegistry& registry = Registry(); + CHECK_EQ(registry.count(type), 0) + << "Layer type " << type << " already registered."; + registry[type] = creator; + } + + // Get a layer using a LayerParameter. + static shared_ptr > CreateLayer(const LayerParameter& param) { + LOG(INFO) << "Creating layer " << param.name(); + const string& type = param.type(); + CreatorRegistry& registry = Registry(); + CHECK_EQ(registry.count(type), 1) << "Unknown layer type: " << type + << " (known types: " << LayerTypeList() << ")"; + return registry[type](param); + } + + private: + // Layer registry should never be instantiated - everything is done with its + // static variables. + LayerRegistry() {} + + static string LayerTypeList() { + CreatorRegistry& registry = Registry(); + string layer_types; + for (typename CreatorRegistry::iterator iter = registry.begin(); + iter != registry.end(); ++iter) { + if (iter != registry.begin()) { + layer_types += ", "; + } + layer_types += iter->first; + } + return layer_types; + } +}; + + +template +class LayerRegisterer { + public: + LayerRegisterer(const string& type, + shared_ptr > (*creator)(const LayerParameter&)) { + // LOG(INFO) << "Registering layer type: " << type; + LayerRegistry::AddCreator(type, creator); + } +}; + + +#define REGISTER_LAYER_CREATOR(type, creator) \ + static LayerRegisterer g_creator_f_##type(#type, creator); \ + static LayerRegisterer g_creator_d_##type(#type, creator) \ + +#define REGISTER_LAYER_CLASS(type) \ + template \ + shared_ptr > Creator_##type##Layer(const LayerParameter& param) \ + { \ + return shared_ptr >(new type##Layer(param)); \ + } \ + REGISTER_LAYER_CREATOR(type, Creator_##type##Layer) + +} // namespace caffe + +#endif // CAFFE_LAYER_FACTORY_H_ diff --git a/include/caffe/loss_layers.hpp b/include/caffe/loss_layers.hpp index 08aa7752d4a..36413ccd176 100644 --- a/include/caffe/loss_layers.hpp +++ b/include/caffe/loss_layers.hpp @@ -33,14 +33,11 @@ class AccuracyLayer : public Layer { explicit AccuracyLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); - - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_ACCURACY; - } + const vector*>& top); + virtual inline const char* type() const { return "Accuracy"; } virtual inline int ExactNumBottomBlobs() const { return 2; } virtual inline int ExactNumTopBlobs() const { return 1; } @@ -70,12 +67,12 @@ class AccuracyLayer : public Layer { * @f$ */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /// @brief Not implemented -- AccuracyLayer cannot be used as a loss. virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { for (int i = 0; i < propagate_down.size(); ++i) { if (propagate_down[i]) { NOT_IMPLEMENTED; } } @@ -98,9 +95,9 @@ class LossLayer : public Layer { explicit LossLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp( - const vector*>& bottom, vector*>* top); + const vector*>& bottom, const vector*>& top); virtual void Reshape( - const vector*>& bottom, vector*>* top); + const vector*>& bottom, const vector*>& top); virtual inline int ExactNumBottomBlobs() const { return 2; } @@ -151,12 +148,10 @@ class ContrastiveLossLayer : public LossLayer { explicit ContrastiveLossLayer(const LayerParameter& param) : LossLayer(param), diff_() {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual inline int ExactNumBottomBlobs() const { return 3; } - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_CONTRASTIVE_LOSS; - } + virtual inline const char* type() const { return "ContrastiveLoss"; } /** * Unlike most loss layers, in the ContrastiveLossLayer we can backpropagate * to the first two inputs. @@ -168,14 +163,14 @@ class ContrastiveLossLayer : public LossLayer { protected: /// @copydoc ContrastiveLossLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the Contrastive error gradient w.r.t. the inputs. - * - * Computes the gradients with respect to the two input vectors (bottom[0] and + * + * Computes the gradients with respect to the two input vectors (bottom[0] and * bottom[1]), but not the similarity label (bottom[2]). * * @param top output Blob vector (length 1), providing the error gradient with @@ -194,13 +189,13 @@ class ContrastiveLossLayer : public LossLayer { * the features @f$a@f$; Backward fills their diff with * gradients if propagate_down[0] * -# @f$ (N \times C \times 1 \times 1) @f$ - * the features @f$b@f$; Backward fills their diff with gradients if + * the features @f$b@f$; Backward fills their diff with gradients if * propagate_down[1] */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); Blob diff_; // cached for backward pass Blob dist_sq_; // cached for backward pass @@ -240,12 +235,9 @@ class EuclideanLossLayer : public LossLayer { explicit EuclideanLossLayer(const LayerParameter& param) : LossLayer(param), diff_() {} virtual void Reshape(const vector*>& bottom, - vector*>* top); - - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_EUCLIDEAN_LOSS; - } + const vector*>& top); + virtual inline const char* type() const { return "EuclideanLoss"; } /** * Unlike most loss layers, in the EuclideanLossLayer we can backpropagate * to both inputs -- override to return true and always allow force_backward. @@ -257,9 +249,9 @@ class EuclideanLossLayer : public LossLayer { protected: /// @copydoc EuclideanLossLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the Euclidean error gradient w.r.t. the inputs. @@ -295,9 +287,9 @@ class EuclideanLossLayer : public LossLayer { * @f$ if propagate_down[1] */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); Blob diff_; }; @@ -351,14 +343,12 @@ class HingeLossLayer : public LossLayer { explicit HingeLossLayer(const LayerParameter& param) : LossLayer(param) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_HINGE_LOSS; - } + virtual inline const char* type() const { return "HingeLoss"; } protected: /// @copydoc HingeLossLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the hinge loss error gradient w.r.t. the predictions. @@ -388,7 +378,7 @@ class HingeLossLayer : public LossLayer { * the labels -- ignored as we can't compute their error gradients */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); }; /** @@ -429,9 +419,9 @@ class InfogainLossLayer : public LossLayer { explicit InfogainLossLayer(const LayerParameter& param) : LossLayer(param), infogain_() {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); // InfogainLossLayer takes 2-3 bottom Blobs; if there are 3 the third should // be the infogain matrix. (Otherwise the infogain matrix is loaded from a @@ -440,14 +430,12 @@ class InfogainLossLayer : public LossLayer { virtual inline int MinBottomBlobs() const { return 2; } virtual inline int MaxBottomBlobs() const { return 3; } - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_INFOGAIN_LOSS; - } + virtual inline const char* type() const { return "InfogainLoss"; } protected: /// @copydoc InfogainLossLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the infogain loss error gradient w.r.t. the predictions. @@ -482,7 +470,7 @@ class InfogainLossLayer : public LossLayer { * gradient computation is not implemented. */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); Blob infogain_; }; @@ -522,16 +510,14 @@ class MultinomialLogisticLossLayer : public LossLayer { explicit MultinomialLogisticLossLayer(const LayerParameter& param) : LossLayer(param) {} virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_MULTINOMIAL_LOGISTIC_LOSS; - } + virtual inline const char* type() const { return "MultinomialLogisticLoss"; } protected: /// @copydoc MultinomialLogisticLossLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the multinomial logistic loss error gradient w.r.t. the @@ -562,7 +548,7 @@ class MultinomialLogisticLossLayer : public LossLayer { * the labels -- ignored as we can't compute their error gradients */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); }; /** @@ -602,20 +588,18 @@ class SigmoidCrossEntropyLossLayer : public LossLayer { sigmoid_layer_(new SigmoidLayer(param)), sigmoid_output_(new Blob()) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_SIGMOID_CROSS_ENTROPY_LOSS; - } + virtual inline const char* type() const { return "SigmoidCrossEntropyLoss"; } protected: /// @copydoc SigmoidCrossEntropyLossLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the sigmoid cross-entropy loss error gradient w.r.t. the @@ -648,9 +632,9 @@ class SigmoidCrossEntropyLossLayer : public LossLayer { * the labels -- ignored as we can't compute their error gradients */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); /// The internal SigmoidLayer used to map predictions to probabilities. shared_ptr > sigmoid_layer_; @@ -696,20 +680,22 @@ template class SoftmaxLayer; template class SoftmaxWithLossLayer : public LossLayer { public: + /** + * @param param provides LossParameter loss_param, with options: + * - ignore_label (optional) + * Specify a label value that should be ignored when computing the loss. + * - normalize (optional, default true) + * If true, the loss is normalized by the number of (nonignored) labels + * present; otherwise the loss is simply summed over spatial locations. + */ explicit SoftmaxWithLossLayer(const LayerParameter& param) - : LossLayer(param), - softmax_layer_(new SoftmaxLayer(param)) {} + : LossLayer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_SOFTMAX_LOSS; - } - virtual inline int ExactNumBottomBlobs() const { return -1; } - virtual inline int MinBottomBlobs() const { return 2; } - virtual inline int MaxBottomBlobs() const { return 3; } + virtual inline const char* type() const { return "SoftmaxWithLoss"; } virtual inline int ExactNumTopBlobs() const { return -1; } virtual inline int MinTopBlobs() const { return 1; } virtual inline int MaxTopBlobs() const { return 2; } @@ -717,10 +703,9 @@ class SoftmaxWithLossLayer : public LossLayer { protected: /// @copydoc SoftmaxWithLossLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); - + const vector*>& top); /** * @brief Computes the softmax loss error gradient w.r.t. the predictions. * @@ -749,18 +734,26 @@ class SoftmaxWithLossLayer : public LossLayer { * the labels -- ignored as we can't compute their error gradients */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); + /// The internal SoftmaxLayer used to map predictions to a distribution. - shared_ptr > softmax_layer_; + shared_ptr > softmax_layer_; /// prob stores the output probability predictions from the SoftmaxLayer. Blob prob_; /// bottom vector holder used in call to the underlying SoftmaxLayer::Forward vector*> softmax_bottom_vec_; /// top vector holder used in call to the underlying SoftmaxLayer::Forward vector*> softmax_top_vec_; + /// Whether to ignore instances with a certain label. + bool has_ignore_label_; + /// The label indicating that an instance should be ignored. + int ignore_label_; + /// Whether to normalize the loss by the total number of values present + /// (otherwise just by the batch size). + bool normalize_; }; } // namespace caffe diff --git a/include/caffe/net.hpp b/include/caffe/net.hpp index 1d06dc45533..075afebc9b0 100644 --- a/include/caffe/net.hpp +++ b/include/caffe/net.hpp @@ -24,7 +24,7 @@ template class Net { public: explicit Net(const NetParameter& param); - explicit Net(const string& param_file); + explicit Net(const string& param_file, Phase phase); virtual ~Net() {} /// @brief Initialize a network with a NetParameter. @@ -89,7 +89,7 @@ class Net { * @brief For an already initialized net, implicitly copies (i.e., using no * additional memory) the pre-trained layers from another Net. */ - void ShareTrainedLayersWith(Net* other); + void ShareTrainedLayersWith(const Net* other); // For an already initialized net, CopyTrainedLayersFrom() copies the already // trained layers from another net parameter instance. /** @@ -99,51 +99,76 @@ class Net { void CopyTrainedLayersFrom(const NetParameter& param); void CopyTrainedLayersFrom(const string trained_filename); /// @brief Writes the net to a proto. - void ToProto(NetParameter* param, bool write_diff = false); + void ToProto(NetParameter* param, bool write_diff = false) const; /// @brief returns the network name. - inline const string& name() { return name_; } + inline const string& name() const { return name_; } /// @brief returns the layer names - inline const vector& layer_names() { return layer_names_; } + inline const vector& layer_names() const { return layer_names_; } /// @brief returns the blob names - inline const vector& blob_names() { return blob_names_; } + inline const vector& blob_names() const { return blob_names_; } /// @brief returns the blobs - inline const vector > >& blobs() { return blobs_; } + inline const vector > >& blobs() const { + return blobs_; + } /// @brief returns the layers - inline const vector > >& layers() { return layers_; } + inline const vector > >& layers() const { + return layers_; + } + /// @brief returns the phase: TRAIN or TEST + inline Phase phase() const { return phase_; } /** * @brief returns the bottom vecs for each layer -- usually you won't * need this unless you do per-layer checks such as gradients. */ - inline vector*> >& bottom_vecs() { return bottom_vecs_; } + inline const vector*> >& bottom_vecs() const { + return bottom_vecs_; + } /** * @brief returns the top vecs for each layer -- usually you won't * need this unless you do per-layer checks such as gradients. */ - inline vector*> >& top_vecs() { return top_vecs_; } - inline vector >& bottom_need_backward() { + inline const vector*> >& top_vecs() const { + return top_vecs_; + } + inline const vector >& bottom_need_backward() const { return bottom_need_backward_; } - inline vector& blob_loss_weights() { + inline const vector& blob_loss_weights() const { return blob_loss_weights_; } /// @brief returns the parameters - inline vector > >& params() { return params_; } + inline const vector > >& params() const { + return params_; + } /// @brief returns the parameter learning rate multipliers - inline vector& params_lr() { return params_lr_; } - inline vector& params_weight_decay() { return params_weight_decay_; } - const map& param_names_index() { return param_names_index_; } + inline const vector& params_lr() const { return params_lr_; } + inline const vector& params_weight_decay() const { + return params_weight_decay_; + } + const map& param_names_index() const { + return param_names_index_; + } + inline const vector& param_owners() const { return param_owners_; } /// @brief Input and output blob numbers - inline int num_inputs() { return net_input_blobs_.size(); } - inline int num_outputs() { return net_output_blobs_.size(); } - inline vector*>& input_blobs() { return net_input_blobs_; } - inline vector*>& output_blobs() { return net_output_blobs_; } - inline vector& input_blob_indices() { return net_input_blob_indices_; } - inline vector& output_blob_indices() { return net_output_blob_indices_; } - bool has_blob(const string& blob_name); - const shared_ptr > blob_by_name(const string& blob_name); - bool has_layer(const string& layer_name); - const shared_ptr > layer_by_name(const string& layer_name); + inline int num_inputs() const { return net_input_blobs_.size(); } + inline int num_outputs() const { return net_output_blobs_.size(); } + inline const vector*>& input_blobs() const { + return net_input_blobs_; + } + inline const vector*>& output_blobs() const { + return net_output_blobs_; + } + inline const vector& input_blob_indices() const { + return net_input_blob_indices_; + } + inline const vector& output_blob_indices() const { + return net_output_blob_indices_; + } + bool has_blob(const string& blob_name) const; + const shared_ptr > blob_by_name(const string& blob_name) const; + bool has_layer(const string& layer_name) const; + const shared_ptr > layer_by_name(const string& layer_name) const; void set_debug_info(const bool value) { debug_info_ = value; } @@ -172,6 +197,8 @@ class Net { void AppendParam(const NetParameter& param, const int layer_id, const int param_id); + /// @brief Helper for displaying debug info in Forward about input Blobs. + void InputDebugInfo(const int layer_id); /// @brief Helper for displaying debug info in Forward. void ForwardDebugInfo(const int layer_id); /// @brief Helper for displaying debug info in Backward. @@ -182,6 +209,10 @@ class Net { /// @brief Get misc parameters, e.g. the LR multiplier and weight decay. void GetLearningRateAndWeightDecay(); + /// @brief The network name + string name_; + /// @brief The phase: TRAIN or TEST + Phase phase_; /// @brief Individual layers in the net vector > > layers_; vector layer_names_; @@ -204,6 +235,7 @@ class Net { /// Vector of weight in the loss (or objective) function of each net blob, /// indexed by blob_id. vector blob_loss_weights_; + vector > param_id_vecs_; vector param_owners_; vector param_display_names_; vector > param_layer_indices_; @@ -213,7 +245,6 @@ class Net { vector net_output_blob_indices_; vector*> net_input_blobs_; vector*> net_output_blobs_; - string name_; /// The parameters in the network. vector > > params_; /// the learning rate multipliers diff --git a/include/caffe/neuron_layers.hpp b/include/caffe/neuron_layers.hpp index 0968a2007dc..0c306fb41bf 100644 --- a/include/caffe/neuron_layers.hpp +++ b/include/caffe/neuron_layers.hpp @@ -8,6 +8,7 @@ #include "caffe/blob.hpp" #include "caffe/common.hpp" #include "caffe/layer.hpp" +#include "caffe/net.hpp" #include "caffe/proto/caffe.pb.h" #define HDF5_DATA_DATASET_NAME "data" @@ -27,11 +28,8 @@ class NeuronLayer : public Layer { explicit NeuronLayer(const LayerParameter& param) : Layer(param) {} virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_NONE; - } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } }; @@ -52,20 +50,18 @@ class AbsValLayer : public NeuronLayer { explicit AbsValLayer(const LayerParameter& param) : NeuronLayer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_ABSVAL; - } + virtual inline const char* type() const { return "AbsVal"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: /// @copydoc AbsValLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the error gradient w.r.t. the absolute value inputs. @@ -85,9 +81,9 @@ class AbsValLayer : public NeuronLayer { * @f$ if propagate_down[0] */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); }; /** @@ -113,16 +109,14 @@ class BNLLLayer : public NeuronLayer { explicit BNLLLayer(const LayerParameter& param) : NeuronLayer(param) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_BNLL; - } + virtual inline const char* type() const { return "BNLL"; } protected: /// @copydoc BNLLLayer virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the error gradient w.r.t. the BNLL inputs. @@ -141,9 +135,9 @@ class BNLLLayer : public NeuronLayer { * @f$ if propagate_down[0] */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); }; /** @@ -169,13 +163,11 @@ class DropoutLayer : public NeuronLayer { explicit DropoutLayer(const LayerParameter& param) : NeuronLayer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_DROPOUT; - } + virtual inline const char* type() const { return "Dropout"; } protected: /** @@ -195,13 +187,13 @@ class DropoutLayer : public NeuronLayer { * @f$ y_{\mbox{test}} = \mathbb{E}[y_{\mbox{train}}] = x @f$. */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); /// when divided by UINT_MAX, the randomly generated values @f$u\sim U(0,1)@f$ Blob rand_vec_; @@ -212,6 +204,70 @@ class DropoutLayer : public NeuronLayer { unsigned int uint_thres_; }; +/** + * @brief Computes @f$ y = \gamma ^ {\alpha x + \beta} @f$, + * as specified by the scale @f$ \alpha @f$, shift @f$ \beta @f$, + * and base @f$ \gamma @f$. + */ +template +class ExpLayer : public NeuronLayer { + public: + /** + * @param param provides ExpParameter exp_param, + * with ExpLayer options: + * - scale (\b optional, default 1) the scale @f$ \alpha @f$ + * - shift (\b optional, default 0) the shift @f$ \beta @f$ + * - base (\b optional, default -1 for a value of @f$ e \approx 2.718 @f$) + * the base @f$ \gamma @f$ + */ + explicit ExpLayer(const LayerParameter& param) + : NeuronLayer(param) {} + virtual void LayerSetUp(const vector*>& bottom, + const vector*>& top); + + virtual inline const char* type() const { return "Exp"; } + + protected: + /** + * @param bottom input Blob vector (length 1) + * -# @f$ (N \times C \times H \times W) @f$ + * the inputs @f$ x @f$ + * @param top output Blob vector (length 1) + * -# @f$ (N \times C \times H \times W) @f$ + * the computed outputs @f$ + * y = \gamma ^ {\alpha x + \beta} + * @f$ + */ + virtual void Forward_cpu(const vector*>& bottom, + const vector*>& top); + virtual void Forward_gpu(const vector*>& bottom, + const vector*>& top); + + /** + * @brief Computes the error gradient w.r.t. the exp inputs. + * + * @param top output Blob vector (length 1), providing the error gradient with + * respect to the outputs + * -# @f$ (N \times C \times H \times W) @f$ + * containing error gradients @f$ \frac{\partial E}{\partial y} @f$ + * with respect to computed outputs @f$ y @f$ + * @param propagate_down see Layer::Backward. + * @param bottom input Blob vector (length 1) + * -# @f$ (N \times C \times H \times W) @f$ + * the inputs @f$ x @f$; Backward fills their diff with + * gradients @f$ + * \frac{\partial E}{\partial x} = + * \frac{\partial E}{\partial y} y \alpha \log_e(gamma) + * @f$ if propagate_down[0] + */ + virtual void Backward_cpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom); + virtual void Backward_gpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom); + + Dtype inner_scale_, outer_scale_; +}; + /** * @brief Computes @f$ y = (\alpha x + \beta) ^ \gamma @f$, * as specified by the scale @f$ \alpha @f$, shift @f$ \beta @f$, @@ -230,11 +286,9 @@ class PowerLayer : public NeuronLayer { explicit PowerLayer(const LayerParameter& param) : NeuronLayer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_POWER; - } + virtual inline const char* type() const { return "Power"; } protected: /** @@ -248,9 +302,9 @@ class PowerLayer : public NeuronLayer { * @f$ */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the error gradient w.r.t. the power inputs. @@ -273,9 +327,9 @@ class PowerLayer : public NeuronLayer { * @f$ if propagate_down[0] */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); /// @brief @f$ \gamma @f$ from layer_param_.power_param() Dtype power_; @@ -303,9 +357,7 @@ class ReLULayer : public NeuronLayer { explicit ReLULayer(const LayerParameter& param) : NeuronLayer(param) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_RELU; - } + virtual inline const char* type() const { return "ReLU"; } protected: /** @@ -320,9 +372,9 @@ class ReLULayer : public NeuronLayer { * the computed outputs are @f$ y = \max(0, x) + \nu \min(0, x) @f$. */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the error gradient w.r.t. the ReLU inputs. @@ -353,9 +405,9 @@ class ReLULayer : public NeuronLayer { * @f$. */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); }; #ifdef USE_CUDNN @@ -366,19 +418,20 @@ template class CuDNNReLULayer : public ReLULayer { public: explicit CuDNNReLULayer(const LayerParameter& param) - : ReLULayer(param) {} + : ReLULayer(param), handles_setup_(false) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual ~CuDNNReLULayer(); protected: virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); + bool handles_setup_; cudnnHandle_t handle_; cudnnTensor4dDescriptor_t bottom_desc_; cudnnTensor4dDescriptor_t top_desc_; @@ -399,9 +452,7 @@ class SigmoidLayer : public NeuronLayer { explicit SigmoidLayer(const LayerParameter& param) : NeuronLayer(param) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_SIGMOID; - } + virtual inline const char* type() const { return "Sigmoid"; } protected: /** @@ -415,9 +466,9 @@ class SigmoidLayer : public NeuronLayer { * @f$ */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the error gradient w.r.t. the sigmoid inputs. @@ -437,9 +488,9 @@ class SigmoidLayer : public NeuronLayer { * @f$ if propagate_down[0] */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); }; #ifdef USE_CUDNN @@ -450,19 +501,20 @@ template class CuDNNSigmoidLayer : public SigmoidLayer { public: explicit CuDNNSigmoidLayer(const LayerParameter& param) - : SigmoidLayer(param) {} + : SigmoidLayer(param), handles_setup_(false) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual ~CuDNNSigmoidLayer(); protected: virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); + bool handles_setup_; cudnnHandle_t handle_; cudnnTensor4dDescriptor_t bottom_desc_; cudnnTensor4dDescriptor_t top_desc_; @@ -483,9 +535,7 @@ class TanHLayer : public NeuronLayer { explicit TanHLayer(const LayerParameter& param) : NeuronLayer(param) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_TANH; - } + virtual inline const char* type() const { return "TanH"; } protected: /** @@ -499,9 +549,9 @@ class TanHLayer : public NeuronLayer { * @f$ */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /** * @brief Computes the error gradient w.r.t. the sigmoid inputs. @@ -523,9 +573,9 @@ class TanHLayer : public NeuronLayer { * @f$ if propagate_down[0] */ virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); }; #ifdef USE_CUDNN @@ -536,19 +586,20 @@ template class CuDNNTanHLayer : public TanHLayer { public: explicit CuDNNTanHLayer(const LayerParameter& param) - : TanHLayer(param) {} + : TanHLayer(param), handles_setup_(false) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual ~CuDNNTanHLayer(); protected: virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); + bool handles_setup_; cudnnHandle_t handle_; cudnnTensor4dDescriptor_t bottom_desc_; cudnnTensor4dDescriptor_t top_desc_; @@ -571,11 +622,9 @@ class ThresholdLayer : public NeuronLayer { explicit ThresholdLayer(const LayerParameter& param) : NeuronLayer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_THRESHOLD; - } + virtual inline const char* type() const { return "Threshold"; } protected: /** @@ -593,12 +642,12 @@ class ThresholdLayer : public NeuronLayer { * @f$ */ virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); /// @brief Not implemented (non-differentiable function) virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { NOT_IMPLEMENTED; } diff --git a/include/caffe/python_layer.hpp b/include/caffe/python_layer.hpp new file mode 100644 index 00000000000..816ef453720 --- /dev/null +++ b/include/caffe/python_layer.hpp @@ -0,0 +1,68 @@ +#ifndef CAFFE_PYTHON_LAYER_HPP_ +#define CAFFE_PYTHON_LAYER_HPP_ + +#include +#include + +#include "caffe/layer.hpp" + +namespace bp = boost::python; + +namespace caffe { + +template +class PythonLayer : public Layer { + public: + PythonLayer(PyObject* self, const LayerParameter& param) + : Layer(param), self_(self) { } + + virtual void LayerSetUp(const vector*>& bottom, + const vector*>& top) { + try { + bp::call_method(self_, "setup", bottom, top); + } catch (bp::error_already_set) { + PyErr_Print(); + throw; + } + } + + virtual void Reshape(const vector*>& bottom, + const vector*>& top) { + try { + bp::call_method(self_, "reshape", bottom, top); + } catch (bp::error_already_set) { + PyErr_Print(); + throw; + } + } + + virtual inline const char* type() const { return "Python"; } + + protected: + virtual void Forward_cpu(const vector*>& bottom, + const vector*>& top) { + try { + bp::call_method(self_, "forward", bottom, top); + } catch (bp::error_already_set) { + PyErr_Print(); + throw; + } + } + virtual void Backward_cpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom) { + try { + bp::call_method(self_, "backward", top, propagate_down, + bottom); + } catch (bp::error_already_set) { + PyErr_Print(); + throw; + } + } + + private: + PyObject* self_; +}; + +} // namespace caffe + +#endif diff --git a/include/caffe/solver.hpp b/include/caffe/solver.hpp index 51aebb323c5..2510de748de 100644 --- a/include/caffe/solver.hpp +++ b/include/caffe/solver.hpp @@ -26,16 +26,15 @@ class Solver { // in a non-zero iter number to resume training for a pre-trained net. virtual void Solve(const char* resume_file = NULL); inline void Solve(const string resume_file) { Solve(resume_file.c_str()); } + void Step(int iters); virtual ~Solver() {} inline shared_ptr > net() { return net_; } inline const vector > >& test_nets() { return test_nets_; } + int iter() { return iter_; } protected: - // PreSolve is run before any solving iteration starts, allowing one to - // put up some scaffold. - virtual void PreSolve() {} // Get the update value for the current iteration. virtual void ComputeUpdateValue() = 0; // The Solver::Snapshot function implements the basic snapshotting utility @@ -72,16 +71,17 @@ template class SGDSolver : public Solver { public: explicit SGDSolver(const SolverParameter& param) - : Solver(param) {} + : Solver(param) { PreSolve(); } explicit SGDSolver(const string& param_file) - : Solver(param_file) {} + : Solver(param_file) { PreSolve(); } const vector > >& history() { return history_; } protected: - virtual void PreSolve(); + void PreSolve(); Dtype GetLearningRate(); virtual void ComputeUpdateValue(); + virtual void ClipGradients(); virtual void SnapshotSolverState(SolverState * state); virtual void RestoreSolverState(const SolverState& state); // history maintains the historical momentum data. diff --git a/include/caffe/syncedmem.hpp b/include/caffe/syncedmem.hpp index db8d0e80e12..2564e0716ef 100644 --- a/include/caffe/syncedmem.hpp +++ b/include/caffe/syncedmem.hpp @@ -24,6 +24,7 @@ namespace caffe { inline void CaffeMallocHost(void** ptr, size_t size) { *ptr = malloc(size); + CHECK(*ptr) << "host allocation of size " << size << " failed"; } inline void CaffeFreeHost(void* ptr) { diff --git a/include/caffe/test/test_caffe_main.hpp b/include/caffe/test/test_caffe_main.hpp index 438acf2bf17..bd5f31e063f 100644 --- a/include/caffe/test/test_caffe_main.hpp +++ b/include/caffe/test/test_caffe_main.hpp @@ -15,7 +15,7 @@ using std::cout; using std::endl; #ifdef CMAKE_BUILD - #include + #include "caffe_config.h" #else #define CUDA_TEST_DEVICE -1 #define CMAKE_SOURCE_DIR "src/" diff --git a/include/caffe/test/test_gradient_check_util.hpp b/include/caffe/test/test_gradient_check_util.hpp index 5a8d382ff26..22937711b58 100644 --- a/include/caffe/test/test_gradient_check_util.hpp +++ b/include/caffe/test/test_gradient_check_util.hpp @@ -30,24 +30,24 @@ class GradientChecker { // layers. // Note that after the gradient check, we do not guarantee that the data // stored in the layer parameters and the blobs are unchanged. - void CheckGradient(Layer* layer, vector*>* bottom, - vector*>* top, int check_bottom = -1) { - layer->SetUp(*bottom, top); + void CheckGradient(Layer* layer, const vector*>& bottom, + const vector*>& top, int check_bottom = -1) { + layer->SetUp(bottom, top); CheckGradientSingle(layer, bottom, top, check_bottom, -1, -1); } void CheckGradientExhaustive(Layer* layer, - vector*>* bottom, vector*>* top, + const vector*>& bottom, const vector*>& top, int check_bottom = -1); // CheckGradientEltwise can be used to test layers that perform element-wise // computation only (e.g., neuron layers) -- where (d y_i) / (d x_j) = 0 when // i != j. void CheckGradientEltwise(Layer* layer, - vector*>* bottom, vector*>* top); + const vector*>& bottom, const vector*>& top); - void CheckGradientSingle(Layer* layer, vector*>* bottom, - vector*>* top, int check_bottom, int top_id, - int top_data_id, bool element_wise = false); + void CheckGradientSingle(Layer* layer, + const vector*>& bottom, const vector*>& top, + int check_bottom, int top_id, int top_data_id, bool element_wise = false); // Checks the gradient of a network. This network should not have any data // layers or loss layers, since the function does not explicitly deal with @@ -57,8 +57,8 @@ class GradientChecker { const vector*>& input); protected: - Dtype GetObjAndGradient(const Layer& layer, vector*>* top, - int top_id = -1, int top_data_id = -1); + Dtype GetObjAndGradient(const Layer& layer, + const vector*>& top, int top_id = -1, int top_data_id = -1); Dtype stepsize_; Dtype threshold_; unsigned int seed_; @@ -69,40 +69,40 @@ class GradientChecker { template void GradientChecker::CheckGradientSingle(Layer* layer, - vector*>* bottom, vector*>* top, + const vector*>& bottom, const vector*>& top, int check_bottom, int top_id, int top_data_id, bool element_wise) { if (element_wise) { CHECK_EQ(0, layer->blobs().size()); CHECK_LE(0, top_id); CHECK_LE(0, top_data_id); - const int top_count = (*top)[top_id]->count(); - for (int blob_id = 0; blob_id < bottom->size(); ++blob_id) { - CHECK_EQ(top_count, (*bottom)[blob_id]->count()); + const int top_count = top[top_id]->count(); + for (int blob_id = 0; blob_id < bottom.size(); ++blob_id) { + CHECK_EQ(top_count, bottom[blob_id]->count()); } } // First, figure out what blobs we need to check against. vector*> blobs_to_check; - vector propagate_down(bottom->size(), check_bottom < 0); + vector propagate_down(bottom.size(), check_bottom < 0); for (int i = 0; i < layer->blobs().size(); ++i) { blobs_to_check.push_back(layer->blobs()[i].get()); } if (check_bottom < 0) { - for (int i = 0; i < bottom->size(); ++i) { - blobs_to_check.push_back((*bottom)[i]); + for (int i = 0; i < bottom.size(); ++i) { + blobs_to_check.push_back(bottom[i]); } } else { - CHECK_LT(check_bottom, bottom->size()); - blobs_to_check.push_back((*bottom)[check_bottom]); + CHECK_LT(check_bottom, bottom.size()); + blobs_to_check.push_back(bottom[check_bottom]); propagate_down[check_bottom] = true; } // Compute the gradient analytically using Backward Caffe::set_random_seed(seed_); // Ignore the loss from the layer (it's just the weighted sum of the losses // from the top blobs, whose gradients we may want to test individually). - layer->Forward(*bottom, top); + layer->Forward(bottom, top); // Get additional loss from the objective GetObjAndGradient(*layer, top, top_id, top_data_id); - layer->Backward(*top, propagate_down, bottom); + layer->Backward(top, propagate_down, bottom); // Store computed gradients for all checked blobs vector > > computed_gradient_blobs(blobs_to_check.size()); @@ -127,8 +127,8 @@ void GradientChecker::CheckGradientSingle(Layer* layer, // << current_blob->count() << " parameters."; for (int feat_id = 0; feat_id < current_blob->count(); ++feat_id) { // For an element-wise layer, we only need to do finite differencing to - // compute the derivative of (*top)[top_id][top_data_id] w.r.t. - // (*bottom)[blob_id][i] only for i == top_data_id. For any other + // compute the derivative of top[top_id][top_data_id] w.r.t. + // bottom[blob_id][i] only for i == top_data_id. For any other // i != top_data_id, we know the derivative is 0 by definition, and simply // check that that's true. Dtype estimated_gradient = 0; @@ -139,13 +139,13 @@ void GradientChecker::CheckGradientSingle(Layer* layer, // Compute loss with stepsize_ added to input. current_blob->mutable_cpu_data()[feat_id] += stepsize_; Caffe::set_random_seed(seed_); - layer->Forward(*bottom, top); + layer->Forward(bottom, top); positive_objective = GetObjAndGradient(*layer, top, top_id, top_data_id); // Compute loss with stepsize_ subtracted from input. current_blob->mutable_cpu_data()[feat_id] -= stepsize_ * 2; Caffe::set_random_seed(seed_); - layer->Forward(*bottom, top); + layer->Forward(bottom, top); negative_objective = GetObjAndGradient(*layer, top, top_id, top_data_id); // Recover original input value. @@ -179,13 +179,14 @@ void GradientChecker::CheckGradientSingle(Layer* layer, template void GradientChecker::CheckGradientExhaustive(Layer* layer, - vector*>* bottom, vector*>* top, int check_bottom) { - layer->SetUp(*bottom, top); - CHECK_GT(top->size(), 0) << "Exhaustive mode requires at least one top blob."; + const vector*>& bottom, const vector*>& top, + int check_bottom) { + layer->SetUp(bottom, top); + CHECK_GT(top.size(), 0) << "Exhaustive mode requires at least one top blob."; // LOG(ERROR) << "Exhaustive Mode."; - for (int i = 0; i < top->size(); ++i) { + for (int i = 0; i < top.size(); ++i) { // LOG(ERROR) << "Exhaustive: blob " << i << " size " << top[i]->count(); - for (int j = 0; j < (*top)[i]->count(); ++j) { + for (int j = 0; j < top[i]->count(); ++j) { // LOG(ERROR) << "Exhaustive: blob " << i << " data " << j; CheckGradientSingle(layer, bottom, top, check_bottom, i, j); } @@ -194,13 +195,13 @@ void GradientChecker::CheckGradientExhaustive(Layer* layer, template void GradientChecker::CheckGradientEltwise(Layer* layer, - vector*>* bottom, vector*>* top) { - layer->SetUp(*bottom, top); - CHECK_GT(top->size(), 0) << "Eltwise mode requires at least one top blob."; + const vector*>& bottom, const vector*>& top) { + layer->SetUp(bottom, top); + CHECK_GT(top.size(), 0) << "Eltwise mode requires at least one top blob."; const int check_bottom = -1; const bool element_wise = true; - for (int i = 0; i < top->size(); ++i) { - for (int j = 0; j < (*top)[i]->count(); ++j) { + for (int i = 0; i < top.size(); ++i) { + for (int j = 0; j < top[i]->count(); ++j) { CheckGradientSingle(layer, bottom, top, check_bottom, i, j, element_wise); } } @@ -221,12 +222,12 @@ void GradientChecker::CheckGradientNet( template Dtype GradientChecker::GetObjAndGradient(const Layer& layer, - vector*>* top, int top_id, int top_data_id) { + const vector*>& top, int top_id, int top_data_id) { Dtype loss = 0; if (top_id < 0) { // the loss will be half of the sum of squares of all outputs - for (int i = 0; i < top->size(); ++i) { - Blob* top_blob = (*top)[i]; + for (int i = 0; i < top.size(); ++i) { + Blob* top_blob = top[i]; const Dtype* top_blob_data = top_blob->cpu_data(); Dtype* top_blob_diff = top_blob->mutable_cpu_diff(); int count = top_blob->count(); @@ -239,14 +240,14 @@ Dtype GradientChecker::GetObjAndGradient(const Layer& layer, loss /= 2.; } else { // the loss will be the top_data_id-th element in the top_id-th blob. - for (int i = 0; i < top->size(); ++i) { - Blob* top_blob = (*top)[i]; + for (int i = 0; i < top.size(); ++i) { + Blob* top_blob = top[i]; Dtype* top_blob_diff = top_blob->mutable_cpu_diff(); caffe_set(top_blob->count(), Dtype(0), top_blob_diff); } const Dtype loss_weight = 2; - loss = (*top)[top_id]->cpu_data()[top_data_id] * loss_weight; - (*top)[top_id]->mutable_cpu_diff()[top_data_id] = loss_weight; + loss = top[top_id]->cpu_data()[top_data_id] * loss_weight; + top[top_id]->mutable_cpu_diff()[top_data_id] = loss_weight; } return loss; } diff --git a/include/caffe/util/benchmark.hpp b/include/caffe/util/benchmark.hpp index f7ef8eaf3ee..d63582776ee 100644 --- a/include/caffe/util/benchmark.hpp +++ b/include/caffe/util/benchmark.hpp @@ -11,10 +11,11 @@ class Timer { public: Timer(); virtual ~Timer(); - void Start(); - void Stop(); - float MilliSeconds(); - float Seconds(); + virtual void Start(); + virtual void Stop(); + virtual float MilliSeconds(); + virtual float MicroSeconds(); + virtual float Seconds(); inline bool initted() { return initted_; } inline bool running() { return running_; } @@ -33,6 +34,17 @@ class Timer { boost::posix_time::ptime start_cpu_; boost::posix_time::ptime stop_cpu_; float elapsed_milliseconds_; + float elapsed_microseconds_; +}; + +class CPUTimer : public Timer { + public: + explicit CPUTimer(); + virtual ~CPUTimer() {} + virtual void Start(); + virtual void Stop(); + virtual float MilliSeconds(); + virtual float MicroSeconds(); }; } // namespace caffe diff --git a/include/caffe/util/cudnn.hpp b/include/caffe/util/cudnn.hpp index aca5bd713fc..eaed7333df8 100644 --- a/include/caffe/util/cudnn.hpp +++ b/include/caffe/util/cudnn.hpp @@ -4,6 +4,7 @@ #include +#include "caffe/common.hpp" #include "caffe/proto/caffe.pb.h" #define CUDNN_CHECK(condition) \ diff --git a/include/caffe/util/db.hpp b/include/caffe/util/db.hpp new file mode 100644 index 00000000000..afdb8d2c4f8 --- /dev/null +++ b/include/caffe/util/db.hpp @@ -0,0 +1,190 @@ +#ifndef CAFFE_UTIL_DB_HPP +#define CAFFE_UTIL_DB_HPP + +#include + +#include "leveldb/db.h" +#include "leveldb/write_batch.h" +#include "lmdb.h" + +#include "caffe/common.hpp" +#include "caffe/proto/caffe.pb.h" + +namespace caffe { namespace db { + +enum Mode { READ, WRITE, NEW }; + +class Cursor { + public: + Cursor() { } + virtual ~Cursor() { } + virtual void SeekToFirst() = 0; + virtual void Next() = 0; + virtual string key() = 0; + virtual string value() = 0; + virtual bool valid() = 0; + + DISABLE_COPY_AND_ASSIGN(Cursor); +}; + +class Transaction { + public: + Transaction() { } + virtual ~Transaction() { } + virtual void Put(const string& key, const string& value) = 0; + virtual void Commit() = 0; + + DISABLE_COPY_AND_ASSIGN(Transaction); +}; + +class DB { + public: + DB() { } + virtual ~DB() { } + virtual void Open(const string& source, Mode mode) = 0; + virtual void Close() = 0; + virtual Cursor* NewCursor() = 0; + virtual Transaction* NewTransaction() = 0; + + DISABLE_COPY_AND_ASSIGN(DB); +}; + +class LevelDBCursor : public Cursor { + public: + explicit LevelDBCursor(leveldb::Iterator* iter) + : iter_(iter) { SeekToFirst(); } + ~LevelDBCursor() { delete iter_; } + virtual void SeekToFirst() { iter_->SeekToFirst(); } + virtual void Next() { iter_->Next(); } + virtual string key() { return iter_->key().ToString(); } + virtual string value() { return iter_->value().ToString(); } + virtual bool valid() { return iter_->Valid(); } + + private: + leveldb::Iterator* iter_; +}; + +class LevelDBTransaction : public Transaction { + public: + explicit LevelDBTransaction(leveldb::DB* db) : db_(db) { CHECK_NOTNULL(db_); } + virtual void Put(const string& key, const string& value) { + batch_.Put(key, value); + } + virtual void Commit() { + leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch_); + CHECK(status.ok()) << "Failed to write batch to leveldb " + << std::endl << status.ToString(); + } + + private: + leveldb::DB* db_; + leveldb::WriteBatch batch_; + + DISABLE_COPY_AND_ASSIGN(LevelDBTransaction); +}; + +class LevelDB : public DB { + public: + LevelDB() : db_(NULL) { } + virtual ~LevelDB() { Close(); } + virtual void Open(const string& source, Mode mode); + virtual void Close() { + if (db_ != NULL) { + delete db_; + db_ = NULL; + } + } + virtual LevelDBCursor* NewCursor() { + return new LevelDBCursor(db_->NewIterator(leveldb::ReadOptions())); + } + virtual LevelDBTransaction* NewTransaction() { + return new LevelDBTransaction(db_); + } + + private: + leveldb::DB* db_; +}; + +inline void MDB_CHECK(int mdb_status) { + CHECK_EQ(mdb_status, MDB_SUCCESS) << mdb_strerror(mdb_status); +} + +class LMDBCursor : public Cursor { + public: + explicit LMDBCursor(MDB_txn* mdb_txn, MDB_cursor* mdb_cursor) + : mdb_txn_(mdb_txn), mdb_cursor_(mdb_cursor), valid_(false) { + SeekToFirst(); + } + virtual ~LMDBCursor() { + mdb_cursor_close(mdb_cursor_); + mdb_txn_abort(mdb_txn_); + } + virtual void SeekToFirst() { Seek(MDB_FIRST); } + virtual void Next() { Seek(MDB_NEXT); } + virtual string key() { + return string(static_cast(mdb_key_.mv_data), mdb_key_.mv_size); + } + virtual string value() { + return string(static_cast(mdb_value_.mv_data), + mdb_value_.mv_size); + } + virtual bool valid() { return valid_; } + + private: + void Seek(MDB_cursor_op op) { + int mdb_status = mdb_cursor_get(mdb_cursor_, &mdb_key_, &mdb_value_, op); + if (mdb_status == MDB_NOTFOUND) { + valid_ = false; + } else { + MDB_CHECK(mdb_status); + valid_ = true; + } + } + + MDB_txn* mdb_txn_; + MDB_cursor* mdb_cursor_; + MDB_val mdb_key_, mdb_value_; + bool valid_; +}; + +class LMDBTransaction : public Transaction { + public: + explicit LMDBTransaction(MDB_dbi* mdb_dbi, MDB_txn* mdb_txn) + : mdb_dbi_(mdb_dbi), mdb_txn_(mdb_txn) { } + virtual void Put(const string& key, const string& value); + virtual void Commit() { MDB_CHECK(mdb_txn_commit(mdb_txn_)); } + + private: + MDB_dbi* mdb_dbi_; + MDB_txn* mdb_txn_; + + DISABLE_COPY_AND_ASSIGN(LMDBTransaction); +}; + +class LMDB : public DB { + public: + LMDB() : mdb_env_(NULL) { } + virtual ~LMDB() { Close(); } + virtual void Open(const string& source, Mode mode); + virtual void Close() { + if (mdb_env_ != NULL) { + mdb_dbi_close(mdb_env_, mdb_dbi_); + mdb_env_close(mdb_env_); + mdb_env_ = NULL; + } + } + virtual LMDBCursor* NewCursor(); + virtual LMDBTransaction* NewTransaction(); + + private: + MDB_env* mdb_env_; + MDB_dbi mdb_dbi_; +}; + +DB* GetDB(DataParameter::DB backend); +DB* GetDB(const string& backend); + +} // namespace db +} // namespace caffe + +#endif // CAFFE_UTIL_DB_HPP diff --git a/include/caffe/util/device_alternate.hpp b/include/caffe/util/device_alternate.hpp index 4d731e26199..6ea595dba2d 100644 --- a/include/caffe/util/device_alternate.hpp +++ b/include/caffe/util/device_alternate.hpp @@ -12,22 +12,22 @@ #define STUB_GPU(classname) \ template \ void classname::Forward_gpu(const vector*>& bottom, \ - vector*>* top) { NO_GPU; } \ + const vector*>& top) { NO_GPU; } \ template \ void classname::Backward_gpu(const vector*>& top, \ const vector& propagate_down, \ - vector*>* bottom) { NO_GPU; } \ + const vector*>& bottom) { NO_GPU; } \ #define STUB_GPU_FORWARD(classname, funcname) \ template \ void classname::funcname##_##gpu(const vector*>& bottom, \ - vector*>* top) { NO_GPU; } \ + const vector*>& top) { NO_GPU; } \ #define STUB_GPU_BACKWARD(classname, funcname) \ template \ void classname::funcname##_##gpu(const vector*>& top, \ const vector& propagate_down, \ - vector*>* bottom) { NO_GPU; } \ + const vector*>& bottom) { NO_GPU; } \ #else // Normal GPU + CPU Caffe. diff --git a/include/caffe/util/io.hpp b/include/caffe/util/io.hpp index 8dd338d2603..3a62c3c9fa9 100644 --- a/include/caffe/util/io.hpp +++ b/include/caffe/util/io.hpp @@ -9,15 +9,11 @@ #include "hdf5_hl.h" #include "caffe/blob.hpp" +#include "caffe/common.hpp" #include "caffe/proto/caffe.pb.h" #define HDF5_NUM_DIMS 4 -namespace leveldb { -// Forward declaration for leveldb::Options to be used in GetlevelDBOptions(). -struct Options; -} - namespace caffe { using ::google::protobuf::Message; @@ -25,27 +21,27 @@ using ::google::protobuf::Message; inline void MakeTempFilename(string* temp_filename) { temp_filename->clear(); *temp_filename = "/tmp/caffe_test.XXXXXX"; - char* temp_filename_cstr = new char[temp_filename->size()]; + char* temp_filename_cstr = new char[temp_filename->size() + 1]; // NOLINT_NEXT_LINE(runtime/printf) strcpy(temp_filename_cstr, temp_filename->c_str()); int fd = mkstemp(temp_filename_cstr); CHECK_GE(fd, 0) << "Failed to open a temporary file at: " << *temp_filename; close(fd); *temp_filename = temp_filename_cstr; - delete temp_filename_cstr; + delete[] temp_filename_cstr; } inline void MakeTempDir(string* temp_dirname) { temp_dirname->clear(); *temp_dirname = "/tmp/caffe_test.XXXXXX"; - char* temp_dirname_cstr = new char[temp_dirname->size()]; + char* temp_dirname_cstr = new char[temp_dirname->size() + 1]; // NOLINT_NEXT_LINE(runtime/printf) strcpy(temp_dirname_cstr, temp_dirname->c_str()); char* mkdtemp_result = mkdtemp(temp_dirname_cstr); CHECK(mkdtemp_result != NULL) << "Failed to create a temporary directory at: " << *temp_dirname; *temp_dirname = temp_dirname_cstr; - delete temp_dirname_cstr; + delete[] temp_dirname_cstr; } bool ReadProtoFromTextFile(const char* filename, Message* proto); @@ -89,34 +85,74 @@ inline void WriteProtoToBinaryFile( WriteProtoToBinaryFile(proto, filename.c_str()); } +bool ReadFileToDatum(const string& filename, const int label, Datum* datum); + +inline bool ReadFileToDatum(const string& filename, Datum* datum) { + return ReadFileToDatum(filename, -1, datum); +} + bool ReadImageToDatum(const string& filename, const int label, - const int height, const int width, const bool is_color, Datum* datum); + const int height, const int width, const bool is_color, + const std::string & encoding, Datum* datum); + +inline bool ReadImageToDatum(const string& filename, const int label, + const int height, const int width, const bool is_color, Datum* datum) { + return ReadImageToDatum(filename, label, height, width, is_color, + "", datum); +} inline bool ReadImageToDatum(const string& filename, const int label, const int height, const int width, Datum* datum) { return ReadImageToDatum(filename, label, height, width, true, datum); } +inline bool ReadImageToDatum(const string& filename, const int label, + const bool is_color, Datum* datum) { + return ReadImageToDatum(filename, label, 0, 0, is_color, datum); +} + inline bool ReadImageToDatum(const string& filename, const int label, Datum* datum) { - return ReadImageToDatum(filename, label, 0, 0, datum); + return ReadImageToDatum(filename, label, 0, 0, true, datum); } -leveldb::Options GetLevelDBOptions(); +inline bool ReadImageToDatum(const string& filename, const int label, + const std::string & encoding, Datum* datum) { + return ReadImageToDatum(filename, label, 0, 0, true, encoding, datum); +} + +bool DecodeDatumNative(Datum* datum); +bool DecodeDatum(Datum* datum, bool is_color); + +cv::Mat ReadImageToCVMat(const string& filename, + const int height, const int width, const bool is_color); + +cv::Mat ReadImageToCVMat(const string& filename, + const int height, const int width); + +cv::Mat ReadImageToCVMat(const string& filename, + const bool is_color); + +cv::Mat ReadImageToCVMat(const string& filename); + +cv::Mat DecodeDatumToCVMatNative(const Datum& datum); +cv::Mat DecodeDatumToCVMat(const Datum& datum, bool is_color); + +void CVMatToDatum(const cv::Mat& cv_img, Datum* datum); template void hdf5_load_nd_dataset_helper( - hid_t file_id, const char* dataset_name_, int min_dim, int max_dim, - Blob* blob); + hid_t file_id, const char* dataset_name_, int min_dim, int max_dim, + Blob* blob); template void hdf5_load_nd_dataset( - hid_t file_id, const char* dataset_name_, int min_dim, int max_dim, - Blob* blob); + hid_t file_id, const char* dataset_name_, int min_dim, int max_dim, + Blob* blob); template void hdf5_save_nd_dataset( - const hid_t file_id, const string dataset_name, const Blob& blob); + const hid_t file_id, const string& dataset_name, const Blob& blob); } // namespace caffe diff --git a/include/caffe/util/math_functions.hpp b/include/caffe/util/math_functions.hpp index 62467fb5c52..f43036fcebc 100644 --- a/include/caffe/util/math_functions.hpp +++ b/include/caffe/util/math_functions.hpp @@ -12,7 +12,7 @@ namespace caffe { -// Decaf gemm provides a simpler interface to the gemm functions, with the +// Caffe gemm provides a simpler interface to the gemm functions, with the // limitation that the data has to be contiguous in memory. template void caffe_cpu_gemm(const CBLAS_TRANSPOSE TransA, @@ -108,7 +108,7 @@ Dtype caffe_cpu_asum(const int n, const Dtype* x); // the branchless, type-safe version from // http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c template -inline char caffe_sign(Dtype val) { +inline int8_t caffe_sign(Dtype val) { return (Dtype(0) < val) - (val < Dtype(0)); } @@ -127,12 +127,6 @@ inline char caffe_sign(Dtype val) { } \ } -#define INSTANTIATE_CAFFE_CPU_UNARY_FUNC(name) \ - template <> \ - void caffe_cpu_##name(const int n, const float* x, float* y); \ - template <> \ - void caffe_cpu_##name(const int n, const double* x, double* y) - // output is 1 for the positives, 0 for zero, and -1 for the negatives DEFINE_CAFFE_CPU_UNARY_FUNC(sign, y[i] = caffe_sign(x[i])); @@ -140,7 +134,8 @@ DEFINE_CAFFE_CPU_UNARY_FUNC(sign, y[i] = caffe_sign(x[i])); // The name sngbit is meant to avoid conflicts with std::signbit in the macro. // The extra parens are needed because CUDA < 6.5 defines signbit as a macro, // and we don't want that to expand here when CUDA headers are also included. -DEFINE_CAFFE_CPU_UNARY_FUNC(sgnbit, y[i] = (std::signbit)(x[i])); +DEFINE_CAFFE_CPU_UNARY_FUNC(sgnbit, \ + y[i] = static_cast((std::signbit)(x[i]))); DEFINE_CAFFE_CPU_UNARY_FUNC(fabs, y[i] = std::fabs(x[i])); @@ -205,6 +200,9 @@ void caffe_gpu_div(const int N, const Dtype* a, const Dtype* b, Dtype* y); template void caffe_gpu_abs(const int n, const Dtype* a, Dtype* y); +template +void caffe_gpu_exp(const int n, const Dtype* a, Dtype* y); + template void caffe_gpu_powx(const int n, const Dtype* a, const Dtype b, Dtype* y); @@ -269,7 +267,7 @@ void caffe_gpu_##name(const int n, const double* x, double* y) { \ n, x, y); \ } -#endif // CPU_ONLY +#endif // !CPU_ONLY } // namespace caffe diff --git a/include/caffe/util/upgrade_proto.hpp b/include/caffe/util/upgrade_proto.hpp index 45483685133..c1f21a0d4d8 100644 --- a/include/caffe/util/upgrade_proto.hpp +++ b/include/caffe/util/upgrade_proto.hpp @@ -4,13 +4,15 @@ #include #include "caffe/proto/caffe.pb.h" -#include "caffe/proto/caffe_pretty_print.pb.h" namespace caffe { +// Return true iff the net is not the current version. +bool NetNeedsUpgrade(const NetParameter& net_param); + // Return true iff any layer contains parameters specified using // deprecated V0LayerParameter. -bool NetNeedsUpgrade(const NetParameter& net_param); +bool NetNeedsV0ToV1Upgrade(const NetParameter& net_param); // Perform all necessary transformations to upgrade a V0NetParameter into a // NetParameter (including upgrading padding layers and LayerParameters). @@ -23,11 +25,11 @@ bool UpgradeV0Net(const NetParameter& v0_net_param, NetParameter* net_param); void UpgradeV0PaddingLayers(const NetParameter& param, NetParameter* param_upgraded_pad); -// Upgrade a single V0LayerConnection to the new LayerParameter format. -bool UpgradeLayerParameter(const LayerParameter& v0_layer_connection, - LayerParameter* layer_param); +// Upgrade a single V0LayerConnection to the V1LayerParameter format. +bool UpgradeV0LayerParameter(const V1LayerParameter& v0_layer_connection, + V1LayerParameter* layer_param); -LayerParameter_LayerType UpgradeV0LayerType(const string& type); +V1LayerParameter_LayerType UpgradeV0LayerType(const string& type); // Return true iff any layer contains deprecated data transformation parameters. bool NetNeedsDataUpgrade(const NetParameter& net_param); @@ -36,13 +38,20 @@ bool NetNeedsDataUpgrade(const NetParameter& net_param); // into a TransformationParameter. void UpgradeNetDataTransformation(NetParameter* net_param); -// Convert a NetParameter to NetParameterPrettyPrint used for dumping to -// proto text files. -void NetParameterToPrettyPrint(const NetParameter& param, - NetParameterPrettyPrint* pretty_param); +// Return true iff the Net contains any layers specified as V1LayerParameters. +bool NetNeedsV1ToV2Upgrade(const NetParameter& net_param); + +// Perform all necessary transformations to upgrade a NetParameter with +// deprecated V1LayerParameters. +bool UpgradeV1Net(const NetParameter& v1_net_param, NetParameter* net_param); + +bool UpgradeV1LayerParameter(const V1LayerParameter& v1_layer_param, + LayerParameter* layer_param); + +const char* UpgradeV1LayerType(const V1LayerParameter_LayerType type); // Check for deprecations and upgrade the NetParameter as needed. -void UpgradeNetAsNeeded(NetParameter* param); +bool UpgradeNetAsNeeded(const string& param_file, NetParameter* param); // Read parameters from a file into a NetParameter proto message. void ReadNetParamsFromTextFileOrDie(const string& param_file, diff --git a/include/caffe/vision_layers.hpp b/include/caffe/vision_layers.hpp index 1e7f3fcb297..6cb507a5780 100644 --- a/include/caffe/vision_layers.hpp +++ b/include/caffe/vision_layers.hpp @@ -16,6 +16,101 @@ namespace caffe { +/** + * @brief Abstract base class that factors out the BLAS code common to + * ConvolutionLayer and DeconvolutionLayer. + */ +template +class BaseConvolutionLayer : public Layer { + public: + explicit BaseConvolutionLayer(const LayerParameter& param) + : Layer(param) {} + virtual void LayerSetUp(const vector*>& bottom, + const vector*>& top); + virtual void Reshape(const vector*>& bottom, + const vector*>& top); + + virtual inline int MinBottomBlobs() const { return 1; } + virtual inline int MinTopBlobs() const { return 1; } + virtual inline bool EqualNumBottomTopBlobs() const { return true; } + + protected: + // Helper functions that abstract away the column buffer and gemm arguments. + // The last argument in forward_cpu_gemm is so that we can skip the im2col if + // we just called weight_cpu_gemm with the same input. + void forward_cpu_gemm(const Dtype* input, const Dtype* weights, + Dtype* output, bool skip_im2col = false); + void forward_cpu_bias(Dtype* output, const Dtype* bias); + void backward_cpu_gemm(const Dtype* input, const Dtype* weights, + Dtype* output); + void weight_cpu_gemm(const Dtype* input, const Dtype* output, Dtype* + weights); + void backward_cpu_bias(Dtype* bias, const Dtype* input); + +#ifndef CPU_ONLY + void forward_gpu_gemm(const Dtype* col_input, const Dtype* weights, + Dtype* output, bool skip_im2col = false); + void forward_gpu_bias(Dtype* output, const Dtype* bias); + void backward_gpu_gemm(const Dtype* input, const Dtype* weights, + Dtype* col_output); + void weight_gpu_gemm(const Dtype* col_input, const Dtype* output, Dtype* + weights); + void backward_gpu_bias(Dtype* bias, const Dtype* input); +#endif + + // reverse_dimensions should return true iff we are implementing deconv, so + // that conv helpers know which dimensions are which. + virtual bool reverse_dimensions() = 0; + // Compute height_out_ and width_out_ from other parameters. + virtual void compute_output_shape() = 0; + + int kernel_h_, kernel_w_; + int stride_h_, stride_w_; + int num_; + int channels_; + int pad_h_, pad_w_; + int height_, width_; + int group_; + int num_output_; + int height_out_, width_out_; + bool bias_term_; + bool is_1x1_; + + private: + // wrap im2col/col2im so we don't have to remember the (long) argument lists + inline void conv_im2col_cpu(const Dtype* data, Dtype* col_buff) { + im2col_cpu(data, conv_in_channels_, conv_in_height_, conv_in_width_, + kernel_h_, kernel_w_, pad_h_, pad_w_, stride_h_, stride_w_, col_buff); + } + inline void conv_col2im_cpu(const Dtype* col_buff, Dtype* data) { + col2im_cpu(col_buff, conv_in_channels_, conv_in_height_, conv_in_width_, + kernel_h_, kernel_w_, pad_h_, pad_w_, stride_h_, stride_w_, data); + } +#ifndef CPU_ONLY + inline void conv_im2col_gpu(const Dtype* data, Dtype* col_buff) { + im2col_gpu(data, conv_in_channels_, conv_in_height_, conv_in_width_, + kernel_h_, kernel_w_, pad_h_, pad_w_, stride_h_, stride_w_, col_buff); + } + inline void conv_col2im_gpu(const Dtype* col_buff, Dtype* data) { + col2im_gpu(col_buff, conv_in_channels_, conv_in_height_, conv_in_width_, + kernel_h_, kernel_w_, pad_h_, pad_w_, stride_h_, stride_w_, data); + } +#endif + + int conv_out_channels_; + int conv_in_channels_; + int conv_out_spatial_dim_; + int conv_in_height_; + int conv_in_width_; + int kernel_dim_; + int weight_offset_; + int col_offset_; + int output_offset_; + + Blob col_buffer_; + Blob bias_multiplier_; +}; + /** * @brief Convolves the input image with a bank of learned filters, * and (optionally) adds biases. @@ -33,7 +128,7 @@ namespace caffe { * the output channel N' columns of the output matrix. */ template -class ConvolutionLayer : public Layer { +class ConvolutionLayer : public BaseConvolutionLayer { public: /** * @param param provides ConvolutionParameter convolution_param, @@ -64,51 +159,56 @@ class ConvolutionLayer : public Layer { * kernels + stream parallelism) engines. */ explicit ConvolutionLayer(const LayerParameter& param) - : Layer(param) {} - virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); - virtual void Reshape(const vector*>& bottom, - vector*>* top); + : BaseConvolutionLayer(param) {} - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_CONVOLUTION; - } - virtual inline int MinBottomBlobs() const { return 1; } - virtual inline int MinTopBlobs() const { return 1; } - virtual inline bool EqualNumBottomTopBlobs() const { return true; } + virtual inline const char* type() const { return "Convolution"; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); + virtual inline bool reverse_dimensions() { return false; } + virtual void compute_output_shape(); +}; - int kernel_h_, kernel_w_; - int stride_h_, stride_w_; - int num_; - int channels_; - int pad_h_, pad_w_; - int height_, width_; - int group_; - int num_output_; - int height_out_, width_out_; - bool bias_term_; +/** + * @brief Convolve the input with a bank of learned filters, and (optionally) + * add biases, treating filters and convolution parameters in the + * opposite sense as ConvolutionLayer. + * + * ConvolutionLayer computes each output value by dotting an input window with + * a filter; DeconvolutionLayer multiplies each input value by a filter + * elementwise, and sums over the resulting output windows. In other words, + * DeconvolutionLayer is ConvolutionLayer with the forward and backward passes + * reversed. DeconvolutionLayer reuses ConvolutionParameter for its + * parameters, but they take the opposite sense as in ConvolutionLayer (so + * padding is removed from the output rather than added to the input, and + * stride results in upsampling rather than downsampling). + */ +template +class DeconvolutionLayer : public BaseConvolutionLayer { + public: + explicit DeconvolutionLayer(const LayerParameter& param) + : BaseConvolutionLayer(param) {} - /// M_ is the channel dimension of the output for a single group, which is the - /// leading dimension of the filter matrix. - int M_; - /// K_ is the dimension of an unrolled input for a single group, which is the - /// leading dimension of the data matrix. - int K_; - /// N_ is the spatial dimension of the output, the H x W, which are the last - /// dimensions of the data and filter matrices. - int N_; - Blob col_buffer_; - Blob bias_multiplier_; + virtual inline const char* type() const { return "Deconvolution"; } + + protected: + virtual void Forward_cpu(const vector*>& bottom, + const vector*>& top); + virtual void Forward_gpu(const vector*>& bottom, + const vector*>& top); + virtual void Backward_cpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom); + virtual void Backward_gpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom); + virtual inline bool reverse_dimensions() { return true; } + virtual void compute_output_shape(); }; #ifdef USE_CUDNN @@ -130,19 +230,20 @@ template class CuDNNConvolutionLayer : public ConvolutionLayer { public: explicit CuDNNConvolutionLayer(const LayerParameter& param) - : ConvolutionLayer(param) {} + : ConvolutionLayer(param), handles_setup_(false) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual ~CuDNNConvolutionLayer(); protected: virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); + bool handles_setup_; cudnnHandle_t* handle_; cudaStream_t* stream_; vector bottom_descs_, top_descs_; @@ -166,25 +267,23 @@ class Im2colLayer : public Layer { explicit Im2colLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_IM2COL; - } + virtual inline const char* type() const { return "Im2col"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); int kernel_h_, kernel_w_; int stride_h_, stride_w_; @@ -208,43 +307,42 @@ class LRNLayer : public Layer { explicit LRNLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_LRN; - } + virtual inline const char* type() const { return "LRN"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; } protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void CrossChannelForward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void CrossChannelForward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void WithinChannelForward(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void CrossChannelBackward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void CrossChannelBackward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void WithinChannelBackward(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); int size_; int pre_pad_; Dtype alpha_; Dtype beta_; + Dtype k_; int num_; int channels_; int height_; @@ -285,13 +383,11 @@ class PoolingLayer : public Layer { explicit PoolingLayer(const LayerParameter& param) : Layer(param) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); - virtual inline LayerParameter_LayerType type() const { - return LayerParameter_LayerType_POOLING; - } + virtual inline const char* type() const { return "Pooling"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int MinTopBlobs() const { return 1; } // MAX POOL layers can output an extra top blob for the mask; @@ -303,13 +399,13 @@ class PoolingLayer : public Layer { protected: virtual void Forward_cpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); int kernel_h_, kernel_w_; int stride_h_, stride_w_; @@ -317,6 +413,7 @@ class PoolingLayer : public Layer { int channels_; int height_, width_; int pooled_height_, pooled_width_; + bool global_pooling_; Blob rand_idx_; Blob max_idx_; }; @@ -330,19 +427,23 @@ template class CuDNNPoolingLayer : public PoolingLayer { public: explicit CuDNNPoolingLayer(const LayerParameter& param) - : PoolingLayer(param) {} + : PoolingLayer(param), handles_setup_(false) {} virtual void LayerSetUp(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Reshape(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual ~CuDNNPoolingLayer(); + // Currently, cuDNN does not support the extra top blob. + virtual inline int MinTopBlobs() const { return -1; } + virtual inline int ExactNumTopBlobs() const { return 1; } protected: virtual void Forward_gpu(const vector*>& bottom, - vector*>* top); + const vector*>& top); virtual void Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom); + const vector& propagate_down, const vector*>& bottom); + bool handles_setup_; cudnnHandle_t handle_; cudnnTensor4dDescriptor_t bottom_desc_, top_desc_; cudnnPoolingDescriptor_t pooling_desc_; diff --git a/matlab/CMakeLists.txt b/matlab/CMakeLists.txt index f6a03ee4625..791a4e70f43 100644 --- a/matlab/CMakeLists.txt +++ b/matlab/CMakeLists.txt @@ -1 +1,72 @@ -project( Matlab ) \ No newline at end of file +# Builds Matlab (or Octave) interface. In case of Matlab caffe must be +# compield as shared library. Octave can link static or shared caffe library +# To install octave run: sudo apt-get install liboctave-dev + +if(NOT BUILD_matlab) + return() +endif() + +if(HAVE_MATLAB AND Octave_compiler) + set(build_using ${Matlab_build_mex_using}) +elseif(HAVE_MATLAB AND NOT Octave_compiler) + set(build_using "Matlab") +elseif(NOT HAVE_MATLAB AND Octave_compiler) + set(build_using "Octave") +else() + return() +endif() + +if(NOT BUILD_SHARED_LIBS AND build_using MATCHES Matlab) + message(FATAL_ERROR "Matlab MEX interface (with default mex options file) can only be built if caffe is compiled as shared library. Please enable 'BUILD_SHARED_LIBS' in CMake. Aternativelly you can switch to Octave compiler.") +endif() + +# helper function to set proper mex file extention +function(caffe_fetch_and_set_proper_mexext mexfile_variable) + execute_process(COMMAND ${Matlab_mexext} OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE res OUTPUT_VARIABLE ext) + if(res MATCHES 0) + get_filename_component(folder ${${mexfile_variable}} PATH) + get_filename_component(name_we ${${mexfile_variable}} NAME_WE) + set(${mexfile_variable} ${folder}/${name_we}.${ext} PARENT_SCOPE) + endif() +endfunction() + +# global settings +file(GLOB Matlab_srcs caffe/matcaffe.cpp) +set(Matlab_caffe_mex ${PROJECT_SOURCE_DIR}/matlab/caffe/caffe.mex) + +caffe_get_current_cflags(cflags) +caffe_parse_linker_libs(Caffe_LINKER_LIBS folders libflags macos_frameworks) +set(folders $ ${folders}) + +# prepare linker flag lists +string(REPLACE ";" ";-L" link_folders "-L${folders}") +string(REPLACE ";" ":" rpath_folders "${folders}") + +if(build_using MATCHES "Matlab") + set(libflags -lcaffe${CAffe_POSTFIX} ${libflags}) # Matlab R2014a complans for -Wl,--whole-archive + + caffe_fetch_and_set_proper_mexext(Matlab_caffe_mex) + add_custom_command(OUTPUT ${Matlab_caffe_mex} COMMAND ${Matlab_mex} + ARGS -output ${Matlab_caffe_mex} ${Matlab_srcs} ${cflags} ${link_folders} ${libflags} + DEPENDS caffe COMMENT "Building Matlab interface: ${Matlab_caffe_mex}" VERBATIM) + add_custom_target(matlab ALL DEPENDS ${Matlab_caffe_mex} SOURCES ${Matlab_srcs}) + +elseif(build_using MATCHES "Octave") + + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(libflags -Wl,-force_load,$ ${libflags}) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(libflags -Wl,--whole-archive -lcaffe${CAffe_POSTFIX} -Wl,--no-whole-archive ${libflags}) + endif() + + add_custom_command(OUTPUT ${Matlab_caffe_mex} COMMAND ${Octave_compiler} + ARGS --mex -o ${Matlab_caffe_mex} ${Matlab_srcs} ${cflags} ${link_folders} ${libflags} -Wl,-rpath,${rpath_folders} + DEPENDS caffe COMMENT "Building Octave interface: ${Matlab_caffe_mex}" VERBATIM) + + add_custom_target(octave ALL DEPENDS ${Matlab_caffe_mex} SOURCES ${Matlab_srcs}) +endif() + +# ---[ Install +file(GLOB mfiles caffe/*.m) +install(FILES ${mfiles} ${Matlab_caffe_mex} DESTINATION matlab) + diff --git a/matlab/caffe/hdf5creation/.gitignore b/matlab/caffe/hdf5creation/.gitignore new file mode 100644 index 00000000000..e2333dd1776 --- /dev/null +++ b/matlab/caffe/hdf5creation/.gitignore @@ -0,0 +1,2 @@ +*.h5 +list.txt diff --git a/matlab/caffe/hdf5creation/demo.m b/matlab/caffe/hdf5creation/demo.m new file mode 100644 index 00000000000..f554b87e5f6 --- /dev/null +++ b/matlab/caffe/hdf5creation/demo.m @@ -0,0 +1,64 @@ +%% WRITING TO HDF5 +filename='trial.h5'; + +num_total_samples=10000; +% to simulate data being read from disk / generated etc. +data_disk=rand(5,5,1,num_total_samples); +label_disk=rand(10,num_total_samples); + +chunksz=100; +created_flag=false; +totalct=0; +for batchno=1:num_total_samples/chunksz + fprintf('batch no. %d\n', batchno); + last_read=(batchno-1)*chunksz; + + % to simulate maximum data to be held in memory before dumping to hdf5 file + batchdata=data_disk(:,:,1,last_read+1:last_read+chunksz); + batchlabs=label_disk(:,last_read+1:last_read+chunksz); + + % store to hdf5 + startloc=struct('dat',[1,1,1,totalct+1], 'lab', [1,totalct+1]); + curr_dat_sz=store2hdf5(filename, batchdata, batchlabs, ~created_flag, startloc, chunksz); + created_flag=true;% flag set so that file is created only once + totalct=curr_dat_sz(end);% updated dataset size (#samples) +end + +% display structure of the stored HDF5 file +h5disp(filename); + +%% READING FROM HDF5 + +% Read data and labels for samples #1000 to 1999 +data_rd=h5read(filename, '/data', [1 1 1 1000], [5, 5, 1, 1000]); +label_rd=h5read(filename, '/label', [1 1000], [10, 1000]); +fprintf('Testing ...\n'); +try + assert(isequal(data_rd, single(data_disk(:,:,:,1000:1999))), 'Data do not match'); + assert(isequal(label_rd, single(label_disk(:,1000:1999))), 'Labels do not match'); + + fprintf('Success!\n'); +catch err + fprintf('Test failed ...\n'); + getReport(err) +end + +%delete(filename); + +% CREATE list.txt containing filename, to be used as source for HDF5_DATA_LAYER +FILE=fopen('list.txt', 'w'); +fprintf(FILE, '%s', filename); +fclose(FILE); +fprintf('HDF5 filename listed in %s \n', 'list.txt'); + +% NOTE: In net definition prototxt, use list.txt as input to HDF5_DATA as: +% layers { +% name: "data" +% type: HDF5_DATA +% top: "data" +% top: "labelvec" +% hdf5_data_param { +% source: "/path/to/list.txt" +% batch_size: 64 +% } +% } diff --git a/matlab/caffe/hdf5creation/store2hdf5.m b/matlab/caffe/hdf5creation/store2hdf5.m new file mode 100644 index 00000000000..0a0016dca40 --- /dev/null +++ b/matlab/caffe/hdf5creation/store2hdf5.m @@ -0,0 +1,59 @@ +function [curr_dat_sz, curr_lab_sz] = store2hdf5(filename, data, labels, create, startloc, chunksz) + % *data* is W*H*C*N matrix of images should be normalized (e.g. to lie between 0 and 1) beforehand + % *label* is D*N matrix of labels (D labels per sample) + % *create* [0/1] specifies whether to create file newly or to append to previously created file, useful to store information in batches when a dataset is too big to be held in memory (default: 1) + % *startloc* (point at which to start writing data). By default, + % if create=1 (create mode), startloc.data=[1 1 1 1], and startloc.lab=[1 1]; + % if create=0 (append mode), startloc.data=[1 1 1 K+1], and startloc.lab = [1 K+1]; where K is the current number of samples stored in the HDF + % chunksz (used only in create mode), specifies number of samples to be stored per chunk (see HDF5 documentation on chunking) for creating HDF5 files with unbounded maximum size - TLDR; higher chunk sizes allow faster read-write operations + + % verify that format is right + dat_dims=size(data); + lab_dims=size(labels); + num_samples=dat_dims(end); + + assert(lab_dims(end)==num_samples, 'Number of samples should be matched between data and labels'); + + if ~exist('create','var') + create=true; + end + + + if create + %fprintf('Creating dataset with %d samples\n', num_samples); + if ~exist('chunksz', 'var') + chunksz=1000; + end + if exist(filename, 'file') + fprintf('Warning: replacing existing file %s \n', filename); + delete(filename); + end + h5create(filename, '/data', [dat_dims(1:end-1) Inf], 'Datatype', 'single', 'ChunkSize', [dat_dims(1:end-1) chunksz]); % width, height, channels, number + h5create(filename, '/label', [lab_dims(1:end-1) Inf], 'Datatype', 'single', 'ChunkSize', [lab_dims(1:end-1) chunksz]); % width, height, channels, number + if ~exist('startloc','var') + startloc.dat=[ones(1,length(dat_dims)-1), 1]; + startloc.lab=[ones(1,length(lab_dims)-1), 1]; + end + else % append mode + if ~exist('startloc','var') + info=h5info(filename); + prev_dat_sz=info.Datasets(1).Dataspace.Size; + prev_lab_sz=info.Datasets(2).Dataspace.Size; + assert(prev_dat_sz(1:end-1)==dat_dims(1:end-1), 'Data dimensions must match existing dimensions in dataset'); + assert(prev_lab_sz(1:end-1)==lab_dims(1:end-1), 'Label dimensions must match existing dimensions in dataset'); + startloc.dat=[ones(1,length(dat_dims)-1), prev_dat_sz(end)+1]; + startloc.lab=[ones(1,length(lab_dims)-1), prev_lab_sz(end)+1]; + end + end + + if ~isempty(data) + h5write(filename, '/data', single(data), startloc.dat, size(data)); + h5write(filename, '/label', single(labels), startloc.lab, size(labels)); + end + + if nargout + info=h5info(filename); + curr_dat_sz=info.Datasets(1).Dataspace.Size; + curr_lab_sz=info.Datasets(2).Dataspace.Size; + end +end diff --git a/matlab/caffe/matcaffe.cpp b/matlab/caffe/matcaffe.cpp index 83786caed0d..996d3d2149c 100644 --- a/matlab/caffe/matcaffe.cpp +++ b/matlab/caffe/matcaffe.cpp @@ -3,6 +3,7 @@ // caffe::Caffe functions so that one could easily call it from matlab. // Note that for matlab, we will simply use float as the data type. +#include #include #include @@ -12,6 +13,12 @@ #define MEX_ARGS int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs +// Log and throw a Mex error +inline void mex_error(const std::string &msg) { + LOG(ERROR) << msg; + mexErrMsgTxt(msg.c_str()); +} + using namespace caffe; // NOLINT(build/namespaces) // The pointer to the internal caffe::Net instance @@ -45,15 +52,23 @@ static int init_key = -2; // input and outputs a cell array. static mxArray* do_forward(const mxArray* const bottom) { - vector*>& input_blobs = net_->input_blobs(); - CHECK_EQ(static_cast(mxGetDimensions(bottom)[0]), - input_blobs.size()); + const vector*>& input_blobs = net_->input_blobs(); + if (static_cast(mxGetDimensions(bottom)[0]) != + input_blobs.size()) { + mex_error("Invalid input size"); + } for (unsigned int i = 0; i < input_blobs.size(); ++i) { const mxArray* const elem = mxGetCell(bottom, i); - CHECK(mxIsSingle(elem)) - << "MatCaffe require single-precision float point data"; - CHECK_EQ(mxGetNumberOfElements(elem), input_blobs[i]->count()) - << "MatCaffe input size does not match the input size of the network"; + if (!mxIsSingle(elem)) { + mex_error("MatCaffe require single-precision float point data"); + } + if (mxGetNumberOfElements(elem) != input_blobs[i]->count()) { + std::string error_msg; + error_msg += "MatCaffe input size does not match the input size "; + error_msg += "of the network"; + mex_error(error_msg); + } + const float* const data_ptr = reinterpret_cast(mxGetPr(elem)); switch (Caffe::mode()) { @@ -66,7 +81,7 @@ static mxArray* do_forward(const mxArray* const bottom) { input_blobs[i]->mutable_gpu_data()); break; default: - LOG(FATAL) << "Unknown Caffe mode."; + mex_error("Unknown Caffe mode"); } // switch (Caffe::mode()) } const vector*>& output_blobs = net_->ForwardPrefilled(); @@ -89,7 +104,7 @@ static mxArray* do_forward(const mxArray* const bottom) { data_ptr); break; default: - LOG(FATAL) << "Unknown Caffe mode."; + mex_error("Unknown Caffe mode"); } // switch (Caffe::mode()) } @@ -97,10 +112,12 @@ static mxArray* do_forward(const mxArray* const bottom) { } static mxArray* do_backward(const mxArray* const top_diff) { - vector*>& output_blobs = net_->output_blobs(); - vector*>& input_blobs = net_->input_blobs(); - CHECK_EQ(static_cast(mxGetDimensions(top_diff)[0]), - output_blobs.size()); + const vector*>& output_blobs = net_->output_blobs(); + const vector*>& input_blobs = net_->input_blobs(); + if (static_cast(mxGetDimensions(top_diff)[0]) != + output_blobs.size()) { + mex_error("Invalid input size"); + } // First, copy the output diff for (unsigned int i = 0; i < output_blobs.size(); ++i) { const mxArray* const elem = mxGetCell(top_diff, i); @@ -116,7 +133,7 @@ static mxArray* do_backward(const mxArray* const top_diff) { output_blobs[i]->mutable_gpu_diff()); break; default: - LOG(FATAL) << "Unknown Caffe mode."; + mex_error("Unknown Caffe mode"); } // switch (Caffe::mode()) } // LOG(INFO) << "Start"; @@ -139,7 +156,7 @@ static mxArray* do_backward(const mxArray* const top_diff) { caffe_copy(input_blobs[i]->count(), input_blobs[i]->gpu_diff(), data_ptr); break; default: - LOG(FATAL) << "Unknown Caffe mode."; + mex_error("Unknown Caffe mode"); } // switch (Caffe::mode()) } @@ -216,7 +233,7 @@ static mxArray* do_get_weights() { weights_ptr); break; default: - LOG(FATAL) << "Unknown caffe mode: " << Caffe::mode(); + mex_error("Unknown Caffe mode"); } } } @@ -237,18 +254,11 @@ static void set_mode_gpu(MEX_ARGS) { Caffe::set_mode(Caffe::GPU); } -static void set_phase_train(MEX_ARGS) { - Caffe::set_phase(Caffe::TRAIN); -} - -static void set_phase_test(MEX_ARGS) { - Caffe::set_phase(Caffe::TEST); -} - static void set_device(MEX_ARGS) { if (nrhs != 1) { - LOG(ERROR) << "Only given " << nrhs << " arguments"; - mexErrMsgTxt("Wrong number of arguments"); + ostringstream error_msg; + error_msg << "Expected 1 argument, got " << nrhs; + mex_error(error_msg.str()); } int device_id = static_cast(mxGetScalar(prhs[0])); @@ -260,19 +270,31 @@ static void get_init_key(MEX_ARGS) { } static void init(MEX_ARGS) { - if (nrhs != 2) { - LOG(ERROR) << "Only given " << nrhs << " arguments"; - mexErrMsgTxt("Wrong number of arguments"); + if (nrhs != 3) { + ostringstream error_msg; + error_msg << "Expected 2 arguments, got " << nrhs; + mex_error(error_msg.str()); } char* param_file = mxArrayToString(prhs[0]); char* model_file = mxArrayToString(prhs[1]); + char* phase_name = mxArrayToString(prhs[2]); + + Phase phase; + if (strcmp(phase_name, "train") == 0) { + phase = TRAIN; + } else if (strcmp(phase_name, "test") == 0) { + phase = TEST; + } else { + mex_error("Unknown phase."); + } - net_.reset(new Net(string(param_file))); + net_.reset(new Net(string(param_file), phase)); net_->CopyTrainedLayersFrom(string(model_file)); mxFree(param_file); mxFree(model_file); + mxFree(phase_name); init_key = random(); // NOLINT(caffe/random_fn) @@ -291,8 +313,9 @@ static void reset(MEX_ARGS) { static void forward(MEX_ARGS) { if (nrhs != 1) { - LOG(ERROR) << "Only given " << nrhs << " arguments"; - mexErrMsgTxt("Wrong number of arguments"); + ostringstream error_msg; + error_msg << "Expected 1 argument, got " << nrhs; + mex_error(error_msg.str()); } plhs[0] = do_forward(prhs[0]); @@ -300,8 +323,9 @@ static void forward(MEX_ARGS) { static void backward(MEX_ARGS) { if (nrhs != 1) { - LOG(ERROR) << "Only given " << nrhs << " arguments"; - mexErrMsgTxt("Wrong number of arguments"); + ostringstream error_msg; + error_msg << "Expected 1 argument, got " << nrhs; + mex_error(error_msg.str()); } plhs[0] = do_backward(prhs[0]); @@ -322,7 +346,7 @@ static void read_mean(MEX_ARGS) { } const string& mean_file = mxArrayToString(prhs[0]); Blob data_mean; - LOG(INFO) << "Loading mean file from" << mean_file; + LOG(INFO) << "Loading mean file from: " << mean_file; BlobProto blob_proto; bool result = ReadProtoFromBinaryFile(mean_file.c_str(), &blob_proto); if (!result) { @@ -356,8 +380,6 @@ static handler_registry handlers[] = { { "is_initialized", is_initialized }, { "set_mode_cpu", set_mode_cpu }, { "set_mode_gpu", set_mode_gpu }, - { "set_phase_train", set_phase_train }, - { "set_phase_test", set_phase_test }, { "set_device", set_device }, { "get_weights", get_weights }, { "get_init_key", get_init_key }, @@ -372,9 +394,9 @@ static handler_registry handlers[] = { ** matlab entry point: caffe(api_command, arg1, arg2, ...) **/ void mexFunction(MEX_ARGS) { + mexLock(); // Avoid clearing the mex file. if (nrhs == 0) { - LOG(ERROR) << "No API command given"; - mexErrMsgTxt("An API command is requires"); + mex_error("No API command given"); return; } @@ -390,8 +412,9 @@ void mexFunction(MEX_ARGS) { } } if (!dispatched) { - LOG(ERROR) << "Unknown command `" << cmd << "'"; - mexErrMsgTxt("API command not recognized"); + ostringstream error_msg; + error_msg << "Unknown command '" << cmd << "'"; + mex_error(error_msg.str()); } mxFree(cmd); } diff --git a/matlab/caffe/matcaffe_demo_vgg.m b/matlab/caffe/matcaffe_demo_vgg.m new file mode 100644 index 00000000000..4e5a98eb5f4 --- /dev/null +++ b/matlab/caffe/matcaffe_demo_vgg.m @@ -0,0 +1,96 @@ +function scores = matcaffe_demo_vgg(im, use_gpu, model_def_file, model_file, mean_file) +% scores = matcaffe_demo_vgg(im, use_gpu, model_def_file, model_file, mean_file) +% +% Demo of the matlab wrapper using the networks described in the BMVC-2014 paper "Return of the Devil in the Details: Delving Deep into Convolutional Nets" +% +% INPUT +% im - color image as uint8 HxWx3 +% use_gpu - 1 to use the GPU, 0 to use the CPU +% model_def_file - network configuration (.prototxt file) +% model_file - network weights (.caffemodel file) +% mean_file - mean BGR image as uint8 HxWx3 (.mat file) +% +% OUTPUT +% scores 1000-dimensional ILSVRC score vector +% +% EXAMPLE USAGE +% model_def_file = 'zoo/VGG_CNN_F_deploy.prototxt'; +% model_file = 'zoo/VGG_CNN_F.caffemodel'; +% mean_file = 'zoo/VGG_mean.mat'; +% use_gpu = true; +% im = imread('../../examples/images/cat.jpg'); +% scores = matcaffe_demo_vgg(im, use_gpu, model_def_file, model_file, mean_file); +% +% NOTES +% the image crops are prepared as described in the paper (the aspect ratio is preserved) +% +% PREREQUISITES +% You may need to do the following before you start matlab: +% $ export LD_LIBRARY_PATH=/opt/intel/mkl/lib/intel64:/usr/local/cuda/lib64 +% $ export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 +% Or the equivalent based on where things are installed on your system + +% init caffe network (spews logging info) +matcaffe_init(use_gpu, model_def_file, model_file); + +% prepare oversampled input +% input_data is Height x Width x Channel x Num +tic; +input_data = {prepare_image(im, mean_file)}; +toc; + +% do forward pass to get scores +% scores are now Width x Height x Channels x Num +tic; +scores = caffe('forward', input_data); +toc; + +scores = scores{1}; +% size(scores) +scores = squeeze(scores); +% scores = mean(scores,2); + +% [~,maxlabel] = max(scores); + +% ------------------------------------------------------------------------ +function images = prepare_image(im, mean_file) +% ------------------------------------------------------------------------ +IMAGE_DIM = 256; +CROPPED_DIM = 224; + +d = load(mean_file); +IMAGE_MEAN = d.image_mean; + +% resize to fixed input size +im = single(im); + +if size(im, 1) < size(im, 2) + im = imresize(im, [IMAGE_DIM NaN]); +else + im = imresize(im, [NaN IMAGE_DIM]); +end + +% RGB -> BGR +im = im(:, :, [3 2 1]); + +% oversample (4 corners, center, and their x-axis flips) +images = zeros(CROPPED_DIM, CROPPED_DIM, 3, 10, 'single'); + +indices_y = [0 size(im,1)-CROPPED_DIM] + 1; +indices_x = [0 size(im,2)-CROPPED_DIM] + 1; +center_y = floor(indices_y(2) / 2)+1; +center_x = floor(indices_x(2) / 2)+1; + +curr = 1; +for i = indices_y + for j = indices_x + images(:, :, :, curr) = ... + permute(im(i:i+CROPPED_DIM-1, j:j+CROPPED_DIM-1, :)-IMAGE_MEAN, [2 1 3]); + images(:, :, :, curr+5) = images(end:-1:1, :, :, curr); + curr = curr + 1; + end +end +images(:,:,:,5) = ... + permute(im(center_y:center_y+CROPPED_DIM-1,center_x:center_x+CROPPED_DIM-1,:)-IMAGE_MEAN, ... + [2 1 3]); +images(:,:,:,10) = images(end:-1:1, :, :, curr); diff --git a/matlab/caffe/matcaffe_demo_vgg_mean_pix.m b/matlab/caffe/matcaffe_demo_vgg_mean_pix.m new file mode 100644 index 00000000000..5f7898a7029 --- /dev/null +++ b/matlab/caffe/matcaffe_demo_vgg_mean_pix.m @@ -0,0 +1,102 @@ +function scores = matcaffe_demo_vgg_mean_pix(im, use_gpu, model_def_file, model_file) +% scores = matcaffe_demo_vgg(im, use_gpu, model_def_file, model_file) +% +% Demo of the matlab wrapper based on the networks used for the "VGG" entry +% in the ILSVRC-2014 competition and described in the tech. report +% "Very Deep Convolutional Networks for Large-Scale Image Recognition" +% http://arxiv.org/abs/1409.1556/ +% +% INPUT +% im - color image as uint8 HxWx3 +% use_gpu - 1 to use the GPU, 0 to use the CPU +% model_def_file - network configuration (.prototxt file) +% model_file - network weights (.caffemodel file) +% +% OUTPUT +% scores 1000-dimensional ILSVRC score vector +% +% EXAMPLE USAGE +% model_def_file = 'zoo/deploy.prototxt'; +% model_file = 'zoo/model.caffemodel'; +% use_gpu = true; +% im = imread('../../examples/images/cat.jpg'); +% scores = matcaffe_demo_vgg(im, use_gpu, model_def_file, model_file); +% +% NOTES +% mean pixel subtraction is used instead of the mean image subtraction +% +% PREREQUISITES +% You may need to do the following before you start matlab: +% $ export LD_LIBRARY_PATH=/opt/intel/mkl/lib/intel64:/usr/local/cuda/lib64 +% $ export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 +% Or the equivalent based on where things are installed on your system + +% init caffe network (spews logging info) +matcaffe_init(use_gpu, model_def_file, model_file); + +% mean BGR pixel +mean_pix = [103.939, 116.779, 123.68]; + +% prepare oversampled input +% input_data is Height x Width x Channel x Num +tic; +input_data = {prepare_image(im, mean_pix)}; +toc; + +% do forward pass to get scores +% scores are now Width x Height x Channels x Num +tic; +scores = caffe('forward', input_data); +toc; + +scores = scores{1}; +% size(scores) +scores = squeeze(scores); +% scores = mean(scores,2); + +% [~,maxlabel] = max(scores); + +% ------------------------------------------------------------------------ +function images = prepare_image(im, mean_pix) +% ------------------------------------------------------------------------ +IMAGE_DIM = 256; +CROPPED_DIM = 224; + +% resize to fixed input size +im = single(im); + +if size(im, 1) < size(im, 2) + im = imresize(im, [IMAGE_DIM NaN]); +else + im = imresize(im, [NaN IMAGE_DIM]); +end + +% RGB -> BGR +im = im(:, :, [3 2 1]); + +% oversample (4 corners, center, and their x-axis flips) +images = zeros(CROPPED_DIM, CROPPED_DIM, 3, 10, 'single'); + +indices_y = [0 size(im,1)-CROPPED_DIM] + 1; +indices_x = [0 size(im,2)-CROPPED_DIM] + 1; +center_y = floor(indices_y(2) / 2)+1; +center_x = floor(indices_x(2) / 2)+1; + +curr = 1; +for i = indices_y + for j = indices_x + images(:, :, :, curr) = ... + permute(im(i:i+CROPPED_DIM-1, j:j+CROPPED_DIM-1, :), [2 1 3]); + images(:, :, :, curr+5) = images(end:-1:1, :, :, curr); + curr = curr + 1; + end +end +images(:,:,:,5) = ... + permute(im(center_y:center_y+CROPPED_DIM-1,center_x:center_x+CROPPED_DIM-1,:), ... + [2 1 3]); +images(:,:,:,10) = images(end:-1:1, :, :, curr); + +% mean BGR pixel subtraction +for c = 1:3 + images(:, :, c, :) = images(:, :, c, :) - mean_pix(c); +end diff --git a/matlab/caffe/matcaffe_init.m b/matlab/caffe/matcaffe_init.m index 7cc6935758e..5d0a0a70bde 100644 --- a/matlab/caffe/matcaffe_init.m +++ b/matlab/caffe/matcaffe_init.m @@ -25,7 +25,8 @@ function matcaffe_init(use_gpu, model_def_file, model_file) % NOTE: you'll have to get network definition error('You need the network prototxt definition'); end - caffe('init', model_def_file, model_file) + % load network in TEST phase + caffe('init', model_def_file, model_file, 'test') end fprintf('Done with init\n'); @@ -38,7 +39,3 @@ function matcaffe_init(use_gpu, model_def_file, model_file) caffe('set_mode_cpu'); end fprintf('Done with set_mode\n'); - -% put into test mode -caffe('set_phase_test'); -fprintf('Done with set_phase_test\n'); diff --git a/models/bvlc_alexnet/deploy.prototxt b/models/bvlc_alexnet/deploy.prototxt index d010753f3fc..ced055b85d0 100644 --- a/models/bvlc_alexnet/deploy.prototxt +++ b/models/bvlc_alexnet/deploy.prototxt @@ -4,241 +4,273 @@ input_dim: 10 input_dim: 3 input_dim: 227 input_dim: 227 -layers { +layer { name: "conv1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + type: "Convolution" + bottom: "data" + top: "conv1" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 11 stride: 4 } - bottom: "data" - top: "conv1" } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" + bottom: "conv1" + top: "norm1" lrn_param { local_size: 5 alpha: 0.0001 beta: 0.75 } - bottom: "conv1" - top: "norm1" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" + bottom: "norm1" + top: "pool1" pooling_param { pool: MAX kernel_size: 3 stride: 2 } - bottom: "norm1" - top: "pool1" } -layers { +layer { name: "conv2" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + type: "Convolution" + bottom: "pool1" + top: "conv2" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 2 kernel_size: 5 group: 2 } - bottom: "pool1" - top: "conv2" } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" + bottom: "conv2" + top: "norm2" lrn_param { local_size: 5 alpha: 0.0001 beta: 0.75 } - bottom: "conv2" - top: "norm2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" + bottom: "norm2" + top: "pool2" pooling_param { pool: MAX kernel_size: 3 stride: 2 } - bottom: "norm2" - top: "pool2" } -layers { +layer { name: "conv3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + type: "Convolution" + bottom: "pool2" + top: "conv3" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 kernel_size: 3 } - bottom: "pool2" - top: "conv3" } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + type: "Convolution" + bottom: "conv3" + top: "conv4" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 kernel_size: 3 group: 2 } - bottom: "conv3" - top: "conv4" } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + type: "Convolution" + bottom: "conv4" + top: "conv5" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 1 kernel_size: 3 group: 2 } - bottom: "conv4" - top: "conv5" } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" + bottom: "conv5" + top: "pool5" pooling_param { pool: MAX kernel_size: 3 stride: 2 } - bottom: "conv5" - top: "pool5" } -layers { +layer { name: "fc6" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + type: "InnerProduct" + bottom: "pool5" + top: "fc6" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 } - bottom: "pool5" - top: "fc6" } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6" top: "fc6" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" + bottom: "fc6" + top: "fc6" dropout_param { dropout_ratio: 0.5 } - bottom: "fc6" - top: "fc6" } -layers { +layer { name: "fc7" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + type: "InnerProduct" + bottom: "fc6" + top: "fc7" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 } - bottom: "fc6" - top: "fc7" } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7" top: "fc7" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" + bottom: "fc7" + top: "fc7" dropout_param { dropout_ratio: 0.5 } - bottom: "fc7" - top: "fc7" } -layers { +layer { name: "fc8" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + 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 } - bottom: "fc7" - top: "fc8" } -layers { +layer { name: "prob" - type: SOFTMAX + type: "Softmax" bottom: "fc8" top: "prob" } diff --git a/models/bvlc_alexnet/train_val.prototxt b/models/bvlc_alexnet/train_val.prototxt index 717b6fa447c..588b4ea7cb5 100644 --- a/models/bvlc_alexnet/train_val.prototxt +++ b/models/bvlc_alexnet/train_val.prototxt @@ -1,47 +1,55 @@ name: "AlexNet" -layers { +layer { name: "data" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/imagenet/ilsvrc12_train_lmdb" - backend: LMDB - batch_size: 256 + include { + phase: TRAIN } transform_param { + mirror: true crop_size: 227 mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - mirror: true } - include: { phase: TRAIN } + data_param { + source: "examples/imagenet/ilsvrc12_train_lmdb" + batch_size: 256 + backend: LMDB + } } -layers { +layer { name: "data" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/imagenet/ilsvrc12_val_lmdb" - backend: LMDB - batch_size: 50 + include { + phase: TEST } transform_param { + mirror: false crop_size: 227 mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - mirror: false } - include: { phase: TEST } + data_param { + source: "examples/imagenet/ilsvrc12_val_lmdb" + batch_size: 50 + backend: LMDB + } } -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 11 @@ -56,15 +64,15 @@ layers { } } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "conv1" top: "norm1" lrn_param { @@ -73,9 +81,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "norm1" top: "pool1" pooling_param { @@ -84,15 +92,19 @@ layers { stride: 2 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "pool1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 2 @@ -108,15 +120,15 @@ layers { } } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "conv2" top: "norm2" lrn_param { @@ -125,9 +137,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "norm2" top: "pool2" pooling_param { @@ -136,15 +148,19 @@ layers { stride: 2 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "pool2" top: "conv3" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -159,21 +175,25 @@ layers { } } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION + type: "Convolution" bottom: "conv3" top: "conv4" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -189,21 +209,25 @@ layers { } } } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION + type: "Convolution" bottom: "conv4" top: "conv5" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 1 @@ -219,15 +243,15 @@ layers { } } } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" bottom: "conv5" top: "pool5" pooling_param { @@ -236,15 +260,19 @@ layers { stride: 2 } } -layers { +layer { name: "fc6" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool5" top: "fc6" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 weight_filler { @@ -257,30 +285,34 @@ layers { } } } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6" top: "fc6" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" bottom: "fc6" top: "fc6" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc7" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc6" top: "fc7" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 weight_filler { @@ -293,30 +325,34 @@ layers { } } } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7" top: "fc7" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" bottom: "fc7" top: "fc7" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc8" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc7" top: "fc8" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 1000 weight_filler { @@ -329,17 +365,19 @@ layers { } } } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "fc8" bottom: "label" top: "accuracy" - include: { phase: TEST } + include { + phase: TEST + } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "fc8" bottom: "label" top: "loss" diff --git a/models/bvlc_googlenet/deploy.prototxt b/models/bvlc_googlenet/deploy.prototxt index e31a4c9cd00..4648bf26efc 100644 --- a/models/bvlc_googlenet/deploy.prototxt +++ b/models/bvlc_googlenet/deploy.prototxt @@ -4,15 +4,19 @@ input_dim: 10 input_dim: 3 input_dim: 224 input_dim: 224 -layers { +layer { + name: "conv1/7x7_s2" + type: "Convolution" bottom: "data" top: "conv1/7x7_s2" - name: "conv1/7x7_s2" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 pad: 3 @@ -28,43 +32,47 @@ layers { } } } -layers { +layer { + name: "conv1/relu_7x7" + type: "ReLU" bottom: "conv1/7x7_s2" top: "conv1/7x7_s2" - name: "conv1/relu_7x7" - type: RELU } -layers { +layer { + name: "pool1/3x3_s2" + type: "Pooling" bottom: "conv1/7x7_s2" top: "pool1/3x3_s2" - name: "pool1/3x3_s2" - type: POOLING pooling_param { pool: MAX kernel_size: 3 stride: 2 } } -layers { +layer { + name: "pool1/norm1" + type: "LRN" bottom: "pool1/3x3_s2" top: "pool1/norm1" - name: "pool1/norm1" - type: LRN lrn_param { local_size: 5 alpha: 0.0001 beta: 0.75 } } -layers { +layer { + name: "conv2/3x3_reduce" + type: "Convolution" bottom: "pool1/norm1" top: "conv2/3x3_reduce" - name: "conv2/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -78,21 +86,25 @@ layers { } } } -layers { +layer { + name: "conv2/relu_3x3_reduce" + type: "ReLU" bottom: "conv2/3x3_reduce" top: "conv2/3x3_reduce" - name: "conv2/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "conv2/3x3" + type: "Convolution" bottom: "conv2/3x3_reduce" top: "conv2/3x3" - name: "conv2/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 192 pad: 1 @@ -107,43 +119,47 @@ layers { } } } -layers { +layer { + name: "conv2/relu_3x3" + type: "ReLU" bottom: "conv2/3x3" top: "conv2/3x3" - name: "conv2/relu_3x3" - type: RELU } -layers { +layer { + name: "conv2/norm2" + type: "LRN" bottom: "conv2/3x3" top: "conv2/norm2" - name: "conv2/norm2" - type: LRN lrn_param { local_size: 5 alpha: 0.0001 beta: 0.75 } } -layers { +layer { + name: "pool2/3x3_s2" + type: "Pooling" bottom: "conv2/norm2" top: "pool2/3x3_s2" - name: "pool2/3x3_s2" - type: POOLING pooling_param { pool: MAX kernel_size: 3 stride: 2 } } -layers { +layer { + name: "inception_3a/1x1" + type: "Convolution" bottom: "pool2/3x3_s2" top: "inception_3a/1x1" - name: "inception_3a/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -157,21 +173,25 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_1x1" + type: "ReLU" bottom: "inception_3a/1x1" top: "inception_3a/1x1" - name: "inception_3a/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_3a/3x3_reduce" + type: "Convolution" bottom: "pool2/3x3_s2" top: "inception_3a/3x3_reduce" - name: "inception_3a/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 1 @@ -185,21 +205,25 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_3x3_reduce" + type: "ReLU" bottom: "inception_3a/3x3_reduce" top: "inception_3a/3x3_reduce" - name: "inception_3a/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_3a/3x3" + type: "Convolution" bottom: "inception_3a/3x3_reduce" top: "inception_3a/3x3" - name: "inception_3a/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 pad: 1 @@ -214,21 +238,25 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_3x3" + type: "ReLU" bottom: "inception_3a/3x3" top: "inception_3a/3x3" - name: "inception_3a/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_3a/5x5_reduce" + type: "Convolution" bottom: "pool2/3x3_s2" top: "inception_3a/5x5_reduce" - name: "inception_3a/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 16 kernel_size: 1 @@ -242,21 +270,25 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_5x5_reduce" + type: "ReLU" bottom: "inception_3a/5x5_reduce" top: "inception_3a/5x5_reduce" - name: "inception_3a/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_3a/5x5" + type: "Convolution" bottom: "inception_3a/5x5_reduce" top: "inception_3a/5x5" - name: "inception_3a/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 pad: 2 @@ -271,17 +303,17 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_5x5" + type: "ReLU" bottom: "inception_3a/5x5" top: "inception_3a/5x5" - name: "inception_3a/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_3a/pool" + type: "Pooling" bottom: "pool2/3x3_s2" top: "inception_3a/pool" - name: "inception_3a/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -289,15 +321,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_3a/pool_proj" + type: "Convolution" bottom: "inception_3a/pool" top: "inception_3a/pool_proj" - name: "inception_3a/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -311,30 +347,34 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_pool_proj" + type: "ReLU" bottom: "inception_3a/pool_proj" top: "inception_3a/pool_proj" - name: "inception_3a/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_3a/output" + type: "Concat" bottom: "inception_3a/1x1" bottom: "inception_3a/3x3" bottom: "inception_3a/5x5" bottom: "inception_3a/pool_proj" top: "inception_3a/output" - name: "inception_3a/output" - type: CONCAT } -layers { +layer { + name: "inception_3b/1x1" + type: "Convolution" bottom: "inception_3a/output" top: "inception_3b/1x1" - name: "inception_3b/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -348,21 +388,25 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_1x1" + type: "ReLU" bottom: "inception_3b/1x1" top: "inception_3b/1x1" - name: "inception_3b/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_3b/3x3_reduce" + type: "Convolution" bottom: "inception_3a/output" top: "inception_3b/3x3_reduce" - name: "inception_3b/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -376,21 +420,25 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_3x3_reduce" + type: "ReLU" bottom: "inception_3b/3x3_reduce" top: "inception_3b/3x3_reduce" - name: "inception_3b/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_3b/3x3" + type: "Convolution" bottom: "inception_3b/3x3_reduce" top: "inception_3b/3x3" - name: "inception_3b/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 192 pad: 1 @@ -405,21 +453,25 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_3x3" + type: "ReLU" bottom: "inception_3b/3x3" top: "inception_3b/3x3" - name: "inception_3b/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_3b/5x5_reduce" + type: "Convolution" bottom: "inception_3a/output" top: "inception_3b/5x5_reduce" - name: "inception_3b/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -433,21 +485,25 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_5x5_reduce" + type: "ReLU" bottom: "inception_3b/5x5_reduce" top: "inception_3b/5x5_reduce" - name: "inception_3b/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_3b/5x5" + type: "Convolution" bottom: "inception_3b/5x5_reduce" top: "inception_3b/5x5" - name: "inception_3b/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 pad: 2 @@ -462,17 +518,17 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_5x5" + type: "ReLU" bottom: "inception_3b/5x5" top: "inception_3b/5x5" - name: "inception_3b/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_3b/pool" + type: "Pooling" bottom: "inception_3a/output" top: "inception_3b/pool" - name: "inception_3b/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -480,15 +536,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_3b/pool_proj" + type: "Convolution" bottom: "inception_3b/pool" top: "inception_3b/pool_proj" - name: "inception_3b/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -502,41 +562,45 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_pool_proj" + type: "ReLU" bottom: "inception_3b/pool_proj" top: "inception_3b/pool_proj" - name: "inception_3b/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_3b/output" + type: "Concat" bottom: "inception_3b/1x1" bottom: "inception_3b/3x3" bottom: "inception_3b/5x5" bottom: "inception_3b/pool_proj" top: "inception_3b/output" - name: "inception_3b/output" - type: CONCAT } -layers { +layer { + name: "pool3/3x3_s2" + type: "Pooling" bottom: "inception_3b/output" top: "pool3/3x3_s2" - name: "pool3/3x3_s2" - type: POOLING pooling_param { pool: MAX kernel_size: 3 stride: 2 } } -layers { +layer { + name: "inception_4a/1x1" + type: "Convolution" bottom: "pool3/3x3_s2" top: "inception_4a/1x1" - name: "inception_4a/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 192 kernel_size: 1 @@ -550,21 +614,25 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_1x1" + type: "ReLU" bottom: "inception_4a/1x1" top: "inception_4a/1x1" - name: "inception_4a/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4a/3x3_reduce" + type: "Convolution" bottom: "pool3/3x3_s2" top: "inception_4a/3x3_reduce" - name: "inception_4a/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 1 @@ -578,21 +646,25 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4a/3x3_reduce" top: "inception_4a/3x3_reduce" - name: "inception_4a/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4a/3x3" + type: "Convolution" bottom: "inception_4a/3x3_reduce" top: "inception_4a/3x3" - name: "inception_4a/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 208 pad: 1 @@ -607,21 +679,25 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_3x3" + type: "ReLU" bottom: "inception_4a/3x3" top: "inception_4a/3x3" - name: "inception_4a/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4a/5x5_reduce" + type: "Convolution" bottom: "pool3/3x3_s2" top: "inception_4a/5x5_reduce" - name: "inception_4a/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 16 kernel_size: 1 @@ -635,21 +711,25 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4a/5x5_reduce" top: "inception_4a/5x5_reduce" - name: "inception_4a/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4a/5x5" + type: "Convolution" bottom: "inception_4a/5x5_reduce" top: "inception_4a/5x5" - name: "inception_4a/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 48 pad: 2 @@ -664,17 +744,17 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_5x5" + type: "ReLU" bottom: "inception_4a/5x5" top: "inception_4a/5x5" - name: "inception_4a/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4a/pool" + type: "Pooling" bottom: "pool3/3x3_s2" top: "inception_4a/pool" - name: "inception_4a/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -682,15 +762,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4a/pool_proj" + type: "Convolution" bottom: "inception_4a/pool" top: "inception_4a/pool_proj" - name: "inception_4a/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -704,30 +788,34 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_pool_proj" + type: "ReLU" bottom: "inception_4a/pool_proj" top: "inception_4a/pool_proj" - name: "inception_4a/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4a/output" + type: "Concat" bottom: "inception_4a/1x1" bottom: "inception_4a/3x3" bottom: "inception_4a/5x5" bottom: "inception_4a/pool_proj" top: "inception_4a/output" - name: "inception_4a/output" - type: CONCAT } -layers { +layer { + name: "inception_4b/1x1" + type: "Convolution" bottom: "inception_4a/output" top: "inception_4b/1x1" - name: "inception_4b/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 160 kernel_size: 1 @@ -741,21 +829,25 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_1x1" + type: "ReLU" bottom: "inception_4b/1x1" top: "inception_4b/1x1" - name: "inception_4b/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4b/3x3_reduce" + type: "Convolution" bottom: "inception_4a/output" top: "inception_4b/3x3_reduce" - name: "inception_4b/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 112 kernel_size: 1 @@ -769,21 +861,25 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4b/3x3_reduce" top: "inception_4b/3x3_reduce" - name: "inception_4b/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4b/3x3" + type: "Convolution" bottom: "inception_4b/3x3_reduce" top: "inception_4b/3x3" - name: "inception_4b/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 224 pad: 1 @@ -798,21 +894,25 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_3x3" + type: "ReLU" bottom: "inception_4b/3x3" top: "inception_4b/3x3" - name: "inception_4b/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4b/5x5_reduce" + type: "Convolution" bottom: "inception_4a/output" top: "inception_4b/5x5_reduce" - name: "inception_4b/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 24 kernel_size: 1 @@ -826,21 +926,25 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4b/5x5_reduce" top: "inception_4b/5x5_reduce" - name: "inception_4b/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4b/5x5" + type: "Convolution" bottom: "inception_4b/5x5_reduce" top: "inception_4b/5x5" - name: "inception_4b/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 pad: 2 @@ -855,17 +959,17 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_5x5" + type: "ReLU" bottom: "inception_4b/5x5" top: "inception_4b/5x5" - name: "inception_4b/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4b/pool" + type: "Pooling" bottom: "inception_4a/output" top: "inception_4b/pool" - name: "inception_4b/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -873,15 +977,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4b/pool_proj" + type: "Convolution" bottom: "inception_4b/pool" top: "inception_4b/pool_proj" - name: "inception_4b/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -895,30 +1003,34 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_pool_proj" + type: "ReLU" bottom: "inception_4b/pool_proj" top: "inception_4b/pool_proj" - name: "inception_4b/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4b/output" + type: "Concat" bottom: "inception_4b/1x1" bottom: "inception_4b/3x3" bottom: "inception_4b/5x5" bottom: "inception_4b/pool_proj" top: "inception_4b/output" - name: "inception_4b/output" - type: CONCAT } -layers { +layer { + name: "inception_4c/1x1" + type: "Convolution" bottom: "inception_4b/output" top: "inception_4c/1x1" - name: "inception_4c/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -932,21 +1044,25 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_1x1" + type: "ReLU" bottom: "inception_4c/1x1" top: "inception_4c/1x1" - name: "inception_4c/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4c/3x3_reduce" + type: "Convolution" bottom: "inception_4b/output" top: "inception_4c/3x3_reduce" - name: "inception_4c/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -960,21 +1076,25 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4c/3x3_reduce" top: "inception_4c/3x3_reduce" - name: "inception_4c/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4c/3x3" + type: "Convolution" bottom: "inception_4c/3x3_reduce" top: "inception_4c/3x3" - name: "inception_4c/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 1 @@ -989,21 +1109,25 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_3x3" + type: "ReLU" bottom: "inception_4c/3x3" top: "inception_4c/3x3" - name: "inception_4c/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4c/5x5_reduce" + type: "Convolution" bottom: "inception_4b/output" top: "inception_4c/5x5_reduce" - name: "inception_4c/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 24 kernel_size: 1 @@ -1017,21 +1141,25 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4c/5x5_reduce" top: "inception_4c/5x5_reduce" - name: "inception_4c/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4c/5x5" + type: "Convolution" bottom: "inception_4c/5x5_reduce" top: "inception_4c/5x5" - name: "inception_4c/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 pad: 2 @@ -1046,17 +1174,17 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_5x5" + type: "ReLU" bottom: "inception_4c/5x5" top: "inception_4c/5x5" - name: "inception_4c/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4c/pool" + type: "Pooling" bottom: "inception_4b/output" top: "inception_4c/pool" - name: "inception_4c/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1064,15 +1192,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4c/pool_proj" + type: "Convolution" bottom: "inception_4c/pool" top: "inception_4c/pool_proj" - name: "inception_4c/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -1086,30 +1218,34 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_pool_proj" + type: "ReLU" bottom: "inception_4c/pool_proj" top: "inception_4c/pool_proj" - name: "inception_4c/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4c/output" + type: "Concat" bottom: "inception_4c/1x1" bottom: "inception_4c/3x3" bottom: "inception_4c/5x5" bottom: "inception_4c/pool_proj" top: "inception_4c/output" - name: "inception_4c/output" - type: CONCAT } -layers { +layer { + name: "inception_4d/1x1" + type: "Convolution" bottom: "inception_4c/output" top: "inception_4d/1x1" - name: "inception_4d/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 112 kernel_size: 1 @@ -1123,21 +1259,25 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_1x1" + type: "ReLU" bottom: "inception_4d/1x1" top: "inception_4d/1x1" - name: "inception_4d/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4d/3x3_reduce" + type: "Convolution" bottom: "inception_4c/output" top: "inception_4d/3x3_reduce" - name: "inception_4d/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 144 kernel_size: 1 @@ -1151,21 +1291,25 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4d/3x3_reduce" top: "inception_4d/3x3_reduce" - name: "inception_4d/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4d/3x3" + type: "Convolution" bottom: "inception_4d/3x3_reduce" top: "inception_4d/3x3" - name: "inception_4d/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 288 pad: 1 @@ -1180,21 +1324,25 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_3x3" + type: "ReLU" bottom: "inception_4d/3x3" top: "inception_4d/3x3" - name: "inception_4d/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4d/5x5_reduce" + type: "Convolution" bottom: "inception_4c/output" top: "inception_4d/5x5_reduce" - name: "inception_4d/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -1208,21 +1356,25 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4d/5x5_reduce" top: "inception_4d/5x5_reduce" - name: "inception_4d/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4d/5x5" + type: "Convolution" bottom: "inception_4d/5x5_reduce" top: "inception_4d/5x5" - name: "inception_4d/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 pad: 2 @@ -1237,17 +1389,17 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_5x5" + type: "ReLU" bottom: "inception_4d/5x5" top: "inception_4d/5x5" - name: "inception_4d/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4d/pool" + type: "Pooling" bottom: "inception_4c/output" top: "inception_4d/pool" - name: "inception_4d/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1255,15 +1407,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4d/pool_proj" + type: "Convolution" bottom: "inception_4d/pool" top: "inception_4d/pool_proj" - name: "inception_4d/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -1277,30 +1433,34 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_pool_proj" + type: "ReLU" bottom: "inception_4d/pool_proj" top: "inception_4d/pool_proj" - name: "inception_4d/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4d/output" + type: "Concat" bottom: "inception_4d/1x1" bottom: "inception_4d/3x3" bottom: "inception_4d/5x5" bottom: "inception_4d/pool_proj" top: "inception_4d/output" - name: "inception_4d/output" - type: CONCAT } -layers { +layer { + name: "inception_4e/1x1" + type: "Convolution" bottom: "inception_4d/output" top: "inception_4e/1x1" - name: "inception_4e/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 kernel_size: 1 @@ -1314,21 +1474,25 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_1x1" + type: "ReLU" bottom: "inception_4e/1x1" top: "inception_4e/1x1" - name: "inception_4e/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4e/3x3_reduce" + type: "Convolution" bottom: "inception_4d/output" top: "inception_4e/3x3_reduce" - name: "inception_4e/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 160 kernel_size: 1 @@ -1342,21 +1506,25 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4e/3x3_reduce" top: "inception_4e/3x3_reduce" - name: "inception_4e/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4e/3x3" + type: "Convolution" bottom: "inception_4e/3x3_reduce" top: "inception_4e/3x3" - name: "inception_4e/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 320 pad: 1 @@ -1371,21 +1539,25 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_3x3" + type: "ReLU" bottom: "inception_4e/3x3" top: "inception_4e/3x3" - name: "inception_4e/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4e/5x5_reduce" + type: "Convolution" bottom: "inception_4d/output" top: "inception_4e/5x5_reduce" - name: "inception_4e/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -1399,21 +1571,25 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4e/5x5_reduce" top: "inception_4e/5x5_reduce" - name: "inception_4e/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4e/5x5" + type: "Convolution" bottom: "inception_4e/5x5_reduce" top: "inception_4e/5x5" - name: "inception_4e/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 pad: 2 @@ -1428,17 +1604,17 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_5x5" + type: "ReLU" bottom: "inception_4e/5x5" top: "inception_4e/5x5" - name: "inception_4e/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4e/pool" + type: "Pooling" bottom: "inception_4d/output" top: "inception_4e/pool" - name: "inception_4e/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1446,15 +1622,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4e/pool_proj" + type: "Convolution" bottom: "inception_4e/pool" top: "inception_4e/pool_proj" - name: "inception_4e/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -1468,41 +1648,45 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_pool_proj" + type: "ReLU" bottom: "inception_4e/pool_proj" top: "inception_4e/pool_proj" - name: "inception_4e/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4e/output" + type: "Concat" bottom: "inception_4e/1x1" bottom: "inception_4e/3x3" bottom: "inception_4e/5x5" bottom: "inception_4e/pool_proj" top: "inception_4e/output" - name: "inception_4e/output" - type: CONCAT } -layers { +layer { + name: "pool4/3x3_s2" + type: "Pooling" bottom: "inception_4e/output" top: "pool4/3x3_s2" - name: "pool4/3x3_s2" - type: POOLING pooling_param { pool: MAX kernel_size: 3 stride: 2 } } -layers { +layer { + name: "inception_5a/1x1" + type: "Convolution" bottom: "pool4/3x3_s2" top: "inception_5a/1x1" - name: "inception_5a/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 kernel_size: 1 @@ -1516,21 +1700,25 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_1x1" + type: "ReLU" bottom: "inception_5a/1x1" top: "inception_5a/1x1" - name: "inception_5a/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_5a/3x3_reduce" + type: "Convolution" bottom: "pool4/3x3_s2" top: "inception_5a/3x3_reduce" - name: "inception_5a/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 160 kernel_size: 1 @@ -1544,21 +1732,25 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_3x3_reduce" + type: "ReLU" bottom: "inception_5a/3x3_reduce" top: "inception_5a/3x3_reduce" - name: "inception_5a/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_5a/3x3" + type: "Convolution" bottom: "inception_5a/3x3_reduce" top: "inception_5a/3x3" - name: "inception_5a/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 320 pad: 1 @@ -1573,21 +1765,25 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_3x3" + type: "ReLU" bottom: "inception_5a/3x3" top: "inception_5a/3x3" - name: "inception_5a/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_5a/5x5_reduce" + type: "Convolution" bottom: "pool4/3x3_s2" top: "inception_5a/5x5_reduce" - name: "inception_5a/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -1601,21 +1797,25 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_5x5_reduce" + type: "ReLU" bottom: "inception_5a/5x5_reduce" top: "inception_5a/5x5_reduce" - name: "inception_5a/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_5a/5x5" + type: "Convolution" bottom: "inception_5a/5x5_reduce" top: "inception_5a/5x5" - name: "inception_5a/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 pad: 2 @@ -1630,17 +1830,17 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_5x5" + type: "ReLU" bottom: "inception_5a/5x5" top: "inception_5a/5x5" - name: "inception_5a/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_5a/pool" + type: "Pooling" bottom: "pool4/3x3_s2" top: "inception_5a/pool" - name: "inception_5a/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1648,15 +1848,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_5a/pool_proj" + type: "Convolution" bottom: "inception_5a/pool" top: "inception_5a/pool_proj" - name: "inception_5a/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -1670,30 +1874,34 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_pool_proj" + type: "ReLU" bottom: "inception_5a/pool_proj" top: "inception_5a/pool_proj" - name: "inception_5a/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_5a/output" + type: "Concat" bottom: "inception_5a/1x1" bottom: "inception_5a/3x3" bottom: "inception_5a/5x5" bottom: "inception_5a/pool_proj" top: "inception_5a/output" - name: "inception_5a/output" - type: CONCAT } -layers { +layer { + name: "inception_5b/1x1" + type: "Convolution" bottom: "inception_5a/output" top: "inception_5b/1x1" - name: "inception_5b/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 kernel_size: 1 @@ -1707,21 +1915,25 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_1x1" + type: "ReLU" bottom: "inception_5b/1x1" top: "inception_5b/1x1" - name: "inception_5b/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_5b/3x3_reduce" + type: "Convolution" bottom: "inception_5a/output" top: "inception_5b/3x3_reduce" - name: "inception_5b/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 192 kernel_size: 1 @@ -1735,21 +1947,25 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_3x3_reduce" + type: "ReLU" bottom: "inception_5b/3x3_reduce" top: "inception_5b/3x3_reduce" - name: "inception_5b/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_5b/3x3" + type: "Convolution" bottom: "inception_5b/3x3_reduce" top: "inception_5b/3x3" - name: "inception_5b/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -1764,21 +1980,25 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_3x3" + type: "ReLU" bottom: "inception_5b/3x3" top: "inception_5b/3x3" - name: "inception_5b/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_5b/5x5_reduce" + type: "Convolution" bottom: "inception_5a/output" top: "inception_5b/5x5_reduce" - name: "inception_5b/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 48 kernel_size: 1 @@ -1792,21 +2012,25 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_5x5_reduce" + type: "ReLU" bottom: "inception_5b/5x5_reduce" top: "inception_5b/5x5_reduce" - name: "inception_5b/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_5b/5x5" + type: "Convolution" bottom: "inception_5b/5x5_reduce" top: "inception_5b/5x5" - name: "inception_5b/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 pad: 2 @@ -1821,17 +2045,17 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_5x5" + type: "ReLU" bottom: "inception_5b/5x5" top: "inception_5b/5x5" - name: "inception_5b/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_5b/pool" + type: "Pooling" bottom: "inception_5a/output" top: "inception_5b/pool" - name: "inception_5b/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1839,15 +2063,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_5b/pool_proj" + type: "Convolution" bottom: "inception_5b/pool" top: "inception_5b/pool_proj" - name: "inception_5b/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -1861,50 +2089,54 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_pool_proj" + type: "ReLU" bottom: "inception_5b/pool_proj" top: "inception_5b/pool_proj" - name: "inception_5b/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_5b/output" + type: "Concat" bottom: "inception_5b/1x1" bottom: "inception_5b/3x3" bottom: "inception_5b/5x5" bottom: "inception_5b/pool_proj" top: "inception_5b/output" - name: "inception_5b/output" - type: CONCAT } -layers { +layer { + name: "pool5/7x7_s1" + type: "Pooling" bottom: "inception_5b/output" top: "pool5/7x7_s1" - name: "pool5/7x7_s1" - type: POOLING pooling_param { pool: AVE kernel_size: 7 stride: 1 } } -layers { +layer { + name: "pool5/drop_7x7_s1" + type: "Dropout" bottom: "pool5/7x7_s1" top: "pool5/7x7_s1" - name: "pool5/drop_7x7_s1" - type: DROPOUT dropout_param { dropout_ratio: 0.4 } } -layers { +layer { + name: "loss3/classifier" + type: "InnerProduct" bottom: "pool5/7x7_s1" top: "loss3/classifier" - name: "loss3/classifier" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 1000 weight_filler { @@ -1916,9 +2148,9 @@ layers { } } } -layers { +layer { name: "prob" - type: SOFTMAX + type: "Softmax" bottom: "loss3/classifier" top: "prob" -} \ No newline at end of file +} diff --git a/models/bvlc_googlenet/train_val.prototxt b/models/bvlc_googlenet/train_val.prototxt index 156d05fd254..79ede2b9d9c 100644 --- a/models/bvlc_googlenet/train_val.prototxt +++ b/models/bvlc_googlenet/train_val.prototxt @@ -1,57 +1,59 @@ name: "GoogleNet" -layers { +layer { + name: "data" + type: "Data" top: "data" top: "label" - name: "data" - type: DATA - data_param { - source: "examples/imagenet/ilsvrc12_train_lmdb" - batch_size: 32 - backend: LMDB - } include { phase: TRAIN } transform_param { mirror: true crop_size: 224 - mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - # mean_value: 104 - # mean_value: 117 - # mean_value: 123 + mean_value: 104 + mean_value: 117 + mean_value: 123 } -} -layers { - top: "data" - top: "label" - name: "data" - type: DATA data_param { - source: "examples/imagenet/ilsvrc12_val_lmdb" - batch_size: 50 + source: "examples/imagenet/ilsvrc12_train_lmdb" + batch_size: 32 backend: LMDB } +} +layer { + name: "data" + type: "Data" + top: "data" + top: "label" include { phase: TEST } transform_param { mirror: false crop_size: 224 - mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - # mean_value: 104 - # mean_value: 117 - # mean_value: 123 + mean_value: 104 + mean_value: 117 + mean_value: 123 + } + data_param { + source: "examples/imagenet/ilsvrc12_val_lmdb" + batch_size: 50 + backend: LMDB } } -layers { +layer { + name: "conv1/7x7_s2" + type: "Convolution" bottom: "data" top: "conv1/7x7_s2" - name: "conv1/7x7_s2" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 pad: 3 @@ -67,43 +69,47 @@ layers { } } } -layers { +layer { + name: "conv1/relu_7x7" + type: "ReLU" bottom: "conv1/7x7_s2" top: "conv1/7x7_s2" - name: "conv1/relu_7x7" - type: RELU } -layers { +layer { + name: "pool1/3x3_s2" + type: "Pooling" bottom: "conv1/7x7_s2" top: "pool1/3x3_s2" - name: "pool1/3x3_s2" - type: POOLING pooling_param { pool: MAX kernel_size: 3 stride: 2 } } -layers { +layer { + name: "pool1/norm1" + type: "LRN" bottom: "pool1/3x3_s2" top: "pool1/norm1" - name: "pool1/norm1" - type: LRN lrn_param { local_size: 5 alpha: 0.0001 beta: 0.75 } } -layers { +layer { + name: "conv2/3x3_reduce" + type: "Convolution" bottom: "pool1/norm1" top: "conv2/3x3_reduce" - name: "conv2/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -117,21 +123,25 @@ layers { } } } -layers { +layer { + name: "conv2/relu_3x3_reduce" + type: "ReLU" bottom: "conv2/3x3_reduce" top: "conv2/3x3_reduce" - name: "conv2/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "conv2/3x3" + type: "Convolution" bottom: "conv2/3x3_reduce" top: "conv2/3x3" - name: "conv2/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 192 pad: 1 @@ -146,43 +156,47 @@ layers { } } } -layers { +layer { + name: "conv2/relu_3x3" + type: "ReLU" bottom: "conv2/3x3" top: "conv2/3x3" - name: "conv2/relu_3x3" - type: RELU } -layers { +layer { + name: "conv2/norm2" + type: "LRN" bottom: "conv2/3x3" top: "conv2/norm2" - name: "conv2/norm2" - type: LRN lrn_param { local_size: 5 alpha: 0.0001 beta: 0.75 } } -layers { +layer { + name: "pool2/3x3_s2" + type: "Pooling" bottom: "conv2/norm2" top: "pool2/3x3_s2" - name: "pool2/3x3_s2" - type: POOLING pooling_param { pool: MAX kernel_size: 3 stride: 2 } } -layers { +layer { + name: "inception_3a/1x1" + type: "Convolution" bottom: "pool2/3x3_s2" top: "inception_3a/1x1" - name: "inception_3a/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -196,21 +210,25 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_1x1" + type: "ReLU" bottom: "inception_3a/1x1" top: "inception_3a/1x1" - name: "inception_3a/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_3a/3x3_reduce" + type: "Convolution" bottom: "pool2/3x3_s2" top: "inception_3a/3x3_reduce" - name: "inception_3a/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 1 @@ -224,21 +242,25 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_3x3_reduce" + type: "ReLU" bottom: "inception_3a/3x3_reduce" top: "inception_3a/3x3_reduce" - name: "inception_3a/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_3a/3x3" + type: "Convolution" bottom: "inception_3a/3x3_reduce" top: "inception_3a/3x3" - name: "inception_3a/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 pad: 1 @@ -253,21 +275,25 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_3x3" + type: "ReLU" bottom: "inception_3a/3x3" top: "inception_3a/3x3" - name: "inception_3a/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_3a/5x5_reduce" + type: "Convolution" bottom: "pool2/3x3_s2" top: "inception_3a/5x5_reduce" - name: "inception_3a/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 16 kernel_size: 1 @@ -281,21 +307,25 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_5x5_reduce" + type: "ReLU" bottom: "inception_3a/5x5_reduce" top: "inception_3a/5x5_reduce" - name: "inception_3a/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_3a/5x5" + type: "Convolution" bottom: "inception_3a/5x5_reduce" top: "inception_3a/5x5" - name: "inception_3a/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 pad: 2 @@ -310,17 +340,17 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_5x5" + type: "ReLU" bottom: "inception_3a/5x5" top: "inception_3a/5x5" - name: "inception_3a/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_3a/pool" + type: "Pooling" bottom: "pool2/3x3_s2" top: "inception_3a/pool" - name: "inception_3a/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -328,15 +358,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_3a/pool_proj" + type: "Convolution" bottom: "inception_3a/pool" top: "inception_3a/pool_proj" - name: "inception_3a/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -350,30 +384,34 @@ layers { } } } -layers { +layer { + name: "inception_3a/relu_pool_proj" + type: "ReLU" bottom: "inception_3a/pool_proj" top: "inception_3a/pool_proj" - name: "inception_3a/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_3a/output" + type: "Concat" bottom: "inception_3a/1x1" bottom: "inception_3a/3x3" bottom: "inception_3a/5x5" bottom: "inception_3a/pool_proj" top: "inception_3a/output" - name: "inception_3a/output" - type: CONCAT } -layers { +layer { + name: "inception_3b/1x1" + type: "Convolution" bottom: "inception_3a/output" top: "inception_3b/1x1" - name: "inception_3b/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -387,21 +425,25 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_1x1" + type: "ReLU" bottom: "inception_3b/1x1" top: "inception_3b/1x1" - name: "inception_3b/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_3b/3x3_reduce" + type: "Convolution" bottom: "inception_3a/output" top: "inception_3b/3x3_reduce" - name: "inception_3b/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -415,21 +457,25 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_3x3_reduce" + type: "ReLU" bottom: "inception_3b/3x3_reduce" top: "inception_3b/3x3_reduce" - name: "inception_3b/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_3b/3x3" + type: "Convolution" bottom: "inception_3b/3x3_reduce" top: "inception_3b/3x3" - name: "inception_3b/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 192 pad: 1 @@ -444,21 +490,25 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_3x3" + type: "ReLU" bottom: "inception_3b/3x3" top: "inception_3b/3x3" - name: "inception_3b/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_3b/5x5_reduce" + type: "Convolution" bottom: "inception_3a/output" top: "inception_3b/5x5_reduce" - name: "inception_3b/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -472,21 +522,25 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_5x5_reduce" + type: "ReLU" bottom: "inception_3b/5x5_reduce" top: "inception_3b/5x5_reduce" - name: "inception_3b/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_3b/5x5" + type: "Convolution" bottom: "inception_3b/5x5_reduce" top: "inception_3b/5x5" - name: "inception_3b/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 pad: 2 @@ -501,17 +555,17 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_5x5" + type: "ReLU" bottom: "inception_3b/5x5" top: "inception_3b/5x5" - name: "inception_3b/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_3b/pool" + type: "Pooling" bottom: "inception_3a/output" top: "inception_3b/pool" - name: "inception_3b/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -519,15 +573,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_3b/pool_proj" + type: "Convolution" bottom: "inception_3b/pool" top: "inception_3b/pool_proj" - name: "inception_3b/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -541,41 +599,45 @@ layers { } } } -layers { +layer { + name: "inception_3b/relu_pool_proj" + type: "ReLU" bottom: "inception_3b/pool_proj" top: "inception_3b/pool_proj" - name: "inception_3b/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_3b/output" + type: "Concat" bottom: "inception_3b/1x1" bottom: "inception_3b/3x3" bottom: "inception_3b/5x5" bottom: "inception_3b/pool_proj" top: "inception_3b/output" - name: "inception_3b/output" - type: CONCAT } -layers { +layer { + name: "pool3/3x3_s2" + type: "Pooling" bottom: "inception_3b/output" top: "pool3/3x3_s2" - name: "pool3/3x3_s2" - type: POOLING pooling_param { pool: MAX kernel_size: 3 stride: 2 } } -layers { +layer { + name: "inception_4a/1x1" + type: "Convolution" bottom: "pool3/3x3_s2" top: "inception_4a/1x1" - name: "inception_4a/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 192 kernel_size: 1 @@ -589,21 +651,25 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_1x1" + type: "ReLU" bottom: "inception_4a/1x1" top: "inception_4a/1x1" - name: "inception_4a/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4a/3x3_reduce" + type: "Convolution" bottom: "pool3/3x3_s2" top: "inception_4a/3x3_reduce" - name: "inception_4a/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 1 @@ -617,21 +683,25 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4a/3x3_reduce" top: "inception_4a/3x3_reduce" - name: "inception_4a/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4a/3x3" + type: "Convolution" bottom: "inception_4a/3x3_reduce" top: "inception_4a/3x3" - name: "inception_4a/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 208 pad: 1 @@ -646,21 +716,25 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_3x3" + type: "ReLU" bottom: "inception_4a/3x3" top: "inception_4a/3x3" - name: "inception_4a/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4a/5x5_reduce" + type: "Convolution" bottom: "pool3/3x3_s2" top: "inception_4a/5x5_reduce" - name: "inception_4a/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 16 kernel_size: 1 @@ -674,21 +748,25 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4a/5x5_reduce" top: "inception_4a/5x5_reduce" - name: "inception_4a/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4a/5x5" + type: "Convolution" bottom: "inception_4a/5x5_reduce" top: "inception_4a/5x5" - name: "inception_4a/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 48 pad: 2 @@ -703,17 +781,17 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_5x5" + type: "ReLU" bottom: "inception_4a/5x5" top: "inception_4a/5x5" - name: "inception_4a/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4a/pool" + type: "Pooling" bottom: "pool3/3x3_s2" top: "inception_4a/pool" - name: "inception_4a/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -721,15 +799,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4a/pool_proj" + type: "Convolution" bottom: "inception_4a/pool" top: "inception_4a/pool_proj" - name: "inception_4a/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -743,41 +825,45 @@ layers { } } } -layers { +layer { + name: "inception_4a/relu_pool_proj" + type: "ReLU" bottom: "inception_4a/pool_proj" top: "inception_4a/pool_proj" - name: "inception_4a/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4a/output" + type: "Concat" bottom: "inception_4a/1x1" bottom: "inception_4a/3x3" bottom: "inception_4a/5x5" bottom: "inception_4a/pool_proj" top: "inception_4a/output" - name: "inception_4a/output" - type: CONCAT } -layers { +layer { + name: "loss1/ave_pool" + type: "Pooling" bottom: "inception_4a/output" top: "loss1/ave_pool" - name: "loss1/ave_pool" - type: POOLING pooling_param { pool: AVE kernel_size: 5 stride: 3 } } -layers { +layer { + name: "loss1/conv" + type: "Convolution" bottom: "loss1/ave_pool" top: "loss1/conv" - name: "loss1/conv" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -791,21 +877,25 @@ layers { } } } -layers { +layer { + name: "loss1/relu_conv" + type: "ReLU" bottom: "loss1/conv" top: "loss1/conv" - name: "loss1/relu_conv" - type: RELU } -layers { +layer { + name: "loss1/fc" + type: "InnerProduct" bottom: "loss1/conv" top: "loss1/fc" - name: "loss1/fc" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 1024 weight_filler { @@ -818,30 +908,34 @@ layers { } } } -layers { +layer { + name: "loss1/relu_fc" + type: "ReLU" bottom: "loss1/fc" top: "loss1/fc" - name: "loss1/relu_fc" - type: RELU } -layers { +layer { + name: "loss1/drop_fc" + type: "Dropout" bottom: "loss1/fc" top: "loss1/fc" - name: "loss1/drop_fc" - type: DROPOUT dropout_param { dropout_ratio: 0.7 } } -layers { +layer { + name: "loss1/classifier" + type: "InnerProduct" bottom: "loss1/fc" top: "loss1/classifier" - name: "loss1/classifier" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 1000 weight_filler { @@ -854,46 +948,50 @@ layers { } } } -layers { +layer { + name: "loss1/loss" + type: "SoftmaxWithLoss" bottom: "loss1/classifier" bottom: "label" top: "loss1/loss1" - name: "loss1/loss" - type: SOFTMAX_LOSS loss_weight: 0.3 } -layers { +layer { + name: "loss1/top-1" + type: "Accuracy" bottom: "loss1/classifier" bottom: "label" top: "loss1/top-1" - name: "loss1/top-1" - type: ACCURACY include { phase: TEST } } -layers { +layer { + name: "loss1/top-5" + type: "Accuracy" bottom: "loss1/classifier" bottom: "label" top: "loss1/top-5" - name: "loss1/top-5" - type: ACCURACY - accuracy_param { - top_k: 5 - } include { phase: TEST } + accuracy_param { + top_k: 5 + } } -layers { +layer { + name: "inception_4b/1x1" + type: "Convolution" bottom: "inception_4a/output" top: "inception_4b/1x1" - name: "inception_4b/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 160 kernel_size: 1 @@ -907,21 +1005,25 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_1x1" + type: "ReLU" bottom: "inception_4b/1x1" top: "inception_4b/1x1" - name: "inception_4b/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4b/3x3_reduce" + type: "Convolution" bottom: "inception_4a/output" top: "inception_4b/3x3_reduce" - name: "inception_4b/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 112 kernel_size: 1 @@ -935,21 +1037,25 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4b/3x3_reduce" top: "inception_4b/3x3_reduce" - name: "inception_4b/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4b/3x3" + type: "Convolution" bottom: "inception_4b/3x3_reduce" top: "inception_4b/3x3" - name: "inception_4b/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 224 pad: 1 @@ -964,21 +1070,25 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_3x3" + type: "ReLU" bottom: "inception_4b/3x3" top: "inception_4b/3x3" - name: "inception_4b/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4b/5x5_reduce" + type: "Convolution" bottom: "inception_4a/output" top: "inception_4b/5x5_reduce" - name: "inception_4b/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 24 kernel_size: 1 @@ -992,21 +1102,25 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4b/5x5_reduce" top: "inception_4b/5x5_reduce" - name: "inception_4b/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4b/5x5" + type: "Convolution" bottom: "inception_4b/5x5_reduce" top: "inception_4b/5x5" - name: "inception_4b/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 pad: 2 @@ -1021,17 +1135,17 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_5x5" + type: "ReLU" bottom: "inception_4b/5x5" top: "inception_4b/5x5" - name: "inception_4b/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4b/pool" + type: "Pooling" bottom: "inception_4a/output" top: "inception_4b/pool" - name: "inception_4b/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1039,15 +1153,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4b/pool_proj" + type: "Convolution" bottom: "inception_4b/pool" top: "inception_4b/pool_proj" - name: "inception_4b/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -1061,30 +1179,34 @@ layers { } } } -layers { +layer { + name: "inception_4b/relu_pool_proj" + type: "ReLU" bottom: "inception_4b/pool_proj" top: "inception_4b/pool_proj" - name: "inception_4b/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4b/output" + type: "Concat" bottom: "inception_4b/1x1" bottom: "inception_4b/3x3" bottom: "inception_4b/5x5" bottom: "inception_4b/pool_proj" top: "inception_4b/output" - name: "inception_4b/output" - type: CONCAT } -layers { +layer { + name: "inception_4c/1x1" + type: "Convolution" bottom: "inception_4b/output" top: "inception_4c/1x1" - name: "inception_4c/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -1098,21 +1220,25 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_1x1" + type: "ReLU" bottom: "inception_4c/1x1" top: "inception_4c/1x1" - name: "inception_4c/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4c/3x3_reduce" + type: "Convolution" bottom: "inception_4b/output" top: "inception_4c/3x3_reduce" - name: "inception_4c/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -1126,21 +1252,25 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4c/3x3_reduce" top: "inception_4c/3x3_reduce" - name: "inception_4c/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4c/3x3" + type: "Convolution" bottom: "inception_4c/3x3_reduce" top: "inception_4c/3x3" - name: "inception_4c/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 1 @@ -1155,21 +1285,25 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_3x3" + type: "ReLU" bottom: "inception_4c/3x3" top: "inception_4c/3x3" - name: "inception_4c/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4c/5x5_reduce" + type: "Convolution" bottom: "inception_4b/output" top: "inception_4c/5x5_reduce" - name: "inception_4c/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 24 kernel_size: 1 @@ -1183,21 +1317,25 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4c/5x5_reduce" top: "inception_4c/5x5_reduce" - name: "inception_4c/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4c/5x5" + type: "Convolution" bottom: "inception_4c/5x5_reduce" top: "inception_4c/5x5" - name: "inception_4c/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 pad: 2 @@ -1212,17 +1350,17 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_5x5" + type: "ReLU" bottom: "inception_4c/5x5" top: "inception_4c/5x5" - name: "inception_4c/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4c/pool" + type: "Pooling" bottom: "inception_4b/output" top: "inception_4c/pool" - name: "inception_4c/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1230,15 +1368,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4c/pool_proj" + type: "Convolution" bottom: "inception_4c/pool" top: "inception_4c/pool_proj" - name: "inception_4c/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -1252,30 +1394,34 @@ layers { } } } -layers { +layer { + name: "inception_4c/relu_pool_proj" + type: "ReLU" bottom: "inception_4c/pool_proj" top: "inception_4c/pool_proj" - name: "inception_4c/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4c/output" + type: "Concat" bottom: "inception_4c/1x1" bottom: "inception_4c/3x3" bottom: "inception_4c/5x5" bottom: "inception_4c/pool_proj" top: "inception_4c/output" - name: "inception_4c/output" - type: CONCAT } -layers { +layer { + name: "inception_4d/1x1" + type: "Convolution" bottom: "inception_4c/output" top: "inception_4d/1x1" - name: "inception_4d/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 112 kernel_size: 1 @@ -1289,21 +1435,25 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_1x1" + type: "ReLU" bottom: "inception_4d/1x1" top: "inception_4d/1x1" - name: "inception_4d/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4d/3x3_reduce" + type: "Convolution" bottom: "inception_4c/output" top: "inception_4d/3x3_reduce" - name: "inception_4d/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 144 kernel_size: 1 @@ -1317,21 +1467,25 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4d/3x3_reduce" top: "inception_4d/3x3_reduce" - name: "inception_4d/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4d/3x3" + type: "Convolution" bottom: "inception_4d/3x3_reduce" top: "inception_4d/3x3" - name: "inception_4d/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 288 pad: 1 @@ -1346,21 +1500,25 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_3x3" + type: "ReLU" bottom: "inception_4d/3x3" top: "inception_4d/3x3" - name: "inception_4d/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4d/5x5_reduce" + type: "Convolution" bottom: "inception_4c/output" top: "inception_4d/5x5_reduce" - name: "inception_4d/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -1374,21 +1532,25 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4d/5x5_reduce" top: "inception_4d/5x5_reduce" - name: "inception_4d/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4d/5x5" + type: "Convolution" bottom: "inception_4d/5x5_reduce" top: "inception_4d/5x5" - name: "inception_4d/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 pad: 2 @@ -1403,17 +1565,17 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_5x5" + type: "ReLU" bottom: "inception_4d/5x5" top: "inception_4d/5x5" - name: "inception_4d/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4d/pool" + type: "Pooling" bottom: "inception_4c/output" top: "inception_4d/pool" - name: "inception_4d/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1421,15 +1583,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4d/pool_proj" + type: "Convolution" bottom: "inception_4d/pool" top: "inception_4d/pool_proj" - name: "inception_4d/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 64 kernel_size: 1 @@ -1443,41 +1609,45 @@ layers { } } } -layers { +layer { + name: "inception_4d/relu_pool_proj" + type: "ReLU" bottom: "inception_4d/pool_proj" top: "inception_4d/pool_proj" - name: "inception_4d/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4d/output" + type: "Concat" bottom: "inception_4d/1x1" bottom: "inception_4d/3x3" bottom: "inception_4d/5x5" bottom: "inception_4d/pool_proj" top: "inception_4d/output" - name: "inception_4d/output" - type: CONCAT } -layers { +layer { + name: "loss2/ave_pool" + type: "Pooling" bottom: "inception_4d/output" top: "loss2/ave_pool" - name: "loss2/ave_pool" - type: POOLING pooling_param { pool: AVE kernel_size: 5 stride: 3 } } -layers { +layer { + name: "loss2/conv" + type: "Convolution" bottom: "loss2/ave_pool" top: "loss2/conv" - name: "loss2/conv" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -1491,21 +1661,25 @@ layers { } } } -layers { +layer { + name: "loss2/relu_conv" + type: "ReLU" bottom: "loss2/conv" top: "loss2/conv" - name: "loss2/relu_conv" - type: RELU } -layers { +layer { + name: "loss2/fc" + type: "InnerProduct" bottom: "loss2/conv" top: "loss2/fc" - name: "loss2/fc" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 1024 weight_filler { @@ -1518,30 +1692,34 @@ layers { } } } -layers { +layer { + name: "loss2/relu_fc" + type: "ReLU" bottom: "loss2/fc" top: "loss2/fc" - name: "loss2/relu_fc" - type: RELU } -layers { +layer { + name: "loss2/drop_fc" + type: "Dropout" bottom: "loss2/fc" top: "loss2/fc" - name: "loss2/drop_fc" - type: DROPOUT dropout_param { dropout_ratio: 0.7 } } -layers { +layer { + name: "loss2/classifier" + type: "InnerProduct" bottom: "loss2/fc" top: "loss2/classifier" - name: "loss2/classifier" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 1000 weight_filler { @@ -1554,46 +1732,50 @@ layers { } } } -layers { +layer { + name: "loss2/loss" + type: "SoftmaxWithLoss" bottom: "loss2/classifier" bottom: "label" top: "loss2/loss1" - name: "loss2/loss" - type: SOFTMAX_LOSS loss_weight: 0.3 } -layers { +layer { + name: "loss2/top-1" + type: "Accuracy" bottom: "loss2/classifier" bottom: "label" top: "loss2/top-1" - name: "loss2/top-1" - type: ACCURACY include { phase: TEST } } -layers { +layer { + name: "loss2/top-5" + type: "Accuracy" bottom: "loss2/classifier" bottom: "label" top: "loss2/top-5" - name: "loss2/top-5" - type: ACCURACY - accuracy_param { - top_k: 5 - } include { phase: TEST } + accuracy_param { + top_k: 5 + } } -layers { +layer { + name: "inception_4e/1x1" + type: "Convolution" bottom: "inception_4d/output" top: "inception_4e/1x1" - name: "inception_4e/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 kernel_size: 1 @@ -1607,21 +1789,25 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_1x1" + type: "ReLU" bottom: "inception_4e/1x1" top: "inception_4e/1x1" - name: "inception_4e/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_4e/3x3_reduce" + type: "Convolution" bottom: "inception_4d/output" top: "inception_4e/3x3_reduce" - name: "inception_4e/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 160 kernel_size: 1 @@ -1635,21 +1821,25 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_3x3_reduce" + type: "ReLU" bottom: "inception_4e/3x3_reduce" top: "inception_4e/3x3_reduce" - name: "inception_4e/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_4e/3x3" + type: "Convolution" bottom: "inception_4e/3x3_reduce" top: "inception_4e/3x3" - name: "inception_4e/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 320 pad: 1 @@ -1664,21 +1854,25 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_3x3" + type: "ReLU" bottom: "inception_4e/3x3" top: "inception_4e/3x3" - name: "inception_4e/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_4e/5x5_reduce" + type: "Convolution" bottom: "inception_4d/output" top: "inception_4e/5x5_reduce" - name: "inception_4e/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -1692,21 +1886,25 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_5x5_reduce" + type: "ReLU" bottom: "inception_4e/5x5_reduce" top: "inception_4e/5x5_reduce" - name: "inception_4e/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_4e/5x5" + type: "Convolution" bottom: "inception_4e/5x5_reduce" top: "inception_4e/5x5" - name: "inception_4e/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 pad: 2 @@ -1721,17 +1919,17 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_5x5" + type: "ReLU" bottom: "inception_4e/5x5" top: "inception_4e/5x5" - name: "inception_4e/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_4e/pool" + type: "Pooling" bottom: "inception_4d/output" top: "inception_4e/pool" - name: "inception_4e/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1739,15 +1937,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_4e/pool_proj" + type: "Convolution" bottom: "inception_4e/pool" top: "inception_4e/pool_proj" - name: "inception_4e/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -1761,41 +1963,45 @@ layers { } } } -layers { +layer { + name: "inception_4e/relu_pool_proj" + type: "ReLU" bottom: "inception_4e/pool_proj" top: "inception_4e/pool_proj" - name: "inception_4e/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_4e/output" + type: "Concat" bottom: "inception_4e/1x1" bottom: "inception_4e/3x3" bottom: "inception_4e/5x5" bottom: "inception_4e/pool_proj" top: "inception_4e/output" - name: "inception_4e/output" - type: CONCAT } -layers { +layer { + name: "pool4/3x3_s2" + type: "Pooling" bottom: "inception_4e/output" top: "pool4/3x3_s2" - name: "pool4/3x3_s2" - type: POOLING pooling_param { pool: MAX kernel_size: 3 stride: 2 } } -layers { +layer { + name: "inception_5a/1x1" + type: "Convolution" bottom: "pool4/3x3_s2" top: "inception_5a/1x1" - name: "inception_5a/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 kernel_size: 1 @@ -1809,21 +2015,25 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_1x1" + type: "ReLU" bottom: "inception_5a/1x1" top: "inception_5a/1x1" - name: "inception_5a/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_5a/3x3_reduce" + type: "Convolution" bottom: "pool4/3x3_s2" top: "inception_5a/3x3_reduce" - name: "inception_5a/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 160 kernel_size: 1 @@ -1837,21 +2047,25 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_3x3_reduce" + type: "ReLU" bottom: "inception_5a/3x3_reduce" top: "inception_5a/3x3_reduce" - name: "inception_5a/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_5a/3x3" + type: "Convolution" bottom: "inception_5a/3x3_reduce" top: "inception_5a/3x3" - name: "inception_5a/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 320 pad: 1 @@ -1866,21 +2080,25 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_3x3" + type: "ReLU" bottom: "inception_5a/3x3" top: "inception_5a/3x3" - name: "inception_5a/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_5a/5x5_reduce" + type: "Convolution" bottom: "pool4/3x3_s2" top: "inception_5a/5x5_reduce" - name: "inception_5a/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 32 kernel_size: 1 @@ -1894,21 +2112,25 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_5x5_reduce" + type: "ReLU" bottom: "inception_5a/5x5_reduce" top: "inception_5a/5x5_reduce" - name: "inception_5a/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_5a/5x5" + type: "Convolution" bottom: "inception_5a/5x5_reduce" top: "inception_5a/5x5" - name: "inception_5a/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 pad: 2 @@ -1923,17 +2145,17 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_5x5" + type: "ReLU" bottom: "inception_5a/5x5" top: "inception_5a/5x5" - name: "inception_5a/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_5a/pool" + type: "Pooling" bottom: "pool4/3x3_s2" top: "inception_5a/pool" - name: "inception_5a/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -1941,15 +2163,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_5a/pool_proj" + type: "Convolution" bottom: "inception_5a/pool" top: "inception_5a/pool_proj" - name: "inception_5a/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -1963,30 +2189,34 @@ layers { } } } -layers { +layer { + name: "inception_5a/relu_pool_proj" + type: "ReLU" bottom: "inception_5a/pool_proj" top: "inception_5a/pool_proj" - name: "inception_5a/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_5a/output" + type: "Concat" bottom: "inception_5a/1x1" bottom: "inception_5a/3x3" bottom: "inception_5a/5x5" bottom: "inception_5a/pool_proj" top: "inception_5a/output" - name: "inception_5a/output" - type: CONCAT } -layers { +layer { + name: "inception_5b/1x1" + type: "Convolution" bottom: "inception_5a/output" top: "inception_5b/1x1" - name: "inception_5b/1x1" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 kernel_size: 1 @@ -2000,21 +2230,25 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_1x1" + type: "ReLU" bottom: "inception_5b/1x1" top: "inception_5b/1x1" - name: "inception_5b/relu_1x1" - type: RELU } -layers { +layer { + name: "inception_5b/3x3_reduce" + type: "Convolution" bottom: "inception_5a/output" top: "inception_5b/3x3_reduce" - name: "inception_5b/3x3_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 192 kernel_size: 1 @@ -2028,21 +2262,25 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_3x3_reduce" + type: "ReLU" bottom: "inception_5b/3x3_reduce" top: "inception_5b/3x3_reduce" - name: "inception_5b/relu_3x3_reduce" - type: RELU } -layers { +layer { + name: "inception_5b/3x3" + type: "Convolution" bottom: "inception_5b/3x3_reduce" top: "inception_5b/3x3" - name: "inception_5b/3x3" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -2057,21 +2295,25 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_3x3" + type: "ReLU" bottom: "inception_5b/3x3" top: "inception_5b/3x3" - name: "inception_5b/relu_3x3" - type: RELU } -layers { +layer { + name: "inception_5b/5x5_reduce" + type: "Convolution" bottom: "inception_5a/output" top: "inception_5b/5x5_reduce" - name: "inception_5b/5x5_reduce" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 48 kernel_size: 1 @@ -2085,21 +2327,25 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_5x5_reduce" + type: "ReLU" bottom: "inception_5b/5x5_reduce" top: "inception_5b/5x5_reduce" - name: "inception_5b/relu_5x5_reduce" - type: RELU } -layers { +layer { + name: "inception_5b/5x5" + type: "Convolution" bottom: "inception_5b/5x5_reduce" top: "inception_5b/5x5" - name: "inception_5b/5x5" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 pad: 2 @@ -2114,17 +2360,17 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_5x5" + type: "ReLU" bottom: "inception_5b/5x5" top: "inception_5b/5x5" - name: "inception_5b/relu_5x5" - type: RELU } -layers { +layer { + name: "inception_5b/pool" + type: "Pooling" bottom: "inception_5a/output" top: "inception_5b/pool" - name: "inception_5b/pool" - type: POOLING pooling_param { pool: MAX kernel_size: 3 @@ -2132,15 +2378,19 @@ layers { pad: 1 } } -layers { +layer { + name: "inception_5b/pool_proj" + type: "Convolution" bottom: "inception_5b/pool" top: "inception_5b/pool_proj" - name: "inception_5b/pool_proj" - type: CONVOLUTION - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 128 kernel_size: 1 @@ -2154,50 +2404,54 @@ layers { } } } -layers { +layer { + name: "inception_5b/relu_pool_proj" + type: "ReLU" bottom: "inception_5b/pool_proj" top: "inception_5b/pool_proj" - name: "inception_5b/relu_pool_proj" - type: RELU } -layers { +layer { + name: "inception_5b/output" + type: "Concat" bottom: "inception_5b/1x1" bottom: "inception_5b/3x3" bottom: "inception_5b/5x5" bottom: "inception_5b/pool_proj" top: "inception_5b/output" - name: "inception_5b/output" - type: CONCAT } -layers { +layer { + name: "pool5/7x7_s1" + type: "Pooling" bottom: "inception_5b/output" top: "pool5/7x7_s1" - name: "pool5/7x7_s1" - type: POOLING pooling_param { pool: AVE kernel_size: 7 stride: 1 } } -layers { +layer { + name: "pool5/drop_7x7_s1" + type: "Dropout" bottom: "pool5/7x7_s1" top: "pool5/7x7_s1" - name: "pool5/drop_7x7_s1" - type: DROPOUT dropout_param { dropout_ratio: 0.4 } } -layers { +layer { + name: "loss3/classifier" + type: "InnerProduct" bottom: "pool5/7x7_s1" top: "loss3/classifier" - name: "loss3/classifier" - type: INNER_PRODUCT - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 1000 weight_filler { @@ -2209,34 +2463,34 @@ layers { } } } -layers { +layer { + name: "loss3/loss3" + type: "SoftmaxWithLoss" bottom: "loss3/classifier" bottom: "label" top: "loss3/loss3" - name: "loss3/loss3" - type: SOFTMAX_LOSS loss_weight: 1 } -layers { +layer { + name: "loss3/top-1" + type: "Accuracy" bottom: "loss3/classifier" bottom: "label" top: "loss3/top-1" - name: "loss3/top-1" - type: ACCURACY include { phase: TEST } } -layers { +layer { + name: "loss3/top-5" + type: "Accuracy" bottom: "loss3/classifier" bottom: "label" top: "loss3/top-5" - name: "loss3/top-5" - type: ACCURACY - accuracy_param { - top_k: 5 - } include { phase: TEST } + accuracy_param { + top_k: 5 + } } diff --git a/models/bvlc_reference_caffenet/deploy.prototxt b/models/bvlc_reference_caffenet/deploy.prototxt index 4e494f420b5..29ccf1469f7 100644 --- a/models/bvlc_reference_caffenet/deploy.prototxt +++ b/models/bvlc_reference_caffenet/deploy.prototxt @@ -4,9 +4,9 @@ input_dim: 10 input_dim: 3 input_dim: 227 input_dim: 227 -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" convolution_param { @@ -15,15 +15,15 @@ layers { stride: 4 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -32,9 +32,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { @@ -43,9 +43,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" convolution_param { @@ -55,15 +55,15 @@ layers { group: 2 } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -72,9 +72,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { @@ -83,9 +83,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" convolution_param { @@ -94,15 +94,15 @@ layers { kernel_size: 3 } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION + type: "Convolution" bottom: "conv3" top: "conv4" convolution_param { @@ -112,15 +112,15 @@ layers { group: 2 } } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION + type: "Convolution" bottom: "conv4" top: "conv5" convolution_param { @@ -130,15 +130,15 @@ layers { group: 2 } } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" bottom: "conv5" top: "pool5" pooling_param { @@ -147,66 +147,66 @@ layers { stride: 2 } } -layers { +layer { name: "fc6" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool5" top: "fc6" inner_product_param { num_output: 4096 } } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6" top: "fc6" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" bottom: "fc6" top: "fc6" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc7" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc6" top: "fc7" inner_product_param { num_output: 4096 } } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7" top: "fc7" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" bottom: "fc7" top: "fc7" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc8" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc7" top: "fc8" inner_product_param { num_output: 1000 } } -layers { +layer { name: "prob" - type: SOFTMAX + type: "Softmax" bottom: "fc8" top: "prob" } diff --git a/models/bvlc_reference_caffenet/train_val.prototxt b/models/bvlc_reference_caffenet/train_val.prototxt index 073d8aeff4a..c79472e09ab 100644 --- a/models/bvlc_reference_caffenet/train_val.prototxt +++ b/models/bvlc_reference_caffenet/train_val.prototxt @@ -1,47 +1,71 @@ name: "CaffeNet" -layers { +layer { name: "data" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/imagenet/ilsvrc12_train_lmdb" - backend: LMDB - batch_size: 256 + include { + phase: TRAIN } transform_param { + mirror: true crop_size: 227 mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - mirror: true } - include: { phase: TRAIN } +# mean pixel / channel-wise mean instead of mean image +# transform_param { +# crop_size: 227 +# mean_value: 104 +# mean_value: 117 +# mean_value: 123 +# mirror: true +# } + data_param { + source: "examples/imagenet/ilsvrc12_train_lmdb" + batch_size: 256 + backend: LMDB + } } -layers { +layer { name: "data" - type: DATA + type: "Data" top: "data" top: "label" - data_param { - source: "examples/imagenet/ilsvrc12_val_lmdb" - backend: LMDB - batch_size: 50 + include { + phase: TEST } transform_param { + mirror: false crop_size: 227 mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - mirror: false } - include: { phase: TEST } +# mean pixel / channel-wise mean instead of mean image +# transform_param { +# crop_size: 227 +# mean_value: 104 +# mean_value: 117 +# mean_value: 123 +# mirror: true +# } + data_param { + source: "examples/imagenet/ilsvrc12_val_lmdb" + batch_size: 50 + backend: LMDB + } } -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 11 @@ -56,15 +80,15 @@ layers { } } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -73,9 +97,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { @@ -84,15 +108,19 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 2 @@ -108,15 +136,15 @@ layers { } } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -125,9 +153,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { @@ -136,15 +164,19 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -159,21 +191,25 @@ layers { } } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION + type: "Convolution" bottom: "conv3" top: "conv4" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -189,21 +225,25 @@ layers { } } } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION + type: "Convolution" bottom: "conv4" top: "conv5" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 1 @@ -219,15 +259,15 @@ layers { } } } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" bottom: "conv5" top: "pool5" pooling_param { @@ -236,15 +276,19 @@ layers { stride: 2 } } -layers { +layer { name: "fc6" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool5" top: "fc6" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 weight_filler { @@ -257,30 +301,34 @@ layers { } } } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6" top: "fc6" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" bottom: "fc6" top: "fc6" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc7" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc6" top: "fc7" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 weight_filler { @@ -293,30 +341,34 @@ layers { } } } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7" top: "fc7" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" bottom: "fc7" top: "fc7" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc8" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc7" top: "fc8" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 1000 weight_filler { @@ -329,17 +381,19 @@ layers { } } } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "fc8" bottom: "label" top: "accuracy" - include: { phase: TEST } + include { + phase: TEST + } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "fc8" bottom: "label" top: "loss" diff --git a/models/bvlc_reference_rcnn_ilsvrc13/deploy.prototxt b/models/bvlc_reference_rcnn_ilsvrc13/deploy.prototxt index ef75a0a5e95..ea9cf98a926 100644 --- a/models/bvlc_reference_rcnn_ilsvrc13/deploy.prototxt +++ b/models/bvlc_reference_rcnn_ilsvrc13/deploy.prototxt @@ -4,9 +4,9 @@ input_dim: 10 input_dim: 3 input_dim: 227 input_dim: 227 -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" convolution_param { @@ -15,15 +15,15 @@ layers { stride: 4 } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -32,9 +32,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { @@ -43,9 +43,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" convolution_param { @@ -55,15 +55,15 @@ layers { group: 2 } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -72,9 +72,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { @@ -83,9 +83,9 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" convolution_param { @@ -94,15 +94,15 @@ layers { kernel_size: 3 } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION + type: "Convolution" bottom: "conv3" top: "conv4" convolution_param { @@ -112,15 +112,15 @@ layers { group: 2 } } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION + type: "Convolution" bottom: "conv4" top: "conv5" convolution_param { @@ -130,15 +130,15 @@ layers { group: 2 } } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" bottom: "conv5" top: "pool5" pooling_param { @@ -147,48 +147,48 @@ layers { stride: 2 } } -layers { +layer { name: "fc6" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool5" top: "fc6" inner_product_param { num_output: 4096 } } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6" top: "fc6" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" bottom: "fc6" top: "fc6" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc7" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc6" top: "fc7" inner_product_param { num_output: 4096 } } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7" top: "fc7" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" bottom: "fc7" top: "fc7" dropout_param { @@ -196,9 +196,9 @@ layers { } } # R-CNN classification layer made from R-CNN ILSVRC13 SVMs. -layers { +layer { name: "fc-rcnn" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc7" top: "fc-rcnn" inner_product_param { diff --git a/models/finetune_flickr_style/deploy.prototxt b/models/finetune_flickr_style/deploy.prototxt new file mode 100644 index 00000000000..4a924f74927 --- /dev/null +++ b/models/finetune_flickr_style/deploy.prototxt @@ -0,0 +1,342 @@ +name: "FlickrStyleCaffeNet" +input: "data" +input_dim: 10 +input_dim: 3 +input_dim: 227 +input_dim: 227 +layer { + name: "conv1" + type: "Convolution" + bottom: "data" + top: "conv1" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } + convolution_param { + num_output: 96 + kernel_size: 11 + stride: 4 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + value: 0 + } + } +} +layer { + name: "relu1" + type: "ReLU" + bottom: "conv1" + top: "conv1" +} +layer { + name: "pool1" + type: "Pooling" + bottom: "conv1" + top: "pool1" + pooling_param { + pool: MAX + kernel_size: 3 + stride: 2 + } +} +layer { + name: "norm1" + type: "LRN" + bottom: "pool1" + top: "norm1" + lrn_param { + local_size: 5 + alpha: 0.0001 + beta: 0.75 + } +} +layer { + name: "conv2" + type: "Convolution" + bottom: "norm1" + top: "conv2" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } + convolution_param { + num_output: 256 + pad: 2 + kernel_size: 5 + group: 2 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + value: 1 + } + } +} +layer { + name: "relu2" + type: "ReLU" + bottom: "conv2" + top: "conv2" +} +layer { + name: "pool2" + type: "Pooling" + bottom: "conv2" + top: "pool2" + pooling_param { + pool: MAX + kernel_size: 3 + stride: 2 + } +} +layer { + name: "norm2" + type: "LRN" + bottom: "pool2" + top: "norm2" + lrn_param { + local_size: 5 + alpha: 0.0001 + beta: 0.75 + } +} +layer { + name: "conv3" + type: "Convolution" + bottom: "norm2" + top: "conv3" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } + convolution_param { + num_output: 384 + pad: 1 + kernel_size: 3 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + value: 0 + } + } +} +layer { + name: "relu3" + type: "ReLU" + bottom: "conv3" + top: "conv3" +} +layer { + name: "conv4" + type: "Convolution" + bottom: "conv3" + top: "conv4" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } + convolution_param { + num_output: 384 + pad: 1 + kernel_size: 3 + group: 2 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + value: 1 + } + } +} +layer { + name: "relu4" + type: "ReLU" + bottom: "conv4" + top: "conv4" +} +layer { + name: "conv5" + type: "Convolution" + bottom: "conv4" + top: "conv5" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } + convolution_param { + num_output: 256 + pad: 1 + kernel_size: 3 + group: 2 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + value: 1 + } + } +} +layer { + name: "relu5" + type: "ReLU" + bottom: "conv5" + top: "conv5" +} +layer { + name: "pool5" + type: "Pooling" + bottom: "conv5" + top: "pool5" + pooling_param { + pool: MAX + kernel_size: 3 + stride: 2 + } +} +layer { + name: "fc6" + type: "InnerProduct" + bottom: "pool5" + top: "fc6" + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } + inner_product_param { + num_output: 4096 + weight_filler { + type: "gaussian" + std: 0.005 + } + bias_filler { + type: "constant" + value: 1 + } + } +} +layer { + name: "relu6" + type: "ReLU" + bottom: "fc6" + top: "fc6" +} +layer { + name: "drop6" + type: "Dropout" + bottom: "fc6" + top: "fc6" + dropout_param { + dropout_ratio: 0.5 + } +} +layer { + name: "fc7" + type: "InnerProduct" + bottom: "fc6" + top: "fc7" + # Note that lr_mult can be set to 0 to disable any fine-tuning of this, and any other, layer + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } + inner_product_param { + num_output: 4096 + weight_filler { + type: "gaussian" + std: 0.005 + } + bias_filler { + type: "constant" + value: 1 + } + } +} +layer { + name: "relu7" + type: "ReLU" + bottom: "fc7" + top: "fc7" +} +layer { + name: "drop7" + type: "Dropout" + bottom: "fc7" + top: "fc7" + dropout_param { + dropout_ratio: 0.5 + } +} +layer { + name: "fc8_flickr" + type: "InnerProduct" + bottom: "fc7" + top: "fc8_flickr" + # lr_mult is set to higher than for other layers, because this layer is starting from random while the others are already trained + param { + lr_mult: 10 + decay_mult: 1 + } + param { + lr_mult: 20 + decay_mult: 0 + } + inner_product_param { + num_output: 20 + weight_filler { + type: "gaussian" + std: 0.01 + } + bias_filler { + type: "constant" + value: 0 + } + } +} +layer { + name: "prob" + type: "Softmax" + bottom: "fc8_flickr" + top: "prob" +} diff --git a/models/finetune_flickr_style/train_val.prototxt b/models/finetune_flickr_style/train_val.prototxt index 7155c492360..aa9c73e17ce 100644 --- a/models/finetune_flickr_style/train_val.prototxt +++ b/models/finetune_flickr_style/train_val.prototxt @@ -1,49 +1,57 @@ name: "FlickrStyleCaffeNet" -layers { +layer { name: "data" - type: IMAGE_DATA + type: "ImageData" top: "data" top: "label" + include { + phase: TRAIN + } + transform_param { + mirror: true + crop_size: 227 + mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" + } image_data_param { source: "data/flickr_style/train.txt" batch_size: 50 new_height: 256 new_width: 256 } - transform_param { - crop_size: 227 - mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - mirror: true - } - include: { phase: TRAIN } } -layers { +layer { name: "data" - type: IMAGE_DATA + type: "ImageData" top: "data" top: "label" + include { + phase: TEST + } + transform_param { + mirror: false + crop_size: 227 + mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" + } image_data_param { source: "data/flickr_style/test.txt" batch_size: 50 new_height: 256 new_width: 256 } - transform_param { - crop_size: 227 - mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" - mirror: false - } - include: { phase: TEST } } -layers { +layer { name: "conv1" - type: CONVOLUTION + type: "Convolution" bottom: "data" top: "conv1" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 96 kernel_size: 11 @@ -58,15 +66,15 @@ layers { } } } -layers { +layer { name: "relu1" - type: RELU + type: "ReLU" bottom: "conv1" top: "conv1" } -layers { +layer { name: "pool1" - type: POOLING + type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { @@ -75,9 +83,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm1" - type: LRN + type: "LRN" bottom: "pool1" top: "norm1" lrn_param { @@ -86,15 +94,19 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv2" - type: CONVOLUTION + type: "Convolution" bottom: "norm1" top: "conv2" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 2 @@ -110,15 +122,15 @@ layers { } } } -layers { +layer { name: "relu2" - type: RELU + type: "ReLU" bottom: "conv2" top: "conv2" } -layers { +layer { name: "pool2" - type: POOLING + type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { @@ -127,9 +139,9 @@ layers { stride: 2 } } -layers { +layer { name: "norm2" - type: LRN + type: "LRN" bottom: "pool2" top: "norm2" lrn_param { @@ -138,15 +150,19 @@ layers { beta: 0.75 } } -layers { +layer { name: "conv3" - type: CONVOLUTION + type: "Convolution" bottom: "norm2" top: "conv3" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -161,21 +177,25 @@ layers { } } } -layers { +layer { name: "relu3" - type: RELU + type: "ReLU" bottom: "conv3" top: "conv3" } -layers { +layer { name: "conv4" - type: CONVOLUTION + type: "Convolution" bottom: "conv3" top: "conv4" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 384 pad: 1 @@ -191,21 +211,25 @@ layers { } } } -layers { +layer { name: "relu4" - type: RELU + type: "ReLU" bottom: "conv4" top: "conv4" } -layers { +layer { name: "conv5" - type: CONVOLUTION + type: "Convolution" bottom: "conv4" top: "conv5" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } convolution_param { num_output: 256 pad: 1 @@ -221,15 +245,15 @@ layers { } } } -layers { +layer { name: "relu5" - type: RELU + type: "ReLU" bottom: "conv5" top: "conv5" } -layers { +layer { name: "pool5" - type: POOLING + type: "Pooling" bottom: "conv5" top: "pool5" pooling_param { @@ -238,15 +262,19 @@ layers { stride: 2 } } -layers { +layer { name: "fc6" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "pool5" top: "fc6" - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 weight_filler { @@ -259,31 +287,35 @@ layers { } } } -layers { +layer { name: "relu6" - type: RELU + type: "ReLU" bottom: "fc6" top: "fc6" } -layers { +layer { name: "drop6" - type: DROPOUT + type: "Dropout" bottom: "fc6" top: "fc6" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc7" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc6" top: "fc7" - # Note that blobs_lr can be set to 0 to disable any fine-tuning of this, and any other, layer - blobs_lr: 1 - blobs_lr: 2 - weight_decay: 1 - weight_decay: 0 + # Note that lr_mult can be set to 0 to disable any fine-tuning of this, and any other, layer + param { + lr_mult: 1 + decay_mult: 1 + } + param { + lr_mult: 2 + decay_mult: 0 + } inner_product_param { num_output: 4096 weight_filler { @@ -296,31 +328,35 @@ layers { } } } -layers { +layer { name: "relu7" - type: RELU + type: "ReLU" bottom: "fc7" top: "fc7" } -layers { +layer { name: "drop7" - type: DROPOUT + type: "Dropout" bottom: "fc7" top: "fc7" dropout_param { dropout_ratio: 0.5 } } -layers { +layer { name: "fc8_flickr" - type: INNER_PRODUCT + type: "InnerProduct" bottom: "fc7" top: "fc8_flickr" - # blobs_lr is set to higher than for other layers, because this layer is starting from random while the others are already trained - blobs_lr: 10 - blobs_lr: 20 - weight_decay: 1 - weight_decay: 0 + # lr_mult is set to higher than for other layers, because this layer is starting from random while the others are already trained + param { + lr_mult: 10 + decay_mult: 1 + } + param { + lr_mult: 20 + decay_mult: 0 + } inner_product_param { num_output: 20 weight_filler { @@ -333,17 +369,19 @@ layers { } } } -layers { +layer { name: "loss" - type: SOFTMAX_LOSS + type: "SoftmaxWithLoss" bottom: "fc8_flickr" bottom: "label" } -layers { +layer { name: "accuracy" - type: ACCURACY + type: "Accuracy" bottom: "fc8_flickr" bottom: "label" top: "accuracy" - include: { phase: TEST } + include { + phase: TEST + } } diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 8642c39367a..6afed4fa183 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,20 +1,30 @@ -project( Python ) +if(NOT HAVE_PYTHON) + message(STATUS "Python interface is disabled or not all required dependecies found. Building without it...") + return() +endif() -# Python -find_package(PythonLibs REQUIRED) -include_directories(${PYTHON_INCLUDE_DIRS}) +include_directories(${PYTHON_INCLUDE_DIRS} ${NUMPY_INCLUDE_DIR} ${Boost_INCLUDE_DIRS}) +file(GLOB_RECURSE python_srcs ${PROJECT_SOURCE_DIR}/python/*.cpp) -# Boost.Python -find_package(Boost 1.46 COMPONENTS python REQUIRED) -include_directories(${Boost_INCLUDE_DIRS}) +add_library(pycaffe SHARED ${python_srcs}) +target_link_libraries(pycaffe ${Caffe_LINK} ${PYTHON_LIBRARIES} ${Boost_LIBRARIES}) +set_target_properties(pycaffe PROPERTIES PREFIX "" OUTPUT_NAME "_caffe") +caffe_default_properties(pycaffe) -file(GLOB_RECURSE Python_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +if(UNIX OR APPLE) + set(__linkname "${PROJECT_SOURCE_DIR}/python/caffe/_caffe.so") + add_custom_command(TARGET pycaffe POST_BUILD + COMMAND ln -sf $ "${__linkname}" + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_SOURCE_DIR}/python/caffe/proto + COMMAND touch ${PROJECT_SOURCE_DIR}/python/caffe/proto/__init__.py + COMMAND cp ${proto_gen_folder}/*.py ${PROJECT_SOURCE_DIR}/python/caffe/proto/ + COMMENT "Creating symlink ${__linkname} -> ${PROJECT_BINARY_DIR}/lib/_caffe${CAffe_POSTFIX}.so") +endif() -add_library(pycaffe SHARED ${Python_SOURCES}) -target_link_libraries(pycaffe caffe ${PYTHON_LIBRARIES} ${Boost_LIBRARIES}) +# ---[ Install +file(GLOB files *.py requirements.txt) +install(FILES ${files} DESTINATION python) +install(DIRECTORY caffe DESTINATION python) +install(TARGETS pycaffe DESTINATION python/caffe) -### Install ################################################################################# -install(DIRECTORY caffe DESTINATION python) -install(FILES requirements.txt DESTINATION python) -install(TARGETS pycaffe DESTINATION python/caffe) \ No newline at end of file diff --git a/python/caffe/__init__.py b/python/caffe/__init__.py index 8dd2464d1c4..37e8956da4f 100644 --- a/python/caffe/__init__.py +++ b/python/caffe/__init__.py @@ -1,6 +1,6 @@ from .pycaffe import Net, SGDSolver -from .pycaffe import set_mode_cpu, set_mode_gpu, set_phase_train, set_phase_test -from .pycaffe import set_device +from ._caffe import set_mode_cpu, set_mode_gpu, set_device, Layer, get_solver +from .proto.caffe_pb2 import TRAIN, TEST from .classifier import Classifier from .detector import Detector import io diff --git a/python/caffe/_caffe.cpp b/python/caffe/_caffe.cpp index 2b279e8a6ce..a5d0e64605e 100644 --- a/python/caffe/_caffe.cpp +++ b/python/caffe/_caffe.cpp @@ -1,16 +1,20 @@ -// pycaffe provides a wrapper of the caffe::Net class as well as some -// caffe::Caffe functions so that one could easily call it from Python. -// Note that for Python, we will simply use float as the data type. +#include // NOLINT(build/include_alpha) +// Produce deprecation warnings (needs to come before arrayobject.h inclusion). +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + +#include +#include #include +#include // these need to be included after boost on OS X #include // NOLINT(build/include_order) #include // NOLINT(build/include_order) #include // NOLINT -#include "_caffe.hpp" #include "caffe/caffe.hpp" +#include "caffe/python_layer.hpp" // Temporary solution for numpy < 1.7 versions: old macro, no promises. // You're strongly advised to upgrade to >= 1.7. @@ -19,12 +23,22 @@ #define PyArray_SetBaseObject(arr, x) (PyArray_BASE(arr) = (x)) #endif +namespace bp = boost::python; + namespace caffe { -// for convenience, check that input files can be opened, and raise an +// For Python, for now, we'll just always use float as the type. +typedef float Dtype; +const int NPY_DTYPE = NPY_FLOAT32; + +// Selecting mode. +void set_mode_cpu() { Caffe::set_mode(Caffe::CPU); } +void set_mode_gpu() { Caffe::set_mode(Caffe::GPU); } + +// For convenience, check that input files can be opened, and raise an // exception that boost will send to Python if not (caffe could still crash // later if the input files are disturbed before they are actually used, but -// this saves frustration in most cases) +// this saves frustration in most cases). static void CheckFile(const string& filename) { std::ifstream f(filename.c_str()); if (!f.good()) { @@ -34,42 +48,7 @@ static void CheckFile(const string& filename) { f.close(); } -bp::object PyBlobWrap::get_data() { - npy_intp dims[] = {num(), channels(), height(), width()}; - - PyObject *obj = PyArray_SimpleNewFromData(4, dims, NPY_FLOAT32, - blob_->mutable_cpu_data()); - PyArray_SetBaseObject(reinterpret_cast(obj), self_); - Py_INCREF(self_); - bp::handle<> h(obj); - - return bp::object(h); -} - -bp::object PyBlobWrap::get_diff() { - npy_intp dims[] = {num(), channels(), height(), width()}; - - PyObject *obj = PyArray_SimpleNewFromData(4, dims, NPY_FLOAT32, - blob_->mutable_cpu_diff()); - PyArray_SetBaseObject(reinterpret_cast(obj), self_); - Py_INCREF(self_); - bp::handle<> h(obj); - - return bp::object(h); -} - -PyNet::PyNet(string param_file, string pretrained_param_file) { - Init(param_file); - CheckFile(pretrained_param_file); - net_->CopyTrainedLayersFrom(pretrained_param_file); -} - -void PyNet::Init(string param_file) { - CheckFile(param_file); - net_.reset(new Net(param_file)); -} - -void PyNet::check_contiguous_array(PyArrayObject* arr, string name, +void CheckContiguousArray(PyArrayObject* arr, string name, int channels, int height, int width) { if (!(PyArray_FLAGS(arr) & NPY_ARRAY_C_CONTIGUOUS)) { throw std::runtime_error(name + " must be C contiguous"); @@ -91,10 +70,39 @@ void PyNet::check_contiguous_array(PyArrayObject* arr, string name, } } -void PyNet::set_input_arrays(bp::object data_obj, bp::object labels_obj) { +// Net constructor for passing phase as int +shared_ptr > Net_Init( + string param_file, int phase) { + CheckFile(param_file); + + shared_ptr > net(new Net(param_file, + static_cast(phase))); + return net; +} + +// Net construct-and-load convenience constructor +shared_ptr > Net_Init_Load( + string param_file, string pretrained_param_file, int phase) { + CheckFile(param_file); + CheckFile(pretrained_param_file); + + shared_ptr > net(new Net(param_file, + static_cast(phase))); + net->CopyTrainedLayersFrom(pretrained_param_file); + return net; +} + +void Net_Save(const Net& net, string filename) { + NetParameter net_param; + net.ToProto(&net_param, false); + WriteProtoToBinaryFile(net_param, filename.c_str()); +} + +void Net_SetInputArrays(Net* net, bp::object data_obj, + bp::object labels_obj) { // check that this network has an input MemoryDataLayer - shared_ptr > md_layer = - boost::dynamic_pointer_cast >(net_->layers()[0]); + shared_ptr > md_layer = + boost::dynamic_pointer_cast >(net->layers()[0]); if (!md_layer) { throw std::runtime_error("set_input_arrays may only be called if the" " first layer is a MemoryDataLayer"); @@ -105,9 +113,9 @@ void PyNet::set_input_arrays(bp::object data_obj, bp::object labels_obj) { reinterpret_cast(data_obj.ptr()); PyArrayObject* labels_arr = reinterpret_cast(labels_obj.ptr()); - check_contiguous_array(data_arr, "data array", md_layer->datum_channels(), - md_layer->datum_height(), md_layer->datum_width()); - check_contiguous_array(labels_arr, "labels array", 1, 1, 1); + CheckContiguousArray(data_arr, "data array", md_layer->channels(), + md_layer->height(), md_layer->width()); + CheckContiguousArray(labels_arr, "labels array", 1, 1, 1); if (PyArray_DIMS(data_arr)[0] != PyArray_DIMS(labels_arr)[0]) { throw std::runtime_error("data and labels must have the same first" " dimension"); @@ -117,88 +125,155 @@ void PyNet::set_input_arrays(bp::object data_obj, bp::object labels_obj) { " multiple of batch size"); } - // hold references - input_data_ = data_obj; - input_labels_ = labels_obj; - - md_layer->Reset(static_cast(PyArray_DATA(data_arr)), - static_cast(PyArray_DATA(labels_arr)), + md_layer->Reset(static_cast(PyArray_DATA(data_arr)), + static_cast(PyArray_DATA(labels_arr)), PyArray_DIMS(data_arr)[0]); } -PySGDSolver::PySGDSolver(const string& param_file) { - // as in PyNet, (as a convenience, not a guarantee), create a Python - // exception if param_file can't be opened - CheckFile(param_file); - solver_.reset(new SGDSolver(param_file)); - // we need to explicitly store the net wrapper, rather than constructing - // it on the fly, so that it can hold references to Python objects - net_.reset(new PyNet(solver_->net())); +Solver* GetSolverFromFile(const string& filename) { + SolverParameter param; + ReadProtoFromTextFileOrDie(filename, ¶m); + return GetSolver(param); } -void PySGDSolver::SolveResume(const string& resume_file) { - CheckFile(resume_file); - return solver_->Solve(resume_file); -} +struct NdarrayConverterGenerator { + template struct apply; +}; -BOOST_PYTHON_MODULE(_caffe) { - // Caffe utility methods - bp::def("set_mode_cpu", &set_mode_cpu); - bp::def("set_mode_gpu", &set_mode_gpu); - bp::def("set_phase_train", &set_phase_train); - bp::def("set_phase_test", &set_phase_test); - bp::def("set_device", &Caffe::SetDevice); +template <> +struct NdarrayConverterGenerator::apply { + struct type { + PyObject* operator() (Dtype* data) const { + // Just store the data pointer, and add the shape information in postcall. + return PyArray_SimpleNewFromData(0, NULL, NPY_DTYPE, data); + } + const PyTypeObject* get_pytype() { + return &PyArray_Type; + } + }; +}; +struct NdarrayCallPolicies : public bp::default_call_policies { + typedef NdarrayConverterGenerator result_converter; + PyObject* postcall(PyObject* pyargs, PyObject* result) { + bp::object pyblob = bp::extract(pyargs)()[0]; + shared_ptr > blob = + bp::extract > >(pyblob); + // Free the temporary pointer-holding array, and construct a new one with + // the shape information from the blob. + void* data = PyArray_DATA(reinterpret_cast(result)); + Py_DECREF(result); + npy_intp dims[] = {blob->num(), blob->channels(), + blob->height(), blob->width()}; + PyObject* arr_obj = PyArray_SimpleNewFromData(4, dims, NPY_FLOAT32, data); + // SetBaseObject steals a ref, so we need to INCREF. + Py_INCREF(pyblob.ptr()); + PyArray_SetBaseObject(reinterpret_cast(arr_obj), + pyblob.ptr()); + return arr_obj; + } +}; + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SolveOverloads, Solve, 0, 1); + +BOOST_PYTHON_MODULE(_caffe) { // below, we prepend an underscore to methods that will be replaced // in Python - bp::class_ >( - "Net", bp::init()) - .def(bp::init()) - .def("_forward", &PyNet::Forward) - .def("_backward", &PyNet::Backward) - .def("reshape", &PyNet::Reshape) - .add_property("_blobs", &PyNet::blobs) - .add_property("layers", &PyNet::layers) - .add_property("_blob_names", &PyNet::blob_names) - .add_property("_layer_names", &PyNet::layer_names) - .add_property("inputs", &PyNet::inputs) - .add_property("outputs", &PyNet::outputs) - .add_property("mean", &PyNet::mean_) - .add_property("input_scale", &PyNet::input_scale_) - .add_property("raw_scale", &PyNet::raw_scale_) - .add_property("channel_swap", &PyNet::channel_swap_) - .def("_set_input_arrays", &PyNet::set_input_arrays) - .def("save", &PyNet::save); - - bp::class_, PyBlobWrap>( - "Blob", bp::no_init) - .add_property("num", &PyBlob::num) - .add_property("channels", &PyBlob::channels) - .add_property("height", &PyBlob::height) - .add_property("width", &PyBlob::width) - .add_property("count", &PyBlob::count) - .def("reshape", &PyBlob::Reshape) - .add_property("data", &PyBlobWrap::get_data) - .add_property("diff", &PyBlobWrap::get_diff); - - bp::class_( - "Layer", bp::no_init) - .add_property("blobs", &PyLayer::blobs); - - bp::class_( - "SGDSolver", bp::init()) - .add_property("net", &PySGDSolver::net) - .def("solve", &PySGDSolver::Solve) - .def("solve", &PySGDSolver::SolveResume); - - bp::class_ > >("BlobVec") - .def(bp::vector_indexing_suite >, true>()); - - bp::class_ >("LayerVec") - .def(bp::vector_indexing_suite, true>()); + // Caffe utility functions + bp::def("set_mode_cpu", &set_mode_cpu); + bp::def("set_mode_gpu", &set_mode_gpu); + bp::def("set_device", &Caffe::SetDevice); + + bp::class_, shared_ptr >, boost::noncopyable >("Net", + bp::no_init) + .def("__init__", bp::make_constructor(&Net_Init)) + .def("__init__", bp::make_constructor(&Net_Init_Load)) + .def("_forward", &Net::ForwardFromTo) + .def("_backward", &Net::BackwardFromTo) + .def("reshape", &Net::Reshape) + // The cast is to select a particular overload. + .def("copy_from", static_cast::*)(const string)>( + &Net::CopyTrainedLayersFrom)) + .def("share_with", &Net::ShareTrainedLayersWith) + .add_property("_blobs", bp::make_function(&Net::blobs, + bp::return_internal_reference<>())) + .add_property("layers", bp::make_function(&Net::layers, + bp::return_internal_reference<>())) + .add_property("_blob_names", bp::make_function(&Net::blob_names, + bp::return_value_policy())) + .add_property("_layer_names", bp::make_function(&Net::layer_names, + bp::return_value_policy())) + .add_property("_inputs", bp::make_function(&Net::input_blob_indices, + bp::return_value_policy())) + .add_property("_outputs", + bp::make_function(&Net::output_blob_indices, + bp::return_value_policy())) + .def("_set_input_arrays", &Net_SetInputArrays, + bp::with_custodian_and_ward<1, 2, bp::with_custodian_and_ward<1, 3> >()) + .def("save", &Net_Save); + + bp::class_, shared_ptr >, boost::noncopyable>( + "Blob", bp::no_init) + .add_property("num", &Blob::num) + .add_property("channels", &Blob::channels) + .add_property("height", &Blob::height) + .add_property("width", &Blob::width) + .add_property("count", &Blob::count) + .def("reshape", &Blob::Reshape) + .add_property("data", bp::make_function(&Blob::mutable_cpu_data, + NdarrayCallPolicies())) + .add_property("diff", bp::make_function(&Blob::mutable_cpu_diff, + NdarrayCallPolicies())); + + bp::class_, shared_ptr >, + boost::noncopyable>("Layer", bp::init()) + .add_property("blobs", bp::make_function(&Layer::blobs, + bp::return_internal_reference<>())) + .def("setup", &Layer::LayerSetUp) + .def("reshape", &Layer::Reshape) + .add_property("type", bp::make_function(&Layer::type)); + bp::register_ptr_to_python > >(); + + bp::class_("LayerParameter", bp::no_init); + + bp::class_, shared_ptr >, boost::noncopyable>( + "Solver", bp::no_init) + .add_property("net", &Solver::net) + .add_property("test_nets", bp::make_function(&Solver::test_nets, + bp::return_internal_reference<>())) + .add_property("iter", &Solver::iter) + .def("solve", static_cast::*)(const char*)>( + &Solver::Solve), SolveOverloads()) + .def("step", &Solver::Step); + + bp::class_, bp::bases >, + shared_ptr >, boost::noncopyable>( + "SGDSolver", bp::init()); + bp::class_, bp::bases >, + shared_ptr >, boost::noncopyable>( + "NesterovSolver", bp::init()); + bp::class_, bp::bases >, + shared_ptr >, boost::noncopyable>( + "AdaGradSolver", bp::init()); + + bp::def("get_solver", &GetSolverFromFile, + bp::return_value_policy()); + // vector wrappers for all the vector types we use + bp::class_ > > >("BlobVec") + .def(bp::vector_indexing_suite > >, true>()); + bp::class_*> >("RawBlobVec") + .def(bp::vector_indexing_suite*>, true>()); + bp::class_ > > >("LayerVec") + .def(bp::vector_indexing_suite > >, true>()); bp::class_ >("StringVec") - .def(bp::vector_indexing_suite >()); + .def(bp::vector_indexing_suite >()); + bp::class_ >("IntVec") + .def(bp::vector_indexing_suite >()); + bp::class_ > > >("NetVec") + .def(bp::vector_indexing_suite > >, true>()); + bp::class_ >("BoolVec") + .def(bp::vector_indexing_suite >()); import_array(); } diff --git a/python/caffe/_caffe.hpp b/python/caffe/_caffe.hpp deleted file mode 100644 index e2e1ca348ab..00000000000 --- a/python/caffe/_caffe.hpp +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef PYTHON_CAFFE__CAFFE_HPP_ -#define PYTHON_CAFFE__CAFFE_HPP_ - -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -#include -#include -#include - -// these need to be included after boost on OS X -#include // NOLINT(build/include_order) -#include // NOLINT(build/include_order) - -#include "caffe/caffe.hpp" - -namespace bp = boost::python; -using boost::shared_ptr; - -namespace caffe { - -// Selecting mode and phase. -void set_mode_cpu() { Caffe::set_mode(Caffe::CPU); } -void set_mode_gpu() { Caffe::set_mode(Caffe::GPU); } -void set_phase_train() { Caffe::set_phase(Caffe::TRAIN); } -void set_phase_test() { Caffe::set_phase(Caffe::TEST); } - -// wrap shared_ptr in a class that we construct in C++ and pass -// to Python -template -class PyBlob { - public: - explicit PyBlob(const shared_ptr > &blob) - : blob_(blob) {} - - int num() const { return blob_->num(); } - int channels() const { return blob_->channels(); } - int height() const { return blob_->height(); } - int width() const { return blob_->width(); } - int count() const { return blob_->count(); } - void Reshape(const int n, const int c, const int h, const int w) { - return blob_->Reshape(n, c, h, w); - } - - // this is here only to satisfy boost's vector_indexing_suite - bool operator == (const PyBlob &other) { - return this->blob_ == other.blob_; - } - - protected: - shared_ptr > blob_; -}; - -// We need another wrapper (used as boost::python's HeldType) that receives a -// self PyObject * which we can use as ndarray.base, so that data/diff memory -// is not freed while still being used in Python. -class PyBlobWrap : public PyBlob { - public: - PyBlobWrap(PyObject *p, const PyBlob &blob) - : PyBlob(blob), self_(p) {} - - bp::object get_data(); - bp::object get_diff(); - - private: - PyObject *self_; -}; - -class PyLayer { - public: - explicit PyLayer(const shared_ptr > &layer) - : layer_(layer) {} - - vector > blobs() { - return vector >(layer_->blobs().begin(), - layer_->blobs().end()); - } - - // this is here only to satisfy boost's vector_indexing_suite - bool operator == (const PyLayer &other) { - return this->layer_ == other.layer_; - } - - protected: - shared_ptr > layer_; -}; - -class PyNet { - public: - // For cases where parameters will be determined later by the Python user, - // create a Net with unallocated parameters (which will not be zero-filled - // when accessed). - explicit PyNet(string param_file) { Init(param_file); } - PyNet(string param_file, string pretrained_param_file); - explicit PyNet(shared_ptr > net) - : net_(net) {} - virtual ~PyNet() {} - - void Init(string param_file); - - - // Generate Python exceptions for badly shaped or discontiguous arrays. - inline void check_contiguous_array(PyArrayObject* arr, string name, - int channels, int height, int width); - - void Forward(int start, int end) { net_->ForwardFromTo(start, end); } - void Backward(int start, int end) { net_->BackwardFromTo(start, end); } - void Reshape() { net_->Reshape(); } - - void set_input_arrays(bp::object data_obj, bp::object labels_obj); - - // Save the network weights to binary proto for net surgeries. - void save(string filename) { - NetParameter net_param; - net_->ToProto(&net_param, false); - WriteProtoToBinaryFile(net_param, filename.c_str()); - } - - vector > blobs() { - return vector >(net_->blobs().begin(), net_->blobs().end()); - } - - vector layers() { - return vector(net_->layers().begin(), net_->layers().end()); - } - - vector blob_names() { return net_->blob_names(); } - vector layer_names() { return net_->layer_names(); } - - bp::list inputs() { - bp::list input_blob_names; - for (int i = 0; i < net_->input_blob_indices().size(); ++i) { - input_blob_names.append( - net_->blob_names()[net_->input_blob_indices()[i]]); - } - return input_blob_names; - } - - bp::list outputs() { - bp::list output_blob_names; - for (int i = 0; i < net_->output_blob_indices().size(); ++i) { - output_blob_names.append( - net_->blob_names()[net_->output_blob_indices()[i]]); - } - return output_blob_names; - } - - // Input preprocessing configuration attributes. These are public for - // direct access from Python. - bp::dict mean_; - bp::dict input_scale_; - bp::dict raw_scale_; - bp::dict channel_swap_; - - protected: - // The pointer to the internal caffe::Net instant. - shared_ptr > net_; - // if taking input from an ndarray, we need to hold references - bp::object input_data_; - bp::object input_labels_; -}; - -class PySGDSolver { - public: - explicit PySGDSolver(const string& param_file); - - shared_ptr net() { return net_; } - void Solve() { return solver_->Solve(); } - void SolveResume(const string& resume_file); - - protected: - shared_ptr net_; - shared_ptr > solver_; -}; - -// Declare the module init function created by boost::python, so that we can -// use this module from C++ when embedding Python. -PyMODINIT_FUNC init_caffe(void); - -} // namespace caffe - -#endif diff --git a/python/caffe/classifier.py b/python/caffe/classifier.py index f9a13a39865..94dd063a2c7 100644 --- a/python/caffe/classifier.py +++ b/python/caffe/classifier.py @@ -14,33 +14,32 @@ class Classifier(caffe.Net): by scaling, center cropping, or oversampling. """ def __init__(self, model_file, pretrained_file, image_dims=None, - gpu=False, mean=None, input_scale=None, raw_scale=None, + mean=None, input_scale=None, raw_scale=None, channel_swap=None): """ Take image_dims: dimensions to scale input for cropping/sampling. Default is to scale to net input size for whole-image crop. - gpu, mean, input_scale, raw_scale, channel_swap: params for + mean, input_scale, raw_scale, channel_swap: params for preprocessing options. """ - caffe.Net.__init__(self, model_file, pretrained_file) - caffe.set_phase_test() - - if gpu: - caffe.set_mode_gpu() - else: - caffe.set_mode_cpu() + caffe.Net.__init__(self, model_file, pretrained_file, caffe.TEST) + # configure pre-processing + in_ = self.inputs[0] + self.transformer = caffe.io.Transformer( + {in_: self.blobs[in_].data.shape for in_ in self.inputs}) + self.transformer.set_transpose(in_, (2,0,1)) if mean is not None: - self.set_mean(self.inputs[0], mean) + self.transformer.set_mean(in_, mean) if input_scale is not None: - self.set_input_scale(self.inputs[0], input_scale) + self.transformer.set_input_scale(in_, input_scale) if raw_scale is not None: - self.set_raw_scale(self.inputs[0], raw_scale) + self.transformer.set_raw_scale(in_, raw_scale) if channel_swap is not None: - self.set_channel_swap(self.inputs[0], channel_swap) + self.transformer.set_channel_swap(in_, channel_swap) - self.crop_dims = np.array(self.blobs[self.inputs[0]].data.shape[2:]) + self.crop_dims = np.array(self.blobs[in_].data.shape[2:]) if not image_dims: image_dims = self.crop_dims self.image_dims = image_dims @@ -82,7 +81,7 @@ def predict(self, inputs, oversample=True): caffe_in = np.zeros(np.array(input_.shape)[[0,3,1,2]], dtype=np.float32) for ix, in_ in enumerate(input_): - caffe_in[ix] = self.preprocess(self.inputs[0], in_) + caffe_in[ix] = self.transformer.preprocess(self.inputs[0], in_) out = self.forward_all(**{self.inputs[0]: caffe_in}) predictions = out[self.outputs[0]].squeeze(axis=(2,3)) diff --git a/python/caffe/detector.py b/python/caffe/detector.py index b78abd12a89..4ea07fb7b36 100644 --- a/python/caffe/detector.py +++ b/python/caffe/detector.py @@ -29,28 +29,27 @@ def __init__(self, model_file, pretrained_file, gpu=False, mean=None, context_pad=None): """ Take - gpu, mean, input_scale, raw_scale, channel_swap: params for + mean, input_scale, raw_scale, channel_swap: params for preprocessing options. context_pad: amount of surrounding context to take s.t. a `context_pad` sized border of pixels in the network input image is context, as in R-CNN feature extraction. """ - caffe.Net.__init__(self, model_file, pretrained_file) - caffe.set_phase_test() - - if gpu: - caffe.set_mode_gpu() - else: - caffe.set_mode_cpu() + caffe.Net.__init__(self, model_file, pretrained_file, caffe.TEST) + # configure pre-processing + in_ = self.inputs[0] + self.transformer = caffe.io.Transformer( + {in_: self.blobs[in_].data.shape for in_ in self.inputs}) + self.transformer.set_transpose(in_, (2,0,1)) if mean is not None: - self.set_mean(self.inputs[0], mean) + self.transformer.set_mean(in_, mean) if input_scale is not None: - self.set_input_scale(self.inputs[0], input_scale) + self.transformer.set_input_scale(in_, input_scale) if raw_scale is not None: - self.set_raw_scale(self.inputs[0], raw_scale) + self.transformer.set_raw_scale(in_, raw_scale) if channel_swap is not None: - self.set_channel_swap(self.inputs[0], channel_swap) + self.transformer.set_channel_swap(in_, channel_swap) self.configure_crop(context_pad) @@ -76,12 +75,13 @@ def detect_windows(self, images_windows): window_inputs.append(self.crop(image, window)) # Run through the net (warping windows to input dimensions). + in_ = self.inputs[0] caffe_in = np.zeros((len(window_inputs), window_inputs[0].shape[2]) - + self.blobs[self.inputs[0]].data.shape[2:], + + self.blobs[in_].data.shape[2:], dtype=np.float32) for ix, window_in in enumerate(window_inputs): - caffe_in[ix] = self.preprocess(self.inputs[0], window_in) - out = self.forward_all(**{self.inputs[0]: caffe_in}) + caffe_in[ix] = self.transformer.preprocess(in_, window_in) + out = self.forward_all(**{in_: caffe_in}) predictions = out[self.outputs[0]].squeeze(axis=(2,3)) # Package predictions with images and windows. @@ -170,7 +170,7 @@ def crop(self, im, window): # with mean padding context_crop = im[box[0]:box[2], box[1]:box[3]] context_crop = caffe.io.resize_image(context_crop, (crop_h, crop_w)) - crop = self.crop_mean.copy() + crop = np.ones(self.crop_dims, dtype=np.float32) * self.crop_mean crop[pad_y:(pad_y + crop_h), pad_x:(pad_x + crop_w)] = context_crop return crop @@ -178,20 +178,30 @@ def crop(self, im, window): def configure_crop(self, context_pad): """ - Configure amount of context for cropping. + Configure crop dimensions and amount of context for cropping. If context is included, make the special input mean for context padding. Take context_pad: amount of context for cropping. """ + # crop dimensions + in_ = self.inputs[0] + tpose = self.transformer.transpose[in_] + inv_tpose = [tpose[t] for t in tpose] + self.crop_dims = np.array(self.blobs[in_].data.shape[1:])[inv_tpose] + #.transpose(inv_tpose) + # context padding self.context_pad = context_pad if self.context_pad: - raw_scale = self.raw_scale.get(self.inputs[0]) - channel_order = self.channel_swap.get(self.inputs[0]) + in_ = self.inputs[0] + transpose = self.transformer.transpose.get(in_) + channel_order = self.transformer.channel_swap.get(in_) + raw_scale = self.transformer.raw_scale.get(in_) # Padding context crops needs the mean in unprocessed input space. - mean = self.mean.get(self.inputs[0]) + mean = self.transformer.mean.get(in_) if mean is not None: - crop_mean = mean.copy().transpose((1,2,0)) + inv_transpose = [transpose[t] for t in transpose] + crop_mean = mean.copy().transpose(inv_transpose) if channel_order is not None: channel_order_inverse = [channel_order.index(i) for i in range(crop_mean.shape[2])] @@ -200,5 +210,4 @@ def configure_crop(self, context_pad): crop_mean /= raw_scale self.crop_mean = crop_mean else: - self.crop_mean = np.zeros(self.blobs[self.inputs[0]].data.shape, - dtype=np.float32) + self.crop_mean = np.zeros(self.crop_dims, dtype=np.float32) diff --git a/python/caffe/draw.py b/python/caffe/draw.py index f8631cfa09e..6a4dbd47351 100644 --- a/python/caffe/draw.py +++ b/python/caffe/draw.py @@ -11,52 +11,141 @@ import pydot # Internal layer and blob styles. -LAYER_STYLE = {'shape': 'record', 'fillcolor': '#6495ED', +LAYER_STYLE_DEFAULT = {'shape': 'record', 'fillcolor': '#6495ED', 'style': 'filled'} NEURON_LAYER_STYLE = {'shape': 'record', 'fillcolor': '#90EE90', 'style': 'filled'} -BLOB_STYLE = {'shape': 'octagon', 'fillcolor': '#F0E68C', +BLOB_STYLE = {'shape': 'octagon', 'fillcolor': '#E0E0E0', 'style': 'filled'} -def get_enum_name_by_value(): - desc = caffe_pb2.LayerParameter.LayerType.DESCRIPTOR - d = {} - for k,v in desc.values_by_name.items(): - d[v.number] = k - return d - -def get_pydot_graph(caffe_net): - pydot_graph = pydot.Dot(caffe_net.name, graph_type='digraph', rankdir="BT") + +def get_pooling_types_dict(): + """Get dictionary mapping pooling type number to type name + """ + desc = caffe_pb2.PoolingParameter.PoolMethod.DESCRIPTOR + d = {} + for k,v in desc.values_by_name.items(): + d[v.number] = k + return d + + +def determine_edge_label_by_layertype(layer, layertype): + """Define edge label based on layer type + """ + + if layertype == 'Data': + edge_label = 'Batch ' + str(layer.data_param.batch_size) + elif layertype == 'Convolution': + edge_label = str(layer.convolution_param.num_output) + elif layertype == 'InnerProduct': + edge_label = str(layer.inner_product_param.num_output) + else: + edge_label = '""' + + return edge_label + + +def determine_node_label_by_layertype(layer, layertype, rankdir): + """Define node label based on layer type + """ + + if rankdir in ('TB', 'BT'): + # If graph orientation is vertical, horizontal space is free and + # vertical space is not; separate words with spaces + separator = ' ' + else: + # If graph orientation is horizontal, vertical space is free and + # horizontal space is not; separate words with newlines + separator = '\n' + + if layertype == 'Convolution': + # Outer double quotes needed or else colon characters don't parse + # properly + node_label = '"%s%s(%s)%skernel size: %d%sstride: %d%spad: %d"' %\ + (layer.name, + separator, + layertype, + separator, + layer.convolution_param.kernel_size, + separator, + layer.convolution_param.stride, + separator, + layer.convolution_param.pad) + elif layertype == 'Pooling': + pooling_types_dict = get_pooling_types_dict() + node_label = '"%s%s(%s %s)%skernel size: %d%sstride: %d%spad: %d"' %\ + (layer.name, + separator, + pooling_types_dict[layer.pooling_param.pool], + layertype, + separator, + layer.pooling_param.kernel_size, + separator, + layer.pooling_param.stride, + separator, + layer.pooling_param.pad) + else: + node_label = '"%s%s(%s)"' % (layer.name, separator, layertype) + return node_label + + +def choose_color_by_layertype(layertype): + """Define colors for nodes based on the layer type + """ + color = '#6495ED' # Default + if layertype == 'Convolution': + color = '#FF5050' + elif layertype == 'Pooling': + color = '#FF9900' + elif layertype == 'InnerProduct': + color = '#CC33FF' + return color + + +def get_pydot_graph(caffe_net, rankdir, label_edges=True): + pydot_graph = pydot.Dot(caffe_net.name, graph_type='digraph', rankdir=rankdir) pydot_nodes = {} pydot_edges = [] - d = get_enum_name_by_value() - for layer in caffe_net.layers: + for layer in caffe_net.layer: name = layer.name - layertype = d[layer.type] + layertype = layer.type + node_label = determine_node_label_by_layertype(layer, layertype, rankdir) if (len(layer.bottom) == 1 and len(layer.top) == 1 and layer.bottom[0] == layer.top[0]): # We have an in-place neuron layer. pydot_nodes[name + '_' + layertype] = pydot.Node( - '%s (%s)' % (name, layertype), **NEURON_LAYER_STYLE) + node_label, **NEURON_LAYER_STYLE) else: + layer_style = LAYER_STYLE_DEFAULT + layer_style['fillcolor'] = choose_color_by_layertype(layertype) pydot_nodes[name + '_' + layertype] = pydot.Node( - '%s (%s)' % (name, layertype), **LAYER_STYLE) + node_label, **layer_style) for bottom_blob in layer.bottom: pydot_nodes[bottom_blob + '_blob'] = pydot.Node( '%s' % (bottom_blob), **BLOB_STYLE) - pydot_edges.append((bottom_blob + '_blob', name + '_' + layertype)) + edge_label = '""' + pydot_edges.append({'src': bottom_blob + '_blob', + 'dst': name + '_' + layertype, + 'label': edge_label}) for top_blob in layer.top: pydot_nodes[top_blob + '_blob'] = pydot.Node( '%s' % (top_blob)) - pydot_edges.append((name + '_' + layertype, top_blob + '_blob')) + if label_edges: + edge_label = determine_edge_label_by_layertype(layer, layertype) + else: + edge_label = '""' + pydot_edges.append({'src': name + '_' + layertype, + 'dst': top_blob + '_blob', + 'label': edge_label}) # Now, add the nodes and edges to the graph. for node in pydot_nodes.values(): pydot_graph.add_node(node) for edge in pydot_edges: pydot_graph.add_edge( - pydot.Edge(pydot_nodes[edge[0]], pydot_nodes[edge[1]])) + pydot.Edge(pydot_nodes[edge['src']], pydot_nodes[edge['dst']], + label=edge['label'])) return pydot_graph -def draw_net(caffe_net, ext='png'): +def draw_net(caffe_net, rankdir, ext='png'): """Draws a caffe net and returns the image string encoded using the given extension. @@ -64,13 +153,13 @@ def draw_net(caffe_net, ext='png'): caffe_net: a caffe.proto.caffe_pb2.NetParameter protocol buffer. ext: the image extension. Default 'png'. """ - return get_pydot_graph(caffe_net).create(format=ext) + return get_pydot_graph(caffe_net, rankdir).create(format=ext) -def draw_net_to_file(caffe_net, filename): +def draw_net_to_file(caffe_net, filename, rankdir='LR'): """Draws a caffe net, and saves it to file using the format given as the file extension. Use '.raw' to output raw text that you can manually feed to graphviz to draw graphs. """ ext = filename[filename.rfind('.')+1:] with open(filename, 'wb') as fid: - fid.write(draw_net(caffe_net, ext)) + fid.write(draw_net(caffe_net, rankdir, ext)) diff --git a/python/caffe/io.py b/python/caffe/io.py index aabcfddbbeb..0ce9ecfeeed 100644 --- a/python/caffe/io.py +++ b/python/caffe/io.py @@ -5,6 +5,255 @@ from caffe.proto import caffe_pb2 +## proto / datum / ndarray conversion + +def blobproto_to_array(blob, return_diff=False): + """Convert a blob proto to an array. In default, we will just return the data, + unless return_diff is True, in which case we will return the diff. + """ + if return_diff: + return np.array(blob.diff).reshape( + blob.num, blob.channels, blob.height, blob.width) + else: + return np.array(blob.data).reshape( + blob.num, blob.channels, blob.height, blob.width) + + +def array_to_blobproto(arr, diff=None): + """Converts a 4-dimensional array to blob proto. If diff is given, also + convert the diff. You need to make sure that arr and diff have the same + shape, and this function does not do sanity check. + """ + if arr.ndim != 4: + raise ValueError('Incorrect array shape.') + blob = caffe_pb2.BlobProto() + blob.num, blob.channels, blob.height, blob.width = arr.shape; + blob.data.extend(arr.astype(float).flat) + if diff is not None: + blob.diff.extend(diff.astype(float).flat) + return blob + + +def arraylist_to_blobprotovecor_str(arraylist): + """Converts a list of arrays to a serialized blobprotovec, which could be + then passed to a network for processing. + """ + vec = caffe_pb2.BlobProtoVector() + vec.blobs.extend([array_to_blobproto(arr) for arr in arraylist]) + return vec.SerializeToString() + + +def blobprotovector_str_to_arraylist(str): + """Converts a serialized blobprotovec to a list of arrays. + """ + vec = caffe_pb2.BlobProtoVector() + vec.ParseFromString(str) + return [blobproto_to_array(blob) for blob in vec.blobs] + + +def array_to_datum(arr, label=0): + """Converts a 3-dimensional array to datum. If the array has dtype uint8, + the output data will be encoded as a string. Otherwise, the output data + will be stored in float format. + """ + if arr.ndim != 3: + raise ValueError('Incorrect array shape.') + datum = caffe_pb2.Datum() + datum.channels, datum.height, datum.width = arr.shape + if arr.dtype == np.uint8: + datum.data = arr.tostring() + else: + datum.float_data.extend(arr.flat) + datum.label = label + return datum + + +def datum_to_array(datum): + """Converts a datum to an array. Note that the label is not returned, + as one can easily get it by calling datum.label. + """ + if len(datum.data): + return np.fromstring(datum.data, dtype = np.uint8).reshape( + datum.channels, datum.height, datum.width) + else: + return np.array(datum.float_data).astype(float).reshape( + datum.channels, datum.height, datum.width) + + +## Pre-processing + +class Transformer: + """ + Transform input for feeding into a Net. + + Note: this is mostly for illustrative purposes and it is likely better + to define your own input preprocessing routine for your needs. + + Take + net: a Net for which the input should be prepared + """ + def __init__(self, inputs): + self.inputs = inputs + self.transpose = {} + self.channel_swap = {} + self.raw_scale = {} + self.mean = {} + self.input_scale = {} + + + def __check_input(self, in_): + if in_ not in self.inputs: + raise Exception('{} is not one of the net inputs: {}'.format( + in_, self.inputs)) + + + def preprocess(self, in_, data): + """ + Format input for Caffe: + - convert to single + - resize to input dimensions (preserving number of channels) + - transpose dimensions to K x H x W + - reorder channels (for instance color to BGR) + - scale raw input (e.g. from [0, 1] to [0, 255] for ImageNet models) + - subtract mean + - scale feature + + Take + in_: name of input blob to preprocess for + data: (H' x W' x K) ndarray + + Give + caffe_in: (K x H x W) ndarray for input to a Net + """ + self.__check_input(in_) + caffe_in = data.astype(np.float32, copy=False) + transpose = self.transpose.get(in_) + channel_swap = self.channel_swap.get(in_) + raw_scale = self.raw_scale.get(in_) + mean = self.mean.get(in_) + input_scale = self.input_scale.get(in_) + in_dims = self.inputs[in_][2:] + if caffe_in.shape[:2] != in_dims: + caffe_in = resize_image(caffe_in, in_dims) + if transpose is not None: + caffe_in = caffe_in.transpose(transpose) + if channel_swap is not None: + caffe_in = caffe_in[channel_swap, :, :] + if raw_scale is not None: + caffe_in *= raw_scale + if mean is not None: + caffe_in -= mean + if input_scale is not None: + caffe_in *= input_scale + return caffe_in + + + def deprocess(self, in_, data): + """ + Invert Caffe formatting; see preprocess(). + """ + self.__check_input(in_) + decaf_in = data.copy().squeeze() + transpose = self.transpose.get(in_) + channel_swap = self.channel_swap.get(in_) + raw_scale = self.raw_scale.get(in_) + mean = self.mean.get(in_) + input_scale = self.input_scale.get(in_) + if input_scale is not None: + decaf_in /= input_scale + if mean is not None: + decaf_in += mean + if raw_scale is not None: + decaf_in /= raw_scale + if channel_swap is not None: + decaf_in = decaf_in[channel_swap, :, :] + if transpose is not None: + decaf_in = decaf_in.transpose([transpose[t] for t in transpose]) + return decaf_in + + + def set_transpose(self, in_, order): + """ + Set the input channel order for e.g. RGB to BGR conversion + as needed for the reference ImageNet model. + + Take + in_: which input to assign this channel order + order: the order to transpose the dimensions + """ + self.__check_input(in_) + if len(order) != len(self.inputs[in_]) - 1: + raise Exception('Transpose order needs to have the same number of ' + 'dimensions as the input.') + self.transpose[in_] = order + + + def set_channel_swap(self, in_, order): + """ + Set the input channel order for e.g. RGB to BGR conversion + as needed for the reference ImageNet model. + N.B. this assumes the channels are the first dimension AFTER transpose. + + Take + in_: which input to assign this channel order + order: the order to take the channels. + (2,1,0) maps RGB to BGR for example. + """ + self.__check_input(in_) + if len(order) != self.inputs[in_][1]: + raise Exception('Channel swap needs to have the same number of ' + 'dimensions as the input channels.') + self.channel_swap[in_] = order + + + def set_raw_scale(self, in_, scale): + """ + Set the scale of raw features s.t. the input blob = input * scale. + While Python represents images in [0, 1], certain Caffe models + like CaffeNet and AlexNet represent images in [0, 255] so the raw_scale + of these models must be 255. + + Take + in_: which input to assign this scale factor + scale: scale coefficient + """ + self.__check_input(in_) + self.raw_scale[in_] = scale + + + def set_mean(self, in_, mean): + """ + Set the mean to subtract for centering the data. + + Take + in_: which input to assign this mean. + mean: mean ndarray (input dimensional or broadcastable) + """ + self.__check_input(in_) + if mean.ndim == 1: + mean = mean[:, np.newaxis, np.newaxis] + mk, mh, mw = mean.shape + in_k, in_h, in_w = self.inputs[in_][1:] + #if mk != in_k or (mh, mw) != (in_h, in_w) and (mh, mw) != (1, 1): + # raise Exception('Mean shape incompatible with input shape.') + self.mean[in_] = mean + + + def set_input_scale(self, in_, scale): + """ + Set the scale of preprocessed inputs s.t. the blob = blob * scale. + N.B. input_scale is done AFTER mean subtraction and other preprocessing + while raw_scale is done BEFORE. + + Take + in_: which input to assign this scale factor + scale: scale coefficient + """ + self.__check_input(in_) + self.input_scale[in_] = scale + + +## Image IO def load_image(filename, color=True): """ @@ -43,11 +292,17 @@ def resize_image(im, new_dims, interp_order=1): im: resized ndarray with shape (new_dims[0], new_dims[1], K) """ if im.shape[-1] == 1 or im.shape[-1] == 3: - # skimage is fast but only understands {1,3} channel images in [0, 1]. im_min, im_max = im.min(), im.max() - im_std = (im - im_min) / (im_max - im_min) - resized_std = resize(im_std, new_dims, order=interp_order) - resized_im = resized_std * (im_max - im_min) + im_min + if im_max > im_min: + # skimage is fast but only understands {1,3} channel images in [0, 1]. + im_std = (im - im_min) / (im_max - im_min) + resized_std = resize(im_std, new_dims, order=interp_order) + resized_im = resized_std * (im_max - im_min) + im_min + else: + # the image is a constant -- avoid divide by 0 + ret = np.empty((new_dims[0], new_dims[1], im.shape[-1]), dtype=np.float32) + ret.fill(im_min) + return ret else: # ndimage interpolates anything but more slowly. scale = tuple(np.array(new_dims) / np.array(im.shape[:2])) @@ -96,76 +351,3 @@ def oversample(images, crop_dims): ix += 1 crops[ix-5:ix] = crops[ix-5:ix, :, ::-1, :] # flip for mirrors return crops - - -def blobproto_to_array(blob, return_diff=False): - """Convert a blob proto to an array. In default, we will just return the data, - unless return_diff is True, in which case we will return the diff. - """ - if return_diff: - return np.array(blob.diff).reshape( - blob.num, blob.channels, blob.height, blob.width) - else: - return np.array(blob.data).reshape( - blob.num, blob.channels, blob.height, blob.width) - - -def array_to_blobproto(arr, diff=None): - """Converts a 4-dimensional array to blob proto. If diff is given, also - convert the diff. You need to make sure that arr and diff have the same - shape, and this function does not do sanity check. - """ - if arr.ndim != 4: - raise ValueError('Incorrect array shape.') - blob = caffe_pb2.BlobProto() - blob.num, blob.channels, blob.height, blob.width = arr.shape; - blob.data.extend(arr.astype(float).flat) - if diff is not None: - blob.diff.extend(diff.astype(float).flat) - return blob - - -def arraylist_to_blobprotovecor_str(arraylist): - """Converts a list of arrays to a serialized blobprotovec, which could be - then passed to a network for processing. - """ - vec = caffe_pb2.BlobProtoVector() - vec.blobs.extend([array_to_blobproto(arr) for arr in arraylist]) - return vec.SerializeToString() - - -def blobprotovector_str_to_arraylist(str): - """Converts a serialized blobprotovec to a list of arrays. - """ - vec = caffe_pb2.BlobProtoVector() - vec.ParseFromString(str) - return [blobproto_to_array(blob) for blob in vec.blobs] - - -def array_to_datum(arr, label=0): - """Converts a 3-dimensional array to datum. If the array has dtype uint8, - the output data will be encoded as a string. Otherwise, the output data - will be stored in float format. - """ - if arr.ndim != 3: - raise ValueError('Incorrect array shape.') - datum = caffe_pb2.Datum() - datum.channels, datum.height, datum.width = arr.shape - if arr.dtype == np.uint8: - datum.data = arr.tostring() - else: - datum.float_data.extend(arr.flat) - datum.label = label - return datum - - -def datum_to_array(datum): - """Converts a datum to an array. Note that the label is not returned, - as one can easily get it by calling datum.label. - """ - if len(datum.data): - return np.fromstring(datum.data, dtype = np.uint8).reshape( - datum.channels, datum.height, datum.width) - else: - return np.array(datum.float_data).astype(float).reshape( - datum.channels, datum.height, datum.width) diff --git a/python/caffe/pycaffe.py b/python/caffe/pycaffe.py index 8899309ddfd..31c145d77a5 100644 --- a/python/caffe/pycaffe.py +++ b/python/caffe/pycaffe.py @@ -8,8 +8,6 @@ import numpy as np from ._caffe import Net, SGDSolver -from ._caffe import set_mode_cpu, set_mode_gpu, set_phase_train, set_phase_test -from ._caffe import set_device import caffe.io # We directly update methods from Net here (rather than using composition or @@ -37,6 +35,17 @@ def _Net_params(self): for name, lr in zip(self._layer_names, self.layers) if len(lr.blobs) > 0]) + +@property +def _Net_inputs(self): + return [self.blobs.keys()[i] for i in self._inputs] + + +@property +def _Net_outputs(self): + return [self.blobs.keys()[i] for i in self._outputs] + + def _Net_forward(self, blobs=None, start=None, end=None, **kwargs): """ Forward pass: prepare inputs and run the net forward. @@ -204,138 +213,6 @@ def _Net_forward_backward_all(self, blobs=None, diffs=None, **kwargs): return all_outs, all_diffs -def _Net_set_mean(self, input_, mean, mode='elementwise'): - """ - Set the mean to subtract for data centering. - - Take - input_: which input to assign this mean. - mean: mean K x H x W ndarray (input dimensional or broadcastable) - mode: elementwise = use the whole mean (and check dimensions) - channel = channel constant (e.g. mean pixel instead of mean image) - """ - if input_ not in self.inputs: - raise Exception('Input not in {}'.format(self.inputs)) - in_shape = self.blobs[input_].data.shape - if mode == 'elementwise': - if mean.shape[1:] != in_shape[2:]: - # Resize mean (which requires H x W x K input). - mean = caffe.io.resize_image(mean.transpose((1,2,0)), - in_shape[2:]).transpose((2,0,1)) - self.mean[input_] = mean - elif mode == 'channel': - self.mean[input_] = mean.mean(1).mean(1).reshape((in_shape[1], 1, 1)) - else: - raise Exception('Mode not in {}'.format(['elementwise', 'channel'])) - - -def _Net_set_input_scale(self, input_, scale): - """ - Set the scale of preprocessed inputs s.t. the blob = blob * scale. - N.B. input_scale is done AFTER mean subtraction and other preprocessing - while raw_scale is done BEFORE. - - Take - input_: which input to assign this scale factor - scale: scale coefficient - """ - if input_ not in self.inputs: - raise Exception('Input not in {}'.format(self.inputs)) - self.input_scale[input_] = scale - - -def _Net_set_raw_scale(self, input_, scale): - """ - Set the scale of raw features s.t. the input blob = input * scale. - While Python represents images in [0, 1], certain Caffe models - like CaffeNet and AlexNet represent images in [0, 255] so the raw_scale - of these models must be 255. - - Take - input_: which input to assign this scale factor - scale: scale coefficient - """ - if input_ not in self.inputs: - raise Exception('Input not in {}'.format(self.inputs)) - self.raw_scale[input_] = scale - - -def _Net_set_channel_swap(self, input_, order): - """ - Set the input channel order for e.g. RGB to BGR conversion - as needed for the reference ImageNet model. - - Take - input_: which input to assign this channel order - order: the order to take the channels. - (2,1,0) maps RGB to BGR for example. - """ - if input_ not in self.inputs: - raise Exception('Input not in {}'.format(self.inputs)) - self.channel_swap[input_] = order - - -def _Net_preprocess(self, input_name, input_): - """ - Format input for Caffe: - - convert to single - - resize to input dimensions (preserving number of channels) - - reorder channels (for instance color to BGR) - - scale raw input (e.g. from [0, 1] to [0, 255] for ImageNet models) - - transpose dimensions to K x H x W - - subtract mean - - scale feature - - Take - input_name: name of input blob to preprocess for - input_: (H' x W' x K) ndarray - - Give - caffe_inputs: (K x H x W) ndarray - """ - caffe_in = input_.astype(np.float32, copy=False) - mean = self.mean.get(input_name) - input_scale = self.input_scale.get(input_name) - raw_scale = self.raw_scale.get(input_name) - channel_order = self.channel_swap.get(input_name) - in_size = self.blobs[input_name].data.shape[2:] - if caffe_in.shape[:2] != in_size: - caffe_in = caffe.io.resize_image(caffe_in, in_size) - if channel_order is not None: - caffe_in = caffe_in[:, :, channel_order] - caffe_in = caffe_in.transpose((2, 0, 1)) - if raw_scale is not None: - caffe_in *= raw_scale - if mean is not None: - caffe_in -= mean - if input_scale is not None: - caffe_in *= input_scale - return caffe_in - - -def _Net_deprocess(self, input_name, input_): - """ - Invert Caffe formatting; see Net.preprocess(). - """ - decaf_in = input_.copy().squeeze() - mean = self.mean.get(input_name) - input_scale = self.input_scale.get(input_name) - raw_scale = self.raw_scale.get(input_name) - channel_order = self.channel_swap.get(input_name) - if input_scale is not None: - decaf_in /= input_scale - if mean is not None: - decaf_in += mean - if raw_scale is not None: - decaf_in /= raw_scale - decaf_in = decaf_in.transpose((1,2,0)) - if channel_order is not None: - channel_order_inverse = [channel_order.index(i) - for i in range(decaf_in.shape[2])] - decaf_in = decaf_in[:, :, channel_order_inverse] - return decaf_in - - def _Net_set_input_arrays(self, data, labels): """ Set input arrays of the in-memory MemoryDataLayer. @@ -378,7 +255,6 @@ def _Net_batch(self, blobs): padding]) yield padded_batch - # Attach methods to Net. Net.blobs = _Net_blobs Net.params = _Net_params @@ -386,11 +262,7 @@ def _Net_batch(self, blobs): Net.backward = _Net_backward Net.forward_all = _Net_forward_all Net.forward_backward_all = _Net_forward_backward_all -Net.set_mean = _Net_set_mean -Net.set_input_scale = _Net_set_input_scale -Net.set_raw_scale = _Net_set_raw_scale -Net.set_channel_swap = _Net_set_channel_swap -Net.preprocess = _Net_preprocess -Net.deprocess = _Net_deprocess Net.set_input_arrays = _Net_set_input_arrays Net._batch = _Net_batch +Net.inputs = _Net_inputs +Net.outputs = _Net_outputs diff --git a/python/caffe/test/test_net.py b/python/caffe/test/test_net.py new file mode 100644 index 00000000000..62b407da8aa --- /dev/null +++ b/python/caffe/test/test_net.py @@ -0,0 +1,78 @@ +import unittest +import tempfile +import os +import numpy as np + +import caffe + +def simple_net_file(num_output): + """Make a simple net prototxt, based on test_net.cpp, returning the name + of the (temporary) file.""" + + f = tempfile.NamedTemporaryFile(delete=False) + f.write("""name: 'testnet' force_backward: true + layer { type: 'DummyData' name: 'data' top: 'data' top: 'label' + dummy_data_param { num: 5 channels: 2 height: 3 width: 4 + num: 5 channels: 1 height: 1 width: 1 + data_filler { type: 'gaussian' std: 1 } + data_filler { type: 'constant' } } } + layer { type: 'Convolution' name: 'conv' bottom: 'data' top: 'conv' + convolution_param { num_output: 11 kernel_size: 2 pad: 3 + weight_filler { type: 'gaussian' std: 1 } + bias_filler { type: 'constant' value: 2 } } + param { decay_mult: 1 } param { decay_mult: 0 } + } + layer { type: 'InnerProduct' name: 'ip' bottom: 'conv' top: 'ip' + inner_product_param { num_output: """ + str(num_output) + """ + weight_filler { type: 'gaussian' std: 2.5 } + bias_filler { type: 'constant' value: -3 } } } + layer { type: 'SoftmaxWithLoss' name: 'loss' bottom: 'ip' bottom: 'label' + top: 'loss' }""") + f.close() + return f.name + +class TestNet(unittest.TestCase): + def setUp(self): + self.num_output = 13 + net_file = simple_net_file(self.num_output) + self.net = caffe.Net(net_file, caffe.TRAIN) + # fill in valid labels + self.net.blobs['label'].data[...] = \ + np.random.randint(self.num_output, + size=self.net.blobs['label'].data.shape) + os.remove(net_file) + + def test_memory(self): + """Check that holding onto blob data beyond the life of a Net is OK""" + + params = sum(map(list, self.net.params.itervalues()), []) + blobs = self.net.blobs.values() + del self.net + + # now sum everything (forcing all memory to be read) + total = 0 + for p in params: + total += p.data.sum() + p.diff.sum() + for bl in blobs: + total += bl.data.sum() + bl.diff.sum() + + def test_forward_backward(self): + self.net.forward() + self.net.backward() + + def test_inputs_outputs(self): + self.assertEqual(self.net.inputs, []) + self.assertEqual(self.net.outputs, ['loss']) + + def test_save_and_read(self): + f = tempfile.NamedTemporaryFile(delete=False) + f.close() + self.net.save(f.name) + net_file = simple_net_file(self.num_output) + net2 = caffe.Net(net_file, f.name, caffe.TRAIN) + os.remove(net_file) + os.remove(f.name) + for name in self.net.params: + for i in range(len(self.net.params[name])): + self.assertEqual(abs(self.net.params[name][i].data + - net2.params[name][i].data).sum(), 0) diff --git a/python/caffe/test/test_python_layer.py b/python/caffe/test/test_python_layer.py new file mode 100644 index 00000000000..383c283959d --- /dev/null +++ b/python/caffe/test/test_python_layer.py @@ -0,0 +1,62 @@ +import unittest +import tempfile +import os + +import caffe + +class SimpleLayer(caffe.Layer): + """A layer that just multiplies by ten""" + + def setup(self, bottom, top): + pass + + def reshape(self, bottom, top): + top[0].reshape(bottom[0].num, bottom[0].channels, bottom[0].height, + bottom[0].width) + + def forward(self, bottom, top): + top[0].data[...] = 10 * bottom[0].data + + def backward(self, top, propagate_down, bottom): + bottom[0].diff[...] = 10 * top[0].diff + +def python_net_file(): + f = tempfile.NamedTemporaryFile(delete=False) + f.write("""name: 'pythonnet' force_backward: true + input: 'data' input_dim: 10 input_dim: 9 input_dim: 8 input_dim: 7 + layer { type: 'Python' name: 'one' bottom: 'data' top: 'one' + python_param { module: 'test_python_layer' layer: 'SimpleLayer' } } + layer { type: 'Python' name: 'two' bottom: 'one' top: 'two' + python_param { module: 'test_python_layer' layer: 'SimpleLayer' } } + layer { type: 'Python' name: 'three' bottom: 'two' top: 'three' + python_param { module: 'test_python_layer' layer: 'SimpleLayer' } }""") + f.close() + return f.name + +class TestPythonLayer(unittest.TestCase): + def setUp(self): + net_file = python_net_file() + self.net = caffe.Net(net_file, caffe.TRAIN) + os.remove(net_file) + + def test_forward(self): + x = 8 + self.net.blobs['data'].data[...] = x + self.net.forward() + for y in self.net.blobs['three'].data.flat: + self.assertEqual(y, 10**3 * x) + + def test_backward(self): + x = 7 + self.net.blobs['three'].diff[...] = x + self.net.backward() + for y in self.net.blobs['data'].diff.flat: + self.assertEqual(y, 10**3 * x) + + def test_reshape(self): + s = 4 + self.net.blobs['data'].reshape(s, s, s, s) + self.net.forward() + for blob in self.net.blobs.itervalues(): + for d in blob.data.shape: + self.assertEqual(s, d) diff --git a/python/caffe/test/test_solver.py b/python/caffe/test/test_solver.py new file mode 100644 index 00000000000..d59f23d973a --- /dev/null +++ b/python/caffe/test/test_solver.py @@ -0,0 +1,51 @@ +import unittest +import tempfile +import os +import numpy as np + +import caffe +from test_net import simple_net_file + +class TestSolver(unittest.TestCase): + def setUp(self): + self.num_output = 13 + net_f = simple_net_file(self.num_output) + f = tempfile.NamedTemporaryFile(delete=False) + f.write("""net: '""" + net_f + """' + test_iter: 10 test_interval: 10 base_lr: 0.01 momentum: 0.9 + weight_decay: 0.0005 lr_policy: 'inv' gamma: 0.0001 power: 0.75 + display: 100 max_iter: 100 snapshot_after_train: false""") + f.close() + self.solver = caffe.SGDSolver(f.name) + # also make sure get_solver runs + caffe.get_solver(f.name) + caffe.set_mode_cpu() + # fill in valid labels + self.solver.net.blobs['label'].data[...] = \ + np.random.randint(self.num_output, + size=self.solver.net.blobs['label'].data.shape) + self.solver.test_nets[0].blobs['label'].data[...] = \ + np.random.randint(self.num_output, + size=self.solver.test_nets[0].blobs['label'].data.shape) + os.remove(f.name) + os.remove(net_f) + + def test_solve(self): + self.assertEqual(self.solver.iter, 0) + self.solver.solve() + self.assertEqual(self.solver.iter, 100) + + def test_net_memory(self): + """Check that nets survive after the solver is destroyed.""" + + nets = [self.solver.net] + list(self.solver.test_nets) + self.assertEqual(len(nets), 2) + del self.solver + + total = 0 + for net in nets: + for ps in net.params.itervalues(): + for p in ps: + total += p.data.sum() + p.diff.sum() + for bl in net.blobs.itervalues(): + total += bl.data.sum() + bl.diff.sum() diff --git a/python/classify.py b/python/classify.py index 873b5e38f19..d435a572266 100755 --- a/python/classify.py +++ b/python/classify.py @@ -60,8 +60,8 @@ def main(argv): "--mean_file", default=os.path.join(pycaffe_dir, 'caffe/imagenet/ilsvrc_2012_mean.npy'), - help="Data set image mean of H x W x K dimensions (numpy array). " + - "Set to '' for no mean subtraction." + help="Data set image mean of [Channels x Height x Width] dimensions " + + "(numpy array). Set to '' for no mean subtraction." ) parser.add_argument( "--input_scale", diff --git a/python/detect.py b/python/detect.py index b67b500aafd..cb0c2645761 100755 --- a/python/detect.py +++ b/python/detect.py @@ -102,6 +102,8 @@ def main(argv): mean, channel_swap = None, None if args.mean_file: mean = np.load(args.mean_file) + if mean.shape[1:] != (1, 1): + mean = mean.mean(1).mean(1) if args.channel_swap: channel_swap = [int(s) for s in args.channel_swap.split(',')] diff --git a/python/draw_net.py b/python/draw_net.py index abf701572a2..4457b793e86 100755 --- a/python/draw_net.py +++ b/python/draw_net.py @@ -2,24 +2,43 @@ """ Draw a graph of the net architecture. """ -import os +import argparse from google.protobuf import text_format -import caffe, caffe.draw +import caffe +import caffe.draw from caffe.proto import caffe_pb2 -def main(argv): - if len(argv) != 3: - print 'Usage: %s input_net_prototxt output_image_file' % \ - os.path.basename(sys.argv[0]) - else: - net = caffe_pb2.NetParameter() - text_format.Merge(open(sys.argv[1]).read(), net) - print 'Drawing net to %s' % sys.argv[2] - caffe.draw.draw_net_to_file(net, sys.argv[2]) +def parse_args(): + """Parse input arguments + """ + + parser = argparse.ArgumentParser(description='Draw a network graph') + + parser.add_argument('input_net_proto_file', + help='Input network prototxt file') + parser.add_argument('output_image_file', + help='Output image file') + parser.add_argument('--rankdir', + help=('One of TB (top-bottom, i.e., vertical), ' + 'RL (right-left, i.e., horizontal), or another' + 'valid dot option; see' + 'http://www.graphviz.org/doc/info/attrs.html#k:rankdir' + '(default: LR)'), + default='LR') + + args = parser.parse_args() + return args + + +def main(): + args = parse_args() + net = caffe_pb2.NetParameter() + text_format.Merge(open(args.input_net_proto_file).read(), net) + print 'Drawing net to %s' % args.output_image_file + caffe.draw.draw_net_to_file(net, args.output_image_file, args.rankdir) if __name__ == '__main__': - import sys - main(sys.argv) + main() diff --git a/scripts/travis/travis_build_and_test.sh b/scripts/travis/travis_build_and_test.sh index dec4d097c17..8ff63f31fdd 100755 --- a/scripts/travis/travis_build_and_test.sh +++ b/scripts/travis/travis_build_and_test.sh @@ -7,7 +7,7 @@ MAKE="make --jobs=$NUM_THREADS --keep-going" if $WITH_CMAKE; then mkdir build cd build - cmake -DBUILD_PYTHON=ON -DBUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=Release -DCPU_ONLY=ON .. + cmake -DBUILD_python=ON -DCMAKE_BUILD_TYPE=Release -DCPU_ONLY=ON .. $MAKE if ! $WITH_CUDA; then $MAKE runtest @@ -26,6 +26,7 @@ else $MAKE all $MAKE test $MAKE pycaffe + $MAKE pytest $MAKE warn if ! $WITH_CUDA; then $MAKE lint diff --git a/scripts/travis/travis_install.sh b/scripts/travis/travis_install.sh index e17f253ecdc..82f386cf029 100755 --- a/scripts/travis/travis_install.sh +++ b/scripts/travis/travis_install.sh @@ -57,3 +57,14 @@ $MAKE $MAKE install popd rm -f $LMDB_FILE + +# Install the Python runtime dependencies via miniconda (this is much faster +# than using pip for everything). +wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh +chmod +x miniconda.sh +./miniconda.sh -b +export PATH=/home/travis/miniconda/bin:$PATH +conda update --yes conda +conda install --yes numpy scipy matplotlib scikit-image pip +pip install protobuf +rm /home/travis/miniconda/lib/libm.* diff --git a/scripts/travis/travis_setup_makefile_config.sh b/scripts/travis/travis_setup_makefile_config.sh index a309bb3d6fe..ba326262bf8 100755 --- a/scripts/travis/travis_setup_makefile_config.sh +++ b/scripts/travis/travis_setup_makefile_config.sh @@ -5,10 +5,19 @@ set -e mv Makefile.config.example Makefile.config if $WITH_CUDA; then - # Remove default gencode set; only generate compute_50. - sed -i 's/-gencode arch=.*\\//' Makefile.config - sed -i 's/CUDA_ARCH :=//' Makefile.config + # Only generate compute_50. GENCODE="-gencode arch=compute_50,code=sm_50" GENCODE="$GENCODE -gencode arch=compute_50,code=compute_50" echo "CUDA_ARCH := $GENCODE" >> Makefile.config fi + +cat << 'EOF' >> Makefile.config +ANACONDA_HOME := $(HOME)/miniconda +PYTHON_INCLUDE := $(ANACONDA_HOME)/include \ + $(ANACONDA_HOME)/include/python2.7 \ + $(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include +PYTHON_LIB := $(ANACONDA_HOME)/lib +INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include +LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib +WITH_PYTHON_LAYER := 1 +EOF diff --git a/scripts/upload_model_to_gist.sh b/scripts/upload_model_to_gist.sh index 2dfbabd72a3..3c4fd64e3fc 100755 --- a/scripts/upload_model_to_gist.sh +++ b/scripts/upload_model_to_gist.sh @@ -7,7 +7,7 @@ if [ ! -f $DIRNAME/readme.md ]; then echo " /readme.md must exist" fi cd $DIRNAME -FILES=`find . -type f -maxdepth 1 ! -name "*.caffemodel*" | xargs echo` +FILES=`find . -maxdepth 1 -type f ! -name "*.caffemodel*" | xargs echo` # Check for gist tool. gist -v >/dev/null 2>&1 || { echo >&2 "I require 'gist' but it's not installed. Do 'gem install gist'."; exit 1; } diff --git a/src/caffe/CMakeLists.txt b/src/caffe/CMakeLists.txt index dc6b5a2baac..40e6c11f5b0 100644 --- a/src/caffe/CMakeLists.txt +++ b/src/caffe/CMakeLists.txt @@ -1,135 +1,36 @@ -project( CaffeSrc ) +# generate protobuf sources +file(GLOB proto_files proto/*.proto) +caffe_protobuf_generate_cpp_py(${proto_gen_folder} proto_srcs proto_hdrs proto_python ${proto_files}) -# Threads -find_package(Threads REQUIRED) +# include python files either to force generation +add_library(proto STATIC ${proto_hdrs} ${proto_srcs} ${proto_python}) +set(Caffe_LINKER_LIBS proto ${Caffe_LINKER_LIBS}) # note, crucial to prepend! +caffe_default_properties(proto) -# Google-glog -find_package(Glog REQUIRED) -include_directories(${GLOG_INCLUDE_DIRS}) +# --[ Caffe library -# Google-gflags -find_package(GFlags REQUIRED) -include_directories(${GFLAGS_INCLUDE_DIRS}) +# creates 'test_srcs', 'srcs', 'test_cuda', 'cuda' lists +caffe_pickup_caffe_sources(${PROJECT_SOURCE_DIR}) -# BLAS -if(BLAS STREQUAL "atlas") - - find_package(Atlas REQUIRED) - include_directories(${Atlas_INCLUDE_DIR}) - set(BLAS_LIBRARIES ${Atlas_LIBRARIES}) - -elseif(BLAS STREQUAL "open") - - find_package(OpenBLAS REQUIRED) - include_directories(${OpenBLAS_INCLUDE_DIR}) - set(BLAS_LIBRARIES ${OpenBLAS_LIB}) - -elseif(BLAS STREQUAL "mkl") - - find_package(MKL REQUIRED) - include_directories(${MKL_INCLUDE_DIR}) - set(BLAS_LIBRARIES ${MKL_LIBRARIES}) - -endif() - -# HDF5 -find_package(HDF5 COMPONENTS HL REQUIRED) -include_directories(${HDF5_INCLUDE_DIRS}) - -# OpenCV -find_package(OpenCV REQUIRED core highgui imgproc) -include_directories(${OpenCV_INCLUDE_DIRS}) - -# LevelDB -find_package(LevelDB REQUIRED) -include_directories(${LEVELDB_INCLUDE}) -if(LEVELDB_FOUND) - find_package(Snappy REQUIRED) - include_directories(${SNAPPY_INCLUDE_DIR}) - set(LEVELDB_LIBS - ${LEVELDB_LIBS} - ${SNAPPY_LIBS} - ) +if(HAVE_CUDA) + caffe_cuda_compile(cuda_objs ${cuda}) + list(APPEND srcs ${cuda_objs} ${cuda}) endif() -# LMDB -find_package(LMDB REQUIRED) -include_directories(${LMDB_INCLUDE_DIR}) - -# Boost -find_package(Boost 1.46 COMPONENTS system thread REQUIRED) -include_directories( ${Boost_INCLUDE_DIR} ) -link_directories( ${Boost_LIBRARY_DIRS} ) - -add_subdirectory(proto) - -# Recursively find source files -## test sources -file(GLOB_RECURSE TEST_CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_*.cpp) - -## all cpp sources -file(GLOB_RECURSE CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) - -## remove test sources from cpp sources -list(REMOVE_ITEM CPP_SOURCES ${TEST_CPP_SOURCES}) - -add_library(caffe ${CPP_SOURCES}) -# both depend on proto -add_dependencies(caffe proto) - -# CUDA -if(NOT CPU_ONLY) - set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} - -gencode arch=compute_20,code=sm_20 - -gencode arch=compute_20,code=sm_21 - -gencode arch=compute_30,code=sm_30 - -gencode arch=compute_35,code=sm_35 - -gencode arch=compute_50,code=sm_50 - -gencode arch=compute_50,code=compute_50 - ) - -# https://github.com/ComputationalRadiationPhysics/picongpu/blob/master/src/picongpu/CMakeLists.txt - # work-arounds -if(Boost_VERSION EQUAL 105500) - # see https://svn.boost.org/trac/boost/ticket/9392 - message(STATUS "Boost: Applying noinline work around") - # avoid warning for CMake >= 2.8.12 - set(CUDA_NVCC_FLAGS - "${CUDA_NVCC_FLAGS} \"-DBOOST_NOINLINE=__attribute__((noinline))\" ") -endif(Boost_VERSION EQUAL 105500) - - # cuda sources - file(GLOB_RECURSE CU_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cu) - file(GLOB_RECURSE TEST_CU_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_*.cu) - list(REMOVE_ITEM CU_SOURCES ${TEST_CU_SOURCES}) - cuda_add_library(caffe_cu ${CU_SOURCES}) - add_dependencies(caffe_cu proto) - target_link_libraries(caffe caffe_cu - ${CUDA_CUBLAS_LIBRARIES} - ${CUDA_curand_LIBRARY} - ) -endif() +add_library(caffe ${srcs}) +target_link_libraries(caffe proto ${Caffe_LINKER_LIBS}) +caffe_default_properties(caffe) -target_link_libraries(caffe proto - ${BLAS_LIBRARIES} - ${Boost_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} - ${GFLAGS_LIBRARIES} - ${GLOG_LIBRARIES} - ${HDF5_LIBRARIES} - ${LEVELDB_LIBS} - ${LMDB_LIBRARIES} - ${OpenCV_LIBS} -) +# ---[ Tests + add_subdirectory(test) -#set output directory -set_target_properties(caffe PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib -) +# ---[ Install +install(DIRECTORY ${Caffe_INCLUDE_DIR}/caffe DESTINATION include) +install(FILES ${proto_hdrs} DESTINATION include/caffe/proto) +install(TARGETS caffe proto EXPORT CaffeTargets DESTINATION lib) -add_subdirectory(test) +file(WRITE ${PROJECT_BINARY_DIR}/__init__.py) +list(APPEND proto_python ${PROJECT_BINARY_DIR}/__init__.py) +install(PROGRAMS ${proto_python} DESTINATION python/caffe/proto) -### Install ################################################################################# -install(TARGETS caffe DESTINATION lib) diff --git a/src/caffe/blob.cpp b/src/caffe/blob.cpp index cfffc379eb1..fbc1361a19d 100644 --- a/src/caffe/blob.cpp +++ b/src/caffe/blob.cpp @@ -205,6 +205,146 @@ Dtype Blob::asum_diff() const { return 0; } +template <> unsigned int Blob::sumsq_data() const { + NOT_IMPLEMENTED; + return 0; +} + +template <> int Blob::sumsq_data() const { + NOT_IMPLEMENTED; + return 0; +} + +template +Dtype Blob::sumsq_data() const { + Dtype sumsq; + const Dtype* data; + if (!data_) { return 0; } + switch (data_->head()) { + case SyncedMemory::HEAD_AT_CPU: + data = cpu_data(); + sumsq = caffe_cpu_dot(count_, data, data); + break; + case SyncedMemory::HEAD_AT_GPU: + case SyncedMemory::SYNCED: +#ifndef CPU_ONLY + data = gpu_data(); + caffe_gpu_dot(count_, data, data, &sumsq); +#else + NO_GPU; +#endif + break; + case SyncedMemory::UNINITIALIZED: + return 0; + default: + LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head(); + } + return sumsq; +} + +template <> unsigned int Blob::sumsq_diff() const { + NOT_IMPLEMENTED; + return 0; +} + +template <> int Blob::sumsq_diff() const { + NOT_IMPLEMENTED; + return 0; +} + +template +Dtype Blob::sumsq_diff() const { + Dtype sumsq; + const Dtype* diff; + if (!diff_) { return 0; } + switch (diff_->head()) { + case SyncedMemory::HEAD_AT_CPU: + diff = cpu_diff(); + sumsq = caffe_cpu_dot(count_, diff, diff); + break; + case SyncedMemory::HEAD_AT_GPU: + case SyncedMemory::SYNCED: +#ifndef CPU_ONLY + diff = gpu_diff(); + caffe_gpu_dot(count_, diff, diff, &sumsq); + break; +#else + NO_GPU; +#endif + case SyncedMemory::UNINITIALIZED: + return 0; + default: + LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head(); + } + return sumsq; +} + +template <> void Blob::scale_data(unsigned int scale_factor) { + NOT_IMPLEMENTED; +} + +template <> void Blob::scale_data(int scale_factor) { + NOT_IMPLEMENTED; +} + +template +void Blob::scale_data(Dtype scale_factor) { + Dtype* data; + if (!data_) { return; } + switch (data_->head()) { + case SyncedMemory::HEAD_AT_CPU: + data = mutable_cpu_data(); + caffe_scal(count_, scale_factor, data); + return; + case SyncedMemory::HEAD_AT_GPU: + case SyncedMemory::SYNCED: +#ifndef CPU_ONLY + data = mutable_gpu_data(); + caffe_gpu_scal(count_, scale_factor, data); + return; +#else + NO_GPU; +#endif + case SyncedMemory::UNINITIALIZED: + return; + default: + LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head(); + } +} + +template <> void Blob::scale_diff(unsigned int scale_factor) { + NOT_IMPLEMENTED; +} + +template <> void Blob::scale_diff(int scale_factor) { + NOT_IMPLEMENTED; +} + +template +void Blob::scale_diff(Dtype scale_factor) { + Dtype* diff; + if (!diff_) { return; } + switch (diff_->head()) { + case SyncedMemory::HEAD_AT_CPU: + diff = mutable_cpu_diff(); + caffe_scal(count_, scale_factor, diff); + return; + case SyncedMemory::HEAD_AT_GPU: + case SyncedMemory::SYNCED: +#ifndef CPU_ONLY + diff = mutable_gpu_diff(); + caffe_gpu_scal(count_, scale_factor, diff); + return; +#else + NO_GPU; +#endif + case SyncedMemory::UNINITIALIZED: + return; + default: + LOG(FATAL) << "Unknown SyncedMemory head state: " << diff_->head(); + } +} + template void Blob::CopyFrom(const Blob& source, bool copy_diff, bool reshape) { if (num_ != source.num() || channels_ != source.channels() || diff --git a/src/caffe/common.cpp b/src/caffe/common.cpp index 94fdf924f44..af96cac40aa 100644 --- a/src/caffe/common.cpp +++ b/src/caffe/common.cpp @@ -35,12 +35,14 @@ void GlobalInit(int* pargc, char*** pargv) { ::gflags::ParseCommandLineFlags(pargc, pargv, true); // Google logging. ::google::InitGoogleLogging(*(pargv)[0]); + // Provide a backtrace on segfault. + ::google::InstallFailureSignalHandler(); } #ifdef CPU_ONLY // CPU-only Caffe. Caffe::Caffe() - : random_generator_(), mode_(Caffe::CPU), phase_(Caffe::TRAIN) { } + : random_generator_(), mode_(Caffe::CPU) { } Caffe::~Caffe() { } @@ -84,7 +86,7 @@ void* Caffe::RNG::generator() { Caffe::Caffe() : cublas_handle_(NULL), curand_generator_(NULL), random_generator_(), - mode_(Caffe::CPU), phase_(Caffe::TRAIN) { + mode_(Caffe::CPU) { // Try to create a cublas handler, and report an error if failed (but we will // keep the program running as one might just want to run CPU code). if (cublasCreate(&cublas_handle_) != CUBLAS_STATUS_SUCCESS) { diff --git a/src/caffe/data_transformer.cpp b/src/caffe/data_transformer.cpp index 7150fd99c18..b0b98e478c1 100644 --- a/src/caffe/data_transformer.cpp +++ b/src/caffe/data_transformer.cpp @@ -1,95 +1,404 @@ +#include + #include +#include #include "caffe/data_transformer.hpp" +#include "caffe/util/io.hpp" #include "caffe/util/math_functions.hpp" #include "caffe/util/rng.hpp" namespace caffe { template -void DataTransformer::Transform(const int batch_item_id, - const Datum& datum, - const Dtype* mean, +DataTransformer::DataTransformer(const TransformationParameter& param, + Phase phase) + : param_(param), phase_(phase) { + // check if we want to use mean_file + if (param_.has_mean_file()) { + CHECK_EQ(param_.mean_value_size(), 0) << + "Cannot specify mean_file and mean_value at the same time"; + const string& mean_file = param.mean_file(); + LOG(INFO) << "Loading mean file from: " << mean_file; + BlobProto blob_proto; + ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto); + data_mean_.FromProto(blob_proto); + } + // check if we want to use mean_value + if (param_.mean_value_size() > 0) { + CHECK(param_.has_mean_file() == false) << + "Cannot specify mean_file and mean_value at the same time"; + for (int c = 0; c < param_.mean_value_size(); ++c) { + mean_values_.push_back(param_.mean_value(c)); + } + } +} + +template +void DataTransformer::Transform(const Datum& datum, Dtype* transformed_data) { const string& data = datum.data(); - const int channels = datum.channels(); - const int height = datum.height(); - const int width = datum.width(); - const int size = datum.channels() * datum.height() * datum.width(); + const int datum_channels = datum.channels(); + const int datum_height = datum.height(); + const int datum_width = datum.width(); const int crop_size = param_.crop_size(); - const bool mirror = param_.mirror(); const Dtype scale = param_.scale(); + const bool do_mirror = param_.mirror() && Rand(2); + const bool has_mean_file = param_.has_mean_file(); + const bool has_uint8 = data.size() > 0; + const bool has_mean_values = mean_values_.size() > 0; + + CHECK_GT(datum_channels, 0); + CHECK_GE(datum_height, crop_size); + CHECK_GE(datum_width, crop_size); - if (mirror && crop_size == 0) { - LOG(FATAL) << "Current implementation requires mirror and crop_size to be " - << "set at the same time."; + Dtype* mean = NULL; + if (has_mean_file) { + CHECK_EQ(datum_channels, data_mean_.channels()); + CHECK_EQ(datum_height, data_mean_.height()); + CHECK_EQ(datum_width, data_mean_.width()); + mean = data_mean_.mutable_cpu_data(); + } + if (has_mean_values) { + CHECK(mean_values_.size() == 1 || mean_values_.size() == datum_channels) << + "Specify either 1 mean_value or as many as channels: " << datum_channels; + if (datum_channels > 1 && mean_values_.size() == 1) { + // Replicate the mean_value for simplicity + for (int c = 1; c < datum_channels; ++c) { + mean_values_.push_back(mean_values_[0]); + } + } } + int height = datum_height; + int width = datum_width; + + int h_off = 0; + int w_off = 0; if (crop_size) { - CHECK(data.size()) << "Image cropping only support uint8 data"; - int h_off, w_off; + height = crop_size; + width = crop_size; // We only do random crop when we do training. - if (phase_ == Caffe::TRAIN) { - h_off = Rand() % (height - crop_size); - w_off = Rand() % (width - crop_size); + if (phase_ == TRAIN) { + h_off = Rand(datum_height - crop_size + 1); + w_off = Rand(datum_width - crop_size + 1); } else { - h_off = (height - crop_size) / 2; - w_off = (width - crop_size) / 2; + h_off = (datum_height - crop_size) / 2; + w_off = (datum_width - crop_size) / 2; } - if (mirror && Rand() % 2) { - // Copy mirrored version - for (int c = 0; c < channels; ++c) { - for (int h = 0; h < crop_size; ++h) { - for (int w = 0; w < crop_size; ++w) { - int data_index = (c * height + h + h_off) * width + w + w_off; - int top_index = ((batch_item_id * channels + c) * crop_size + h) - * crop_size + (crop_size - 1 - w); - Dtype datum_element = - static_cast(static_cast(data[data_index])); + } + + Dtype datum_element; + int top_index, data_index; + for (int c = 0; c < datum_channels; ++c) { + for (int h = 0; h < height; ++h) { + for (int w = 0; w < width; ++w) { + data_index = (c * datum_height + h_off + h) * datum_width + w_off + w; + if (do_mirror) { + top_index = (c * height + h) * width + (width - 1 - w); + } else { + top_index = (c * height + h) * width + w; + } + if (has_uint8) { + datum_element = + static_cast(static_cast(data[data_index])); + } else { + datum_element = datum.float_data(data_index); + } + if (has_mean_file) { + transformed_data[top_index] = + (datum_element - mean[data_index]) * scale; + } else { + if (has_mean_values) { transformed_data[top_index] = - (datum_element - mean[data_index]) * scale; + (datum_element - mean_values_[c]) * scale; + } else { + transformed_data[top_index] = datum_element * scale; } } } + } + } +} + +template +void DataTransformer::Transform(const Datum& datum, + Blob* transformed_blob) { + const int datum_channels = datum.channels(); + const int datum_height = datum.height(); + const int datum_width = datum.width(); + + const int channels = transformed_blob->channels(); + const int height = transformed_blob->height(); + const int width = transformed_blob->width(); + const int num = transformed_blob->num(); + + CHECK_EQ(channels, datum_channels); + CHECK_LE(height, datum_height); + CHECK_LE(width, datum_width); + CHECK_GE(num, 1); + + const int crop_size = param_.crop_size(); + + if (crop_size) { + CHECK_EQ(crop_size, height); + CHECK_EQ(crop_size, width); + } else { + CHECK_EQ(datum_height, height); + CHECK_EQ(datum_width, width); + } + + Dtype* transformed_data = transformed_blob->mutable_cpu_data(); + Transform(datum, transformed_data); +} + +template +void DataTransformer::Transform(const vector & datum_vector, + Blob* transformed_blob) { + const int datum_num = datum_vector.size(); + const int num = transformed_blob->num(); + const int channels = transformed_blob->channels(); + const int height = transformed_blob->height(); + const int width = transformed_blob->width(); + + CHECK_GT(datum_num, 0) << "There is no datum to add"; + CHECK_LE(datum_num, num) << + "The size of datum_vector must be no greater than transformed_blob->num()"; + Blob uni_blob(1, channels, height, width); + for (int item_id = 0; item_id < datum_num; ++item_id) { + int offset = transformed_blob->offset(item_id); + uni_blob.set_cpu_data(transformed_blob->mutable_cpu_data() + offset); + Transform(datum_vector[item_id], &uni_blob); + } +} + +template +void DataTransformer::Transform(const vector & mat_vector, + Blob* transformed_blob) { + const int mat_num = mat_vector.size(); + const int num = transformed_blob->num(); + const int channels = transformed_blob->channels(); + const int height = transformed_blob->height(); + const int width = transformed_blob->width(); + + CHECK_GT(mat_num, 0) << "There is no MAT to add"; + CHECK_EQ(mat_num, num) << + "The size of mat_vector must be equals to transformed_blob->num()"; + Blob uni_blob(1, channels, height, width); + for (int item_id = 0; item_id < mat_num; ++item_id) { + int offset = transformed_blob->offset(item_id); + uni_blob.set_cpu_data(transformed_blob->mutable_cpu_data() + offset); + Transform(mat_vector[item_id], &uni_blob); + } +} + +template +void DataTransformer::Transform(const cv::Mat& cv_img, + Blob* transformed_blob) { + const int img_channels = cv_img.channels(); + const int img_height = cv_img.rows; + const int img_width = cv_img.cols; + + const int channels = transformed_blob->channels(); + const int height = transformed_blob->height(); + const int width = transformed_blob->width(); + const int num = transformed_blob->num(); + + CHECK_EQ(channels, img_channels); + CHECK_LE(height, img_height); + CHECK_LE(width, img_width); + CHECK_GE(num, 1); + + CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte"; + + const int crop_size = param_.crop_size(); + const Dtype scale = param_.scale(); + const bool do_mirror = param_.mirror() && Rand(2); + const bool has_mean_file = param_.has_mean_file(); + const bool has_mean_values = mean_values_.size() > 0; + + CHECK_GT(img_channels, 0); + CHECK_GE(img_height, crop_size); + CHECK_GE(img_width, crop_size); + + Dtype* mean = NULL; + if (has_mean_file) { + CHECK_EQ(img_channels, data_mean_.channels()); + CHECK_EQ(img_height, data_mean_.height()); + CHECK_EQ(img_width, data_mean_.width()); + mean = data_mean_.mutable_cpu_data(); + } + if (has_mean_values) { + CHECK(mean_values_.size() == 1 || mean_values_.size() == img_channels) << + "Specify either 1 mean_value or as many as channels: " << img_channels; + if (img_channels > 1 && mean_values_.size() == 1) { + // Replicate the mean_value for simplicity + for (int c = 1; c < img_channels; ++c) { + mean_values_.push_back(mean_values_[0]); + } + } + } + + int h_off = 0; + int w_off = 0; + cv::Mat cv_cropped_img = cv_img; + if (crop_size) { + CHECK_EQ(crop_size, height); + CHECK_EQ(crop_size, width); + // We only do random crop when we do training. + if (phase_ == TRAIN) { + h_off = Rand(img_height - crop_size + 1); + w_off = Rand(img_width - crop_size + 1); } else { - // Normal copy - for (int c = 0; c < channels; ++c) { - for (int h = 0; h < crop_size; ++h) { - for (int w = 0; w < crop_size; ++w) { - int top_index = ((batch_item_id * channels + c) * crop_size + h) - * crop_size + w; - int data_index = (c * height + h + h_off) * width + w + w_off; - Dtype datum_element = - static_cast(static_cast(data[data_index])); + h_off = (img_height - crop_size) / 2; + w_off = (img_width - crop_size) / 2; + } + cv::Rect roi(w_off, h_off, crop_size, crop_size); + cv_cropped_img = cv_img(roi); + } else { + CHECK_EQ(img_height, height); + CHECK_EQ(img_width, width); + } + + CHECK(cv_cropped_img.data); + + Dtype* transformed_data = transformed_blob->mutable_cpu_data(); + int top_index; + for (int h = 0; h < height; ++h) { + const uchar* ptr = cv_cropped_img.ptr(h); + int img_index = 0; + for (int w = 0; w < width; ++w) { + for (int c = 0; c < img_channels; ++c) { + if (do_mirror) { + top_index = (c * height + h) * width + (width - 1 - w); + } else { + top_index = (c * height + h) * width + w; + } + // int top_index = (c * height + h) * width + w; + Dtype pixel = static_cast(ptr[img_index++]); + if (has_mean_file) { + int mean_index = (c * img_height + h_off + h) * img_width + w_off + w; + transformed_data[top_index] = + (pixel - mean[mean_index]) * scale; + } else { + if (has_mean_values) { transformed_data[top_index] = - (datum_element - mean[data_index]) * scale; + (pixel - mean_values_[c]) * scale; + } else { + transformed_data[top_index] = pixel * scale; } } } } + } +} + +template +void DataTransformer::Transform(Blob* input_blob, + Blob* transformed_blob) { + const int input_num = input_blob->num(); + const int input_channels = input_blob->channels(); + const int input_height = input_blob->height(); + const int input_width = input_blob->width(); + + const int num = transformed_blob->num(); + const int channels = transformed_blob->channels(); + const int height = transformed_blob->height(); + const int width = transformed_blob->width(); + const int size = transformed_blob->count(); + + CHECK_LE(input_num, num); + CHECK_EQ(input_channels, channels); + CHECK_GE(input_height, height); + CHECK_GE(input_width, width); + + const int crop_size = param_.crop_size(); + const Dtype scale = param_.scale(); + const bool do_mirror = param_.mirror() && Rand(2); + const bool has_mean_file = param_.has_mean_file(); + const bool has_mean_values = mean_values_.size() > 0; + + int h_off = 0; + int w_off = 0; + if (crop_size) { + CHECK_EQ(crop_size, height); + CHECK_EQ(crop_size, width); + // We only do random crop when we do training. + if (phase_ == TRAIN) { + h_off = Rand(input_height - crop_size + 1); + w_off = Rand(input_width - crop_size + 1); + } else { + h_off = (input_height - crop_size) / 2; + w_off = (input_width - crop_size) / 2; + } } else { - // we will prefer to use data() first, and then try float_data() - if (data.size()) { - for (int j = 0; j < size; ++j) { - Dtype datum_element = - static_cast(static_cast(data[j])); - transformed_data[j + batch_item_id * size] = - (datum_element - mean[j]) * scale; - } + CHECK_EQ(input_height, height); + CHECK_EQ(input_width, width); + } + + Dtype* input_data = input_blob->mutable_cpu_data(); + if (has_mean_file) { + CHECK_EQ(input_channels, data_mean_.channels()); + CHECK_EQ(input_height, data_mean_.height()); + CHECK_EQ(input_width, data_mean_.width()); + for (int n = 0; n < input_num; ++n) { + int offset = input_blob->offset(n); + caffe_sub(data_mean_.count(), input_data + offset, + data_mean_.cpu_data(), input_data + offset); + } + } + + if (has_mean_values) { + CHECK(mean_values_.size() == 1 || mean_values_.size() == input_channels) << + "Specify either 1 mean_value or as many as channels: " << input_channels; + if (mean_values_.size() == 1) { + caffe_add_scalar(input_blob->count(), -(mean_values_[0]), input_data); } else { - for (int j = 0; j < size; ++j) { - transformed_data[j + batch_item_id * size] = - (datum.float_data(j) - mean[j]) * scale; + for (int n = 0; n < input_num; ++n) { + for (int c = 0; c < input_channels; ++c) { + int offset = input_blob->offset(n, c); + caffe_add_scalar(input_height * input_width, -(mean_values_[c]), + input_data + offset); + } } } } + + Dtype* transformed_data = transformed_blob->mutable_cpu_data(); + + for (int n = 0; n < input_num; ++n) { + int top_index_n = n * channels; + int data_index_n = n * channels; + for (int c = 0; c < channels; ++c) { + int top_index_c = (top_index_n + c) * height; + int data_index_c = (data_index_n + c) * input_height + h_off; + for (int h = 0; h < height; ++h) { + int top_index_h = (top_index_c + h) * width; + int data_index_h = (data_index_c + h) * input_width + w_off; + if (do_mirror) { + int top_index_w = top_index_h + width - 1; + for (int w = 0; w < width; ++w) { + transformed_data[top_index_w-w] = input_data[data_index_h + w]; + } + } else { + for (int w = 0; w < width; ++w) { + transformed_data[top_index_h + w] = input_data[data_index_h + w]; + } + } + } + } + } + if (scale != Dtype(1)) { + DLOG(INFO) << "Scale: " << scale; + caffe_scal(size, scale, transformed_data); + } } template void DataTransformer::InitRand() { - const bool needs_rand = (phase_ == Caffe::TRAIN) && - (param_.mirror() || param_.crop_size()); + const bool needs_rand = param_.mirror() || + (phase_ == TRAIN && param_.crop_size()); if (needs_rand) { const unsigned int rng_seed = caffe_rng_rand(); rng_.reset(new Caffe::RNG(rng_seed)); @@ -99,11 +408,12 @@ void DataTransformer::InitRand() { } template -unsigned int DataTransformer::Rand() { +int DataTransformer::Rand(int n) { CHECK(rng_); + CHECK_GT(n, 0); caffe::rng_t* rng = static_cast(rng_->generator()); - return (*rng)(); + return ((*rng)() % n); } INSTANTIATE_CLASS(DataTransformer); diff --git a/src/caffe/layer_factory.cpp b/src/caffe/layer_factory.cpp index b78167f21eb..d6a1cac5090 100644 --- a/src/caffe/layer_factory.cpp +++ b/src/caffe/layer_factory.cpp @@ -1,17 +1,19 @@ #include #include "caffe/layer.hpp" +#include "caffe/layer_factory.hpp" #include "caffe/proto/caffe.pb.h" #include "caffe/vision_layers.hpp" -namespace caffe { +#ifdef WITH_PYTHON_LAYER +#include "caffe/python_layer.hpp" +#endif -// GetLayer() defines the overall layer factory. The Get*Layer() functions -// define factories for layers with multiple computational engines. +namespace caffe { // Get convolution layer according to engine. template -ConvolutionLayer* GetConvolutionLayer(const string& name, +shared_ptr > GetConvolutionLayer( const LayerParameter& param) { ConvolutionParameter_Engine engine = param.convolution_param().engine(); if (engine == ConvolutionParameter_Engine_DEFAULT) { @@ -21,25 +23,21 @@ ConvolutionLayer* GetConvolutionLayer(const string& name, #endif } if (engine == ConvolutionParameter_Engine_CAFFE) { - return new ConvolutionLayer(param); + return shared_ptr >(new ConvolutionLayer(param)); #ifdef USE_CUDNN } else if (engine == ConvolutionParameter_Engine_CUDNN) { - return new CuDNNConvolutionLayer(param); + return shared_ptr >(new CuDNNConvolutionLayer(param)); #endif } else { - LOG(FATAL) << "Layer " << name << " has unknown engine."; + LOG(FATAL) << "Layer " << param.name() << " has unknown engine."; } } -template ConvolutionLayer* GetConvolutionLayer(const string& name, - const LayerParameter& param); -template ConvolutionLayer* GetConvolutionLayer(const string& name, - const LayerParameter& param); +REGISTER_LAYER_CREATOR(Convolution, GetConvolutionLayer); // Get pooling layer according to engine. template -PoolingLayer* GetPoolingLayer(const string& name, - const LayerParameter& param) { +shared_ptr > GetPoolingLayer(const LayerParameter& param) { PoolingParameter_Engine engine = param.pooling_param().engine(); if (engine == PoolingParameter_Engine_DEFAULT) { engine = PoolingParameter_Engine_CAFFE; @@ -48,25 +46,28 @@ PoolingLayer* GetPoolingLayer(const string& name, #endif } if (engine == PoolingParameter_Engine_CAFFE) { - return new PoolingLayer(param); + return shared_ptr >(new PoolingLayer(param)); #ifdef USE_CUDNN } else if (engine == PoolingParameter_Engine_CUDNN) { - return new CuDNNPoolingLayer(param); + PoolingParameter p_param = param.pooling_param(); + if (p_param.pad() || p_param.pad_h() || p_param.pad_w() || + param.top_size() > 1) { + LOG(INFO) << "CUDNN does not support padding or multiple tops. " + << "Using Caffe's own pooling layer."; + return shared_ptr >(new PoolingLayer(param)); + } + return shared_ptr >(new CuDNNPoolingLayer(param)); #endif } else { - LOG(FATAL) << "Layer " << name << " has unknown engine."; + LOG(FATAL) << "Layer " << param.name() << " has unknown engine."; } } -template PoolingLayer* GetPoolingLayer(const string& name, - const LayerParameter& param); -template PoolingLayer* GetPoolingLayer(const string& name, - const LayerParameter& param); +REGISTER_LAYER_CREATOR(Pooling, GetPoolingLayer); // Get relu layer according to engine. template -ReLULayer* GetReLULayer(const string& name, - const LayerParameter& param) { +shared_ptr > GetReLULayer(const LayerParameter& param) { ReLUParameter_Engine engine = param.relu_param().engine(); if (engine == ReLUParameter_Engine_DEFAULT) { engine = ReLUParameter_Engine_CAFFE; @@ -75,25 +76,21 @@ ReLULayer* GetReLULayer(const string& name, #endif } if (engine == ReLUParameter_Engine_CAFFE) { - return new ReLULayer(param); + return shared_ptr >(new ReLULayer(param)); #ifdef USE_CUDNN } else if (engine == ReLUParameter_Engine_CUDNN) { - return new CuDNNReLULayer(param); + return shared_ptr >(new CuDNNReLULayer(param)); #endif } else { - LOG(FATAL) << "Layer " << name << " has unknown engine."; + LOG(FATAL) << "Layer " << param.name() << " has unknown engine."; } } -template ReLULayer* GetReLULayer(const string& name, - const LayerParameter& param); -template ReLULayer* GetReLULayer(const string& name, - const LayerParameter& param); +REGISTER_LAYER_CREATOR(ReLU, GetReLULayer); // Get sigmoid layer according to engine. template -SigmoidLayer* GetSigmoidLayer(const string& name, - const LayerParameter& param) { +shared_ptr > GetSigmoidLayer(const LayerParameter& param) { SigmoidParameter_Engine engine = param.sigmoid_param().engine(); if (engine == SigmoidParameter_Engine_DEFAULT) { engine = SigmoidParameter_Engine_CAFFE; @@ -102,165 +99,81 @@ SigmoidLayer* GetSigmoidLayer(const string& name, #endif } if (engine == SigmoidParameter_Engine_CAFFE) { - return new SigmoidLayer(param); + return shared_ptr >(new SigmoidLayer(param)); #ifdef USE_CUDNN } else if (engine == SigmoidParameter_Engine_CUDNN) { - return new CuDNNSigmoidLayer(param); + return shared_ptr >(new CuDNNSigmoidLayer(param)); #endif } else { - LOG(FATAL) << "Layer " << name << " has unknown engine."; + LOG(FATAL) << "Layer " << param.name() << " has unknown engine."; } } -template SigmoidLayer* GetSigmoidLayer(const string& name, - const LayerParameter& param); -template SigmoidLayer* GetSigmoidLayer(const string& name, - const LayerParameter& param); +REGISTER_LAYER_CREATOR(Sigmoid, GetSigmoidLayer); -// Get tanh layer according to engine. +// Get softmax layer according to engine. template -TanHLayer* GetTanHLayer(const string& name, - const LayerParameter& param) { - TanHParameter_Engine engine = param.tanh_param().engine(); - if (engine == TanHParameter_Engine_DEFAULT) { - engine = TanHParameter_Engine_CAFFE; +shared_ptr > GetSoftmaxLayer(const LayerParameter& param) { + SoftmaxParameter_Engine engine = param.softmax_param().engine(); + if (engine == SoftmaxParameter_Engine_DEFAULT) { + engine = SoftmaxParameter_Engine_CAFFE; #ifdef USE_CUDNN - engine = TanHParameter_Engine_CUDNN; + engine = SoftmaxParameter_Engine_CUDNN; #endif } - if (engine == TanHParameter_Engine_CAFFE) { - return new TanHLayer(param); + if (engine == SoftmaxParameter_Engine_CAFFE) { + return shared_ptr >(new SoftmaxLayer(param)); #ifdef USE_CUDNN - } else if (engine == TanHParameter_Engine_CUDNN) { - return new CuDNNTanHLayer(param); + } else if (engine == SoftmaxParameter_Engine_CUDNN) { + return shared_ptr >(new CuDNNSoftmaxLayer(param)); #endif } else { - LOG(FATAL) << "Layer " << name << " has unknown engine."; + LOG(FATAL) << "Layer " << param.name() << " has unknown engine."; } } -template TanHLayer* GetTanHLayer(const string& name, - const LayerParameter& param); -template TanHLayer* GetTanHLayer(const string& name, - const LayerParameter& param); +REGISTER_LAYER_CREATOR(Softmax, GetSoftmaxLayer); -// Get softmax layer according to engine. +// Get tanh layer according to engine. template -SoftmaxLayer* GetSoftmaxLayer(const string& name, - const LayerParameter& param) { - SoftmaxParameter_Engine engine = param.softmax_param().engine(); - if (engine == SoftmaxParameter_Engine_DEFAULT) { - engine = SoftmaxParameter_Engine_CAFFE; +shared_ptr > GetTanHLayer(const LayerParameter& param) { + TanHParameter_Engine engine = param.tanh_param().engine(); + if (engine == TanHParameter_Engine_DEFAULT) { + engine = TanHParameter_Engine_CAFFE; #ifdef USE_CUDNN - engine = SoftmaxParameter_Engine_CUDNN; + engine = TanHParameter_Engine_CUDNN; #endif } - if (engine == SoftmaxParameter_Engine_CAFFE) { - return new SoftmaxLayer(param); + if (engine == TanHParameter_Engine_CAFFE) { + return shared_ptr >(new TanHLayer(param)); #ifdef USE_CUDNN - } else if (engine == SoftmaxParameter_Engine_CUDNN) { - return new CuDNNSoftmaxLayer(param); + } else if (engine == TanHParameter_Engine_CUDNN) { + return shared_ptr >(new CuDNNTanHLayer(param)); #endif } else { - LOG(FATAL) << "Layer " << name << " has unknown engine."; + LOG(FATAL) << "Layer " << param.name() << " has unknown engine."; } } -template SoftmaxLayer* GetSoftmaxLayer(const string& name, - const LayerParameter& param); -template SoftmaxLayer* GetSoftmaxLayer(const string& name, - const LayerParameter& param); +REGISTER_LAYER_CREATOR(TanH, GetTanHLayer); -// A function to get a specific layer from the specification given in -// LayerParameter. Ideally this would be replaced by a factory pattern, -// but we will leave it this way for now. +#ifdef WITH_PYTHON_LAYER template -Layer* GetLayer(const LayerParameter& param) { - const string& name = param.name(); - const LayerParameter_LayerType& type = param.type(); - switch (type) { - case LayerParameter_LayerType_ACCURACY: - return new AccuracyLayer(param); - case LayerParameter_LayerType_ABSVAL: - return new AbsValLayer(param); - case LayerParameter_LayerType_ARGMAX: - return new ArgMaxLayer(param); - case LayerParameter_LayerType_BNLL: - return new BNLLLayer(param); - case LayerParameter_LayerType_CONCAT: - return new ConcatLayer(param); - case LayerParameter_LayerType_CONTRASTIVE_LOSS: - return new ContrastiveLossLayer(param); - case LayerParameter_LayerType_CONVOLUTION: - return GetConvolutionLayer(name, param); - case LayerParameter_LayerType_DATA: - return new DataLayer(param); - case LayerParameter_LayerType_DROPOUT: - return new DropoutLayer(param); - case LayerParameter_LayerType_DUMMY_DATA: - return new DummyDataLayer(param); - case LayerParameter_LayerType_EUCLIDEAN_LOSS: - return new EuclideanLossLayer(param); - case LayerParameter_LayerType_ELTWISE: - return new EltwiseLayer(param); - case LayerParameter_LayerType_FLATTEN: - return new FlattenLayer(param); - case LayerParameter_LayerType_HDF5_DATA: - return new HDF5DataLayer(param); - case LayerParameter_LayerType_HDF5_OUTPUT: - return new HDF5OutputLayer(param); - case LayerParameter_LayerType_HINGE_LOSS: - return new HingeLossLayer(param); - case LayerParameter_LayerType_IMAGE_DATA: - return new ImageDataLayer(param); - case LayerParameter_LayerType_IM2COL: - return new Im2colLayer(param); - case LayerParameter_LayerType_INFOGAIN_LOSS: - return new InfogainLossLayer(param); - case LayerParameter_LayerType_INNER_PRODUCT: - return new InnerProductLayer(param); - case LayerParameter_LayerType_LRN: - return new LRNLayer(param); - case LayerParameter_LayerType_MEMORY_DATA: - return new MemoryDataLayer(param); - case LayerParameter_LayerType_MVN: - return new MVNLayer(param); - case LayerParameter_LayerType_MULTINOMIAL_LOGISTIC_LOSS: - return new MultinomialLogisticLossLayer(param); - case LayerParameter_LayerType_POOLING: - return GetPoolingLayer(name, param); - case LayerParameter_LayerType_POWER: - return new PowerLayer(param); - case LayerParameter_LayerType_RELU: - return GetReLULayer(name, param); - case LayerParameter_LayerType_SILENCE: - return new SilenceLayer(param); - case LayerParameter_LayerType_SIGMOID: - return GetSigmoidLayer(name, param); - case LayerParameter_LayerType_SIGMOID_CROSS_ENTROPY_LOSS: - return new SigmoidCrossEntropyLossLayer(param); - case LayerParameter_LayerType_SLICE: - return new SliceLayer(param); - case LayerParameter_LayerType_SOFTMAX: - return GetSoftmaxLayer(name, param); - case LayerParameter_LayerType_SOFTMAX_LOSS: - return new SoftmaxWithLossLayer(param); - case LayerParameter_LayerType_SPLIT: - return new SplitLayer(param); - case LayerParameter_LayerType_TANH: - return GetTanHLayer(name, param); - case LayerParameter_LayerType_WINDOW_DATA: - return new WindowDataLayer(param); - case LayerParameter_LayerType_NONE: - LOG(FATAL) << "Layer " << name << " has unspecified type."; - default: - LOG(FATAL) << "Layer " << name << " has unknown type " << type; +shared_ptr > GetPythonLayer(const LayerParameter& param) { + Py_Initialize(); + try { + bp::object module = bp::import(param.python_param().module().c_str()); + bp::object layer = module.attr(param.python_param().layer().c_str())(param); + return bp::extract > >(layer)(); + } catch (bp::error_already_set) { + PyErr_Print(); + throw; } - // just to suppress old compiler warnings. - return (Layer*)(NULL); } -template Layer* GetLayer(const LayerParameter& param); -template Layer* GetLayer(const LayerParameter& param); +REGISTER_LAYER_CREATOR(Python, GetPythonLayer); +#endif +// Layers that use their constructor as their default creator should be +// registered in their corresponding cpp files. Do not register them here. } // namespace caffe diff --git a/src/caffe/layers/absval_layer.cpp b/src/caffe/layers/absval_layer.cpp index ce9d05cc764..5ce28c9e2b4 100644 --- a/src/caffe/layers/absval_layer.cpp +++ b/src/caffe/layers/absval_layer.cpp @@ -8,30 +8,29 @@ namespace caffe { template void AbsValLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { NeuronLayer::LayerSetUp(bottom, top); - CHECK_NE((*top)[0], bottom[0]) << this->type_name() << " Layer does not " + CHECK_NE(top[0], bottom[0]) << this->type() << " Layer does not " "allow in-place computation."; } template void AbsValLayer::Forward_cpu( - const vector*>& bottom, vector*>* top) { - const int count = (*top)[0]->count(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + const vector*>& bottom, const vector*>& top) { + const int count = top[0]->count(); + Dtype* top_data = top[0]->mutable_cpu_data(); caffe_abs(count, bottom[0]->cpu_data(), top_data); } template void AbsValLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const int count = top[0]->count(); - const Dtype* top_data = top[0]->cpu_data(); const Dtype* top_diff = top[0]->cpu_diff(); if (propagate_down[0]) { - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - caffe_div(count, top_data, bottom_data, bottom_diff); + const Dtype* bottom_data = bottom[0]->cpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + caffe_cpu_sign(count, bottom_data, bottom_diff); caffe_mul(count, bottom_diff, top_diff, bottom_diff); } } @@ -41,6 +40,6 @@ STUB_GPU(AbsValLayer); #endif INSTANTIATE_CLASS(AbsValLayer); - +REGISTER_LAYER_CLASS(AbsVal); } // namespace caffe diff --git a/src/caffe/layers/absval_layer.cu b/src/caffe/layers/absval_layer.cu index 46778aa79b1..91f3c77fe9a 100644 --- a/src/caffe/layers/absval_layer.cu +++ b/src/caffe/layers/absval_layer.cu @@ -8,27 +8,27 @@ namespace caffe { template void AbsValLayer::Forward_gpu( - const vector*>& bottom, vector*>* top) { - const int count = (*top)[0]->count(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + const vector*>& bottom, const vector*>& top) { + const int count = top[0]->count(); + Dtype* top_data = top[0]->mutable_gpu_data(); caffe_gpu_abs(count, bottom[0]->gpu_data(), top_data); } template void AbsValLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const int count = top[0]->count(); const Dtype* top_data = top[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); if (propagate_down[0]) { - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); - caffe_gpu_div(count, top_data, bottom_data, bottom_diff); + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + caffe_gpu_sign(count, bottom_data, bottom_diff); caffe_gpu_mul(count, bottom_diff, top_diff, bottom_diff); } } -INSTANTIATE_CLASS(AbsValLayer); +INSTANTIATE_LAYER_GPU_FUNCS(AbsValLayer); } // namespace caffe diff --git a/src/caffe/layers/accuracy_layer.cpp b/src/caffe/layers/accuracy_layer.cpp index 3e69bc84faa..3e8df34c0d6 100644 --- a/src/caffe/layers/accuracy_layer.cpp +++ b/src/caffe/layers/accuracy_layer.cpp @@ -12,13 +12,13 @@ namespace caffe { template void AccuracyLayer::LayerSetUp( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { top_k_ = this->layer_param_.accuracy_param().top_k(); } template void AccuracyLayer::Reshape( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { CHECK_EQ(bottom[0]->num(), bottom[1]->num()) << "The data and label should have the same number."; CHECK_LE(top_k_, bottom[0]->count() / bottom[0]->num()) @@ -26,12 +26,12 @@ void AccuracyLayer::Reshape( CHECK_EQ(bottom[1]->channels(), 1); CHECK_EQ(bottom[1]->height(), 1); CHECK_EQ(bottom[1]->width(), 1); - (*top)[0]->Reshape(1, 1, 1, 1); + top[0]->Reshape(1, 1, 1, 1); } template void AccuracyLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { Dtype accuracy = 0; const Dtype* bottom_data = bottom[0]->cpu_data(); const Dtype* bottom_label = bottom[1]->cpu_data(); @@ -59,10 +59,11 @@ void AccuracyLayer::Forward_cpu(const vector*>& bottom, } // LOG(INFO) << "Accuracy: " << accuracy; - (*top)[0]->mutable_cpu_data()[0] = accuracy / num; + top[0]->mutable_cpu_data()[0] = accuracy / num; // Accuracy layer should not be used as a loss function. } INSTANTIATE_CLASS(AccuracyLayer); +REGISTER_LAYER_CLASS(Accuracy); } // namespace caffe diff --git a/src/caffe/layers/argmax_layer.cpp b/src/caffe/layers/argmax_layer.cpp index 0d1a107257b..c4040cdcaaa 100644 --- a/src/caffe/layers/argmax_layer.cpp +++ b/src/caffe/layers/argmax_layer.cpp @@ -10,7 +10,7 @@ namespace caffe { template void ArgMaxLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { out_max_val_ = this->layer_param_.argmax_param().out_max_val(); top_k_ = this->layer_param_.argmax_param().top_k(); CHECK_GE(top_k_, 1) << " top k must not be less than 1."; @@ -20,21 +20,21 @@ void ArgMaxLayer::LayerSetUp(const vector*>& bottom, template void ArgMaxLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { if (out_max_val_) { // Produces max_ind and max_val - (*top)[0]->Reshape(bottom[0]->num(), 2, top_k_, 1); + top[0]->Reshape(bottom[0]->num(), 2, top_k_, 1); } else { // Produces only max_ind - (*top)[0]->Reshape(bottom[0]->num(), 1, top_k_, 1); + top[0]->Reshape(bottom[0]->num(), 1, top_k_, 1); } } template void ArgMaxLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); int num = bottom[0]->num(); int dim = bottom[0]->count() / bottom[0]->num(); for (int i = 0; i < num; ++i) { @@ -47,16 +47,17 @@ void ArgMaxLayer::Forward_cpu(const vector*>& bottom, bottom_data_vector.begin(), bottom_data_vector.begin() + top_k_, bottom_data_vector.end(), std::greater >()); for (int j = 0; j < top_k_; ++j) { - top_data[(*top)[0]->offset(i, 0, j)] = bottom_data_vector[j].second; + top_data[top[0]->offset(i, 0, j)] = bottom_data_vector[j].second; } if (out_max_val_) { for (int j = 0; j < top_k_; ++j) { - top_data[(*top)[0]->offset(i, 1, j)] = bottom_data_vector[j].first; + top_data[top[0]->offset(i, 1, j)] = bottom_data_vector[j].first; } } } } INSTANTIATE_CLASS(ArgMaxLayer); +REGISTER_LAYER_CLASS(ArgMax); } // namespace caffe diff --git a/src/caffe/layers/base_conv_layer.cpp b/src/caffe/layers/base_conv_layer.cpp new file mode 100644 index 00000000000..dccd5170c11 --- /dev/null +++ b/src/caffe/layers/base_conv_layer.cpp @@ -0,0 +1,293 @@ +#include + +#include "caffe/filler.hpp" +#include "caffe/layer.hpp" +#include "caffe/util/im2col.hpp" +#include "caffe/util/math_functions.hpp" +#include "caffe/vision_layers.hpp" + +namespace caffe { + +template +void BaseConvolutionLayer::LayerSetUp(const vector*>& bottom, + const vector*>& top) { + // Configure the kernel size, padding, stride, and inputs. + ConvolutionParameter conv_param = this->layer_param_.convolution_param(); + CHECK(!conv_param.has_kernel_size() != + !(conv_param.has_kernel_h() && conv_param.has_kernel_w())) + << "Filter size is kernel_size OR kernel_h and kernel_w; not both"; + CHECK(conv_param.has_kernel_size() || + (conv_param.has_kernel_h() && conv_param.has_kernel_w())) + << "For non-square filters both kernel_h and kernel_w are required."; + CHECK((!conv_param.has_pad() && conv_param.has_pad_h() + && conv_param.has_pad_w()) + || (!conv_param.has_pad_h() && !conv_param.has_pad_w())) + << "pad is pad OR pad_h and pad_w are required."; + CHECK((!conv_param.has_stride() && conv_param.has_stride_h() + && conv_param.has_stride_w()) + || (!conv_param.has_stride_h() && !conv_param.has_stride_w())) + << "Stride is stride OR stride_h and stride_w are required."; + if (conv_param.has_kernel_size()) { + kernel_h_ = kernel_w_ = conv_param.kernel_size(); + } else { + kernel_h_ = conv_param.kernel_h(); + kernel_w_ = conv_param.kernel_w(); + } + CHECK_GT(kernel_h_, 0) << "Filter dimensions cannot be zero."; + CHECK_GT(kernel_w_, 0) << "Filter dimensions cannot be zero."; + if (!conv_param.has_pad_h()) { + pad_h_ = pad_w_ = conv_param.pad(); + } else { + pad_h_ = conv_param.pad_h(); + pad_w_ = conv_param.pad_w(); + } + if (!conv_param.has_stride_h()) { + stride_h_ = stride_w_ = conv_param.stride(); + } else { + stride_h_ = conv_param.stride_h(); + stride_w_ = conv_param.stride_w(); + } + // Special case: im2col is the identity for 1x1 convolution with stride 1 + // and no padding, so flag for skipping the buffer and transformation. + is_1x1_ = kernel_w_ == 1 && kernel_h_ == 1 + && stride_h_ == 1 && stride_w_ == 1 && pad_h_ == 0 && pad_w_ == 0; + // Configure output channels and groups. + channels_ = bottom[0]->channels(); + num_output_ = this->layer_param_.convolution_param().num_output(); + CHECK_GT(num_output_, 0); + group_ = this->layer_param_.convolution_param().group(); + CHECK_EQ(channels_ % group_, 0); + CHECK_EQ(num_output_ % group_, 0) + << "Number of output should be multiples of group."; + if (reverse_dimensions()) { + conv_out_channels_ = channels_; + conv_in_channels_ = num_output_; + } else { + conv_out_channels_ = num_output_; + conv_in_channels_ = channels_; + } + // Handle the parameters: weights and biases. + // - blobs_[0] holds the filter weights + // - blobs_[1] holds the biases (optional) + bias_term_ = this->layer_param_.convolution_param().bias_term(); + if (this->blobs_.size() > 0) { + LOG(INFO) << "Skipping parameter initialization"; + } else { + if (bias_term_) { + this->blobs_.resize(2); + } else { + this->blobs_.resize(1); + } + // Initialize and fill the weights: + // output channels x input channels per-group x kernel height x kernel width + this->blobs_[0].reset(new Blob( + conv_out_channels_, conv_in_channels_ / group_, kernel_h_, kernel_w_)); + shared_ptr > weight_filler(GetFiller( + this->layer_param_.convolution_param().weight_filler())); + weight_filler->Fill(this->blobs_[0].get()); + // If necessary, initialize and fill the biases: + // 1 x 1 x 1 x output channels + if (bias_term_) { + this->blobs_[1].reset(new Blob(1, 1, 1, num_output_)); + shared_ptr > bias_filler(GetFiller( + this->layer_param_.convolution_param().bias_filler())); + bias_filler->Fill(this->blobs_[1].get()); + } + } + // Propagate gradients to the parameters (as directed by backward pass). + this->param_propagate_down_.resize(this->blobs_.size(), true); +} + +template +void BaseConvolutionLayer::Reshape(const vector*>& bottom, + const vector*>& top) { + num_ = bottom[0]->num(); + height_ = bottom[0]->height(); + width_ = bottom[0]->width(); + CHECK_EQ(bottom[0]->channels(), channels_) << "Input size incompatible with" + " convolution kernel."; + // TODO: generalize to handle inputs of different shapes. + for (int bottom_id = 1; bottom_id < bottom.size(); ++bottom_id) { + CHECK_EQ(num_, bottom[bottom_id]->num()) << "Inputs must have same num."; + CHECK_EQ(channels_, bottom[bottom_id]->channels()) + << "Inputs must have same channels."; + CHECK_EQ(height_, bottom[bottom_id]->height()) + << "Inputs must have same height."; + CHECK_EQ(width_, bottom[bottom_id]->width()) + << "Inputs must have same width."; + } + // Shape the tops. + compute_output_shape(); + for (int top_id = 0; top_id < top.size(); ++top_id) { + top[top_id]->Reshape(num_, num_output_, height_out_, width_out_); + } + if (reverse_dimensions()) { + conv_in_height_ = height_out_; + conv_in_width_ = width_out_; + conv_out_spatial_dim_ = height_ * width_; + } else { + conv_in_height_ = height_; + conv_in_width_ = width_; + conv_out_spatial_dim_ = height_out_ * width_out_; + } + kernel_dim_ = conv_in_channels_ * kernel_h_ * kernel_w_; + weight_offset_ = conv_out_channels_ * kernel_dim_ / group_ / group_; + col_offset_ = kernel_dim_ * conv_out_spatial_dim_ / group_; + output_offset_ = conv_out_channels_ * conv_out_spatial_dim_ / group_; + // The im2col result buffer will only hold one image at a time to avoid + // overly large memory usage. In the special case of 1x1 convolution + // it goes lazily unused to save memory. + if (reverse_dimensions()) { + col_buffer_.Reshape(1, kernel_dim_, height_, width_); + } else { + col_buffer_.Reshape(1, kernel_dim_, height_out_, width_out_); + } + // Set up the all ones "bias multiplier" for adding biases by BLAS + if (bias_term_) { + bias_multiplier_.Reshape(1, 1, 1, height_out_ * width_out_); + caffe_set(bias_multiplier_.count(), Dtype(1), + bias_multiplier_.mutable_cpu_data()); + } +} + +template +void BaseConvolutionLayer::forward_cpu_gemm(const Dtype* input, + const Dtype* weights, Dtype* output, bool skip_im2col) { + const Dtype* col_buff = input; + if (!is_1x1_) { + if (!skip_im2col) { + conv_im2col_cpu(input, col_buffer_.mutable_cpu_data()); + } + col_buff = col_buffer_.cpu_data(); + } + for (int g = 0; g < group_; ++g) { + caffe_cpu_gemm(CblasNoTrans, CblasNoTrans, conv_out_channels_ / + group_, conv_out_spatial_dim_, kernel_dim_ / group_, + (Dtype)1., weights + weight_offset_ * g, col_buff + col_offset_ * g, + (Dtype)0., output + output_offset_ * g); + } +} + +template +void BaseConvolutionLayer::forward_cpu_bias(Dtype* output, + const Dtype* bias) { + caffe_cpu_gemm(CblasNoTrans, CblasNoTrans, num_output_, + height_out_ * width_out_, 1, (Dtype)1., bias, bias_multiplier_.cpu_data(), + (Dtype)1., output); +} + +template +void BaseConvolutionLayer::backward_cpu_gemm(const Dtype* output, + const Dtype* weights, Dtype* input) { + Dtype* col_buff = col_buffer_.mutable_cpu_data(); + if (is_1x1_) { + col_buff = input; + } + for (int g = 0; g < group_; ++g) { + caffe_cpu_gemm(CblasTrans, CblasNoTrans, kernel_dim_ / group_, + conv_out_spatial_dim_, conv_out_channels_ / group_, + (Dtype)1., weights + weight_offset_ * g, output + output_offset_ * g, + (Dtype)0., col_buff + col_offset_ * g); + } + if (!is_1x1_) { + conv_col2im_cpu(col_buff, input); + } +} + +template +void BaseConvolutionLayer::weight_cpu_gemm(const Dtype* input, + const Dtype* output, Dtype* weights) { + const Dtype* col_buff = input; + if (!is_1x1_) { + conv_im2col_cpu(input, col_buffer_.mutable_cpu_data()); + col_buff = col_buffer_.cpu_data(); + } + for (int g = 0; g < group_; ++g) { + caffe_cpu_gemm(CblasNoTrans, CblasTrans, conv_out_channels_ / group_, + kernel_dim_ / group_, conv_out_spatial_dim_, + (Dtype)1., output + output_offset_ * g, col_buff + col_offset_ * g, + (Dtype)1., weights + weight_offset_ * g); + } +} + +template +void BaseConvolutionLayer::backward_cpu_bias(Dtype* bias, + const Dtype* input) { + caffe_cpu_gemv(CblasNoTrans, num_output_, height_out_ * width_out_, 1., + input, bias_multiplier_.cpu_data(), 1., bias); +} + +#ifndef CPU_ONLY + +template +void BaseConvolutionLayer::forward_gpu_gemm(const Dtype* input, + const Dtype* weights, Dtype* output, bool skip_im2col) { + const Dtype* col_buff = input; + if (!is_1x1_) { + if (!skip_im2col) { + conv_im2col_gpu(input, col_buffer_.mutable_gpu_data()); + } + col_buff = col_buffer_.gpu_data(); + } + for (int g = 0; g < group_; ++g) { + caffe_gpu_gemm(CblasNoTrans, CblasNoTrans, conv_out_channels_ / + group_, conv_out_spatial_dim_, kernel_dim_ / group_, + (Dtype)1., weights + weight_offset_ * g, col_buff + col_offset_ * g, + (Dtype)0., output + output_offset_ * g); + } +} + +template +void BaseConvolutionLayer::forward_gpu_bias(Dtype* output, + const Dtype* bias) { + caffe_gpu_gemm(CblasNoTrans, CblasNoTrans, num_output_, + height_out_ * width_out_, 1, (Dtype)1., bias, bias_multiplier_.gpu_data(), + (Dtype)1., output); +} + +template +void BaseConvolutionLayer::backward_gpu_gemm(const Dtype* output, + const Dtype* weights, Dtype* input) { + Dtype* col_buff = col_buffer_.mutable_gpu_data(); + if (is_1x1_) { + col_buff = input; + } + for (int g = 0; g < group_; ++g) { + caffe_gpu_gemm(CblasTrans, CblasNoTrans, kernel_dim_ / group_, + conv_out_spatial_dim_, conv_out_channels_ / group_, + (Dtype)1., weights + weight_offset_ * g, output + output_offset_ * g, + (Dtype)0., col_buff + col_offset_ * g); + } + if (!is_1x1_) { + conv_col2im_gpu(col_buff, input); + } +} + +template +void BaseConvolutionLayer::weight_gpu_gemm(const Dtype* input, + const Dtype* output, Dtype* weights) { + const Dtype* col_buff = input; + if (!is_1x1_) { + conv_im2col_gpu(input, col_buffer_.mutable_gpu_data()); + col_buff = col_buffer_.gpu_data(); + } + for (int g = 0; g < group_; ++g) { + caffe_gpu_gemm(CblasNoTrans, CblasTrans, conv_out_channels_ / group_, + kernel_dim_ / group_, conv_out_spatial_dim_, + (Dtype)1., output + output_offset_ * g, col_buff + col_offset_ * g, + (Dtype)1., weights + weight_offset_ * g); + } +} + +template +void BaseConvolutionLayer::backward_gpu_bias(Dtype* bias, + const Dtype* input) { + caffe_gpu_gemv(CblasNoTrans, num_output_, height_out_ * width_out_, 1., + input, bias_multiplier_.gpu_data(), 1., bias); +} + +#endif // !CPU_ONLY + +INSTANTIATE_CLASS(BaseConvolutionLayer); + +} // namespace caffe diff --git a/src/caffe/layers/base_data_layer.cpp b/src/caffe/layers/base_data_layer.cpp index 9b1d55831a7..352200915d7 100644 --- a/src/caffe/layers/base_data_layer.cpp +++ b/src/caffe/layers/base_data_layer.cpp @@ -2,6 +2,7 @@ #include #include "caffe/data_layers.hpp" +#include "caffe/net.hpp" #include "caffe/util/io.hpp" namespace caffe { @@ -9,49 +10,27 @@ namespace caffe { template BaseDataLayer::BaseDataLayer(const LayerParameter& param) : Layer(param), - transform_param_(param.transform_param()), - data_transformer_(transform_param_) { + transform_param_(param.transform_param()) { } template void BaseDataLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { - if (top->size() == 1) { + const vector*>& top) { + if (top.size() == 1) { output_labels_ = false; } else { output_labels_ = true; } + // The subclasses should setup the size of bottom and top DataLayerSetUp(bottom, top); - // The subclasses should setup the datum channels, height and width - CHECK_GT(datum_channels_, 0); - CHECK_GT(datum_height_, 0); - CHECK_GT(datum_width_, 0); - if (transform_param_.crop_size() > 0) { - CHECK_GE(datum_height_, transform_param_.crop_size()); - CHECK_GE(datum_width_, transform_param_.crop_size()); - } - // check if we want to have mean - if (transform_param_.has_mean_file()) { - const string& mean_file = transform_param_.mean_file(); - LOG(INFO) << "Loading mean file from" << mean_file; - BlobProto blob_proto; - ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto); - data_mean_.FromProto(blob_proto); - CHECK_GE(data_mean_.num(), 1); - CHECK_GE(data_mean_.channels(), datum_channels_); - CHECK_GE(data_mean_.height(), datum_height_); - CHECK_GE(data_mean_.width(), datum_width_); - } else { - // Simply initialize an all-empty mean. - data_mean_.Reshape(1, datum_channels_, datum_height_, datum_width_); - } - mean_ = data_mean_.cpu_data(); - data_transformer_.InitRand(); + data_transformer_.reset( + new DataTransformer(transform_param_, this->phase_)); + data_transformer_->InitRand(); } template void BasePrefetchingDataLayer::LayerSetUp( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { BaseDataLayer::LayerSetUp(bottom, top); // Now, start the prefetch thread. Before calling prefetch, we make two // cpu_data calls so that the prefetch thread does not accidentally make @@ -68,8 +47,7 @@ void BasePrefetchingDataLayer::LayerSetUp( template void BasePrefetchingDataLayer::CreatePrefetchThread() { - this->phase_ = Caffe::phase(); - this->data_transformer_.InitRand(); + this->data_transformer_->InitRand(); CHECK(StartInternalThread()) << "Thread execution failed"; } @@ -80,17 +58,23 @@ void BasePrefetchingDataLayer::JoinPrefetchThread() { template void BasePrefetchingDataLayer::Forward_cpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { // First, join the thread JoinPrefetchThread(); + DLOG(INFO) << "Thread joined"; + // Reshape to loaded data. + top[0]->Reshape(this->prefetch_data_.num(), this->prefetch_data_.channels(), + this->prefetch_data_.height(), this->prefetch_data_.width()); // Copy the data caffe_copy(prefetch_data_.count(), prefetch_data_.cpu_data(), - (*top)[0]->mutable_cpu_data()); + top[0]->mutable_cpu_data()); + DLOG(INFO) << "Prefetch copied"; if (this->output_labels_) { caffe_copy(prefetch_label_.count(), prefetch_label_.cpu_data(), - (*top)[1]->mutable_cpu_data()); + top[1]->mutable_cpu_data()); } // Start a new prefetch thread + DLOG(INFO) << "CreatePrefetchThread"; CreatePrefetchThread(); } diff --git a/src/caffe/layers/base_data_layer.cu b/src/caffe/layers/base_data_layer.cu index 8189c79c9d4..775f6c47f7e 100644 --- a/src/caffe/layers/base_data_layer.cu +++ b/src/caffe/layers/base_data_layer.cu @@ -6,20 +6,23 @@ namespace caffe { template void BasePrefetchingDataLayer::Forward_gpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { // First, join the thread JoinPrefetchThread(); + // Reshape to loaded data. + top[0]->Reshape(this->prefetch_data_.num(), this->prefetch_data_.channels(), + this->prefetch_data_.height(), this->prefetch_data_.width()); // Copy the data caffe_copy(prefetch_data_.count(), prefetch_data_.cpu_data(), - (*top)[0]->mutable_gpu_data()); + top[0]->mutable_gpu_data()); if (this->output_labels_) { caffe_copy(prefetch_label_.count(), prefetch_label_.cpu_data(), - (*top)[1]->mutable_gpu_data()); + top[1]->mutable_gpu_data()); } // Start a new prefetch thread CreatePrefetchThread(); } -INSTANTIATE_CLASS(BasePrefetchingDataLayer); +INSTANTIATE_LAYER_GPU_FORWARD(BasePrefetchingDataLayer); } // namespace caffe diff --git a/src/caffe/layers/bnll_layer.cpp b/src/caffe/layers/bnll_layer.cpp index ef98326a23e..9ba0ea9a715 100644 --- a/src/caffe/layers/bnll_layer.cpp +++ b/src/caffe/layers/bnll_layer.cpp @@ -10,9 +10,9 @@ const float kBNLL_THRESHOLD = 50.; template void BNLLLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); const int count = bottom[0]->count(); for (int i = 0; i < count; ++i) { top_data[i] = bottom_data[i] > 0 ? @@ -24,12 +24,12 @@ void BNLLLayer::Forward_cpu(const vector*>& bottom, template void BNLLLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); + const Dtype* bottom_data = bottom[0]->cpu_data(); const Dtype* top_diff = top[0]->cpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + const int count = bottom[0]->count(); Dtype expval; for (int i = 0; i < count; ++i) { expval = exp(std::min(bottom_data[i], Dtype(kBNLL_THRESHOLD))); @@ -43,6 +43,6 @@ STUB_GPU(BNLLLayer); #endif INSTANTIATE_CLASS(BNLLLayer); - +REGISTER_LAYER_CLASS(BNLL); } // namespace caffe diff --git a/src/caffe/layers/bnll_layer.cu b/src/caffe/layers/bnll_layer.cu index b940133b4b3..d963d0687d2 100644 --- a/src/caffe/layers/bnll_layer.cu +++ b/src/caffe/layers/bnll_layer.cu @@ -19,9 +19,9 @@ __global__ void BNLLForward(const int n, const Dtype* in, Dtype* out) { template void BNLLLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); const int count = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) BNLLForward<<>>( @@ -41,12 +41,12 @@ __global__ void BNLLBackward(const int n, const Dtype* in_diff, template void BNLLLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); + const Dtype* bottom_data = bottom[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + const int count = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) BNLLBackward<<>>( count, top_diff, bottom_data, bottom_diff); @@ -54,7 +54,7 @@ void BNLLLayer::Backward_gpu(const vector*>& top, } } -INSTANTIATE_CLASS(BNLLLayer); +INSTANTIATE_LAYER_GPU_FUNCS(BNLLLayer); } // namespace caffe diff --git a/src/caffe/layers/concat_layer.cpp b/src/caffe/layers/concat_layer.cpp index 10a11f1bb7c..fc88433c42b 100644 --- a/src/caffe/layers/concat_layer.cpp +++ b/src/caffe/layers/concat_layer.cpp @@ -8,7 +8,7 @@ namespace caffe { template void ConcatLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { concat_dim_ = this->layer_param_.concat_param().concat_dim(); CHECK_GE(concat_dim_, 0) << "concat_dim should be >= 0"; @@ -18,7 +18,7 @@ void ConcatLayer::LayerSetUp(const vector*>& bottom, template void ConcatLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { // Initialize with the first blob. count_ = bottom[0]->count(); num_ = bottom[0]->num(); @@ -37,20 +37,20 @@ void ConcatLayer::Reshape(const vector*>& bottom, width_ += bottom[i]->width(); } } - (*top)[0]->Reshape(num_, channels_, height_, width_); - CHECK_EQ(count_, (*top)[0]->count()); + top[0]->Reshape(num_, channels_, height_, width_); + CHECK_EQ(count_, top[0]->count()); } template void ConcatLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + const vector*>& top) { + Dtype* top_data = top[0]->mutable_cpu_data(); if (concat_dim_== 0) { int offset_num = 0; for (int i = 0; i < bottom.size(); ++i) { const Dtype* bottom_data = bottom[i]->cpu_data(); int num_elem = bottom[i]->count(); - caffe_copy(num_elem, bottom_data, top_data+(*top)[0]->offset(offset_num)); + caffe_copy(num_elem, bottom_data, top_data+top[0]->offset(offset_num)); offset_num += bottom[i]->num(); } } else if (concat_dim_ == 1) { @@ -61,7 +61,7 @@ void ConcatLayer::Forward_cpu(const vector*>& bottom, bottom[i]->channels()*bottom[i]->height()*bottom[i]->width(); for (int n = 0; n < num_; ++n) { caffe_copy(num_elem, bottom_data+bottom[i]->offset(n), - top_data+(*top)[0]->offset(n, offset_channel)); + top_data+top[0]->offset(n, offset_channel)); } offset_channel += bottom[i]->channels(); } // concat_dim_ is guaranteed to be 0 or 1 by LayerSetUp. @@ -70,12 +70,12 @@ void ConcatLayer::Forward_cpu(const vector*>& bottom, template void ConcatLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const Dtype* top_diff = top[0]->cpu_diff(); if (concat_dim_ == 0) { int offset_num = 0; - for (int i = 0; i < bottom->size(); ++i) { - Blob* blob = (*bottom)[i]; + for (int i = 0; i < bottom.size(); ++i) { + Blob* blob = bottom[i]; if (propagate_down[i]) { Dtype* bottom_diff = blob->mutable_cpu_diff(); caffe_copy(blob->count(), top_diff + top[0]->offset(offset_num), @@ -85,8 +85,8 @@ void ConcatLayer::Backward_cpu(const vector*>& top, } } else if (concat_dim_ == 1) { int offset_channel = 0; - for (int i = 0; i < bottom->size(); ++i) { - Blob* blob = (*bottom)[i]; + for (int i = 0; i < bottom.size(); ++i) { + Blob* blob = bottom[i]; if (propagate_down[i]) { Dtype* bottom_diff = blob->mutable_cpu_diff(); int num_elem = blob->channels()*blob->height()*blob->width(); @@ -105,5 +105,6 @@ STUB_GPU(ConcatLayer); #endif INSTANTIATE_CLASS(ConcatLayer); +REGISTER_LAYER_CLASS(Concat); } // namespace caffe diff --git a/src/caffe/layers/concat_layer.cu b/src/caffe/layers/concat_layer.cu index 99c55da25cb..88fc090025f 100644 --- a/src/caffe/layers/concat_layer.cu +++ b/src/caffe/layers/concat_layer.cu @@ -8,14 +8,14 @@ namespace caffe { template void ConcatLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + const vector*>& top) { + Dtype* top_data = top[0]->mutable_gpu_data(); if (concat_dim_ == 0) { int offset_num = 0; for (int i = 0; i < bottom.size(); ++i) { const Dtype* bottom_data = bottom[i]->gpu_data(); caffe_copy(bottom[i]->count(), bottom_data, - top_data + (*top)[0]->offset(offset_num)); + top_data + top[0]->offset(offset_num)); offset_num += bottom[i]->num(); } } else if (concat_dim_ == 1) { @@ -26,7 +26,7 @@ void ConcatLayer::Forward_gpu(const vector*>& bottom, bottom[i]->channels() * bottom[i]->height() * bottom[i]->width(); for (int n = 0; n < num_; ++n) { caffe_copy(num_elem, bottom_data+bottom[i]->offset(n), - top_data + (*top)[0]->offset(n, offset_channel)); + top_data + top[0]->offset(n, offset_channel)); } offset_channel += bottom[i]->channels(); } @@ -38,12 +38,12 @@ void ConcatLayer::Forward_gpu(const vector*>& bottom, template void ConcatLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const Dtype* top_diff = top[0]->gpu_diff(); if (concat_dim_ == 0) { int offset_num = 0; - for (int i = 0; i < bottom->size(); ++i) { - Blob* blob = (*bottom)[i]; + for (int i = 0; i < bottom.size(); ++i) { + Blob* blob = bottom[i]; if (propagate_down[i]) { Dtype* bottom_diff = blob->mutable_gpu_diff(); caffe_copy(blob->count(), top_diff + top[0]->offset(offset_num), @@ -53,8 +53,8 @@ void ConcatLayer::Backward_gpu(const vector*>& top, } } else if (concat_dim_ == 1) { int offset_channel = 0; - for (int i = 0; i < bottom->size(); ++i) { - Blob* blob = (*bottom)[i]; + for (int i = 0; i < bottom.size(); ++i) { + Blob* blob = bottom[i]; if (propagate_down[i]) { Dtype* bottom_diff = blob->mutable_gpu_diff(); int num_elem = blob->channels()*blob->height()*blob->width(); @@ -71,6 +71,6 @@ void ConcatLayer::Backward_gpu(const vector*>& top, } } -INSTANTIATE_CLASS(ConcatLayer); +INSTANTIATE_LAYER_GPU_FUNCS(ConcatLayer); } // namespace caffe diff --git a/src/caffe/layers/contrastive_loss_layer.cpp b/src/caffe/layers/contrastive_loss_layer.cpp index 072a5a535be..0692c11c257 100644 --- a/src/caffe/layers/contrastive_loss_layer.cpp +++ b/src/caffe/layers/contrastive_loss_layer.cpp @@ -10,7 +10,7 @@ namespace caffe { template void ContrastiveLossLayer::LayerSetUp( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::LayerSetUp(bottom, top); CHECK_EQ(bottom[0]->channels(), bottom[1]->channels()); CHECK_EQ(bottom[0]->height(), 1); @@ -32,7 +32,7 @@ void ContrastiveLossLayer::LayerSetUp( template void ContrastiveLossLayer::Forward_cpu( const vector*>& bottom, - vector*>* top) { + const vector*>& top) { int count = bottom[0]->count(); caffe_sub( count, @@ -52,23 +52,23 @@ void ContrastiveLossLayer::Forward_cpu( } } loss = loss / static_cast(bottom[0]->num()) / Dtype(2); - (*top)[0]->mutable_cpu_data()[0] = loss; + top[0]->mutable_cpu_data()[0] = loss; } template void ContrastiveLossLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { Dtype margin = this->layer_param_.contrastive_loss_param().margin(); for (int i = 0; i < 2; ++i) { if (propagate_down[i]) { const Dtype sign = (i == 0) ? 1 : -1; const Dtype alpha = sign * top[0]->cpu_diff()[0] / - static_cast((*bottom)[i]->num()); - int num = (*bottom)[i]->num(); - int channels = (*bottom)[i]->channels(); + static_cast(bottom[i]->num()); + int num = bottom[i]->num(); + int channels = bottom[i]->channels(); for (int j = 0; j < num; ++j) { - Dtype* bout = (*bottom)[i]->mutable_cpu_diff(); - if (static_cast((*bottom)[2]->cpu_data()[j])) { // similar pairs + Dtype* bout = bottom[i]->mutable_cpu_diff(); + if (static_cast(bottom[2]->cpu_data()[j])) { // similar pairs caffe_cpu_axpby( channels, alpha, @@ -97,5 +97,6 @@ STUB_GPU(ContrastiveLossLayer); #endif INSTANTIATE_CLASS(ContrastiveLossLayer); +REGISTER_LAYER_CLASS(ContrastiveLoss); } // namespace caffe diff --git a/src/caffe/layers/contrastive_loss_layer.cu b/src/caffe/layers/contrastive_loss_layer.cu index 672ad5bc2f8..78a55995a0a 100644 --- a/src/caffe/layers/contrastive_loss_layer.cu +++ b/src/caffe/layers/contrastive_loss_layer.cu @@ -10,7 +10,7 @@ namespace caffe { template void ContrastiveLossLayer::Forward_gpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { const int count = bottom[0]->count(); caffe_gpu_sub( count, @@ -41,7 +41,7 @@ void ContrastiveLossLayer::Forward_gpu( } } loss = loss / static_cast(bottom[0]->num()) / Dtype(2); - (*top)[0]->mutable_cpu_data()[0] = loss; + top[0]->mutable_cpu_data()[0] = loss; } template @@ -65,27 +65,27 @@ __global__ void CLLForward(const int count, const int channels, template void ContrastiveLossLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { for (int i = 0; i < 2; ++i) { if (propagate_down[i]) { - const int count = (*bottom)[0]->count(); - const int channels = (*bottom)[0]->channels(); + const int count = bottom[0]->count(); + const int channels = bottom[0]->channels(); Dtype margin = this->layer_param_.contrastive_loss_param().margin(); const Dtype sign = (i == 0) ? 1 : -1; const Dtype alpha = sign * top[0]->cpu_diff()[0] / - static_cast((*bottom)[0]->num()); + static_cast(bottom[0]->num()); // NOLINT_NEXT_LINE(whitespace/operators) CLLForward<<>>( count, channels, margin, alpha, - (*bottom)[2]->gpu_data(), // pair similarity 0 or 1 + bottom[2]->gpu_data(), // pair similarity 0 or 1 diff_.gpu_data(), // the cached eltwise difference between a and b dist_sq_.gpu_data(), // the cached square distance between a and b - (*bottom)[i]->mutable_gpu_diff()); + bottom[i]->mutable_gpu_diff()); CUDA_POST_KERNEL_CHECK; } } } -INSTANTIATE_CLASS(ContrastiveLossLayer); +INSTANTIATE_LAYER_GPU_FUNCS(ContrastiveLossLayer); } // namespace caffe diff --git a/src/caffe/layers/conv_layer.cpp b/src/caffe/layers/conv_layer.cpp index 58918fd4baf..c0c9f6f3371 100644 --- a/src/caffe/layers/conv_layer.cpp +++ b/src/caffe/layers/conv_layer.cpp @@ -9,157 +9,26 @@ namespace caffe { template -void ConvolutionLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { - // Configure the kernel size, padding, stride, and inputs. - ConvolutionParameter conv_param = this->layer_param_.convolution_param(); - CHECK(!conv_param.has_kernel_size() != - !(conv_param.has_kernel_h() && conv_param.has_kernel_w())) - << "Filter size is kernel_size OR kernel_h and kernel_w; not both"; - CHECK(conv_param.has_kernel_size() || - (conv_param.has_kernel_h() && conv_param.has_kernel_w())) - << "For non-square filters both kernel_h and kernel_w are required."; - CHECK((!conv_param.has_pad() && conv_param.has_pad_h() - && conv_param.has_pad_w()) - || (!conv_param.has_pad_h() && !conv_param.has_pad_w())) - << "pad is pad OR pad_h and pad_w are required."; - CHECK((!conv_param.has_stride() && conv_param.has_stride_h() - && conv_param.has_stride_w()) - || (!conv_param.has_stride_h() && !conv_param.has_stride_w())) - << "Stride is stride OR stride_h and stride_w are required."; - if (conv_param.has_kernel_size()) { - kernel_h_ = kernel_w_ = conv_param.kernel_size(); - } else { - kernel_h_ = conv_param.kernel_h(); - kernel_w_ = conv_param.kernel_w(); - } - CHECK_GT(kernel_h_, 0) << "Filter dimensions cannot be zero."; - CHECK_GT(kernel_w_, 0) << "Filter dimensions cannot be zero."; - if (!conv_param.has_pad_h()) { - pad_h_ = pad_w_ = conv_param.pad(); - } else { - pad_h_ = conv_param.pad_h(); - pad_w_ = conv_param.pad_w(); - } - if (!conv_param.has_stride_h()) { - stride_h_ = stride_w_ = conv_param.stride(); - } else { - stride_h_ = conv_param.stride_h(); - stride_w_ = conv_param.stride_w(); - } - // Configure output channels and groups. - channels_ = bottom[0]->channels(); - num_output_ = this->layer_param_.convolution_param().num_output(); - CHECK_GT(num_output_, 0); - group_ = this->layer_param_.convolution_param().group(); - CHECK_EQ(channels_ % group_, 0); - CHECK_EQ(num_output_ % group_, 0) - << "Number of output should be multiples of group."; - // Handle the parameters: weights and biases. - // - blobs_[0] holds the filter weights - // - blobs_[1] holds the biases (optional) - bias_term_ = this->layer_param_.convolution_param().bias_term(); - if (this->blobs_.size() > 0) { - LOG(INFO) << "Skipping parameter initialization"; - } else { - if (bias_term_) { - this->blobs_.resize(2); - } else { - this->blobs_.resize(1); - } - // Initialize and fill the weights: - // output channels x input channels per-group x kernel height x kernel width - this->blobs_[0].reset(new Blob( - num_output_, channels_ / group_, kernel_h_, kernel_w_)); - shared_ptr > weight_filler(GetFiller( - this->layer_param_.convolution_param().weight_filler())); - weight_filler->Fill(this->blobs_[0].get()); - // If necessary, initialize and fill the biases: - // 1 x 1 x 1 x output channels - if (bias_term_) { - this->blobs_[1].reset(new Blob(1, 1, 1, num_output_)); - shared_ptr > bias_filler(GetFiller( - this->layer_param_.convolution_param().bias_filler())); - bias_filler->Fill(this->blobs_[1].get()); - } - } - // Propagate gradients to the parameters (as directed by backward pass). - this->param_propagate_down_.resize(this->blobs_.size(), true); -} - -template -void ConvolutionLayer::Reshape(const vector*>& bottom, - vector*>* top) { - num_ = bottom[0]->num(); - height_ = bottom[0]->height(); - width_ = bottom[0]->width(); - CHECK_EQ(bottom[0]->channels(), channels_) << "Input size incompatible with" - " convolution kernel."; - // TODO: generalize to handle inputs of different shapes. - for (int bottom_id = 1; bottom_id < bottom.size(); ++bottom_id) { - CHECK_EQ(num_, bottom[bottom_id]->num()) << "Inputs must have same num."; - CHECK_EQ(channels_, bottom[bottom_id]->channels()) - << "Inputs must have same channels."; - CHECK_EQ(height_, bottom[bottom_id]->height()) - << "Inputs must have same height."; - CHECK_EQ(width_, bottom[bottom_id]->width()) - << "Inputs must have same width."; - } - // Shape the tops. - height_out_ = - (height_ + 2 * pad_h_ - kernel_h_) / stride_h_ + 1; - width_out_ = (width_ + 2 * pad_w_ - kernel_w_) / stride_w_ + 1; - for (int top_id = 0; top_id < top->size(); ++top_id) { - (*top)[top_id]->Reshape(num_, num_output_, height_out_, width_out_); - } - // Prepare the matrix multiplication computation. - // Each input will be convolved as a single GEMM. - M_ = num_output_ / group_; - K_ = channels_ * kernel_h_ * kernel_w_ / group_; - N_ = height_out_ * width_out_; - // The im2col result buffer will only hold one image at a time to avoid - // overly large memory usage. - col_buffer_.Reshape( - 1, channels_ * kernel_h_ * kernel_w_, height_out_, width_out_); - for (int top_id = 0; top_id < top->size(); ++top_id) { - (*top)[top_id]->Reshape(num_, num_output_, height_out_, width_out_); - } - // Set up the all ones "bias multiplier" for adding biases by BLAS - if (bias_term_) { - bias_multiplier_.Reshape(1, 1, 1, N_); - caffe_set(N_, Dtype(1), bias_multiplier_.mutable_cpu_data()); - } +void ConvolutionLayer::compute_output_shape() { + this->height_out_ = (this->height_ + 2 * this->pad_h_ - this->kernel_h_) + / this->stride_h_ + 1; + this->width_out_ = (this->width_ + 2 * this->pad_w_ - this->kernel_w_) + / this->stride_w_ + 1; } template void ConvolutionLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { + const Dtype* weight = this->blobs_[0]->cpu_data(); for (int i = 0; i < bottom.size(); ++i) { const Dtype* bottom_data = bottom[i]->cpu_data(); - Dtype* top_data = (*top)[i]->mutable_cpu_data(); - Dtype* col_data = col_buffer_.mutable_cpu_data(); - const Dtype* weight = this->blobs_[0]->cpu_data(); - int weight_offset = M_ * K_; // number of filter parameters in a group - int col_offset = K_ * N_; // number of values in an input region / column - int top_offset = M_ * N_; // number of values in an output region / column - for (int n = 0; n < num_; ++n) { - // im2col transformation: unroll input regions for filtering - // into column matrix for multplication. - im2col_cpu(bottom_data + bottom[i]->offset(n), channels_, height_, - width_, kernel_h_, kernel_w_, pad_h_, pad_w_, stride_h_, stride_w_, - col_data); - // Take inner products for groups. - for (int g = 0; g < group_; ++g) { - caffe_cpu_gemm(CblasNoTrans, CblasNoTrans, M_, N_, K_, - (Dtype)1., weight + weight_offset * g, col_data + col_offset * g, - (Dtype)0., top_data + (*top)[i]->offset(n) + top_offset * g); - } - // Add bias. - if (bias_term_) { - caffe_cpu_gemm(CblasNoTrans, CblasNoTrans, num_output_, - N_, 1, (Dtype)1., this->blobs_[1]->cpu_data(), - bias_multiplier_.cpu_data(), - (Dtype)1., top_data + (*top)[i]->offset(n)); + Dtype* top_data = top[i]->mutable_cpu_data(); + for (int n = 0; n < this->num_; ++n) { + this->forward_cpu_gemm(bottom_data + bottom[i]->offset(n), weight, + top_data + top[i]->offset(n)); + if (this->bias_term_) { + const Dtype* bias = this->blobs_[1]->cpu_data(); + this->forward_cpu_bias(top_data + top[i]->offset(n), bias); } } } @@ -167,72 +36,38 @@ void ConvolutionLayer::Forward_cpu(const vector*>& bottom, template void ConvolutionLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { - const Dtype* weight = NULL; - Dtype* weight_diff = NULL; + const vector& propagate_down, const vector*>& bottom) { + const Dtype* weight = this->blobs_[0]->cpu_data(); + Dtype* weight_diff = this->blobs_[0]->mutable_cpu_diff(); if (this->param_propagate_down_[0]) { - weight = this->blobs_[0]->cpu_data(); - weight_diff = this->blobs_[0]->mutable_cpu_diff(); caffe_set(this->blobs_[0]->count(), Dtype(0), weight_diff); } - Dtype* bias_diff = NULL; - if (bias_term_ && this->param_propagate_down_[1]) { - bias_diff = this->blobs_[1]->mutable_cpu_diff(); - caffe_set(this->blobs_[1]->count(), Dtype(0), bias_diff); + if (this->bias_term_ && this->param_propagate_down_[1]) { + caffe_set(this->blobs_[1]->count(), Dtype(0), + this->blobs_[1]->mutable_cpu_diff()); } - const int weight_offset = M_ * K_; - const int col_offset = K_ * N_; - const int top_offset = M_ * N_; for (int i = 0; i < top.size(); ++i) { - const Dtype* top_diff = NULL; + const Dtype* top_diff = top[i]->cpu_diff(); + const Dtype* bottom_data = bottom[i]->cpu_data(); + Dtype* bottom_diff = bottom[i]->mutable_cpu_diff(); // Bias gradient, if necessary. - if (bias_term_ && this->param_propagate_down_[1]) { - top_diff = top[i]->cpu_diff(); - for (int n = 0; n < num_; ++n) { - caffe_cpu_gemv(CblasNoTrans, num_output_, N_, - 1., top_diff + top[0]->offset(n), - bias_multiplier_.cpu_data(), 1., - bias_diff); + if (this->bias_term_ && this->param_propagate_down_[1]) { + Dtype* bias_diff = this->blobs_[1]->mutable_cpu_diff(); + for (int n = 0; n < this->num_; ++n) { + this->backward_cpu_bias(bias_diff, top_diff + top[i]->offset(n)); } } if (this->param_propagate_down_[0] || propagate_down[i]) { - if (!top_diff) { - top_diff = top[i]->cpu_diff(); - } - Dtype* col_data = col_buffer_.mutable_cpu_data(); - Dtype* col_diff = col_buffer_.mutable_cpu_diff(); - const Dtype* bottom_data = (*bottom)[i]->cpu_data(); - Dtype* bottom_diff = (*bottom)[i]->mutable_cpu_diff(); - for (int n = 0; n < num_; ++n) { - // Since we saved memory in the forward pass by not storing all col - // data, we will need to recompute them. - im2col_cpu(bottom_data + (*bottom)[i]->offset(n), channels_, height_, - width_, kernel_h_, kernel_w_, pad_h_, pad_w_, - stride_h_, stride_w_, col_data); + for (int n = 0; n < this->num_; ++n) { // gradient w.r.t. weight. Note that we will accumulate diffs. if (this->param_propagate_down_[0]) { - for (int g = 0; g < group_; ++g) { - caffe_cpu_gemm(CblasNoTrans, CblasTrans, M_, K_, N_, - (Dtype)1., top_diff + top[i]->offset(n) + top_offset * g, - col_data + col_offset * g, (Dtype)1., - weight_diff + weight_offset * g); - } + this->weight_cpu_gemm(bottom_data + bottom[i]->offset(n), + top_diff + top[i]->offset(n), weight_diff); } // gradient w.r.t. bottom data, if necessary. if (propagate_down[i]) { - if (weight == NULL) { - weight = this->blobs_[0]->cpu_data(); - } - for (int g = 0; g < group_; ++g) { - caffe_cpu_gemm(CblasTrans, CblasNoTrans, K_, N_, M_, - (Dtype)1., weight + weight_offset * g, - top_diff + top[i]->offset(n) + top_offset * g, - (Dtype)0., col_diff + col_offset * g); - } - // col2im back to the data - col2im_cpu(col_diff, channels_, height_, width_, - kernel_h_, kernel_w_, pad_h_, pad_w_, - stride_h_, stride_w_, bottom_diff + (*bottom)[i]->offset(n)); + this->backward_cpu_gemm(top_diff + top[i]->offset(n), weight, + bottom_diff + bottom[i]->offset(n)); } } } diff --git a/src/caffe/layers/conv_layer.cu b/src/caffe/layers/conv_layer.cu index 43f76a2368f..3902fdf3930 100644 --- a/src/caffe/layers/conv_layer.cu +++ b/src/caffe/layers/conv_layer.cu @@ -8,117 +8,64 @@ namespace caffe { -/// @brief refer to CPU forward -- the BLAS implementation is the same. template void ConvolutionLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { + const Dtype* weight = this->blobs_[0]->gpu_data(); for (int i = 0; i < bottom.size(); ++i) { const Dtype* bottom_data = bottom[i]->gpu_data(); - Dtype* top_data = (*top)[i]->mutable_gpu_data(); - Dtype* col_data = col_buffer_.mutable_gpu_data(); - const Dtype* weight = this->blobs_[0]->gpu_data(); - int weight_offset = M_ * K_; - int col_offset = K_ * N_; - int top_offset = M_ * N_; - for (int n = 0; n < num_; ++n) { - // im2col transformation: unroll input regions for filtering - // into column matrix for multplication. - im2col_gpu(bottom_data + bottom[i]->offset(n), channels_, height_, - width_, kernel_h_, kernel_w_, pad_h_, pad_w_, stride_h_, stride_w_, - col_data); - // Take inner products for groups. - for (int g = 0; g < group_; ++g) { - caffe_gpu_gemm(CblasNoTrans, CblasNoTrans, M_, N_, K_, - (Dtype)1., weight + weight_offset * g, col_data + col_offset * g, - (Dtype)0., top_data + (*top)[i]->offset(n) + top_offset * g); - } - // Add bias. - if (bias_term_) { - caffe_gpu_gemm(CblasNoTrans, CblasNoTrans, num_output_, - N_, 1, (Dtype)1., this->blobs_[1]->gpu_data(), - bias_multiplier_.gpu_data(), - (Dtype)1., top_data + (*top)[i]->offset(n)); + Dtype* top_data = top[i]->mutable_gpu_data(); + for (int n = 0; n < this->num_; ++n) { + this->forward_gpu_gemm(bottom_data + bottom[i]->offset(n), weight, + top_data + top[i]->offset(n)); + if (this->bias_term_) { + const Dtype* bias = this->blobs_[1]->gpu_data(); + this->forward_gpu_bias(top_data + top[i]->offset(n), bias); } } } } -/// @brief refer to CPU backward -- the BLAS implementation is the same. template void ConvolutionLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { - const Dtype* weight = NULL; - Dtype* weight_diff = NULL; + const vector& propagate_down, const vector*>& bottom) { + const Dtype* weight = this->blobs_[0]->gpu_data(); + Dtype* weight_diff = this->blobs_[0]->mutable_gpu_diff(); if (this->param_propagate_down_[0]) { - weight = this->blobs_[0]->gpu_data(); - weight_diff = this->blobs_[0]->mutable_gpu_diff(); caffe_gpu_set(this->blobs_[0]->count(), Dtype(0), weight_diff); } - Dtype* bias_diff = NULL; - if (bias_term_ && this->param_propagate_down_[1]) { - bias_diff = this->blobs_[1]->mutable_gpu_diff(); - caffe_gpu_set(this->blobs_[1]->count(), Dtype(0), bias_diff); + if (this->bias_term_ && this->param_propagate_down_[1]) { + caffe_gpu_set(this->blobs_[1]->count(), Dtype(0), + this->blobs_[1]->mutable_gpu_diff()); } - const int weight_offset = M_ * K_; - const int col_offset = K_ * N_; - const int top_offset = M_ * N_; for (int i = 0; i < top.size(); ++i) { - const Dtype* top_diff = NULL; + const Dtype* top_diff = top[i]->gpu_diff(); // Bias gradient, if necessary. - if (bias_term_ && this->param_propagate_down_[1]) { - top_diff = top[i]->gpu_diff(); - for (int n = 0; n < num_; ++n) { - caffe_gpu_gemv(CblasNoTrans, num_output_, N_, - 1., top_diff + top[0]->offset(n), - bias_multiplier_.gpu_data(), 1., - bias_diff); + if (this->bias_term_ && this->param_propagate_down_[1]) { + Dtype* bias_diff = this->blobs_[1]->mutable_gpu_diff(); + for (int n = 0; n < this->num_; ++n) { + this->backward_gpu_bias(bias_diff, top_diff + top[i]->offset(n)); } } if (this->param_propagate_down_[0] || propagate_down[i]) { - if (!top_diff) { - top_diff = top[i]->gpu_diff(); - } - Dtype* col_data = col_buffer_.mutable_gpu_data(); - Dtype* col_diff = col_buffer_.mutable_gpu_diff(); - const Dtype* bottom_data = (*bottom)[i]->gpu_data(); - Dtype* bottom_diff = (*bottom)[i]->mutable_gpu_diff(); - for (int n = 0; n < num_; ++n) { - // Since we saved memory in the forward pass by not storing all col - // data, we will need to recompute them. - im2col_gpu(bottom_data + (*bottom)[i]->offset(n), channels_, height_, - width_, kernel_h_, kernel_w_, pad_h_, pad_w_, - stride_h_, stride_w_, col_data); + const Dtype* bottom_data = bottom[i]->gpu_data(); + Dtype* bottom_diff = bottom[i]->mutable_gpu_diff(); + for (int n = 0; n < this->num_; ++n) { // gradient w.r.t. weight. Note that we will accumulate diffs. if (this->param_propagate_down_[0]) { - for (int g = 0; g < group_; ++g) { - caffe_gpu_gemm(CblasNoTrans, CblasTrans, M_, K_, N_, - (Dtype)1., top_diff + top[i]->offset(n) + top_offset * g, - col_data + col_offset * g, (Dtype)1., - weight_diff + weight_offset * g); - } + this->weight_gpu_gemm(bottom_data + bottom[i]->offset(n), + top_diff + top[i]->offset(n), weight_diff); } - // gradient w.r.t. bottom data, if necessary + // gradient w.r.t. bottom data, if necessary. if (propagate_down[i]) { - if (weight == NULL) { - weight = this->blobs_[0]->gpu_data(); - } - for (int g = 0; g < group_; ++g) { - caffe_gpu_gemm(CblasTrans, CblasNoTrans, K_, N_, M_, - (Dtype)1., weight + weight_offset * g, - top_diff + top[i]->offset(n) + top_offset * g, - (Dtype)0., col_diff + col_offset * g); - } - // col2im back to the data - col2im_gpu(col_diff, channels_, height_, width_, - kernel_h_, kernel_w_, pad_h_, pad_w_, stride_h_, stride_w_, - bottom_diff + (*bottom)[i]->offset(n)); + this->backward_gpu_gemm(top_diff + top[i]->offset(n), weight, + bottom_diff + bottom[i]->offset(n)); } } } } } - -INSTANTIATE_CLASS(ConvolutionLayer); +INSTANTIATE_LAYER_GPU_FUNCS(ConvolutionLayer); } // namespace caffe diff --git a/src/caffe/layers/cudnn_conv_layer.cpp b/src/caffe/layers/cudnn_conv_layer.cpp index 137bbab1976..4a69ca20d0a 100644 --- a/src/caffe/layers/cudnn_conv_layer.cpp +++ b/src/caffe/layers/cudnn_conv_layer.cpp @@ -19,7 +19,7 @@ namespace caffe { */ template void CuDNNConvolutionLayer::LayerSetUp( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { ConvolutionLayer::LayerSetUp(bottom, top); // Initialize CUDA streams and cuDNN. stream_ = new cudaStream_t[this->group_ * CUDNN_STREAMS_PER_GROUP]; @@ -58,11 +58,13 @@ void CuDNNConvolutionLayer::LayerSetUp( if (this->bias_term_) { cudnn::createTensor4dDesc(&bias_desc_); } + + handles_setup_ = true; } template void CuDNNConvolutionLayer::Reshape( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { ConvolutionLayer::Reshape(bottom, top); bottom_offset_ = (this->channels_ / this->group_) * this->height_ * this->width_; @@ -98,6 +100,9 @@ void CuDNNConvolutionLayer::Reshape( template CuDNNConvolutionLayer::~CuDNNConvolutionLayer() { + // Check that handles have been setup before destroying. + if (!handles_setup_) { return; } + for (int i = 0; i < bottom_descs_.size(); i++) { cudnnDestroyTensor4dDescriptor(bottom_descs_[i]); cudnnDestroyTensor4dDescriptor(top_descs_[i]); diff --git a/src/caffe/layers/cudnn_conv_layer.cu b/src/caffe/layers/cudnn_conv_layer.cu index 2af13309e18..071014e1b48 100644 --- a/src/caffe/layers/cudnn_conv_layer.cu +++ b/src/caffe/layers/cudnn_conv_layer.cu @@ -13,10 +13,10 @@ __global__ void sync_conv_groups() { } template void CuDNNConvolutionLayer::Forward_gpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { for (int i = 0; i < bottom.size(); ++i) { const Dtype* bottom_data = bottom[i]->gpu_data(); - Dtype* top_data = (*top)[i]->mutable_gpu_data(); + Dtype* top_data = top[i]->mutable_gpu_data(); const Dtype* weight = this->blobs_[0]->gpu_data(); // Forward through cuDNN in parallel over groups. @@ -48,7 +48,7 @@ void CuDNNConvolutionLayer::Forward_gpu( template void CuDNNConvolutionLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const Dtype* weight = NULL; Dtype* weight_diff = NULL; if (this->param_propagate_down_[0]) { @@ -75,7 +75,7 @@ void CuDNNConvolutionLayer::Backward_gpu(const vector*>& top, // Gradient w.r.t. weights. if (this->param_propagate_down_[0]) { - const Dtype* bottom_data = (*bottom)[i]->gpu_data(); + const Dtype* bottom_data = bottom[i]->gpu_data(); CUDNN_CHECK(cudnnConvolutionBackwardFilter(handle_[1*this->group_ + g], bottom_descs_[i], bottom_data + bottom_offset_ * g, top_descs_[i], top_diff + top_offset_ * g, @@ -86,7 +86,10 @@ void CuDNNConvolutionLayer::Backward_gpu(const vector*>& top, // Gradient w.r.t. bottom data. if (propagate_down[i]) { - Dtype* bottom_diff = (*bottom)[i]->mutable_gpu_diff(); + if (weight == NULL) { + weight = this->blobs_[0]->gpu_data(); + } + Dtype* bottom_diff = bottom[i]->mutable_gpu_diff(); CUDNN_CHECK(cudnnConvolutionBackwardData(handle_[2*this->group_ + g], filter_desc_, weight + weight_offset_ * g, top_descs_[i], top_diff + top_offset_ * g, @@ -103,7 +106,7 @@ void CuDNNConvolutionLayer::Backward_gpu(const vector*>& top, } } -INSTANTIATE_CLASS(CuDNNConvolutionLayer); +INSTANTIATE_LAYER_GPU_FUNCS(CuDNNConvolutionLayer); } // namespace caffe #endif diff --git a/src/caffe/layers/cudnn_pooling_layer.cpp b/src/caffe/layers/cudnn_pooling_layer.cpp index 5aea0dc886e..dd90195637b 100644 --- a/src/caffe/layers/cudnn_pooling_layer.cpp +++ b/src/caffe/layers/cudnn_pooling_layer.cpp @@ -11,20 +11,23 @@ namespace caffe { template void CuDNNPoolingLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { PoolingLayer::LayerSetUp(bottom, top); - + // Sanity check: CUDNN currently only supports pad == 0. + CHECK_EQ(this->pad_h_, 0); + CHECK_EQ(this->pad_w_, 0); CUDNN_CHECK(cudnnCreate(&handle_)); cudnn::createTensor4dDesc(&bottom_desc_); cudnn::createTensor4dDesc(&top_desc_); cudnn::createPoolingDesc(&pooling_desc_, this->layer_param_.pooling_param().pool(), &mode_, this->kernel_h_, this->kernel_w_, this->stride_h_, this->stride_w_); + handles_setup_ = true; } template void CuDNNPoolingLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { PoolingLayer::Reshape(bottom, top); cudnn::setTensor4dDesc(&bottom_desc_, bottom[0]->num(), this->channels_, this->height_, this->width_); @@ -34,6 +37,9 @@ void CuDNNPoolingLayer::Reshape(const vector*>& bottom, template CuDNNPoolingLayer::~CuDNNPoolingLayer() { + // Check that handles have been setup before destroying. + if (!handles_setup_) { return; } + cudnnDestroyTensor4dDescriptor(bottom_desc_); cudnnDestroyTensor4dDescriptor(top_desc_); cudnnDestroyPoolingDescriptor(pooling_desc_); diff --git a/src/caffe/layers/cudnn_pooling_layer.cu b/src/caffe/layers/cudnn_pooling_layer.cu index 7cdc0afadcb..1c113aad75f 100644 --- a/src/caffe/layers/cudnn_pooling_layer.cu +++ b/src/caffe/layers/cudnn_pooling_layer.cu @@ -11,44 +11,29 @@ namespace caffe { template void CuDNNPoolingLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { - // Fallback to Caffe for padded pooling, max top mask. - if ((this->pad_h_ > 0 || this->pad_w_ > 0) || (*top).size() > 1) { - LOG_FIRST_N(WARNING, 1) - << "Falling back to standard Caffe for padded pooling."; - return PoolingLayer::Forward_gpu(bottom, top); - } - + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); CUDNN_CHECK(cudnnPoolingForward(handle_, pooling_desc_, bottom_desc_, bottom_data, top_desc_, top_data)); } template void CuDNNPoolingLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (!propagate_down[0]) { return; } - - // Fallback to Caffe for padded pooling, max top mask. - if ((this->pad_h_ > 0 || this->pad_w_ > 0) || top.size() > 1) { - LOG_FIRST_N(WARNING, 1) - << "Falling back to standard Caffe for padded pooling."; - return PoolingLayer::Backward_gpu(top, propagate_down, bottom); - } - const Dtype* top_diff = top[0]->gpu_diff(); const Dtype* top_data = top[0]->gpu_data(); - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); CUDNN_CHECK(cudnnPoolingBackward(handle_, pooling_desc_, top_desc_, top_data, top_desc_, top_diff, bottom_desc_, bottom_data, bottom_desc_, bottom_diff)); } -INSTANTIATE_CLASS(CuDNNPoolingLayer); +INSTANTIATE_LAYER_GPU_FUNCS(CuDNNPoolingLayer); } // namespace caffe #endif diff --git a/src/caffe/layers/cudnn_relu_layer.cpp b/src/caffe/layers/cudnn_relu_layer.cpp index 083868f572f..0b8a6bc3248 100644 --- a/src/caffe/layers/cudnn_relu_layer.cpp +++ b/src/caffe/layers/cudnn_relu_layer.cpp @@ -9,17 +9,18 @@ namespace caffe { template void CuDNNReLULayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { ReLULayer::LayerSetUp(bottom, top); // initialize cuDNN CUDNN_CHECK(cudnnCreate(&handle_)); cudnn::createTensor4dDesc(&bottom_desc_); cudnn::createTensor4dDesc(&top_desc_); + handles_setup_ = true; } template void CuDNNReLULayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { ReLULayer::Reshape(bottom, top); const int N = bottom[0]->num(); const int K = bottom[0]->channels(); @@ -31,6 +32,9 @@ void CuDNNReLULayer::Reshape(const vector*>& bottom, template CuDNNReLULayer::~CuDNNReLULayer() { + // Check that handles have been setup before destroying. + if (!handles_setup_) { return; } + cudnnDestroyTensor4dDescriptor(this->bottom_desc_); cudnnDestroyTensor4dDescriptor(this->top_desc_); cudnnDestroy(this->handle_); diff --git a/src/caffe/layers/cudnn_relu_layer.cu b/src/caffe/layers/cudnn_relu_layer.cu index a8519f340cc..862508707a0 100644 --- a/src/caffe/layers/cudnn_relu_layer.cu +++ b/src/caffe/layers/cudnn_relu_layer.cu @@ -9,14 +9,14 @@ namespace caffe { template void CuDNNReLULayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { // Fallback to standard Caffe for leaky ReLU. if (ReLULayer::layer_param_.relu_param().negative_slope() != 0) { return ReLULayer::Forward_gpu(bottom, top); } const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); CUDNN_CHECK(cudnnActivationForward(this->handle_, CUDNN_ACTIVATION_RELU, this->bottom_desc_, bottom_data, this->top_desc_, top_data)); @@ -25,7 +25,7 @@ void CuDNNReLULayer::Forward_gpu(const vector*>& bottom, template void CuDNNReLULayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (!propagate_down[0]) { return; } @@ -37,15 +37,15 @@ void CuDNNReLULayer::Backward_gpu(const vector*>& top, const Dtype* top_data = top[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); CUDNN_CHECK(cudnnActivationBackward(this->handle_, CUDNN_ACTIVATION_RELU, this->top_desc_, top_data, this->top_desc_, top_diff, this->bottom_desc_, bottom_data, this->bottom_desc_, bottom_diff)); } -INSTANTIATE_CLASS(CuDNNReLULayer); +INSTANTIATE_LAYER_GPU_FUNCS(CuDNNReLULayer); } // namespace caffe #endif diff --git a/src/caffe/layers/cudnn_sigmoid_layer.cpp b/src/caffe/layers/cudnn_sigmoid_layer.cpp index 3fe800db6f4..67bd9c373b0 100644 --- a/src/caffe/layers/cudnn_sigmoid_layer.cpp +++ b/src/caffe/layers/cudnn_sigmoid_layer.cpp @@ -9,17 +9,18 @@ namespace caffe { template void CuDNNSigmoidLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { SigmoidLayer::LayerSetUp(bottom, top); // initialize cuDNN CUDNN_CHECK(cudnnCreate(&handle_)); cudnn::createTensor4dDesc(&bottom_desc_); cudnn::createTensor4dDesc(&top_desc_); + handles_setup_ = true; } template void CuDNNSigmoidLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { SigmoidLayer::Reshape(bottom, top); const int N = bottom[0]->num(); const int K = bottom[0]->channels(); @@ -31,6 +32,9 @@ void CuDNNSigmoidLayer::Reshape(const vector*>& bottom, template CuDNNSigmoidLayer::~CuDNNSigmoidLayer() { + // Check that handles have been setup before destroying. + if (!handles_setup_) { return; } + cudnnDestroyTensor4dDescriptor(this->bottom_desc_); cudnnDestroyTensor4dDescriptor(this->top_desc_); cudnnDestroy(this->handle_); diff --git a/src/caffe/layers/cudnn_sigmoid_layer.cu b/src/caffe/layers/cudnn_sigmoid_layer.cu index 43019bd78ae..31b094e25d4 100644 --- a/src/caffe/layers/cudnn_sigmoid_layer.cu +++ b/src/caffe/layers/cudnn_sigmoid_layer.cu @@ -9,9 +9,9 @@ namespace caffe { template void CuDNNSigmoidLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); CUDNN_CHECK(cudnnActivationForward(this->handle_, CUDNN_ACTIVATION_SIGMOID, this->bottom_desc_, bottom_data, this->top_desc_, top_data)); @@ -20,22 +20,22 @@ void CuDNNSigmoidLayer::Forward_gpu(const vector*>& bottom, template void CuDNNSigmoidLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (!propagate_down[0]) { return; } const Dtype* top_data = top[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); CUDNN_CHECK(cudnnActivationBackward(this->handle_, CUDNN_ACTIVATION_SIGMOID, this->top_desc_, top_data, this->top_desc_, top_diff, this->bottom_desc_, bottom_data, this->bottom_desc_, bottom_diff)); } -INSTANTIATE_CLASS(CuDNNSigmoidLayer); +INSTANTIATE_LAYER_GPU_FUNCS(CuDNNSigmoidLayer); } // namespace caffe #endif diff --git a/src/caffe/layers/cudnn_softmax_layer.cpp b/src/caffe/layers/cudnn_softmax_layer.cpp index 79ba5237ae3..83a5b69a626 100644 --- a/src/caffe/layers/cudnn_softmax_layer.cpp +++ b/src/caffe/layers/cudnn_softmax_layer.cpp @@ -13,17 +13,18 @@ namespace caffe { template void CuDNNSoftmaxLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { SoftmaxLayer::LayerSetUp(bottom, top); // Initialize CUDNN. CUDNN_CHECK(cudnnCreate(&handle_)); cudnn::createTensor4dDesc(&bottom_desc_); cudnn::createTensor4dDesc(&top_desc_); + handles_setup_ = true; } template void CuDNNSoftmaxLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { SoftmaxLayer::Reshape(bottom, top); int N = bottom[0]->num(); int K = bottom[0]->channels(); @@ -35,6 +36,9 @@ void CuDNNSoftmaxLayer::Reshape(const vector*>& bottom, template CuDNNSoftmaxLayer::~CuDNNSoftmaxLayer() { + // Check that handles have been setup before destroying. + if (!handles_setup_) { return; } + cudnnDestroyTensor4dDescriptor(bottom_desc_); cudnnDestroyTensor4dDescriptor(top_desc_); cudnnDestroy(handle_); diff --git a/src/caffe/layers/cudnn_softmax_layer.cu b/src/caffe/layers/cudnn_softmax_layer.cu index 300bdc496c8..f328afdd831 100644 --- a/src/caffe/layers/cudnn_softmax_layer.cu +++ b/src/caffe/layers/cudnn_softmax_layer.cu @@ -13,9 +13,9 @@ namespace caffe { template void CuDNNSoftmaxLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); CUDNN_CHECK(cudnnSoftmaxForward(handle_, CUDNN_SOFTMAX_ACCURATE, CUDNN_SOFTMAX_MODE_CHANNEL, bottom_desc_, bottom_data, top_desc_, top_data)); @@ -23,19 +23,19 @@ void CuDNNSoftmaxLayer::Forward_gpu(const vector*>& bottom, template void CuDNNSoftmaxLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (propagate_down[0]) { const Dtype* top_data = top[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); CUDNN_CHECK(cudnnSoftmaxBackward(handle_, CUDNN_SOFTMAX_ACCURATE, CUDNN_SOFTMAX_MODE_CHANNEL, top_desc_, top_data, top_desc_, top_diff, bottom_desc_, bottom_diff)); } } -INSTANTIATE_CLASS(CuDNNSoftmaxLayer); +INSTANTIATE_LAYER_GPU_FUNCS(CuDNNSoftmaxLayer); } // namespace caffe #endif diff --git a/src/caffe/layers/cudnn_tanh_layer.cpp b/src/caffe/layers/cudnn_tanh_layer.cpp index 7a5c06f6596..b1d2b86384e 100644 --- a/src/caffe/layers/cudnn_tanh_layer.cpp +++ b/src/caffe/layers/cudnn_tanh_layer.cpp @@ -9,17 +9,18 @@ namespace caffe { template void CuDNNTanHLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { TanHLayer::LayerSetUp(bottom, top); // initialize cuDNN CUDNN_CHECK(cudnnCreate(&handle_)); cudnn::createTensor4dDesc(&bottom_desc_); cudnn::createTensor4dDesc(&top_desc_); + handles_setup_ = true; } template void CuDNNTanHLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { TanHLayer::Reshape(bottom, top); const int N = bottom[0]->num(); const int K = bottom[0]->channels(); @@ -31,6 +32,9 @@ void CuDNNTanHLayer::Reshape(const vector*>& bottom, template CuDNNTanHLayer::~CuDNNTanHLayer() { + // Check that handles have been setup before destroying. + if (!handles_setup_) { return; } + cudnnDestroyTensor4dDescriptor(this->bottom_desc_); cudnnDestroyTensor4dDescriptor(this->top_desc_); cudnnDestroy(this->handle_); diff --git a/src/caffe/layers/cudnn_tanh_layer.cu b/src/caffe/layers/cudnn_tanh_layer.cu index c475b08c0ee..bf9ec7cfac4 100644 --- a/src/caffe/layers/cudnn_tanh_layer.cu +++ b/src/caffe/layers/cudnn_tanh_layer.cu @@ -9,9 +9,9 @@ namespace caffe { template void CuDNNTanHLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); CUDNN_CHECK(cudnnActivationForward(this->handle_, CUDNN_ACTIVATION_TANH, this->bottom_desc_, bottom_data, this->top_desc_, top_data)); @@ -20,22 +20,22 @@ void CuDNNTanHLayer::Forward_gpu(const vector*>& bottom, template void CuDNNTanHLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (!propagate_down[0]) { return; } const Dtype* top_data = top[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); CUDNN_CHECK(cudnnActivationBackward(this->handle_, CUDNN_ACTIVATION_TANH, this->top_desc_, top_data, this->top_desc_, top_diff, this->bottom_desc_, bottom_data, this->bottom_desc_, bottom_diff)); } -INSTANTIATE_CLASS(CuDNNTanHLayer); +INSTANTIATE_LAYER_GPU_FUNCS(CuDNNTanHLayer); } // namespace caffe #endif diff --git a/src/caffe/layers/data_layer.cpp b/src/caffe/layers/data_layer.cpp index d2071e2fa4f..8877caf89c8 100644 --- a/src/caffe/layers/data_layer.cpp +++ b/src/caffe/layers/data_layer.cpp @@ -1,4 +1,5 @@ -#include +#include + #include #include @@ -8,6 +9,7 @@ #include "caffe/data_layers.hpp" #include "caffe/layer.hpp" #include "caffe/proto/caffe.pb.h" +#include "caffe/util/benchmark.hpp" #include "caffe/util/io.hpp" #include "caffe/util/math_functions.hpp" #include "caffe/util/rng.hpp" @@ -17,192 +19,141 @@ namespace caffe { template DataLayer::~DataLayer() { this->JoinPrefetchThread(); - // clean up the database resources - switch (this->layer_param_.data_param().backend()) { - case DataParameter_DB_LEVELDB: - break; // do nothing - case DataParameter_DB_LMDB: - mdb_cursor_close(mdb_cursor_); - mdb_close(mdb_env_, mdb_dbi_); - mdb_txn_abort(mdb_txn_); - mdb_env_close(mdb_env_); - break; - default: - LOG(FATAL) << "Unknown database backend"; - } } template void DataLayer::DataLayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { // Initialize DB - switch (this->layer_param_.data_param().backend()) { - case DataParameter_DB_LEVELDB: - { - leveldb::DB* db_temp; - leveldb::Options options = GetLevelDBOptions(); - options.create_if_missing = false; - LOG(INFO) << "Opening leveldb " << this->layer_param_.data_param().source(); - leveldb::Status status = leveldb::DB::Open( - options, this->layer_param_.data_param().source(), &db_temp); - CHECK(status.ok()) << "Failed to open leveldb " - << this->layer_param_.data_param().source() << std::endl - << status.ToString(); - db_.reset(db_temp); - iter_.reset(db_->NewIterator(leveldb::ReadOptions())); - iter_->SeekToFirst(); - } - break; - case DataParameter_DB_LMDB: - CHECK_EQ(mdb_env_create(&mdb_env_), MDB_SUCCESS) << "mdb_env_create failed"; - CHECK_EQ(mdb_env_set_mapsize(mdb_env_, 1099511627776), MDB_SUCCESS); // 1TB - CHECK_EQ(mdb_env_open(mdb_env_, - this->layer_param_.data_param().source().c_str(), - MDB_RDONLY|MDB_NOTLS, 0664), MDB_SUCCESS) << "mdb_env_open failed"; - CHECK_EQ(mdb_txn_begin(mdb_env_, NULL, MDB_RDONLY, &mdb_txn_), MDB_SUCCESS) - << "mdb_txn_begin failed"; - CHECK_EQ(mdb_open(mdb_txn_, NULL, 0, &mdb_dbi_), MDB_SUCCESS) - << "mdb_open failed"; - CHECK_EQ(mdb_cursor_open(mdb_txn_, mdb_dbi_, &mdb_cursor_), MDB_SUCCESS) - << "mdb_cursor_open failed"; - LOG(INFO) << "Opening lmdb " << this->layer_param_.data_param().source(); - CHECK_EQ(mdb_cursor_get(mdb_cursor_, &mdb_key_, &mdb_value_, MDB_FIRST), - MDB_SUCCESS) << "mdb_cursor_get failed"; - break; - default: - LOG(FATAL) << "Unknown database backend"; - } + db_.reset(db::GetDB(this->layer_param_.data_param().backend())); + db_->Open(this->layer_param_.data_param().source(), db::READ); + cursor_.reset(db_->NewCursor()); - // Check if we would need to randomly skip a few data points + // Check if we should randomly skip a few data points if (this->layer_param_.data_param().rand_skip()) { unsigned int skip = caffe_rng_rand() % this->layer_param_.data_param().rand_skip(); LOG(INFO) << "Skipping first " << skip << " data points."; while (skip-- > 0) { - switch (this->layer_param_.data_param().backend()) { - case DataParameter_DB_LEVELDB: - iter_->Next(); - if (!iter_->Valid()) { - iter_->SeekToFirst(); - } - break; - case DataParameter_DB_LMDB: - if (mdb_cursor_get(mdb_cursor_, &mdb_key_, &mdb_value_, MDB_NEXT) - != MDB_SUCCESS) { - CHECK_EQ(mdb_cursor_get(mdb_cursor_, &mdb_key_, &mdb_value_, - MDB_FIRST), MDB_SUCCESS); - } - break; - default: - LOG(FATAL) << "Unknown database backend"; - } + cursor_->Next(); } } // Read a data point, and use it to initialize the top blob. Datum datum; - switch (this->layer_param_.data_param().backend()) { - case DataParameter_DB_LEVELDB: - datum.ParseFromString(iter_->value().ToString()); - break; - case DataParameter_DB_LMDB: - datum.ParseFromArray(mdb_value_.mv_data, mdb_value_.mv_size); - break; - default: - LOG(FATAL) << "Unknown database backend"; - } + datum.ParseFromString(cursor_->value()); + bool force_color = this->layer_param_.data_param().force_encoded_color(); + if ((force_color && DecodeDatum(&datum, true)) || + DecodeDatumNative(&datum)) { + LOG(INFO) << "Decoding Datum"; + } // image int crop_size = this->layer_param_.transform_param().crop_size(); if (crop_size > 0) { - (*top)[0]->Reshape(this->layer_param_.data_param().batch_size(), - datum.channels(), crop_size, crop_size); + top[0]->Reshape(this->layer_param_.data_param().batch_size(), + datum.channels(), crop_size, crop_size); this->prefetch_data_.Reshape(this->layer_param_.data_param().batch_size(), datum.channels(), crop_size, crop_size); + this->transformed_data_.Reshape(1, datum.channels(), crop_size, crop_size); } else { - (*top)[0]->Reshape( + top[0]->Reshape( this->layer_param_.data_param().batch_size(), datum.channels(), datum.height(), datum.width()); this->prefetch_data_.Reshape(this->layer_param_.data_param().batch_size(), datum.channels(), datum.height(), datum.width()); + this->transformed_data_.Reshape(1, datum.channels(), + datum.height(), datum.width()); } - LOG(INFO) << "output data size: " << (*top)[0]->num() << "," - << (*top)[0]->channels() << "," << (*top)[0]->height() << "," - << (*top)[0]->width(); + LOG(INFO) << "output data size: " << top[0]->num() << "," + << top[0]->channels() << "," << top[0]->height() << "," + << top[0]->width(); // label if (this->output_labels_) { - (*top)[1]->Reshape(this->layer_param_.data_param().batch_size(), 1, 1, 1); + top[1]->Reshape(this->layer_param_.data_param().batch_size(), 1, 1, 1); this->prefetch_label_.Reshape(this->layer_param_.data_param().batch_size(), 1, 1, 1); } - // datum size - this->datum_channels_ = datum.channels(); - this->datum_height_ = datum.height(); - this->datum_width_ = datum.width(); - this->datum_size_ = datum.channels() * datum.height() * datum.width(); } // This function is used to create a thread that prefetches the data. template void DataLayer::InternalThreadEntry() { - Datum datum; + CPUTimer batch_timer; + batch_timer.Start(); + double read_time = 0; + double trans_time = 0; + CPUTimer timer; CHECK(this->prefetch_data_.count()); + CHECK(this->transformed_data_.count()); + + // Reshape on single input batches for inputs of varying dimension. + const int batch_size = this->layer_param_.data_param().batch_size(); + const int crop_size = this->layer_param_.transform_param().crop_size(); + if (batch_size == 1 && crop_size == 0) { + Datum datum; + datum.ParseFromString(cursor_->value()); + this->prefetch_data_.Reshape(1, datum.channels(), + datum.height(), datum.width()); + this->transformed_data_.Reshape(1, datum.channels(), + datum.height(), datum.width()); + } + Dtype* top_data = this->prefetch_data_.mutable_cpu_data(); Dtype* top_label = NULL; // suppress warnings about uninitialized variables + if (this->output_labels_) { top_label = this->prefetch_label_.mutable_cpu_data(); } - const int batch_size = this->layer_param_.data_param().batch_size(); - + bool force_color = this->layer_param_.data_param().force_encoded_color(); for (int item_id = 0; item_id < batch_size; ++item_id) { + timer.Start(); // get a blob - switch (this->layer_param_.data_param().backend()) { - case DataParameter_DB_LEVELDB: - CHECK(iter_); - CHECK(iter_->Valid()); - datum.ParseFromString(iter_->value().ToString()); - break; - case DataParameter_DB_LMDB: - CHECK_EQ(mdb_cursor_get(mdb_cursor_, &mdb_key_, - &mdb_value_, MDB_GET_CURRENT), MDB_SUCCESS); - datum.ParseFromArray(mdb_value_.mv_data, - mdb_value_.mv_size); - break; - default: - LOG(FATAL) << "Unknown database backend"; + Datum datum; + datum.ParseFromString(cursor_->value()); + + cv::Mat cv_img; + if (datum.encoded()) { + if (force_color) { + cv_img = DecodeDatumToCVMat(datum, true); + } else { + cv_img = DecodeDatumToCVMatNative(datum); + } + if (cv_img.channels() != this->transformed_data_.channels()) { + LOG(WARNING) << "Your dataset contains encoded images with mixed " + << "channel sizes. Consider adding a 'force_color' flag to the " + << "model definition, or rebuild your dataset using " + << "convert_imageset."; + } } + read_time += timer.MicroSeconds(); + timer.Start(); // Apply data transformations (mirror, scale, crop...) - this->data_transformer_.Transform(item_id, datum, this->mean_, top_data); - + int offset = this->prefetch_data_.offset(item_id); + this->transformed_data_.set_cpu_data(top_data + offset); + if (datum.encoded()) { + this->data_transformer_->Transform(cv_img, &(this->transformed_data_)); + } else { + this->data_transformer_->Transform(datum, &(this->transformed_data_)); + } if (this->output_labels_) { top_label[item_id] = datum.label(); } - + trans_time += timer.MicroSeconds(); // go to the next iter - switch (this->layer_param_.data_param().backend()) { - case DataParameter_DB_LEVELDB: - iter_->Next(); - if (!iter_->Valid()) { - // We have reached the end. Restart from the first. - DLOG(INFO) << "Restarting data prefetching from start."; - iter_->SeekToFirst(); - } - break; - case DataParameter_DB_LMDB: - if (mdb_cursor_get(mdb_cursor_, &mdb_key_, - &mdb_value_, MDB_NEXT) != MDB_SUCCESS) { - // We have reached the end. Restart from the first. - DLOG(INFO) << "Restarting data prefetching from start."; - CHECK_EQ(mdb_cursor_get(mdb_cursor_, &mdb_key_, - &mdb_value_, MDB_FIRST), MDB_SUCCESS); - } - break; - default: - LOG(FATAL) << "Unknown database backend"; + cursor_->Next(); + if (!cursor_->valid()) { + DLOG(INFO) << "Restarting data prefetching from start."; + cursor_->SeekToFirst(); } } + batch_timer.Stop(); + DLOG(INFO) << "Prefetch batch: " << batch_timer.MilliSeconds() << " ms."; + DLOG(INFO) << " Read time: " << read_time / 1000 << " ms."; + DLOG(INFO) << "Transform time: " << trans_time / 1000 << " ms."; } INSTANTIATE_CLASS(DataLayer); +REGISTER_LAYER_CLASS(Data); } // namespace caffe diff --git a/src/caffe/layers/deconv_layer.cpp b/src/caffe/layers/deconv_layer.cpp new file mode 100644 index 00000000000..e6d65ab526b --- /dev/null +++ b/src/caffe/layers/deconv_layer.cpp @@ -0,0 +1,86 @@ +#include + +#include "caffe/filler.hpp" +#include "caffe/layer.hpp" +#include "caffe/util/im2col.hpp" +#include "caffe/util/math_functions.hpp" +#include "caffe/vision_layers.hpp" + +namespace caffe { + +template +void DeconvolutionLayer::compute_output_shape() { + this->height_out_ = this->stride_h_ * (this->height_ - 1) + this->kernel_h_ + - 2 * this->pad_h_; + this->width_out_ = this->stride_w_ * (this->width_ - 1) + this->kernel_w_ + - 2 * this->pad_w_; +} + +template +void DeconvolutionLayer::Forward_cpu(const vector*>& bottom, + const vector*>& top) { + const Dtype* weight = this->blobs_[0]->cpu_data(); + for (int i = 0; i < bottom.size(); ++i) { + const Dtype* bottom_data = bottom[i]->cpu_data(); + Dtype* top_data = top[i]->mutable_cpu_data(); + for (int n = 0; n < this->num_; ++n) { + this->backward_cpu_gemm(bottom_data + bottom[i]->offset(n), weight, + top_data + top[i]->offset(n)); + if (this->bias_term_) { + const Dtype* bias = this->blobs_[1]->cpu_data(); + this->forward_cpu_bias(top_data + top[i]->offset(n), bias); + } + } + } +} + +template +void DeconvolutionLayer::Backward_cpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom) { + const Dtype* weight = this->blobs_[0]->cpu_data(); + Dtype* weight_diff = this->blobs_[0]->mutable_cpu_diff(); + if (this->param_propagate_down_[0]) { + caffe_set(this->blobs_[0]->count(), Dtype(0), weight_diff); + } + if (this->bias_term_ && this->param_propagate_down_[1]) { + caffe_set(this->blobs_[1]->count(), Dtype(0), + this->blobs_[1]->mutable_cpu_diff()); + } + for (int i = 0; i < top.size(); ++i) { + const Dtype* top_diff = top[i]->cpu_diff(); + const Dtype* bottom_data = bottom[i]->cpu_data(); + Dtype* bottom_diff = bottom[i]->mutable_cpu_diff(); + // Bias gradient, if necessary. + if (this->bias_term_ && this->param_propagate_down_[1]) { + Dtype* bias_diff = this->blobs_[1]->mutable_cpu_diff(); + for (int n = 0; n < this->num_; ++n) { + this->backward_cpu_bias(bias_diff, top_diff + top[i]->offset(n)); + } + } + if (this->param_propagate_down_[0] || propagate_down[i]) { + for (int n = 0; n < this->num_; ++n) { + // Gradient w.r.t. weight. Note that we will accumulate diffs. + if (this->param_propagate_down_[0]) { + this->weight_cpu_gemm(top_diff + top[i]->offset(n), + bottom_data + bottom[i]->offset(n), weight_diff); + } + // Gradient w.r.t. bottom data, if necessary, reusing the column buffer + // we might have just computed above. + if (propagate_down[i]) { + this->forward_cpu_gemm(top_diff + top[i]->offset(n), weight, + bottom_diff + bottom[i]->offset(n), + this->param_propagate_down_[0]); + } + } + } + } +} + +#ifdef CPU_ONLY +STUB_GPU(DeconvolutionLayer); +#endif + +INSTANTIATE_CLASS(DeconvolutionLayer); +REGISTER_LAYER_CLASS(Deconvolution); + +} // namespace caffe diff --git a/src/caffe/layers/deconv_layer.cu b/src/caffe/layers/deconv_layer.cu new file mode 100644 index 00000000000..9198dd64c72 --- /dev/null +++ b/src/caffe/layers/deconv_layer.cu @@ -0,0 +1,71 @@ +#include + +#include "caffe/filler.hpp" +#include "caffe/layer.hpp" +#include "caffe/util/im2col.hpp" +#include "caffe/util/math_functions.hpp" +#include "caffe/vision_layers.hpp" + +namespace caffe { + +template +void DeconvolutionLayer::Forward_gpu(const vector*>& bottom, + const vector*>& top) { + const Dtype* weight = this->blobs_[0]->gpu_data(); + for (int i = 0; i < bottom.size(); ++i) { + const Dtype* bottom_data = bottom[i]->gpu_data(); + Dtype* top_data = top[i]->mutable_gpu_data(); + for (int n = 0; n < this->num_; ++n) { + this->backward_gpu_gemm(bottom_data + bottom[i]->offset(n), weight, + top_data + top[i]->offset(n)); + if (this->bias_term_) { + const Dtype* bias = this->blobs_[1]->gpu_data(); + this->forward_gpu_bias(top_data + top[i]->offset(n), bias); + } + } + } +} + +template +void DeconvolutionLayer::Backward_gpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom) { + const Dtype* weight = this->blobs_[0]->gpu_data(); + Dtype* weight_diff = this->blobs_[0]->mutable_gpu_diff(); + if (this->param_propagate_down_[0]) { + caffe_gpu_set(this->blobs_[0]->count(), Dtype(0), weight_diff); + } + if (this->bias_term_ && this->param_propagate_down_[1]) { + caffe_gpu_set(this->blobs_[1]->count(), Dtype(0), + this->blobs_[1]->mutable_gpu_diff()); + } + for (int i = 0; i < top.size(); ++i) { + const Dtype* top_diff = top[i]->gpu_diff(); + const Dtype* bottom_data = bottom[i]->gpu_data(); + Dtype* bottom_diff = bottom[i]->mutable_gpu_diff(); + // Bias gradient, if necessary. + if (this->bias_term_ && this->param_propagate_down_[1]) { + Dtype* bias_diff = this->blobs_[1]->mutable_gpu_diff(); + for (int n = 0; n < this->num_; ++n) { + this->backward_gpu_bias(bias_diff, top_diff + top[i]->offset(n)); + } + } + if (this->param_propagate_down_[0] || propagate_down[i]) { + for (int n = 0; n < this->num_; ++n) { + // gradient w.r.t. weight. Note that we will accumulate diffs. + if (this->param_propagate_down_[0]) { + this->weight_gpu_gemm(top_diff + top[i]->offset(n), + bottom_data + bottom[i]->offset(n), weight_diff); + } + // gradient w.r.t. bottom data, if necessary. + if (propagate_down[i]) { + this->forward_gpu_gemm(top_diff + top[i]->offset(n), weight, + bottom_diff + bottom[i]->offset(n)); + } + } + } + } +} + +INSTANTIATE_LAYER_GPU_FUNCS(DeconvolutionLayer); + +} // namespace caffe diff --git a/src/caffe/layers/dropout_layer.cpp b/src/caffe/layers/dropout_layer.cpp index 47feb1d2543..ec1256fd2fa 100644 --- a/src/caffe/layers/dropout_layer.cpp +++ b/src/caffe/layers/dropout_layer.cpp @@ -12,7 +12,7 @@ namespace caffe { template void DropoutLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { NeuronLayer::LayerSetUp(bottom, top); threshold_ = this->layer_param_.dropout_param().dropout_ratio(); DCHECK(threshold_ > 0.); @@ -23,7 +23,7 @@ void DropoutLayer::LayerSetUp(const vector*>& bottom, template void DropoutLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { NeuronLayer::Reshape(bottom, top); // Set up the cache for random number generation rand_vec_.Reshape(bottom[0]->num(), bottom[0]->channels(), @@ -32,12 +32,12 @@ void DropoutLayer::Reshape(const vector*>& bottom, template void DropoutLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); unsigned int* mask = rand_vec_.mutable_cpu_data(); const int count = bottom[0]->count(); - if (Caffe::phase() == Caffe::TRAIN) { + if (this->phase_ == TRAIN) { // Create random numbers caffe_rng_bernoulli(count, 1. - threshold_, mask); for (int i = 0; i < count; ++i) { @@ -51,13 +51,13 @@ void DropoutLayer::Forward_cpu(const vector*>& bottom, template void DropoutLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { const Dtype* top_diff = top[0]->cpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - if (Caffe::phase() == Caffe::TRAIN) { + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + if (this->phase_ == TRAIN) { const unsigned int* mask = rand_vec_.cpu_data(); - const int count = (*bottom)[0]->count(); + const int count = bottom[0]->count(); for (int i = 0; i < count; ++i) { bottom_diff[i] = top_diff[i] * mask[i] * scale_; } @@ -73,6 +73,6 @@ STUB_GPU(DropoutLayer); #endif INSTANTIATE_CLASS(DropoutLayer); - +REGISTER_LAYER_CLASS(Dropout); } // namespace caffe diff --git a/src/caffe/layers/dropout_layer.cu b/src/caffe/layers/dropout_layer.cu index 9756c862183..f9ea04f4acf 100644 --- a/src/caffe/layers/dropout_layer.cu +++ b/src/caffe/layers/dropout_layer.cu @@ -22,11 +22,11 @@ __global__ void DropoutForward(const int n, const Dtype* in, template void DropoutLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); const int count = bottom[0]->count(); - if (Caffe::phase() == Caffe::TRAIN) { + if (this->phase_ == TRAIN) { unsigned int* mask = static_cast(rand_vec_.mutable_gpu_data()); caffe_gpu_rng_uniform(count, mask); @@ -52,14 +52,14 @@ __global__ void DropoutBackward(const int n, const Dtype* in_diff, template void DropoutLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { const Dtype* top_diff = top[0]->gpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); - if (Caffe::phase() == Caffe::TRAIN) { + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + if (this->phase_ == TRAIN) { const unsigned int* mask = static_cast(rand_vec_.gpu_data()); - const int count = (*bottom)[0]->count(); + const int count = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) DropoutBackward<<>>( @@ -71,7 +71,7 @@ void DropoutLayer::Backward_gpu(const vector*>& top, } } -INSTANTIATE_CLASS(DropoutLayer); +INSTANTIATE_LAYER_GPU_FUNCS(DropoutLayer); } // namespace caffe diff --git a/src/caffe/layers/dummy_data_layer.cpp b/src/caffe/layers/dummy_data_layer.cpp index 883f2528ef8..d254eb1f961 100644 --- a/src/caffe/layers/dummy_data_layer.cpp +++ b/src/caffe/layers/dummy_data_layer.cpp @@ -8,8 +8,8 @@ namespace caffe { template void DummyDataLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { - const int num_top = top->size(); + const vector*>& top) { + const int num_top = top.size(); const DummyDataParameter& param = this->layer_param_.dummy_data_param(); const int num_data_filler = param.data_filler_size(); CHECK(num_data_filler == 0 || num_data_filler == 1 || @@ -70,7 +70,7 @@ void DummyDataLayer::LayerSetUp(const vector*>& bottom, (param.height_size() == 1) ? param.height(0) : param.height(i); const int width = (param.width_size() == 1) ? param.width(0) : param.width(i); - (*top)[i]->Reshape(num, channels, height, width); + top[i]->Reshape(num, channels, height, width); } // Run Forward once, with refill_ inverted, to fill the constant Blobs. this->Forward(bottom, top); @@ -83,15 +83,16 @@ void DummyDataLayer::LayerSetUp(const vector*>& bottom, template void DummyDataLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { - for (int i = 0; i < top->size(); ++i) { + const vector*>& top) { + for (int i = 0; i < top.size(); ++i) { const int filler_id = (fillers_.size() > 1) ? i : 0; if (refill_[filler_id]) { - fillers_[filler_id]->Fill((*top)[i]); + fillers_[filler_id]->Fill(top[i]); } } } INSTANTIATE_CLASS(DummyDataLayer); +REGISTER_LAYER_CLASS(DummyData); } // namespace caffe diff --git a/src/caffe/layers/eltwise_layer.cpp b/src/caffe/layers/eltwise_layer.cpp index 569560f97d3..bbc34449588 100644 --- a/src/caffe/layers/eltwise_layer.cpp +++ b/src/caffe/layers/eltwise_layer.cpp @@ -9,7 +9,7 @@ namespace caffe { template void EltwiseLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { CHECK(this->layer_param().eltwise_param().coeff_size() == 0 || this->layer_param().eltwise_param().coeff_size() == bottom.size()) << "Eltwise Layer takes one coefficient per bottom blob."; @@ -30,7 +30,7 @@ void EltwiseLayer::LayerSetUp(const vector*>& bottom, template void EltwiseLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const int num = bottom[0]->num(); const int channels = bottom[0]->channels(); const int height = bottom[0]->height(); @@ -41,22 +41,22 @@ void EltwiseLayer::Reshape(const vector*>& bottom, CHECK_EQ(height, bottom[i]->height()); CHECK_EQ(width, bottom[i]->width()); } - (*top)[0]->Reshape(num, channels, height, width); + top[0]->Reshape(num, channels, height, width); // If max operation, we will initialize the vector index part. if (this->layer_param_.eltwise_param().operation() == - EltwiseParameter_EltwiseOp_MAX && top->size() == 1) { + EltwiseParameter_EltwiseOp_MAX && top.size() == 1) { max_idx_.Reshape(bottom[0]->num(), channels, height, width); } } template void EltwiseLayer::Forward_cpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { int* mask = NULL; const Dtype* bottom_data_a = NULL; const Dtype* bottom_data_b = NULL; - const int count = (*top)[0]->count(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + const int count = top[0]->count(); + Dtype* top_data = top[0]->mutable_cpu_data(); switch (op_) { case EltwiseParameter_EltwiseOp_PROD: caffe_mul(count, bottom[0]->cpu_data(), bottom[1]->cpu_data(), top_data); @@ -106,26 +106,26 @@ void EltwiseLayer::Forward_cpu( template void EltwiseLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const int* mask = NULL; const int count = top[0]->count(); const Dtype* top_data = top[0]->cpu_data(); const Dtype* top_diff = top[0]->cpu_diff(); - for (int i = 0; i < bottom->size(); ++i) { + for (int i = 0; i < bottom.size(); ++i) { if (propagate_down[i]) { - const Dtype* bottom_data = (*bottom)[i]->cpu_data(); - Dtype* bottom_diff = (*bottom)[i]->mutable_cpu_diff(); + const Dtype* bottom_data = bottom[i]->cpu_data(); + Dtype* bottom_diff = bottom[i]->mutable_cpu_diff(); switch (op_) { case EltwiseParameter_EltwiseOp_PROD: if (stable_prod_grad_) { bool initialized = false; - for (int j = 0; j < bottom->size(); ++j) { + for (int j = 0; j < bottom.size(); ++j) { if (i == j) { continue; } if (!initialized) { - caffe_copy(count, (*bottom)[j]->cpu_data(), bottom_diff); + caffe_copy(count, bottom[j]->cpu_data(), bottom_diff); initialized = true; } else { - caffe_mul(count, (*bottom)[j]->cpu_data(), bottom_diff, + caffe_mul(count, bottom[j]->cpu_data(), bottom_diff, bottom_diff); } } @@ -163,6 +163,6 @@ STUB_GPU(EltwiseLayer); #endif INSTANTIATE_CLASS(EltwiseLayer); - +REGISTER_LAYER_CLASS(Eltwise); } // namespace caffe diff --git a/src/caffe/layers/eltwise_layer.cu b/src/caffe/layers/eltwise_layer.cu index 16cb6cc77e3..2247870d97f 100644 --- a/src/caffe/layers/eltwise_layer.cu +++ b/src/caffe/layers/eltwise_layer.cu @@ -33,10 +33,10 @@ __global__ void MaxForward(const int nthreads, const Dtype* bottom_data_a, template void EltwiseLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { int* mask = NULL; - const int count = (*top)[0]->count(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + const int count = top[0]->count(); + Dtype* top_data = top[0]->mutable_gpu_data(); switch (op_) { case EltwiseParameter_EltwiseOp_PROD: caffe_gpu_mul(count, bottom[0]->gpu_data(), bottom[1]->gpu_data(), @@ -82,26 +82,26 @@ __global__ void MaxBackward(const int nthreads, const Dtype* top_diff, template void EltwiseLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const int* mask = NULL; const int count = top[0]->count(); const Dtype* top_data = top[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - for (int i = 0; i < bottom->size(); ++i) { + for (int i = 0; i < bottom.size(); ++i) { if (propagate_down[i]) { - const Dtype* bottom_data = (*bottom)[i]->gpu_data(); - Dtype* bottom_diff = (*bottom)[i]->mutable_gpu_diff(); + const Dtype* bottom_data = bottom[i]->gpu_data(); + Dtype* bottom_diff = bottom[i]->mutable_gpu_diff(); switch (op_) { case EltwiseParameter_EltwiseOp_PROD: if (stable_prod_grad_) { bool initialized = false; - for (int j = 0; j < bottom->size(); ++j) { + for (int j = 0; j < bottom.size(); ++j) { if (i == j) { continue; } if (!initialized) { - caffe_copy(count, (*bottom)[j]->gpu_data(), bottom_diff); + caffe_copy(count, bottom[j]->gpu_data(), bottom_diff); initialized = true; } else { - caffe_gpu_mul(count, (*bottom)[j]->gpu_data(), bottom_diff, + caffe_gpu_mul(count, bottom[j]->gpu_data(), bottom_diff, bottom_diff); } } @@ -130,6 +130,6 @@ void EltwiseLayer::Backward_gpu(const vector*>& top, } } -INSTANTIATE_CLASS(EltwiseLayer); +INSTANTIATE_LAYER_GPU_FUNCS(EltwiseLayer); } // namespace caffe diff --git a/src/caffe/layers/euclidean_loss_layer.cpp b/src/caffe/layers/euclidean_loss_layer.cpp index 1b4a13d2ddc..b539d3487f5 100644 --- a/src/caffe/layers/euclidean_loss_layer.cpp +++ b/src/caffe/layers/euclidean_loss_layer.cpp @@ -9,7 +9,7 @@ namespace caffe { template void EuclideanLossLayer::Reshape( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::Reshape(bottom, top); CHECK_EQ(bottom[0]->channels(), bottom[1]->channels()); CHECK_EQ(bottom[0]->height(), bottom[1]->height()); @@ -20,7 +20,7 @@ void EuclideanLossLayer::Reshape( template void EuclideanLossLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { int count = bottom[0]->count(); caffe_sub( count, @@ -29,22 +29,22 @@ void EuclideanLossLayer::Forward_cpu(const vector*>& bottom, diff_.mutable_cpu_data()); Dtype dot = caffe_cpu_dot(count, diff_.cpu_data(), diff_.cpu_data()); Dtype loss = dot / bottom[0]->num() / Dtype(2); - (*top)[0]->mutable_cpu_data()[0] = loss; + top[0]->mutable_cpu_data()[0] = loss; } template void EuclideanLossLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { for (int i = 0; i < 2; ++i) { if (propagate_down[i]) { const Dtype sign = (i == 0) ? 1 : -1; - const Dtype alpha = sign * top[0]->cpu_diff()[0] / (*bottom)[i]->num(); + const Dtype alpha = sign * top[0]->cpu_diff()[0] / bottom[i]->num(); caffe_cpu_axpby( - (*bottom)[i]->count(), // count + bottom[i]->count(), // count alpha, // alpha diff_.cpu_data(), // a Dtype(0), // beta - (*bottom)[i]->mutable_cpu_diff()); // b + bottom[i]->mutable_cpu_diff()); // b } } } @@ -54,5 +54,6 @@ STUB_GPU(EuclideanLossLayer); #endif INSTANTIATE_CLASS(EuclideanLossLayer); +REGISTER_LAYER_CLASS(EuclideanLoss); } // namespace caffe diff --git a/src/caffe/layers/euclidean_loss_layer.cu b/src/caffe/layers/euclidean_loss_layer.cu index 70b1b9ee9ea..5b1de3ad2d9 100644 --- a/src/caffe/layers/euclidean_loss_layer.cu +++ b/src/caffe/layers/euclidean_loss_layer.cu @@ -9,7 +9,7 @@ namespace caffe { template void EuclideanLossLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { int count = bottom[0]->count(); caffe_gpu_sub( count, @@ -19,26 +19,26 @@ void EuclideanLossLayer::Forward_gpu(const vector*>& bottom, Dtype dot; caffe_gpu_dot(count, diff_.gpu_data(), diff_.gpu_data(), &dot); Dtype loss = dot / bottom[0]->num() / Dtype(2); - (*top)[0]->mutable_cpu_data()[0] = loss; + top[0]->mutable_cpu_data()[0] = loss; } template void EuclideanLossLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { for (int i = 0; i < 2; ++i) { if (propagate_down[i]) { const Dtype sign = (i == 0) ? 1 : -1; - const Dtype alpha = sign * top[0]->cpu_diff()[0] / (*bottom)[i]->num(); + const Dtype alpha = sign * top[0]->cpu_diff()[0] / bottom[i]->num(); caffe_gpu_axpby( - (*bottom)[i]->count(), // count + bottom[i]->count(), // count alpha, // alpha diff_.gpu_data(), // a Dtype(0), // beta - (*bottom)[i]->mutable_gpu_diff()); // b + bottom[i]->mutable_gpu_diff()); // b } } } -INSTANTIATE_CLASS(EuclideanLossLayer); +INSTANTIATE_LAYER_GPU_FUNCS(EuclideanLossLayer); } // namespace caffe diff --git a/src/caffe/layers/exp_layer.cpp b/src/caffe/layers/exp_layer.cpp new file mode 100644 index 00000000000..c7e7c60cfad --- /dev/null +++ b/src/caffe/layers/exp_layer.cpp @@ -0,0 +1,69 @@ +#include +#include + +#include "caffe/layer.hpp" +#include "caffe/util/math_functions.hpp" +#include "caffe/vision_layers.hpp" + +namespace caffe { + +template +void ExpLayer::LayerSetUp(const vector*>& bottom, + const vector*>& top) { + NeuronLayer::LayerSetUp(bottom, top); + const Dtype base = this->layer_param_.exp_param().base(); + if (base != Dtype(-1)) { + CHECK_GT(base, 0) << "base must be strictly positive."; + } + // If base == -1, interpret the base as e and set log_base = 1 exactly. + // Otherwise, calculate its log explicitly. + const Dtype log_base = (base == Dtype(-1)) ? Dtype(1) : log(base); + CHECK(!isnan(log_base)) + << "NaN result: log(base) = log(" << base << ") = " << log_base; + CHECK(!isinf(log_base)) + << "Inf result: log(base) = log(" << base << ") = " << log_base; + const Dtype input_scale = this->layer_param_.exp_param().scale(); + const Dtype input_shift = this->layer_param_.exp_param().shift(); + inner_scale_ = log_base * input_scale; + outer_scale_ = (input_shift == Dtype(0)) ? Dtype(1) : pow(base, input_shift); +} + +template +void ExpLayer::Forward_cpu(const vector*>& bottom, + const vector*>& top) { + const int count = bottom[0]->count(); + const Dtype* bottom_data = bottom[0]->cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); + if (inner_scale_ == Dtype(1)) { + caffe_exp(count, bottom_data, top_data); + } else { + caffe_cpu_scale(count, inner_scale_, bottom_data, top_data); + caffe_exp(count, top_data, top_data); + } + if (outer_scale_ != Dtype(1)) { + caffe_scal(count, outer_scale_, top_data); + } +} + +template +void ExpLayer::Backward_cpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom) { + if (!propagate_down[0]) { return; } + const int count = bottom[0]->count(); + const Dtype* top_data = top[0]->cpu_data(); + const Dtype* top_diff = top[0]->cpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + caffe_mul(count, top_data, top_diff, bottom_diff); + if (inner_scale_ != Dtype(1)) { + caffe_scal(count, inner_scale_, bottom_diff); + } +} + +#ifdef CPU_ONLY +STUB_GPU(ExpLayer); +#endif + +INSTANTIATE_CLASS(ExpLayer); +REGISTER_LAYER_CLASS(Exp); + +} // namespace caffe diff --git a/src/caffe/layers/exp_layer.cu b/src/caffe/layers/exp_layer.cu new file mode 100644 index 00000000000..2d75d8dd6c7 --- /dev/null +++ b/src/caffe/layers/exp_layer.cu @@ -0,0 +1,44 @@ +#include +#include + +#include "caffe/layer.hpp" +#include "caffe/util/math_functions.hpp" +#include "caffe/vision_layers.hpp" + +namespace caffe { + +template +void ExpLayer::Forward_gpu(const vector*>& bottom, + const vector*>& top) { + const int count = bottom[0]->count(); + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); + if (inner_scale_ == Dtype(1)) { + caffe_gpu_exp(count, bottom_data, top_data); + } else { + caffe_gpu_scale(count, inner_scale_, bottom_data, top_data); + caffe_gpu_exp(count, top_data, top_data); + } + if (outer_scale_ != Dtype(1)) { + caffe_gpu_scal(count, outer_scale_, top_data); + } +} + +template +void ExpLayer::Backward_gpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom) { + if (!propagate_down[0]) { return; } + const int count = bottom[0]->count(); + const Dtype* top_data = top[0]->gpu_data(); + const Dtype* top_diff = top[0]->gpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + caffe_gpu_mul(count, top_data, top_diff, bottom_diff); + if (inner_scale_ != Dtype(1)) { + caffe_gpu_scal(count, inner_scale_, bottom_diff); + } +} + +INSTANTIATE_LAYER_GPU_FUNCS(ExpLayer); + + +} // namespace caffe diff --git a/src/caffe/layers/flatten_layer.cpp b/src/caffe/layers/flatten_layer.cpp index 65310cd1669..eb7b42bc10b 100644 --- a/src/caffe/layers/flatten_layer.cpp +++ b/src/caffe/layers/flatten_layer.cpp @@ -8,25 +8,25 @@ namespace caffe { template void FlattenLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { int channels_out = bottom[0]->channels() * bottom[0]->height() * bottom[0]->width(); - (*top)[0]->Reshape(bottom[0]->num(), channels_out, 1, 1); + top[0]->Reshape(bottom[0]->num(), channels_out, 1, 1); count_ = bottom[0]->num() * channels_out; CHECK_EQ(count_, bottom[0]->count()); - CHECK_EQ(count_, (*top)[0]->count()); + CHECK_EQ(count_, top[0]->count()); } template void FlattenLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { - (*top)[0]->ShareData(*bottom[0]); + const vector*>& top) { + top[0]->ShareData(*bottom[0]); } template void FlattenLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { - (*bottom)[0]->ShareDiff(*top[0]); + const vector& propagate_down, const vector*>& bottom) { + bottom[0]->ShareDiff(*top[0]); } #ifdef CPU_ONLY @@ -34,5 +34,6 @@ STUB_GPU(FlattenLayer); #endif INSTANTIATE_CLASS(FlattenLayer); +REGISTER_LAYER_CLASS(Flatten); } // namespace caffe diff --git a/src/caffe/layers/flatten_layer.cu b/src/caffe/layers/flatten_layer.cu index ff23f523fee..42abdad4499 100644 --- a/src/caffe/layers/flatten_layer.cu +++ b/src/caffe/layers/flatten_layer.cu @@ -8,16 +8,16 @@ namespace caffe { template void FlattenLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { - (*top)[0]->ShareData(*bottom[0]); + const vector*>& top) { + top[0]->ShareData(*bottom[0]); } template void FlattenLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { - (*bottom)[0]->ShareDiff(*top[0]); + const vector& propagate_down, const vector*>& bottom) { + bottom[0]->ShareDiff(*top[0]); } -INSTANTIATE_CLASS(FlattenLayer); +INSTANTIATE_LAYER_GPU_FUNCS(FlattenLayer); } // namespace caffe diff --git a/src/caffe/layers/hdf5_data_layer.cpp b/src/caffe/layers/hdf5_data_layer.cpp index 3f1396a9acc..3d856ec3001 100644 --- a/src/caffe/layers/hdf5_data_layer.cpp +++ b/src/caffe/layers/hdf5_data_layer.cpp @@ -26,35 +26,44 @@ HDF5DataLayer::~HDF5DataLayer() { } // Load data and label from HDF5 filename into the class property blobs. template void HDF5DataLayer::LoadHDF5FileData(const char* filename) { - LOG(INFO) << "Loading HDF5 file" << filename; + DLOG(INFO) << "Loading HDF5 file: " << filename; hid_t file_id = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); if (file_id < 0) { - LOG(ERROR) << "Failed opening HDF5 file" << filename; - return; + LOG(FATAL) << "Failed opening HDF5 file: " << filename; } - const int MIN_DATA_DIM = 2; + int top_size = this->layer_param_.top_size(); + hdf_blobs_.resize(top_size); + + const int MIN_DATA_DIM = 1; const int MAX_DATA_DIM = 4; - hdf5_load_nd_dataset( - file_id, "data", MIN_DATA_DIM, MAX_DATA_DIM, &data_blob_); - const int MIN_LABEL_DIM = 1; - const int MAX_LABEL_DIM = 2; - hdf5_load_nd_dataset( - file_id, "label", MIN_LABEL_DIM, MAX_LABEL_DIM, &label_blob_); + for (int i = 0; i < top_size; ++i) { + hdf_blobs_[i] = shared_ptr >(new Blob()); + hdf5_load_nd_dataset(file_id, this->layer_param_.top(i).c_str(), + MIN_DATA_DIM, MAX_DATA_DIM, hdf_blobs_[i].get()); + } herr_t status = H5Fclose(file_id); - CHECK_GE(status, 0) << "Failed to close HDF5 file " << filename; - CHECK_EQ(data_blob_.num(), label_blob_.num()); - LOG(INFO) << "Successully loaded " << data_blob_.num() << " rows"; + CHECK_GE(status, 0) << "Failed to close HDF5 file: " << filename; + + // MinTopBlobs==1 guarantees at least one top blob + int num = hdf_blobs_[0]->num(); + for (int i = 1; i < top_size; ++i) { + CHECK_EQ(hdf_blobs_[i]->num(), num); + } + DLOG(INFO) << "Successully loaded " << hdf_blobs_[0]->num() << " rows"; } template void HDF5DataLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { + // Refuse transformation parameters since HDF5 is totally generic. + CHECK(!this->layer_param_.has_transform_param()) << + this->type() << " does not transform data."; // Read the source to parse the filenames. const string& source = this->layer_param_.hdf5_data_param().source(); - LOG(INFO) << "Loading filename from " << source; + LOG(INFO) << "Loading list of HDF5 filenames from: " << source; hdf_filenames_.clear(); std::ifstream source_file(source.c_str()); if (source_file.is_open()) { @@ -62,11 +71,15 @@ void HDF5DataLayer::LayerSetUp(const vector*>& bottom, while (source_file >> line) { hdf_filenames_.push_back(line); } + } else { + LOG(FATAL) << "Failed to open source file: " << source; } source_file.close(); num_files_ = hdf_filenames_.size(); current_file_ = 0; - LOG(INFO) << "Number of files: " << num_files_; + LOG(INFO) << "Number of HDF5 files: " << num_files_; + CHECK_GE(num_files_, 1) << "Must have at least 1 HDF5 filename listed in " + << source; // Load the first HDF5 file and initialize the line counter. LoadHDF5FileData(hdf_filenames_[current_file_].c_str()); @@ -74,39 +87,35 @@ void HDF5DataLayer::LayerSetUp(const vector*>& bottom, // Reshape blobs. const int batch_size = this->layer_param_.hdf5_data_param().batch_size(); - (*top)[0]->Reshape(batch_size, data_blob_.channels(), - data_blob_.height(), data_blob_.width()); - (*top)[1]->Reshape(batch_size, label_blob_.channels(), - label_blob_.height(), label_blob_.width()); - LOG(INFO) << "output data size: " << (*top)[0]->num() << "," - << (*top)[0]->channels() << "," << (*top)[0]->height() << "," - << (*top)[0]->width(); + const int top_size = this->layer_param_.top_size(); + for (int i = 0; i < top_size; ++i) { + top[i]->Reshape(batch_size, hdf_blobs_[i]->channels(), + hdf_blobs_[i]->height(), hdf_blobs_[i]->width()); + } } template void HDF5DataLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const int batch_size = this->layer_param_.hdf5_data_param().batch_size(); - const int data_count = (*top)[0]->count() / (*top)[0]->num(); - const int label_data_count = (*top)[1]->count() / (*top)[1]->num(); - for (int i = 0; i < batch_size; ++i, ++current_row_) { - if (current_row_ == data_blob_.num()) { + if (current_row_ == hdf_blobs_[0]->num()) { if (num_files_ > 1) { - current_file_ += 1; + ++current_file_; if (current_file_ == num_files_) { current_file_ = 0; - LOG(INFO) << "looping around to first file"; + DLOG(INFO) << "Looping around to first file."; } LoadHDF5FileData(hdf_filenames_[current_file_].c_str()); } current_row_ = 0; } - caffe_copy(data_count, &data_blob_.cpu_data()[current_row_ * data_count], - &(*top)[0]->mutable_cpu_data()[i * data_count]); - caffe_copy(label_data_count, - &label_blob_.cpu_data()[current_row_ * label_data_count], - &(*top)[1]->mutable_cpu_data()[i * label_data_count]); + for (int j = 0; j < this->layer_param_.top_size(); ++j) { + int data_dim = top[j]->count() / top[j]->num(); + caffe_copy(data_dim, + &hdf_blobs_[j]->cpu_data()[current_row_ * data_dim], + &top[j]->mutable_cpu_data()[i * data_dim]); + } } } @@ -115,5 +124,6 @@ STUB_GPU_FORWARD(HDF5DataLayer, Forward); #endif INSTANTIATE_CLASS(HDF5DataLayer); +REGISTER_LAYER_CLASS(HDF5Data); } // namespace caffe diff --git a/src/caffe/layers/hdf5_data_layer.cu b/src/caffe/layers/hdf5_data_layer.cu index 79cc536eb28..02e3821d104 100644 --- a/src/caffe/layers/hdf5_data_layer.cu +++ b/src/caffe/layers/hdf5_data_layer.cu @@ -18,34 +18,29 @@ namespace caffe { template void HDF5DataLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const int batch_size = this->layer_param_.hdf5_data_param().batch_size(); - const int data_count = (*top)[0]->count() / (*top)[0]->num(); - const int label_data_count = (*top)[1]->count() / (*top)[1]->num(); - for (int i = 0; i < batch_size; ++i, ++current_row_) { - if (current_row_ == data_blob_.num()) { + if (current_row_ == hdf_blobs_[0]->num()) { if (num_files_ > 1) { current_file_ += 1; - if (current_file_ == num_files_) { current_file_ = 0; - LOG(INFO) << "looping around to first file"; + DLOG(INFO) << "Looping around to first file."; } - LoadHDF5FileData(hdf_filenames_[current_file_].c_str()); } current_row_ = 0; } - caffe_copy(data_count, - &data_blob_.cpu_data()[current_row_ * data_count], - &(*top)[0]->mutable_gpu_data()[i * data_count]); - caffe_copy(label_data_count, - &label_blob_.cpu_data()[current_row_ * label_data_count], - &(*top)[1]->mutable_gpu_data()[i * label_data_count]); + for (int j = 0; j < this->layer_param_.top_size(); ++j) { + int data_dim = top[j]->count() / top[j]->num(); + caffe_copy(data_dim, + &hdf_blobs_[j]->cpu_data()[current_row_ * data_dim], + &top[j]->mutable_gpu_data()[i * data_dim]); + } } } -INSTANTIATE_CLASS(HDF5DataLayer); +INSTANTIATE_LAYER_GPU_FUNCS(HDF5DataLayer); } // namespace caffe diff --git a/src/caffe/layers/hdf5_output_layer.cpp b/src/caffe/layers/hdf5_output_layer.cpp index 3cdbbb31a6a..f63375c3dc6 100644 --- a/src/caffe/layers/hdf5_output_layer.cpp +++ b/src/caffe/layers/hdf5_output_layer.cpp @@ -12,25 +12,27 @@ namespace caffe { template -HDF5OutputLayer::HDF5OutputLayer(const LayerParameter& param) - : Layer(param), - file_name_(param.hdf5_output_param().file_name()) { - /* create a HDF5 file */ +void HDF5OutputLayer::LayerSetUp(const vector*>& bottom, + const vector*>& top) { + file_name_ = this->layer_param_.hdf5_output_param().file_name(); file_id_ = H5Fcreate(file_name_.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); CHECK_GE(file_id_, 0) << "Failed to open HDF5 file" << file_name_; + file_opened_ = true; } template HDF5OutputLayer::~HDF5OutputLayer() { - herr_t status = H5Fclose(file_id_); - CHECK_GE(status, 0) << "Failed to close HDF5 file " << file_name_; + if (file_opened_) { + herr_t status = H5Fclose(file_id_); + CHECK_GE(status, 0) << "Failed to close HDF5 file " << file_name_; + } } template void HDF5OutputLayer::SaveBlobs() { // TODO: no limit on the number of blobs - LOG(INFO) << "Saving HDF5 file" << file_name_; + LOG(INFO) << "Saving HDF5 file " << file_name_; CHECK_EQ(data_blob_.num(), label_blob_.num()) << "data blob and label blob must have the same batch size"; hdf5_save_nd_dataset(file_id_, HDF5_DATA_DATASET_NAME, data_blob_); @@ -40,7 +42,7 @@ void HDF5OutputLayer::SaveBlobs() { template void HDF5OutputLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { CHECK_GE(bottom.size(), 2); CHECK_EQ(bottom[0]->num(), bottom[1]->num()); data_blob_.Reshape(bottom[0]->num(), bottom[0]->channels(), @@ -61,7 +63,7 @@ void HDF5OutputLayer::Forward_cpu(const vector*>& bottom, template void HDF5OutputLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { return; } @@ -70,5 +72,6 @@ STUB_GPU(HDF5OutputLayer); #endif INSTANTIATE_CLASS(HDF5OutputLayer); +REGISTER_LAYER_CLASS(HDF5Output); } // namespace caffe diff --git a/src/caffe/layers/hdf5_output_layer.cu b/src/caffe/layers/hdf5_output_layer.cu index 0813c02a440..ae497c34fc2 100644 --- a/src/caffe/layers/hdf5_output_layer.cu +++ b/src/caffe/layers/hdf5_output_layer.cu @@ -13,7 +13,7 @@ namespace caffe { template void HDF5OutputLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { CHECK_GE(bottom.size(), 2); CHECK_EQ(bottom[0]->num(), bottom[1]->num()); data_blob_.Reshape(bottom[0]->num(), bottom[0]->channels(), @@ -34,10 +34,10 @@ void HDF5OutputLayer::Forward_gpu(const vector*>& bottom, template void HDF5OutputLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { return; } -INSTANTIATE_CLASS(HDF5OutputLayer); +INSTANTIATE_LAYER_GPU_FUNCS(HDF5OutputLayer); } // namespace caffe diff --git a/src/caffe/layers/hinge_loss_layer.cpp b/src/caffe/layers/hinge_loss_layer.cpp index 8022aae279c..a2fb2a18309 100644 --- a/src/caffe/layers/hinge_loss_layer.cpp +++ b/src/caffe/layers/hinge_loss_layer.cpp @@ -12,7 +12,7 @@ namespace caffe { template void HingeLossLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); const Dtype* label = bottom[1]->cpu_data(); @@ -30,7 +30,7 @@ void HingeLossLayer::Forward_cpu(const vector*>& bottom, Dtype(0), 1 + bottom_diff[i * dim + j]); } } - Dtype* loss = (*top)[0]->mutable_cpu_data(); + Dtype* loss = top[0]->mutable_cpu_data(); switch (this->layer_param_.hinge_loss_param().norm()) { case HingeLossParameter_Norm_L1: loss[0] = caffe_cpu_asum(count, bottom_diff) / num; @@ -45,16 +45,16 @@ void HingeLossLayer::Forward_cpu(const vector*>& bottom, template void HingeLossLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (propagate_down[1]) { - LOG(FATAL) << this->type_name() + LOG(FATAL) << this->type() << " Layer cannot backpropagate to label inputs."; } if (propagate_down[0]) { - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - const Dtype* label = (*bottom)[1]->cpu_data(); - int num = (*bottom)[0]->num(); - int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + const Dtype* label = bottom[1]->cpu_data(); + int num = bottom[0]->num(); + int count = bottom[0]->count(); int dim = count / num; for (int i = 0; i < num; ++i) { @@ -77,5 +77,6 @@ void HingeLossLayer::Backward_cpu(const vector*>& top, } INSTANTIATE_CLASS(HingeLossLayer); +REGISTER_LAYER_CLASS(HingeLoss); } // namespace caffe diff --git a/src/caffe/layers/im2col_layer.cpp b/src/caffe/layers/im2col_layer.cpp index 870d5a9bde3..112226116c8 100644 --- a/src/caffe/layers/im2col_layer.cpp +++ b/src/caffe/layers/im2col_layer.cpp @@ -9,7 +9,7 @@ namespace caffe { template void Im2colLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { ConvolutionParameter conv_param = this->layer_param_.convolution_param(); CHECK(!conv_param.has_kernel_size() != !(conv_param.has_kernel_h() && conv_param.has_kernel_w())) @@ -49,11 +49,11 @@ void Im2colLayer::LayerSetUp(const vector*>& bottom, template void Im2colLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { channels_ = bottom[0]->channels(); height_ = bottom[0]->height(); width_ = bottom[0]->width(); - (*top)[0]->Reshape( + top[0]->Reshape( bottom[0]->num(), channels_ * kernel_h_ * kernel_w_, (height_ + 2 * pad_h_ - kernel_h_) / stride_h_ + 1, (width_ + 2 * pad_w_ - kernel_w_) / stride_w_ + 1); @@ -61,25 +61,25 @@ void Im2colLayer::Reshape(const vector*>& bottom, template void Im2colLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); for (int n = 0; n < bottom[0]->num(); ++n) { im2col_cpu(bottom_data + bottom[0]->offset(n), channels_, height_, width_, kernel_h_, kernel_w_, pad_h_, pad_w_, - stride_h_, stride_w_, top_data + (*top)[0]->offset(n)); + stride_h_, stride_w_, top_data + top[0]->offset(n)); } } template void Im2colLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const Dtype* top_diff = top[0]->cpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); for (int n = 0; n < top[0]->num(); ++n) { col2im_cpu(top_diff + top[0]->offset(n), channels_, height_, width_, kernel_h_, kernel_w_, pad_h_, pad_w_, - stride_h_, stride_w_, bottom_diff + (*bottom)[0]->offset(n)); + stride_h_, stride_w_, bottom_diff + bottom[0]->offset(n)); } } @@ -88,5 +88,6 @@ STUB_GPU(Im2colLayer); #endif INSTANTIATE_CLASS(Im2colLayer); +REGISTER_LAYER_CLASS(Im2col); } // namespace caffe diff --git a/src/caffe/layers/im2col_layer.cu b/src/caffe/layers/im2col_layer.cu index 8df061d88e1..9c338b14cb7 100644 --- a/src/caffe/layers/im2col_layer.cu +++ b/src/caffe/layers/im2col_layer.cu @@ -9,29 +9,29 @@ namespace caffe { template void Im2colLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); for (int n = 0; n < bottom[0]->num(); ++n) { im2col_gpu(bottom_data + bottom[0]->offset(n), channels_, height_, width_, kernel_h_, kernel_w_, pad_h_, pad_w_, - stride_h_, stride_w_, top_data + (*top)[0]->offset(n)); + stride_h_, stride_w_, top_data + top[0]->offset(n)); } } template void Im2colLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const Dtype* top_diff = top[0]->gpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); for (int n = 0; n < top[0]->num(); ++n) { col2im_gpu(top_diff + top[0]->offset(n), channels_, height_, width_, kernel_h_, kernel_w_, pad_h_, pad_w_, - stride_h_, stride_w_, bottom_diff + (*bottom)[0]->offset(n)); + stride_h_, stride_w_, bottom_diff + bottom[0]->offset(n)); } } -INSTANTIATE_CLASS(Im2colLayer); +INSTANTIATE_LAYER_GPU_FUNCS(Im2colLayer); } // namespace caffe diff --git a/src/caffe/layers/image_data_layer.cpp b/src/caffe/layers/image_data_layer.cpp index b5a34ff81aa..f9046e1b3a1 100644 --- a/src/caffe/layers/image_data_layer.cpp +++ b/src/caffe/layers/image_data_layer.cpp @@ -1,3 +1,5 @@ +#include + #include // NOLINT(readability/streams) #include // NOLINT(readability/streams) #include @@ -6,6 +8,7 @@ #include "caffe/data_layers.hpp" #include "caffe/layer.hpp" +#include "caffe/util/benchmark.hpp" #include "caffe/util/io.hpp" #include "caffe/util/math_functions.hpp" #include "caffe/util/rng.hpp" @@ -19,19 +22,19 @@ ImageDataLayer::~ImageDataLayer() { template void ImageDataLayer::DataLayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const int new_height = this->layer_param_.image_data_param().new_height(); const int new_width = this->layer_param_.image_data_param().new_width(); + const bool is_color = this->layer_param_.image_data_param().is_color(); + string root_folder = this->layer_param_.image_data_param().root_folder(); + CHECK((new_height == 0 && new_width == 0) || (new_height > 0 && new_width > 0)) << "Current implementation requires " "new_height and new_width to be set at the same time."; // Read the file with filenames and labels const string& source = this->layer_param_.image_data_param().source(); - CHECK_GT(source.size(), 0); LOG(INFO) << "Opening file " << source; std::ifstream infile(source.c_str()); - CHECK(infile.good()) - << "Could not open image list (filename: \""+ source + "\")"; string filename; int label; while (infile >> filename >> label) { @@ -56,36 +59,30 @@ void ImageDataLayer::DataLayerSetUp(const vector*>& bottom, CHECK_GT(lines_.size(), skip) << "Not enough points to skip"; lines_id_ = skip; } - CHECK(!lines_.empty()) - << "Image list is empty (filename: \"" + source + "\")"; - // Read a data point, and use it to initialize the top blob. - Datum datum; - CHECK(ReadImageToDatum(lines_[lines_id_].first, lines_[lines_id_].second, - new_height, new_width, &datum)); + // Read an image, and use it to initialize the top blob. + cv::Mat cv_img = ReadImageToCVMat(root_folder + lines_[lines_id_].first, + new_height, new_width, is_color); + const int channels = cv_img.channels(); + const int height = cv_img.rows; + const int width = cv_img.cols; // image const int crop_size = this->layer_param_.transform_param().crop_size(); const int batch_size = this->layer_param_.image_data_param().batch_size(); if (crop_size > 0) { - (*top)[0]->Reshape(batch_size, datum.channels(), crop_size, crop_size); - this->prefetch_data_.Reshape(batch_size, datum.channels(), crop_size, - crop_size); + top[0]->Reshape(batch_size, channels, crop_size, crop_size); + this->prefetch_data_.Reshape(batch_size, channels, crop_size, crop_size); + this->transformed_data_.Reshape(1, channels, crop_size, crop_size); } else { - (*top)[0]->Reshape(batch_size, datum.channels(), datum.height(), - datum.width()); - this->prefetch_data_.Reshape(batch_size, datum.channels(), datum.height(), - datum.width()); + top[0]->Reshape(batch_size, channels, height, width); + this->prefetch_data_.Reshape(batch_size, channels, height, width); + this->transformed_data_.Reshape(1, channels, height, width); } - LOG(INFO) << "output data size: " << (*top)[0]->num() << "," - << (*top)[0]->channels() << "," << (*top)[0]->height() << "," - << (*top)[0]->width(); + LOG(INFO) << "output data size: " << top[0]->num() << "," + << top[0]->channels() << "," << top[0]->height() << "," + << top[0]->width(); // label - (*top)[1]->Reshape(batch_size, 1, 1, 1); + top[1]->Reshape(batch_size, 1, 1, 1); this->prefetch_label_.Reshape(batch_size, 1, 1, 1); - // datum size - this->datum_channels_ = datum.channels(); - this->datum_height_ = datum.height(); - this->datum_width_ = datum.width(); - this->datum_size_ = datum.channels() * datum.height() * datum.width(); } template @@ -98,30 +95,52 @@ void ImageDataLayer::ShuffleImages() { // This function is used to create a thread that prefetches the data. template void ImageDataLayer::InternalThreadEntry() { - Datum datum; + CPUTimer batch_timer; + batch_timer.Start(); + double read_time = 0; + double trans_time = 0; + CPUTimer timer; CHECK(this->prefetch_data_.count()); - Dtype* top_data = this->prefetch_data_.mutable_cpu_data(); - Dtype* top_label = this->prefetch_label_.mutable_cpu_data(); + CHECK(this->transformed_data_.count()); ImageDataParameter image_data_param = this->layer_param_.image_data_param(); const int batch_size = image_data_param.batch_size(); const int new_height = image_data_param.new_height(); const int new_width = image_data_param.new_width(); + const int crop_size = this->layer_param_.transform_param().crop_size(); + const bool is_color = image_data_param.is_color(); + string root_folder = image_data_param.root_folder(); + + // Reshape on single input batches for inputs of varying dimension. + if (batch_size == 1 && crop_size == 0 && new_height == 0 && new_width == 0) { + cv::Mat cv_img = ReadImageToCVMat(root_folder + lines_[lines_id_].first, + 0, 0, is_color); + this->prefetch_data_.Reshape(1, cv_img.channels(), + cv_img.rows, cv_img.cols); + this->transformed_data_.Reshape(1, cv_img.channels(), + cv_img.rows, cv_img.cols); + } + + Dtype* prefetch_data = this->prefetch_data_.mutable_cpu_data(); + Dtype* prefetch_label = this->prefetch_label_.mutable_cpu_data(); // datum scales const int lines_size = lines_.size(); for (int item_id = 0; item_id < batch_size; ++item_id) { // get a blob + timer.Start(); CHECK_GT(lines_size, lines_id_); - if (!ReadImageToDatum(lines_[lines_id_].first, - lines_[lines_id_].second, - new_height, new_width, &datum)) { - continue; - } - - // Apply transformations (mirror, crop...) to the data - this->data_transformer_.Transform(item_id, datum, this->mean_, top_data); + cv::Mat cv_img = ReadImageToCVMat(root_folder + lines_[lines_id_].first, + new_height, new_width, is_color); + CHECK(cv_img.data) << "Could not load " << lines_[lines_id_].first; + read_time += timer.MicroSeconds(); + timer.Start(); + // Apply transformations (mirror, crop...) to the image + int offset = this->prefetch_data_.offset(item_id); + this->transformed_data_.set_cpu_data(prefetch_data + offset); + this->data_transformer_->Transform(cv_img, &(this->transformed_data_)); + trans_time += timer.MicroSeconds(); - top_label[item_id] = datum.label(); + prefetch_label[item_id] = lines_[lines_id_].second; // go to the next iter lines_id_++; if (lines_id_ >= lines_size) { @@ -133,8 +152,13 @@ void ImageDataLayer::InternalThreadEntry() { } } } + batch_timer.Stop(); + DLOG(INFO) << "Prefetch batch: " << batch_timer.MilliSeconds() << " ms."; + DLOG(INFO) << " Read time: " << read_time / 1000 << " ms."; + DLOG(INFO) << "Transform time: " << trans_time / 1000 << " ms."; } INSTANTIATE_CLASS(ImageDataLayer); +REGISTER_LAYER_CLASS(ImageData); } // namespace caffe diff --git a/src/caffe/layers/infogain_loss_layer.cpp b/src/caffe/layers/infogain_loss_layer.cpp index 894cb69811a..a1e0b40de0e 100644 --- a/src/caffe/layers/infogain_loss_layer.cpp +++ b/src/caffe/layers/infogain_loss_layer.cpp @@ -12,7 +12,7 @@ namespace caffe { template void InfogainLossLayer::LayerSetUp( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::LayerSetUp(bottom, top); if (bottom.size() < 3) { CHECK(this->layer_param_.infogain_loss_param().has_source()) @@ -26,7 +26,7 @@ void InfogainLossLayer::LayerSetUp( template void InfogainLossLayer::Reshape( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::Reshape(bottom, top); Blob* infogain = NULL; if (bottom.size() < 3) { @@ -48,7 +48,7 @@ void InfogainLossLayer::Reshape( template void InfogainLossLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); const Dtype* bottom_label = bottom[1]->cpu_data(); const Dtype* infogain_mat = NULL; @@ -67,33 +67,33 @@ void InfogainLossLayer::Forward_cpu(const vector*>& bottom, loss -= infogain_mat[label * dim + j] * log(prob); } } - (*top)[0]->mutable_cpu_data()[0] = loss / num; + top[0]->mutable_cpu_data()[0] = loss / num; } template void InfogainLossLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[1]) { - LOG(FATAL) << this->type_name() + LOG(FATAL) << this->type() << " Layer cannot backpropagate to label inputs."; } if (propagate_down.size() > 2 && propagate_down[2]) { - LOG(FATAL) << this->type_name() + LOG(FATAL) << this->type() << " Layer cannot backpropagate to infogain inputs."; } if (propagate_down[0]) { - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); - const Dtype* bottom_label = (*bottom)[1]->cpu_data(); + const Dtype* bottom_data = bottom[0]->cpu_data(); + const Dtype* bottom_label = bottom[1]->cpu_data(); const Dtype* infogain_mat = NULL; - if (bottom->size() < 3) { + if (bottom.size() < 3) { infogain_mat = infogain_.cpu_data(); } else { - infogain_mat = (*bottom)[2]->cpu_data(); + infogain_mat = bottom[2]->cpu_data(); } - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - int num = (*bottom)[0]->num(); - int dim = (*bottom)[0]->count() / (*bottom)[0]->num(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + int num = bottom[0]->num(); + int dim = bottom[0]->count() / bottom[0]->num(); const Dtype scale = - top[0]->cpu_diff()[0] / num; for (int i = 0; i < num; ++i) { const int label = static_cast(bottom_label[i]); @@ -106,5 +106,5 @@ void InfogainLossLayer::Backward_cpu(const vector*>& top, } INSTANTIATE_CLASS(InfogainLossLayer); - +REGISTER_LAYER_CLASS(InfogainLoss); } // namespace caffe diff --git a/src/caffe/layers/inner_product_layer.cpp b/src/caffe/layers/inner_product_layer.cpp index ecd05a030db..b1ec6cb25c0 100644 --- a/src/caffe/layers/inner_product_layer.cpp +++ b/src/caffe/layers/inner_product_layer.cpp @@ -11,7 +11,7 @@ namespace caffe { template void InnerProductLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const int num_output = this->layer_param_.inner_product_param().num_output(); bias_term_ = this->layer_param_.inner_product_param().bias_term(); N_ = num_output; @@ -44,12 +44,12 @@ void InnerProductLayer::LayerSetUp(const vector*>& bottom, template void InnerProductLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { // Figure out the dimensions M_ = bottom[0]->num(); CHECK_EQ(bottom[0]->count() / bottom[0]->num(), K_) << "Input size " "incompatible with inner product parameters."; - (*top)[0]->Reshape(bottom[0]->num(), N_, 1, 1); + top[0]->Reshape(bottom[0]->num(), N_, 1, 1); // Set up the bias multiplier if (bias_term_) { bias_multiplier_.Reshape(1, 1, 1, M_); @@ -59,9 +59,9 @@ void InnerProductLayer::Reshape(const vector*>& bottom, template void InnerProductLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); const Dtype* weight = this->blobs_[0]->cpu_data(); caffe_cpu_gemm(CblasNoTrans, CblasTrans, M_, N_, K_, (Dtype)1., bottom_data, weight, (Dtype)0., top_data); @@ -75,10 +75,10 @@ void InnerProductLayer::Forward_cpu(const vector*>& bottom, template void InnerProductLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (this->param_propagate_down_[0]) { const Dtype* top_diff = top[0]->cpu_diff(); - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); + const Dtype* bottom_data = bottom[0]->cpu_data(); // Gradient with respect to weight caffe_cpu_gemm(CblasTrans, CblasNoTrans, N_, K_, M_, (Dtype)1., top_diff, bottom_data, (Dtype)0., this->blobs_[0]->mutable_cpu_diff()); @@ -95,7 +95,7 @@ void InnerProductLayer::Backward_cpu(const vector*>& top, // Gradient with respect to bottom data caffe_cpu_gemm(CblasNoTrans, CblasNoTrans, M_, K_, N_, (Dtype)1., top_diff, this->blobs_[0]->cpu_data(), (Dtype)0., - (*bottom)[0]->mutable_cpu_diff()); + bottom[0]->mutable_cpu_diff()); } } @@ -104,5 +104,6 @@ STUB_GPU(InnerProductLayer); #endif INSTANTIATE_CLASS(InnerProductLayer); +REGISTER_LAYER_CLASS(InnerProduct); } // namespace caffe diff --git a/src/caffe/layers/inner_product_layer.cu b/src/caffe/layers/inner_product_layer.cu index 3a0d4388352..a9e1784a205 100644 --- a/src/caffe/layers/inner_product_layer.cu +++ b/src/caffe/layers/inner_product_layer.cu @@ -11,9 +11,9 @@ namespace caffe { template void InnerProductLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); const Dtype* weight = this->blobs_[0]->gpu_data(); caffe_gpu_gemm(CblasNoTrans, CblasTrans, M_, N_, K_, (Dtype)1., bottom_data, weight, (Dtype)0., top_data); @@ -27,10 +27,10 @@ void InnerProductLayer::Forward_gpu(const vector*>& bottom, template void InnerProductLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (this->param_propagate_down_[0]) { const Dtype* top_diff = top[0]->gpu_diff(); - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); + const Dtype* bottom_data = bottom[0]->gpu_data(); // Gradient with respect to weight caffe_gpu_gemm(CblasTrans, CblasNoTrans, N_, K_, M_, (Dtype)1., top_diff, bottom_data, (Dtype)0., this->blobs_[0]->mutable_gpu_diff()); @@ -47,10 +47,10 @@ void InnerProductLayer::Backward_gpu(const vector*>& top, // Gradient with respect to bottom data caffe_gpu_gemm(CblasNoTrans, CblasNoTrans, M_, K_, N_, (Dtype)1., top_diff, this->blobs_[0]->gpu_data(), (Dtype)0., - (*bottom)[0]->mutable_gpu_diff()); + bottom[0]->mutable_gpu_diff()); } } -INSTANTIATE_CLASS(InnerProductLayer); +INSTANTIATE_LAYER_GPU_FUNCS(InnerProductLayer); } // namespace caffe diff --git a/src/caffe/layers/loss_layer.cpp b/src/caffe/layers/loss_layer.cpp index 9eb9dbd5c5b..a5b6d11b065 100644 --- a/src/caffe/layers/loss_layer.cpp +++ b/src/caffe/layers/loss_layer.cpp @@ -12,7 +12,7 @@ namespace caffe { template void LossLayer::LayerSetUp( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { // LossLayers have a non-zero (1) loss by default. if (this->layer_param_.loss_weight_size() == 0) { this->layer_param_.add_loss_weight(Dtype(1)); @@ -21,10 +21,10 @@ void LossLayer::LayerSetUp( template void LossLayer::Reshape( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { CHECK_EQ(bottom[0]->num(), bottom[1]->num()) << "The data and label should have the same number."; - (*top)[0]->Reshape(1, 1, 1, 1); + top[0]->Reshape(1, 1, 1, 1); } INSTANTIATE_CLASS(LossLayer); diff --git a/src/caffe/layers/lrn_layer.cpp b/src/caffe/layers/lrn_layer.cpp index d9e41e9c137..5e3e7c429ef 100644 --- a/src/caffe/layers/lrn_layer.cpp +++ b/src/caffe/layers/lrn_layer.cpp @@ -8,12 +8,13 @@ namespace caffe { template void LRNLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { size_ = this->layer_param_.lrn_param().local_size(); CHECK_EQ(size_ % 2, 1) << "LRN only supports odd values for local_size"; pre_pad_ = (size_ - 1) / 2; alpha_ = this->layer_param_.lrn_param().alpha(); beta_ = this->layer_param_.lrn_param().beta(); + k_ = this->layer_param_.lrn_param().k(); if (this->layer_param_.lrn_param().norm_region() == LRNParameter_NormRegion_WITHIN_CHANNEL) { // Set up split_layer_ to use inputs in the numerator and denominator. @@ -22,7 +23,7 @@ void LRNLayer::LayerSetUp(const vector*>& bottom, split_top_vec_.push_back(&square_input_); LayerParameter split_param; split_layer_.reset(new SplitLayer(split_param)); - split_layer_->SetUp(bottom, &split_top_vec_); + split_layer_->SetUp(bottom, split_top_vec_); // Set up square_layer_ to square the inputs. square_bottom_vec_.clear(); square_top_vec_.clear(); @@ -31,7 +32,7 @@ void LRNLayer::LayerSetUp(const vector*>& bottom, LayerParameter square_param; square_param.mutable_power_param()->set_power(Dtype(2)); square_layer_.reset(new PowerLayer(square_param)); - square_layer_->SetUp(square_bottom_vec_, &square_top_vec_); + square_layer_->SetUp(square_bottom_vec_, square_top_vec_); // Set up pool_layer_ to sum over square neighborhoods of the input. pool_top_vec_.clear(); pool_top_vec_.push_back(&pool_output_); @@ -41,7 +42,7 @@ void LRNLayer::LayerSetUp(const vector*>& bottom, pool_param.mutable_pooling_param()->set_pad(pre_pad_); pool_param.mutable_pooling_param()->set_kernel_size(size_); pool_layer_.reset(new PoolingLayer(pool_param)); - pool_layer_->SetUp(square_top_vec_, &pool_top_vec_); + pool_layer_->SetUp(square_top_vec_, pool_top_vec_); // Set up power_layer_ to compute (1 + alpha_/N^2 s)^-beta_, where s is // the sum of a squared neighborhood (the output of pool_layer_). power_top_vec_.clear(); @@ -51,7 +52,7 @@ void LRNLayer::LayerSetUp(const vector*>& bottom, power_param.mutable_power_param()->set_scale(alpha_); power_param.mutable_power_param()->set_shift(Dtype(1)); power_layer_.reset(new PowerLayer(power_param)); - power_layer_->SetUp(pool_top_vec_, &power_top_vec_); + power_layer_->SetUp(pool_top_vec_, power_top_vec_); // Set up a product_layer_ to compute outputs by multiplying inputs by the // inverse demoninator computed by the power layer. product_bottom_vec_.clear(); @@ -67,21 +68,21 @@ void LRNLayer::LayerSetUp(const vector*>& bottom, template void LRNLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { num_ = bottom[0]->num(); channels_ = bottom[0]->channels(); height_ = bottom[0]->height(); width_ = bottom[0]->width(); switch (this->layer_param_.lrn_param().norm_region()) { case LRNParameter_NormRegion_ACROSS_CHANNELS: - (*top)[0]->Reshape(num_, channels_, height_, width_); + top[0]->Reshape(num_, channels_, height_, width_); scale_.Reshape(num_, channels_, height_, width_); break; case LRNParameter_NormRegion_WITHIN_CHANNEL: - split_layer_->Reshape(bottom, &split_top_vec_); - square_layer_->Reshape(square_bottom_vec_, &square_top_vec_); - pool_layer_->Reshape(square_top_vec_, &pool_top_vec_); - power_layer_->Reshape(pool_top_vec_, &power_top_vec_); + split_layer_->Reshape(bottom, split_top_vec_); + square_layer_->Reshape(square_bottom_vec_, square_top_vec_); + pool_layer_->Reshape(square_top_vec_, pool_top_vec_); + power_layer_->Reshape(pool_top_vec_, power_top_vec_); product_layer_->Reshape(product_bottom_vec_, top); break; } @@ -89,7 +90,7 @@ void LRNLayer::Reshape(const vector*>& bottom, template void LRNLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { switch (this->layer_param_.lrn_param().norm_region()) { case LRNParameter_NormRegion_ACROSS_CHANNELS: CrossChannelForward_cpu(bottom, top); @@ -104,13 +105,13 @@ void LRNLayer::Forward_cpu(const vector*>& bottom, template void LRNLayer::CrossChannelForward_cpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); Dtype* scale_data = scale_.mutable_cpu_data(); // start with the constant value for (int i = 0; i < scale_.count(); ++i) { - scale_data[i] = 1.; + scale_data[i] = k_; } Blob padded_square(1, channels_ + size_ - 1, height_, width_); Dtype* padded_square_data = padded_square.mutable_cpu_data(); @@ -151,17 +152,17 @@ void LRNLayer::CrossChannelForward_cpu( template void LRNLayer::WithinChannelForward( - const vector*>& bottom, vector*>* top) { - split_layer_->Forward(bottom, &split_top_vec_); - square_layer_->Forward(square_bottom_vec_, &square_top_vec_); - pool_layer_->Forward(square_top_vec_, &pool_top_vec_); - power_layer_->Forward(pool_top_vec_, &power_top_vec_); + const vector*>& bottom, const vector*>& top) { + split_layer_->Forward(bottom, split_top_vec_); + square_layer_->Forward(square_bottom_vec_, square_top_vec_); + pool_layer_->Forward(square_top_vec_, pool_top_vec_); + power_layer_->Forward(pool_top_vec_, power_top_vec_); product_layer_->Forward(product_bottom_vec_, top); } template void LRNLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { switch (this->layer_param_.lrn_param().norm_region()) { case LRNParameter_NormRegion_ACROSS_CHANNELS: CrossChannelBackward_cpu(top, propagate_down, bottom); @@ -177,12 +178,12 @@ void LRNLayer::Backward_cpu(const vector*>& top, template void LRNLayer::CrossChannelBackward_cpu( const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { const Dtype* top_diff = top[0]->cpu_diff(); const Dtype* top_data = top[0]->cpu_data(); - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); + const Dtype* bottom_data = bottom[0]->cpu_data(); const Dtype* scale_data = scale_.cpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); Blob padded_ratio(1, channels_ + size_ - 1, height_, width_); Blob accum_ratio(1, 1, height_, width_); Dtype* padded_ratio_data = padded_ratio.mutable_cpu_data(); @@ -232,14 +233,14 @@ void LRNLayer::CrossChannelBackward_cpu( template void LRNLayer::WithinChannelBackward( const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { vector product_propagate_down(2, true); - product_layer_->Backward(top, product_propagate_down, &product_bottom_vec_); - power_layer_->Backward(power_top_vec_, propagate_down, &pool_top_vec_); - pool_layer_->Backward(pool_top_vec_, propagate_down, &square_top_vec_); + product_layer_->Backward(top, product_propagate_down, product_bottom_vec_); + power_layer_->Backward(power_top_vec_, propagate_down, pool_top_vec_); + pool_layer_->Backward(pool_top_vec_, propagate_down, square_top_vec_); square_layer_->Backward(square_top_vec_, propagate_down, - &square_bottom_vec_); + square_bottom_vec_); split_layer_->Backward(split_top_vec_, propagate_down, bottom); } } @@ -251,6 +252,6 @@ STUB_GPU_BACKWARD(LRNLayer, CrossChannelBackward); #endif INSTANTIATE_CLASS(LRNLayer); - +REGISTER_LAYER_CLASS(LRN); } // namespace caffe diff --git a/src/caffe/layers/lrn_layer.cu b/src/caffe/layers/lrn_layer.cu index d6cb23bf245..58c39926c72 100644 --- a/src/caffe/layers/lrn_layer.cu +++ b/src/caffe/layers/lrn_layer.cu @@ -10,7 +10,7 @@ template __global__ void LRNFillScale(const int nthreads, const Dtype* in, const int num, const int channels, const int height, const int width, const int size, const Dtype alpha_over_size, - Dtype* scale) { + const Dtype k, Dtype* scale) { CUDA_KERNEL_LOOP(index, nthreads) { // find out the local offset int w = index % width; @@ -33,20 +33,20 @@ __global__ void LRNFillScale(const int nthreads, const Dtype* in, // until we reach size, nothing needs to be subtracted while (head < size) { accum_scale += in[head * step] * in[head * step]; - scale[(head - post_pad) * step] = 1. + accum_scale * alpha_over_size; + scale[(head - post_pad) * step] = k + accum_scale * alpha_over_size; ++head; } // both add and subtract while (head < channels) { accum_scale += in[head * step] * in[head * step]; accum_scale -= in[(head - size) * step] * in[(head - size) * step]; - scale[(head - post_pad) * step] = 1. + accum_scale * alpha_over_size; + scale[(head - post_pad) * step] = k + accum_scale * alpha_over_size; ++head; } // subtract only while (head < channels + post_pad) { accum_scale -= in[(head - size) * step] * in[(head - size) * step]; - scale[(head - post_pad) * step] = 1. + accum_scale * alpha_over_size; + scale[(head - post_pad) * step] = k + accum_scale * alpha_over_size; ++head; } } @@ -55,7 +55,7 @@ __global__ void LRNFillScale(const int nthreads, const Dtype* in, template void LRNLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { switch (this->layer_param_.lrn_param().norm_region()) { case LRNParameter_NormRegion_ACROSS_CHANNELS: CrossChannelForward_gpu(bottom, top); @@ -79,10 +79,10 @@ __global__ void LRNComputeOutput(const int nthreads, const Dtype* in, template void LRNLayer::CrossChannelForward_gpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { // First, compute scale const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); Dtype* scale_data = scale_.mutable_gpu_data(); // We will launch one kernel for each pixel location, and have the kernel // go through all the channels. @@ -90,7 +90,7 @@ void LRNLayer::CrossChannelForward_gpu( // NOLINT_NEXT_LINE(whitespace/operators) LRNFillScale<<>>( n_threads, bottom_data, num_, channels_, height_, width_, size_, - alpha_ / size_, scale_data); + alpha_ / size_, k_, scale_data); CUDA_POST_KERNEL_CHECK; n_threads = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) @@ -98,11 +98,15 @@ void LRNLayer::CrossChannelForward_gpu( n_threads, bottom_data, scale_data, -beta_, top_data); CUDA_POST_KERNEL_CHECK; } +template void LRNLayer::CrossChannelForward_gpu( + const vector*>& bottom, const vector*>& top); +template void LRNLayer::CrossChannelForward_gpu( + const vector*>& bottom, const vector*>& top); template void LRNLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { switch (this->layer_param_.lrn_param().norm_region()) { case LRNParameter_NormRegion_ACROSS_CHANNELS: CrossChannelBackward_gpu(top, propagate_down, bottom); @@ -179,17 +183,24 @@ __global__ void LRNComputeDiff(const int nthreads, const Dtype* bottom_data, template void LRNLayer::CrossChannelBackward_gpu( const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { int n_threads = num_ * height_ * width_; // NOLINT_NEXT_LINE(whitespace/operators) LRNComputeDiff<<>>( - n_threads, (*bottom)[0]->gpu_data(), top[0]->gpu_data(), + n_threads, bottom[0]->gpu_data(), top[0]->gpu_data(), scale_.gpu_data(), top[0]->gpu_diff(), num_, channels_, height_, width_, size_, -beta_, Dtype(2. * alpha_ * beta_ / size_), - (*bottom)[0]->mutable_gpu_diff()); + bottom[0]->mutable_gpu_diff()); } +template void LRNLayer::CrossChannelBackward_gpu( + const vector*>& top, const vector& propagate_down, + const vector*>& bottom); +template void LRNLayer::CrossChannelBackward_gpu( + const vector*>& top, const vector& propagate_down, + const vector*>& bottom); -INSTANTIATE_CLASS(LRNLayer); + +INSTANTIATE_LAYER_GPU_FUNCS(LRNLayer); } // namespace caffe diff --git a/src/caffe/layers/memory_data_layer.cpp b/src/caffe/layers/memory_data_layer.cpp index ab631a884fb..effdad90aff 100644 --- a/src/caffe/layers/memory_data_layer.cpp +++ b/src/caffe/layers/memory_data_layer.cpp @@ -1,3 +1,5 @@ +#include + #include #include "caffe/data_layers.hpp" @@ -8,21 +10,18 @@ namespace caffe { template void MemoryDataLayer::DataLayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { batch_size_ = this->layer_param_.memory_data_param().batch_size(); - this->datum_channels_ = this->layer_param_.memory_data_param().channels(); - this->datum_height_ = this->layer_param_.memory_data_param().height(); - this->datum_width_ = this->layer_param_.memory_data_param().width(); - this->datum_size_ = this->datum_channels_ * this->datum_height_ * - this->datum_width_; - CHECK_GT(batch_size_ * this->datum_size_, 0) << + channels_ = this->layer_param_.memory_data_param().channels(); + height_ = this->layer_param_.memory_data_param().height(); + width_ = this->layer_param_.memory_data_param().width(); + size_ = channels_ * height_ * width_; + CHECK_GT(batch_size_ * size_, 0) << "batch_size, channels, height, and width must be specified and" " positive in memory_data_param"; - (*top)[0]->Reshape(batch_size_, this->datum_channels_, this->datum_height_, - this->datum_width_); - (*top)[1]->Reshape(batch_size_, 1, 1, 1); - added_data_.Reshape(batch_size_, this->datum_channels_, this->datum_height_, - this->datum_width_); + top[0]->Reshape(batch_size_, channels_, height_, width_); + top[1]->Reshape(batch_size_, 1, 1, 1); + added_data_.Reshape(batch_size_, channels_, height_, width_); added_label_.Reshape(batch_size_, 1, 1, 1); data_ = NULL; labels_ = NULL; @@ -33,23 +32,47 @@ void MemoryDataLayer::DataLayerSetUp(const vector*>& bottom, template void MemoryDataLayer::AddDatumVector(const vector& datum_vector) { CHECK(!has_new_data_) << - "Can't add Datum when earlier ones haven't been consumed" - << " by the upper layers"; + "Can't add data until current data has been consumed."; size_t num = datum_vector.size(); - CHECK_GT(num, 0) << "There is no datum to add"; - CHECK_LE(num, batch_size_) << - "The number of added datum must be no greater than the batch size"; - + CHECK_GT(num, 0) << "There is no datum to add."; + CHECK_EQ(num % batch_size_, 0) << + "The added data must be a multiple of the batch size."; + added_data_.Reshape(num, channels_, height_, width_); + added_label_.Reshape(num, 1, 1, 1); + // Apply data transformations (mirror, scale, crop...) + this->data_transformer_->Transform(datum_vector, &added_data_); + // Copy Labels + Dtype* top_label = added_label_.mutable_cpu_data(); + for (int item_id = 0; item_id < num; ++item_id) { + top_label[item_id] = datum_vector[item_id].label(); + } + // num_images == batch_size_ Dtype* top_data = added_data_.mutable_cpu_data(); + Reset(top_data, top_label, num); + has_new_data_ = true; +} + +template +void MemoryDataLayer::AddMatVector(const vector& mat_vector, + const vector& labels) { + size_t num = mat_vector.size(); + CHECK(!has_new_data_) << + "Can't add mat until current data has been consumed."; + CHECK_GT(num, 0) << "There is no mat to add"; + CHECK_EQ(num % batch_size_, 0) << + "The added data must be a multiple of the batch size."; + added_data_.Reshape(num, channels_, height_, width_); + added_label_.Reshape(num, 1, 1, 1); + // Apply data transformations (mirror, scale, crop...) + this->data_transformer_->Transform(mat_vector, &added_data_); + // Copy Labels Dtype* top_label = added_label_.mutable_cpu_data(); - for (int batch_item_id = 0; batch_item_id < num; ++batch_item_id) { - // Apply data transformations (mirror, scale, crop...) - this->data_transformer_.Transform( - batch_item_id, datum_vector[batch_item_id], this->mean_, top_data); - top_label[batch_item_id] = datum_vector[batch_item_id].label(); + for (int item_id = 0; item_id < num; ++item_id) { + top_label[item_id] = labels[item_id]; } // num_images == batch_size_ - Reset(top_data, top_label, batch_size_); + Dtype* top_data = added_data_.mutable_cpu_data(); + Reset(top_data, top_label, num); has_new_data_ = true; } @@ -58,22 +81,40 @@ void MemoryDataLayer::Reset(Dtype* data, Dtype* labels, int n) { CHECK(data); CHECK(labels); CHECK_EQ(n % batch_size_, 0) << "n must be a multiple of batch size"; + // Warn with transformation parameters since a memory array is meant to + // be generic and no transformations are done with Reset(). + if (this->layer_param_.has_transform_param()) { + LOG(WARNING) << this->type() << " does not transform array data on Reset()"; + } data_ = data; labels_ = labels; n_ = n; pos_ = 0; } +template +void MemoryDataLayer::set_batch_size(int new_size) { + CHECK(!has_new_data_) << + "Can't change batch_size until current data has been consumed."; + batch_size_ = new_size; + added_data_.Reshape(batch_size_, channels_, height_, width_); + added_label_.Reshape(batch_size_, 1, 1, 1); +} + template void MemoryDataLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { CHECK(data_) << "MemoryDataLayer needs to be initalized by calling Reset"; - (*top)[0]->set_cpu_data(data_ + pos_ * this->datum_size_); - (*top)[1]->set_cpu_data(labels_ + pos_); + top[0]->Reshape(batch_size_, channels_, height_, width_); + top[1]->Reshape(batch_size_, 1, 1, 1); + top[0]->set_cpu_data(data_ + pos_ * size_); + top[1]->set_cpu_data(labels_ + pos_); pos_ = (pos_ + batch_size_) % n_; - has_new_data_ = false; + if (pos_ == 0) + has_new_data_ = false; } INSTANTIATE_CLASS(MemoryDataLayer); +REGISTER_LAYER_CLASS(MemoryData); } // namespace caffe diff --git a/src/caffe/layers/multinomial_logistic_loss_layer.cpp b/src/caffe/layers/multinomial_logistic_loss_layer.cpp index c0fe1966a4d..4267a594a0f 100644 --- a/src/caffe/layers/multinomial_logistic_loss_layer.cpp +++ b/src/caffe/layers/multinomial_logistic_loss_layer.cpp @@ -12,7 +12,7 @@ namespace caffe { template void MultinomialLogisticLossLayer::Reshape( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::Reshape(bottom, top); CHECK_EQ(bottom[1]->channels(), 1); CHECK_EQ(bottom[1]->height(), 1); @@ -21,7 +21,7 @@ void MultinomialLogisticLossLayer::Reshape( template void MultinomialLogisticLossLayer::Forward_cpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); const Dtype* bottom_label = bottom[1]->cpu_data(); int num = bottom[0]->num(); @@ -33,24 +33,24 @@ void MultinomialLogisticLossLayer::Forward_cpu( bottom_data[i * dim + label], Dtype(kLOG_THRESHOLD)); loss -= log(prob); } - (*top)[0]->mutable_cpu_data()[0] = loss / num; + top[0]->mutable_cpu_data()[0] = loss / num; } template void MultinomialLogisticLossLayer::Backward_cpu( const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[1]) { - LOG(FATAL) << this->type_name() + LOG(FATAL) << this->type() << " Layer cannot backpropagate to label inputs."; } if (propagate_down[0]) { - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); - const Dtype* bottom_label = (*bottom)[1]->cpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - int num = (*bottom)[0]->num(); - int dim = (*bottom)[0]->count() / (*bottom)[0]->num(); - caffe_set((*bottom)[0]->count(), Dtype(0), bottom_diff); + const Dtype* bottom_data = bottom[0]->cpu_data(); + const Dtype* bottom_label = bottom[1]->cpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + int num = bottom[0]->num(); + int dim = bottom[0]->count() / bottom[0]->num(); + caffe_set(bottom[0]->count(), Dtype(0), bottom_diff); const Dtype scale = - top[0]->cpu_diff()[0] / num; for (int i = 0; i < num; ++i) { int label = static_cast(bottom_label[i]); @@ -62,5 +62,6 @@ void MultinomialLogisticLossLayer::Backward_cpu( } INSTANTIATE_CLASS(MultinomialLogisticLossLayer); +REGISTER_LAYER_CLASS(MultinomialLogisticLoss); } // namespace caffe diff --git a/src/caffe/layers/mvn_layer.cpp b/src/caffe/layers/mvn_layer.cpp index 6a57b3ea7fc..b74d7b4f300 100644 --- a/src/caffe/layers/mvn_layer.cpp +++ b/src/caffe/layers/mvn_layer.cpp @@ -9,8 +9,8 @@ namespace caffe { template void MVNLayer::Reshape(const vector*>& bottom, - vector*>* top) { - (*top)[0]->Reshape(bottom[0]->num(), bottom[0]->channels(), + const vector*>& top) { + top[0]->Reshape(bottom[0]->num(), bottom[0]->channels(), bottom[0]->height(), bottom[0]->width()); mean_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1); @@ -26,9 +26,9 @@ void MVNLayer::Reshape(const vector*>& bottom, template void MVNLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); int num; if (this->layer_param_.mvn_param().across_channels()) num = bottom[0]->num(); @@ -89,19 +89,19 @@ void MVNLayer::Forward_cpu(const vector*>& bottom, template void MVNLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { const Dtype* top_diff = top[0]->cpu_diff(); const Dtype* top_data = top[0]->cpu_data(); - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + const Dtype* bottom_data = bottom[0]->cpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); int num; if (this->layer_param_.mvn_param().across_channels()) - num = (*bottom)[0]->num(); + num = bottom[0]->num(); else - num = (*bottom)[0]->num() * (*bottom)[0]->channels(); + num = bottom[0]->num() * bottom[0]->channels(); - int dim = (*bottom)[0]->count() / num; + int dim = bottom[0]->count() / num; Dtype eps = 1e-10; if (this->layer_param_.mvn_param().normalize_variance()) { @@ -159,6 +159,6 @@ STUB_GPU(MVNLayer); #endif INSTANTIATE_CLASS(MVNLayer); - +REGISTER_LAYER_CLASS(MVN); } // namespace caffe diff --git a/src/caffe/layers/mvn_layer.cu b/src/caffe/layers/mvn_layer.cu index 2c02dfe1ddc..0667f50380f 100644 --- a/src/caffe/layers/mvn_layer.cu +++ b/src/caffe/layers/mvn_layer.cu @@ -9,9 +9,9 @@ namespace caffe { template void MVNLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); int num; if (this->layer_param_.mvn_param().across_channels()) num = bottom[0]->num(); @@ -73,19 +73,19 @@ void MVNLayer::Forward_gpu(const vector*>& bottom, template void MVNLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { const Dtype* top_diff = top[0]->gpu_diff(); const Dtype* top_data = top[0]->gpu_data(); - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + const Dtype* bottom_data = bottom[0]->gpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); int num; if (this->layer_param_.mvn_param().across_channels()) - num = (*bottom)[0]->num(); + num = bottom[0]->num(); else - num = (*bottom)[0]->num() * (*bottom)[0]->channels(); + num = bottom[0]->num() * bottom[0]->channels(); - int dim = (*bottom)[0]->count() / num; + int dim = bottom[0]->count() / num; Dtype eps = 1e-10; @@ -139,7 +139,7 @@ void MVNLayer::Backward_gpu(const vector*>& top, } -INSTANTIATE_CLASS(MVNLayer); +INSTANTIATE_LAYER_GPU_FUNCS(MVNLayer); } // namespace caffe diff --git a/src/caffe/layers/neuron_layer.cpp b/src/caffe/layers/neuron_layer.cpp index c28e36ea23b..ba67b43878e 100644 --- a/src/caffe/layers/neuron_layer.cpp +++ b/src/caffe/layers/neuron_layer.cpp @@ -7,8 +7,8 @@ namespace caffe { template void NeuronLayer::Reshape(const vector*>& bottom, - vector*>* top) { - (*top)[0]->ReshapeLike(*bottom[0]); + const vector*>& top) { + top[0]->ReshapeLike(*bottom[0]); } INSTANTIATE_CLASS(NeuronLayer); diff --git a/src/caffe/layers/pooling_layer.cpp b/src/caffe/layers/pooling_layer.cpp index 8e8ffad66a0..6f4c69c861e 100644 --- a/src/caffe/layers/pooling_layer.cpp +++ b/src/caffe/layers/pooling_layer.cpp @@ -15,14 +15,20 @@ using std::max; template void PoolingLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { PoolingParameter pool_param = this->layer_param_.pooling_param(); - CHECK(!pool_param.has_kernel_size() != + if (pool_param.global_pooling()) { + CHECK(!(pool_param.has_kernel_size() || + pool_param.has_kernel_h() || pool_param.has_kernel_w())) + << "With Global_pooling: true Filter size cannot specified"; + } else { + CHECK(!pool_param.has_kernel_size() != !(pool_param.has_kernel_h() && pool_param.has_kernel_w())) << "Filter size is kernel_size OR kernel_h and kernel_w; not both"; - CHECK(pool_param.has_kernel_size() || + CHECK(pool_param.has_kernel_size() || (pool_param.has_kernel_h() && pool_param.has_kernel_w())) << "For non-square filters both kernel_h and kernel_w are required."; + } CHECK((!pool_param.has_pad() && pool_param.has_pad_h() && pool_param.has_pad_w()) || (!pool_param.has_pad_h() && !pool_param.has_pad_w())) @@ -31,11 +37,17 @@ void PoolingLayer::LayerSetUp(const vector*>& bottom, && pool_param.has_stride_w()) || (!pool_param.has_stride_h() && !pool_param.has_stride_w())) << "Stride is stride OR stride_h and stride_w are required."; - if (pool_param.has_kernel_size()) { - kernel_h_ = kernel_w_ = pool_param.kernel_size(); + global_pooling_ = pool_param.global_pooling(); + if (global_pooling_) { + kernel_h_ = bottom[0]->height(); + kernel_w_ = bottom[0]->width(); } else { - kernel_h_ = pool_param.kernel_h(); - kernel_w_ = pool_param.kernel_w(); + if (pool_param.has_kernel_size()) { + kernel_h_ = kernel_w_ = pool_param.kernel_size(); + } else { + kernel_h_ = pool_param.kernel_h(); + kernel_w_ = pool_param.kernel_w(); + } } CHECK_GT(kernel_h_, 0) << "Filter dimensions cannot be zero."; CHECK_GT(kernel_w_, 0) << "Filter dimensions cannot be zero."; @@ -51,6 +63,10 @@ void PoolingLayer::LayerSetUp(const vector*>& bottom, stride_h_ = pool_param.stride_h(); stride_w_ = pool_param.stride_w(); } + if (global_pooling_) { + CHECK(pad_h_ == 0 && pad_w_ == 0 && stride_h_ == 1 && stride_w_ == 1) + << "With Global_pooling: true; only pad = 0 and stride = 1"; + } if (pad_h_ != 0 || pad_w_ != 0) { CHECK(this->layer_param_.pooling_param().pool() == PoolingParameter_PoolMethod_AVE @@ -64,10 +80,14 @@ void PoolingLayer::LayerSetUp(const vector*>& bottom, template void PoolingLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { channels_ = bottom[0]->channels(); height_ = bottom[0]->height(); width_ = bottom[0]->width(); + if (global_pooling_) { + kernel_h_ = bottom[0]->height(); + kernel_w_ = bottom[0]->width(); + } pooled_height_ = static_cast(ceil(static_cast( height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1; pooled_width_ = static_cast(ceil(static_cast( @@ -84,14 +104,14 @@ void PoolingLayer::Reshape(const vector*>& bottom, CHECK_LT((pooled_height_ - 1) * stride_h_, height_ + pad_h_); CHECK_LT((pooled_width_ - 1) * stride_w_, width_ + pad_w_); } - (*top)[0]->Reshape(bottom[0]->num(), channels_, pooled_height_, + top[0]->Reshape(bottom[0]->num(), channels_, pooled_height_, pooled_width_); - if (top->size() > 1) { - (*top)[1]->ReshapeLike(*(*top)[0]); + if (top.size() > 1) { + top[1]->ReshapeLike(*top[0]); } // If max pooling, we will initialize the vector index part. if (this->layer_param_.pooling_param().pool() == - PoolingParameter_PoolMethod_MAX && top->size() == 1) { + PoolingParameter_PoolMethod_MAX && top.size() == 1) { max_idx_.Reshape(bottom[0]->num(), channels_, pooled_height_, pooled_width_); } @@ -107,12 +127,12 @@ void PoolingLayer::Reshape(const vector*>& bottom, // case? template void PoolingLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); - const int top_count = (*top)[0]->count(); + Dtype* top_data = top[0]->mutable_cpu_data(); + const int top_count = top[0]->count(); // We'll output the mask to top[1] if it's of size >1. - const bool use_top_mask = top->size() > 1; + const bool use_top_mask = top.size() > 1; int* mask = NULL; // suppress warnings about uninitalized variables Dtype* top_mask = NULL; // Different pooling methods. We explicitly do the switch outside the for @@ -121,7 +141,7 @@ void PoolingLayer::Forward_cpu(const vector*>& bottom, case PoolingParameter_PoolMethod_MAX: // Initialize if (use_top_mask) { - top_mask = (*top)[1]->mutable_cpu_data(); + top_mask = top[1]->mutable_cpu_data(); caffe_set(top_count, Dtype(-1), top_mask); } else { mask = max_idx_.mutable_cpu_data(); @@ -157,11 +177,11 @@ void PoolingLayer::Forward_cpu(const vector*>& bottom, } // compute offset bottom_data += bottom[0]->offset(0, 1); - top_data += (*top)[0]->offset(0, 1); + top_data += top[0]->offset(0, 1); if (use_top_mask) { - top_mask += (*top)[0]->offset(0, 1); + top_mask += top[0]->offset(0, 1); } else { - mask += (*top)[0]->offset(0, 1); + mask += top[0]->offset(0, 1); } } } @@ -195,7 +215,7 @@ void PoolingLayer::Forward_cpu(const vector*>& bottom, } // compute offset bottom_data += bottom[0]->offset(0, 1); - top_data += (*top)[0]->offset(0, 1); + top_data += top[0]->offset(0, 1); } } break; @@ -209,15 +229,15 @@ void PoolingLayer::Forward_cpu(const vector*>& bottom, template void PoolingLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (!propagate_down[0]) { return; } const Dtype* top_diff = top[0]->cpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); // Different pooling methods. We explicitly do the switch outside the for // loop to save time, although this results in more codes. - caffe_set((*bottom)[0]->count(), Dtype(0), bottom_diff); + caffe_set(bottom[0]->count(), Dtype(0), bottom_diff); // We'll output the mask to top[1] if it's of size >1. const bool use_top_mask = top.size() > 1; const int* mask = NULL; // suppress warnings about uninitialized variables @@ -240,7 +260,7 @@ void PoolingLayer::Backward_cpu(const vector*>& top, bottom_diff[bottom_index] += top_diff[index]; } } - bottom_diff += (*bottom)[0]->offset(0, 1); + bottom_diff += bottom[0]->offset(0, 1); top_diff += top[0]->offset(0, 1); if (use_top_mask) { top_mask += top[0]->offset(0, 1); @@ -274,7 +294,7 @@ void PoolingLayer::Backward_cpu(const vector*>& top, } } // offset - bottom_diff += (*bottom)[0]->offset(0, 1); + bottom_diff += bottom[0]->offset(0, 1); top_diff += top[0]->offset(0, 1); } } @@ -294,5 +314,4 @@ STUB_GPU(PoolingLayer); INSTANTIATE_CLASS(PoolingLayer); - } // namespace caffe diff --git a/src/caffe/layers/pooling_layer.cu b/src/caffe/layers/pooling_layer.cu index e64128b87f2..d1d48501af3 100644 --- a/src/caffe/layers/pooling_layer.cu +++ b/src/caffe/layers/pooling_layer.cu @@ -152,18 +152,18 @@ __global__ void StoPoolForwardTest(const int nthreads, template void PoolingLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); - int count = (*top)[0]->count(); + Dtype* top_data = top[0]->mutable_gpu_data(); + int count = top[0]->count(); // We'll output the mask to top[1] if it's of size >1. - const bool use_top_mask = top->size() > 1; + const bool use_top_mask = top.size() > 1; int* mask = NULL; Dtype* top_mask = NULL; switch (this->layer_param_.pooling_param().pool()) { case PoolingParameter_PoolMethod_MAX: if (use_top_mask) { - top_mask = (*top)[1]->mutable_gpu_data(); + top_mask = top[1]->mutable_gpu_data(); } else { mask = max_idx_.mutable_gpu_data(); } @@ -182,7 +182,7 @@ void PoolingLayer::Forward_gpu(const vector*>& bottom, kernel_w_, stride_h_, stride_w_, pad_h_, pad_w_, top_data); break; case PoolingParameter_PoolMethod_STOCHASTIC: - if (Caffe::phase() == Caffe::TRAIN) { + if (this->phase_ == TRAIN) { // We need to create the random index as well. caffe_gpu_rng_uniform(count, Dtype(0), Dtype(1), rand_idx_.mutable_gpu_data()); @@ -325,13 +325,13 @@ __global__ void StoPoolBackward(const int nthreads, template void PoolingLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (!propagate_down[0]) { return; } const Dtype* top_diff = top[0]->gpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + const int count = bottom[0]->count(); caffe_gpu_set(count, Dtype(0.), bottom_diff); // We'll output the mask to top[1] if it's of size >1. const bool use_top_mask = top.size() > 1; @@ -373,7 +373,7 @@ void PoolingLayer::Backward_gpu(const vector*>& top, } -INSTANTIATE_CLASS(PoolingLayer); +INSTANTIATE_LAYER_GPU_FUNCS(PoolingLayer); } // namespace caffe diff --git a/src/caffe/layers/power_layer.cpp b/src/caffe/layers/power_layer.cpp index bf61955d065..4fe34c49f32 100644 --- a/src/caffe/layers/power_layer.cpp +++ b/src/caffe/layers/power_layer.cpp @@ -9,7 +9,7 @@ namespace caffe { template void PowerLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { NeuronLayer::LayerSetUp(bottom, top); power_ = this->layer_param_.power_param().power(); scale_ = this->layer_param_.power_param().scale(); @@ -20,8 +20,8 @@ void PowerLayer::LayerSetUp(const vector*>& bottom, // Compute y = (shift + scale * x)^power template void PowerLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + const vector*>& top) { + Dtype* top_data = top[0]->mutable_cpu_data(); const int count = bottom[0]->count(); // Special case where we can ignore the input: scale or power is 0. if (diff_scale_ == Dtype(0)) { @@ -45,15 +45,15 @@ void PowerLayer::Forward_cpu(const vector*>& bottom, template void PowerLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + const int count = bottom[0]->count(); const Dtype* top_diff = top[0]->cpu_diff(); if (diff_scale_ == Dtype(0) || power_ == Dtype(1)) { caffe_set(count, diff_scale_, bottom_diff); } else { - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); + const Dtype* bottom_data = bottom[0]->cpu_data(); // Compute dy/dx = scale * power * (shift + scale * x)^(power - 1) // = diff_scale * y / (shift + scale * x) if (power_ == Dtype(2)) { @@ -99,6 +99,6 @@ STUB_GPU(PowerLayer); #endif INSTANTIATE_CLASS(PowerLayer); - +REGISTER_LAYER_CLASS(Power); } // namespace caffe diff --git a/src/caffe/layers/power_layer.cu b/src/caffe/layers/power_layer.cu index a40bc75829d..90d944059b6 100644 --- a/src/caffe/layers/power_layer.cu +++ b/src/caffe/layers/power_layer.cu @@ -9,8 +9,8 @@ namespace caffe { template void PowerLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + const vector*>& top) { + Dtype* top_data = top[0]->mutable_gpu_data(); const int count = bottom[0]->count(); // Special case where we can ignore the input: scale or power is 0. if (diff_scale_ == Dtype(0)) { @@ -34,15 +34,15 @@ void PowerLayer::Forward_gpu(const vector*>& bottom, template void PowerLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + const int count = bottom[0]->count(); const Dtype* top_diff = top[0]->gpu_diff(); if (diff_scale_ == Dtype(0) || power_ == Dtype(1)) { caffe_gpu_set(count, diff_scale_, bottom_diff); } else { - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); + const Dtype* bottom_data = bottom[0]->gpu_data(); // Compute dy/dx = scale * power * (shift + scale * x)^(power - 1) // = diff_scale * y / (shift + scale * x) if (power_ == Dtype(2)) { @@ -81,7 +81,7 @@ void PowerLayer::Backward_gpu(const vector*>& top, } } -INSTANTIATE_CLASS(PowerLayer); +INSTANTIATE_LAYER_GPU_FUNCS(PowerLayer); } // namespace caffe diff --git a/src/caffe/layers/relu_layer.cpp b/src/caffe/layers/relu_layer.cpp index b50352f8526..cc00319a578 100644 --- a/src/caffe/layers/relu_layer.cpp +++ b/src/caffe/layers/relu_layer.cpp @@ -8,9 +8,9 @@ namespace caffe { template void ReLULayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); const int count = bottom[0]->count(); Dtype negative_slope = this->layer_param_.relu_param().negative_slope(); for (int i = 0; i < count; ++i) { @@ -22,12 +22,12 @@ void ReLULayer::Forward_cpu(const vector*>& bottom, template void ReLULayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { - const Dtype* bottom_data = (*bottom)[0]->cpu_data(); + const Dtype* bottom_data = bottom[0]->cpu_data(); const Dtype* top_diff = top[0]->cpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + const int count = bottom[0]->count(); Dtype negative_slope = this->layer_param_.relu_param().negative_slope(); for (int i = 0; i < count; ++i) { bottom_diff[i] = top_diff[i] * ((bottom_data[i] > 0) @@ -43,5 +43,4 @@ STUB_GPU(ReLULayer); INSTANTIATE_CLASS(ReLULayer); - } // namespace caffe diff --git a/src/caffe/layers/relu_layer.cu b/src/caffe/layers/relu_layer.cu index def2bbcd7b9..b8924c855e5 100644 --- a/src/caffe/layers/relu_layer.cu +++ b/src/caffe/layers/relu_layer.cu @@ -16,9 +16,9 @@ __global__ void ReLUForward(const int n, const Dtype* in, Dtype* out, template void ReLULayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); const int count = bottom[0]->count(); Dtype negative_slope = this->layer_param_.relu_param().negative_slope(); // NOLINT_NEXT_LINE(whitespace/operators) @@ -44,12 +44,12 @@ __global__ void ReLUBackward(const int n, const Dtype* in_diff, template void ReLULayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { - const Dtype* bottom_data = (*bottom)[0]->gpu_data(); + const Dtype* bottom_data = bottom[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + const int count = bottom[0]->count(); Dtype negative_slope = this->layer_param_.relu_param().negative_slope(); // NOLINT_NEXT_LINE(whitespace/operators) ReLUBackward<<>>( @@ -59,7 +59,7 @@ void ReLULayer::Backward_gpu(const vector*>& top, } -INSTANTIATE_CLASS(ReLULayer); +INSTANTIATE_LAYER_GPU_FUNCS(ReLULayer); } // namespace caffe diff --git a/src/caffe/layers/sigmoid_cross_entropy_loss_layer.cpp b/src/caffe/layers/sigmoid_cross_entropy_loss_layer.cpp index 6a48099ae8b..077d949981c 100644 --- a/src/caffe/layers/sigmoid_cross_entropy_loss_layer.cpp +++ b/src/caffe/layers/sigmoid_cross_entropy_loss_layer.cpp @@ -10,30 +10,30 @@ namespace caffe { template void SigmoidCrossEntropyLossLayer::LayerSetUp( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::LayerSetUp(bottom, top); sigmoid_bottom_vec_.clear(); sigmoid_bottom_vec_.push_back(bottom[0]); sigmoid_top_vec_.clear(); sigmoid_top_vec_.push_back(sigmoid_output_.get()); - sigmoid_layer_->SetUp(sigmoid_bottom_vec_, &sigmoid_top_vec_); + sigmoid_layer_->SetUp(sigmoid_bottom_vec_, sigmoid_top_vec_); } template void SigmoidCrossEntropyLossLayer::Reshape( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::Reshape(bottom, top); CHECK_EQ(bottom[0]->count(), bottom[1]->count()) << "SIGMOID_CROSS_ENTROPY_LOSS layer inputs must have the same count."; - sigmoid_layer_->Reshape(sigmoid_bottom_vec_, &sigmoid_top_vec_); + sigmoid_layer_->Reshape(sigmoid_bottom_vec_, sigmoid_top_vec_); } template void SigmoidCrossEntropyLossLayer::Forward_cpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { // The forward pass computes the sigmoid outputs. sigmoid_bottom_vec_[0] = bottom[0]; - sigmoid_layer_->Forward(sigmoid_bottom_vec_, &sigmoid_top_vec_); + sigmoid_layer_->Forward(sigmoid_bottom_vec_, sigmoid_top_vec_); // Compute the loss (negative log likelihood) const int count = bottom[0]->count(); const int num = bottom[0]->num(); @@ -45,24 +45,24 @@ void SigmoidCrossEntropyLossLayer::Forward_cpu( loss -= input_data[i] * (target[i] - (input_data[i] >= 0)) - log(1 + exp(input_data[i] - 2 * input_data[i] * (input_data[i] >= 0))); } - (*top)[0]->mutable_cpu_data()[0] = loss / num; + top[0]->mutable_cpu_data()[0] = loss / num; } template void SigmoidCrossEntropyLossLayer::Backward_cpu( const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[1]) { - LOG(FATAL) << this->type_name() + LOG(FATAL) << this->type() << " Layer cannot backpropagate to label inputs."; } if (propagate_down[0]) { // First, compute the diff - const int count = (*bottom)[0]->count(); - const int num = (*bottom)[0]->num(); + const int count = bottom[0]->count(); + const int num = bottom[0]->num(); const Dtype* sigmoid_output_data = sigmoid_output_->cpu_data(); - const Dtype* target = (*bottom)[1]->cpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + const Dtype* target = bottom[1]->cpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); caffe_sub(count, sigmoid_output_data, target, bottom_diff); // Scale down gradient const Dtype loss_weight = top[0]->cpu_diff()[0]; @@ -75,6 +75,6 @@ STUB_GPU(SigmoidCrossEntropyLossLayer); #endif INSTANTIATE_CLASS(SigmoidCrossEntropyLossLayer); - +REGISTER_LAYER_CLASS(SigmoidCrossEntropyLoss); } // namespace caffe diff --git a/src/caffe/layers/sigmoid_cross_entropy_loss_layer.cu b/src/caffe/layers/sigmoid_cross_entropy_loss_layer.cu index 8d0fdc6fac4..08f7f492297 100644 --- a/src/caffe/layers/sigmoid_cross_entropy_loss_layer.cu +++ b/src/caffe/layers/sigmoid_cross_entropy_loss_layer.cu @@ -10,10 +10,10 @@ namespace caffe { template void SigmoidCrossEntropyLossLayer::Forward_gpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { // The forward pass computes the sigmoid outputs. sigmoid_bottom_vec_[0] = bottom[0]; - sigmoid_layer_->Forward(sigmoid_bottom_vec_, &sigmoid_top_vec_); + sigmoid_layer_->Forward(sigmoid_bottom_vec_, sigmoid_top_vec_); // Compute the loss (negative log likelihood) const int count = bottom[0]->count(); const int num = bottom[0]->num(); @@ -25,24 +25,24 @@ void SigmoidCrossEntropyLossLayer::Forward_gpu( loss -= input_data[i] * (target[i] - (input_data[i] >= 0)) - log(1 + exp(input_data[i] - 2 * input_data[i] * (input_data[i] >= 0))); } - (*top)[0]->mutable_cpu_data()[0] = loss / num; + top[0]->mutable_cpu_data()[0] = loss / num; } template void SigmoidCrossEntropyLossLayer::Backward_gpu( const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[1]) { - LOG(FATAL) << this->type_name() + LOG(FATAL) << this->type() << " Layer cannot backpropagate to label inputs."; } if (propagate_down[0]) { // First, compute the diff - const int count = (*bottom)[0]->count(); - const int num = (*bottom)[0]->num(); + const int count = bottom[0]->count(); + const int num = bottom[0]->num(); const Dtype* sigmoid_output_data = sigmoid_output_->gpu_data(); - const Dtype* target = (*bottom)[1]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + const Dtype* target = bottom[1]->gpu_data(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); caffe_copy(count, sigmoid_output_data, bottom_diff); caffe_gpu_axpy(count, Dtype(-1), target, bottom_diff); // Scale down gradient @@ -51,7 +51,7 @@ void SigmoidCrossEntropyLossLayer::Backward_gpu( } } -INSTANTIATE_CLASS(SigmoidCrossEntropyLossLayer); +INSTANTIATE_LAYER_GPU_FUNCS(SigmoidCrossEntropyLossLayer); } // namespace caffe diff --git a/src/caffe/layers/sigmoid_layer.cpp b/src/caffe/layers/sigmoid_layer.cpp index d7bba7fbfc3..48c384905bf 100644 --- a/src/caffe/layers/sigmoid_layer.cpp +++ b/src/caffe/layers/sigmoid_layer.cpp @@ -14,9 +14,9 @@ inline Dtype sigmoid(Dtype x) { template void SigmoidLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); const int count = bottom[0]->count(); for (int i = 0; i < count; ++i) { top_data[i] = sigmoid(bottom_data[i]); @@ -26,12 +26,12 @@ void SigmoidLayer::Forward_cpu(const vector*>& bottom, template void SigmoidLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { const Dtype* top_data = top[0]->cpu_data(); const Dtype* top_diff = top[0]->cpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + const int count = bottom[0]->count(); for (int i = 0; i < count; ++i) { const Dtype sigmoid_x = top_data[i]; bottom_diff[i] = top_diff[i] * sigmoid_x * (1. - sigmoid_x); diff --git a/src/caffe/layers/sigmoid_layer.cu b/src/caffe/layers/sigmoid_layer.cu index e1ebb1f6c41..e1af0657ec1 100644 --- a/src/caffe/layers/sigmoid_layer.cu +++ b/src/caffe/layers/sigmoid_layer.cu @@ -16,9 +16,9 @@ __global__ void SigmoidForward(const int n, const Dtype* in, Dtype* out) { template void SigmoidLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); const int count = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) SigmoidForward<<>>( @@ -43,12 +43,12 @@ __global__ void SigmoidBackward(const int n, const Dtype* in_diff, template void SigmoidLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { const Dtype* top_data = top[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + const int count = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) SigmoidBackward<<>>( count, top_diff, top_data, bottom_diff); @@ -56,7 +56,7 @@ void SigmoidLayer::Backward_gpu(const vector*>& top, } } -INSTANTIATE_CLASS(SigmoidLayer); +INSTANTIATE_LAYER_GPU_FUNCS(SigmoidLayer); } // namespace caffe diff --git a/src/caffe/layers/silence_layer.cpp b/src/caffe/layers/silence_layer.cpp index 75dbbf31f0d..4abf9eff4a2 100644 --- a/src/caffe/layers/silence_layer.cpp +++ b/src/caffe/layers/silence_layer.cpp @@ -8,11 +8,11 @@ namespace caffe { template void SilenceLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { - for (int i = 0; i < bottom->size(); ++i) { + const vector& propagate_down, const vector*>& bottom) { + for (int i = 0; i < bottom.size(); ++i) { if (propagate_down[i]) { - caffe_set((*bottom)[i]->count(), Dtype(0), - (*bottom)[i]->mutable_cpu_data()); + caffe_set(bottom[i]->count(), Dtype(0), + bottom[i]->mutable_cpu_data()); } } } @@ -22,5 +22,6 @@ STUB_GPU(SilenceLayer); #endif INSTANTIATE_CLASS(SilenceLayer); +REGISTER_LAYER_CLASS(Silence); } // namespace caffe diff --git a/src/caffe/layers/silence_layer.cu b/src/caffe/layers/silence_layer.cu index 735abe61eaf..8d044ee7307 100644 --- a/src/caffe/layers/silence_layer.cu +++ b/src/caffe/layers/silence_layer.cu @@ -8,21 +8,21 @@ namespace caffe { template void SilenceLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { // Do nothing. } template void SilenceLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { - for (int i = 0; i < bottom->size(); ++i) { + const vector& propagate_down, const vector*>& bottom) { + for (int i = 0; i < bottom.size(); ++i) { if (propagate_down[i]) { - caffe_gpu_set((*bottom)[i]->count(), Dtype(0), - (*bottom)[i]->mutable_gpu_data()); + caffe_gpu_set(bottom[i]->count(), Dtype(0), + bottom[i]->mutable_gpu_data()); } } } -INSTANTIATE_CLASS(SilenceLayer); +INSTANTIATE_LAYER_GPU_FUNCS(SilenceLayer); } // namespace caffe diff --git a/src/caffe/layers/slice_layer.cpp b/src/caffe/layers/slice_layer.cpp index ed679a9169e..46c3acd6513 100644 --- a/src/caffe/layers/slice_layer.cpp +++ b/src/caffe/layers/slice_layer.cpp @@ -9,7 +9,7 @@ namespace caffe { template void SliceLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const SliceParameter& slice_param = this->layer_param_.slice_param(); slice_dim_ = slice_param.slice_dim(); CHECK_GE(slice_dim_, 0); @@ -22,18 +22,18 @@ void SliceLayer::LayerSetUp(const vector*>& bottom, template void SliceLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { count_ = 0; num_ = bottom[0]->num(); channels_ = bottom[0]->channels(); height_ = bottom[0]->height(); width_ = bottom[0]->width(); if (slice_point_.size() != 0) { - CHECK_EQ(slice_point_.size(), top->size() - 1); + CHECK_EQ(slice_point_.size(), top.size() - 1); if (slice_dim_ == 0) { - CHECK_LE(top->size(), num_); + CHECK_LE(top.size(), num_); } else { - CHECK_LE(top->size(), channels_); + CHECK_LE(top.size(), channels_); } int prev = 0; vector slices; @@ -44,32 +44,32 @@ void SliceLayer::Reshape(const vector*>& bottom, } if (slice_dim_ == 0) { slices.push_back(num_ - prev); - for (int i = 0; i < top->size(); ++i) { - (*top)[i]->Reshape(slices[i], channels_, height_, width_); - count_ += (*top)[i]->count(); + for (int i = 0; i < top.size(); ++i) { + top[i]->Reshape(slices[i], channels_, height_, width_); + count_ += top[i]->count(); } } else { slices.push_back(channels_ - prev); - for (int i = 0; i < top->size(); ++i) { - (*top)[i]->Reshape(num_, slices[i], height_, width_); - count_ += (*top)[i]->count(); + for (int i = 0; i < top.size(); ++i) { + top[i]->Reshape(num_, slices[i], height_, width_); + count_ += top[i]->count(); } } } else { if (slice_dim_ == 0) { - CHECK_EQ(num_ % top->size(), 0) - << "Number of top blobs (" << top->size() << ") " + CHECK_EQ(num_ % top.size(), 0) + << "Number of top blobs (" << top.size() << ") " << "should evenly divide input num ( " << num_ << ")"; - num_ = num_ / top->size(); + num_ = num_ / top.size(); } else { - CHECK_EQ(channels_ % top->size(), 0) - << "Number of top blobs (" << top->size() << ") " + CHECK_EQ(channels_ % top.size(), 0) + << "Number of top blobs (" << top.size() << ") " << "should evenly divide input channels ( " << channels_ << ")"; - channels_ = channels_ / top->size(); + channels_ = channels_ / top.size(); } - for (int i = 0; i < top->size(); ++i) { - (*top)[i]->Reshape(num_, channels_, height_, width_); - count_ += (*top)[i]->count(); + for (int i = 0; i < top.size(); ++i) { + top[i]->Reshape(num_, channels_, height_, width_); + count_ += top[i]->count(); } } CHECK_EQ(count_, bottom[0]->count()); @@ -77,12 +77,12 @@ void SliceLayer::Reshape(const vector*>& bottom, template void SliceLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->mutable_cpu_data(); if (slice_dim_ == 0) { int offset_num = 0; - for (int i = 0; i < top->size(); ++i) { - Blob* blob = (*top)[i]; + for (int i = 0; i < top.size(); ++i) { + Blob* blob = top[i]; Dtype* top_data = blob->mutable_cpu_data(); caffe_copy(blob->count(), bottom_data + bottom[0]->offset(offset_num), top_data); @@ -90,8 +90,8 @@ void SliceLayer::Forward_cpu(const vector*>& bottom, } } else if (slice_dim_ == 1) { int offset_channel = 0; - for (int i = 0; i < top->size(); ++i) { - Blob* blob = (*top)[i]; + for (int i = 0; i < top.size(); ++i) { + Blob* blob = top[i]; Dtype* top_data = blob->mutable_cpu_data(); const int num_elem = blob->channels() * blob->height() * blob->width(); for (int n = 0; n < num_; ++n) { @@ -105,16 +105,16 @@ void SliceLayer::Forward_cpu(const vector*>& bottom, template void SliceLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (!propagate_down[0]) { return; } - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); if (slice_dim_ == 0) { int offset_num = 0; for (int i = 0; i < top.size(); ++i) { Blob* blob = top[i]; const Dtype* top_diff = blob->cpu_diff(); caffe_copy(blob->count(), top_diff, - bottom_diff + (*bottom)[0]->offset(offset_num)); + bottom_diff + bottom[0]->offset(offset_num)); offset_num += blob->num(); } } else if (slice_dim_ == 1) { @@ -125,7 +125,7 @@ void SliceLayer::Backward_cpu(const vector*>& top, const int num_elem = blob->channels() * blob->height() * blob->width(); for (int n = 0; n < num_; ++n) { caffe_copy(num_elem, top_diff + blob->offset(n), - bottom_diff + (*bottom)[0]->offset(n, offset_channel)); + bottom_diff + bottom[0]->offset(n, offset_channel)); } offset_channel += blob->channels(); } @@ -137,5 +137,6 @@ STUB_GPU(SliceLayer); #endif INSTANTIATE_CLASS(SliceLayer); +REGISTER_LAYER_CLASS(Slice); } // namespace caffe diff --git a/src/caffe/layers/slice_layer.cu b/src/caffe/layers/slice_layer.cu index f64e5754890..b5c5e61533f 100644 --- a/src/caffe/layers/slice_layer.cu +++ b/src/caffe/layers/slice_layer.cu @@ -8,12 +8,12 @@ namespace caffe { template void SliceLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->mutable_gpu_data(); if (slice_dim_ == 0) { int offset_num = 0; - for (int i = 0; i < top->size(); ++i) { - Blob* blob = (*top)[i]; + for (int i = 0; i < top.size(); ++i) { + Blob* blob = top[i]; Dtype* top_data = blob->mutable_gpu_data(); caffe_copy(blob->count(), bottom_data + bottom[0]->offset(offset_num), top_data); @@ -21,8 +21,8 @@ void SliceLayer::Forward_gpu(const vector*>& bottom, } } else if (slice_dim_ == 1) { int offset_channel = 0; - for (int i = 0; i < top->size(); ++i) { - Blob* blob = (*top)[i]; + for (int i = 0; i < top.size(); ++i) { + Blob* blob = top[i]; Dtype* top_data = blob->mutable_gpu_data(); const int num_elem = blob->channels() * blob->height() * blob->width(); for (int n = 0; n < num_; ++n) { @@ -36,16 +36,16 @@ void SliceLayer::Forward_gpu(const vector*>& bottom, template void SliceLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (!propagate_down[0]) { return; } - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); if (slice_dim_ == 0) { int offset_num = 0; for (int i = 0; i < top.size(); ++i) { Blob* blob = top[i]; const Dtype* top_diff = blob->gpu_diff(); caffe_copy(blob->count(), top_diff, - bottom_diff + (*bottom)[0]->offset(offset_num)); + bottom_diff + bottom[0]->offset(offset_num)); offset_num += blob->num(); } } else if (slice_dim_ == 1) { @@ -56,13 +56,13 @@ void SliceLayer::Backward_gpu(const vector*>& top, const int num_elem = blob->channels() * blob->height() * blob->width(); for (int n = 0; n < num_; ++n) { caffe_copy(num_elem, top_diff + blob->offset(n), - bottom_diff + (*bottom)[0]->offset(n, offset_channel)); + bottom_diff + bottom[0]->offset(n, offset_channel)); } offset_channel += blob->channels(); } } // slice_dim_ is guaranteed to be 0 or 1 by SetUp. } -INSTANTIATE_CLASS(SliceLayer); +INSTANTIATE_LAYER_GPU_FUNCS(SliceLayer); } // namespace caffe diff --git a/src/caffe/layers/softmax_layer.cpp b/src/caffe/layers/softmax_layer.cpp index 60668a3f8ce..25142fdec53 100644 --- a/src/caffe/layers/softmax_layer.cpp +++ b/src/caffe/layers/softmax_layer.cpp @@ -9,8 +9,8 @@ namespace caffe { template void SoftmaxLayer::Reshape(const vector*>& bottom, - vector*>* top) { - (*top)[0]->Reshape(bottom[0]->num(), bottom[0]->channels(), + const vector*>& top) { + top[0]->Reshape(bottom[0]->num(), bottom[0]->channels(), bottom[0]->height(), bottom[0]->width()); sum_multiplier_.Reshape(1, bottom[0]->channels(), 1, 1); Dtype* multiplier_data = sum_multiplier_.mutable_cpu_data(); @@ -22,9 +22,9 @@ void SoftmaxLayer::Reshape(const vector*>& bottom, template void SoftmaxLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); Dtype* scale_data = scale_.mutable_cpu_data(); int num = bottom[0]->num(); int channels = bottom[0]->channels(); @@ -52,8 +52,8 @@ void SoftmaxLayer::Forward_cpu(const vector*>& bottom, top_data + i * dim, sum_multiplier_.cpu_data(), 0., scale_data); // division for (int j = 0; j < channels; j++) { - caffe_div(spatial_dim, top_data + (*top)[0]->offset(i, j), scale_data, - top_data + (*top)[0]->offset(i, j)); + caffe_div(spatial_dim, top_data + top[0]->offset(i, j), scale_data, + top_data + top[0]->offset(i, j)); } } } @@ -61,10 +61,10 @@ void SoftmaxLayer::Forward_cpu(const vector*>& bottom, template void SoftmaxLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { const Dtype* top_diff = top[0]->cpu_diff(); const Dtype* top_data = top[0]->cpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); Dtype* scale_data = scale_.mutable_cpu_data(); int num = top[0]->num(); int channels = top[0]->channels(); @@ -93,5 +93,4 @@ STUB_GPU(SoftmaxLayer); INSTANTIATE_CLASS(SoftmaxLayer); - } // namespace caffe diff --git a/src/caffe/layers/softmax_layer.cu b/src/caffe/layers/softmax_layer.cu index f97eafcedb0..6b8871a0b20 100644 --- a/src/caffe/layers/softmax_layer.cu +++ b/src/caffe/layers/softmax_layer.cu @@ -25,14 +25,13 @@ __global__ void kernel_channel_max(const int num, const int channels, } template -__global__ void kernel_channel_subtract(const int num, const int channels, - const int spatial_dim, Dtype* data, const Dtype* channel_max) { - CUDA_KERNEL_LOOP(index, num * spatial_dim) { - int n = index / spatial_dim; +__global__ void kernel_channel_subtract(const int count, + const int num, const int channels, + const int spatial_dim, const Dtype* channel_max, Dtype* data) { + CUDA_KERNEL_LOOP(index, count) { + int n = index / channels / spatial_dim; int s = index % spatial_dim; - for (int c = 0; c < channels; ++c) { - data[(n * channels + c) * spatial_dim + s] -= channel_max[index]; - } + data[index] -= channel_max[n * spatial_dim + s]; } } @@ -58,14 +57,13 @@ __global__ void kernel_channel_sum(const int num, const int channels, } template -__global__ void kernel_channel_div(const int num, const int channels, - const int spatial_dim, Dtype* data, const Dtype* channel_sum) { - CUDA_KERNEL_LOOP(index, num * spatial_dim) { - int n = index / spatial_dim; +__global__ void kernel_channel_div(const int count, + const int num, const int channels, + const int spatial_dim, const Dtype* channel_sum, Dtype* data) { + CUDA_KERNEL_LOOP(index, count) { + int n = index / channels / spatial_dim; int s = index % spatial_dim; - for (int c = 0; c < channels; ++c) { - data[(n * channels + c) * spatial_dim + s] /= channel_sum[index]; - } + data[index] /= channel_sum[n * spatial_dim + s]; } } @@ -87,14 +85,15 @@ __global__ void kernel_channel_dot(const int num, const int channels, template void SoftmaxLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); Dtype* scale_data = scale_.mutable_gpu_data(); + int count = bottom[0]->count(); int num = bottom[0]->num(); int channels = bottom[0]->channels(); int spatial_dim = bottom[0]->height() * bottom[0]->width(); - caffe_copy(bottom[0]->count(), bottom_data, top_data); + caffe_copy(count, bottom_data, top_data); // We need to subtract the max to avoid numerical issues, compute the exp, // and then normalize. // compute max @@ -104,9 +103,9 @@ void SoftmaxLayer::Forward_gpu(const vector*>& bottom, scale_data); // subtract // NOLINT_NEXT_LINE(whitespace/operators) - kernel_channel_subtract<<>>(num, channels, spatial_dim, top_data, - scale_data); + kernel_channel_subtract<<>>(count, num, channels, spatial_dim, + scale_data, top_data); // exponentiate // NOLINT_NEXT_LINE(whitespace/operators) kernel_exp<<::Forward_gpu(const vector*>& bottom, scale_data); // divide // NOLINT_NEXT_LINE(whitespace/operators) - kernel_channel_div<<>>(num, channels, spatial_dim, top_data, - scale_data); + kernel_channel_div<<>>(count, num, channels, spatial_dim, + scale_data, top_data); } template void SoftmaxLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { const Dtype* top_diff = top[0]->gpu_diff(); const Dtype* top_data = top[0]->gpu_data(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); Dtype* scale_data = scale_.mutable_gpu_data(); + int count = top[0]->count(); int num = top[0]->num(); int channels = top[0]->channels(); int spatial_dim = top[0]->height() * top[0]->width(); @@ -141,14 +141,14 @@ void SoftmaxLayer::Backward_gpu(const vector*>& top, CAFFE_CUDA_NUM_THREADS>>>(num, channels, spatial_dim, top_diff, top_data, scale_data); // NOLINT_NEXT_LINE(whitespace/operators) - kernel_channel_subtract<<>>(num, channels, spatial_dim, bottom_diff, - scale_data); + kernel_channel_subtract<<>>(count, num, channels, spatial_dim, + scale_data, bottom_diff); // elementwise multiplication caffe_gpu_mul(top[0]->count(), bottom_diff, top_data, bottom_diff); } -INSTANTIATE_CLASS(SoftmaxLayer); +INSTANTIATE_LAYER_GPU_FUNCS(SoftmaxLayer); } // namespace caffe diff --git a/src/caffe/layers/softmax_loss_layer.cpp b/src/caffe/layers/softmax_loss_layer.cpp index 55392c37ca0..0c9ba2c6626 100644 --- a/src/caffe/layers/softmax_loss_layer.cpp +++ b/src/caffe/layers/softmax_loss_layer.cpp @@ -3,6 +3,7 @@ #include #include "caffe/layer.hpp" +#include "caffe/layer_factory.hpp" #include "caffe/util/math_functions.hpp" #include "caffe/vision_layers.hpp" @@ -10,84 +11,115 @@ namespace caffe { template void SoftmaxWithLossLayer::LayerSetUp( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::LayerSetUp(bottom, top); + LayerParameter softmax_param(this->layer_param_); + softmax_param.set_type("Softmax"); + softmax_layer_ = LayerRegistry::CreateLayer(softmax_param); softmax_bottom_vec_.clear(); softmax_bottom_vec_.push_back(bottom[0]); softmax_top_vec_.clear(); softmax_top_vec_.push_back(&prob_); - softmax_layer_->SetUp(softmax_bottom_vec_, &softmax_top_vec_); + softmax_layer_->SetUp(softmax_bottom_vec_, softmax_top_vec_); + + has_ignore_label_ = + this->layer_param_.loss_param().has_ignore_label(); + if (has_ignore_label_) { + ignore_label_ = this->layer_param_.loss_param().ignore_label(); + } + normalize_ = this->layer_param_.loss_param().normalize(); } template void SoftmaxWithLossLayer::Reshape( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { LossLayer::Reshape(bottom, top); - softmax_layer_->Reshape(softmax_bottom_vec_, &softmax_top_vec_); - if (top->size() >= 2) { + softmax_layer_->Reshape(softmax_bottom_vec_, softmax_top_vec_); + if (top.size() >= 2) { // softmax output - (*top)[1]->ReshapeLike(*bottom[0]); + top[1]->ReshapeLike(*bottom[0]); } } template void SoftmaxWithLossLayer::Forward_cpu( - const vector*>& bottom, vector*>* top) { + const vector*>& bottom, const vector*>& top) { // The forward pass computes the softmax prob values. - softmax_layer_->Forward(softmax_bottom_vec_, &softmax_top_vec_); + softmax_layer_->Forward(softmax_bottom_vec_, softmax_top_vec_); const Dtype* prob_data = prob_.cpu_data(); const Dtype* label = bottom[1]->cpu_data(); int num = prob_.num(); int dim = prob_.count() / num; int spatial_dim = prob_.height() * prob_.width(); + int count = 0; Dtype loss = 0; for (int i = 0; i < num; ++i) { for (int j = 0; j < spatial_dim; j++) { - loss -= log(std::max(prob_data[i * dim + - static_cast(label[i * spatial_dim + j]) * spatial_dim + j], + const int label_value = static_cast(label[i * spatial_dim + j]); + if (has_ignore_label_ && label_value == ignore_label_) { + continue; + } + DCHECK_GE(label_value, 0); + DCHECK_LT(label_value, prob_.channels()); + loss -= log(std::max(prob_data[i * dim + label_value * spatial_dim + j], Dtype(FLT_MIN))); + ++count; } } - (*top)[0]->mutable_cpu_data()[0] = loss / num / spatial_dim; - if (top->size() == 2) { - (*top)[1]->ShareData(prob_); + if (normalize_) { + top[0]->mutable_cpu_data()[0] = loss / count; + } else { + top[0]->mutable_cpu_data()[0] = loss / num; + } + if (top.size() == 2) { + top[1]->ShareData(prob_); } } template void SoftmaxWithLossLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, - vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (propagate_down[1]) { - LOG(FATAL) << this->type_name() + LOG(FATAL) << this->type() << " Layer cannot backpropagate to label inputs."; } if (propagate_down[0]) { - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); const Dtype* prob_data = prob_.cpu_data(); caffe_copy(prob_.count(), prob_data, bottom_diff); - const Dtype* label = (*bottom)[1]->cpu_data(); + const Dtype* label = bottom[1]->cpu_data(); int num = prob_.num(); int dim = prob_.count() / num; int spatial_dim = prob_.height() * prob_.width(); + int count = 0; for (int i = 0; i < num; ++i) { for (int j = 0; j < spatial_dim; ++j) { - bottom_diff[i * dim + static_cast(label[i * spatial_dim + j]) - * spatial_dim + j] -= 1; + const int label_value = static_cast(label[i * spatial_dim + j]); + if (has_ignore_label_ && label_value == ignore_label_) { + for (int c = 0; c < bottom[0]->channels(); ++c) { + bottom_diff[i * dim + c * spatial_dim + j] = 0; + } + } else { + bottom_diff[i * dim + label_value * spatial_dim + j] -= 1; + ++count; + } } } // Scale gradient const Dtype loss_weight = top[0]->cpu_diff()[0]; - caffe_scal(prob_.count(), loss_weight / num / spatial_dim, bottom_diff); + if (normalize_) { + caffe_scal(prob_.count(), loss_weight / count, bottom_diff); + } else { + caffe_scal(prob_.count(), loss_weight / num, bottom_diff); + } } } - #ifdef CPU_ONLY STUB_GPU(SoftmaxWithLossLayer); #endif INSTANTIATE_CLASS(SoftmaxWithLossLayer); - +REGISTER_LAYER_CLASS(SoftmaxWithLoss); } // namespace caffe diff --git a/src/caffe/layers/softmax_loss_layer.cu b/src/caffe/layers/softmax_loss_layer.cu index 9ef8dd23615..215d589ffee 100644 --- a/src/caffe/layers/softmax_loss_layer.cu +++ b/src/caffe/layers/softmax_loss_layer.cu @@ -8,20 +8,122 @@ namespace caffe { +template +__global__ void SoftmaxLossForwardGPU(const int nthreads, + const Dtype* prob_data, const Dtype* label, Dtype* loss, + const int num, const int dim, const int spatial_dim, + const bool has_ignore_label_, const int ignore_label_, + Dtype* counts) { + CUDA_KERNEL_LOOP(index, nthreads) { + const int n = index / spatial_dim; + const int s = index % spatial_dim; + const int label_value = static_cast(label[n * spatial_dim + s]); + if (has_ignore_label_ && label_value == ignore_label_) { + loss[index] = 0; + counts[index] = 0; + } else { + loss[index] = -log(max(prob_data[n * dim + label_value * spatial_dim + s], + Dtype(FLT_MIN))); + counts[index] = 1; + } + } +} + template void SoftmaxWithLossLayer::Forward_gpu( - const vector*>& bottom, vector*>* top) { - Forward_cpu(bottom, top); + const vector*>& bottom, const vector*>& top) { + softmax_layer_->Forward(softmax_bottom_vec_, softmax_top_vec_); + const Dtype* prob_data = prob_.gpu_data(); + const Dtype* label = bottom[1]->gpu_data(); + const int num = prob_.num(); + const int dim = prob_.count() / num; + const int spatial_dim = prob_.height() * prob_.width(); + const int nthreads = num * spatial_dim; + // Since this memory is not used for anything until it is overwritten + // on the backward pass, we use it here to avoid having to allocate new GPU + // memory to accumulate intermediate results in the kernel. + Dtype* loss_data = bottom[0]->mutable_gpu_diff(); + // Similarly, this memory is never used elsewhere, and thus we can use it + // to avoid having to allocate additional GPU memory. + Dtype* counts = prob_.mutable_gpu_diff(); + // NOLINT_NEXT_LINE(whitespace/operators) + SoftmaxLossForwardGPU<<>>(nthreads, prob_data, label, loss_data, + num, dim, spatial_dim, has_ignore_label_, ignore_label_, counts); + Dtype loss; + caffe_gpu_asum(nthreads, loss_data, &loss); + if (normalize_) { + Dtype count; + caffe_gpu_asum(nthreads, counts, &count); + loss /= count; + } else { + loss /= num; + } + top[0]->mutable_cpu_data()[0] = loss; + if (top.size() == 2) { + top[1]->ShareData(prob_); + } } template -void SoftmaxWithLossLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { - // TODO(Yangqing): implement the GPU version of softmax. - Backward_cpu(top, propagate_down, bottom); +__global__ void SoftmaxLossBackwardGPU(const int nthreads, const Dtype* top, + const Dtype* label, Dtype* bottom_diff, const int num, const int dim, + const int spatial_dim, const bool has_ignore_label_, + const int ignore_label_, Dtype* counts) { + const int channels = dim / spatial_dim; + + CUDA_KERNEL_LOOP(index, nthreads) { + const int n = index / spatial_dim; + const int s = index % spatial_dim; + const int label_value = static_cast(label[n * spatial_dim + s]); + + if (has_ignore_label_ && label_value == ignore_label_) { + for (int c = 0; c < channels; ++c) { + bottom_diff[n * dim + c * spatial_dim + s] = 0; + } + counts[index] = 0; + } else { + bottom_diff[n * dim + label_value * spatial_dim + s] -= 1; + counts[index] = 1; + } + } } -INSTANTIATE_CLASS(SoftmaxWithLossLayer); +template +void SoftmaxWithLossLayer::Backward_gpu(const vector*>& top, + const vector& propagate_down, const vector*>& bottom) { + if (propagate_down[1]) { + LOG(FATAL) << this->type() + << " Layer cannot backpropagate to label inputs."; + } + if (propagate_down[0]) { + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + const Dtype* prob_data = prob_.gpu_data(); + const Dtype* top_data = top[0]->gpu_data(); + caffe_gpu_memcpy(prob_.count() * sizeof(Dtype), prob_data, bottom_diff); + const Dtype* label = bottom[1]->gpu_data(); + const int num = prob_.num(); + const int dim = prob_.count() / num; + const int spatial_dim = prob_.height() * prob_.width(); + const int nthreads = num * spatial_dim; + // Since this memory is never used for anything else, + // we use to to avoid allocating new GPU memory. + Dtype* counts = prob_.mutable_gpu_diff(); + // NOLINT_NEXT_LINE(whitespace/operators) + SoftmaxLossBackwardGPU<<>>(nthreads, top_data, label, bottom_diff, + num, dim, spatial_dim, has_ignore_label_, ignore_label_, counts); + const Dtype loss_weight = top[0]->cpu_diff()[0]; + if (normalize_) { + Dtype count; + caffe_gpu_asum(nthreads, counts, &count); + caffe_gpu_scal(prob_.count(), loss_weight / count, bottom_diff); + } else { + caffe_gpu_scal(prob_.count(), loss_weight / num, bottom_diff); + } + } +} +INSTANTIATE_LAYER_GPU_FUNCS(SoftmaxWithLossLayer); } // namespace caffe diff --git a/src/caffe/layers/split_layer.cpp b/src/caffe/layers/split_layer.cpp index 40d3600ff17..d6929b99683 100644 --- a/src/caffe/layers/split_layer.cpp +++ b/src/caffe/layers/split_layer.cpp @@ -8,44 +8,44 @@ namespace caffe { template void SplitLayer::Reshape(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { count_ = bottom[0]->count(); - for (int i = 0; i < top->size(); ++i) { + for (int i = 0; i < top.size(); ++i) { // Do not allow in-place computation in the SplitLayer. Instead, share data // by reference in the forward pass, and keep separate diff allocations in // the backward pass. (Technically, it should be possible to share the diff // blob of the first split output with the input, but this seems to cause // some strange effects in practice...) - CHECK_NE((*top)[i], bottom[0]) << this->type_name() << " Layer does not " + CHECK_NE(top[i], bottom[0]) << this->type() << " Layer does not " "allow in-place computation."; - (*top)[i]->Reshape(bottom[0]->num(), bottom[0]->channels(), + top[i]->Reshape(bottom[0]->num(), bottom[0]->channels(), bottom[0]->height(), bottom[0]->width()); - CHECK_EQ(count_, (*top)[i]->count()); + CHECK_EQ(count_, top[i]->count()); } } template void SplitLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { - for (int i = 0; i < top->size(); ++i) { - (*top)[i]->ShareData(*bottom[0]); + const vector*>& top) { + for (int i = 0; i < top.size(); ++i) { + top[i]->ShareData(*bottom[0]); } } template void SplitLayer::Backward_cpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (!propagate_down[0]) { return; } if (top.size() == 1) { - caffe_copy(count_, top[0]->cpu_diff(), (*bottom)[0]->mutable_cpu_diff()); + caffe_copy(count_, top[0]->cpu_diff(), bottom[0]->mutable_cpu_diff()); return; } caffe_add(count_, top[0]->cpu_diff(), top[1]->cpu_diff(), - (*bottom)[0]->mutable_cpu_diff()); + bottom[0]->mutable_cpu_diff()); // Add remaining top blob diffs. for (int i = 2; i < top.size(); ++i) { const Dtype* top_diff = top[i]->cpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); caffe_axpy(count_, Dtype(1.), top_diff, bottom_diff); } } @@ -56,5 +56,6 @@ STUB_GPU(SplitLayer); #endif INSTANTIATE_CLASS(SplitLayer); +REGISTER_LAYER_CLASS(Split); } // namespace caffe diff --git a/src/caffe/layers/split_layer.cu b/src/caffe/layers/split_layer.cu index fcc0917e67d..a4f5df26452 100644 --- a/src/caffe/layers/split_layer.cu +++ b/src/caffe/layers/split_layer.cu @@ -8,31 +8,31 @@ namespace caffe { template void SplitLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { - for (int i = 0; i < top->size(); ++i) { - (*top)[i]->ShareData(*bottom[0]); + const vector*>& top) { + for (int i = 0; i < top.size(); ++i) { + top[i]->ShareData(*bottom[0]); } } template void SplitLayer::Backward_gpu(const vector*>& top, - const vector& propagate_down, vector*>* bottom) { + const vector& propagate_down, const vector*>& bottom) { if (!propagate_down[0]) { return; } if (top.size() == 1) { - caffe_copy(count_, top[0]->gpu_diff(), (*bottom)[0]->mutable_gpu_diff()); + caffe_copy(count_, top[0]->gpu_diff(), bottom[0]->mutable_gpu_diff()); return; } caffe_gpu_add(count_, top[0]->gpu_diff(), top[1]->gpu_diff(), - (*bottom)[0]->mutable_gpu_diff()); + bottom[0]->mutable_gpu_diff()); // Add remaining top blob diffs. for (int i = 2; i < top.size(); ++i) { const Dtype* top_diff = top[i]->gpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); caffe_gpu_axpy(count_, Dtype(1.), top_diff, bottom_diff); } } -INSTANTIATE_CLASS(SplitLayer); +INSTANTIATE_LAYER_GPU_FUNCS(SplitLayer); } // namespace caffe diff --git a/src/caffe/layers/tanh_layer.cpp b/src/caffe/layers/tanh_layer.cpp index 8dae0054aed..ee5ed773c74 100644 --- a/src/caffe/layers/tanh_layer.cpp +++ b/src/caffe/layers/tanh_layer.cpp @@ -11,26 +11,24 @@ namespace caffe { template void TanHLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); - Dtype exp2x; + Dtype* top_data = top[0]->mutable_cpu_data(); const int count = bottom[0]->count(); for (int i = 0; i < count; ++i) { - exp2x = exp(2 * bottom_data[i]); - top_data[i] = (exp2x - Dtype(1)) / (exp2x + Dtype(1)); + top_data[i] = tanh(bottom_data[i]); } } template void TanHLayer::Backward_cpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { const Dtype* top_data = top[0]->cpu_data(); const Dtype* top_diff = top[0]->cpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); + const int count = bottom[0]->count(); Dtype tanhx; for (int i = 0; i < count; ++i) { tanhx = top_data[i]; diff --git a/src/caffe/layers/tanh_layer.cu b/src/caffe/layers/tanh_layer.cu index bdb7a94978e..ccd6e63ee7c 100644 --- a/src/caffe/layers/tanh_layer.cu +++ b/src/caffe/layers/tanh_layer.cu @@ -12,16 +12,15 @@ namespace caffe { template __global__ void TanHForward(const int n, const Dtype* in, Dtype* out) { CUDA_KERNEL_LOOP(index, n) { - Dtype exp2x = exp(2 * in[index]); - out[index] = (exp2x - Dtype(1)) / (exp2x + Dtype(1)); + out[index] = tanh(in[index]); } } template void TanHLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); const int count = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) TanHForward<<>>( @@ -41,12 +40,12 @@ __global__ void TanHBackward(const int n, const Dtype* in_diff, template void TanHLayer::Backward_gpu(const vector*>& top, const vector& propagate_down, - vector*>* bottom) { + const vector*>& bottom) { if (propagate_down[0]) { const Dtype* top_data = top[0]->gpu_data(); const Dtype* top_diff = top[0]->gpu_diff(); - Dtype* bottom_diff = (*bottom)[0]->mutable_gpu_diff(); - const int count = (*bottom)[0]->count(); + Dtype* bottom_diff = bottom[0]->mutable_gpu_diff(); + const int count = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) TanHBackward<<>>( count, top_diff, top_data, bottom_diff); @@ -54,7 +53,7 @@ void TanHLayer::Backward_gpu(const vector*>& top, } } -INSTANTIATE_CLASS(TanHLayer); +INSTANTIATE_LAYER_GPU_FUNCS(TanHLayer); } // namespace caffe diff --git a/src/caffe/layers/threshold_layer.cpp b/src/caffe/layers/threshold_layer.cpp index 180ea6a322b..2365e7b9c72 100644 --- a/src/caffe/layers/threshold_layer.cpp +++ b/src/caffe/layers/threshold_layer.cpp @@ -8,16 +8,16 @@ namespace caffe { template void ThresholdLayer::LayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { NeuronLayer::LayerSetUp(bottom, top); threshold_ = this->layer_param_.threshold_param().threshold(); } template void ThresholdLayer::Forward_cpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); - Dtype* top_data = (*top)[0]->mutable_cpu_data(); + Dtype* top_data = top[0]->mutable_cpu_data(); const int count = bottom[0]->count(); for (int i = 0; i < count; ++i) { top_data[i] = (bottom_data[i] > threshold_) ? Dtype(1) : Dtype(0); @@ -29,5 +29,6 @@ STUB_GPU_FORWARD(ThresholdLayer, Forward); #endif INSTANTIATE_CLASS(ThresholdLayer); +REGISTER_LAYER_CLASS(Threshold); } // namespace caffe diff --git a/src/caffe/layers/threshold_layer.cu b/src/caffe/layers/threshold_layer.cu index 93430815900..bfa7f159460 100644 --- a/src/caffe/layers/threshold_layer.cu +++ b/src/caffe/layers/threshold_layer.cu @@ -16,9 +16,9 @@ __global__ void ThresholdForward(const int n, const Dtype threshold, template void ThresholdLayer::Forward_gpu(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { const Dtype* bottom_data = bottom[0]->gpu_data(); - Dtype* top_data = (*top)[0]->mutable_gpu_data(); + Dtype* top_data = top[0]->mutable_gpu_data(); const int count = bottom[0]->count(); // NOLINT_NEXT_LINE(whitespace/operators) ThresholdForward<<>>( @@ -27,7 +27,7 @@ void ThresholdLayer::Forward_gpu(const vector*>& bottom, } -INSTANTIATE_CLASS(ThresholdLayer); +INSTANTIATE_LAYER_GPU_FORWARD(ThresholdLayer); } // namespace caffe diff --git a/src/caffe/layers/window_data_layer.cpp b/src/caffe/layers/window_data_layer.cpp index afb430468cc..36e41560327 100644 --- a/src/caffe/layers/window_data_layer.cpp +++ b/src/caffe/layers/window_data_layer.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -13,6 +14,7 @@ #include "caffe/common.hpp" #include "caffe/data_layers.hpp" #include "caffe/layer.hpp" +#include "caffe/util/benchmark.hpp" #include "caffe/util/io.hpp" #include "caffe/util/math_functions.hpp" #include "caffe/util/rng.hpp" @@ -30,7 +32,7 @@ WindowDataLayer::~WindowDataLayer() { template void WindowDataLayer::DataLayerSetUp(const vector*>& bottom, - vector*>* top) { + const vector*>& top) { // LayerSetUp runs through the window_file and creates two structures // that hold windows: one for foreground (object) windows and one // for background (non-object) windows. We use an overlap threshold @@ -52,7 +54,14 @@ void WindowDataLayer::DataLayerSetUp(const vector*>& bottom, << " background (non-object) overlap threshold: " << this->layer_param_.window_data_param().bg_threshold() << std::endl << " foreground sampling fraction: " - << this->layer_param_.window_data_param().fg_fraction(); + << this->layer_param_.window_data_param().fg_fraction() << std::endl + << " cache_images: " + << this->layer_param_.window_data_param().cache_images() << std::endl + << " root_folder: " + << this->layer_param_.window_data_param().root_folder(); + + cache_images_ = this->layer_param_.window_data_param().cache_images(); + string root_folder = this->layer_param_.window_data_param().root_folder(); const bool prefetch_needs_rand = this->transform_param_.mirror() || @@ -81,12 +90,21 @@ void WindowDataLayer::DataLayerSetUp(const vector*>& bottom, // read image path string image_path; infile >> image_path; + image_path = root_folder + image_path; // read image dimensions vector image_size(3); infile >> image_size[0] >> image_size[1] >> image_size[2]; channels = image_size[0]; image_database_.push_back(std::make_pair(image_path, image_size)); + if (cache_images_) { + Datum datum; + if (!ReadFileToDatum(image_path, &datum)) { + LOG(ERROR) << "Could not open or find file " << image_path; + return; + } + image_database_cache_.push_back(std::make_pair(image_path, datum)); + } // read each box int num_windows; infile >> num_windows; @@ -152,21 +170,42 @@ void WindowDataLayer::DataLayerSetUp(const vector*>& bottom, const int crop_size = this->transform_param_.crop_size(); CHECK_GT(crop_size, 0); const int batch_size = this->layer_param_.window_data_param().batch_size(); - (*top)[0]->Reshape(batch_size, channels, crop_size, crop_size); + top[0]->Reshape(batch_size, channels, crop_size, crop_size); this->prefetch_data_.Reshape(batch_size, channels, crop_size, crop_size); - LOG(INFO) << "output data size: " << (*top)[0]->num() << "," - << (*top)[0]->channels() << "," << (*top)[0]->height() << "," - << (*top)[0]->width(); - // datum size - this->datum_channels_ = (*top)[0]->channels(); - this->datum_height_ = (*top)[0]->height(); - this->datum_width_ = (*top)[0]->width(); - this->datum_size_ = - (*top)[0]->channels() * (*top)[0]->height() * (*top)[0]->width(); + LOG(INFO) << "output data size: " << top[0]->num() << "," + << top[0]->channels() << "," << top[0]->height() << "," + << top[0]->width(); // label - (*top)[1]->Reshape(batch_size, 1, 1, 1); + top[1]->Reshape(batch_size, 1, 1, 1); this->prefetch_label_.Reshape(batch_size, 1, 1, 1); + + // data mean + has_mean_file_ = this->transform_param_.has_mean_file(); + has_mean_values_ = this->transform_param_.mean_value_size() > 0; + if (has_mean_file_) { + const string& mean_file = + this->transform_param_.mean_file(); + LOG(INFO) << "Loading mean file from: " << mean_file; + BlobProto blob_proto; + ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto); + data_mean_.FromProto(blob_proto); + } + if (has_mean_values_) { + CHECK(has_mean_file_ == false) << + "Cannot specify mean_file and mean_value at the same time"; + for (int c = 0; c < this->transform_param_.mean_value_size(); ++c) { + mean_values_.push_back(this->transform_param_.mean_value(c)); + } + CHECK(mean_values_.size() == 1 || mean_values_.size() == channels) << + "Specify either 1 mean_value or as many as channels: " << channels; + if (channels > 1 && mean_values_.size() == 1) { + // Replicate the mean_value for simplicity + for (int c = 1; c < channels; ++c) { + mean_values_.push_back(mean_values_[0]); + } + } + } } template @@ -182,7 +221,11 @@ template void WindowDataLayer::InternalThreadEntry() { // At each iteration, sample N windows where N*p are foreground (object) // windows and N*(1-p) are background (non-object) windows - + CPUTimer batch_timer; + batch_timer.Start(); + double read_time = 0; + double trans_time = 0; + CPUTimer timer; Dtype* top_data = this->prefetch_data_.mutable_cpu_data(); Dtype* top_label = this->prefetch_label_.mutable_cpu_data(); const Dtype scale = this->layer_param_.window_data_param().scale(); @@ -192,10 +235,16 @@ void WindowDataLayer::InternalThreadEntry() { const bool mirror = this->transform_param_.mirror(); const float fg_fraction = this->layer_param_.window_data_param().fg_fraction(); - const Dtype* mean = this->data_mean_.cpu_data(); - const int mean_off = (this->data_mean_.width() - crop_size) / 2; - const int mean_width = this->data_mean_.width(); - const int mean_height = this->data_mean_.height(); + Dtype* mean = NULL; + int mean_off = 0; + int mean_width = 0; + int mean_height = 0; + if (this->has_mean_file_) { + mean = this->data_mean_.mutable_cpu_data(); + mean_off = (this->data_mean_.width() - crop_size) / 2; + mean_width = this->data_mean_.width(); + mean_height = this->data_mean_.height(); + } cv::Size cv_crop_size(crop_size, crop_size); const string& crop_mode = this->layer_param_.window_data_param().crop_mode(); @@ -213,25 +262,32 @@ void WindowDataLayer::InternalThreadEntry() { for (int is_fg = 0; is_fg < 2; ++is_fg) { for (int dummy = 0; dummy < num_samples[is_fg]; ++dummy) { // sample a window + timer.Start(); const unsigned int rand_index = PrefetchRand(); vector window = (is_fg) ? fg_windows_[rand_index % fg_windows_.size()] : bg_windows_[rand_index % bg_windows_.size()]; - bool do_mirror = false; - if (mirror && PrefetchRand() % 2) { - do_mirror = true; - } + bool do_mirror = mirror && PrefetchRand() % 2; // load the image containing the window pair > image = image_database_[window[WindowDataLayer::IMAGE_INDEX]]; - cv::Mat cv_img = cv::imread(image.first, CV_LOAD_IMAGE_COLOR); - if (!cv_img.data) { - LOG(ERROR) << "Could not open or find file " << image.first; - return; + cv::Mat cv_img; + if (this->cache_images_) { + pair image_cached = + image_database_cache_[window[WindowDataLayer::IMAGE_INDEX]]; + cv_img = DecodeDatumToCVMat(image_cached.second, true); + } else { + cv_img = cv::imread(image.first, CV_LOAD_IMAGE_COLOR); + if (!cv_img.data) { + LOG(ERROR) << "Could not open or find file " << image.first; + return; + } } + read_time += timer.MicroSeconds(); + timer.Start(); const int channels = cv_img.channels(); // crop window out of image and warp it @@ -334,22 +390,30 @@ void WindowDataLayer::InternalThreadEntry() { } // copy the warped window into top_data - for (int c = 0; c < channels; ++c) { - for (int h = 0; h < cv_cropped_img.rows; ++h) { - for (int w = 0; w < cv_cropped_img.cols; ++w) { - Dtype pixel = - static_cast(cv_cropped_img.at(h, w)[c]); - - top_data[((item_id * channels + c) * crop_size + h + pad_h) - * crop_size + w + pad_w] - = (pixel - - mean[(c * mean_height + h + mean_off + pad_h) - * mean_width + w + mean_off + pad_w]) - * scale; + for (int h = 0; h < cv_cropped_img.rows; ++h) { + const uchar* ptr = cv_cropped_img.ptr(h); + int img_index = 0; + for (int w = 0; w < cv_cropped_img.cols; ++w) { + for (int c = 0; c < channels; ++c) { + int top_index = ((item_id * channels + c) * crop_size + h + pad_h) + * crop_size + w + pad_w; + // int top_index = (c * height + h) * width + w; + Dtype pixel = static_cast(ptr[img_index++]); + if (this->has_mean_file_) { + int mean_index = (c * mean_height + h + mean_off + pad_h) + * mean_width + w + mean_off + pad_w; + top_data[top_index] = (pixel - mean[mean_index]) * scale; + } else { + if (this->has_mean_values_) { + top_data[top_index] = (pixel - this->mean_values_[c]) * scale; + } else { + top_data[top_index] = pixel * scale; + } + } } } } - + trans_time += timer.MicroSeconds(); // get window label top_label[item_id] = window[WindowDataLayer::LABEL]; @@ -389,8 +453,13 @@ void WindowDataLayer::InternalThreadEntry() { item_id++; } } + batch_timer.Stop(); + DLOG(INFO) << "Prefetch batch: " << batch_timer.MilliSeconds() << " ms."; + DLOG(INFO) << " Read time: " << read_time / 1000 << " ms."; + DLOG(INFO) << "Transform time: " << trans_time / 1000 << " ms."; } INSTANTIATE_CLASS(WindowDataLayer); +REGISTER_LAYER_CLASS(WindowData); } // namespace caffe diff --git a/src/caffe/net.cpp b/src/caffe/net.cpp index 6f4a651fb10..c359be9b575 100644 --- a/src/caffe/net.cpp +++ b/src/caffe/net.cpp @@ -24,14 +24,17 @@ Net::Net(const NetParameter& param) { } template -Net::Net(const string& param_file) { +Net::Net(const string& param_file, Phase phase) { NetParameter param; ReadNetParamsFromTextFileOrDie(param_file, ¶m); + param.mutable_state()->set_phase(phase); Init(param); } template void Net::Init(const NetParameter& in_param) { + // Set phase from the state. + phase_ = in_param.state().phase(); // Filter layers based on their include/exclude rules and // the current NetState. NetParameter filtered_param; @@ -55,14 +58,20 @@ void Net::Init(const NetParameter& in_param) { } DLOG(INFO) << "Memory required for data: " << memory_used_ * sizeof(Dtype); // For each layer, set up their input and output - bottom_vecs_.resize(param.layers_size()); - top_vecs_.resize(param.layers_size()); - bottom_id_vecs_.resize(param.layers_size()); - top_id_vecs_.resize(param.layers_size()); - bottom_need_backward_.resize(param.layers_size()); - for (int layer_id = 0; layer_id < param.layers_size(); ++layer_id) { - const LayerParameter& layer_param = param.layers(layer_id); - layers_.push_back(shared_ptr >(GetLayer(layer_param))); + bottom_vecs_.resize(param.layer_size()); + top_vecs_.resize(param.layer_size()); + bottom_id_vecs_.resize(param.layer_size()); + param_id_vecs_.resize(param.layer_size()); + top_id_vecs_.resize(param.layer_size()); + bottom_need_backward_.resize(param.layer_size()); + for (int layer_id = 0; layer_id < param.layer_size(); ++layer_id) { + // Inherit phase from net if unset. + if (!param.layer(layer_id).has_phase()) { + param.mutable_layer(layer_id)->set_phase(phase_); + } + // Setup layer. + const LayerParameter& layer_param = param.layer(layer_id); + layers_.push_back(LayerRegistry::CreateLayer(layer_param)); layer_names_.push_back(layer_param.name()); LOG(INFO) << "Creating Layer " << layer_param.name(); bool need_backward = false; @@ -94,7 +103,7 @@ void Net::Init(const NetParameter& in_param) { } // After this layer is connected, set it up. LOG(INFO) << "Setting up " << layer_names_[layer_id]; - layers_[layer_id]->SetUp(bottom_vecs_[layer_id], &top_vecs_[layer_id]); + layers_[layer_id]->SetUp(bottom_vecs_[layer_id], top_vecs_[layer_id]); for (int top_id = 0; top_id < top_vecs_[layer_id].size(); ++top_id) { if (blob_loss_weights_.size() <= top_id_vecs_[layer_id][top_id]) { blob_loss_weights_.resize(top_id_vecs_[layer_id][top_id] + 1, Dtype(0)); @@ -111,35 +120,19 @@ void Net::Init(const NetParameter& in_param) { memory_used_ += top_vecs_[layer_id][top_id]->count(); } DLOG(INFO) << "Memory required for data: " << memory_used_ * sizeof(Dtype); - const int blobs_lr_size = layer_param.blobs_lr_size(); + const int param_size = layer_param.param_size(); const int num_param_blobs = layers_[layer_id]->blobs().size(); - CHECK(blobs_lr_size == num_param_blobs || blobs_lr_size == 0) - << "Incorrect blobs lr size: should be either 0 " - << "or the same as the number of the layer's parameter blobs."; - if (blobs_lr_size) { - // Check if this layer needs backward operation itself - for (int param_id = 0; param_id < blobs_lr_size; ++param_id) { - const bool param_need_backward = layer_param.blobs_lr(param_id) > 0; - need_backward |= param_need_backward; - layers_[layer_id]->set_param_propagate_down(param_id, - param_need_backward); - } - } else if (layers_[layer_id]->blobs().size()) { - // catch: if a layer param does not specify blobs_lr, we should assume the - // learning rate to be 1. Thus we will need to perform backward. - need_backward = true; - for (int param_id = 0; param_id < blobs_lr_size; ++param_id) { - layers_[layer_id]->set_param_propagate_down(param_id, true); - } + CHECK_LE(param_size, num_param_blobs) + << "Too many params specified for layer " << layer_param.name(); + ParamSpec default_param_spec; + for (int param_id = 0; param_id < num_param_blobs; ++param_id) { + const ParamSpec* param_spec = (param_id < param_size) ? + &layer_param.param(param_id) : &default_param_spec; + const bool param_need_backward = param_spec->lr_mult() > 0; + need_backward |= param_need_backward; + layers_[layer_id]->set_param_propagate_down(param_id, + param_need_backward); } - const int param_size = layer_param.param_size(); - CHECK(param_size == num_param_blobs || param_size == 0) - << "Incorrect param size: should be either 0 or the same as " - "the number of the layer's parameter blobs: " << num_param_blobs; - const int blob_share_mode_size = layer_param.blob_share_mode_size(); - CHECK(blob_share_mode_size == num_param_blobs || blob_share_mode_size == 0) - << "Incorrect blob_share_mode size: should be either 0 or the same as " - "the number of the layer's parameter blobs: " << num_param_blobs; for (int param_id = 0; param_id < num_param_blobs; ++param_id) { AppendParam(param, layer_id, param_id); } @@ -216,34 +209,19 @@ void Net::Init(const NetParameter& in_param) { layer_names_index_[layer_names_[layer_id]] = layer_id; } GetLearningRateAndWeightDecay(); + debug_info_ = param.debug_info(); LOG(INFO) << "Network initialization done."; LOG(INFO) << "Memory required for data: " << memory_used_ * sizeof(Dtype); - // Don't display debug info by default. - debug_info_ = false; } template void Net::FilterNet(const NetParameter& param, NetParameter* param_filtered) { NetState net_state(param.state()); - // Let the phase of the net be the current global phase provided in the Caffe - // singleton, unless explicitly provided by the state. - if (!net_state.has_phase()) { - switch (Caffe::phase()) { - case Caffe::TRAIN: - net_state.set_phase(TRAIN); - break; - case Caffe::TEST: - net_state.set_phase(TEST); - break; - default: - LOG(FATAL) << "Unknown phase: " << Caffe::phase(); - } - } param_filtered->CopyFrom(param); - param_filtered->clear_layers(); - for (int i = 0; i < param.layers_size(); ++i) { - const LayerParameter& layer_param = param.layers(i); + param_filtered->clear_layer(); + for (int i = 0; i < param.layer_size(); ++i) { + const LayerParameter& layer_param = param.layer(i); const string& layer_name = layer_param.name(); CHECK(layer_param.include_size() == 0 || layer_param.exclude_size() == 0) << "Specify either include rules or exclude rules; not both."; @@ -261,7 +239,7 @@ void Net::FilterNet(const NetParameter& param, } } if (layer_included) { - param_filtered->add_layers()->CopyFrom(layer_param); + param_filtered->add_layer()->CopyFrom(layer_param); } } } @@ -334,7 +312,7 @@ void Net::AppendTop(const NetParameter& param, const int layer_id, const int top_id, set* available_blobs, map* blob_name_to_idx) { shared_ptr layer_param((layer_id >= 0) ? - (new LayerParameter(param.layers(layer_id))) : NULL); + (new LayerParameter(param.layer(layer_id))) : NULL); const string& blob_name = layer_param ? (layer_param->top_size() > top_id ? layer_param->top(top_id) : "(automatic)") : param.input(top_id); @@ -384,7 +362,7 @@ template int Net::AppendBottom(const NetParameter& param, const int layer_id, const int bottom_id, set* available_blobs, map* blob_name_to_idx) { - const LayerParameter& layer_param = param.layers(layer_id); + const LayerParameter& layer_param = param.layer(layer_id); const string& blob_name = layer_param.bottom(bottom_id); if (available_blobs->find(blob_name) == available_blobs->end()) { LOG(FATAL) << "Unknown blob input " << blob_name @@ -405,7 +383,8 @@ void Net::AppendParam(const NetParameter& param, const int layer_id, const int param_id) { const LayerParameter& layer_param = layers_[layer_id]->layer_param(); const int param_size = layer_param.param_size(); - string param_name = param_size ? layer_param.param(param_id) : ""; + string param_name = + (param_size > param_id) ? layer_param.param(param_id).name() : ""; if (param_name.size()) { param_display_names_.push_back(param_name); } else { @@ -415,6 +394,7 @@ void Net::AppendParam(const NetParameter& param, const int layer_id, } const int net_param_id = params_.size(); params_.push_back(layers_[layer_id]->blobs()[param_id]); + param_id_vecs_[layer_id].push_back(net_param_id); param_layer_indices_.push_back(make_pair(layer_id, param_id)); if (!param_size || !param_name.size() || (param_name.size() && param_names_index_.find(param_name) == param_names_index_.end())) { @@ -439,10 +419,9 @@ void Net::AppendParam(const NetParameter& param, const int layer_id, Blob* this_blob = layers_[layer_id]->blobs()[param_id].get(); Blob* owner_blob = layers_[owner_layer_id]->blobs()[owner_param_id].get(); - const int blob_share_mode_size = layer_param.blob_share_mode_size(); - if (blob_share_mode_size > param_id && - (layer_param.blob_share_mode(param_id) == - LayerParameter_DimCheckMode_PERMISSIVE)) { + const int param_size = layer_param.param_size(); + if (param_size > param_id && (layer_param.param(param_id).share_mode() == + ParamSpec_DimCheckMode_PERMISSIVE)) { // Permissive dimension checking -- only check counts are the same. CHECK_EQ(this_blob->count(), owner_blob->count()) << "Shared parameter blobs must have the same count."; @@ -465,34 +444,15 @@ void Net::AppendParam(const NetParameter& param, const int layer_id, template void Net::GetLearningRateAndWeightDecay() { LOG(INFO) << "Collecting Learning Rate and Weight Decay."; + ParamSpec default_param_spec; for (int i = 0; i < layers_.size(); ++i) { vector > >& layer_blobs = layers_[i]->blobs(); - // push the learning rate mutlipliers - if (layers_[i]->layer_param().blobs_lr_size()) { - CHECK_EQ(layers_[i]->layer_param().blobs_lr_size(), layer_blobs.size()); - for (int j = 0; j < layer_blobs.size(); ++j) { - float local_lr = layers_[i]->layer_param().blobs_lr(j); - CHECK_GE(local_lr, 0.); - params_lr_.push_back(local_lr); - } - } else { - for (int j = 0; j < layer_blobs.size(); ++j) { - params_lr_.push_back(1.); - } - } - // push the weight decay multipliers - if (layers_[i]->layer_param().weight_decay_size()) { - CHECK_EQ(layers_[i]->layer_param().weight_decay_size(), - layer_blobs.size()); - for (int j = 0; j < layer_blobs.size(); ++j) { - float local_decay = layers_[i]->layer_param().weight_decay(j); - CHECK_GE(local_decay, 0.); - params_weight_decay_.push_back(local_decay); - } - } else { - for (int j = 0; j < layer_blobs.size(); ++j) { - params_weight_decay_.push_back(1.); - } + for (int j = 0; j < layer_blobs.size(); ++j) { + const ParamSpec* param_spec = + (layers_[i]->layer_param().param_size() > j) ? + &layers_[i]->layer_param().param(j) : &default_param_spec; + params_lr_.push_back(param_spec->lr_mult()); + params_weight_decay_.push_back(param_spec->decay_mult()); } } } @@ -502,10 +462,15 @@ Dtype Net::ForwardFromTo(int start, int end) { CHECK_GE(start, 0); CHECK_LT(end, layers_.size()); Dtype loss = 0; + if (debug_info_) { + for (int i = 0; i < net_input_blobs_.size(); ++i) { + InputDebugInfo(i); + } + } for (int i = start; i <= end; ++i) { // LOG(ERROR) << "Forwarding " << layer_names_[i]; - layers_[i]->Reshape(bottom_vecs_[i], &top_vecs_[i]); - Dtype layer_loss = layers_[i]->Forward(bottom_vecs_[i], &top_vecs_[i]); + layers_[i]->Reshape(bottom_vecs_[i], top_vecs_[i]); + Dtype layer_loss = layers_[i]->Forward(bottom_vecs_[i], top_vecs_[i]); loss += layer_loss; if (debug_info_) { ForwardDebugInfo(i); } } @@ -570,12 +535,21 @@ void Net::BackwardFromTo(int start, int end) { for (int i = start; i >= end; --i) { if (layer_need_backward_[i]) { layers_[i]->Backward( - top_vecs_[i], bottom_need_backward_[i], &bottom_vecs_[i]); + top_vecs_[i], bottom_need_backward_[i], bottom_vecs_[i]); if (debug_info_) { BackwardDebugInfo(i); } } } } +template +void Net::InputDebugInfo(const int input_id) { + const Blob& blob = *net_input_blobs_[input_id]; + const string& blob_name = blob_names_[net_input_blob_indices_[input_id]]; + const Dtype data_abs_val_mean = blob.asum_data() / blob.count(); + LOG(INFO) << " [Forward] " + << "Input " << blob_name << " data: " << data_abs_val_mean; +} + template void Net::ForwardDebugInfo(const int layer_id) { for (int top_id = 0; top_id < top_vecs_[layer_id].size(); ++top_id) { @@ -586,6 +560,16 @@ void Net::ForwardDebugInfo(const int layer_id) { << "Layer " << layer_names_[layer_id] << ", top blob " << blob_name << " data: " << data_abs_val_mean; } + for (int param_id = 0; param_id < layers_[layer_id]->blobs().size(); + ++param_id) { + const Blob& blob = *layers_[layer_id]->blobs()[param_id]; + const int net_param_id = param_id_vecs_[layer_id][param_id]; + const string& blob_name = param_display_names_[net_param_id]; + const Dtype data_abs_val_mean = blob.asum_data() / blob.count(); + LOG(INFO) << " [Forward] " + << "Layer " << layer_names_[layer_id] << ", param blob " << blob_name + << " data: " << data_abs_val_mean; + } } template @@ -635,7 +619,7 @@ void Net::UpdateDebugInfo(const int param_id) { } template -void Net::ShareTrainedLayersWith(Net* other) { +void Net::ShareTrainedLayersWith(const Net* other) { int num_source_layers = other->layers().size(); for (int i = 0; i < num_source_layers; ++i) { Layer* source_layer = other->layers()[i].get(); @@ -678,20 +662,35 @@ void Net::BackwardTo(int end) { template void Net::Backward() { BackwardFromTo(layers_.size() - 1, 0); + if (debug_info_) { + Dtype asum_data = 0, asum_diff = 0, sumsq_data = 0, sumsq_diff = 0; + for (int i = 0; i < params_.size(); ++i) { + if (param_owners_[i] >= 0) { continue; } + asum_data += params_[i]->asum_data(); + asum_diff += params_[i]->asum_diff(); + sumsq_data += params_[i]->sumsq_data(); + sumsq_diff += params_[i]->sumsq_diff(); + } + const Dtype l2norm_data = std::sqrt(sumsq_data); + const Dtype l2norm_diff = std::sqrt(sumsq_diff); + LOG(ERROR) << " [Backward] All net params (data, diff): " + << "L1 norm = (" << asum_data << ", " << asum_diff << "); " + << "L2 norm = (" << l2norm_data << ", " << l2norm_diff << ")"; + } } template void Net::Reshape() { for (int i = 0; i < layers_.size(); ++i) { - layers_[i]->Reshape(bottom_vecs_[i], &top_vecs_[i]); + layers_[i]->Reshape(bottom_vecs_[i], top_vecs_[i]); } } template void Net::CopyTrainedLayersFrom(const NetParameter& param) { - int num_source_layers = param.layers_size(); + int num_source_layers = param.layer_size(); for (int i = 0; i < num_source_layers; ++i) { - const LayerParameter& source_layer = param.layers(i); + const LayerParameter& source_layer = param.layer(i); const string& source_layer_name = source_layer.name(); int target_layer_id = 0; while (target_layer_id != layer_names_.size() && @@ -725,7 +724,7 @@ void Net::CopyTrainedLayersFrom(const string trained_filename) { } template -void Net::ToProto(NetParameter* param, bool write_diff) { +void Net::ToProto(NetParameter* param, bool write_diff) const { param->Clear(); param->set_name(name_); // Add bottom and top @@ -734,7 +733,7 @@ void Net::ToProto(NetParameter* param, bool write_diff) { } DLOG(INFO) << "Serializing " << layers_.size() << " layers"; for (int i = 0; i < layers_.size(); ++i) { - LayerParameter* layer_param = param->add_layers(); + LayerParameter* layer_param = param->add_layer(); for (int j = 0; j < bottom_id_vecs_[i].size(); ++j) { layer_param->add_bottom(blob_names_[bottom_id_vecs_[i][j]]); } @@ -784,16 +783,16 @@ void Net::Update() { } template -bool Net::has_blob(const string& blob_name) { +bool Net::has_blob(const string& blob_name) const { return blob_names_index_.find(blob_name) != blob_names_index_.end(); } template const shared_ptr > Net::blob_by_name( - const string& blob_name) { + const string& blob_name) const { shared_ptr > blob_ptr; if (has_blob(blob_name)) { - blob_ptr = blobs_[blob_names_index_[blob_name]]; + blob_ptr = blobs_[blob_names_index_.find(blob_name)->second]; } else { blob_ptr.reset((Blob*)(NULL)); LOG(WARNING) << "Unknown blob name " << blob_name; @@ -802,16 +801,16 @@ const shared_ptr > Net::blob_by_name( } template -bool Net::has_layer(const string& layer_name) { +bool Net::has_layer(const string& layer_name) const { return layer_names_index_.find(layer_name) != layer_names_index_.end(); } template const shared_ptr > Net::layer_by_name( - const string& layer_name) { + const string& layer_name) const { shared_ptr > layer_ptr; if (has_layer(layer_name)) { - layer_ptr = layers_[layer_names_index_[layer_name]]; + layer_ptr = layers_[layer_names_index_.find(layer_name)->second]; } else { layer_ptr.reset((Layer*)(NULL)); LOG(WARNING) << "Unknown layer name " << layer_name; diff --git a/src/caffe/proto/CMakeLists.txt b/src/caffe/proto/CMakeLists.txt deleted file mode 100644 index 186a856509b..00000000000 --- a/src/caffe/proto/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -project( Proto ) - -# Google Protocol Buffers -find_package( Protobuf REQUIRED ) - -# As of Ubuntu 14.04 protoc is no longer a part of libprotobuf-dev package and should be installed -# separately as in: sudo apt-get install protobuf-compiler -if(PROTOBUF_PROTOC_EXECUTABLE) - message(STATUS "Found PROTOBUF Compiler: ${PROTOBUF_PROTOC_EXECUTABLE}") -else() - message(FATAL_ERROR "Could not find PROTOBUF Compiler") -endif() - -include_directories(${PROTOBUF_INCLUDE_DIR}) -file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.proto") -PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles}) - -add_library(proto - ${ProtoSources} - ${ProtoHeaders} -) - -target_link_libraries(proto ${PROTOBUF_LIBRARIES}) - -# Create proto include directory -file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/include/caffe/proto) - -# Copy proto headers to include/caffe/proto/ -foreach(header ${ProtoHeaders}) - - ADD_CUSTOM_COMMAND(TARGET proto - COMMAND cmake -E copy ${header} - ${Caffe_INCLUDE_DIRS}/caffe/proto/ - DEPENDS ${header} -) - -endforeach(header) diff --git a/src/caffe/proto/caffe.proto b/src/caffe/proto/caffe.proto index 88f670fd497..84b475ce3cd 100644 --- a/src/caffe/proto/caffe.proto +++ b/src/caffe/proto/caffe.proto @@ -26,6 +26,8 @@ message Datum { optional int32 label = 5; // Optionally, the datum could also hold float data. repeated float float_data = 6; + // If true data contains an encoded image that need to be decoded + optional bool encoded = 7 [default = false]; } message FillerParameter { @@ -36,14 +38,13 @@ message FillerParameter { optional float max = 4 [default = 1]; // the max value in uniform filler optional float mean = 5 [default = 0]; // the mean value in Gaussian filler optional float std = 6 [default = 1]; // the std value in Gaussian filler - // The expected number of non-zero input weights for a given output in + // The expected number of non-zero output weights for a given input in // Gaussian filler -- the default -1 means don't perform sparsification. optional int32 sparse = 7 [default = -1]; } message NetParameter { optional string name = 1; // consider giving the network a name - repeated LayerParameter layers = 2; // a bunch of layers. // The input blobs to the network. repeated string input = 3; // The dim of the input blobs. For each input blob there should be four @@ -58,12 +59,23 @@ message NetParameter { // Some layers may be included/excluded depending on this state and the states // specified in the layers' include and exclude fields. optional NetState state = 6; + + // Print debugging information about results while running Net::Forward, + // Net::Backward, and Net::Update. + optional bool debug_info = 7 [default = false]; + + // The layers that make up the net. Each of their configurations, including + // connectivity and behavior, is specified as a LayerParameter. + repeated LayerParameter layer = 100; // ID 100 so layers are printed last. + + // DEPRECATED: use 'layer' instead. + repeated V1LayerParameter layers = 2; } // NOTE // Update the next available ID when you add a new SolverParameter field. // -// SolverParameter next available ID: 35 (last added: stepvalue) +// SolverParameter next available ID: 36 (last added: clip_gradients) message SolverParameter { ////////////////////////////////////////////////////////////////////////////// // Specifying the train and test networks @@ -113,7 +125,7 @@ message SolverParameter { // the number of iterations between displaying info. If display = 0, no info // will be displayed. optional int32 display = 6; - // Display the cost averaged over the last average_cost iterations + // Display the loss averaged over the last average_loss iterations optional int32 average_loss = 33 [default = 1]; optional int32 max_iter = 7; // the maximum number of iterations optional string lr_policy = 8; // The learning rate decay policy. @@ -128,6 +140,11 @@ message SolverParameter { optional int32 stepsize = 13; // the stepsize for learning rate policy "multistep" repeated int32 stepvalue = 34; + + // Set clip_gradients to >= 0 to clip parameter gradients to that L2 norm, + // whenever their actual L2 norm is larger. + optional float clip_gradients = 35 [default = -1]; + optional int32 snapshot = 14 [default = 0]; // The snapshot interval optional string snapshot_prefix = 15; // The prefix for the snapshot. // whether to snapshot diff in the results or not. Snapshotting diff will help @@ -201,141 +218,106 @@ message NetStateRule { repeated string not_stage = 5; } -// NOTE -// Update the next available ID when you add a new LayerParameter field. -// -// LayerParameter next available ID: 41 (last added: contrastive_loss_param) -message LayerParameter { - repeated string bottom = 2; // the name of the bottom blobs - repeated string top = 3; // the name of the top blobs - optional string name = 4; // the layer name - - // Rules controlling whether and when a layer is included in the network, - // based on the current NetState. You may specify a non-zero number of rules - // to include OR exclude, but not both. If no include or exclude rules are - // specified, the layer is always included. If the current NetState meets - // ANY (i.e., one or more) of the specified rules, the layer is - // included/excluded. - repeated NetStateRule include = 32; - repeated NetStateRule exclude = 33; - - // NOTE - // Add new LayerTypes to the enum below in lexicographical order (other than - // starting with NONE), starting with the next available ID in the comment - // line above the enum. Update the next available ID when you add a new - // LayerType. - // - // LayerType next available ID: 38 (last added: CONTRASTIVE_LOSS) - enum LayerType { - // "NONE" layer type is 0th enum element so that we don't cause confusion - // by defaulting to an existent LayerType (instead, should usually error if - // the type is unspecified). - NONE = 0; - ABSVAL = 35; - ACCURACY = 1; - ARGMAX = 30; - BNLL = 2; - CONCAT = 3; - CONTRASTIVE_LOSS = 37; - CONVOLUTION = 4; - DATA = 5; - DROPOUT = 6; - DUMMY_DATA = 32; - EUCLIDEAN_LOSS = 7; - ELTWISE = 25; - FLATTEN = 8; - HDF5_DATA = 9; - HDF5_OUTPUT = 10; - HINGE_LOSS = 28; - IM2COL = 11; - IMAGE_DATA = 12; - INFOGAIN_LOSS = 13; - INNER_PRODUCT = 14; - LRN = 15; - MEMORY_DATA = 29; - MULTINOMIAL_LOGISTIC_LOSS = 16; - MVN = 34; - POOLING = 17; - POWER = 26; - RELU = 18; - SIGMOID = 19; - SIGMOID_CROSS_ENTROPY_LOSS = 27; - SILENCE = 36; - SOFTMAX = 20; - SOFTMAX_LOSS = 21; - SPLIT = 22; - SLICE = 33; - TANH = 23; - WINDOW_DATA = 24; - THRESHOLD = 31; - } - optional LayerType type = 5; // the layer type from the enum above - - // The blobs containing the numeric parameters of the layer - repeated BlobProto blobs = 6; +// Specifies training parameters (multipliers on global learning constants, +// and the name and other settings used for weight sharing). +message ParamSpec { // The names of the parameter blobs -- useful for sharing parameters among - // layers (but never required). - repeated string param = 1001; + // layers, but never required otherwise. To share a parameter between two + // layers, give it a (non-empty) name. + optional string name = 1; + // Whether to require shared weights to have the same shape, or just the same // count -- defaults to STRICT if unspecified. - repeated DimCheckMode blob_share_mode = 1002; + optional DimCheckMode share_mode = 2; enum DimCheckMode { // STRICT (default) requires that num, channels, height, width each match. STRICT = 0; // PERMISSIVE requires only the count (num*channels*height*width) to match. PERMISSIVE = 1; } - // The ratio that is multiplied on the global learning rate. If you want to - // set the learning ratio for one blob, you need to set it for all blobs. - repeated float blobs_lr = 7; - // The weight decay that is multiplied on the global weight decay. - repeated float weight_decay = 8; + + // The multiplier on the global learning rate for this parameter. + optional float lr_mult = 3 [default = 1.0]; + + // The multiplier on the global weight decay for this parameter. + optional float decay_mult = 4 [default = 1.0]; +} + +// NOTE +// Update the next available ID when you add a new LayerParameter field. +// +// LayerParameter next available layer-specific ID: 131 (last added: python_param) +message LayerParameter { + optional string name = 1; // the layer name + optional string type = 2; // the layer type + repeated string bottom = 3; // the name of each bottom blob + repeated string top = 4; // the name of each top blob + + // The train / test phase for computation. + optional Phase phase = 10; // The amount of weight to assign each top blob in the objective. // Each layer assigns a default value, usually of either 0 or 1, // to each top blob. - repeated float loss_weight = 35; + repeated float loss_weight = 5; - optional AccuracyParameter accuracy_param = 27; - optional ArgMaxParameter argmax_param = 23; - optional ConcatParameter concat_param = 9; - optional ContrastiveLossParameter contrastive_loss_param = 40; - optional ConvolutionParameter convolution_param = 10; - optional DataParameter data_param = 11; - optional DropoutParameter dropout_param = 12; - optional DummyDataParameter dummy_data_param = 26; - optional EltwiseParameter eltwise_param = 24; - optional HDF5DataParameter hdf5_data_param = 13; - optional HDF5OutputParameter hdf5_output_param = 14; - optional HingeLossParameter hinge_loss_param = 29; - optional ImageDataParameter image_data_param = 15; - optional InfogainLossParameter infogain_loss_param = 16; - optional InnerProductParameter inner_product_param = 17; - optional LRNParameter lrn_param = 18; - optional MemoryDataParameter memory_data_param = 22; - optional MVNParameter mvn_param = 34; - optional PoolingParameter pooling_param = 19; - optional PowerParameter power_param = 21; - optional ReLUParameter relu_param = 30; - optional SigmoidParameter sigmoid_param = 38; - optional SoftmaxParameter softmax_param = 39; - optional SliceParameter slice_param = 31; - optional TanHParameter tanh_param = 37; - optional ThresholdParameter threshold_param = 25; - optional WindowDataParameter window_data_param = 20; + // Specifies training parameters (multipliers on global learning constants, + // and the name and other settings used for weight sharing). + repeated ParamSpec param = 6; + + // The blobs containing the numeric parameters of the layer. + repeated BlobProto blobs = 7; + + // Rules controlling whether and when a layer is included in the network, + // based on the current NetState. You may specify a non-zero number of rules + // to include OR exclude, but not both. If no include or exclude rules are + // specified, the layer is always included. If the current NetState meets + // ANY (i.e., one or more) of the specified rules, the layer is + // included/excluded. + repeated NetStateRule include = 8; + repeated NetStateRule exclude = 9; // Parameters for data pre-processing. - optional TransformationParameter transform_param = 36; + optional TransformationParameter transform_param = 100; + // Parameters shared by loss layers. + optional LossParameter loss_param = 101; + + // Layer type-specific parameters. + // // Note: certain layers may have more than one computational engine // for their implementation. These layers include an Engine type and // engine parameter for selecting the implementation. // The default for the engine is set by the ENGINE switch at compile-time. - - // DEPRECATED: The layer parameters specified as a V0LayerParameter. - // This should never be used by any code except to upgrade to the new - // LayerParameter specification. - optional V0LayerParameter layer = 1; + optional AccuracyParameter accuracy_param = 102; + optional ArgMaxParameter argmax_param = 103; + optional ConcatParameter concat_param = 104; + optional ContrastiveLossParameter contrastive_loss_param = 105; + optional ConvolutionParameter convolution_param = 106; + optional DataParameter data_param = 107; + optional DropoutParameter dropout_param = 108; + optional DummyDataParameter dummy_data_param = 109; + optional EltwiseParameter eltwise_param = 110; + optional ExpParameter exp_param = 111; + optional HDF5DataParameter hdf5_data_param = 112; + optional HDF5OutputParameter hdf5_output_param = 113; + optional HingeLossParameter hinge_loss_param = 114; + optional ImageDataParameter image_data_param = 115; + optional InfogainLossParameter infogain_loss_param = 116; + optional InnerProductParameter inner_product_param = 117; + optional LRNParameter lrn_param = 118; + optional MemoryDataParameter memory_data_param = 119; + optional MVNParameter mvn_param = 120; + optional PoolingParameter pooling_param = 121; + optional PowerParameter power_param = 122; + optional PythonParameter python_param = 130; + optional ReLUParameter relu_param = 123; + optional SigmoidParameter sigmoid_param = 124; + optional SoftmaxParameter softmax_param = 125; + optional SliceParameter slice_param = 126; + optional TanHParameter tanh_param = 127; + optional ThresholdParameter threshold_param = 128; + optional WindowDataParameter window_data_param = 129; } // Message that stores parameters used to apply transformation @@ -349,7 +331,21 @@ message TransformationParameter { optional bool mirror = 2 [default = false]; // Specify if we would like to randomly crop an image. optional uint32 crop_size = 3 [default = 0]; + // mean_file and mean_value cannot be specified at the same time optional string mean_file = 4; + // if specified can be repeated once (would substract it from all the channels) + // or can be repeated the same number of times as channels + // (would subtract them from the corresponding channel) + repeated float mean_value = 5; +} + +// Message that stores parameters shared by loss layers +message LossParameter { + // If specified, ignore instances with the given label. + optional int32 ignore_label = 1; + // If true, normalize each batch across all instances (including spatial + // dimesions, but not ignored instances); else, divide by batch size only. + optional bool normalize = 2 [default = true]; } // Message that stores parameters used by AccuracyLayer @@ -420,7 +416,7 @@ message DataParameter { // The rand_skip variable is for the data layer to skip a few data points // to avoid all asynchronous sgd clients to start at the same point. The skip // point would be set as rand_skip * rand(0,1). Note that rand_skip should not - // be larger than the number of keys in the leveldb. + // be larger than the number of keys in the database. optional uint32 rand_skip = 7 [default = 0]; optional DB backend = 8 [default = LEVELDB]; // DEPRECATED. See TransformationParameter. For data pre-processing, we can do @@ -434,6 +430,8 @@ message DataParameter { // DEPRECATED. See TransformationParameter. Specify if we want to randomly mirror // data. optional bool mirror = 6 [default = false]; + // Force the encoded image to have 3 color channels + optional bool force_encoded_color = 9 [default = false]; } // Message that stores parameters used by DropoutLayer @@ -474,9 +472,14 @@ message EltwiseParameter { optional bool stable_prod_grad = 3 [default = true]; } -// Message that stores parameters used by ThresholdLayer -message ThresholdParameter { - optional float threshold = 1 [default = 0]; // Strictly Positive values +// Message that stores parameters used by ExpLayer +message ExpParameter { + // ExpLayer computes outputs y = base ^ (shift + scale * x), for base > 0. + // Or if base is set to the default (-1), base is set to e, + // so y = exp(shift + scale * x). + optional float base = 1 [default = -1.0]; + optional float scale = 2 [default = 1.0]; + optional float shift = 3 [default = 0.0]; } // Message that stores parameters used by HDF5DataLayer @@ -510,13 +513,15 @@ message ImageDataParameter { // The rand_skip variable is for the data layer to skip a few data points // to avoid all asynchronous sgd clients to start at the same point. The skip // point would be set as rand_skip * rand(0,1). Note that rand_skip should not - // be larger than the number of keys in the leveldb. + // be larger than the number of keys in the database. optional uint32 rand_skip = 7 [default = 0]; // Whether or not ImageLayer should shuffle the list of files at every epoch. optional bool shuffle = 8 [default = false]; // It will also resize images if new_height or new_width are not zero. optional uint32 new_height = 9 [default = 0]; optional uint32 new_width = 10 [default = 0]; + // Specify if the images are color or gray + optional bool is_color = 11 [default = true]; // DEPRECATED. See TransformationParameter. For data pre-processing, we can do // simple scaling and subtracting the data mean, if provided. Note that the // mean subtraction is always carried out before scaling. @@ -528,6 +533,7 @@ message ImageDataParameter { // DEPRECATED. See TransformationParameter. Specify if we want to randomly mirror // data. optional bool mirror = 6 [default = false]; + optional string root_folder = 12 [default = ""]; } // Message that stores parameters InfogainLossLayer @@ -554,6 +560,7 @@ message LRNParameter { WITHIN_CHANNEL = 1; } optional NormRegion norm_region = 4 [default = ACROSS_CHANNELS]; + optional float k = 5 [default = 1.]; } // Message that stores parameters used by MemoryDataLayer @@ -598,6 +605,9 @@ message PoolingParameter { CUDNN = 2; } optional Engine engine = 11 [default = DEFAULT]; + // If global_pooling then it will pool over the size of the bottom by doing + // kernel_h = bottom->height and kernel_w = bottom->width + optional bool global_pooling = 12 [default = false]; } // Message that stores parameters used by PowerLayer @@ -608,6 +618,12 @@ message PowerParameter { optional float shift = 3 [default = 0.0]; } +// Message that stores parameters used by PythonLayer +message PythonParameter { + optional string module = 1; + optional string layer = 2; +} + // Message that stores parameters used by ReLULayer message ReLUParameter { // Allow non-zero slope for negative inputs to speed up optimization @@ -644,7 +660,7 @@ message SliceParameter { repeated uint32 slice_point = 2; } -// Message that stores parameters used by SoftmaxLayer, SoftMaxWithLossLayer +// Message that stores parameters used by SoftmaxLayer, SoftmaxWithLossLayer message SoftmaxParameter { enum Engine { DEFAULT = 0; @@ -654,7 +670,7 @@ message SoftmaxParameter { optional Engine engine = 1 [default = DEFAULT]; } -// Message that stores parameters used by SigmoidLayer +// Message that stores parameters used by TanHLayer message TanHParameter { enum Engine { DEFAULT = 0; @@ -664,6 +680,11 @@ message TanHParameter { optional Engine engine = 1 [default = DEFAULT]; } +// Message that stores parameters used by ThresholdLayer +message ThresholdParameter { + optional float threshold = 1 [default = 0]; // Strictly positive values +} + // Message that stores parameters used by WindowDataLayer message WindowDataParameter { // Specify the data source. @@ -692,6 +713,103 @@ message WindowDataParameter { // warp: cropped window is warped to a fixed size and aspect ratio // square: the tightest square around the window is cropped optional string crop_mode = 11 [default = "warp"]; + // cache_images: will load all images in memory for faster access + optional bool cache_images = 12 [default = false]; + // append root_folder to locate images + optional string root_folder = 13 [default = ""]; +} + +// DEPRECATED: use LayerParameter. +message V1LayerParameter { + repeated string bottom = 2; + repeated string top = 3; + optional string name = 4; + repeated NetStateRule include = 32; + repeated NetStateRule exclude = 33; + enum LayerType { + NONE = 0; + ABSVAL = 35; + ACCURACY = 1; + ARGMAX = 30; + BNLL = 2; + CONCAT = 3; + CONTRASTIVE_LOSS = 37; + CONVOLUTION = 4; + DATA = 5; + DECONVOLUTION = 39; + DROPOUT = 6; + DUMMY_DATA = 32; + EUCLIDEAN_LOSS = 7; + ELTWISE = 25; + EXP = 38; + FLATTEN = 8; + HDF5_DATA = 9; + HDF5_OUTPUT = 10; + HINGE_LOSS = 28; + IM2COL = 11; + IMAGE_DATA = 12; + INFOGAIN_LOSS = 13; + INNER_PRODUCT = 14; + LRN = 15; + MEMORY_DATA = 29; + MULTINOMIAL_LOGISTIC_LOSS = 16; + MVN = 34; + POOLING = 17; + POWER = 26; + RELU = 18; + SIGMOID = 19; + SIGMOID_CROSS_ENTROPY_LOSS = 27; + SILENCE = 36; + SOFTMAX = 20; + SOFTMAX_LOSS = 21; + SPLIT = 22; + SLICE = 33; + TANH = 23; + WINDOW_DATA = 24; + THRESHOLD = 31; + } + optional LayerType type = 5; + repeated BlobProto blobs = 6; + repeated string param = 1001; + repeated DimCheckMode blob_share_mode = 1002; + enum DimCheckMode { + STRICT = 0; + PERMISSIVE = 1; + } + repeated float blobs_lr = 7; + repeated float weight_decay = 8; + repeated float loss_weight = 35; + optional AccuracyParameter accuracy_param = 27; + optional ArgMaxParameter argmax_param = 23; + optional ConcatParameter concat_param = 9; + optional ContrastiveLossParameter contrastive_loss_param = 40; + optional ConvolutionParameter convolution_param = 10; + optional DataParameter data_param = 11; + optional DropoutParameter dropout_param = 12; + optional DummyDataParameter dummy_data_param = 26; + optional EltwiseParameter eltwise_param = 24; + optional ExpParameter exp_param = 41; + optional HDF5DataParameter hdf5_data_param = 13; + optional HDF5OutputParameter hdf5_output_param = 14; + optional HingeLossParameter hinge_loss_param = 29; + optional ImageDataParameter image_data_param = 15; + optional InfogainLossParameter infogain_loss_param = 16; + optional InnerProductParameter inner_product_param = 17; + optional LRNParameter lrn_param = 18; + optional MemoryDataParameter memory_data_param = 22; + optional MVNParameter mvn_param = 34; + optional PoolingParameter pooling_param = 19; + optional PowerParameter power_param = 21; + optional ReLUParameter relu_param = 30; + optional SigmoidParameter sigmoid_param = 38; + optional SoftmaxParameter softmax_param = 39; + optional SliceParameter slice_param = 31; + optional TanHParameter tanh_param = 37; + optional ThresholdParameter threshold_param = 25; + optional WindowDataParameter window_data_param = 20; + optional TransformationParameter transform_param = 36; + optional LossParameter loss_param = 42; + optional V0LayerParameter layer = 1; } // DEPRECATED: V0LayerParameter is the old way of specifying layer parameters @@ -721,6 +839,7 @@ message V0LayerParameter { optional uint32 local_size = 13 [default = 5]; // for local response norm optional float alpha = 14 [default = 1.]; // for local response norm optional float beta = 15 [default = 0.75]; // for local response norm + optional float k = 22 [default = 1.]; // For data layers, specify the data source optional string source = 16; @@ -747,7 +866,7 @@ message V0LayerParameter { // The rand_skip variable is for the data layer to skip a few data points // to avoid all asynchronous sgd clients to start at the same point. The skip // point would be set as rand_skip * rand(0,1). Note that rand_skip should not - // be larger than the number of keys in the leveldb. + // be larger than the number of keys in the database. optional uint32 rand_skip = 53 [default = 0]; // Fields related to detection (det_*) diff --git a/src/caffe/proto/caffe_pretty_print.proto b/src/caffe/proto/caffe_pretty_print.proto deleted file mode 100644 index 6f0a5f6b8df..00000000000 --- a/src/caffe/proto/caffe_pretty_print.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto2"; - -package caffe; - -import "caffe.proto"; - -// A near-duplicate of NetParameter with fields re-numbered to beautify -// automatic prototext dumps. The main practical purpose is to print inputs -// before layers, because having inputs at the end looks weird. -// NetParameterPrettyPrint should never be used in code except for conversion -// FROM NetParameter and subsequent dumping to proto text file. -message NetParameterPrettyPrint { - optional string name = 1; - optional bool force_backward = 2 [default = false]; - repeated string input = 3; - repeated int32 input_dim = 4; - repeated LayerParameter layers = 5; -} diff --git a/src/caffe/solver.cpp b/src/caffe/solver.cpp index 0fd8d0f89dc..8ed8aec2fc8 100644 --- a/src/caffe/solver.cpp +++ b/src/caffe/solver.cpp @@ -32,6 +32,7 @@ void Solver::Init(const SolverParameter& param) { LOG(INFO) << "Initializing solver from parameters: " << std::endl << param.DebugString(); param_ = param; + CHECK_GE(param_.average_loss(), 1) << "average_loss should be non-negative."; if (param_.random_seed() >= 0) { Caffe::set_random_seed(param_.random_seed()); } @@ -39,6 +40,8 @@ void Solver::Init(const SolverParameter& param) { InitTrainNet(); InitTestNets(); LOG(INFO) << "Solver scaffolding done."; + iter_ = 0; + current_step_ = 0; } template @@ -151,43 +154,20 @@ void Solver::InitTestNets() { LOG(INFO) << "Creating test net (#" << i << ") specified by " << sources[i]; test_nets_[i].reset(new Net(net_params[i])); + test_nets_[i]->set_debug_info(param_.debug_info()); } } template -void Solver::Solve(const char* resume_file) { - Caffe::set_phase(Caffe::TRAIN); - LOG(INFO) << "Solving " << net_->name(); - LOG(INFO) << "Learning Rate Policy: " << param_.lr_policy(); - PreSolve(); - - iter_ = 0; - current_step_ = 0; - if (resume_file) { - LOG(INFO) << "Restoring previous solver status from " << resume_file; - Restore(resume_file); - } - // Remember the initial iter_ value; will be non-zero if we loaded from a - // resume_file above. +void Solver::Step(int iters) { + vector*> bottom_vec; const int start_iter = iter_; - + const int stop_iter = iter_ + iters; int average_loss = this->param_.average_loss(); - - CHECK_GE(average_loss, 1) << "average_cost should be non-negative."; - vector losses; Dtype smoothed_loss = 0; - // For a network that is trained by the solver, no bottom or top vecs - // should be given, and we will just provide dummy vecs. - vector*> bottom_vec; - for (; iter_ < param_.max_iter(); ++iter_) { - // Save a snapshot if needed. - if (param_.snapshot() && iter_ > start_iter && - iter_ % param_.snapshot() == 0) { - Snapshot(); - } - + for (; iter_ < stop_iter; ++iter_) { if (param_.test_interval() && iter_ % param_.test_interval() == 0 && (iter_ > 0 || param_.test_initialization())) { TestAll(); @@ -227,13 +207,35 @@ void Solver::Solve(const char* resume_file) { } } } - ComputeUpdateValue(); net_->Update(); + + // Save a snapshot if needed. + if (param_.snapshot() && (iter_ + 1) % param_.snapshot() == 0) { + Snapshot(); + } + } +} + +template +void Solver::Solve(const char* resume_file) { + LOG(INFO) << "Solving " << net_->name(); + LOG(INFO) << "Learning Rate Policy: " << param_.lr_policy(); + + if (resume_file) { + LOG(INFO) << "Restoring previous solver status from " << resume_file; + Restore(resume_file); + } + + // For a network that is trained by the solver, no bottom or top vecs + // should be given, and we will just provide dummy vecs. + Step(param_.max_iter() - iter_); + // If we haven't already, save a snapshot after optimization, unless + // overridden by setting snapshot_after_train := false + if (param_.snapshot_after_train() + && (!param_.snapshot() || iter_ % param_.snapshot() != 0)) { + Snapshot(); } - // Always save a snapshot after optimization, unless overridden by setting - // snapshot_after_train := false. - if (param_.snapshot_after_train()) { Snapshot(); } // After the optimization is done, run an additional train and test pass to // display the train and test loss/outputs if appropriate (based on the // display and test_interval settings, respectively). Unlike in the rest of @@ -242,7 +244,7 @@ void Solver::Solve(const char* resume_file) { // display the loss, which is computed in the forward pass. if (param_.display() && iter_ % param_.display() == 0) { Dtype loss; - net_->Forward(bottom_vec, &loss); + net_->ForwardPrefilled(&loss); LOG(INFO) << "Iteration " << iter_ << ", loss = " << loss; } if (param_.test_interval() && iter_ % param_.test_interval() == 0) { @@ -263,8 +265,6 @@ template void Solver::Test(const int test_net_id) { LOG(INFO) << "Iteration " << iter_ << ", Testing net (#" << test_net_id << ")"; - // We need to set phase to test before running. - Caffe::set_phase(Caffe::TEST); CHECK_NOTNULL(test_nets_[test_net_id].get())-> ShareTrainedLayersWith(net_.get()); vector test_score; @@ -315,7 +315,6 @@ void Solver::Test(const int test_net_id) { LOG(INFO) << " Test net output #" << i << ": " << output_name << " = " << mean_score << loss_msg_stream.str(); } - Caffe::set_phase(Caffe::TRAIN); } @@ -328,14 +327,15 @@ void Solver::Snapshot() { string model_filename, snapshot_filename; const int kBufferSize = 20; char iter_str_buffer[kBufferSize]; - snprintf(iter_str_buffer, kBufferSize, "_iter_%d", iter_); + // Add one to iter_ to get the number of iterations that have completed. + snprintf(iter_str_buffer, kBufferSize, "_iter_%d", iter_ + 1); filename += iter_str_buffer; model_filename = filename + ".caffemodel"; LOG(INFO) << "Snapshotting to " << model_filename; WriteProtoToBinaryFile(net_param, model_filename.c_str()); SolverState state; SnapshotSolverState(&state); - state.set_iter(iter_); + state.set_iter(iter_ + 1); state.set_learned_net(model_filename); state.set_current_step(current_step_); snapshot_filename = filename + ".solverstate"; @@ -415,7 +415,7 @@ Dtype SGDSolver::GetLearningRate() { template void SGDSolver::PreSolve() { // Initialize the history - vector > >& net_params = this->net_->params(); + const vector > >& net_params = this->net_->params(); history_.clear(); update_.clear(); temp_.clear(); @@ -433,17 +433,43 @@ void SGDSolver::PreSolve() { } } +template +void SGDSolver::ClipGradients() { + const Dtype clip_gradients = this->param_.clip_gradients(); + if (clip_gradients < 0) { return; } + const vector > >& net_params = this->net_->params(); + Dtype sumsq_diff = 0; + for (int i = 0; i < net_params.size(); ++i) { + if (this->net_->param_owners()[i] < 0) { + sumsq_diff += net_params[i]->sumsq_diff(); + } + } + const Dtype l2norm_diff = std::sqrt(sumsq_diff); + if (l2norm_diff > clip_gradients) { + Dtype scale_factor = clip_gradients / l2norm_diff; + LOG(INFO) << "Gradient clipping: scaling down gradients (L2 norm " + << l2norm_diff << " > " << clip_gradients << ") " + << "by scale factor " << scale_factor; + for (int i = 0; i < net_params.size(); ++i) { + if (this->net_->param_owners()[i] < 0) { + net_params[i]->scale_diff(scale_factor); + } + } + } +} template void SGDSolver::ComputeUpdateValue() { - vector > >& net_params = this->net_->params(); - vector& net_params_lr = this->net_->params_lr(); - vector& net_params_weight_decay = this->net_->params_weight_decay(); + const vector > >& net_params = this->net_->params(); + const vector& net_params_lr = this->net_->params_lr(); + const vector& net_params_weight_decay = + this->net_->params_weight_decay(); // get the learning rate Dtype rate = GetLearningRate(); if (this->param_.display() && this->iter_ % this->param_.display() == 0) { LOG(INFO) << "Iteration " << this->iter_ << ", lr = " << rate; } + ClipGradients(); Dtype momentum = this->param_.momentum(); Dtype weight_decay = this->param_.weight_decay(); string regularization_type = this->param_.regularization_type(); @@ -549,14 +575,16 @@ void SGDSolver::RestoreSolverState(const SolverState& state) { template void NesterovSolver::ComputeUpdateValue() { - vector > >& net_params = this->net_->params(); - vector& net_params_lr = this->net_->params_lr(); - vector& net_params_weight_decay = this->net_->params_weight_decay(); + const vector > >& net_params = this->net_->params(); + const vector& net_params_lr = this->net_->params_lr(); + const vector& net_params_weight_decay = + this->net_->params_weight_decay(); // get the learning rate Dtype rate = this->GetLearningRate(); if (this->param_.display() && this->iter_ % this->param_.display() == 0) { LOG(INFO) << "Iteration " << this->iter_ << ", lr = " << rate; } + SGDSolver::ClipGradients(); Dtype momentum = this->param_.momentum(); Dtype weight_decay = this->param_.weight_decay(); string regularization_type = this->param_.regularization_type(); @@ -664,15 +692,17 @@ void NesterovSolver::ComputeUpdateValue() { template void AdaGradSolver::ComputeUpdateValue() { - vector > >& net_params = this->net_->params(); - vector& net_params_lr = this->net_->params_lr(); - vector& net_params_weight_decay = this->net_->params_weight_decay(); + const vector > >& net_params = this->net_->params(); + const vector& net_params_lr = this->net_->params_lr(); + const vector& net_params_weight_decay = + this->net_->params_weight_decay(); // get the learning rate Dtype rate = this->GetLearningRate(); Dtype delta = this->param_.delta(); if (this->param_.display() && this->iter_ % this->param_.display() == 0) { LOG(INFO) << "Iteration " << this->iter_ << ", lr = " << rate; } + SGDSolver::ClipGradients(); Dtype weight_decay = this->param_.weight_decay(); string regularization_type = this->param_.regularization_type(); switch (Caffe::mode()) { diff --git a/src/caffe/test/CMakeLists.txt b/src/caffe/test/CMakeLists.txt index 20a1d084d6a..35a803f2f41 100644 --- a/src/caffe/test/CMakeLists.txt +++ b/src/caffe/test/CMakeLists.txt @@ -1,105 +1,36 @@ -# -# -# All test files' names must begin with a "test_" prefix -# -# -project( Test ) - -# Configuration -set(TEST_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test) # test executables are going to be placed there -set(TEST_EXT .testbin) # test executable extension -set(ALL_TEST test${TEST_EXT}) # name of an executable comprising of all tests -set(RUN_TEST runtest) # dummy target for running tests -set(TEST_MAIN test_caffe_main.cpp) # main test file (with main function) - -# Generate config files -add_definitions(-DCMAKE_BUILD) # definition needed in order to include CMake's generated files -set(IN_EXT .in) # generator input file extension -set(GEN_EXT .gen.cmake) # generated output file extension -set(TEST_DEFINES_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake_test_defines.hpp) -set(TEST_DATA_FILE ${CMAKE_CURRENT_SOURCE_DIR}/test_data/sample_data_list.txt) - -# Function prepares name of a test executable -# @output_name - output variable's name -# @filename - test_*.cpp file path -function(test_name output_name filename) - get_filename_component(name ${filename} NAME_WE) - set(${output_name} ${name}${TEST_EXT} PARENT_SCOPE) -endfunction() - -set(IN_FILES # generator input files - ${TEST_DEFINES_FILE} - ${TEST_DATA_FILE} -) - -foreach(in_file ${IN_FILES}) - configure_file( - ${in_file}${IN_EXT} - ${in_file}${GEN_EXT} - ) -endforeach() - -include_directories( - ${Caffe_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} -) - -# Remove main from test sources and prepare an Object lib with main -file(GLOB TEST_MAIN ${TEST_MAIN}) -list(REMOVE_ITEM TEST_CPP_SOURCES ${TEST_MAIN}) -add_library(main_obj EXCLUDE_FROM_ALL OBJECT ${TEST_MAIN} ../common.cpp) - -# Build each test separately from *.cpp files -foreach(source ${TEST_CPP_SOURCES}) - test_name(TEST_NAME ${source}) - - # - add_library(${TEST_NAME}.obj EXCLUDE_FROM_ALL OBJECT ${source}) - set(TEST_OBJ_LIB $) - - add_executable(${TEST_NAME} EXCLUDE_FROM_ALL ${TEST_OBJ_LIB} $) - target_link_libraries(${TEST_NAME} gtest caffe) - - # output dir - set_target_properties(${TEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test) - - # Targets and object libs - set(TEST_TARGETS ${TEST_TARGETS} ${TEST_NAME}) - set(TEST_OBJ_LIBS ${TEST_OBJ_LIBS} ${TEST_OBJ_LIB}) -endforeach() - -# Build each test separately from *.cu files -foreach(source ${TEST_CU_SOURCES}) - test_name(TEST_NAME ${source}) - - cuda_add_library(${TEST_NAME}.lib EXCLUDE_FROM_ALL ${source}) - - add_executable(${TEST_NAME} EXCLUDE_FROM_ALL $) - target_link_libraries(${TEST_NAME} ${TEST_NAME}.lib gtest caffe) - - # output dir - set_target_properties(${TEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test) - - # Targets and object libs - set(TEST_TARGETS ${TEST_TARGETS} ${TEST_NAME}) - set(TEST_CU_LIBS ${TEST_CU_LIBS} ${TEST_NAME}.lib) -endforeach() - -# Build a compound test excluded from the ALL target -add_executable(${ALL_TEST} EXCLUDE_FROM_ALL ${TEST_OBJ_LIBS} $) -if(NOT CPU_ONLY) - target_link_libraries(${ALL_TEST} ${TEST_CU_LIBS}) +# The option allows to include in build only selected test files and exclude all others +# Usage example: +# cmake -DBUILD_only_tests="common,net,blob,im2col_kernel" +set(BUILD_only_tests "" CACHE STRING "Blank or comma-separated list of test files to build without 'test_' prefix and extention") +caffe_leave_only_selected_tests(test_srcs ${BUILD_only_tests}) +caffe_leave_only_selected_tests(test_cuda ${BUILD_only_tests}) + +# For 'make runtest' target we don't need to embed test data paths to +# source files, because test target is executed in source directory +# That's why the lines below are commented. TODO: remove them + +# definition needed to include CMake generated files +#add_definitions(-DCMAKE_BUILD) + +# generates test_data/sample_data_list.txt.gen.cmake +#caffe_configure_testdatafile(test_data/sample_data_list.txt) + +set(the_target test.testbin) +set(test_args --gtest_shuffle) + +if(HAVE_CUDA) + caffe_cuda_compile(test_cuda_objs ${test_cuda}) + list(APPEND test_srcs ${test_cuda_objs} ${test_cuda}) +else() + list(APPEND test_args --gtest_filter="-*GPU*") endif() -target_link_libraries(${ALL_TEST} gtest caffe) -add_dependencies(${ALL_TEST} ${TEST_TARGETS}) - -# Output directory -set_target_properties(${ALL_TEST} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TEST_OUTPUT_DIRECTORY}) -# Test command -set(TEST_ARGS --gtest_shuffle) -if(CPU_ONLY) - set(TEST_ARGS ${TEST_ARGS} --gtest_filter="-*GPU*") -endif() +# ---[ Adding test target +add_executable(${the_target} EXCLUDE_FROM_ALL ${test_srcs}) +target_link_libraries(${the_target} gtest ${Caffe_LINK}) +caffe_default_properties(${the_target}) +caffe_set_runtime_directory(${the_target} "${PROJECT_BINARY_DIR}/test") -add_custom_target(${RUN_TEST} COMMAND ${ALL_TEST} ${TEST_ARGS}) +# ---[ Adding runtest +add_custom_target(runtest COMMAND ${the_target} ${test_args} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) diff --git a/src/caffe/test/cmake_test_defines.hpp.in b/src/caffe/test/cmake_test_defines.hpp.in deleted file mode 100644 index 870eaf5c26e..00000000000 --- a/src/caffe/test/cmake_test_defines.hpp.in +++ /dev/null @@ -1,4 +0,0 @@ -#define CUDA_TEST_DEVICE @CUDA_TEST_DEVICE@ -#define CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/src/" -#define EXAMPLES_SOURCE_DIR "@CMAKE_SOURCE_DIR@/examples/" -#define CMAKE_EXT ".gen.cmake" diff --git a/src/caffe/test/test_accuracy_layer.cpp b/src/caffe/test/test_accuracy_layer.cpp index e11e3f2a981..fa59fab1e8a 100644 --- a/src/caffe/test/test_accuracy_layer.cpp +++ b/src/caffe/test/test_accuracy_layer.cpp @@ -59,7 +59,7 @@ TYPED_TEST_CASE(AccuracyLayerTest, TestDtypes); TYPED_TEST(AccuracyLayerTest, TestSetup) { LayerParameter layer_param; AccuracyLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 1); EXPECT_EQ(this->blob_top_->channels(), 1); EXPECT_EQ(this->blob_top_->height(), 1); @@ -72,7 +72,7 @@ TYPED_TEST(AccuracyLayerTest, TestSetupTopK) { layer_param.mutable_accuracy_param(); accuracy_param->set_top_k(5); AccuracyLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 1); EXPECT_EQ(this->blob_top_->channels(), 1); EXPECT_EQ(this->blob_top_->height(), 1); @@ -83,8 +83,8 @@ TYPED_TEST(AccuracyLayerTest, TestForwardCPU) { LayerParameter layer_param; Caffe::set_mode(Caffe::CPU); AccuracyLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); TypeParam max_value; int max_id; @@ -111,8 +111,8 @@ TYPED_TEST(AccuracyLayerTest, TestForwardCPUTopK) { AccuracyParameter* accuracy_param = layer_param.mutable_accuracy_param(); accuracy_param->set_top_k(this->top_k_); AccuracyLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); TypeParam current_value; int current_rank; diff --git a/src/caffe/test/test_argmax_layer.cpp b/src/caffe/test/test_argmax_layer.cpp index fb3951c3098..3487d42f21e 100644 --- a/src/caffe/test/test_argmax_layer.cpp +++ b/src/caffe/test/test_argmax_layer.cpp @@ -41,7 +41,7 @@ TYPED_TEST_CASE(ArgMaxLayerTest, TestDtypes); TYPED_TEST(ArgMaxLayerTest, TestSetup) { LayerParameter layer_param; ArgMaxLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), 1); } @@ -51,7 +51,7 @@ TYPED_TEST(ArgMaxLayerTest, TestSetupMaxVal) { ArgMaxParameter* argmax_param = layer_param.mutable_argmax_param(); argmax_param->set_out_max_val(true); ArgMaxLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), 2); } @@ -59,8 +59,8 @@ TYPED_TEST(ArgMaxLayerTest, TestSetupMaxVal) { TYPED_TEST(ArgMaxLayerTest, TestCPU) { LayerParameter layer_param; ArgMaxLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const TypeParam* bottom_data = this->blob_bottom_->cpu_data(); const TypeParam* top_data = this->blob_top_->cpu_data(); @@ -84,8 +84,8 @@ TYPED_TEST(ArgMaxLayerTest, TestCPUMaxVal) { ArgMaxParameter* argmax_param = layer_param.mutable_argmax_param(); argmax_param->set_out_max_val(true); ArgMaxLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const TypeParam* bottom_data = this->blob_bottom_->cpu_data(); const TypeParam* top_data = this->blob_top_->cpu_data(); @@ -110,8 +110,8 @@ TYPED_TEST(ArgMaxLayerTest, TestCPUTopK) { ArgMaxParameter* argmax_param = layer_param.mutable_argmax_param(); argmax_param->set_top_k(this->top_k_); ArgMaxLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values int max_ind; TypeParam max_val; @@ -140,8 +140,8 @@ TYPED_TEST(ArgMaxLayerTest, TestCPUMaxValTopK) { argmax_param->set_out_max_val(true); argmax_param->set_top_k(this->top_k_); ArgMaxLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values int max_ind; TypeParam max_val; diff --git a/src/caffe/test/test_benchmark.cpp b/src/caffe/test/test_benchmark.cpp index dbbee08d667..43aaa639b3c 100644 --- a/src/caffe/test/test_benchmark.cpp +++ b/src/caffe/test/test_benchmark.cpp @@ -9,6 +9,8 @@ namespace caffe { +const float kMillisecondsThreshold = 30; + template class BenchmarkTest : public MultiDeviceTest {}; @@ -63,8 +65,8 @@ TYPED_TEST(BenchmarkTest, TestTimerMilliSeconds) { EXPECT_FALSE(timer.has_run_at_least_once()); timer.Start(); usleep(300 * 1000); - EXPECT_GE(timer.MilliSeconds(), 290); - EXPECT_LE(timer.MilliSeconds(), 310); + EXPECT_GE(timer.MilliSeconds(), 300 - kMillisecondsThreshold); + EXPECT_LE(timer.MilliSeconds(), 300 + kMillisecondsThreshold); EXPECT_TRUE(timer.initted()); EXPECT_FALSE(timer.running()); EXPECT_TRUE(timer.has_run_at_least_once()); @@ -78,8 +80,8 @@ TYPED_TEST(BenchmarkTest, TestTimerSeconds) { EXPECT_FALSE(timer.has_run_at_least_once()); timer.Start(); usleep(300 * 1000); - EXPECT_GE(timer.Seconds(), 0.290); - EXPECT_LE(timer.Seconds(), 0.310); + EXPECT_GE(timer.Seconds(), 0.3 - kMillisecondsThreshold / 1000.); + EXPECT_LE(timer.Seconds(), 0.3 + kMillisecondsThreshold / 1000.); EXPECT_TRUE(timer.initted()); EXPECT_FALSE(timer.running()); EXPECT_TRUE(timer.has_run_at_least_once()); diff --git a/src/caffe/test/test_blob.cpp b/src/caffe/test/test_blob.cpp index adf7a4d38e9..e0678061173 100644 --- a/src/caffe/test/test_blob.cpp +++ b/src/caffe/test/test_blob.cpp @@ -54,4 +54,190 @@ TYPED_TEST(BlobSimpleTest, TestReshape) { EXPECT_EQ(this->blob_->count(), 120); } +template +class BlobMathTest : public MultiDeviceTest { + typedef typename TypeParam::Dtype Dtype; + protected: + BlobMathTest() + : blob_(new Blob(2, 3, 4, 5)), + epsilon_(1e-6) {} + + virtual ~BlobMathTest() { delete blob_; } + Blob* const blob_; + Dtype epsilon_; +}; + +TYPED_TEST_CASE(BlobMathTest, TestDtypesAndDevices); + +TYPED_TEST(BlobMathTest, TestSumOfSquares) { + typedef typename TypeParam::Dtype Dtype; + + // Uninitialized Blob should have sum of squares == 0. + EXPECT_EQ(0, this->blob_->sumsq_data()); + EXPECT_EQ(0, this->blob_->sumsq_diff()); + FillerParameter filler_param; + filler_param.set_min(-3); + filler_param.set_max(3); + UniformFiller filler(filler_param); + filler.Fill(this->blob_); + Dtype expected_sumsq = 0; + const Dtype* data = this->blob_->cpu_data(); + for (int i = 0; i < this->blob_->count(); ++i) { + expected_sumsq += data[i] * data[i]; + } + // Do a mutable access on the current device, + // so that the sumsq computation is done on that device. + // (Otherwise, this would only check the CPU sumsq implementation.) + switch (TypeParam::device) { + case Caffe::CPU: + this->blob_->mutable_cpu_data(); + break; + case Caffe::GPU: + this->blob_->mutable_gpu_data(); + break; + default: + LOG(FATAL) << "Unknown device: " << TypeParam::device; + } + EXPECT_NEAR(expected_sumsq, this->blob_->sumsq_data(), + this->epsilon_ * expected_sumsq); + EXPECT_EQ(0, this->blob_->sumsq_diff()); + + // Check sumsq_diff too. + const Dtype kDiffScaleFactor = 7; + caffe_cpu_scale(this->blob_->count(), kDiffScaleFactor, data, + this->blob_->mutable_cpu_diff()); + switch (TypeParam::device) { + case Caffe::CPU: + this->blob_->mutable_cpu_diff(); + break; + case Caffe::GPU: + this->blob_->mutable_gpu_diff(); + break; + default: + LOG(FATAL) << "Unknown device: " << TypeParam::device; + } + EXPECT_NEAR(expected_sumsq, this->blob_->sumsq_data(), + this->epsilon_ * expected_sumsq); + const Dtype expected_sumsq_diff = + expected_sumsq * kDiffScaleFactor * kDiffScaleFactor; + EXPECT_NEAR(expected_sumsq_diff, this->blob_->sumsq_diff(), + this->epsilon_ * expected_sumsq_diff); +} + +TYPED_TEST(BlobMathTest, TestAsum) { + typedef typename TypeParam::Dtype Dtype; + + // Uninitialized Blob should have asum == 0. + EXPECT_EQ(0, this->blob_->asum_data()); + EXPECT_EQ(0, this->blob_->asum_diff()); + FillerParameter filler_param; + filler_param.set_min(-3); + filler_param.set_max(3); + UniformFiller filler(filler_param); + filler.Fill(this->blob_); + Dtype expected_asum = 0; + const Dtype* data = this->blob_->cpu_data(); + for (int i = 0; i < this->blob_->count(); ++i) { + expected_asum += std::fabs(data[i]); + } + // Do a mutable access on the current device, + // so that the asum computation is done on that device. + // (Otherwise, this would only check the CPU asum implementation.) + switch (TypeParam::device) { + case Caffe::CPU: + this->blob_->mutable_cpu_data(); + break; + case Caffe::GPU: + this->blob_->mutable_gpu_data(); + break; + default: + LOG(FATAL) << "Unknown device: " << TypeParam::device; + } + EXPECT_NEAR(expected_asum, this->blob_->asum_data(), + this->epsilon_ * expected_asum); + EXPECT_EQ(0, this->blob_->asum_diff()); + + // Check asum_diff too. + const Dtype kDiffScaleFactor = 7; + caffe_cpu_scale(this->blob_->count(), kDiffScaleFactor, data, + this->blob_->mutable_cpu_diff()); + switch (TypeParam::device) { + case Caffe::CPU: + this->blob_->mutable_cpu_diff(); + break; + case Caffe::GPU: + this->blob_->mutable_gpu_diff(); + break; + default: + LOG(FATAL) << "Unknown device: " << TypeParam::device; + } + EXPECT_NEAR(expected_asum, this->blob_->asum_data(), + this->epsilon_ * expected_asum); + const Dtype expected_diff_asum = expected_asum * kDiffScaleFactor; + EXPECT_NEAR(expected_diff_asum, this->blob_->asum_diff(), + this->epsilon_ * expected_diff_asum); +} + +TYPED_TEST(BlobMathTest, TestScaleData) { + typedef typename TypeParam::Dtype Dtype; + + EXPECT_EQ(0, this->blob_->asum_data()); + EXPECT_EQ(0, this->blob_->asum_diff()); + FillerParameter filler_param; + filler_param.set_min(-3); + filler_param.set_max(3); + UniformFiller filler(filler_param); + filler.Fill(this->blob_); + const Dtype asum_before_scale = this->blob_->asum_data(); + // Do a mutable access on the current device, + // so that the asum computation is done on that device. + // (Otherwise, this would only check the CPU asum implementation.) + switch (TypeParam::device) { + case Caffe::CPU: + this->blob_->mutable_cpu_data(); + break; + case Caffe::GPU: + this->blob_->mutable_gpu_data(); + break; + default: + LOG(FATAL) << "Unknown device: " << TypeParam::device; + } + const Dtype kDataScaleFactor = 3; + this->blob_->scale_data(kDataScaleFactor); + EXPECT_NEAR(asum_before_scale * kDataScaleFactor, this->blob_->asum_data(), + this->epsilon_ * asum_before_scale * kDataScaleFactor); + EXPECT_EQ(0, this->blob_->asum_diff()); + + // Check scale_diff too. + const Dtype kDataToDiffScaleFactor = 7; + const Dtype* data = this->blob_->cpu_data(); + caffe_cpu_scale(this->blob_->count(), kDataToDiffScaleFactor, data, + this->blob_->mutable_cpu_diff()); + const Dtype expected_asum_before_scale = asum_before_scale * kDataScaleFactor; + EXPECT_NEAR(expected_asum_before_scale, this->blob_->asum_data(), + this->epsilon_ * expected_asum_before_scale); + const Dtype expected_diff_asum_before_scale = + asum_before_scale * kDataScaleFactor * kDataToDiffScaleFactor; + EXPECT_NEAR(expected_diff_asum_before_scale, this->blob_->asum_diff(), + this->epsilon_ * expected_diff_asum_before_scale); + switch (TypeParam::device) { + case Caffe::CPU: + this->blob_->mutable_cpu_diff(); + break; + case Caffe::GPU: + this->blob_->mutable_gpu_diff(); + break; + default: + LOG(FATAL) << "Unknown device: " << TypeParam::device; + } + const Dtype kDiffScaleFactor = 3; + this->blob_->scale_diff(kDiffScaleFactor); + EXPECT_NEAR(asum_before_scale * kDataScaleFactor, this->blob_->asum_data(), + this->epsilon_ * asum_before_scale * kDataScaleFactor); + const Dtype expected_diff_asum = + expected_diff_asum_before_scale * kDiffScaleFactor; + EXPECT_NEAR(expected_diff_asum, this->blob_->asum_diff(), + this->epsilon_ * expected_diff_asum); +} + } // namespace caffe diff --git a/src/caffe/test/test_caffe_main.cpp b/src/caffe/test/test_caffe_main.cpp index bff0c4ed20e..c8caf5ac58e 100644 --- a/src/caffe/test/test_caffe_main.cpp +++ b/src/caffe/test/test_caffe_main.cpp @@ -1,6 +1,7 @@ // The main caffe test code. Your test cpp code should include this hpp // to allow a main function to be compiled into the binary. +#include "caffe/caffe.hpp" #include "caffe/test/test_caffe_main.hpp" namespace caffe { @@ -15,7 +16,7 @@ using caffe::CAFFE_TEST_CUDA_PROP; int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - ::google::InitGoogleLogging(argv[0]); + caffe::GlobalInit(&argc, &argv); #ifndef CPU_ONLY // Before starting testing, let's first print out a few cuda defice info. int device; diff --git a/src/caffe/test/test_common.cpp b/src/caffe/test/test_common.cpp index 0b3639c7706..b3a61b0fd25 100644 --- a/src/caffe/test/test_common.cpp +++ b/src/caffe/test/test_common.cpp @@ -29,13 +29,6 @@ TEST_F(CommonTest, TestBrewMode) { EXPECT_EQ(Caffe::mode(), Caffe::GPU); } -TEST_F(CommonTest, TestPhase) { - Caffe::set_phase(Caffe::TRAIN); - EXPECT_EQ(Caffe::phase(), Caffe::TRAIN); - Caffe::set_phase(Caffe::TEST); - EXPECT_EQ(Caffe::phase(), Caffe::TEST); -} - TEST_F(CommonTest, TestRandSeedCPU) { SyncedMemory data_a(10 * sizeof(int)); SyncedMemory data_b(10 * sizeof(int)); diff --git a/src/caffe/test/test_concat_layer.cpp b/src/caffe/test/test_concat_layer.cpp index c60b7f744cc..f14f1d2fa4f 100644 --- a/src/caffe/test/test_concat_layer.cpp +++ b/src/caffe/test/test_concat_layer.cpp @@ -63,7 +63,7 @@ TYPED_TEST(ConcatLayerTest, TestSetupNum) { LayerParameter layer_param; layer_param.mutable_concat_param()->set_concat_dim(0); ConcatLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_1, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_1, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_0->num() + this->blob_bottom_2->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_0->channels()); @@ -75,7 +75,7 @@ TYPED_TEST(ConcatLayerTest, TestSetupChannels) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ConcatLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_0, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_0, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_0->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_0->channels()+this->blob_bottom_1->channels()); @@ -88,8 +88,8 @@ TYPED_TEST(ConcatLayerTest, TestNum) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ConcatLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_0, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_0, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_0, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_0, this->blob_top_vec_); for (int n = 0; n < this->blob_top_->num(); ++n) { for (int c = 0; c < this->blob_bottom_0->channels(); ++c) { for (int h = 0; h < this->blob_top_->height(); ++h) { @@ -115,8 +115,8 @@ TYPED_TEST(ConcatLayerTest, TestGradient) { LayerParameter layer_param; ConcatLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradient(&layer, &(this->blob_bottom_vec_0), - &(this->blob_top_vec_)); + checker.CheckGradient(&layer, this->blob_bottom_vec_0, + this->blob_top_vec_); } } // namespace caffe diff --git a/src/caffe/test/test_contrastive_loss_layer.cpp b/src/caffe/test/test_contrastive_loss_layer.cpp index a5bef4c9826..d269fbc26f2 100644 --- a/src/caffe/test/test_contrastive_loss_layer.cpp +++ b/src/caffe/test/test_contrastive_loss_layer.cpp @@ -62,8 +62,8 @@ TYPED_TEST(ContrastiveLossLayerTest, TestForward) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ContrastiveLossLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // manually compute to compare const Dtype margin = layer_param.contrastive_loss_param().margin(); const int num = this->blob_bottom_data_i_->num(); @@ -90,13 +90,13 @@ TYPED_TEST(ContrastiveLossLayerTest, TestGradient) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ContrastiveLossLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); GradientChecker checker(1e-2, 1e-2, 1701); // check the gradient for the first two bottom layers - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_), 0); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_), 1); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 1); } } // namespace caffe diff --git a/src/caffe/test/test_convolution_layer.cpp b/src/caffe/test/test_convolution_layer.cpp index a38ad3fd1a8..c1fe3b58c58 100644 --- a/src/caffe/test/test_convolution_layer.cpp +++ b/src/caffe/test/test_convolution_layer.cpp @@ -157,7 +157,7 @@ TYPED_TEST(ConvolutionLayerTest, TestSetup) { this->blob_top_vec_.push_back(this->blob_top_2_); shared_ptr > layer( new ConvolutionLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 4); EXPECT_EQ(this->blob_top_->height(), 2); @@ -170,7 +170,7 @@ TYPED_TEST(ConvolutionLayerTest, TestSetup) { convolution_param->set_num_output(3); convolution_param->set_group(3); layer.reset(new ConvolutionLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 3); EXPECT_EQ(this->blob_top_->height(), 2); @@ -182,7 +182,6 @@ TYPED_TEST(ConvolutionLayerTest, TestSetup) { } TYPED_TEST(ConvolutionLayerTest, TestSimpleConvolution) { - // We will simply see if the convolution layer carries out averaging well. typedef typename TypeParam::Dtype Dtype; this->blob_bottom_vec_.push_back(this->blob_bottom_2_); this->blob_top_vec_.push_back(this->blob_top_2_); @@ -197,8 +196,8 @@ TYPED_TEST(ConvolutionLayerTest, TestSimpleConvolution) { convolution_param->mutable_bias_filler()->set_value(0.1); shared_ptr > layer( new ConvolutionLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Check against reference convolution. const Dtype* top_data; const Dtype* ref_top_data; @@ -218,8 +217,34 @@ TYPED_TEST(ConvolutionLayerTest, TestSimpleConvolution) { } } +TYPED_TEST(ConvolutionLayerTest, Test1x1Convolution) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + ConvolutionParameter* convolution_param = + layer_param.mutable_convolution_param(); + convolution_param->set_kernel_size(1); + convolution_param->set_stride(1); + convolution_param->set_num_output(4); + convolution_param->mutable_weight_filler()->set_type("gaussian"); + convolution_param->mutable_bias_filler()->set_type("constant"); + convolution_param->mutable_bias_filler()->set_value(0.1); + shared_ptr > layer( + new ConvolutionLayer(layer_param)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); + // Check against reference convolution. + const Dtype* top_data; + const Dtype* ref_top_data; + caffe_conv(this->blob_bottom_, convolution_param, layer->blobs(), + this->MakeReferenceTop(this->blob_top_)); + top_data = this->blob_top_->cpu_data(); + ref_top_data = this->ref_blob_top_->cpu_data(); + for (int i = 0; i < this->blob_top_->count(); ++i) { + EXPECT_NEAR(top_data[i], ref_top_data[i], 1e-4); + } +} + TYPED_TEST(ConvolutionLayerTest, TestSimpleConvolutionGroup) { - // We will simply see if the convolution layer carries out averaging well. typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ConvolutionParameter* convolution_param = @@ -233,8 +258,8 @@ TYPED_TEST(ConvolutionLayerTest, TestSimpleConvolutionGroup) { convolution_param->mutable_bias_filler()->set_value(0.1); shared_ptr > layer( new ConvolutionLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Check against reference convolution. const Dtype* top_data; const Dtype* ref_top_data; @@ -284,8 +309,8 @@ TYPED_TEST(ConvolutionLayerTest, TestSobelConvolution) { weights[i + 7] = 0; weights[i + 8] = 1; } - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Compute Sobel G_x operator as separable 3 x 1 and 1 x 3 convolutions. // (1) the [1 2 1] column filter vector*> sep_blob_bottom_vec; @@ -311,8 +336,8 @@ TYPED_TEST(ConvolutionLayerTest, TestSobelConvolution) { weights_1[i + 1] = 2; weights_1[i + 2] = 1; } - layer->SetUp(sep_blob_bottom_vec, &(sep_blob_top_vec)); - layer->Forward(sep_blob_bottom_vec, &(sep_blob_top_vec)); + layer->SetUp(sep_blob_bottom_vec, sep_blob_top_vec); + layer->Forward(sep_blob_bottom_vec, sep_blob_top_vec); // (2) the [-1 0 1] row filter blob_sep->CopyFrom(*this->blob_top_2_, false, true); sep_blob_bottom_vec.clear(); @@ -333,8 +358,8 @@ TYPED_TEST(ConvolutionLayerTest, TestSobelConvolution) { weights_2[i + 1] = 0; weights_2[i + 2] = 1; } - layer->SetUp(sep_blob_bottom_vec, &(sep_blob_top_vec)); - layer->Forward(sep_blob_bottom_vec, &(sep_blob_top_vec)); + layer->SetUp(sep_blob_bottom_vec, sep_blob_top_vec); + layer->Forward(sep_blob_bottom_vec, sep_blob_top_vec); // Test equivalence of full and separable filters. const Dtype* top_data = this->blob_top_->cpu_data(); const Dtype* sep_top_data = this->blob_top_2_->cpu_data(); @@ -357,8 +382,26 @@ TYPED_TEST(ConvolutionLayerTest, TestGradient) { convolution_param->mutable_bias_filler()->set_type("gaussian"); ConvolutionLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); +} + +TYPED_TEST(ConvolutionLayerTest, Test1x1Gradient) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + ConvolutionParameter* convolution_param = + layer_param.mutable_convolution_param(); + this->blob_bottom_vec_.push_back(this->blob_bottom_2_); + this->blob_top_vec_.push_back(this->blob_top_2_); + convolution_param->set_kernel_size(1); + convolution_param->set_stride(1); + convolution_param->set_num_output(2); + convolution_param->mutable_weight_filler()->set_type("gaussian"); + convolution_param->mutable_bias_filler()->set_type("gaussian"); + ConvolutionLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-3); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(ConvolutionLayerTest, TestGradientGroup) { @@ -374,8 +417,8 @@ TYPED_TEST(ConvolutionLayerTest, TestGradientGroup) { convolution_param->mutable_bias_filler()->set_type("gaussian"); ConvolutionLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } #ifdef USE_CUDNN @@ -437,7 +480,7 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSetupCuDNN) { this->blob_top_vec_.push_back(this->blob_top_2_); shared_ptr > layer( new CuDNNConvolutionLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 4); EXPECT_EQ(this->blob_top_->height(), 2); @@ -450,7 +493,7 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSetupCuDNN) { convolution_param->set_num_output(3); convolution_param->set_group(3); layer.reset(new CuDNNConvolutionLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 3); EXPECT_EQ(this->blob_top_->height(), 2); @@ -462,7 +505,6 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSetupCuDNN) { } TYPED_TEST(CuDNNConvolutionLayerTest, TestSimpleConvolutionCuDNN) { - // We will simply see if the convolution layer carries out averaging well. Caffe::set_mode(Caffe::GPU); this->blob_bottom_vec_.push_back(this->blob_bottom_2_); this->blob_top_vec_.push_back(this->blob_top_2_); @@ -477,8 +519,8 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSimpleConvolutionCuDNN) { convolution_param->mutable_bias_filler()->set_value(0.1); shared_ptr > layer( new CuDNNConvolutionLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Check against reference convolution. const TypeParam* top_data; const TypeParam* ref_top_data; @@ -499,7 +541,6 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSimpleConvolutionCuDNN) { } TYPED_TEST(CuDNNConvolutionLayerTest, TestSimpleConvolutionGroupCuDNN) { - // We will simply see if the convolution layer carries out averaging well. Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; ConvolutionParameter* convolution_param = @@ -513,8 +554,8 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSimpleConvolutionGroupCuDNN) { convolution_param->mutable_bias_filler()->set_value(0.1); shared_ptr > layer( new CuDNNConvolutionLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Check against reference convolution. const TypeParam* top_data; const TypeParam* ref_top_data; @@ -564,8 +605,8 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSobelConvolutionCuDNN) { weights[i + 7] = 0; weights[i + 8] = 1; } - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Compute Sobel G_x operator as separable 3 x 1 and 1 x 3 convolutions. // (1) the [1 2 1] column filter vector*> sep_blob_bottom_vec; @@ -591,8 +632,8 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSobelConvolutionCuDNN) { weights_1[i + 1] = 2; weights_1[i + 2] = 1; } - layer->SetUp(sep_blob_bottom_vec, &(sep_blob_top_vec)); - layer->Forward(sep_blob_bottom_vec, &(sep_blob_top_vec)); + layer->SetUp(sep_blob_bottom_vec, sep_blob_top_vec); + layer->Forward(sep_blob_bottom_vec, sep_blob_top_vec); // (2) the [-1 0 1] row filter blob_sep->CopyFrom(*this->blob_top_2_, false, true); sep_blob_bottom_vec.clear(); @@ -613,8 +654,8 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestSobelConvolutionCuDNN) { weights_2[i + 1] = 0; weights_2[i + 2] = 1; } - layer->SetUp(sep_blob_bottom_vec, &(sep_blob_top_vec)); - layer->Forward(sep_blob_bottom_vec, &(sep_blob_top_vec)); + layer->SetUp(sep_blob_bottom_vec, sep_blob_top_vec); + layer->Forward(sep_blob_bottom_vec, sep_blob_top_vec); // Test equivalence of full and separable filters. const TypeParam* top_data = this->blob_top_->cpu_data(); const TypeParam* sep_top_data = this->blob_top_2_->cpu_data(); @@ -637,8 +678,8 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestGradientCuDNN) { convolution_param->mutable_bias_filler()->set_type("gaussian"); CuDNNConvolutionLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(CuDNNConvolutionLayerTest, TestGradientGroupCuDNN) { @@ -654,8 +695,8 @@ TYPED_TEST(CuDNNConvolutionLayerTest, TestGradientGroupCuDNN) { convolution_param->mutable_bias_filler()->set_type("gaussian"); CuDNNConvolutionLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } #endif diff --git a/src/caffe/test/test_data/generate_sample_data.py b/src/caffe/test/test_data/generate_sample_data.py index 2ab238611d8..e5dbc3406d8 100644 --- a/src/caffe/test/test_data/generate_sample_data.py +++ b/src/caffe/test/test_data/generate_sample_data.py @@ -20,12 +20,17 @@ label = 1 + np.arange(num_rows)[:, np.newaxis] label = label.astype('float32') +# We add an extra label2 dataset to test HDF5 layer's ability +# to handle arbitrary number of output ("top") Blobs. +label2 = label + 1 + print data print label with h5py.File(os.path.dirname(__file__) + '/sample_data.h5', 'w') as f: f['data'] = data f['label'] = label + f['label2'] = label2 with h5py.File(os.path.dirname(__file__) + '/sample_data_2_gzip.h5', 'w') as f: f.create_dataset( @@ -36,6 +41,10 @@ 'label', data=label, compression='gzip', compression_opts=1 ) + f.create_dataset( + 'label2', data=label2, + compression='gzip', compression_opts=1 + ) with open(os.path.dirname(__file__) + '/sample_data_list.txt', 'w') as f: f.write(os.path.dirname(__file__) + '/sample_data.h5\n') diff --git a/src/caffe/test/test_data/sample_data.h5 b/src/caffe/test/test_data/sample_data.h5 index 90eaaa56ec7..236e66b0d12 100644 Binary files a/src/caffe/test/test_data/sample_data.h5 and b/src/caffe/test/test_data/sample_data.h5 differ diff --git a/src/caffe/test/test_data/sample_data_2_gzip.h5 b/src/caffe/test/test_data/sample_data_2_gzip.h5 index ff49db4ea74..a138e0367be 100644 Binary files a/src/caffe/test/test_data/sample_data_2_gzip.h5 and b/src/caffe/test/test_data/sample_data_2_gzip.h5 differ diff --git a/src/caffe/test/test_data/sample_data_list.txt.in b/src/caffe/test/test_data/sample_data_list.txt.in deleted file mode 100644 index 9860ef583ab..00000000000 --- a/src/caffe/test/test_data/sample_data_list.txt.in +++ /dev/null @@ -1,2 +0,0 @@ -@CMAKE_SOURCE_DIR@/src/caffe/test/test_data/sample_data.h5 -@CMAKE_SOURCE_DIR@/src/caffe/test/test_data/sample_data_2_gzip.h5 \ No newline at end of file diff --git a/src/caffe/test/test_data_layer.cpp b/src/caffe/test/test_data_layer.cpp index 887124aa5bc..afe2a40d227 100644 --- a/src/caffe/test/test_data_layer.cpp +++ b/src/caffe/test/test_data_layer.cpp @@ -1,20 +1,23 @@ #include #include +#include "boost/scoped_ptr.hpp" #include "gtest/gtest.h" -#include "leveldb/db.h" #include "caffe/blob.hpp" #include "caffe/common.hpp" +#include "caffe/data_layers.hpp" #include "caffe/filler.hpp" #include "caffe/proto/caffe.pb.h" +#include "caffe/util/db.hpp" #include "caffe/util/io.hpp" -#include "caffe/vision_layers.hpp" #include "caffe/test/test_caffe_main.hpp" namespace caffe { +using boost::scoped_ptr; + template class DataLayerTest : public MultiDeviceTest { typedef typename TypeParam::Dtype Dtype; @@ -33,56 +36,15 @@ class DataLayerTest : public MultiDeviceTest { blob_top_vec_.push_back(blob_top_label_); } - // Fill the LevelDB with data: if unique_pixels, each pixel is unique but + // Fill the DB with data: if unique_pixels, each pixel is unique but // all images are the same; else each image is unique but all pixels within // an image are the same. - void FillLevelDB(const bool unique_pixels) { - backend_ = DataParameter_DB_LEVELDB; - LOG(INFO) << "Using temporary leveldb " << *filename_; - leveldb::DB* db; - leveldb::Options options; - options.error_if_exists = true; - options.create_if_missing = true; - leveldb::Status status = - leveldb::DB::Open(options, filename_->c_str(), &db); - CHECK(status.ok()); - for (int i = 0; i < 5; ++i) { - Datum datum; - datum.set_label(i); - datum.set_channels(2); - datum.set_height(3); - datum.set_width(4); - std::string* data = datum.mutable_data(); - for (int j = 0; j < 24; ++j) { - int datum = unique_pixels ? j : i; - data->push_back(static_cast(datum)); - } - stringstream ss; - ss << i; - db->Put(leveldb::WriteOptions(), ss.str(), datum.SerializeAsString()); - } - delete db; - } - - // Fill the LMDB with data: unique_pixels has same meaning as in FillLevelDB. - void FillLMDB(const bool unique_pixels) { - backend_ = DataParameter_DB_LMDB; - LOG(INFO) << "Using temporary lmdb " << *filename_; - CHECK_EQ(mkdir(filename_->c_str(), 0744), 0) << "mkdir " << filename_ - << "failed"; - MDB_env *env; - MDB_dbi dbi; - MDB_val mdbkey, mdbdata; - MDB_txn *txn; - CHECK_EQ(mdb_env_create(&env), MDB_SUCCESS) << "mdb_env_create failed"; - CHECK_EQ(mdb_env_set_mapsize(env, 1099511627776), MDB_SUCCESS) // 1TB - << "mdb_env_set_mapsize failed"; - CHECK_EQ(mdb_env_open(env, filename_->c_str(), 0, 0664), MDB_SUCCESS) - << "mdb_env_open failed"; - CHECK_EQ(mdb_txn_begin(env, NULL, 0, &txn), MDB_SUCCESS) - << "mdb_txn_begin failed"; - CHECK_EQ(mdb_open(txn, NULL, 0, &dbi), MDB_SUCCESS) << "mdb_open failed"; - + void Fill(const bool unique_pixels, DataParameter_DB backend) { + backend_ = backend; + LOG(INFO) << "Using temporary dataset " << *filename_; + scoped_ptr db(db::GetDB(backend)); + db->Open(*filename_, db::NEW); + scoped_ptr txn(db->NewTransaction()); for (int i = 0; i < 5; ++i) { Datum datum; datum.set_label(i); @@ -96,25 +58,18 @@ class DataLayerTest : public MultiDeviceTest { } stringstream ss; ss << i; - - string value; - datum.SerializeToString(&value); - mdbdata.mv_size = value.size(); - mdbdata.mv_data = reinterpret_cast(&value[0]); - string keystr = ss.str(); - mdbkey.mv_size = keystr.size(); - mdbkey.mv_data = reinterpret_cast(&keystr[0]); - CHECK_EQ(mdb_put(txn, dbi, &mdbkey, &mdbdata, 0), MDB_SUCCESS) - << "mdb_put failed"; + string out; + CHECK(datum.SerializeToString(&out)); + txn->Put(ss.str(), out); } - CHECK_EQ(mdb_txn_commit(txn), MDB_SUCCESS) << "mdb_txn_commit failed"; - mdb_close(env, dbi); - mdb_env_close(env); + txn->Commit(); + db->Close(); } void TestRead() { const Dtype scale = 3; LayerParameter param; + param.set_phase(TRAIN); DataParameter* data_param = param.mutable_data_param(); data_param->set_batch_size(5); data_param->set_source(filename_->c_str()); @@ -125,7 +80,7 @@ class DataLayerTest : public MultiDeviceTest { transform_param->set_scale(scale); DataLayer layer(param); - layer.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); EXPECT_EQ(blob_top_data_->num(), 5); EXPECT_EQ(blob_top_data_->channels(), 2); EXPECT_EQ(blob_top_data_->height(), 3); @@ -136,7 +91,7 @@ class DataLayerTest : public MultiDeviceTest { EXPECT_EQ(blob_top_label_->width(), 1); for (int iter = 0; iter < 100; ++iter) { - layer.Forward(blob_bottom_vec_, &blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); for (int i = 0; i < 5; ++i) { EXPECT_EQ(i, blob_top_label_->cpu_data()[i]); } @@ -149,9 +104,75 @@ class DataLayerTest : public MultiDeviceTest { } } - void TestReadCrop() { + void TestReshape(DataParameter_DB backend) { + const int num_inputs = 5; + // Save data of varying shapes. + LOG(INFO) << "Using temporary dataset " << *filename_; + scoped_ptr db(db::GetDB(backend)); + db->Open(*filename_, db::NEW); + scoped_ptr txn(db->NewTransaction()); + for (int i = 0; i < num_inputs; ++i) { + Datum datum; + datum.set_label(i); + datum.set_channels(2); + datum.set_height(i % 2 + 1); + datum.set_width(i % 4 + 1); + std::string* data = datum.mutable_data(); + const int data_size = datum.channels() * datum.height() * datum.width(); + for (int j = 0; j < data_size; ++j) { + data->push_back(static_cast(j)); + } + stringstream ss; + ss << i; + string out; + CHECK(datum.SerializeToString(&out)); + txn->Put(ss.str(), out); + } + txn->Commit(); + db->Close(); + + // Load and check data of various shapes. + LayerParameter param; + param.set_phase(TEST); + DataParameter* data_param = param.mutable_data_param(); + data_param->set_batch_size(1); + data_param->set_source(filename_->c_str()); + data_param->set_backend(backend); + + DataLayer layer(param); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); + EXPECT_EQ(blob_top_data_->num(), 1); + EXPECT_EQ(blob_top_data_->channels(), 2); + EXPECT_EQ(blob_top_label_->num(), 1); + EXPECT_EQ(blob_top_label_->channels(), 1); + EXPECT_EQ(blob_top_label_->height(), 1); + EXPECT_EQ(blob_top_label_->width(), 1); + + for (int iter = 0; iter < num_inputs; ++iter) { + layer.Forward(blob_bottom_vec_, blob_top_vec_); + EXPECT_EQ(blob_top_data_->height(), iter % 2 + 1); + EXPECT_EQ(blob_top_data_->width(), iter % 4 + 1); + EXPECT_EQ(iter, blob_top_label_->cpu_data()[0]); + const int channels = blob_top_data_->channels(); + const int height = blob_top_data_->height(); + const int width = blob_top_data_->width(); + for (int c = 0; c < channels; ++c) { + for (int h = 0; h < height; ++h) { + for (int w = 0; w < width; ++w) { + const int idx = (c * height + h) * width + w; + EXPECT_EQ(idx, static_cast(blob_top_data_->cpu_data()[idx])) + << "debug: iter " << iter << " c " << c + << " h " << h << " w " << w; + } + } + } + } + } + + void TestReadCrop(Phase phase) { const Dtype scale = 3; LayerParameter param; + param.set_phase(phase); Caffe::set_random_seed(1701); DataParameter* data_param = param.mutable_data_param(); @@ -165,7 +186,7 @@ class DataLayerTest : public MultiDeviceTest { transform_param->set_crop_size(1); DataLayer layer(param); - layer.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); EXPECT_EQ(blob_top_data_->num(), 5); EXPECT_EQ(blob_top_data_->channels(), 2); EXPECT_EQ(blob_top_data_->height(), 1); @@ -176,7 +197,7 @@ class DataLayerTest : public MultiDeviceTest { EXPECT_EQ(blob_top_label_->width(), 1); for (int iter = 0; iter < 2; ++iter) { - layer.Forward(blob_bottom_vec_, &blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); for (int i = 0; i < 5; ++i) { EXPECT_EQ(i, blob_top_label_->cpu_data()[i]); } @@ -187,7 +208,7 @@ class DataLayerTest : public MultiDeviceTest { num_with_center_value += (center_value == blob_top_data_->cpu_data()[i * 2 + j]); // At TEST time, check that we always get center value. - if (Caffe::phase() == Caffe::TEST) { + if (phase == caffe::TEST) { EXPECT_EQ(center_value, this->blob_top_data_->cpu_data()[i * 2 + j]) << "debug: iter " << iter << " i " << i << " j " << j; } @@ -196,7 +217,7 @@ class DataLayerTest : public MultiDeviceTest { // At TRAIN time, check that we did not get the center crop all 10 times. // (This check fails with probability 1-1/12^10 in a correct // implementation, so we call set_random_seed.) - if (Caffe::phase() == Caffe::TRAIN) { + if (phase == caffe::TRAIN) { EXPECT_LT(num_with_center_value, 10); } } @@ -204,6 +225,7 @@ class DataLayerTest : public MultiDeviceTest { void TestReadCropTrainSequenceSeeded() { LayerParameter param; + param.set_phase(TRAIN); DataParameter* data_param = param.mutable_data_param(); data_param->set_batch_size(5); data_param->set_source(filename_->c_str()); @@ -219,9 +241,9 @@ class DataLayerTest : public MultiDeviceTest { vector > crop_sequence; { DataLayer layer1(param); - layer1.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer1.SetUp(blob_bottom_vec_, blob_top_vec_); for (int iter = 0; iter < 2; ++iter) { - layer1.Forward(blob_bottom_vec_, &blob_top_vec_); + layer1.Forward(blob_bottom_vec_, blob_top_vec_); for (int i = 0; i < 5; ++i) { EXPECT_EQ(i, blob_top_label_->cpu_data()[i]); } @@ -234,15 +256,15 @@ class DataLayerTest : public MultiDeviceTest { } crop_sequence.push_back(iter_crop_sequence); } - } // destroy 1st data layer and unlock the leveldb + } // destroy 1st data layer and unlock the db // Get crop sequence after reseeding Caffe with 1701. // Check that the sequence is the same as the original. Caffe::set_random_seed(seed_); DataLayer layer2(param); - layer2.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer2.SetUp(blob_bottom_vec_, blob_top_vec_); for (int iter = 0; iter < 2; ++iter) { - layer2.Forward(blob_bottom_vec_, &blob_top_vec_); + layer2.Forward(blob_bottom_vec_, blob_top_vec_); for (int i = 0; i < 5; ++i) { EXPECT_EQ(i, blob_top_label_->cpu_data()[i]); } @@ -258,6 +280,7 @@ class DataLayerTest : public MultiDeviceTest { void TestReadCropTrainSequenceUnseeded() { LayerParameter param; + param.set_phase(TRAIN); DataParameter* data_param = param.mutable_data_param(); data_param->set_batch_size(5); data_param->set_source(filename_->c_str()); @@ -274,9 +297,9 @@ class DataLayerTest : public MultiDeviceTest { vector > crop_sequence; { DataLayer layer1(param); - layer1.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer1.SetUp(blob_bottom_vec_, blob_top_vec_); for (int iter = 0; iter < 2; ++iter) { - layer1.Forward(blob_bottom_vec_, &blob_top_vec_); + layer1.Forward(blob_bottom_vec_, blob_top_vec_); for (int i = 0; i < 5; ++i) { EXPECT_EQ(i, blob_top_label_->cpu_data()[i]); } @@ -289,15 +312,15 @@ class DataLayerTest : public MultiDeviceTest { } crop_sequence.push_back(iter_crop_sequence); } - } // destroy 1st data layer and unlock the leveldb + } // destroy 1st data layer and unlock the db // Get crop sequence continuing from previous Caffe RNG state; reseed // srand with 1701. Check that the sequence differs from the original. srand(seed_); DataLayer layer2(param); - layer2.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer2.SetUp(blob_bottom_vec_, blob_top_vec_); for (int iter = 0; iter < 2; ++iter) { - layer2.Forward(blob_bottom_vec_, &blob_top_vec_); + layer2.Forward(blob_bottom_vec_, blob_top_vec_); for (int i = 0; i < 5; ++i) { EXPECT_EQ(i, blob_top_label_->cpu_data()[i]); } @@ -327,78 +350,78 @@ TYPED_TEST_CASE(DataLayerTest, TestDtypesAndDevices); TYPED_TEST(DataLayerTest, TestReadLevelDB) { const bool unique_pixels = false; // all pixels the same; images different - this->FillLevelDB(unique_pixels); + this->Fill(unique_pixels, DataParameter_DB_LEVELDB); this->TestRead(); } +TYPED_TEST(DataLayerTest, TestReshapeLevelDB) { + this->TestReshape(DataParameter_DB_LEVELDB); +} + TYPED_TEST(DataLayerTest, TestReadCropTrainLevelDB) { - Caffe::set_phase(Caffe::TRAIN); const bool unique_pixels = true; // all images the same; pixels different - this->FillLevelDB(unique_pixels); - this->TestReadCrop(); + this->Fill(unique_pixels, DataParameter_DB_LEVELDB); + this->TestReadCrop(TRAIN); } // Test that the sequence of random crops is consistent when using // Caffe::set_random_seed. TYPED_TEST(DataLayerTest, TestReadCropTrainSequenceSeededLevelDB) { - Caffe::set_phase(Caffe::TRAIN); const bool unique_pixels = true; // all images the same; pixels different - this->FillLevelDB(unique_pixels); + this->Fill(unique_pixels, DataParameter_DB_LEVELDB); this->TestReadCropTrainSequenceSeeded(); } // Test that the sequence of random crops differs across iterations when // Caffe::set_random_seed isn't called (and seeds from srand are ignored). TYPED_TEST(DataLayerTest, TestReadCropTrainSequenceUnseededLevelDB) { - Caffe::set_phase(Caffe::TRAIN); const bool unique_pixels = true; // all images the same; pixels different - this->FillLevelDB(unique_pixels); + this->Fill(unique_pixels, DataParameter_DB_LEVELDB); this->TestReadCropTrainSequenceUnseeded(); } TYPED_TEST(DataLayerTest, TestReadCropTestLevelDB) { - Caffe::set_phase(Caffe::TEST); const bool unique_pixels = true; // all images the same; pixels different - this->FillLevelDB(unique_pixels); - this->TestReadCrop(); + this->Fill(unique_pixels, DataParameter_DB_LEVELDB); + this->TestReadCrop(TEST); } TYPED_TEST(DataLayerTest, TestReadLMDB) { const bool unique_pixels = false; // all pixels the same; images different - this->FillLMDB(unique_pixels); + this->Fill(unique_pixels, DataParameter_DB_LMDB); this->TestRead(); } +TYPED_TEST(DataLayerTest, TestReshapeLMDB) { + this->TestReshape(DataParameter_DB_LMDB); +} + TYPED_TEST(DataLayerTest, TestReadCropTrainLMDB) { - Caffe::set_phase(Caffe::TRAIN); const bool unique_pixels = true; // all images the same; pixels different - this->FillLMDB(unique_pixels); - this->TestReadCrop(); + this->Fill(unique_pixels, DataParameter_DB_LMDB); + this->TestReadCrop(TRAIN); } // Test that the sequence of random crops is consistent when using // Caffe::set_random_seed. TYPED_TEST(DataLayerTest, TestReadCropTrainSequenceSeededLMDB) { - Caffe::set_phase(Caffe::TRAIN); const bool unique_pixels = true; // all images the same; pixels different - this->FillLMDB(unique_pixels); + this->Fill(unique_pixels, DataParameter_DB_LMDB); this->TestReadCropTrainSequenceSeeded(); } // Test that the sequence of random crops differs across iterations when // Caffe::set_random_seed isn't called (and seeds from srand are ignored). TYPED_TEST(DataLayerTest, TestReadCropTrainSequenceUnseededLMDB) { - Caffe::set_phase(Caffe::TRAIN); const bool unique_pixels = true; // all images the same; pixels different - this->FillLMDB(unique_pixels); + this->Fill(unique_pixels, DataParameter_DB_LMDB); this->TestReadCropTrainSequenceUnseeded(); } TYPED_TEST(DataLayerTest, TestReadCropTestLMDB) { - Caffe::set_phase(Caffe::TEST); const bool unique_pixels = true; // all images the same; pixels different - this->FillLMDB(unique_pixels); - this->TestReadCrop(); + this->Fill(unique_pixels, DataParameter_DB_LMDB); + this->TestReadCrop(TEST); } } // namespace caffe diff --git a/src/caffe/test/test_data_transformer.cpp b/src/caffe/test/test_data_transformer.cpp new file mode 100644 index 00000000000..16570e20356 --- /dev/null +++ b/src/caffe/test/test_data_transformer.cpp @@ -0,0 +1,355 @@ +#include +#include + +#include "gtest/gtest.h" +#include "leveldb/db.h" + +#include "caffe/blob.hpp" +#include "caffe/common.hpp" +#include "caffe/data_transformer.hpp" +#include "caffe/filler.hpp" +#include "caffe/proto/caffe.pb.h" +#include "caffe/util/io.hpp" + +#include "caffe/test/test_caffe_main.hpp" + +namespace caffe { + +void FillDatum(const int label, const int channels, const int height, + const int width, const bool unique_pixels, Datum * datum) { + datum->set_label(label); + datum->set_channels(channels); + datum->set_height(height); + datum->set_width(width); + int size = channels * height * width; + std::string* data = datum->mutable_data(); + for (int j = 0; j < size; ++j) { + int datum = unique_pixels ? j : label; + data->push_back(static_cast(datum)); + } +} + +template +class DataTransformTest : public ::testing::Test { + protected: + DataTransformTest() + : seed_(1701), + num_iter_(10) {} + + int NumSequenceMatches(const TransformationParameter transform_param, + const Datum& datum, Phase phase) { + // Get crop sequence with Caffe seed 1701. + DataTransformer* transformer = + new DataTransformer(transform_param, phase); + const int crop_size = transform_param.crop_size(); + Caffe::set_random_seed(seed_); + transformer->InitRand(); + Blob* blob = + new Blob(1, datum.channels(), datum.height(), datum.width()); + if (transform_param.crop_size() > 0) { + blob->Reshape(1, datum.channels(), crop_size, crop_size); + } + + vector > crop_sequence; + for (int iter = 0; iter < this->num_iter_; ++iter) { + vector iter_crop_sequence; + transformer->Transform(datum, blob); + for (int j = 0; j < blob->count(); ++j) { + iter_crop_sequence.push_back(blob->cpu_data()[j]); + } + crop_sequence.push_back(iter_crop_sequence); + } + // Check if the sequence differs from the previous + int num_sequence_matches = 0; + for (int iter = 0; iter < this->num_iter_; ++iter) { + vector iter_crop_sequence = crop_sequence[iter]; + transformer->Transform(datum, blob); + for (int j = 0; j < blob->count(); ++j) { + num_sequence_matches += + (crop_sequence[iter][j] == blob->cpu_data()[j]); + } + } + return num_sequence_matches; + } + + virtual ~DataTransformTest() { } + + int seed_; + int num_iter_; +}; + +TYPED_TEST_CASE(DataTransformTest, TestDtypes); + +TYPED_TEST(DataTransformTest, TestEmptyTransform) { + TransformationParameter transform_param; + const bool unique_pixels = false; // all pixels the same equal to label + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + Blob* blob = new Blob(1, channels, height, width); + DataTransformer* transformer = + new DataTransformer(transform_param, TEST); + transformer->InitRand(); + transformer->Transform(datum, blob); + EXPECT_EQ(blob->num(), 1); + EXPECT_EQ(blob->channels(), datum.channels()); + EXPECT_EQ(blob->height(), datum.height()); + EXPECT_EQ(blob->width(), datum.width()); + for (int j = 0; j < blob->count(); ++j) { + EXPECT_EQ(blob->cpu_data()[j], label); + } +} + +TYPED_TEST(DataTransformTest, TestEmptyTransformUniquePixels) { + TransformationParameter transform_param; + const bool unique_pixels = true; // pixels are consecutive ints [0,size] + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + Blob* blob = new Blob(1, 3, 4, 5); + DataTransformer* transformer = + new DataTransformer(transform_param, TEST); + transformer->InitRand(); + transformer->Transform(datum, blob); + EXPECT_EQ(blob->num(), 1); + EXPECT_EQ(blob->channels(), datum.channels()); + EXPECT_EQ(blob->height(), datum.height()); + EXPECT_EQ(blob->width(), datum.width()); + for (int j = 0; j < blob->count(); ++j) { + EXPECT_EQ(blob->cpu_data()[j], j); + } +} + +TYPED_TEST(DataTransformTest, TestCropSize) { + TransformationParameter transform_param; + const bool unique_pixels = false; // all pixels the same equal to label + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int crop_size = 2; + + transform_param.set_crop_size(crop_size); + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + DataTransformer* transformer = + new DataTransformer(transform_param, TEST); + transformer->InitRand(); + Blob* blob = + new Blob(1, channels, crop_size, crop_size); + for (int iter = 0; iter < this->num_iter_; ++iter) { + transformer->Transform(datum, blob); + EXPECT_EQ(blob->num(), 1); + EXPECT_EQ(blob->channels(), datum.channels()); + EXPECT_EQ(blob->height(), crop_size); + EXPECT_EQ(blob->width(), crop_size); + for (int j = 0; j < blob->count(); ++j) { + EXPECT_EQ(blob->cpu_data()[j], label); + } + } +} + +TYPED_TEST(DataTransformTest, TestCropTrain) { + TransformationParameter transform_param; + const bool unique_pixels = true; // pixels are consecutive ints [0,size] + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int crop_size = 2; + const int size = channels * crop_size * crop_size; + + transform_param.set_crop_size(crop_size); + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + int num_matches = this->NumSequenceMatches(transform_param, datum, TRAIN); + EXPECT_LT(num_matches, size * this->num_iter_); +} + +TYPED_TEST(DataTransformTest, TestCropTest) { + TransformationParameter transform_param; + const bool unique_pixels = true; // pixels are consecutive ints [0,size] + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int crop_size = 2; + const int size = channels * crop_size * crop_size; + + transform_param.set_crop_size(crop_size); + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + int num_matches = this->NumSequenceMatches(transform_param, datum, TEST); + EXPECT_EQ(num_matches, size * this->num_iter_); +} + +TYPED_TEST(DataTransformTest, TestMirrorTrain) { + TransformationParameter transform_param; + const bool unique_pixels = true; // pixels are consecutive ints [0,size] + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int size = channels * height * width; + + transform_param.set_mirror(true); + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + int num_matches = this->NumSequenceMatches(transform_param, datum, TRAIN); + EXPECT_LT(num_matches, size * this->num_iter_); +} + +TYPED_TEST(DataTransformTest, TestMirrorTest) { + TransformationParameter transform_param; + const bool unique_pixels = true; // pixels are consecutive ints [0,size] + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int size = channels * height * width; + + transform_param.set_mirror(true); + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + int num_matches = this->NumSequenceMatches(transform_param, datum, TEST); + EXPECT_LT(num_matches, size * this->num_iter_); +} + +TYPED_TEST(DataTransformTest, TestCropMirrorTrain) { + TransformationParameter transform_param; + const bool unique_pixels = true; // pixels are consecutive ints [0,size] + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int crop_size = 2; + + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + transform_param.set_crop_size(crop_size); + int num_matches_crop = this->NumSequenceMatches( + transform_param, datum, TRAIN); + + transform_param.set_mirror(true); + int num_matches_crop_mirror = + this->NumSequenceMatches(transform_param, datum, TRAIN); + // When doing crop and mirror we expect less num_matches than just crop + EXPECT_LE(num_matches_crop_mirror, num_matches_crop); +} + +TYPED_TEST(DataTransformTest, TestCropMirrorTest) { + TransformationParameter transform_param; + const bool unique_pixels = true; // pixels are consecutive ints [0,size] + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int crop_size = 2; + + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + transform_param.set_crop_size(crop_size); + int num_matches_crop = this->NumSequenceMatches(transform_param, datum, TEST); + + transform_param.set_mirror(true); + int num_matches_crop_mirror = + this->NumSequenceMatches(transform_param, datum, TEST); + // When doing crop and mirror we expect less num_matches than just crop + EXPECT_LT(num_matches_crop_mirror, num_matches_crop); +} + + +TYPED_TEST(DataTransformTest, TestMeanValue) { + TransformationParameter transform_param; + const bool unique_pixels = false; // pixels are equal to label + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int mean_value = 2; + + transform_param.add_mean_value(mean_value); + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + Blob* blob = new Blob(1, channels, height, width); + DataTransformer* transformer = + new DataTransformer(transform_param, TEST); + transformer->InitRand(); + transformer->Transform(datum, blob); + for (int j = 0; j < blob->count(); ++j) { + EXPECT_EQ(blob->cpu_data()[j], label - mean_value); + } +} + +TYPED_TEST(DataTransformTest, TestMeanValues) { + TransformationParameter transform_param; + const bool unique_pixels = false; // pixels are equal to label + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + + transform_param.add_mean_value(0); + transform_param.add_mean_value(1); + transform_param.add_mean_value(2); + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + Blob* blob = new Blob(1, channels, height, width); + DataTransformer* transformer = + new DataTransformer(transform_param, TEST); + transformer->InitRand(); + transformer->Transform(datum, blob); + for (int c = 0; c < channels; ++c) { + for (int j = 0; j < height * width; ++j) { + EXPECT_EQ(blob->cpu_data()[blob->offset(0, c) + j], label - c); + } + } +} + +TYPED_TEST(DataTransformTest, TestMeanFile) { + TransformationParameter transform_param; + const bool unique_pixels = true; // pixels are consecutive ints [0,size] + const int label = 0; + const int channels = 3; + const int height = 4; + const int width = 5; + const int size = channels * height * width; + + // Create a mean file + string* mean_file = new string(); + MakeTempFilename(mean_file); + BlobProto blob_mean; + blob_mean.set_num(1); + blob_mean.set_channels(channels); + blob_mean.set_height(height); + blob_mean.set_width(width); + + for (int j = 0; j < size; ++j) { + blob_mean.add_data(j); + } + + LOG(INFO) << "Using temporary mean_file " << *mean_file; + WriteProtoToBinaryFile(blob_mean, *mean_file); + + transform_param.set_mean_file(*mean_file); + Datum datum; + FillDatum(label, channels, height, width, unique_pixels, &datum); + Blob* blob = new Blob(1, channels, height, width); + DataTransformer* transformer = + new DataTransformer(transform_param, TEST); + transformer->InitRand(); + transformer->Transform(datum, blob); + for (int j = 0; j < blob->count(); ++j) { + EXPECT_EQ(blob->cpu_data()[j], 0); + } +} + +} // namespace caffe diff --git a/src/caffe/test/test_db.cpp b/src/caffe/test/test_db.cpp new file mode 100644 index 00000000000..5b2ac230a0b --- /dev/null +++ b/src/caffe/test/test_db.cpp @@ -0,0 +1,134 @@ +#include + +#include "boost/scoped_ptr.hpp" +#include "gtest/gtest.h" + +#include "caffe/common.hpp" +#include "caffe/proto/caffe.pb.h" +#include "caffe/util/db.hpp" +#include "caffe/util/io.hpp" + +#include "caffe/test/test_caffe_main.hpp" + +namespace caffe { + +using boost::scoped_ptr; + +template +class DBTest : public ::testing::Test { + protected: + DBTest() + : backend_(TypeParam::backend), + root_images_(string(EXAMPLES_SOURCE_DIR) + string("images/")) {} + + virtual void SetUp() { + MakeTempDir(&source_); + source_ += "/db"; + string keys[] = {"cat.jpg", "fish-bike.jpg"}; + LOG(INFO) << "Using temporary db " << source_; + scoped_ptr db(db::GetDB(TypeParam::backend)); + db->Open(this->source_, db::NEW); + scoped_ptr txn(db->NewTransaction()); + for (int i = 0; i < 2; ++i) { + Datum datum; + ReadImageToDatum(root_images_ + keys[i], i, &datum); + string out; + CHECK(datum.SerializeToString(&out)); + txn->Put(keys[i], out); + } + txn->Commit(); + } + + virtual ~DBTest() { } + + DataParameter_DB backend_; + string source_; + string root_images_; +}; + +struct TypeLevelDB { + static DataParameter_DB backend; +}; +DataParameter_DB TypeLevelDB::backend = DataParameter_DB_LEVELDB; + +struct TypeLMDB { + static DataParameter_DB backend; +}; +DataParameter_DB TypeLMDB::backend = DataParameter_DB_LMDB; + +// typedef ::testing::Types TestTypes; +typedef ::testing::Types TestTypes; + +TYPED_TEST_CASE(DBTest, TestTypes); + +TYPED_TEST(DBTest, TestGetDB) { + scoped_ptr db(db::GetDB(TypeParam::backend)); +} + +TYPED_TEST(DBTest, TestNext) { + scoped_ptr db(db::GetDB(TypeParam::backend)); + db->Open(this->source_, db::READ); + scoped_ptr cursor(db->NewCursor()); + EXPECT_TRUE(cursor->valid()); + cursor->Next(); + EXPECT_TRUE(cursor->valid()); + cursor->Next(); + EXPECT_FALSE(cursor->valid()); +} + +TYPED_TEST(DBTest, TestSeekToFirst) { + scoped_ptr db(db::GetDB(TypeParam::backend)); + db->Open(this->source_, db::READ); + scoped_ptr cursor(db->NewCursor()); + cursor->Next(); + cursor->SeekToFirst(); + EXPECT_TRUE(cursor->valid()); + string key = cursor->key(); + Datum datum; + datum.ParseFromString(cursor->value()); + EXPECT_EQ(key, "cat.jpg"); + EXPECT_EQ(datum.channels(), 3); + EXPECT_EQ(datum.height(), 360); + EXPECT_EQ(datum.width(), 480); +} + +TYPED_TEST(DBTest, TestKeyValue) { + scoped_ptr db(db::GetDB(TypeParam::backend)); + db->Open(this->source_, db::READ); + scoped_ptr cursor(db->NewCursor()); + EXPECT_TRUE(cursor->valid()); + string key = cursor->key(); + Datum datum; + datum.ParseFromString(cursor->value()); + EXPECT_EQ(key, "cat.jpg"); + EXPECT_EQ(datum.channels(), 3); + EXPECT_EQ(datum.height(), 360); + EXPECT_EQ(datum.width(), 480); + cursor->Next(); + EXPECT_TRUE(cursor->valid()); + key = cursor->key(); + datum.ParseFromString(cursor->value()); + EXPECT_EQ(key, "fish-bike.jpg"); + EXPECT_EQ(datum.channels(), 3); + EXPECT_EQ(datum.height(), 323); + EXPECT_EQ(datum.width(), 481); + cursor->Next(); + EXPECT_FALSE(cursor->valid()); +} + +TYPED_TEST(DBTest, TestWrite) { + scoped_ptr db(db::GetDB(TypeParam::backend)); + db->Open(this->source_, db::WRITE); + scoped_ptr txn(db->NewTransaction()); + Datum datum; + ReadFileToDatum(this->root_images_ + "cat.jpg", 0, &datum); + string out; + CHECK(datum.SerializeToString(&out)); + txn->Put("cat.jpg", out); + ReadFileToDatum(this->root_images_ + "fish-bike.jpg", 1, &datum); + CHECK(datum.SerializeToString(&out)); + txn->Put("fish-bike.jpg", out); + txn->Commit(); +} + +} // namespace caffe diff --git a/src/caffe/test/test_deconvolution_layer.cpp b/src/caffe/test/test_deconvolution_layer.cpp new file mode 100644 index 00000000000..fc63d5efbe3 --- /dev/null +++ b/src/caffe/test/test_deconvolution_layer.cpp @@ -0,0 +1,158 @@ +#include +#include + +#include "gtest/gtest.h" + +#include "caffe/blob.hpp" +#include "caffe/common.hpp" +#include "caffe/filler.hpp" +#include "caffe/vision_layers.hpp" + +#include "caffe/test/test_caffe_main.hpp" +#include "caffe/test/test_gradient_check_util.hpp" + +namespace caffe { + +// Since ConvolutionLayerTest checks the shared conv/deconv code in detail, +// we'll just do a simple forward test and a gradient check. +template +class DeconvolutionLayerTest : public MultiDeviceTest { + typedef typename TypeParam::Dtype Dtype; + + protected: + DeconvolutionLayerTest() + : blob_bottom_(new Blob(2, 3, 6, 4)), + blob_bottom_2_(new Blob(2, 3, 6, 4)), + blob_top_(new Blob()), + blob_top_2_(new Blob()) {} + virtual void SetUp() { + // fill the values + FillerParameter filler_param; + filler_param.set_value(1.); + GaussianFiller filler(filler_param); + filler.Fill(this->blob_bottom_); + filler.Fill(this->blob_bottom_2_); + blob_bottom_vec_.push_back(blob_bottom_); + blob_top_vec_.push_back(blob_top_); + } + + virtual ~DeconvolutionLayerTest() { + delete blob_bottom_; + delete blob_bottom_2_; + delete blob_top_; + delete blob_top_2_; + } + + Blob* const blob_bottom_; + Blob* const blob_bottom_2_; + Blob* const blob_top_; + Blob* const blob_top_2_; + vector*> blob_bottom_vec_; + vector*> blob_top_vec_; +}; + +TYPED_TEST_CASE(DeconvolutionLayerTest, TestDtypesAndDevices); + +TYPED_TEST(DeconvolutionLayerTest, TestSetup) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + ConvolutionParameter* convolution_param = + layer_param.mutable_convolution_param(); + convolution_param->set_kernel_size(3); + convolution_param->set_stride(2); + convolution_param->set_num_output(4); + this->blob_bottom_vec_.push_back(this->blob_bottom_2_); + this->blob_top_vec_.push_back(this->blob_top_2_); + shared_ptr > layer( + new DeconvolutionLayer(layer_param)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + EXPECT_EQ(this->blob_top_->num(), 2); + EXPECT_EQ(this->blob_top_->channels(), 4); + EXPECT_EQ(this->blob_top_->height(), 13); + EXPECT_EQ(this->blob_top_->width(), 9); + EXPECT_EQ(this->blob_top_2_->num(), 2); + EXPECT_EQ(this->blob_top_2_->channels(), 4); + EXPECT_EQ(this->blob_top_2_->height(), 13); + EXPECT_EQ(this->blob_top_2_->width(), 9); + // setting group should not change the shape + convolution_param->set_num_output(3); + convolution_param->set_group(3); + layer.reset(new DeconvolutionLayer(layer_param)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + EXPECT_EQ(this->blob_top_->num(), 2); + EXPECT_EQ(this->blob_top_->channels(), 3); + EXPECT_EQ(this->blob_top_->height(), 13); + EXPECT_EQ(this->blob_top_->width(), 9); + EXPECT_EQ(this->blob_top_2_->num(), 2); + EXPECT_EQ(this->blob_top_2_->channels(), 3); + EXPECT_EQ(this->blob_top_2_->height(), 13); + EXPECT_EQ(this->blob_top_2_->width(), 9); +} + +TYPED_TEST(DeconvolutionLayerTest, TestSimpleDeconvolution) { + typedef typename TypeParam::Dtype Dtype; + this->blob_bottom_vec_.push_back(this->blob_bottom_2_); + this->blob_top_vec_.push_back(this->blob_top_2_); + LayerParameter layer_param; + ConvolutionParameter* convolution_param = + layer_param.mutable_convolution_param(); + convolution_param->set_kernel_size(3); + convolution_param->set_stride(2); + convolution_param->set_num_output(4); + convolution_param->mutable_weight_filler()->set_type("constant"); + convolution_param->mutable_weight_filler()->set_value(1); + convolution_param->mutable_bias_filler()->set_type("constant"); + convolution_param->mutable_bias_filler()->set_value(0.1); + shared_ptr > layer( + new DeconvolutionLayer(layer_param)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + // constant-fill the bottom blobs + FillerParameter filler_param; + filler_param.set_value(1.); + ConstantFiller filler(filler_param); + filler.Fill(this->blob_bottom_); + filler.Fill(this->blob_bottom_2_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); + // simply check that accumulation works with overlapping filters + const Dtype* top_data = this->blob_top_->cpu_data(); + for (int n = 0; n < this->blob_top_->num(); ++n) { + for (int c = 0; c < this->blob_top_->channels(); ++c) { + for (int h = 0; h < this->blob_top_->height(); ++h) { + for (int w = 0; w < this->blob_top_->width(); ++w) { + Dtype expected = 3.1; + bool h_overlap = h % 2 == 0 && h > 0 + && h < this->blob_top_->height() - 1; + bool w_overlap = w % 2 == 0 && w > 0 + && w < this->blob_top_->width() - 1; + if (h_overlap && w_overlap) { + expected += 9; + } else if (h_overlap || w_overlap) { + expected += 3; + } + EXPECT_NEAR(top_data[this->blob_top_->offset(n, c, h, w)], + expected, 1e-4); + } + } + } + } +} + +TYPED_TEST(DeconvolutionLayerTest, TestGradient) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + ConvolutionParameter* convolution_param = + layer_param.mutable_convolution_param(); + this->blob_bottom_vec_.push_back(this->blob_bottom_2_); + this->blob_top_vec_.push_back(this->blob_top_2_); + convolution_param->set_kernel_size(2); + convolution_param->set_stride(1); + convolution_param->set_num_output(1); + convolution_param->mutable_weight_filler()->set_type("gaussian"); + convolution_param->mutable_bias_filler()->set_type("gaussian"); + DeconvolutionLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-3); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); +} + +} // namespace caffe diff --git a/src/caffe/test/test_dummy_data_layer.cpp b/src/caffe/test/test_dummy_data_layer.cpp index 4188bb68c9e..99548352746 100644 --- a/src/caffe/test/test_dummy_data_layer.cpp +++ b/src/caffe/test/test_dummy_data_layer.cpp @@ -10,9 +10,6 @@ #include "caffe/test/test_caffe_main.hpp" -using std::string; -using std::stringstream; - namespace caffe { template @@ -56,7 +53,7 @@ TYPED_TEST(DummyDataLayerTest, TestOneTopConstant) { dummy_data_param->add_width(4); this->blob_top_vec_.resize(1); DummyDataLayer layer(param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_a_->num(), 5); EXPECT_EQ(this->blob_top_a_->channels(), 3); EXPECT_EQ(this->blob_top_a_->height(), 2); @@ -68,7 +65,7 @@ TYPED_TEST(DummyDataLayerTest, TestOneTopConstant) { EXPECT_EQ(0, this->blob_top_vec_[i]->cpu_data()[j]); } } - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_top_vec_.size(); ++i) { for (int j = 0; j < this->blob_top_vec_[i]->count(); ++j) { EXPECT_EQ(0, this->blob_top_vec_[i]->cpu_data()[j]); @@ -92,7 +89,7 @@ TYPED_TEST(DummyDataLayerTest, TestTwoTopConstant) { data_filler_param->set_value(7); this->blob_top_vec_.resize(2); DummyDataLayer layer(param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_a_->num(), 5); EXPECT_EQ(this->blob_top_a_->channels(), 3); EXPECT_EQ(this->blob_top_a_->height(), 2); @@ -107,7 +104,7 @@ TYPED_TEST(DummyDataLayerTest, TestTwoTopConstant) { EXPECT_EQ(7, this->blob_top_vec_[i]->cpu_data()[j]); } } - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_top_vec_.size(); ++i) { for (int j = 0; j < this->blob_top_vec_[i]->count(); ++j) { EXPECT_EQ(7, this->blob_top_vec_[i]->cpu_data()[j]); @@ -134,7 +131,7 @@ TYPED_TEST(DummyDataLayerTest, TestThreeTopConstantGaussianConstant) { FillerParameter* data_filler_param_c = dummy_data_param->add_data_filler(); data_filler_param_c->set_value(9); DummyDataLayer layer(param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_a_->num(), 5); EXPECT_EQ(this->blob_top_a_->channels(), 3); EXPECT_EQ(this->blob_top_a_->height(), 2); @@ -160,7 +157,7 @@ TYPED_TEST(DummyDataLayerTest, TestThreeTopConstantGaussianConstant) { } // Do a Forward pass to fill in Blob b with Gaussian data. - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_top_a_->count(); ++i) { EXPECT_EQ(7, this->blob_top_a_->cpu_data()[i]); } @@ -180,7 +177,7 @@ TYPED_TEST(DummyDataLayerTest, TestThreeTopConstantGaussianConstant) { // Do another Forward pass to fill in Blob b with Gaussian data again, // checking that we get different values. - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_top_a_->count(); ++i) { EXPECT_EQ(7, this->blob_top_a_->cpu_data()[i]); } diff --git a/src/caffe/test/test_eltwise_layer.cpp b/src/caffe/test/test_eltwise_layer.cpp index d5cf08229ab..be0c1347709 100644 --- a/src/caffe/test/test_eltwise_layer.cpp +++ b/src/caffe/test/test_eltwise_layer.cpp @@ -58,7 +58,7 @@ TYPED_TEST(EltwiseLayerTest, TestSetUp) { eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD); shared_ptr > layer( new EltwiseLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 3); EXPECT_EQ(this->blob_top_->height(), 4); @@ -72,8 +72,8 @@ TYPED_TEST(EltwiseLayerTest, TestProd) { eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD); shared_ptr > layer( new EltwiseLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); const Dtype* in_data_a = this->blob_bottom_a_->cpu_data(); @@ -91,8 +91,8 @@ TYPED_TEST(EltwiseLayerTest, TestSum) { eltwise_param->set_operation(EltwiseParameter_EltwiseOp_SUM); shared_ptr > layer( new EltwiseLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); const Dtype* in_data_a = this->blob_bottom_a_->cpu_data(); @@ -113,8 +113,8 @@ TYPED_TEST(EltwiseLayerTest, TestSumCoeff) { eltwise_param->add_coeff(2); shared_ptr > layer( new EltwiseLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); const Dtype* in_data_a = this->blob_bottom_a_->cpu_data(); @@ -134,8 +134,8 @@ TYPED_TEST(EltwiseLayerTest, TestStableProdGradient) { eltwise_param->set_stable_prod_grad(true); EltwiseLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(EltwiseLayerTest, TestUnstableProdGradient) { @@ -146,8 +146,8 @@ TYPED_TEST(EltwiseLayerTest, TestUnstableProdGradient) { eltwise_param->set_stable_prod_grad(false); EltwiseLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(EltwiseLayerTest, TestSumGradient) { @@ -157,8 +157,8 @@ TYPED_TEST(EltwiseLayerTest, TestSumGradient) { eltwise_param->set_operation(EltwiseParameter_EltwiseOp_SUM); EltwiseLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(EltwiseLayerTest, TestSumCoeffGradient) { @@ -171,8 +171,8 @@ TYPED_TEST(EltwiseLayerTest, TestSumCoeffGradient) { eltwise_param->add_coeff(2); EltwiseLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(EltwiseLayerTest, TestMax) { @@ -182,8 +182,8 @@ TYPED_TEST(EltwiseLayerTest, TestMax) { eltwise_param->set_operation(EltwiseParameter_EltwiseOp_MAX); shared_ptr > layer( new EltwiseLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); const Dtype* in_data_a = this->blob_bottom_a_->cpu_data(); @@ -202,8 +202,8 @@ TYPED_TEST(EltwiseLayerTest, TestMaxGradient) { eltwise_param->set_operation(EltwiseParameter_EltwiseOp_MAX); EltwiseLayer layer(layer_param); GradientChecker checker(1e-4, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } // namespace caffe diff --git a/src/caffe/test/test_euclidean_loss_layer.cpp b/src/caffe/test/test_euclidean_loss_layer.cpp index d7d2de7e9d4..1949742bbcb 100644 --- a/src/caffe/test/test_euclidean_loss_layer.cpp +++ b/src/caffe/test/test_euclidean_loss_layer.cpp @@ -44,18 +44,18 @@ class EuclideanLossLayerTest : public MultiDeviceTest { // equivalent to explicitly specifiying a weight of 1. LayerParameter layer_param; EuclideanLossLayer layer_weight_1(layer_param); - layer_weight_1.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer_weight_1.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype loss_weight_1 = - layer_weight_1.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer_weight_1.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Get the loss again with a different objective weight; check that it is // scaled appropriately. const Dtype kLossWeight = 3.7; layer_param.add_loss_weight(kLossWeight); EuclideanLossLayer layer_weight_2(layer_param); - layer_weight_2.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer_weight_2.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype loss_weight_2 = - layer_weight_2.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer_weight_2.Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype kErrorMargin = 1e-5; EXPECT_NEAR(loss_weight_1 * kLossWeight, loss_weight_2, kErrorMargin); // Make sure the loss is non-trivial. @@ -82,10 +82,10 @@ TYPED_TEST(EuclideanLossLayerTest, TestGradient) { const Dtype kLossWeight = 3.7; layer_param.add_loss_weight(kLossWeight); EuclideanLossLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); GradientChecker checker(1e-2, 1e-2, 1701); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } // namespace caffe diff --git a/src/caffe/test/test_flatten_layer.cpp b/src/caffe/test/test_flatten_layer.cpp index cbd01f245f2..3042d293cf7 100644 --- a/src/caffe/test/test_flatten_layer.cpp +++ b/src/caffe/test/test_flatten_layer.cpp @@ -41,7 +41,7 @@ TYPED_TEST(FlattenLayerTest, TestSetup) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; FlattenLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 3 * 6 * 5); EXPECT_EQ(this->blob_top_->height(), 1); @@ -52,8 +52,8 @@ TYPED_TEST(FlattenLayerTest, Test) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; FlattenLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int c = 0; c < 3 * 6 * 5; ++c) { EXPECT_EQ(this->blob_top_->data_at(0, c, 0, 0), this->blob_bottom_->data_at(0, c / (6 * 5), (c / 5) % 6, c % 5)); @@ -67,8 +67,8 @@ TYPED_TEST(FlattenLayerTest, TestGradient) { LayerParameter layer_param; FlattenLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } diff --git a/src/caffe/test/test_gradient_based_solver.cpp b/src/caffe/test/test_gradient_based_solver.cpp index 3040eb134a4..eb2569c04f2 100644 --- a/src/caffe/test/test_gradient_based_solver.cpp +++ b/src/caffe/test/test_gradient_based_solver.cpp @@ -64,9 +64,9 @@ class GradientBasedSolverTest : public MultiDeviceTest { "lr_policy: 'fixed' " "net_param { " " name: 'TestNetwork' " - " layers: { " + " layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: " << num_ << " " " channels: " << channels_ << " " @@ -83,9 +83,9 @@ class GradientBasedSolverTest : public MultiDeviceTest { " top: 'data' " " top: 'targets' " " } " - " layers: { " + " layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 1 " " weight_filler { " @@ -100,9 +100,9 @@ class GradientBasedSolverTest : public MultiDeviceTest { " bottom: 'data' " " top: 'innerprod' " " } " - " layers: { " + " layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod' " " bottom: 'targets' " " } " @@ -323,7 +323,6 @@ class SGDSolverTest : public GradientBasedSolverTest { TYPED_TEST_CASE(SGDSolverTest, TestDtypesAndDevices); TYPED_TEST(SGDSolverTest, TestLeastSquaresUpdate) { - typedef typename TypeParam::Dtype Dtype; this->TestLeastSquaresUpdate(); } @@ -390,7 +389,6 @@ class AdaGradSolverTest : public GradientBasedSolverTest { TYPED_TEST_CASE(AdaGradSolverTest, TestDtypesAndDevices); TYPED_TEST(AdaGradSolverTest, TestAdaGradLeastSquaresUpdate) { - typedef typename TypeParam::Dtype Dtype; this->TestLeastSquaresUpdate(); } @@ -435,7 +433,6 @@ class NesterovSolverTest : public GradientBasedSolverTest { TYPED_TEST_CASE(NesterovSolverTest, TestDtypesAndDevices); TYPED_TEST(NesterovSolverTest, TestNesterovLeastSquaresUpdate) { - typedef typename TypeParam::Dtype Dtype; this->TestLeastSquaresUpdate(); } diff --git a/src/caffe/test/test_hdf5_output_layer.cpp b/src/caffe/test/test_hdf5_output_layer.cpp index eb09c8d1f3a..a23034f284a 100644 --- a/src/caffe/test/test_hdf5_output_layer.cpp +++ b/src/caffe/test/test_hdf5_output_layer.cpp @@ -11,9 +11,6 @@ #include "caffe/test/test_caffe_main.hpp" -using std::string; -using std::vector; - namespace caffe { template @@ -95,9 +92,9 @@ TYPED_TEST(HDF5OutputLayerTest, TestForward) { // the output hdf5 file is closed. { HDF5OutputLayer layer(param); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(layer.file_name(), this->output_file_name_); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); } file_id = H5Fopen(this->output_file_name_.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); diff --git a/src/caffe/test/test_hdf5data_layer.cpp b/src/caffe/test/test_hdf5data_layer.cpp index 29e70c9417f..8d3b3d1e987 100644 --- a/src/caffe/test/test_hdf5data_layer.cpp +++ b/src/caffe/test/test_hdf5data_layer.cpp @@ -1,8 +1,6 @@ #include #include -#include "leveldb/db.h" - #include "gtest/gtest.h" #include "caffe/blob.hpp" @@ -13,8 +11,6 @@ #include "caffe/test/test_caffe_main.hpp" -using std::string; - namespace caffe { template @@ -25,10 +21,12 @@ class HDF5DataLayerTest : public MultiDeviceTest { HDF5DataLayerTest() : filename(NULL), blob_top_data_(new Blob()), - blob_top_label_(new Blob()) {} + blob_top_label_(new Blob()), + blob_top_label2_(new Blob()) {} virtual void SetUp() { blob_top_vec_.push_back(blob_top_data_); blob_top_vec_.push_back(blob_top_label_); + blob_top_vec_.push_back(blob_top_label2_); // Check out generate_sample_data.py in the same directory. filename = new string( @@ -39,12 +37,14 @@ class HDF5DataLayerTest : public MultiDeviceTest { virtual ~HDF5DataLayerTest() { delete blob_top_data_; delete blob_top_label_; + delete blob_top_label2_; delete filename; } string* filename; Blob* const blob_top_data_; Blob* const blob_top_label_; + Blob* const blob_top_label2_; vector*> blob_bottom_vec_; vector*> blob_top_vec_; }; @@ -57,6 +57,10 @@ TYPED_TEST(HDF5DataLayerTest, TestRead) { // The data file we are reading has 10 rows and 8 columns, // with values from 0 to 10*8 reshaped in row-major order. LayerParameter param; + param.add_top("data"); + param.add_top("label"); + param.add_top("label2"); + HDF5DataParameter* hdf5_data_param = param.mutable_hdf5_data_param(); int batch_size = 5; hdf5_data_param->set_batch_size(batch_size); @@ -67,7 +71,7 @@ TYPED_TEST(HDF5DataLayerTest, TestRead) { // Test that the layer setup got the correct parameters. HDF5DataLayer layer(param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_data_->num(), batch_size); EXPECT_EQ(this->blob_top_data_->channels(), num_cols); EXPECT_EQ(this->blob_top_data_->height(), height); @@ -78,17 +82,23 @@ TYPED_TEST(HDF5DataLayerTest, TestRead) { EXPECT_EQ(this->blob_top_label_->height(), 1); EXPECT_EQ(this->blob_top_label_->width(), 1); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + EXPECT_EQ(this->blob_top_label2_->num(), batch_size); + EXPECT_EQ(this->blob_top_label2_->channels(), 1); + EXPECT_EQ(this->blob_top_label2_->height(), 1); + EXPECT_EQ(this->blob_top_label2_->width(), 1); + + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); // Go through the data 10 times (5 batches). const int data_size = num_cols * height * width; for (int iter = 0; iter < 10; ++iter) { - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // On even iterations, we're reading the first half of the data. // On odd iterations, we're reading the second half of the data. // NB: label is 1-indexed int label_offset = 1 + ((iter % 2 == 0) ? 0 : batch_size); + int label2_offset = 1 + label_offset; int data_offset = (iter % 2 == 0) ? 0 : batch_size * data_size; // Every two iterations we are reading the second file, @@ -100,6 +110,9 @@ TYPED_TEST(HDF5DataLayerTest, TestRead) { EXPECT_EQ( label_offset + i, this->blob_top_label_->cpu_data()[i]); + EXPECT_EQ( + label2_offset + i, + this->blob_top_label2_->cpu_data()[i]); } for (int i = 0; i < batch_size; ++i) { for (int j = 0; j < num_cols; ++j) { diff --git a/src/caffe/test/test_hinge_loss_layer.cpp b/src/caffe/test/test_hinge_loss_layer.cpp index 3c11b9ac491..b6a99022905 100644 --- a/src/caffe/test/test_hinge_loss_layer.cpp +++ b/src/caffe/test/test_hinge_loss_layer.cpp @@ -57,8 +57,8 @@ TYPED_TEST(HingeLossLayerTest, TestGradientL1) { LayerParameter layer_param; HingeLossLayer layer(layer_param); GradientChecker checker(1e-2, 2e-3, 1701, 1, 0.01); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_), 0); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); } TYPED_TEST(HingeLossLayerTest, TestGradientL2) { @@ -69,8 +69,8 @@ TYPED_TEST(HingeLossLayerTest, TestGradientL2) { hinge_loss_param->set_norm(HingeLossParameter_Norm_L2); HingeLossLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2, 1701); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_), 0); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); } } // namespace caffe diff --git a/src/caffe/test/test_im2col_layer.cpp b/src/caffe/test/test_im2col_layer.cpp index 32cf6369361..f50abe103f8 100644 --- a/src/caffe/test/test_im2col_layer.cpp +++ b/src/caffe/test/test_im2col_layer.cpp @@ -44,7 +44,7 @@ TYPED_TEST(Im2colLayerTest, TestSetup) { convolution_param->set_kernel_size(3); convolution_param->set_stride(2); Im2colLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 27); EXPECT_EQ(this->blob_top_->height(), 2); @@ -59,8 +59,8 @@ TYPED_TEST(Im2colLayerTest, TestForward) { convolution_param->set_kernel_size(3); convolution_param->set_stride(2); Im2colLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // We are lazy and will only check the top left block for (int c = 0; c < 27; ++c) { EXPECT_EQ(this->blob_bottom_->data_at(0, (c / 9), (c / 3) % 3, c % 3), @@ -77,8 +77,8 @@ TYPED_TEST(Im2colLayerTest, TestGradient) { convolution_param->set_stride(2); Im2colLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } @@ -91,8 +91,8 @@ TYPED_TEST(Im2colLayerTest, TestRect) { convolution_param->set_kernel_w(3); convolution_param->set_stride(2); Im2colLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // We are lazy and will only check the top left block for (int c = 0; c < 45; ++c) { EXPECT_EQ(this->blob_top_->data_at(0, c, 0, 0), @@ -111,8 +111,8 @@ TYPED_TEST(Im2colLayerTest, TestRectGradient) { convolution_param->set_stride(2); Im2colLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } // namespace caffe diff --git a/src/caffe/test/test_image_data_layer.cpp b/src/caffe/test/test_image_data_layer.cpp index d098c765258..931a5ebf137 100644 --- a/src/caffe/test/test_image_data_layer.cpp +++ b/src/caffe/test/test_image_data_layer.cpp @@ -25,17 +25,24 @@ class ImageDataLayerTest : public MultiDeviceTest { blob_top_data_(new Blob()), blob_top_label_(new Blob()) {} virtual void SetUp() { - MakeTempFilename(&filename_); blob_top_vec_.push_back(blob_top_data_); blob_top_vec_.push_back(blob_top_label_); Caffe::set_random_seed(seed_); - // Create a Vector of files with labels + // Create test input file. + MakeTempFilename(&filename_); std::ofstream outfile(filename_.c_str(), std::ofstream::out); LOG(INFO) << "Using temporary file " << filename_; for (int i = 0; i < 5; ++i) { outfile << EXAMPLES_SOURCE_DIR "images/cat.jpg " << i; } outfile.close(); + // Create test input file for images of distinct sizes. + MakeTempFilename(&filename_reshape_); + std::ofstream reshapefile(filename_reshape_.c_str(), std::ofstream::out); + LOG(INFO) << "Using temporary file " << filename_reshape_; + reshapefile << EXAMPLES_SOURCE_DIR "images/cat.jpg " << 0; + reshapefile << EXAMPLES_SOURCE_DIR "images/fish-bike.jpg " << 1; + reshapefile.close(); } virtual ~ImageDataLayerTest() { @@ -45,6 +52,7 @@ class ImageDataLayerTest : public MultiDeviceTest { int seed_; string filename_; + string filename_reshape_; Blob* const blob_top_data_; Blob* const blob_top_label_; vector*> blob_bottom_vec_; @@ -61,7 +69,7 @@ TYPED_TEST(ImageDataLayerTest, TestRead) { image_data_param->set_source(this->filename_.c_str()); image_data_param->set_shuffle(false); ImageDataLayer layer(param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_data_->num(), 5); EXPECT_EQ(this->blob_top_data_->channels(), 3); EXPECT_EQ(this->blob_top_data_->height(), 360); @@ -72,7 +80,7 @@ TYPED_TEST(ImageDataLayerTest, TestRead) { EXPECT_EQ(this->blob_top_label_->width(), 1); // Go through the data twice for (int iter = 0; iter < 2; ++iter) { - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < 5; ++i) { EXPECT_EQ(i, this->blob_top_label_->cpu_data()[i]); } @@ -89,7 +97,7 @@ TYPED_TEST(ImageDataLayerTest, TestResize) { image_data_param->set_new_width(256); image_data_param->set_shuffle(false); ImageDataLayer layer(param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_data_->num(), 5); EXPECT_EQ(this->blob_top_data_->channels(), 3); EXPECT_EQ(this->blob_top_data_->height(), 256); @@ -100,13 +108,40 @@ TYPED_TEST(ImageDataLayerTest, TestResize) { EXPECT_EQ(this->blob_top_label_->width(), 1); // Go through the data twice for (int iter = 0; iter < 2; ++iter) { - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < 5; ++i) { EXPECT_EQ(i, this->blob_top_label_->cpu_data()[i]); } } } +TYPED_TEST(ImageDataLayerTest, TestReshape) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter param; + ImageDataParameter* image_data_param = param.mutable_image_data_param(); + image_data_param->set_batch_size(1); + image_data_param->set_source(this->filename_reshape_.c_str()); + image_data_param->set_shuffle(false); + ImageDataLayer layer(param); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + EXPECT_EQ(this->blob_top_label_->num(), 1); + EXPECT_EQ(this->blob_top_label_->channels(), 1); + EXPECT_EQ(this->blob_top_label_->height(), 1); + EXPECT_EQ(this->blob_top_label_->width(), 1); + // cat.jpg + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); + EXPECT_EQ(this->blob_top_data_->num(), 1); + EXPECT_EQ(this->blob_top_data_->channels(), 3); + EXPECT_EQ(this->blob_top_data_->height(), 360); + EXPECT_EQ(this->blob_top_data_->width(), 480); + // fish-bike.jpg + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); + EXPECT_EQ(this->blob_top_data_->num(), 1); + EXPECT_EQ(this->blob_top_data_->channels(), 3); + EXPECT_EQ(this->blob_top_data_->height(), 323); + EXPECT_EQ(this->blob_top_data_->width(), 481); +} + TYPED_TEST(ImageDataLayerTest, TestShuffle) { typedef typename TypeParam::Dtype Dtype; LayerParameter param; @@ -115,7 +150,7 @@ TYPED_TEST(ImageDataLayerTest, TestShuffle) { image_data_param->set_source(this->filename_.c_str()); image_data_param->set_shuffle(true); ImageDataLayer layer(param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_data_->num(), 5); EXPECT_EQ(this->blob_top_data_->channels(), 3); EXPECT_EQ(this->blob_top_data_->height(), 360); @@ -126,7 +161,7 @@ TYPED_TEST(ImageDataLayerTest, TestShuffle) { EXPECT_EQ(this->blob_top_label_->width(), 1); // Go through the data twice for (int iter = 0; iter < 2; ++iter) { - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); map values_to_indices; int num_in_order = 0; for (int i = 0; i < 5; ++i) { diff --git a/src/caffe/test/test_infogain_loss_layer.cpp b/src/caffe/test/test_infogain_loss_layer.cpp index de2f901af31..7ec2f8073c1 100644 --- a/src/caffe/test/test_infogain_loss_layer.cpp +++ b/src/caffe/test/test_infogain_loss_layer.cpp @@ -63,8 +63,8 @@ TYPED_TEST(InfogainLossLayerTest, TestGradient) { LayerParameter layer_param; InfogainLossLayer layer(layer_param); GradientChecker checker(1e-4, 2e-2, 1701, 1, 0.01); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_), 0); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); } } // namespace caffe diff --git a/src/caffe/test/test_inner_product_layer.cpp b/src/caffe/test/test_inner_product_layer.cpp index 5f9729c4f90..c03df17383a 100644 --- a/src/caffe/test/test_inner_product_layer.cpp +++ b/src/caffe/test/test_inner_product_layer.cpp @@ -48,7 +48,7 @@ TYPED_TEST(InnerProductLayerTest, TestSetUp) { inner_product_param->set_num_output(10); shared_ptr > layer( new InnerProductLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->height(), 1); EXPECT_EQ(this->blob_top_->width(), 1); @@ -73,8 +73,8 @@ TYPED_TEST(InnerProductLayerTest, TestForward) { inner_product_param->mutable_bias_filler()->set_max(2); shared_ptr > layer( new InnerProductLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->blob_top_->cpu_data(); const int count = this->blob_top_->count(); for (int i = 0; i < count; ++i) { @@ -103,8 +103,8 @@ TYPED_TEST(InnerProductLayerTest, TestGradient) { inner_product_param->mutable_bias_filler()->set_max(2); InnerProductLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } else { LOG(ERROR) << "Skipping test due to old architecture."; } diff --git a/src/caffe/test/test_io.cpp b/src/caffe/test/test_io.cpp new file mode 100644 index 00000000000..4ab96311bbc --- /dev/null +++ b/src/caffe/test/test_io.cpp @@ -0,0 +1,422 @@ +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" + +#include "caffe/common.hpp" +#include "caffe/util/io.hpp" + +#include "caffe/test/test_caffe_main.hpp" + +namespace caffe { + +class IOTest : public ::testing::Test {}; + +bool ReadImageToDatumReference(const string& filename, const int label, + const int height, const int width, const bool is_color, Datum* datum) { + cv::Mat cv_img; + int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR : + CV_LOAD_IMAGE_GRAYSCALE); + + cv::Mat cv_img_origin = cv::imread(filename, cv_read_flag); + if (!cv_img_origin.data) { + LOG(ERROR) << "Could not open or find file " << filename; + return false; + } + if (height > 0 && width > 0) { + cv::resize(cv_img_origin, cv_img, cv::Size(width, height)); + } else { + cv_img = cv_img_origin; + } + + int num_channels = (is_color ? 3 : 1); + datum->set_channels(num_channels); + datum->set_height(cv_img.rows); + datum->set_width(cv_img.cols); + datum->set_label(label); + datum->clear_data(); + datum->clear_float_data(); + string* datum_string = datum->mutable_data(); + if (is_color) { + for (int c = 0; c < num_channels; ++c) { + for (int h = 0; h < cv_img.rows; ++h) { + for (int w = 0; w < cv_img.cols; ++w) { + datum_string->push_back( + static_cast(cv_img.at(h, w)[c])); + } + } + } + } else { // Faster than repeatedly testing is_color for each pixel w/i loop + for (int h = 0; h < cv_img.rows; ++h) { + for (int w = 0; w < cv_img.cols; ++w) { + datum_string->push_back( + static_cast(cv_img.at(h, w))); + } + } + } + return true; +} + +TEST_F(IOTest, TestReadImageToDatum) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + ReadImageToDatum(filename, 0, &datum); + EXPECT_EQ(datum.channels(), 3); + EXPECT_EQ(datum.height(), 360); + EXPECT_EQ(datum.width(), 480); +} + +TEST_F(IOTest, TestReadImageToDatumReference) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum, datum_ref; + ReadImageToDatum(filename, 0, 0, 0, true, &datum); + ReadImageToDatumReference(filename, 0, 0, 0, true, &datum_ref); + EXPECT_EQ(datum.channels(), datum_ref.channels()); + EXPECT_EQ(datum.height(), datum_ref.height()); + EXPECT_EQ(datum.width(), datum_ref.width()); + EXPECT_EQ(datum.data().size(), datum_ref.data().size()); + + const string& data = datum.data(); + const string& data_ref = datum.data(); + + for (int i = 0; i < datum.data().size(); ++i) { + EXPECT_TRUE(data[i] == data_ref[i]); + } +} + + +TEST_F(IOTest, TestReadImageToDatumReferenceResized) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum, datum_ref; + ReadImageToDatum(filename, 0, 100, 200, true, &datum); + ReadImageToDatumReference(filename, 0, 100, 200, true, &datum_ref); + EXPECT_EQ(datum.channels(), datum_ref.channels()); + EXPECT_EQ(datum.height(), datum_ref.height()); + EXPECT_EQ(datum.width(), datum_ref.width()); + EXPECT_EQ(datum.data().size(), datum_ref.data().size()); + + const string& data = datum.data(); + const string& data_ref = datum.data(); + + for (int i = 0; i < datum.data().size(); ++i) { + EXPECT_TRUE(data[i] == data_ref[i]); + } +} + +TEST_F(IOTest, TestReadImageToDatumContent) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + ReadImageToDatum(filename, 0, &datum); + cv::Mat cv_img = ReadImageToCVMat(filename); + EXPECT_EQ(datum.channels(), cv_img.channels()); + EXPECT_EQ(datum.height(), cv_img.rows); + EXPECT_EQ(datum.width(), cv_img.cols); + + const string& data = datum.data(); + int index = 0; + for (int c = 0; c < datum.channels(); ++c) { + for (int h = 0; h < datum.height(); ++h) { + for (int w = 0; w < datum.width(); ++w) { + EXPECT_TRUE(data[index++] == + static_cast(cv_img.at(h, w)[c])); + } + } + } +} + +TEST_F(IOTest, TestReadImageToDatumContentGray) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + const bool is_color = false; + ReadImageToDatum(filename, 0, is_color, &datum); + cv::Mat cv_img = ReadImageToCVMat(filename, is_color); + EXPECT_EQ(datum.channels(), cv_img.channels()); + EXPECT_EQ(datum.height(), cv_img.rows); + EXPECT_EQ(datum.width(), cv_img.cols); + + const string& data = datum.data(); + int index = 0; + for (int h = 0; h < datum.height(); ++h) { + for (int w = 0; w < datum.width(); ++w) { + EXPECT_TRUE(data[index++] == static_cast(cv_img.at(h, w))); + } + } +} + +TEST_F(IOTest, TestReadImageToDatumResized) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + ReadImageToDatum(filename, 0, 100, 200, &datum); + EXPECT_EQ(datum.channels(), 3); + EXPECT_EQ(datum.height(), 100); + EXPECT_EQ(datum.width(), 200); +} + + +TEST_F(IOTest, TestReadImageToDatumResizedSquare) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + ReadImageToDatum(filename, 0, 256, 256, &datum); + EXPECT_EQ(datum.channels(), 3); + EXPECT_EQ(datum.height(), 256); + EXPECT_EQ(datum.width(), 256); +} + +TEST_F(IOTest, TestReadImageToDatumGray) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + const bool is_color = false; + ReadImageToDatum(filename, 0, is_color, &datum); + EXPECT_EQ(datum.channels(), 1); + EXPECT_EQ(datum.height(), 360); + EXPECT_EQ(datum.width(), 480); +} + +TEST_F(IOTest, TestReadImageToDatumResizedGray) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + const bool is_color = false; + ReadImageToDatum(filename, 0, 256, 256, is_color, &datum); + EXPECT_EQ(datum.channels(), 1); + EXPECT_EQ(datum.height(), 256); + EXPECT_EQ(datum.width(), 256); +} + +TEST_F(IOTest, TestReadImageToCVMat) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + cv::Mat cv_img = ReadImageToCVMat(filename); + EXPECT_EQ(cv_img.channels(), 3); + EXPECT_EQ(cv_img.rows, 360); + EXPECT_EQ(cv_img.cols, 480); +} + +TEST_F(IOTest, TestReadImageToCVMatResized) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + cv::Mat cv_img = ReadImageToCVMat(filename, 100, 200); + EXPECT_EQ(cv_img.channels(), 3); + EXPECT_EQ(cv_img.rows, 100); + EXPECT_EQ(cv_img.cols, 200); +} + +TEST_F(IOTest, TestReadImageToCVMatResizedSquare) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + cv::Mat cv_img = ReadImageToCVMat(filename, 256, 256); + EXPECT_EQ(cv_img.channels(), 3); + EXPECT_EQ(cv_img.rows, 256); + EXPECT_EQ(cv_img.cols, 256); +} + +TEST_F(IOTest, TestReadImageToCVMatGray) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + const bool is_color = false; + cv::Mat cv_img = ReadImageToCVMat(filename, is_color); + EXPECT_EQ(cv_img.channels(), 1); + EXPECT_EQ(cv_img.rows, 360); + EXPECT_EQ(cv_img.cols, 480); +} + +TEST_F(IOTest, TestReadImageToCVMatResizedGray) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + const bool is_color = false; + cv::Mat cv_img = ReadImageToCVMat(filename, 256, 256, is_color); + EXPECT_EQ(cv_img.channels(), 1); + EXPECT_EQ(cv_img.rows, 256); + EXPECT_EQ(cv_img.cols, 256); +} + +TEST_F(IOTest, TestCVMatToDatum) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + cv::Mat cv_img = ReadImageToCVMat(filename); + Datum datum; + CVMatToDatum(cv_img, &datum); + EXPECT_EQ(datum.channels(), 3); + EXPECT_EQ(datum.height(), 360); + EXPECT_EQ(datum.width(), 480); +} + +TEST_F(IOTest, TestCVMatToDatumContent) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + cv::Mat cv_img = ReadImageToCVMat(filename); + Datum datum; + CVMatToDatum(cv_img, &datum); + Datum datum_ref; + ReadImageToDatum(filename, 0, &datum_ref); + EXPECT_EQ(datum.channels(), datum_ref.channels()); + EXPECT_EQ(datum.height(), datum_ref.height()); + EXPECT_EQ(datum.width(), datum_ref.width()); + EXPECT_EQ(datum.data().size(), datum_ref.data().size()); + + const string& data = datum.data(); + const string& data_ref = datum_ref.data(); + for (int i = 0; i < datum.data().size(); ++i) { + EXPECT_TRUE(data[i] == data_ref[i]); + } +} + +TEST_F(IOTest, TestCVMatToDatumReference) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + cv::Mat cv_img = ReadImageToCVMat(filename); + Datum datum; + CVMatToDatum(cv_img, &datum); + Datum datum_ref; + ReadImageToDatumReference(filename, 0, 0, 0, true, &datum_ref); + EXPECT_EQ(datum.channels(), datum_ref.channels()); + EXPECT_EQ(datum.height(), datum_ref.height()); + EXPECT_EQ(datum.width(), datum_ref.width()); + EXPECT_EQ(datum.data().size(), datum_ref.data().size()); + + const string& data = datum.data(); + const string& data_ref = datum_ref.data(); + for (int i = 0; i < datum.data().size(); ++i) { + EXPECT_TRUE(data[i] == data_ref[i]); + } +} + +TEST_F(IOTest, TestReadFileToDatum) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + EXPECT_TRUE(ReadFileToDatum(filename, &datum)); + EXPECT_TRUE(datum.encoded()); + EXPECT_EQ(datum.label(), -1); + EXPECT_EQ(datum.data().size(), 140391); +} + +TEST_F(IOTest, TestDecodeDatum) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + EXPECT_TRUE(ReadFileToDatum(filename, &datum)); + EXPECT_TRUE(DecodeDatum(&datum, true)); + EXPECT_FALSE(DecodeDatum(&datum, true)); + Datum datum_ref; + ReadImageToDatumReference(filename, 0, 0, 0, true, &datum_ref); + EXPECT_EQ(datum.channels(), datum_ref.channels()); + EXPECT_EQ(datum.height(), datum_ref.height()); + EXPECT_EQ(datum.width(), datum_ref.width()); + EXPECT_EQ(datum.data().size(), datum_ref.data().size()); + + const string& data = datum.data(); + const string& data_ref = datum_ref.data(); + for (int i = 0; i < datum.data().size(); ++i) { + EXPECT_TRUE(data[i] == data_ref[i]); + } +} + +TEST_F(IOTest, TestDecodeDatumToCVMat) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + EXPECT_TRUE(ReadFileToDatum(filename, &datum)); + cv::Mat cv_img = DecodeDatumToCVMat(datum, true); + EXPECT_EQ(cv_img.channels(), 3); + EXPECT_EQ(cv_img.rows, 360); + EXPECT_EQ(cv_img.cols, 480); + cv_img = DecodeDatumToCVMat(datum, false); + EXPECT_EQ(cv_img.channels(), 1); + EXPECT_EQ(cv_img.rows, 360); + EXPECT_EQ(cv_img.cols, 480); +} + +TEST_F(IOTest, TestDecodeDatumToCVMatContent) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + EXPECT_TRUE(ReadImageToDatum(filename, 0, std::string("jpg"), &datum)); + cv::Mat cv_img = DecodeDatumToCVMat(datum, true); + cv::Mat cv_img_ref = ReadImageToCVMat(filename); + EXPECT_EQ(cv_img_ref.channels(), cv_img.channels()); + EXPECT_EQ(cv_img_ref.rows, cv_img.rows); + EXPECT_EQ(cv_img_ref.cols, cv_img.cols); + + for (int c = 0; c < datum.channels(); ++c) { + for (int h = 0; h < datum.height(); ++h) { + for (int w = 0; w < datum.width(); ++w) { + EXPECT_TRUE(cv_img.at(h, w)[c]== + cv_img_ref.at(h, w)[c]); + } + } + } +} + +TEST_F(IOTest, TestDecodeDatumNative) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + EXPECT_TRUE(ReadFileToDatum(filename, &datum)); + EXPECT_TRUE(DecodeDatumNative(&datum)); + EXPECT_FALSE(DecodeDatumNative(&datum)); + Datum datum_ref; + ReadImageToDatumReference(filename, 0, 0, 0, true, &datum_ref); + EXPECT_EQ(datum.channels(), datum_ref.channels()); + EXPECT_EQ(datum.height(), datum_ref.height()); + EXPECT_EQ(datum.width(), datum_ref.width()); + EXPECT_EQ(datum.data().size(), datum_ref.data().size()); + + const string& data = datum.data(); + const string& data_ref = datum_ref.data(); + for (int i = 0; i < datum.data().size(); ++i) { + EXPECT_TRUE(data[i] == data_ref[i]); + } +} + +TEST_F(IOTest, TestDecodeDatumToCVMatNative) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + EXPECT_TRUE(ReadFileToDatum(filename, &datum)); + cv::Mat cv_img = DecodeDatumToCVMatNative(datum); + EXPECT_EQ(cv_img.channels(), 3); + EXPECT_EQ(cv_img.rows, 360); + EXPECT_EQ(cv_img.cols, 480); +} + +TEST_F(IOTest, TestDecodeDatumNativeGray) { + string filename = EXAMPLES_SOURCE_DIR "images/cat_gray.jpg"; + Datum datum; + EXPECT_TRUE(ReadFileToDatum(filename, &datum)); + EXPECT_TRUE(DecodeDatumNative(&datum)); + EXPECT_FALSE(DecodeDatumNative(&datum)); + Datum datum_ref; + ReadImageToDatumReference(filename, 0, 0, 0, false, &datum_ref); + EXPECT_EQ(datum.channels(), datum_ref.channels()); + EXPECT_EQ(datum.height(), datum_ref.height()); + EXPECT_EQ(datum.width(), datum_ref.width()); + EXPECT_EQ(datum.data().size(), datum_ref.data().size()); + + const string& data = datum.data(); + const string& data_ref = datum_ref.data(); + for (int i = 0; i < datum.data().size(); ++i) { + EXPECT_TRUE(data[i] == data_ref[i]); + } +} + +TEST_F(IOTest, TestDecodeDatumToCVMatNativeGray) { + string filename = EXAMPLES_SOURCE_DIR "images/cat_gray.jpg"; + Datum datum; + EXPECT_TRUE(ReadFileToDatum(filename, &datum)); + cv::Mat cv_img = DecodeDatumToCVMatNative(datum); + EXPECT_EQ(cv_img.channels(), 1); + EXPECT_EQ(cv_img.rows, 360); + EXPECT_EQ(cv_img.cols, 480); +} + +TEST_F(IOTest, TestDecodeDatumToCVMatContentNative) { + string filename = EXAMPLES_SOURCE_DIR "images/cat.jpg"; + Datum datum; + EXPECT_TRUE(ReadImageToDatum(filename, 0, std::string("jpg"), &datum)); + cv::Mat cv_img = DecodeDatumToCVMatNative(datum); + cv::Mat cv_img_ref = ReadImageToCVMat(filename); + EXPECT_EQ(cv_img_ref.channels(), cv_img.channels()); + EXPECT_EQ(cv_img_ref.rows, cv_img.rows); + EXPECT_EQ(cv_img_ref.cols, cv_img.cols); + + for (int c = 0; c < datum.channels(); ++c) { + for (int h = 0; h < datum.height(); ++h) { + for (int w = 0; w < datum.width(); ++w) { + EXPECT_TRUE(cv_img.at(h, w)[c]== + cv_img_ref.at(h, w)[c]); + } + } + } +} + +} // namespace caffe diff --git a/src/caffe/test/test_layer_factory.cpp b/src/caffe/test/test_layer_factory.cpp new file mode 100644 index 00000000000..efb1b37ac42 --- /dev/null +++ b/src/caffe/test/test_layer_factory.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include "gtest/gtest.h" + +#include "caffe/common.hpp" +#include "caffe/layer.hpp" +#include "caffe/layer_factory.hpp" + +#include "caffe/test/test_caffe_main.hpp" + +namespace caffe { + +template +class LayerFactoryTest : public MultiDeviceTest {}; + +TYPED_TEST_CASE(LayerFactoryTest, TestDtypesAndDevices); + +TYPED_TEST(LayerFactoryTest, TestCreateLayer) { + typedef typename TypeParam::Dtype Dtype; + typename LayerRegistry::CreatorRegistry& registry = + LayerRegistry::Registry(); + shared_ptr > layer; + LayerParameter layer_param; + for (typename LayerRegistry::CreatorRegistry::iterator iter = + registry.begin(); iter != registry.end(); ++iter) { + // Special case: PythonLayer is checked by pytest + if (iter->first == "Python") { continue; } + layer_param.set_type(iter->first); + layer = LayerRegistry::CreateLayer(layer_param); + EXPECT_EQ(iter->first, layer->type()); + } +} + +} // namespace caffe diff --git a/src/caffe/test/test_lrn_layer.cpp b/src/caffe/test/test_lrn_layer.cpp index 3bd62fd9e18..07425df9b3a 100644 --- a/src/caffe/test/test_lrn_layer.cpp +++ b/src/caffe/test/test_lrn_layer.cpp @@ -116,7 +116,7 @@ TYPED_TEST(LRNLayerTest, TestSetupAcrossChannels) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; LRNLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 7); EXPECT_EQ(this->blob_top_->height(), 3); @@ -127,8 +127,8 @@ TYPED_TEST(LRNLayerTest, TestForwardAcrossChannels) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; LRNLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); Blob top_reference; this->ReferenceLRNForward(*(this->blob_bottom_), layer_param, &top_reference); @@ -143,20 +143,20 @@ TYPED_TEST(LRNLayerTest, TestGradientAcrossChannels) { LayerParameter layer_param; LRNLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_top_->count(); ++i) { this->blob_top_->mutable_cpu_diff()[i] = 1.; } vector propagate_down(this->blob_bottom_vec_.size(), true); layer.Backward(this->blob_top_vec_, propagate_down, - &(this->blob_bottom_vec_)); + this->blob_bottom_vec_); // for (int i = 0; i < this->blob_bottom_->count(); ++i) { // std::cout << "CPU diff " << this->blob_bottom_->cpu_diff()[i] // << std::endl; // } - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(LRNLayerTest, TestSetupWithinChannel) { @@ -166,7 +166,7 @@ TYPED_TEST(LRNLayerTest, TestSetupWithinChannel) { LRNParameter_NormRegion_WITHIN_CHANNEL); layer_param.mutable_lrn_param()->set_local_size(3); LRNLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 2); EXPECT_EQ(this->blob_top_->channels(), 7); EXPECT_EQ(this->blob_top_->height(), 3); @@ -180,8 +180,8 @@ TYPED_TEST(LRNLayerTest, TestForwardWithinChannel) { LRNParameter_NormRegion_WITHIN_CHANNEL); layer_param.mutable_lrn_param()->set_local_size(3); LRNLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); Blob top_reference; this->ReferenceLRNForward(*(this->blob_bottom_), layer_param, &top_reference); @@ -199,13 +199,13 @@ TYPED_TEST(LRNLayerTest, TestGradientWithinChannel) { layer_param.mutable_lrn_param()->set_local_size(3); LRNLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_top_->count(); ++i) { this->blob_top_->mutable_cpu_diff()[i] = 1.; } - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } diff --git a/src/caffe/test/test_maxpool_dropout_layers.cpp b/src/caffe/test/test_maxpool_dropout_layers.cpp index 311c7781be5..611d9790863 100644 --- a/src/caffe/test/test_maxpool_dropout_layers.cpp +++ b/src/caffe/test/test_maxpool_dropout_layers.cpp @@ -47,9 +47,9 @@ TYPED_TEST(MaxPoolingDropoutTest, TestSetup) { pooling_param->set_kernel_size(3); pooling_param->set_stride(2); PoolingLayer max_layer(layer_param); - max_layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + max_layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); DropoutLayer dropout_layer(layer_param); - dropout_layer.SetUp(this->blob_top_vec_, &(this->blob_top_vec_)); + dropout_layer.SetUp(this->blob_top_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_->channels()); EXPECT_EQ(this->blob_top_->height(), 3); @@ -64,8 +64,8 @@ TYPED_TEST(MaxPoolingDropoutTest, TestForward) { pooling_param->set_kernel_size(3); pooling_param->set_stride(2); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* top_data = this->blob_top_->cpu_data(); Dtype sum = 0.; for (int i = 0; i < this->blob_top_->count(); ++i) { @@ -74,8 +74,8 @@ TYPED_TEST(MaxPoolingDropoutTest, TestForward) { EXPECT_EQ(sum, this->blob_top_->count()); // Dropout in-place DropoutLayer dropout_layer(layer_param); - dropout_layer.SetUp(this->blob_top_vec_, &(this->blob_top_vec_)); - dropout_layer.Forward(this->blob_top_vec_, &(this->blob_top_vec_)); + dropout_layer.SetUp(this->blob_top_vec_, this->blob_top_vec_); + dropout_layer.Forward(this->blob_top_vec_, this->blob_top_vec_); sum = 0.; Dtype scale = 1. / (1. - layer_param.dropout_param().dropout_ratio()); top_data = this->blob_top_->cpu_data(); @@ -88,20 +88,20 @@ TYPED_TEST(MaxPoolingDropoutTest, TestForward) { TYPED_TEST(MaxPoolingDropoutTest, TestBackward) { typedef typename TypeParam::Dtype Dtype; - Caffe::set_phase(Caffe::TRAIN); LayerParameter layer_param; + layer_param.set_phase(TRAIN); PoolingParameter* pooling_param = layer_param.mutable_pooling_param(); pooling_param->set_kernel_size(3); pooling_param->set_stride(2); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_top_->count(); ++i) { this->blob_top_->mutable_cpu_diff()[i] = 1.; } vector propagate_down(this->blob_bottom_vec_.size(), true); layer.Backward(this->blob_top_vec_, propagate_down, - &(this->blob_bottom_vec_)); + this->blob_bottom_vec_); const Dtype* bottom_diff = this->blob_bottom_->cpu_diff(); Dtype sum = 0.; for (int i = 0; i < this->blob_bottom_->count(); ++i) { @@ -110,12 +110,12 @@ TYPED_TEST(MaxPoolingDropoutTest, TestBackward) { EXPECT_EQ(sum, this->blob_top_->count()); // Dropout in-place DropoutLayer dropout_layer(layer_param); - dropout_layer.SetUp(this->blob_top_vec_, &(this->blob_top_vec_)); - dropout_layer.Forward(this->blob_top_vec_, &(this->blob_top_vec_)); + dropout_layer.SetUp(this->blob_top_vec_, this->blob_top_vec_); + dropout_layer.Forward(this->blob_top_vec_, this->blob_top_vec_); dropout_layer.Backward(this->blob_top_vec_, propagate_down, - &(this->blob_top_vec_)); + this->blob_top_vec_); layer.Backward(this->blob_top_vec_, propagate_down, - &(this->blob_bottom_vec_)); + this->blob_bottom_vec_); Dtype sum_with_dropout = 0.; bottom_diff = this->blob_bottom_->cpu_diff(); for (int i = 0; i < this->blob_bottom_->count(); ++i) { diff --git a/src/caffe/test/test_memory_data_layer.cpp b/src/caffe/test/test_memory_data_layer.cpp index 3dc00345743..a79033f59f1 100644 --- a/src/caffe/test/test_memory_data_layer.cpp +++ b/src/caffe/test/test_memory_data_layer.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -70,7 +72,7 @@ TYPED_TEST(MemoryDataLayerTest, TestSetup) { md_param->set_width(this->width_); shared_ptr > layer( new MemoryDataLayer(layer_param)); - layer->SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->data_blob_->num(), this->batch_size_); EXPECT_EQ(this->data_blob_->channels(), this->channels_); EXPECT_EQ(this->data_blob_->height(), this->height_); @@ -93,12 +95,12 @@ TYPED_TEST(MemoryDataLayerTest, TestForward) { md_param->set_width(this->width_); shared_ptr > layer( new MemoryDataLayer(layer_param)); - layer->DataLayerSetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->DataLayerSetUp(this->blob_bottom_vec_, this->blob_top_vec_); layer->Reset(this->data_->mutable_cpu_data(), this->labels_->mutable_cpu_data(), this->data_->num()); for (int i = 0; i < this->batches_ * 6; ++i) { int batch_num = i % this->batches_; - layer->Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int j = 0; j < this->data_blob_->count(); ++j) { EXPECT_EQ(this->data_blob_->cpu_data()[j], this->data_->cpu_data()[ @@ -121,13 +123,14 @@ TYPED_TEST(MemoryDataLayerTest, AddDatumVectorDefaultTransform) { memory_data_param->set_height(this->height_); memory_data_param->set_width(this->width_); MemoryDataLayer layer(param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); - - vector datum_vector(this->batch_size_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + // We add batch_size*num_iter items, then for each iteration + // we forward batch_size elements + int num_iter = 5; + vector datum_vector(this->batch_size_ * num_iter); const size_t count = this->channels_ * this->height_ * this->width_; size_t pixel_index = 0; - for (int i = 0; i < this->batch_size_; ++i) { - LOG(ERROR) << "i " << i; + for (int i = 0; i < this->batch_size_ * num_iter; ++i) { datum_vector[i].set_channels(this->channels_); datum_vector[i].set_height(this->height_); datum_vector[i].set_width(this->width_); @@ -138,18 +141,18 @@ TYPED_TEST(MemoryDataLayerTest, AddDatumVectorDefaultTransform) { } datum_vector[i].set_data(&(pixels[0]), count); } - layer.AddDatumVector(datum_vector); int data_index; // Go through the data 5 times - for (int iter = 0; iter < 5; ++iter) { - layer.Forward(this->blob_bottom_vec_, &this->blob_top_vec_); + for (int iter = 0; iter < num_iter; ++iter) { + int offset = this->batch_size_ * iter; + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* data = this->data_blob_->cpu_data(); size_t index = 0; for (int i = 0; i < this->batch_size_; ++i) { - const string& data_string = datum_vector[i].data(); - EXPECT_EQ(i, this->label_blob_->cpu_data()[i]); + const string& data_string = datum_vector[offset + i].data(); + EXPECT_EQ(offset + i, this->label_blob_->cpu_data()[i]); for (int c = 0; c < this->channels_; ++c) { for (int h = 0; h < this->height_; ++h) { for (int w = 0; w < this->width_; ++w) { @@ -164,4 +167,130 @@ TYPED_TEST(MemoryDataLayerTest, AddDatumVectorDefaultTransform) { } } +TYPED_TEST(MemoryDataLayerTest, AddMatVectorDefaultTransform) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter param; + MemoryDataParameter* memory_data_param = param.mutable_memory_data_param(); + memory_data_param->set_batch_size(this->batch_size_); + memory_data_param->set_channels(this->channels_); + memory_data_param->set_height(this->height_); + memory_data_param->set_width(this->width_); + MemoryDataLayer layer(param); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + // We add batch_size*num_iter items, then for each iteration + // we forward batch_size elements + int num_iter = 5; + vector mat_vector(this->batch_size_ * num_iter); + vector label_vector(this->batch_size_ * num_iter); + for (int i = 0; i < this->batch_size_*num_iter; ++i) { + mat_vector[i] = cv::Mat(this->height_, this->width_, CV_8UC4); + label_vector[i] = i; + cv::randu(mat_vector[i], cv::Scalar::all(0), cv::Scalar::all(255)); + } + layer.AddMatVector(mat_vector, label_vector); + + int data_index; + const size_t count = this->channels_ * this->height_ * this->width_; + for (int iter = 0; iter < num_iter; ++iter) { + int offset = this->batch_size_ * iter; + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); + const Dtype* data = this->data_blob_->cpu_data(); + for (int i = 0; i < this->batch_size_; ++i) { + EXPECT_EQ(offset + i, this->label_blob_->cpu_data()[i]); + for (int h = 0; h < this->height_; ++h) { + const unsigned char* ptr_mat = mat_vector[offset + i].ptr(h); + int index = 0; + for (int w = 0; w < this->width_; ++w) { + for (int c = 0; c < this->channels_; ++c) { + data_index = (i*count) + (c * this->height_ + h) * this->width_ + w; + Dtype pixel = static_cast(ptr_mat[index++]); + EXPECT_EQ(static_cast(pixel), + data[data_index]); + } + } + } + } + } +} + +TYPED_TEST(MemoryDataLayerTest, TestSetBatchSize) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter param; + MemoryDataParameter* memory_data_param = param.mutable_memory_data_param(); + memory_data_param->set_batch_size(this->batch_size_); + memory_data_param->set_channels(this->channels_); + memory_data_param->set_height(this->height_); + memory_data_param->set_width(this->width_); + MemoryDataLayer layer(param); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + // first add data as usual + int num_iter = 5; + vector mat_vector(this->batch_size_ * num_iter); + vector label_vector(this->batch_size_ * num_iter); + for (int i = 0; i < this->batch_size_*num_iter; ++i) { + mat_vector[i] = cv::Mat(this->height_, this->width_, CV_8UC4); + label_vector[i] = i; + cv::randu(mat_vector[i], cv::Scalar::all(0), cv::Scalar::all(255)); + } + layer.AddMatVector(mat_vector, label_vector); + // then consume the data + int data_index; + const size_t count = this->channels_ * this->height_ * this->width_; + for (int iter = 0; iter < num_iter; ++iter) { + int offset = this->batch_size_ * iter; + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); + const Dtype* data = this->data_blob_->cpu_data(); + for (int i = 0; i < this->batch_size_; ++i) { + EXPECT_EQ(offset + i, this->label_blob_->cpu_data()[i]); + for (int h = 0; h < this->height_; ++h) { + const unsigned char* ptr_mat = mat_vector[offset + i].ptr(h); + int index = 0; + for (int w = 0; w < this->width_; ++w) { + for (int c = 0; c < this->channels_; ++c) { + data_index = (i*count) + (c * this->height_ + h) * this->width_ + w; + Dtype pixel = static_cast(ptr_mat[index++]); + EXPECT_EQ(static_cast(pixel), data[data_index]); + } + } + } + } + } + // and then add new data with different batch_size + int new_batch_size = 16; + layer.set_batch_size(new_batch_size); + mat_vector.clear(); + mat_vector.resize(new_batch_size * num_iter); + label_vector.clear(); + label_vector.resize(new_batch_size * num_iter); + for (int i = 0; i < new_batch_size*num_iter; ++i) { + mat_vector[i] = cv::Mat(this->height_, this->width_, CV_8UC4); + label_vector[i] = i; + cv::randu(mat_vector[i], cv::Scalar::all(0), cv::Scalar::all(255)); + } + layer.AddMatVector(mat_vector, label_vector); + + // finally consume new data and check if everything is fine + for (int iter = 0; iter < num_iter; ++iter) { + int offset = new_batch_size * iter; + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); + EXPECT_EQ(new_batch_size, this->blob_top_vec_[0]->num()); + EXPECT_EQ(new_batch_size, this->blob_top_vec_[1]->num()); + const Dtype* data = this->data_blob_->cpu_data(); + for (int i = 0; i < new_batch_size; ++i) { + EXPECT_EQ(offset + i, this->label_blob_->cpu_data()[i]); + for (int h = 0; h < this->height_; ++h) { + const unsigned char* ptr_mat = mat_vector[offset + i].ptr(h); + int index = 0; + for (int w = 0; w < this->width_; ++w) { + for (int c = 0; c < this->channels_; ++c) { + data_index = (i*count) + (c * this->height_ + h) * this->width_ + w; + Dtype pixel = static_cast(ptr_mat[index++]); + EXPECT_EQ(static_cast(pixel), data[data_index]); + } + } + } + } + } +} + } // namespace caffe diff --git a/src/caffe/test/test_multinomial_logistic_loss_layer.cpp b/src/caffe/test/test_multinomial_logistic_loss_layer.cpp index 1fc4c42f0f4..9038017e3e2 100644 --- a/src/caffe/test/test_multinomial_logistic_loss_layer.cpp +++ b/src/caffe/test/test_multinomial_logistic_loss_layer.cpp @@ -53,10 +53,10 @@ TYPED_TEST(MultinomialLogisticLossLayerTest, TestGradientCPU) { LayerParameter layer_param; Caffe::set_mode(Caffe::CPU); MultinomialLogisticLossLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); GradientChecker checker(1e-2, 2*1e-2, 1701, 0, 0.05); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_), 0); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); } } // namespace caffe diff --git a/src/caffe/test/test_mvn_layer.cpp b/src/caffe/test/test_mvn_layer.cpp index d3d8012f09f..933b4326417 100644 --- a/src/caffe/test/test_mvn_layer.cpp +++ b/src/caffe/test/test_mvn_layer.cpp @@ -40,8 +40,8 @@ TYPED_TEST(MVNLayerTest, TestForward) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; MVNLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Test mean int num = this->blob_bottom_->num(); int channels = this->blob_bottom_->channels(); @@ -75,8 +75,8 @@ TYPED_TEST(MVNLayerTest, TestForwardMeanOnly) { LayerParameter layer_param; layer_param.ParseFromString("mvn_param{normalize_variance: false}"); MVNLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Test mean int num = this->blob_bottom_->num(); int channels = this->blob_bottom_->channels(); @@ -107,8 +107,8 @@ TYPED_TEST(MVNLayerTest, TestForwardAcrossChannels) { LayerParameter layer_param; layer_param.ParseFromString("mvn_param{across_channels: true}"); MVNLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Test mean int num = this->blob_bottom_->num(); int channels = this->blob_bottom_->channels(); @@ -142,8 +142,8 @@ TYPED_TEST(MVNLayerTest, TestGradient) { LayerParameter layer_param; MVNLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(MVNLayerTest, TestGradientMeanOnly) { @@ -152,8 +152,8 @@ TYPED_TEST(MVNLayerTest, TestGradientMeanOnly) { layer_param.ParseFromString("mvn_param{normalize_variance: false}"); MVNLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(MVNLayerTest, TestGradientAcrossChannels) { @@ -162,8 +162,8 @@ TYPED_TEST(MVNLayerTest, TestGradientAcrossChannels) { layer_param.ParseFromString("mvn_param{across_channels: true}"); MVNLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } // namespace caffe diff --git a/src/caffe/test/test_net.cpp b/src/caffe/test/test_net.cpp index 9b10d100afb..1680a3f28d5 100644 --- a/src/caffe/test/test_net.cpp +++ b/src/caffe/test/test_net.cpp @@ -59,9 +59,9 @@ class NetTest : public MultiDeviceTest { const bool accuracy_layer = false) { string proto = "name: 'TinyTestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 5 " " channels: 2 " @@ -75,13 +75,17 @@ class NetTest : public MultiDeviceTest { " type: 'gaussian' " " std: 0.01 " " } " + " data_filler { " + " type: 'constant' " + " value: 0 " + " } " " } " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerproduct' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 1000 " " weight_filler { " @@ -93,25 +97,29 @@ class NetTest : public MultiDeviceTest { " value: 0 " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'data' " " top: 'innerproduct' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerproduct' " " bottom: 'label' " " top: 'top_loss' " "} "; if (accuracy_layer) { proto += - "layers: { " + "layer { " " name: 'loss' " - " type: ACCURACY " + " type: 'Accuracy' " " bottom: 'innerproduct' " " bottom: 'label' " " top: 'accuracy' " @@ -126,9 +134,9 @@ class NetTest : public MultiDeviceTest { virtual void InitTinyNetEuclidean(const bool force_backward = false) { string proto = "name: 'TinyTestEuclidLossNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 5 " " channels: 2 " @@ -146,9 +154,9 @@ class NetTest : public MultiDeviceTest { " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerproduct' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 1 " " weight_filler { " @@ -160,16 +168,20 @@ class NetTest : public MultiDeviceTest { " value: 0 " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'data' " " top: 'innerproduct' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerproduct' " " bottom: 'label' " "} "; @@ -186,9 +198,9 @@ class NetTest : public MultiDeviceTest { } const string& proto = "name: 'TrickyTestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 5 " " channels: 2 " @@ -206,9 +218,9 @@ class NetTest : public MultiDeviceTest { " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerproduct' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 1000 " " weight_filler { " @@ -220,16 +232,20 @@ class NetTest : public MultiDeviceTest { " value: 0 " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'data' " " top: 'transformed_data' " "} " - "layers: { " + "layer { " " name: 'innerproduct' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 1 " " weight_filler { " @@ -241,16 +257,20 @@ class NetTest : public MultiDeviceTest { " value: 0 " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'label' " " top: 'transformed_label' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + + " type: 'SoftmaxWithLoss' " + loss_weight_stream.str() + " bottom: 'transformed_data' " " bottom: 'transformed_label' " @@ -258,8 +278,8 @@ class NetTest : public MultiDeviceTest { InitNetFromProtoString(proto); } - // loss_weight is the loss weight for the EUCLIDEAN_LOSS layer output. - // midnet_loss_weight is the loss weight for the first INNER_PRODUCT layer + // loss_weight is the loss weight for the 'EuclideanLoss' layer output. + // midnet_loss_weight is the loss weight for the first 'InnerProduct' layer // output. Should both default to 0.0 if unspecified (i.e., if NULL is // passed to this function). virtual void InitUnsharedWeightsNet(const Dtype* loss_weight = NULL, @@ -273,9 +293,9 @@ class NetTest : public MultiDeviceTest { proto << "force_backward: true "; } proto << - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 5 " " channels: 2 " @@ -288,9 +308,9 @@ class NetTest : public MultiDeviceTest { " } " " top: 'data' " "} " - "layers: { " + "layer { " " name: 'innerproduct1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: " << bias_term << @@ -299,14 +319,12 @@ class NetTest : public MultiDeviceTest { " std: 10 " " } " " } " - " param: 'unsharedweights1' "; - if (bias_term) { - proto << " param: '' "; - } - proto << - " blobs_lr: " << blobs_lr_w1; + " param { " + " name: 'unsharedweights1' " + " lr_mult: " << blobs_lr_w1 << + " } "; if (bias_term) { - proto << " blobs_lr: " << blobs_lr_b1; + proto << " param { lr_mult: " << blobs_lr_b1 << " } "; } proto << " bottom: 'data' " @@ -316,9 +334,9 @@ class NetTest : public MultiDeviceTest { } proto << "} " - "layers: { " + "layer { " " name: 'innerproduct2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: " << bias_term << @@ -327,22 +345,20 @@ class NetTest : public MultiDeviceTest { " std: 10 " " } " " } " - " param: 'unsharedweights2' "; + " param { " + " name: 'unsharedweights2' " + " lr_mult: " << blobs_lr_w2 << + " } "; if (bias_term) { - proto << " param: '' "; + proto << " param { lr_mult: " << blobs_lr_b2 << " } "; } proto << " bottom: 'data' " - " blobs_lr: " << blobs_lr_w2; - if (bias_term) { - proto << " blobs_lr: " << blobs_lr_b2; - } - proto << " top: 'innerproduct2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS "; + " type: 'EuclideanLoss' "; if (loss_weight) { proto << " loss_weight: " << *loss_weight << " "; } @@ -356,9 +372,9 @@ class NetTest : public MultiDeviceTest { virtual void InitSharedWeightsNet() { const string& proto = "name: 'SharedWeightsNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 5 " " channels: 2 " @@ -371,9 +387,9 @@ class NetTest : public MultiDeviceTest { " } " " top: 'data' " "} " - "layers: { " + "layer { " " name: 'innerproduct1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -382,13 +398,13 @@ class NetTest : public MultiDeviceTest { " std: 10 " " } " " } " - " param: 'sharedweights' " + " param { name: 'sharedweights' } " " bottom: 'data' " " top: 'innerproduct1' " "} " - "layers: { " + "layer { " " name: 'innerproduct2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -397,13 +413,13 @@ class NetTest : public MultiDeviceTest { " std: 10 " " } " " } " - " param: 'sharedweights' " + " param { name: 'sharedweights' } " " bottom: 'data' " " top: 'innerproduct2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerproduct1' " " bottom: 'innerproduct2' " "} "; @@ -413,9 +429,9 @@ class NetTest : public MultiDeviceTest { virtual void InitDiffDataUnsharedWeightsNet() { const string& proto = "name: 'DiffDataUnsharedWeightsNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 10 " " channels: 10 " @@ -433,9 +449,9 @@ class NetTest : public MultiDeviceTest { " top: 'data1' " " top: 'data2' " "} " - "layers: { " + "layer { " " name: 'innerproduct1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -444,13 +460,13 @@ class NetTest : public MultiDeviceTest { " value: 0.5 " " } " " } " - " param: 'unsharedweights1' " + " param { name: 'unsharedweights1' } " " bottom: 'data1' " " top: 'innerproduct1' " "} " - "layers: { " + "layer { " " name: 'innerproduct2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -459,13 +475,13 @@ class NetTest : public MultiDeviceTest { " value: 0.5 " " } " " } " - " param: 'unsharedweights2' " + " param { name: 'unsharedweights2' } " " bottom: 'innerproduct1' " " top: 'innerproduct2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'data2' " " bottom: 'innerproduct2' " "} "; @@ -475,9 +491,9 @@ class NetTest : public MultiDeviceTest { virtual void InitDiffDataSharedWeightsNet() { const string& proto = "name: 'DiffDataSharedWeightsNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 10 " " channels: 10 " @@ -495,9 +511,9 @@ class NetTest : public MultiDeviceTest { " top: 'data1' " " top: 'data2' " "} " - "layers: { " + "layer { " " name: 'innerproduct1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -506,13 +522,13 @@ class NetTest : public MultiDeviceTest { " value: 0.5 " " } " " } " - " param: 'sharedweights' " + " param { name: 'sharedweights' } " " bottom: 'data1' " " top: 'innerproduct1' " "} " - "layers: { " + "layer { " " name: 'innerproduct2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -521,13 +537,13 @@ class NetTest : public MultiDeviceTest { " value: 0.5 " " } " " } " - " param: 'sharedweights' " + " param { name: 'sharedweights' } " " bottom: 'innerproduct1' " " top: 'innerproduct2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'data2' " " bottom: 'innerproduct2' " "} "; @@ -542,9 +558,9 @@ class NetTest : public MultiDeviceTest { "input_dim: 3 " "input_dim: 100 " "input_dim: 100 " - "layers: { " + "layer { " " name: 'conv1' " - " type: CONVOLUTION " + " type: 'Convolution' " " bottom: 'data' " " top: 'conv1' " " convolution_param { " @@ -561,15 +577,15 @@ class NetTest : public MultiDeviceTest { " } " " } " "} " - "layers: { " + "layer { " " name: 'relu1' " - " type: RELU " + " type: 'ReLU' " " bottom: 'conv1' " " top: 'conv1' " "} " - "layers: { " + "layer { " " name: 'pool1' " - " type: POOLING " + " type: 'Pooling' " " bottom: 'conv1' " " top: 'pool1' " " pooling_param { " @@ -578,18 +594,18 @@ class NetTest : public MultiDeviceTest { " stride: 2 " " } " "} " - "layers: { " + "layer { " " name: 'norm1' " - " type: LRN " + " type: 'LRN' " " bottom: 'pool1' " " top: 'norm1' " " lrn_param { " " local_size: 3 " " } " "} " - "layers: { " + "layer { " " name: 'softmax' " - " type: SOFTMAX " + " type: 'Softmax' " " bottom: 'norm1' " " top: 'softmax' " "} "; @@ -698,7 +714,7 @@ TYPED_TEST(NetTest, TestBottomNeedBackwardTricky) { TYPED_TEST(NetTest, TestLossWeight) { typedef typename TypeParam::Dtype Dtype; // First, compute the loss and gradients with no loss_weight specified. - // In this case, the loss weight for the EUCLIDEAN_LOSS layer should default + // In this case, the loss weight for the 'EuclideanLoss' layer should default // to 1. vector*> bottom; Caffe::set_random_seed(this->seed_); @@ -792,8 +808,8 @@ TYPED_TEST(NetTest, TestComboLossWeight) { const bool kForceBackward = true; const Dtype kErrorMargin = 1e-4; - // Get the loss and gradients with EUCLIDEAN_LOSS weight 1, - // INNER_PRODUCT weight 1. + // Get the loss and gradients with 'EuclideanLoss' weight 1, + // 'InnerProduct' weight 1. loss_weight = 1; midnet_loss_weight = 1; Caffe::set_random_seed(this->seed_); @@ -921,7 +937,7 @@ TYPED_TEST(NetTest, TestBackwardWithAccuracyLayer) { this->InitTinyNet(kForceBackward, kAccuracyLayer); EXPECT_TRUE(this->net_->has_blob("accuracy")); vector*> bottom; - // Test that we can do Backward even though we have an ACCURACY layer. + // Test that we can do Backward even though we have an 'Accuracy' layer. this->net_->ForwardBackward(bottom); } @@ -993,7 +1009,7 @@ TYPED_TEST(NetTest, TestSharedWeightsUpdate) { // Check that data blobs of shared weights share the same location in memory. EXPECT_EQ(ip1_weights->cpu_data(), ip2_weights->cpu_data()); // Check that diff blobs of shared weights are at different locations in - // locations. (The diffs should be accumulated at update time.) + // memory. (The diffs should be accumulated at update time.) EXPECT_NE(ip1_weights->cpu_diff(), ip2_weights->cpu_diff()); this->net_->Forward(bottom); this->net_->Backward(); @@ -1068,6 +1084,54 @@ TYPED_TEST(NetTest, TestSharedWeightsUpdate) { } } +TYPED_TEST(NetTest, TestSharedWeightsResume) { + typedef typename TypeParam::Dtype Dtype; + + // Create a net with weight sharing; Update it once. + Caffe::set_random_seed(this->seed_); + this->InitDiffDataSharedWeightsNet(); + vector*> bottom; + EXPECT_EQ(this->net_->layer_names()[1], "innerproduct1"); + EXPECT_EQ(this->net_->layer_names()[2], "innerproduct2"); + Blob* ip1_weights = this->net_->layers()[1]->blobs()[0].get(); + Blob* ip2_weights = this->net_->layers()[2]->blobs()[0].get(); + // Check that data blobs of shared weights share the same location in memory. + EXPECT_EQ(ip1_weights->cpu_data(), ip2_weights->cpu_data()); + // Check that diff blobs of shared weights are at different locations in + // memory. (The diffs should be accumulated at update time.) + EXPECT_NE(ip1_weights->cpu_diff(), ip2_weights->cpu_diff()); + this->net_->ForwardBackward(bottom); + this->net_->Update(); + Blob shared_params; + const bool kReshape = true; + const bool kCopyDiff = false; + shared_params.CopyFrom(*ip1_weights, kCopyDiff, kReshape); + const int count = ip1_weights->count(); + + // Write the net to a NetParameter, as in Solver::Snapshot. + NetParameter net_param; + this->net_->ToProto(&net_param); + + // Reinitialize the net and copy parameters from net_param, as in + // Solver::Restore. + Caffe::set_random_seed(this->seed_); + this->InitDiffDataSharedWeightsNet(); + this->net_->CopyTrainedLayersFrom(net_param); + ip1_weights = this->net_->layers()[1]->blobs()[0].get(); + ip2_weights = this->net_->layers()[2]->blobs()[0].get(); + ASSERT_FALSE(NULL == ip1_weights); + ASSERT_FALSE(NULL == ip2_weights); + EXPECT_NE(ip1_weights, ip2_weights); + // Check that data blobs of shared weights share the same location in memory. + EXPECT_EQ(ip1_weights->cpu_data(), ip2_weights->cpu_data()); + for (int i = 0; i < count; ++i) { + EXPECT_FLOAT_EQ(shared_params.cpu_data()[i], ip1_weights->cpu_data()[i]); + } + // Check that diff blobs of shared weights are at different locations in + // memory. (The diffs should be accumulated at update time.) + EXPECT_NE(ip1_weights->cpu_diff(), ip2_weights->cpu_diff()); +} + TYPED_TEST(NetTest, TestParamPropagateDown) { typedef typename TypeParam::Dtype Dtype; vector*> bottom; @@ -1107,7 +1171,7 @@ TYPED_TEST(NetTest, TestParamPropagateDown) { for (int i = 0; i < num_params; ++i) { const Dtype param_asum = caffe_cpu_asum(params2[i]->count(), params2[i]->cpu_diff()); - EXPECT_EQ(param_asum, param_asums[i]); + EXPECT_FLOAT_EQ(param_asum, param_asums[i]); } // Change a subset of the learning rates to zero; check that we see zero @@ -1124,9 +1188,9 @@ TYPED_TEST(NetTest, TestParamPropagateDown) { const Dtype param_asum = caffe_cpu_asum(params3[i]->count(), params3[i]->cpu_diff()); if (i == 1 || i == 2) { - EXPECT_EQ(0, param_asum); + EXPECT_FLOAT_EQ(0, param_asum); } else { - EXPECT_EQ(param_asum, param_asums[i]); + EXPECT_FLOAT_EQ(param_asum, param_asums[i]); } } @@ -1143,9 +1207,9 @@ TYPED_TEST(NetTest, TestParamPropagateDown) { const Dtype param_asum = caffe_cpu_asum(params4[i]->count(), params4[i]->cpu_diff()); if (i == 0 || i == 3) { - EXPECT_EQ(0, param_asum); + EXPECT_FLOAT_EQ(0, param_asum); } else { - EXPECT_EQ(param_asum, param_asums[i]); + EXPECT_FLOAT_EQ(param_asum, param_asums[i]); } } } @@ -1209,21 +1273,21 @@ class FilterNetTest : public ::testing::Test { TEST_F(FilterNetTest, TestNoFilter) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1233,9 +1297,9 @@ TEST_F(FilterNetTest, TestNoFilter) { TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { const string& input_proto = "name: 'LeNet' " - "layers { " + "layer { " " name: 'mnist' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " " data_param { " @@ -1247,9 +1311,9 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " include: { phase: TRAIN } " "} " - "layers { " + "layer { " " name: 'mnist' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " " data_param { " @@ -1261,13 +1325,17 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " include: { phase: TEST } " "} " - "layers { " + "layer { " " name: 'conv1' " - " type: CONVOLUTION " + " type: 'Convolution' " " bottom: 'data' " " top: 'conv1' " - " blobs_lr: 1 " - " blobs_lr: 2 " + " param { " + " lr_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " } " " convolution_param { " " num_output: 20 " " kernel_size: 5 " @@ -1280,13 +1348,17 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " } " "} " - "layers { " + "layer { " " name: 'ip1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'conv1' " " top: 'ip1' " - " blobs_lr: 1 " - " blobs_lr: 2 " + " param { " + " lr_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " } " " inner_product_param { " " num_output: 10 " " weight_filler { " @@ -1297,17 +1369,17 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " } " "} " - "layers { " + "layer { " " name: 'accuracy' " - " type: ACCURACY " + " type: 'Accuracy' " " bottom: 'ip1' " " bottom: 'label' " " top: 'accuracy' " " include: { phase: TEST } " "} " - "layers { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'ip2' " " bottom: 'label' " " top: 'loss' " @@ -1316,9 +1388,9 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { const string input_proto_test = "state: { phase: TEST } " + input_proto; const string output_proto_train = "name: 'LeNet' " - "layers { " + "layer { " " name: 'mnist' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " " data_param { " @@ -1330,13 +1402,17 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " include: { phase: TRAIN } " "} " - "layers { " + "layer { " " name: 'conv1' " - " type: CONVOLUTION " + " type: 'Convolution' " " bottom: 'data' " " top: 'conv1' " - " blobs_lr: 1 " - " blobs_lr: 2 " + " param { " + " lr_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " } " " convolution_param { " " num_output: 20 " " kernel_size: 5 " @@ -1349,13 +1425,17 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " } " "} " - "layers { " + "layer { " " name: 'ip1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'conv1' " " top: 'ip1' " - " blobs_lr: 1 " - " blobs_lr: 2 " + " param { " + " lr_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " } " " inner_product_param { " " num_output: 10 " " weight_filler { " @@ -1366,18 +1446,18 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " } " "} " - "layers { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'ip2' " " bottom: 'label' " " top: 'loss' " "} "; const string& output_proto_test = "name: 'LeNet' " - "layers { " + "layer { " " name: 'mnist' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " " data_param { " @@ -1389,13 +1469,17 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " include: { phase: TEST } " "} " - "layers { " + "layer { " " name: 'conv1' " - " type: CONVOLUTION " + " type: 'Convolution' " " bottom: 'data' " " top: 'conv1' " - " blobs_lr: 1 " - " blobs_lr: 2 " + " param { " + " lr_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " } " " convolution_param { " " num_output: 20 " " kernel_size: 5 " @@ -1408,13 +1492,17 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " } " "} " - "layers { " + "layer { " " name: 'ip1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'conv1' " " top: 'ip1' " - " blobs_lr: 1 " - " blobs_lr: 2 " + " param { " + " lr_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " } " " inner_product_param { " " num_output: 10 " " weight_filler { " @@ -1425,17 +1513,17 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { " } " " } " "} " - "layers { " + "layer { " " name: 'accuracy' " - " type: ACCURACY " + " type: 'Accuracy' " " bottom: 'ip1' " " bottom: 'label' " " top: 'accuracy' " " include: { phase: TEST } " "} " - "layers { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'ip2' " " bottom: 'label' " " top: 'loss' " @@ -1446,55 +1534,41 @@ TEST_F(FilterNetTest, TestFilterLeNetTrainTest) { output_proto_test + " state: { phase: TEST } "; this->RunFilterNetTest(input_proto_train, output_proto_train_explicit); this->RunFilterNetTest(input_proto_test, output_proto_test_explicit); - - // Also check that nets are filtered according to the Caffe singleton phase, - // if not explicitly specified in the input proto. - Caffe::set_phase(Caffe::TRAIN); - this->RunFilterNetTest(input_proto, output_proto_train); - Caffe::set_phase(Caffe::TEST); - this->RunFilterNetTest(input_proto, output_proto_test); - - // Finally, check that the current Caffe singleton phase is ignored if the - // phase is explicitly specified in the input proto. - Caffe::set_phase(Caffe::TEST); - this->RunFilterNetTest(input_proto_train, output_proto_train_explicit); - Caffe::set_phase(Caffe::TRAIN); - this->RunFilterNetTest(input_proto_test, output_proto_test_explicit); } TEST_F(FilterNetTest, TestFilterOutByStage) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " " include: { stage: 'mystage' } " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; const string& output_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1504,36 +1578,36 @@ TEST_F(FilterNetTest, TestFilterOutByStage) { TEST_F(FilterNetTest, TestFilterOutByStage2) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { stage: 'mystage' } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; const string& output_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1544,22 +1618,22 @@ TEST_F(FilterNetTest, TestFilterInByStage) { const string& input_proto = "state: { stage: 'mystage' } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { stage: 'mystage' } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1569,22 +1643,22 @@ TEST_F(FilterNetTest, TestFilterInByStage) { TEST_F(FilterNetTest, TestFilterInByStage2) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " exclude: { stage: 'mystage' } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1595,22 +1669,22 @@ TEST_F(FilterNetTest, TestFilterOutByMultipleStage) { const string& input_proto = "state: { stage: 'mystage' } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { stage: 'mystage' stage: 'myotherstage' } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { stage: 'mystage' } " @@ -1618,15 +1692,15 @@ TEST_F(FilterNetTest, TestFilterOutByMultipleStage) { const string& output_proto = "state: { stage: 'mystage' } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { stage: 'mystage' } " @@ -1638,23 +1712,23 @@ TEST_F(FilterNetTest, TestFilterInByMultipleStage) { const string& input_proto = "state: { stage: 'mystage' } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { stage: 'myotherstage' } " " include: { stage: 'mystage' } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { stage: 'mystage' } " @@ -1666,22 +1740,22 @@ TEST_F(FilterNetTest, TestFilterInByMultipleStage2) { const string& input_proto = "state: { stage: 'mystage' stage: 'myotherstage' } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { stage: 'mystage' stage: 'myotherstage' } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { stage: 'mystage' } " @@ -1693,22 +1767,22 @@ TEST_F(FilterNetTest, TestFilterInByNotStage) { const string& input_proto = "state: { stage: 'mystage' } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { not_stage: 'myotherstage' } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { not_stage: 'myotherstage' } " @@ -1720,22 +1794,22 @@ TEST_F(FilterNetTest, TestFilterOutByNotStage) { const string& input_proto = "state: { stage: 'mystage' } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { not_stage: 'mystage' } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { not_stage: 'mystage' } " @@ -1743,9 +1817,9 @@ TEST_F(FilterNetTest, TestFilterOutByNotStage) { const string& output_proto = "state: { stage: 'mystage' } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} "; @@ -1755,36 +1829,36 @@ TEST_F(FilterNetTest, TestFilterOutByNotStage) { TEST_F(FilterNetTest, TestFilterOutByMinLevel) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { min_level: 3 } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; const string& output_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1794,36 +1868,36 @@ TEST_F(FilterNetTest, TestFilterOutByMinLevel) { TEST_F(FilterNetTest, TestFilterOutByMaxLevel) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { max_level: -3 } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; const string& output_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1833,22 +1907,22 @@ TEST_F(FilterNetTest, TestFilterOutByMaxLevel) { TEST_F(FilterNetTest, TestFilterInByMinLevel) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { min_level: 0 } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1859,22 +1933,22 @@ TEST_F(FilterNetTest, TestFilterInByMinLevel2) { const string& input_proto = "state: { level: 7 } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { min_level: 3 } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1884,22 +1958,22 @@ TEST_F(FilterNetTest, TestFilterInByMinLevel2) { TEST_F(FilterNetTest, TestFilterInByMaxLevel) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { max_level: 0 } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1910,22 +1984,22 @@ TEST_F(FilterNetTest, TestFilterInByMaxLevel2) { const string& input_proto = "state: { level: -7 } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { max_level: -3 } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -1935,22 +2009,22 @@ TEST_F(FilterNetTest, TestFilterInByMaxLevel2) { TEST_F(FilterNetTest, TestFilterInOutByIncludeMultiRule) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { min_level: 2 phase: TRAIN } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { min_level: 2 phase: TEST } " @@ -1962,15 +2036,15 @@ TEST_F(FilterNetTest, TestFilterInOutByIncludeMultiRule) { const string& output_proto_train = "state: { level: 4 phase: TRAIN } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { min_level: 2 phase: TRAIN } " @@ -1978,15 +2052,15 @@ TEST_F(FilterNetTest, TestFilterInOutByIncludeMultiRule) { const string& output_proto_test = "state: { level: 4 phase: TEST } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { min_level: 2 phase: TEST } " @@ -1998,23 +2072,23 @@ TEST_F(FilterNetTest, TestFilterInOutByIncludeMultiRule) { TEST_F(FilterNetTest, TestFilterInByIncludeMultiRule) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " include: { min_level: 2 phase: TRAIN } " " include: { phase: TEST } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { min_level: 2 phase: TEST } " @@ -2031,22 +2105,22 @@ TEST_F(FilterNetTest, TestFilterInByIncludeMultiRule) { TEST_F(FilterNetTest, TestFilterInOutByExcludeMultiRule) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " exclude: { min_level: 2 phase: TRAIN } " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " exclude: { min_level: 2 phase: TEST } " @@ -2058,15 +2132,15 @@ TEST_F(FilterNetTest, TestFilterInOutByExcludeMultiRule) { const string& output_proto_train = "state: { level: 4 phase: TRAIN } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " exclude: { min_level: 2 phase: TEST } " @@ -2074,15 +2148,15 @@ TEST_F(FilterNetTest, TestFilterInOutByExcludeMultiRule) { const string& output_proto_test = "state: { level: 4 phase: TEST } " "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " " exclude: { min_level: 2 phase: TRAIN } " diff --git a/src/caffe/test/test_neuron_layer.cpp b/src/caffe/test/test_neuron_layer.cpp index 4c19d3f9bae..ad10720116d 100644 --- a/src/caffe/test/test_neuron_layer.cpp +++ b/src/caffe/test/test_neuron_layer.cpp @@ -1,6 +1,7 @@ #include #include +#include "google/protobuf/text_format.h" #include "gtest/gtest.h" #include "caffe/blob.hpp" @@ -42,10 +43,10 @@ class NeuronLayerTest : public MultiDeviceTest { if (dropout_ratio != 0.5) { layer_param.mutable_dropout_param()->set_dropout_ratio(dropout_ratio); } - Caffe::set_phase(Caffe::TRAIN); DropoutLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer_param.set_phase(TRAIN); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); @@ -66,6 +67,38 @@ class NeuronLayerTest : public MultiDeviceTest { const Dtype empirical_dropout_ratio = 1 - num_kept / Dtype(count); EXPECT_NEAR(empirical_dropout_ratio, dropout_ratio, 1.96 * std_error); } + + void TestExpForward(const float base, const float scale, const float shift) { + LayerParameter layer_param; + layer_param.mutable_exp_param()->set_base(base); + layer_param.mutable_exp_param()->set_scale(scale); + layer_param.mutable_exp_param()->set_shift(shift); + ExpLayer layer(layer_param); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); + const Dtype kDelta = 2e-4; + const Dtype* bottom_data = blob_bottom_->cpu_data(); + const Dtype* top_data = blob_top_->cpu_data(); + for (int i = 0; i < blob_bottom_->count(); ++i) { + const Dtype bottom_val = bottom_data[i]; + const Dtype top_val = top_data[i]; + if (base == -1) { + EXPECT_NEAR(top_val, exp(shift + scale * bottom_val), kDelta); + } else { + EXPECT_NEAR(top_val, pow(base, shift + scale * bottom_val), kDelta); + } + } + } + + void TestExpGradient(const float base, const float scale, const float shift) { + LayerParameter layer_param; + layer_param.mutable_exp_param()->set_base(base); + layer_param.mutable_exp_param()->set_scale(scale); + layer_param.mutable_exp_param()->set_shift(shift); + ExpLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-3); + checker.CheckGradientEltwise(&layer, blob_bottom_vec_, blob_top_vec_); + } }; TYPED_TEST_CASE(NeuronLayerTest, TestDtypesAndDevices); @@ -74,8 +107,8 @@ TYPED_TEST(NeuronLayerTest, TestAbsVal) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; AbsValLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); const int count = this->blob_bottom_->count(); @@ -89,16 +122,16 @@ TYPED_TEST(NeuronLayerTest, TestAbsGradient) { LayerParameter layer_param; AbsValLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3, 1701, 0., 0.01); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(NeuronLayerTest, TestReLU) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ReLULayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); @@ -113,42 +146,47 @@ TYPED_TEST(NeuronLayerTest, TestReLUGradient) { LayerParameter layer_param; ReLULayer layer(layer_param); GradientChecker checker(1e-2, 1e-3, 1701, 0., 0.01); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(NeuronLayerTest, TestReLUWithNegativeSlope) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; - layer_param.ParseFromString("relu_param{negative_slope:0.01}"); + CHECK(google::protobuf::TextFormat::ParseFromString( + "relu_param { negative_slope: 0.01 }", &layer_param)); ReLULayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); for (int i = 0; i < this->blob_bottom_->count(); ++i) { - EXPECT_GE(top_data[i], 0.); - EXPECT_TRUE(top_data[i] == 0 || top_data[i] == bottom_data[i]); + if (top_data[i] >= 0) { + EXPECT_FLOAT_EQ(top_data[i], bottom_data[i]); + } else { + EXPECT_FLOAT_EQ(top_data[i], bottom_data[i] * 0.01); + } } } TYPED_TEST(NeuronLayerTest, TestReLUGradientWithNegativeSlope) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; - layer_param.ParseFromString("relu_param{negative_slope:0.01}"); + CHECK(google::protobuf::TextFormat::ParseFromString( + "relu_param { negative_slope: 0.01 }", &layer_param)); ReLULayer layer(layer_param); GradientChecker checker(1e-2, 1e-3, 1701, 0., 0.01); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(NeuronLayerTest, TestSigmoid) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; SigmoidLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); @@ -165,16 +203,16 @@ TYPED_TEST(NeuronLayerTest, TestSigmoidGradient) { LayerParameter layer_param; SigmoidLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3, 1701, 0., 0.01); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(NeuronLayerTest, TestTanH) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; TanHLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Test exact values for (int i = 0; i < this->blob_bottom_->num(); ++i) { for (int j = 0; j < this->blob_bottom_->channels(); ++j) { @@ -197,8 +235,90 @@ TYPED_TEST(NeuronLayerTest, TestTanHGradient) { LayerParameter layer_param; TanHLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); +} + +TYPED_TEST(NeuronLayerTest, TestExpLayer) { + typedef typename TypeParam::Dtype Dtype; + // Test default base of "-1" -- should actually set base := e. + const Dtype kBase = -1; + const Dtype kScale = 1; + const Dtype kShift = 0; + this->TestExpForward(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpGradient) { + typedef typename TypeParam::Dtype Dtype; + // Test default base of "-1" -- should actually set base := e. + const Dtype kBase = -1; + const Dtype kScale = 1; + const Dtype kShift = 0; + this->TestExpGradient(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpLayerBase2) { + typedef typename TypeParam::Dtype Dtype; + const Dtype kBase = 2; + const Dtype kScale = 1; + const Dtype kShift = 0; + this->TestExpForward(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpGradientBase2) { + typedef typename TypeParam::Dtype Dtype; + const Dtype kBase = 2; + const Dtype kScale = 1; + const Dtype kShift = 0; + this->TestExpGradient(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpLayerBase2Shift1) { + typedef typename TypeParam::Dtype Dtype; + const Dtype kBase = 2; + const Dtype kScale = 1; + const Dtype kShift = 1; + this->TestExpForward(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpGradientBase2Shift1) { + typedef typename TypeParam::Dtype Dtype; + const Dtype kBase = 2; + const Dtype kScale = 1; + const Dtype kShift = 1; + this->TestExpGradient(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpLayerBase2Scale3) { + typedef typename TypeParam::Dtype Dtype; + const Dtype kBase = 2; + const Dtype kScale = 3; + const Dtype kShift = 0; + this->TestExpForward(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpGradientBase2Scale3) { + typedef typename TypeParam::Dtype Dtype; + const Dtype kBase = 2; + const Dtype kScale = 3; + const Dtype kShift = 0; + this->TestExpGradient(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpLayerBase2Shift1Scale3) { + typedef typename TypeParam::Dtype Dtype; + const Dtype kBase = 2; + const Dtype kScale = 3; + const Dtype kShift = 1; + this->TestExpForward(kBase, kScale, kShift); +} + +TYPED_TEST(NeuronLayerTest, TestExpGradientBase2Shift1Scale3) { + typedef typename TypeParam::Dtype Dtype; + const Dtype kBase = 2; + const Dtype kScale = 3; + const Dtype kShift = 1; + this->TestExpGradient(kBase, kScale, kShift); } TYPED_TEST(NeuronLayerTest, TestDropoutHalf) { @@ -214,10 +334,10 @@ TYPED_TEST(NeuronLayerTest, TestDropoutThreeQuarters) { TYPED_TEST(NeuronLayerTest, TestDropoutTestPhase) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; - Caffe::set_phase(Caffe::TEST); + layer_param.set_phase(TEST); DropoutLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); @@ -231,29 +351,29 @@ TYPED_TEST(NeuronLayerTest, TestDropoutTestPhase) { TYPED_TEST(NeuronLayerTest, TestDropoutGradient) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; - Caffe::set_phase(Caffe::TRAIN); + layer_param.set_phase(TRAIN); DropoutLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(NeuronLayerTest, TestDropoutGradientTest) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; - Caffe::set_phase(Caffe::TEST); + layer_param.set_phase(TEST); DropoutLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(NeuronLayerTest, TestBNLL) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; BNLLLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); @@ -268,8 +388,8 @@ TYPED_TEST(NeuronLayerTest, TestBNLLGradient) { LayerParameter layer_param; BNLLLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } #ifdef USE_CUDNN @@ -300,8 +420,8 @@ TYPED_TEST(CuDNNNeuronLayerTest, TestReLUCuDNN) { Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; CuDNNReLULayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const TypeParam* bottom_data = this->blob_bottom_->cpu_data(); const TypeParam* top_data = this->blob_top_->cpu_data(); @@ -316,42 +436,47 @@ TYPED_TEST(CuDNNNeuronLayerTest, TestReLUGradientCuDNN) { LayerParameter layer_param; CuDNNReLULayer layer(layer_param); GradientChecker checker(1e-2, 1e-3, 1701, 0., 0.01); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(CuDNNNeuronLayerTest, TestReLUWithNegativeSlopeCuDNN) { Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; - layer_param.ParseFromString("relu_param{negative_slope:0.01}"); + CHECK(google::protobuf::TextFormat::ParseFromString( + "relu_param { negative_slope: 0.01 }", &layer_param)); CuDNNReLULayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const TypeParam* bottom_data = this->blob_bottom_->cpu_data(); const TypeParam* top_data = this->blob_top_->cpu_data(); for (int i = 0; i < this->blob_bottom_->count(); ++i) { - EXPECT_GE(top_data[i], 0.); - EXPECT_TRUE(top_data[i] == 0 || top_data[i] == bottom_data[i]); + if (top_data[i] >= 0) { + EXPECT_FLOAT_EQ(top_data[i], bottom_data[i]); + } else { + EXPECT_FLOAT_EQ(top_data[i], bottom_data[i] * 0.01); + } } } TYPED_TEST(CuDNNNeuronLayerTest, TestReLUGradientWithNegativeSlopeCuDNN) { Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; - layer_param.ParseFromString("relu_param{negative_slope:0.01}"); + CHECK(google::protobuf::TextFormat::ParseFromString( + "relu_param { negative_slope: 0.01 }", &layer_param)); CuDNNReLULayer layer(layer_param); GradientChecker checker(1e-2, 1e-3, 1701, 0., 0.01); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(CuDNNNeuronLayerTest, TestSigmoidCuDNN) { Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; CuDNNSigmoidLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const TypeParam* bottom_data = this->blob_bottom_->cpu_data(); const TypeParam* top_data = this->blob_top_->cpu_data(); @@ -368,16 +493,16 @@ TYPED_TEST(CuDNNNeuronLayerTest, TestSigmoidGradientCuDNN) { LayerParameter layer_param; CuDNNSigmoidLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3, 1701, 0., 0.01); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } TYPED_TEST(CuDNNNeuronLayerTest, TestTanHCuDNN) { Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; CuDNNTanHLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Test exact values for (int i = 0; i < this->blob_bottom_->num(); ++i) { for (int j = 0; j < this->blob_bottom_->channels(); ++j) { @@ -400,8 +525,8 @@ TYPED_TEST(CuDNNNeuronLayerTest, TestTanHGradientCuDNN) { LayerParameter layer_param; CuDNNTanHLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } #endif diff --git a/src/caffe/test/test_pooling_layer.cpp b/src/caffe/test/test_pooling_layer.cpp index ec23a682c2f..435caa8381e 100644 --- a/src/caffe/test/test_pooling_layer.cpp +++ b/src/caffe/test/test_pooling_layer.cpp @@ -73,7 +73,7 @@ class PoolingLayerTest : public MultiDeviceTest { blob_bottom_->mutable_cpu_data()[i + 14] = 3; } PoolingLayer layer(layer_param); - layer.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); EXPECT_EQ(blob_top_->num(), num); EXPECT_EQ(blob_top_->channels(), channels); EXPECT_EQ(blob_top_->height(), 2); @@ -84,7 +84,7 @@ class PoolingLayerTest : public MultiDeviceTest { EXPECT_EQ(blob_top_mask_->height(), 2); EXPECT_EQ(blob_top_mask_->width(), 4); } - layer.Forward(blob_bottom_vec_, &blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); // Expected output: 2x 2 channels of: // [9 5 5 8] // [9 5 5 8] @@ -171,7 +171,7 @@ class PoolingLayerTest : public MultiDeviceTest { blob_bottom_->mutable_cpu_data()[i + 35] = 11; } PoolingLayer layer(layer_param); - layer.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); EXPECT_EQ(blob_top_->num(), num); EXPECT_EQ(blob_top_->channels(), channels); EXPECT_EQ(blob_top_->height(), 4); @@ -182,7 +182,7 @@ class PoolingLayerTest : public MultiDeviceTest { EXPECT_EQ(blob_top_mask_->height(), 4); EXPECT_EQ(blob_top_mask_->width(), 5); } - layer.Forward(blob_bottom_vec_, &blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); // Expected output: 2x 2 channels of: // [35 32 26 27 27] // [32 33 33 27 27] @@ -296,7 +296,7 @@ class PoolingLayerTest : public MultiDeviceTest { blob_bottom_->mutable_cpu_data()[i + 35] = 11; } PoolingLayer layer(layer_param); - layer.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); EXPECT_EQ(blob_top_->num(), num); EXPECT_EQ(blob_top_->channels(), channels); EXPECT_EQ(blob_top_->height(), 5); @@ -307,7 +307,7 @@ class PoolingLayerTest : public MultiDeviceTest { EXPECT_EQ(blob_top_mask_->height(), 5); EXPECT_EQ(blob_top_mask_->width(), 4); } - layer.Forward(blob_bottom_vec_, &blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); // Expected output: 2x 2 channels of: // [35 32 26 26] // [32 32 27 27] @@ -377,7 +377,7 @@ TYPED_TEST(PoolingLayerTest, TestSetup) { pooling_param->set_kernel_size(3); pooling_param->set_stride(2); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_->channels()); EXPECT_EQ(this->blob_top_->height(), 3); @@ -393,13 +393,27 @@ TYPED_TEST(PoolingLayerTest, TestSetupPadded) { pooling_param->set_pad(1); pooling_param->set_pool(PoolingParameter_PoolMethod_AVE); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_->channels()); EXPECT_EQ(this->blob_top_->height(), 4); EXPECT_EQ(this->blob_top_->width(), 3); } +TYPED_TEST(PoolingLayerTest, TestSetupGlobalPooling) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + PoolingParameter* pooling_param = layer_param.mutable_pooling_param(); + pooling_param->set_global_pooling(true); + pooling_param->set_pool(PoolingParameter_PoolMethod_AVE); + PoolingLayer layer(layer_param); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); + EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_->channels()); + EXPECT_EQ(this->blob_top_->height(), 1); + EXPECT_EQ(this->blob_top_->width(), 1); +} + /* TYPED_TEST(PoolingLayerTest, PrintBackward) { LayerParameter layer_param; @@ -407,8 +421,8 @@ TYPED_TEST(PoolingLayerTest, PrintBackward) { layer_param.set_stride(2); layer_param.set_pool(LayerParameter_PoolMethod_MAX); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_bottom_->count(); ++i) { cout << "bottom data " << i << " " << this->blob_bottom_->cpu_data()[i] << endl; } @@ -419,7 +433,7 @@ TYPED_TEST(PoolingLayerTest, PrintBackward) { for (int i = 0; i < this->blob_top_->count(); ++i) { this->blob_top_->mutable_cpu_diff()[i] = i; } - layer.Backward(this->blob_top_vec_, true, &(this->blob_bottom_vec_)); + layer.Backward(this->blob_top_vec_, true, this->blob_bottom_vec_); for (int i = 0; i < this->blob_bottom_->count(); ++i) { cout << "bottom diff " << i << " " << this->blob_bottom_->cpu_diff()[i] << endl; } @@ -452,8 +466,8 @@ TYPED_TEST(PoolingLayerTest, TestGradientMax) { pooling_param->set_pool(PoolingParameter_PoolMethod_MAX); PoolingLayer layer(layer_param); GradientChecker checker(1e-4, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } } @@ -481,12 +495,12 @@ TYPED_TEST(PoolingLayerTest, TestForwardMaxPadded) { this->blob_bottom_->mutable_cpu_data()[7] = 2; this->blob_bottom_->mutable_cpu_data()[8] = 1; PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 1); EXPECT_EQ(this->blob_top_->channels(), 1); EXPECT_EQ(this->blob_top_->height(), 3); EXPECT_EQ(this->blob_top_->width(), 3); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); Dtype epsilon = 1e-8; // Output: // [ 1 4 4 ] @@ -516,8 +530,8 @@ TYPED_TEST(PoolingLayerTest, TestGradientMaxTopMask) { this->blob_top_vec_.push_back(this->blob_top_mask_); PoolingLayer layer(layer_param); GradientChecker checker(1e-4, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); this->blob_top_vec_.pop_back(); } } @@ -537,12 +551,12 @@ TYPED_TEST(PoolingLayerTest, TestForwardAve) { ConstantFiller filler(filler_param); filler.Fill(this->blob_bottom_); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 1); EXPECT_EQ(this->blob_top_->channels(), 1); EXPECT_EQ(this->blob_top_->height(), 3); EXPECT_EQ(this->blob_top_->width(), 3); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); Dtype epsilon = 1e-5; EXPECT_NEAR(this->blob_top_->cpu_data()[0], 8.0 / 9, epsilon); EXPECT_NEAR(this->blob_top_->cpu_data()[1], 4.0 / 3, epsilon); @@ -567,8 +581,8 @@ TYPED_TEST(PoolingLayerTest, TestGradientAve) { pooling_param->set_pool(PoolingParameter_PoolMethod_AVE); PoolingLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } } @@ -586,8 +600,8 @@ TYPED_TEST(PoolingLayerTest, TestGradientAvePadded) { pooling_param->set_pool(PoolingParameter_PoolMethod_AVE); PoolingLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } } @@ -651,7 +665,7 @@ class CuDNNPoolingLayerTest : public ::testing::Test { blob_bottom_->mutable_cpu_data()[i + 14] = 3; } CuDNNPoolingLayer layer(layer_param); - layer.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); EXPECT_EQ(blob_top_->num(), num); EXPECT_EQ(blob_top_->channels(), channels); EXPECT_EQ(blob_top_->height(), 2); @@ -662,7 +676,7 @@ class CuDNNPoolingLayerTest : public ::testing::Test { EXPECT_EQ(blob_top_mask_->height(), 2); EXPECT_EQ(blob_top_mask_->width(), 4); } - layer.Forward(blob_bottom_vec_, &blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); // Expected output: 2x 2 channels of: // [9 5 5 8] // [9 5 5 8] @@ -749,7 +763,7 @@ class CuDNNPoolingLayerTest : public ::testing::Test { blob_bottom_->mutable_cpu_data()[i + 35] = 11; } CuDNNPoolingLayer layer(layer_param); - layer.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); EXPECT_EQ(blob_top_->num(), num); EXPECT_EQ(blob_top_->channels(), channels); EXPECT_EQ(blob_top_->height(), 4); @@ -760,7 +774,7 @@ class CuDNNPoolingLayerTest : public ::testing::Test { EXPECT_EQ(blob_top_mask_->height(), 4); EXPECT_EQ(blob_top_mask_->width(), 5); } - layer.Forward(blob_bottom_vec_, &blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); // Expected output: 2x 2 channels of: // [35 32 26 27 27] // [32 33 33 27 27] @@ -874,7 +888,7 @@ class CuDNNPoolingLayerTest : public ::testing::Test { blob_bottom_->mutable_cpu_data()[i + 35] = 11; } CuDNNPoolingLayer layer(layer_param); - layer.SetUp(blob_bottom_vec_, &blob_top_vec_); + layer.SetUp(blob_bottom_vec_, blob_top_vec_); EXPECT_EQ(blob_top_->num(), num); EXPECT_EQ(blob_top_->channels(), channels); EXPECT_EQ(blob_top_->height(), 5); @@ -885,7 +899,7 @@ class CuDNNPoolingLayerTest : public ::testing::Test { EXPECT_EQ(blob_top_mask_->height(), 5); EXPECT_EQ(blob_top_mask_->width(), 4); } - layer.Forward(blob_bottom_vec_, &blob_top_vec_); + layer.Forward(blob_bottom_vec_, blob_top_vec_); // Expected output: 2x 2 channels of: // [35 32 26 26] // [32 32 27 27] @@ -955,13 +969,16 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestSetupCuDNN) { pooling_param->set_kernel_size(3); pooling_param->set_stride(2); CuDNNPoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_->channels()); EXPECT_EQ(this->blob_top_->height(), 3); EXPECT_EQ(this->blob_top_->width(), 2); } +// This test and all following cuDNN pooling tests with padding are commented +// for now, since cuDNN pooling does not currently support padding. +/* TYPED_TEST(CuDNNPoolingLayerTest, TestSetupPaddedCuDNN) { Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; @@ -971,12 +988,13 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestSetupPaddedCuDNN) { pooling_param->set_pad(1); pooling_param->set_pool(PoolingParameter_PoolMethod_AVE); CuDNNPoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_->channels()); EXPECT_EQ(this->blob_top_->height(), 4); EXPECT_EQ(this->blob_top_->width(), 3); } +*/ /* TYPED_TEST(CuDNNPoolingLayerTest, PrintBackwardCuDNN) { @@ -986,8 +1004,8 @@ TYPED_TEST(CuDNNPoolingLayerTest, PrintBackwardCuDNN) { layer_param.set_stride(2); layer_param.set_pool(LayerParameter_PoolMethod_MAX); CuDNNPoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_bottom_->count(); ++i) { cout << "bottom data " << i << " " << this->blob_bottom_->cpu_data()[i] << endl; } @@ -998,7 +1016,7 @@ TYPED_TEST(CuDNNPoolingLayerTest, PrintBackwardCuDNN) { for (int i = 0; i < this->blob_top_->count(); ++i) { this->blob_top_->mutable_cpu_diff()[i] = i; } - layer.Backward(this->blob_top_vec_, true, &(this->blob_bottom_vec_)); + layer.Backward(this->blob_top_vec_, true, this->blob_bottom_vec_); for (int i = 0; i < this->blob_bottom_->count(); ++i) { cout << "bottom diff " << i << " " << this->blob_bottom_->cpu_diff()[i] << endl; } @@ -1012,6 +1030,9 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestForwardMaxCuDNN) { this->TestForwardRectWide(); } +// Currently, cuDNN does not support a top mask, so we comment this and +// the corresponding backward test. +/* TYPED_TEST(CuDNNPoolingLayerTest, TestForwardMaxTopMaskCuDNN) { Caffe::set_mode(Caffe::GPU); this->blob_top_vec_.push_back(this->blob_top_mask_); @@ -1019,6 +1040,7 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestForwardMaxTopMaskCuDNN) { this->TestForwardRectHigh(); this->TestForwardRectWide(); } +*/ TYPED_TEST(CuDNNPoolingLayerTest, TestGradientMaxCuDNN) { Caffe::set_mode(Caffe::GPU); @@ -1029,16 +1051,18 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestGradientMaxCuDNN) { pooling_param->set_kernel_h(kernel_h); pooling_param->set_kernel_w(kernel_w); pooling_param->set_stride(2); - pooling_param->set_pad(1); + // currenty, cuDNN pooling does not support padding + pooling_param->set_pad(0); pooling_param->set_pool(PoolingParameter_PoolMethod_MAX); CuDNNPoolingLayer layer(layer_param); GradientChecker checker(1e-4, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } } +/* TYPED_TEST(CuDNNPoolingLayerTest, TestForwardMaxPaddedCuDNN) { Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; @@ -1062,12 +1086,12 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestForwardMaxPaddedCuDNN) { this->blob_bottom_->mutable_cpu_data()[7] = 2; this->blob_bottom_->mutable_cpu_data()[8] = 1; CuDNNPoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 1); EXPECT_EQ(this->blob_top_->channels(), 1); EXPECT_EQ(this->blob_top_->height(), 3); EXPECT_EQ(this->blob_top_->width(), 3); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); TypeParam epsilon = 1e-8; // Output: // [ 1 4 4 ] @@ -1083,7 +1107,9 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestForwardMaxPaddedCuDNN) { EXPECT_NEAR(this->blob_top_->cpu_data()[7], 4, epsilon); EXPECT_NEAR(this->blob_top_->cpu_data()[8], 1, epsilon); } +*/ +/* TYPED_TEST(CuDNNPoolingLayerTest, TestGradientMaxTopMaskCuDNN) { Caffe::set_mode(Caffe::GPU); for (int kernel_h = 3; kernel_h <= 4; kernel_h++) { @@ -1097,12 +1123,13 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestGradientMaxTopMaskCuDNN) { this->blob_top_vec_.push_back(this->blob_top_mask_); CuDNNPoolingLayer layer(layer_param); GradientChecker checker(1e-4, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); this->blob_top_vec_.pop_back(); } } } +*/ TYPED_TEST(CuDNNPoolingLayerTest, TestForwardAveCuDNN) { Caffe::set_mode(Caffe::GPU); @@ -1110,7 +1137,9 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestForwardAveCuDNN) { PoolingParameter* pooling_param = layer_param.mutable_pooling_param(); pooling_param->set_kernel_size(3); pooling_param->set_stride(1); - pooling_param->set_pad(1); + // Currently, cuDNN pooling does not support padding, so we use + // a simplified version of this test. + pooling_param->set_pad(0); pooling_param->set_pool(PoolingParameter_PoolMethod_AVE); this->blob_bottom_->Reshape(1, 1, 3, 3); FillerParameter filler_param; @@ -1118,22 +1147,14 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestForwardAveCuDNN) { ConstantFiller filler(filler_param); filler.Fill(this->blob_bottom_); CuDNNPoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), 1); EXPECT_EQ(this->blob_top_->channels(), 1); - EXPECT_EQ(this->blob_top_->height(), 3); - EXPECT_EQ(this->blob_top_->width(), 3); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + EXPECT_EQ(this->blob_top_->height(), 1); + EXPECT_EQ(this->blob_top_->width(), 1); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); TypeParam epsilon = 1e-5; - EXPECT_NEAR(this->blob_top_->cpu_data()[0], 8.0 / 9, epsilon); - EXPECT_NEAR(this->blob_top_->cpu_data()[1], 4.0 / 3, epsilon); - EXPECT_NEAR(this->blob_top_->cpu_data()[2], 8.0 / 9, epsilon); - EXPECT_NEAR(this->blob_top_->cpu_data()[3], 4.0 / 3, epsilon); - EXPECT_NEAR(this->blob_top_->cpu_data()[4], 2.0 , epsilon); - EXPECT_NEAR(this->blob_top_->cpu_data()[5], 4.0 / 3, epsilon); - EXPECT_NEAR(this->blob_top_->cpu_data()[6], 8.0 / 9, epsilon); - EXPECT_NEAR(this->blob_top_->cpu_data()[7], 4.0 / 3, epsilon); - EXPECT_NEAR(this->blob_top_->cpu_data()[8], 8.0 / 9, epsilon); + EXPECT_NEAR(this->blob_top_->cpu_data()[0], 2.0, epsilon); } TYPED_TEST(CuDNNPoolingLayerTest, TestGradientAveCuDNN) { @@ -1148,12 +1169,13 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestGradientAveCuDNN) { pooling_param->set_pool(PoolingParameter_PoolMethod_AVE); CuDNNPoolingLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } } +/* TYPED_TEST(CuDNNPoolingLayerTest, TestGradientAvePaddedCuDNN) { Caffe::set_mode(Caffe::GPU); for (int kernel_h = 3; kernel_h <= 4; kernel_h++) { @@ -1167,11 +1189,12 @@ TYPED_TEST(CuDNNPoolingLayerTest, TestGradientAvePaddedCuDNN) { pooling_param->set_pool(PoolingParameter_PoolMethod_AVE); CuDNNPoolingLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } } } +*/ #endif diff --git a/src/caffe/test/test_power_layer.cpp b/src/caffe/test/test_power_layer.cpp index 0c104c20b7c..76c9e857f36 100644 --- a/src/caffe/test/test_power_layer.cpp +++ b/src/caffe/test/test_power_layer.cpp @@ -37,8 +37,8 @@ class PowerLayerTest : public MultiDeviceTest { layer_param.mutable_power_param()->set_scale(scale); layer_param.mutable_power_param()->set_shift(shift); PowerLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); @@ -74,9 +74,9 @@ class PowerLayerTest : public MultiDeviceTest { } } } - GradientChecker checker(1e-2, 1e-2, 1701, 0., 0.01); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + GradientChecker checker(1e-3, 1e-2, 1701, 0., 0.01); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } Blob* const blob_bottom_; diff --git a/src/caffe/test/test_protobuf.cpp b/src/caffe/test/test_protobuf.cpp index 0c502d6dd36..01de461afdf 100644 --- a/src/caffe/test/test_protobuf.cpp +++ b/src/caffe/test/test_protobuf.cpp @@ -16,7 +16,7 @@ class ProtoTest : public ::testing::Test {}; TEST_F(ProtoTest, TestSerialization) { LayerParameter param; param.set_name("test"); - param.set_type(LayerParameter_LayerType_NONE); + param.set_type("Test"); std::cout << "Printing in binary format." << std::endl; std::cout << param.SerializeAsString() << std::endl; std::cout << "Printing in text format." << std::endl; diff --git a/src/caffe/test/test_sigmoid_cross_entropy_loss_layer.cpp b/src/caffe/test/test_sigmoid_cross_entropy_loss_layer.cpp index 47ccdea1538..e5737e43f6e 100644 --- a/src/caffe/test/test_sigmoid_cross_entropy_loss_layer.cpp +++ b/src/caffe/test/test_sigmoid_cross_entropy_loss_layer.cpp @@ -79,9 +79,9 @@ class SigmoidCrossEntropyLossLayerTest : public MultiDeviceTest { // Fill the targets vector targets_filler.Fill(this->blob_bottom_targets_); SigmoidCrossEntropyLossLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); Dtype layer_loss = - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); const int count = this->blob_bottom_data_->count(); const int num = this->blob_bottom_data_->num(); const Dtype* blob_bottom_data = this->blob_bottom_data_->cpu_data(); @@ -112,10 +112,10 @@ TYPED_TEST(SigmoidCrossEntropyLossLayerTest, TestGradient) { const Dtype kLossWeight = 3.7; layer_param.add_loss_weight(kLossWeight); SigmoidCrossEntropyLossLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &this->blob_top_vec_); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); GradientChecker checker(1e-2, 1e-2, 1701); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_), 0); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); } diff --git a/src/caffe/test/test_slice_layer.cpp b/src/caffe/test/test_slice_layer.cpp index ee8818781f5..395be280089 100644 --- a/src/caffe/test/test_slice_layer.cpp +++ b/src/caffe/test/test_slice_layer.cpp @@ -64,7 +64,7 @@ TYPED_TEST(SliceLayerTest, TestSetupNum) { LayerParameter layer_param; layer_param.mutable_slice_param()->set_slice_dim(0); SliceLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_1_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_1_); EXPECT_EQ(this->blob_bottom_->num(), 3 * this->blob_top_0_->num()); EXPECT_EQ(this->blob_top_0_->num(), this->blob_top_1_->num()); EXPECT_EQ(this->blob_top_0_->num(), this->blob_top_2_->num()); @@ -78,7 +78,7 @@ TYPED_TEST(SliceLayerTest, TestSetupChannels) { LayerParameter layer_param; layer_param.mutable_slice_param()->add_slice_point(3); SliceLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_0_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_0_); EXPECT_EQ(this->blob_top_0_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_0_->channels(), 3); EXPECT_EQ(this->blob_top_1_->channels(), 9); @@ -93,11 +93,11 @@ TYPED_TEST(SliceLayerTest, TestSliceAcrossNum) { LayerParameter layer_param; layer_param.mutable_slice_param()->set_slice_dim(0); SliceLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_0_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_0_); const int top_num = this->blob_bottom_->num() / 2; ASSERT_EQ(top_num, this->blob_top_0_->num()); ASSERT_EQ(top_num, this->blob_top_1_->num()); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_0_)); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_0_); for (int n = 0; n < top_num; ++n) { for (int c = 0; c < this->blob_top_0_->channels(); ++c) { for (int h = 0; h < this->blob_bottom_->height(); ++h) { @@ -127,12 +127,12 @@ TYPED_TEST(SliceLayerTest, TestSliceAcrossChannels) { layer_param.mutable_slice_param()->add_slice_point(kSlicePoint0); layer_param.mutable_slice_param()->add_slice_point(kSlicePoint1); SliceLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_1_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_1_); ASSERT_EQ(kSlicePoint0, this->blob_top_0_->channels()); ASSERT_EQ(kSlicePoint1 - kSlicePoint0, this->blob_top_1_->channels()); ASSERT_EQ(this->blob_bottom_->channels() - kSlicePoint1, this->blob_top_2_->channels()); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_1_)); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_1_); for (int n = 0; n < this->blob_bottom_->num(); ++n) { for (int c = 0; c < this->blob_top_0_->channels(); ++c) { for (int h = 0; h < this->blob_bottom_->height(); ++h) { @@ -169,8 +169,8 @@ TYPED_TEST(SliceLayerTest, TestGradientAcrossNum) { layer_param.mutable_slice_param()->set_slice_dim(0); SliceLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_0_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_0_); } TYPED_TEST(SliceLayerTest, TestGradientAcrossChannels) { @@ -182,8 +182,8 @@ TYPED_TEST(SliceLayerTest, TestGradientAcrossChannels) { layer_param.mutable_slice_param()->add_slice_point(kSlicePoint); SliceLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_0_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_0_); } } // namespace caffe diff --git a/src/caffe/test/test_softmax_layer.cpp b/src/caffe/test/test_softmax_layer.cpp index 41f643f4e2a..f6674422e56 100644 --- a/src/caffe/test/test_softmax_layer.cpp +++ b/src/caffe/test/test_softmax_layer.cpp @@ -41,8 +41,8 @@ TYPED_TEST(SoftmaxLayerTest, TestForward) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; SoftmaxLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Test sum for (int i = 0; i < this->blob_bottom_->num(); ++i) { for (int k = 0; k < this->blob_bottom_->height(); ++k) { @@ -76,8 +76,8 @@ TYPED_TEST(SoftmaxLayerTest, TestGradient) { LayerParameter layer_param; SoftmaxLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } #ifdef USE_CUDNN @@ -107,8 +107,8 @@ TYPED_TEST(CuDNNSoftmaxLayerTest, TestForwardCuDNN) { Caffe::set_mode(Caffe::GPU); LayerParameter layer_param; CuDNNSoftmaxLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Test sum for (int i = 0; i < this->blob_bottom_->num(); ++i) { for (int k = 0; k < this->blob_bottom_->height(); ++k) { @@ -142,8 +142,8 @@ TYPED_TEST(CuDNNSoftmaxLayerTest, TestGradientCuDNN) { LayerParameter layer_param; CuDNNSoftmaxLayer layer(layer_param); GradientChecker checker(1e-2, 1e-3); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } #endif diff --git a/src/caffe/test/test_softmax_with_loss_layer.cpp b/src/caffe/test/test_softmax_with_loss_layer.cpp index 246d64e116a..1498d5c5ce1 100644 --- a/src/caffe/test/test_softmax_with_loss_layer.cpp +++ b/src/caffe/test/test_softmax_with_loss_layer.cpp @@ -3,6 +3,7 @@ #include #include +#include "boost/scoped_ptr.hpp" #include "gtest/gtest.h" #include "caffe/blob.hpp" @@ -13,6 +14,8 @@ #include "caffe/test/test_caffe_main.hpp" #include "caffe/test/test_gradient_check_util.hpp" +using boost::scoped_ptr; + namespace caffe { template @@ -50,15 +53,58 @@ class SoftmaxWithLossLayerTest : public MultiDeviceTest { TYPED_TEST_CASE(SoftmaxWithLossLayerTest, TestDtypesAndDevices); - TYPED_TEST(SoftmaxWithLossLayerTest, TestGradient) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; layer_param.add_loss_weight(3); SoftmaxWithLossLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2, 1701); - checker.CheckGradientExhaustive(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_), 0); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); +} + +TYPED_TEST(SoftmaxWithLossLayerTest, TestForwardIgnoreLabel) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + layer_param.mutable_loss_param()->set_normalize(false); + // First, compute the loss with all labels + scoped_ptr > layer( + new SoftmaxWithLossLayer(layer_param)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); + Dtype full_loss = this->blob_top_loss_->cpu_data()[0]; + // Now, accumulate the loss, ignoring each label in {0, ..., 4} in turn. + Dtype accum_loss = 0; + for (int label = 0; label < 5; ++label) { + layer_param.mutable_loss_param()->set_ignore_label(label); + layer.reset(new SoftmaxWithLossLayer(layer_param)); + layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_); + accum_loss += this->blob_top_loss_->cpu_data()[0]; + } + // Check that each label was included all but once. + EXPECT_NEAR(4 * full_loss, accum_loss, 1e-4); +} + +TYPED_TEST(SoftmaxWithLossLayerTest, TestGradientIgnoreLabel) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + // labels are in {0, ..., 4}, so we'll ignore about a fifth of them + layer_param.mutable_loss_param()->set_ignore_label(0); + SoftmaxWithLossLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-2, 1701); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); +} + +TYPED_TEST(SoftmaxWithLossLayerTest, TestGradientUnnormalized) { + typedef typename TypeParam::Dtype Dtype; + LayerParameter layer_param; + layer_param.mutable_loss_param()->set_normalize(false); + SoftmaxWithLossLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-2, 1701); + checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_, + this->blob_top_vec_, 0); } } // namespace caffe diff --git a/src/caffe/test/test_solver.cpp b/src/caffe/test/test_solver.cpp index a7dbf77fd95..1c2c9bbb740 100644 --- a/src/caffe/test/test_solver.cpp +++ b/src/caffe/test/test_solver.cpp @@ -51,9 +51,9 @@ TYPED_TEST(SolverTest, TestInitTrainTestNets) { "test_state: {}" "net_param { " " name: 'TestNetwork' " - " layers: { " + " layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 5 " " channels: 3 " @@ -67,26 +67,26 @@ TYPED_TEST(SolverTest, TestInitTrainTestNets) { " top: 'data' " " top: 'label' " " } " - " layers: { " + " layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " } " " bottom: 'data' " " top: 'innerprod' " " } " - " layers: { " + " layer { " " name: 'accuracy' " - " type: ACCURACY " + " type: 'Accuracy' " " bottom: 'innerprod' " " bottom: 'label' " " top: 'accuracy' " " exclude: { phase: TRAIN } " " } " - " layers: { " + " layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " " include: { phase: TRAIN } " diff --git a/src/caffe/test/test_split_layer.cpp b/src/caffe/test/test_split_layer.cpp index e9b942c5c51..be5204bfc3e 100644 --- a/src/caffe/test/test_split_layer.cpp +++ b/src/caffe/test/test_split_layer.cpp @@ -52,7 +52,7 @@ TYPED_TEST(SplitLayerTest, TestSetup) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; SplitLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_a_->num(), 2); EXPECT_EQ(this->blob_top_a_->channels(), 3); EXPECT_EQ(this->blob_top_a_->height(), 6); @@ -67,8 +67,8 @@ TYPED_TEST(SplitLayerTest, Test) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; SplitLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); for (int i = 0; i < this->blob_bottom_->count(); ++i) { Dtype bottom_value = this->blob_bottom_->cpu_data()[i]; EXPECT_EQ(bottom_value, this->blob_top_a_->cpu_data()[i]); @@ -81,8 +81,8 @@ TYPED_TEST(SplitLayerTest, TestGradient) { LayerParameter layer_param; SplitLayer layer(layer_param); GradientChecker checker(1e-2, 1e-2); - checker.CheckGradientEltwise(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } @@ -114,21 +114,21 @@ class SplitLayerInsertionTest : public ::testing::Test { TEST_F(SplitLayerInsertionTest, TestNoInsertion1) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -138,34 +138,34 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertion1) { TEST_F(SplitLayerInsertionTest, TestNoInsertion2) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'data_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'data' " " top: 'data_split_0' " " top: 'data_split_1' " "} " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_split_0' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_split_1' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1' " " bottom: 'innerprod2' " "} "; @@ -175,9 +175,9 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertion2) { TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { const string& input_proto = "name: 'CaffeNet' " - "layers { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " data_param { " " source: '/home/jiayq/Data/ILSVRC12/train-leveldb' " " batch_size: 256 " @@ -190,9 +190,9 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " top: 'data' " " top: 'label' " "} " - "layers { " + "layer { " " name: 'conv1' " - " type: CONVOLUTION " + " type: 'Convolution' " " convolution_param { " " num_output: 96 " " kernel_size: 11 " @@ -206,22 +206,26 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " value: 0. " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'data' " " top: 'conv1' " "} " - "layers { " + "layer { " " name: 'relu1' " - " type: RELU " + " type: 'ReLU' " " bottom: 'conv1' " " top: 'conv1' " "} " - "layers { " + "layer { " " name: 'pool1' " - " type: POOLING " + " type: 'Pooling' " " pooling_param { " " pool: MAX " " kernel_size: 3 " @@ -230,9 +234,9 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " bottom: 'conv1' " " top: 'pool1' " "} " - "layers { " + "layer { " " name: 'norm1' " - " type: LRN " + " type: 'LRN' " " lrn_param { " " local_size: 5 " " alpha: 0.0001 " @@ -241,9 +245,9 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " bottom: 'pool1' " " top: 'norm1' " "} " - "layers { " + "layer { " " name: 'conv2' " - " type: CONVOLUTION " + " type: 'Convolution' " " convolution_param { " " num_output: 256 " " group: 2 " @@ -258,22 +262,26 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " value: 1. " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'norm1' " " top: 'conv2' " "} " - "layers { " + "layer { " " name: 'relu2' " - " type: RELU " + " type: 'ReLU' " " bottom: 'conv2' " " top: 'conv2' " "} " - "layers { " + "layer { " " name: 'pool2' " - " type: POOLING " + " type: 'Pooling' " " pooling_param { " " pool: MAX " " kernel_size: 3 " @@ -282,9 +290,9 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " bottom: 'conv2' " " top: 'pool2' " "} " - "layers { " + "layer { " " name: 'norm2' " - " type: LRN " + " type: 'LRN' " " lrn_param { " " local_size: 5 " " alpha: 0.0001 " @@ -293,9 +301,9 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " bottom: 'pool2' " " top: 'norm2' " "} " - "layers { " + "layer { " " name: 'conv3' " - " type: CONVOLUTION " + " type: 'Convolution' " " convolution_param { " " num_output: 384 " " kernel_size: 3 " @@ -309,22 +317,26 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " value: 0. " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'norm2' " " top: 'conv3' " "} " - "layers { " + "layer { " " name: 'relu3' " - " type: RELU " + " type: 'ReLU' " " bottom: 'conv3' " " top: 'conv3' " "} " - "layers { " + "layer { " " name: 'conv4' " - " type: CONVOLUTION " + " type: 'Convolution' " " convolution_param { " " num_output: 384 " " group: 2 " @@ -339,22 +351,26 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " value: 1. " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'conv3' " " top: 'conv4' " "} " - "layers { " + "layer { " " name: 'relu4' " - " type: RELU " + " type: 'ReLU' " " bottom: 'conv4' " " top: 'conv4' " "} " - "layers { " + "layer { " " name: 'conv5' " - " type: CONVOLUTION " + " type: 'Convolution' " " convolution_param { " " num_output: 256 " " group: 2 " @@ -369,22 +385,26 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " value: 1. " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'conv4' " " top: 'conv5' " "} " - "layers { " + "layer { " " name: 'relu5' " - " type: RELU " + " type: 'ReLU' " " bottom: 'conv5' " " top: 'conv5' " "} " - "layers { " + "layer { " " name: 'pool5' " - " type: POOLING " + " type: 'Pooling' " " pooling_param { " " kernel_size: 3 " " pool: MAX " @@ -393,9 +413,9 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " bottom: 'conv5' " " top: 'pool5' " "} " - "layers { " + "layer { " " name: 'fc6' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 4096 " " weight_filler { " @@ -407,31 +427,35 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " value: 1. " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'pool5' " " top: 'fc6' " "} " - "layers { " + "layer { " " name: 'relu6' " - " type: RELU " + " type: 'ReLU' " " bottom: 'fc6' " " top: 'fc6' " "} " - "layers { " + "layer { " " name: 'drop6' " - " type: DROPOUT " + " type: 'Dropout' " " dropout_param { " " dropout_ratio: 0.5 " " } " " bottom: 'fc6' " " top: 'fc6' " "} " - "layers { " + "layer { " " name: 'fc7' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 4096 " " weight_filler { " @@ -443,31 +467,35 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " value: 1. " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'fc6' " " top: 'fc7' " "} " - "layers { " + "layer { " " name: 'relu7' " - " type: RELU " + " type: 'ReLU' " " bottom: 'fc7' " " top: 'fc7' " "} " - "layers { " + "layer { " " name: 'drop7' " - " type: DROPOUT " + " type: 'Dropout' " " dropout_param { " " dropout_ratio: 0.5 " " } " " bottom: 'fc7' " " top: 'fc7' " "} " - "layers { " + "layer { " " name: 'fc8' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 1000 " " weight_filler { " @@ -479,16 +507,20 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { " value: 0 " " } " " } " - " blobs_lr: 1. " - " blobs_lr: 2. " - " weight_decay: 1. " - " weight_decay: 0. " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " " bottom: 'fc7' " " top: 'fc8' " "} " - "layers { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'fc8' " " bottom: 'label' " "} "; @@ -498,27 +530,27 @@ TEST_F(SplitLayerInsertionTest, TestNoInsertionImageNet) { TEST_F(SplitLayerInsertionTest, TestNoInsertionWithInPlace) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod' " "} " - "layers: { " + "layer { " " name: 'relu' " - " type: RELU " + " type: 'ReLU' " " bottom: 'innerprod' " " top: 'innerprod' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: SOFTMAX_LOSS " + " type: 'SoftmaxWithLoss' " " bottom: 'innerprod' " " bottom: 'label' " "} "; @@ -529,9 +561,9 @@ TEST_F(SplitLayerInsertionTest, TestLossInsertion) { const string& input_proto = "name: 'UnsharedWeightsNetwork' " "force_backward: true " - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 5 " " channels: 2 " @@ -544,9 +576,9 @@ TEST_F(SplitLayerInsertionTest, TestLossInsertion) { " } " " top: 'data' " "} " - "layers: { " + "layer { " " name: 'innerproduct1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -555,14 +587,14 @@ TEST_F(SplitLayerInsertionTest, TestLossInsertion) { " std: 10 " " } " " } " - " param: 'unsharedweights1' " + " param { name: 'unsharedweights1' } " " bottom: 'data' " " top: 'innerproduct1' " " loss_weight: 2.5 " "} " - "layers: { " + "layer { " " name: 'innerproduct2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -571,22 +603,22 @@ TEST_F(SplitLayerInsertionTest, TestLossInsertion) { " std: 10 " " } " " } " - " param: 'unsharedweights2' " + " param { name: 'unsharedweights2' } " " bottom: 'data' " " top: 'innerproduct2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerproduct1' " " bottom: 'innerproduct2' " "} "; const string& expected_output_proto = "name: 'UnsharedWeightsNetwork' " "force_backward: true " - "layers: { " + "layer { " " name: 'data' " - " type: DUMMY_DATA " + " type: 'DummyData' " " dummy_data_param { " " num: 5 " " channels: 2 " @@ -599,16 +631,16 @@ TEST_F(SplitLayerInsertionTest, TestLossInsertion) { " } " " top: 'data' " "} " - "layers: { " + "layer { " " name: 'data_data_0_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'data' " " top: 'data_data_0_split_0' " " top: 'data_data_0_split_1' " "} " - "layers: { " + "layer { " " name: 'innerproduct1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -617,22 +649,22 @@ TEST_F(SplitLayerInsertionTest, TestLossInsertion) { " std: 10 " " } " " } " - " param: 'unsharedweights1' " + " param { name: 'unsharedweights1' } " " bottom: 'data_data_0_split_0' " " top: 'innerproduct1' " "} " - "layers: { " + "layer { " " name: 'innerproduct1_innerproduct1_0_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'innerproduct1' " " top: 'innerproduct1_innerproduct1_0_split_0' " " top: 'innerproduct1_innerproduct1_0_split_1' " " loss_weight: 2.5 " " loss_weight: 0 " "} " - "layers: { " + "layer { " " name: 'innerproduct2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " inner_product_param { " " num_output: 10 " " bias_term: false " @@ -641,13 +673,13 @@ TEST_F(SplitLayerInsertionTest, TestLossInsertion) { " std: 10 " " } " " } " - " param: 'unsharedweights2' " + " param { name: 'unsharedweights2' } " " bottom: 'data_data_0_split_1' " " top: 'innerproduct2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerproduct1_innerproduct1_0_split_1' " " bottom: 'innerproduct2' " "} "; @@ -657,92 +689,92 @@ TEST_F(SplitLayerInsertionTest, TestLossInsertion) { TEST_F(SplitLayerInsertionTest, TestInsertion) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'innerprod3' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod3' " "} " - "layers: { " + "layer { " " name: 'loss1' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1' " " bottom: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'loss2' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod2' " " bottom: 'innerprod3' " "} "; const string& expected_output_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'data_data_0_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'data' " " top: 'data_data_0_split_0' " " top: 'data_data_0_split_1' " " top: 'data_data_0_split_2' " "} " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_data_0_split_0' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_data_0_split_1' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'innerprod2_innerprod2_0_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'innerprod2' " " top: 'innerprod2_innerprod2_0_split_0' " " top: 'innerprod2_innerprod2_0_split_1' " "} " - "layers: { " + "layer { " " name: 'innerprod3' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_data_0_split_2' " " top: 'innerprod3' " "} " - "layers: { " + "layer { " " name: 'loss1' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1' " " bottom: 'innerprod2_innerprod2_0_split_0' " "} " - "layers: { " + "layer { " " name: 'loss2' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod2_innerprod2_0_split_1' " " bottom: 'innerprod3' " "} "; @@ -752,103 +784,103 @@ TEST_F(SplitLayerInsertionTest, TestInsertion) { TEST_F(SplitLayerInsertionTest, TestInsertionTwoTop) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'label' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'innerprod3' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod3' " "} " - "layers: { " + "layer { " " name: 'innerprod4' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'label' " " top: 'innerprod4' " "} " - "layers: { " + "layer { " " name: 'loss1' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1' " " bottom: 'innerprod3' " "} " - "layers: { " + "layer { " " name: 'loss2' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod2' " " bottom: 'innerprod4' " "} "; const string& expected_output_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'data_data_0_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'data' " " top: 'data_data_0_split_0' " " top: 'data_data_0_split_1' " "} " - "layers: { " + "layer { " " name: 'label_data_1_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'label' " " top: 'label_data_1_split_0' " " top: 'label_data_1_split_1' " "} " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_data_0_split_0' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'label_data_1_split_0' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'innerprod3' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_data_0_split_1' " " top: 'innerprod3' " "} " - "layers: { " + "layer { " " name: 'innerprod4' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'label_data_1_split_1' " " top: 'innerprod4' " "} " - "layers: { " + "layer { " " name: 'loss1' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1' " " bottom: 'innerprod3' " "} " - "layers: { " + "layer { " " name: 'loss2' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod2' " " bottom: 'innerprod4' " "} "; @@ -863,21 +895,21 @@ TEST_F(SplitLayerInsertionTest, TestInputInsertion) { "input_dim: 3 " "input_dim: 227 " "input_dim: 227 " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1' " " bottom: 'innerprod2' " "} "; @@ -888,28 +920,28 @@ TEST_F(SplitLayerInsertionTest, TestInputInsertion) { "input_dim: 3 " "input_dim: 227 " "input_dim: 227 " - "layers: { " + "layer { " " name: 'data_input_0_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'data' " " top: 'data_input_0_split_0' " " top: 'data_input_0_split_1' " "} " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_input_0_split_0' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_input_0_split_1' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'loss' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1' " " bottom: 'innerprod2' " "} "; @@ -919,91 +951,91 @@ TEST_F(SplitLayerInsertionTest, TestInputInsertion) { TEST_F(SplitLayerInsertionTest, TestWithInPlace) { const string& input_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'relu1' " - " type: RELU " + " type: 'ReLU' " " bottom: 'innerprod1' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'innerprod1' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'loss1' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1' " " bottom: 'label' " "} " - "layers: { " + "layer { " " name: 'loss2' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod2' " " bottom: 'data' " "} "; const string& expected_output_proto = "name: 'TestNetwork' " - "layers: { " + "layer { " " name: 'data' " - " type: DATA " + " type: 'Data' " " top: 'data' " " top: 'label' " "} " - "layers: { " + "layer { " " name: 'data_data_0_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'data' " " top: 'data_data_0_split_0' " " top: 'data_data_0_split_1' " "} " - "layers: { " + "layer { " " name: 'innerprod1' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'data_data_0_split_0' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'relu1' " - " type: RELU " + " type: 'ReLU' " " bottom: 'innerprod1' " " top: 'innerprod1' " "} " - "layers: { " + "layer { " " name: 'innerprod1_relu1_0_split' " - " type: SPLIT " + " type: 'Split' " " bottom: 'innerprod1' " " top: 'innerprod1_relu1_0_split_0' " " top: 'innerprod1_relu1_0_split_1' " "} " - "layers: { " + "layer { " " name: 'innerprod2' " - " type: INNER_PRODUCT " + " type: 'InnerProduct' " " bottom: 'innerprod1_relu1_0_split_0' " " top: 'innerprod2' " "} " - "layers: { " + "layer { " " name: 'loss1' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod1_relu1_0_split_1' " " bottom: 'label' " "} " - "layers: { " + "layer { " " name: 'loss2' " - " type: EUCLIDEAN_LOSS " + " type: 'EuclideanLoss' " " bottom: 'innerprod2' " " bottom: 'data_data_0_split_1' " "} "; diff --git a/src/caffe/test/test_stochastic_pooling.cpp b/src/caffe/test/test_stochastic_pooling.cpp index 4f13981bd82..12962c65d85 100644 --- a/src/caffe/test/test_stochastic_pooling.cpp +++ b/src/caffe/test/test_stochastic_pooling.cpp @@ -53,7 +53,7 @@ TYPED_TEST(StochasticPoolingLayerTest, TestSetup) { pooling_param->set_kernel_size(3); pooling_param->set_stride(2); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_->channels()); EXPECT_EQ(this->blob_top_->height(), 3); @@ -62,15 +62,15 @@ TYPED_TEST(StochasticPoolingLayerTest, TestSetup) { TYPED_TEST(StochasticPoolingLayerTest, TestStochasticGPU) { Caffe::set_mode(Caffe::GPU); - Caffe::set_phase(Caffe::TRAIN); LayerParameter layer_param; + layer_param.set_phase(TRAIN); PoolingParameter* pooling_param = layer_param.mutable_pooling_param(); pooling_param->set_kernel_size(3); pooling_param->set_stride(2); pooling_param->set_pool(PoolingParameter_PoolMethod_STOCHASTIC); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Check if the output is correct - it should do random sampling const TypeParam* bottom_data = this->blob_bottom_->cpu_data(); @@ -106,15 +106,15 @@ TYPED_TEST(StochasticPoolingLayerTest, TestStochasticGPU) { TYPED_TEST(StochasticPoolingLayerTest, TestStochasticGPUTestPhase) { Caffe::set_mode(Caffe::GPU); - Caffe::set_phase(Caffe::TEST); LayerParameter layer_param; + layer_param.set_phase(TEST); PoolingParameter* pooling_param = layer_param.mutable_pooling_param(); pooling_param->set_kernel_size(3); pooling_param->set_stride(2); pooling_param->set_pool(PoolingParameter_PoolMethod_STOCHASTIC); PoolingLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Check if the output is correct - it should do random sampling const TypeParam* bottom_data = this->blob_bottom_->cpu_data(); @@ -144,8 +144,8 @@ TYPED_TEST(StochasticPoolingLayerTest, TestStochasticGPUTestPhase) { TYPED_TEST(StochasticPoolingLayerTest, TestGradientGPU) { Caffe::set_mode(Caffe::GPU); - Caffe::set_phase(Caffe::TRAIN); LayerParameter layer_param; + layer_param.set_phase(TRAIN); PoolingParameter* pooling_param = layer_param.mutable_pooling_param(); pooling_param->set_kernel_size(3); pooling_param->set_stride(2); @@ -154,8 +154,8 @@ TYPED_TEST(StochasticPoolingLayerTest, TestGradientGPU) { GradientChecker checker(1e-4, 1e-2); // it is too expensive to call curand multiple times, so we don't do an // exhaustive gradient check. - checker.CheckGradient(&layer, &(this->blob_bottom_vec_), - &(this->blob_top_vec_)); + checker.CheckGradient(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); } diff --git a/src/caffe/test/test_tanh_layer.cpp b/src/caffe/test/test_tanh_layer.cpp new file mode 100644 index 00000000000..5dc92832fc8 --- /dev/null +++ b/src/caffe/test/test_tanh_layer.cpp @@ -0,0 +1,101 @@ +#include +#include + +#include "gtest/gtest.h" + +#include "caffe/blob.hpp" +#include "caffe/common.hpp" +#include "caffe/common_layers.hpp" +#include "caffe/filler.hpp" + +#include "caffe/test/test_caffe_main.hpp" +#include "caffe/test/test_gradient_check_util.hpp" + +namespace caffe { + +double tanh_naive(double x) { + if (x < -40) { + // avoid negative overflow + return -1; + } else if (x > 40) { + // avoid positive overflow + return 1; + } else { + // exact expression for tanh, which is unstable for large x + double exp2x = exp(2 * x); + return (exp2x - 1.0) / (exp2x + 1.0); + } +} + +template +class TanHLayerTest : public MultiDeviceTest { + typedef typename TypeParam::Dtype Dtype; + + protected: + TanHLayerTest() + : blob_bottom_(new Blob(2, 3, 4, 5)), + blob_top_(new Blob()) { + Caffe::set_random_seed(1701); + FillerParameter filler_param; + blob_bottom_vec_.push_back(blob_bottom_); + blob_top_vec_.push_back(blob_top_); + } + virtual ~TanHLayerTest() { delete blob_bottom_; delete blob_top_; } + + void TestForward(Dtype filler_std) { + FillerParameter filler_param; + filler_param.set_std(filler_std); + GaussianFiller filler(filler_param); + filler.Fill(this->blob_bottom_); + + LayerParameter layer_param; + TanHLayer layer(layer_param); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); + // Now, check values + const Dtype* bottom_data = this->blob_bottom_->cpu_data(); + const Dtype* top_data = this->blob_top_->cpu_data(); + const Dtype min_precision = 1e-5; + for (int i = 0; i < this->blob_bottom_->count(); ++i) { + Dtype expected_value = tanh_naive(bottom_data[i]); + Dtype precision = std::max( + Dtype(std::abs(expected_value * Dtype(1e-4))), min_precision); + EXPECT_NEAR(expected_value, top_data[i], precision); + } + } + + void TestBackward(Dtype filler_std) { + FillerParameter filler_param; + filler_param.set_std(filler_std); + GaussianFiller filler(filler_param); + filler.Fill(this->blob_bottom_); + + LayerParameter layer_param; + TanHLayer layer(layer_param); + GradientChecker checker(1e-2, 1e-2, 1701); + checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_, + this->blob_top_vec_); + } + + Blob* const blob_bottom_; + Blob* const blob_top_; + vector*> blob_bottom_vec_; + vector*> blob_top_vec_; +}; + +TYPED_TEST_CASE(TanHLayerTest, TestDtypesAndDevices); + +TYPED_TEST(TanHLayerTest, TestTanH) { + this->TestForward(1.0); +} + +TYPED_TEST(TanHLayerTest, TestTanHOverflow) { + // this will fail if tanh overflow is not properly handled + this->TestForward(10000.0); +} + +TYPED_TEST(TanHLayerTest, TestTanHGradient) { + this->TestBackward(1.0); +} + +} // namespace caffe diff --git a/src/caffe/test/test_threshold_layer.cpp b/src/caffe/test/test_threshold_layer.cpp index 32dfbeeac92..05ce82120e6 100644 --- a/src/caffe/test/test_threshold_layer.cpp +++ b/src/caffe/test/test_threshold_layer.cpp @@ -40,7 +40,7 @@ TYPED_TEST(ThresholdLayerTest, TestSetup) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ThresholdLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); EXPECT_EQ(this->blob_top_->num(), this->blob_bottom_->num()); EXPECT_EQ(this->blob_top_->channels(), this->blob_bottom_->channels()); EXPECT_EQ(this->blob_top_->height(), this->blob_bottom_->height()); @@ -51,8 +51,8 @@ TYPED_TEST(ThresholdLayerTest, Test) { typedef typename TypeParam::Dtype Dtype; LayerParameter layer_param; ThresholdLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); @@ -76,8 +76,8 @@ TYPED_TEST(ThresholdLayerTest, Test2) { layer_param.mutable_threshold_param(); threshold_param->set_threshold(0.5); ThresholdLayer layer(layer_param); - layer.SetUp(this->blob_bottom_vec_, &(this->blob_top_vec_)); - layer.Forward(this->blob_bottom_vec_, &(this->blob_top_vec_)); + layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_); + layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_); // Now, check values const Dtype* bottom_data = this->blob_bottom_->cpu_data(); const Dtype* top_data = this->blob_top_->cpu_data(); diff --git a/src/caffe/test/test_upgrade_proto.cpp b/src/caffe/test/test_upgrade_proto.cpp index f46a0e823c1..eec627656ef 100644 --- a/src/caffe/test/test_upgrade_proto.cpp +++ b/src/caffe/test/test_upgrade_proto.cpp @@ -7,12 +7,11 @@ #include "caffe/blob.hpp" #include "caffe/common.hpp" +#include "caffe/layer.hpp" #include "caffe/util/upgrade_proto.hpp" #include "caffe/test/test_caffe_main.hpp" -using std::string; - namespace caffe { class PaddingLayerUpgradeTest : public ::testing::Test { @@ -1085,7 +1084,7 @@ TEST_F(PaddingLayerUpgradeTest, TestImageNet) { this->RunPaddingUpgradeTest(input_proto, expected_output_proto); } -class V0UpgradeTest : public ::testing::Test { +class NetUpgradeTest : public ::testing::Test { protected: void RunV0UpgradeTest( const string& input_param_string, const string& output_param_string) { @@ -1103,10 +1102,27 @@ class V0UpgradeTest : public ::testing::Test { EXPECT_EQ(expected_output_param.DebugString(), actual_output_param.DebugString()); } + + void RunV1UpgradeTest( + const string& input_param_string, const string& output_param_string) { + // Test that UpgradeV0Net called on the NetParameter proto specified by + // input_param_string results in the NetParameter proto specified by + // output_param_string. + NetParameter input_param; + CHECK(google::protobuf::TextFormat::ParseFromString( + input_param_string, &input_param)); + NetParameter expected_output_param; + CHECK(google::protobuf::TextFormat::ParseFromString( + output_param_string, &expected_output_param)); + NetParameter actual_output_param; + UpgradeV1Net(input_param, &actual_output_param); + EXPECT_EQ(expected_output_param.DebugString(), + actual_output_param.DebugString()); + } }; -TEST_F(V0UpgradeTest, TestSimple) { - const string& input_proto = +TEST_F(NetUpgradeTest, TestSimple) { + const string& v0_proto = "name: 'CaffeNet' " "layers { " " layer { " @@ -1182,7 +1198,7 @@ TEST_F(V0UpgradeTest, TestSimple) { " bottom: 'fc8' " " bottom: 'label' " "} "; - const string& expected_output_proto = + const string& expected_v1_proto = "name: 'CaffeNet' " "layers { " " name: 'data' " @@ -1250,11 +1266,89 @@ TEST_F(V0UpgradeTest, TestSimple) { " bottom: 'fc8' " " bottom: 'label' " "} "; - this->RunV0UpgradeTest(input_proto, expected_output_proto); + this->RunV0UpgradeTest(v0_proto, expected_v1_proto); + + const string& expected_v2_proto = + "name: 'CaffeNet' " + "layer { " + " name: 'data' " + " type: 'Data' " + " data_param { " + " source: '/home/jiayq/Data/ILSVRC12/train-leveldb' " + " batch_size: 256 " + " } " + " transform_param { " + " crop_size: 227 " + " mirror: true " + " mean_file: '/home/jiayq/Data/ILSVRC12/image_mean.binaryproto' " + " } " + " top: 'data' " + " top: 'label' " + "} " + "layer { " + " name: 'conv1' " + " type: 'Convolution' " + " convolution_param { " + " num_output: 96 " + " kernel_size: 11 " + " stride: 4 " + " pad: 2 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.01 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 0. " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'data' " + " top: 'conv1' " + "} " + "layer { " + " name: 'fc8' " + " type: 'InnerProduct' " + " inner_product_param { " + " num_output: 1000 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.01 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 0 " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'conv1' " + " top: 'fc8' " + "} " + "layer { " + " name: 'loss' " + " type: 'SoftmaxWithLoss' " + " bottom: 'fc8' " + " bottom: 'label' " + "} "; + this->RunV1UpgradeTest(expected_v1_proto, expected_v2_proto); } // Test any layer or parameter upgrades not covered by other tests. -TEST_F(V0UpgradeTest, TestAllParams) { +TEST_F(NetUpgradeTest, TestAllParams) { const string& input_proto = "name: 'CaffeNet' " "input: 'input_data' " @@ -1754,8 +1848,8 @@ TEST_F(V0UpgradeTest, TestAllParams) { this->RunV0UpgradeTest(input_proto, expected_output_proto); } -TEST_F(V0UpgradeTest, TestImageNet) { - const string& input_proto = +TEST_F(NetUpgradeTest, TestImageNet) { + const string& v0_proto = "name: 'CaffeNet' " "layers { " " layer { " @@ -2120,7 +2214,7 @@ TEST_F(V0UpgradeTest, TestImageNet) { " bottom: 'fc8' " " bottom: 'label' " "} "; - const string& expected_output_proto = + const string& expected_v1_proto = "name: 'CaffeNet' " "layers { " " name: 'data' " @@ -2439,7 +2533,377 @@ TEST_F(V0UpgradeTest, TestImageNet) { " bottom: 'fc8' " " bottom: 'label' " "} "; - this->RunV0UpgradeTest(input_proto, expected_output_proto); + this->RunV0UpgradeTest(v0_proto, expected_v1_proto); + + const string& expected_v2_proto = + "name: 'CaffeNet' " + "layer { " + " name: 'data' " + " type: 'Data' " + " data_param { " + " source: '/home/jiayq/Data/ILSVRC12/train-leveldb' " + " batch_size: 256 " + " } " + " transform_param { " + " crop_size: 227 " + " mirror: true " + " mean_file: '/home/jiayq/Data/ILSVRC12/image_mean.binaryproto' " + " } " + " top: 'data' " + " top: 'label' " + "} " + "layer { " + " name: 'conv1' " + " type: 'Convolution' " + " convolution_param { " + " num_output: 96 " + " kernel_size: 11 " + " stride: 4 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.01 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 0. " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'data' " + " top: 'conv1' " + "} " + "layer { " + " name: 'relu1' " + " type: 'ReLU' " + " bottom: 'conv1' " + " top: 'conv1' " + "} " + "layer { " + " name: 'pool1' " + " type: 'Pooling' " + " pooling_param { " + " pool: MAX " + " kernel_size: 3 " + " stride: 2 " + " } " + " bottom: 'conv1' " + " top: 'pool1' " + "} " + "layer { " + " name: 'norm1' " + " type: 'LRN' " + " lrn_param { " + " local_size: 5 " + " alpha: 0.0001 " + " beta: 0.75 " + " } " + " bottom: 'pool1' " + " top: 'norm1' " + "} " + "layer { " + " name: 'conv2' " + " type: 'Convolution' " + " convolution_param { " + " num_output: 256 " + " group: 2 " + " kernel_size: 5 " + " pad: 2 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.01 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 1. " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'norm1' " + " top: 'conv2' " + "} " + "layer { " + " name: 'relu2' " + " type: 'ReLU' " + " bottom: 'conv2' " + " top: 'conv2' " + "} " + "layer { " + " name: 'pool2' " + " type: 'Pooling' " + " pooling_param { " + " pool: MAX " + " kernel_size: 3 " + " stride: 2 " + " } " + " bottom: 'conv2' " + " top: 'pool2' " + "} " + "layer { " + " name: 'norm2' " + " type: 'LRN' " + " lrn_param { " + " local_size: 5 " + " alpha: 0.0001 " + " beta: 0.75 " + " } " + " bottom: 'pool2' " + " top: 'norm2' " + "} " + "layer { " + " name: 'conv3' " + " type: 'Convolution' " + " convolution_param { " + " num_output: 384 " + " kernel_size: 3 " + " pad: 1 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.01 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 0. " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'norm2' " + " top: 'conv3' " + "} " + "layer { " + " name: 'relu3' " + " type: 'ReLU' " + " bottom: 'conv3' " + " top: 'conv3' " + "} " + "layer { " + " name: 'conv4' " + " type: 'Convolution' " + " convolution_param { " + " num_output: 384 " + " group: 2 " + " kernel_size: 3 " + " pad: 1 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.01 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 1. " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'conv3' " + " top: 'conv4' " + "} " + "layer { " + " name: 'relu4' " + " type: 'ReLU' " + " bottom: 'conv4' " + " top: 'conv4' " + "} " + "layer { " + " name: 'conv5' " + " type: 'Convolution' " + " convolution_param { " + " num_output: 256 " + " group: 2 " + " kernel_size: 3 " + " pad: 1 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.01 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 1. " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'conv4' " + " top: 'conv5' " + "} " + "layer { " + " name: 'relu5' " + " type: 'ReLU' " + " bottom: 'conv5' " + " top: 'conv5' " + "} " + "layer { " + " name: 'pool5' " + " type: 'Pooling' " + " pooling_param { " + " kernel_size: 3 " + " pool: MAX " + " stride: 2 " + " } " + " bottom: 'conv5' " + " top: 'pool5' " + "} " + "layer { " + " name: 'fc6' " + " type: 'InnerProduct' " + " inner_product_param { " + " num_output: 4096 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.005 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 1. " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'pool5' " + " top: 'fc6' " + "} " + "layer { " + " name: 'relu6' " + " type: 'ReLU' " + " bottom: 'fc6' " + " top: 'fc6' " + "} " + "layer { " + " name: 'drop6' " + " type: 'Dropout' " + " dropout_param { " + " dropout_ratio: 0.5 " + " } " + " bottom: 'fc6' " + " top: 'fc6' " + "} " + "layer { " + " name: 'fc7' " + " type: 'InnerProduct' " + " inner_product_param { " + " num_output: 4096 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.005 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 1. " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'fc6' " + " top: 'fc7' " + "} " + "layer { " + " name: 'relu7' " + " type: 'ReLU' " + " bottom: 'fc7' " + " top: 'fc7' " + "} " + "layer { " + " name: 'drop7' " + " type: 'Dropout' " + " dropout_param { " + " dropout_ratio: 0.5 " + " } " + " bottom: 'fc7' " + " top: 'fc7' " + "} " + "layer { " + " name: 'fc8' " + " type: 'InnerProduct' " + " inner_product_param { " + " num_output: 1000 " + " weight_filler { " + " type: 'gaussian' " + " std: 0.01 " + " } " + " bias_filler { " + " type: 'constant' " + " value: 0 " + " } " + " } " + " param { " + " lr_mult: 1 " + " decay_mult: 1 " + " } " + " param { " + " lr_mult: 2 " + " decay_mult: 0 " + " } " + " bottom: 'fc7' " + " top: 'fc8' " + "} " + "layer { " + " name: 'loss' " + " type: 'SoftmaxWithLoss' " + " bottom: 'fc8' " + " bottom: 'label' " + "} "; + this->RunV1UpgradeTest(expected_v1_proto, expected_v2_proto); +} // NOLINT(readability/fn_size) + +TEST_F(NetUpgradeTest, TestUpgradeV1LayerType) { + LayerParameter layer_param; + shared_ptr > layer; + for (int i = 0; i < V1LayerParameter_LayerType_LayerType_ARRAYSIZE; ++i) { + ASSERT_TRUE(V1LayerParameter_LayerType_IsValid(i)); + V1LayerParameter_LayerType v1_type = V1LayerParameter_LayerType(i); + string v2_layer_type(UpgradeV1LayerType(v1_type)); + if (v2_layer_type == "") { + EXPECT_EQ(V1LayerParameter_LayerType_NONE, v1_type); + continue; // Empty string isn't actually a valid layer type. + } + layer_param.set_type(v2_layer_type); + layer = LayerRegistry::CreateLayer(layer_param); + EXPECT_EQ(v2_layer_type, layer->type()); + } } -} // namespace caffe +} // NOLINT(readability/fn_size) // namespace caffe diff --git a/src/caffe/util/benchmark.cpp b/src/caffe/util/benchmark.cpp index 566d06a8f5f..1d269c351c1 100644 --- a/src/caffe/util/benchmark.cpp +++ b/src/caffe/util/benchmark.cpp @@ -55,6 +55,30 @@ void Timer::Stop() { } } + +float Timer::MicroSeconds() { + if (!has_run_at_least_once()) { + LOG(WARNING) << "Timer has never been run before reading time."; + return 0; + } + if (running()) { + Stop(); + } + if (Caffe::mode() == Caffe::GPU) { +#ifndef CPU_ONLY + CUDA_CHECK(cudaEventElapsedTime(&elapsed_milliseconds_, start_gpu_, + stop_gpu_)); + // Cuda only measure milliseconds + elapsed_microseconds_ = elapsed_milliseconds_ * 1000; +#else + NO_GPU; +#endif + } else { + elapsed_microseconds_ = (stop_cpu_ - start_cpu_).total_microseconds(); + } + return elapsed_microseconds_; +} + float Timer::MilliSeconds() { if (!has_run_at_least_once()) { LOG(WARNING) << "Timer has never been run before reading time."; @@ -94,4 +118,51 @@ void Timer::Init() { } } +CPUTimer::CPUTimer() { + this->initted_ = true; + this->running_ = false; + this->has_run_at_least_once_ = false; +} + +void CPUTimer::Start() { + if (!running()) { + this->start_cpu_ = boost::posix_time::microsec_clock::local_time(); + this->running_ = true; + this->has_run_at_least_once_ = true; + } +} + +void CPUTimer::Stop() { + if (running()) { + this->stop_cpu_ = boost::posix_time::microsec_clock::local_time(); + this->running_ = false; + } +} + +float CPUTimer::MilliSeconds() { + if (!has_run_at_least_once()) { + LOG(WARNING) << "Timer has never been run before reading time."; + return 0; + } + if (running()) { + Stop(); + } + this->elapsed_milliseconds_ = (this->stop_cpu_ - + this->start_cpu_).total_milliseconds(); + return this->elapsed_milliseconds_; +} + +float CPUTimer::MicroSeconds() { + if (!has_run_at_least_once()) { + LOG(WARNING) << "Timer has never been run before reading time."; + return 0; + } + if (running()) { + Stop(); + } + this->elapsed_microseconds_ = (this->stop_cpu_ - + this->start_cpu_).total_microseconds(); + return this->elapsed_microseconds_; +} + } // namespace caffe diff --git a/src/caffe/util/db.cpp b/src/caffe/util/db.cpp new file mode 100644 index 00000000000..7f7018107ec --- /dev/null +++ b/src/caffe/util/db.cpp @@ -0,0 +1,84 @@ +#include "caffe/util/db.hpp" + +#include +#include + +namespace caffe { namespace db { + +const size_t LMDB_MAP_SIZE = 1099511627776; // 1 TB + +void LevelDB::Open(const string& source, Mode mode) { + leveldb::Options options; + options.block_size = 65536; + options.write_buffer_size = 268435456; + options.max_open_files = 100; + options.error_if_exists = mode == NEW; + options.create_if_missing = mode != READ; + leveldb::Status status = leveldb::DB::Open(options, source, &db_); + CHECK(status.ok()) << "Failed to open leveldb " << source + << std::endl << status.ToString(); + LOG(INFO) << "Opened leveldb " << source; +} + +void LMDB::Open(const string& source, Mode mode) { + MDB_CHECK(mdb_env_create(&mdb_env_)); + MDB_CHECK(mdb_env_set_mapsize(mdb_env_, LMDB_MAP_SIZE)); + if (mode == NEW) { + CHECK_EQ(mkdir(source.c_str(), 0744), 0) << "mkdir " << source << "failed"; + } + int flags = 0; + if (mode == READ) { + flags = MDB_RDONLY | MDB_NOTLS; + } + MDB_CHECK(mdb_env_open(mdb_env_, source.c_str(), flags, 0664)); + LOG(INFO) << "Opened lmdb " << source; +} + +LMDBCursor* LMDB::NewCursor() { + MDB_txn* mdb_txn; + MDB_cursor* mdb_cursor; + MDB_CHECK(mdb_txn_begin(mdb_env_, NULL, MDB_RDONLY, &mdb_txn)); + MDB_CHECK(mdb_dbi_open(mdb_txn, NULL, 0, &mdb_dbi_)); + MDB_CHECK(mdb_cursor_open(mdb_txn, mdb_dbi_, &mdb_cursor)); + return new LMDBCursor(mdb_txn, mdb_cursor); +} + +LMDBTransaction* LMDB::NewTransaction() { + MDB_txn* mdb_txn; + MDB_CHECK(mdb_txn_begin(mdb_env_, NULL, 0, &mdb_txn)); + MDB_CHECK(mdb_dbi_open(mdb_txn, NULL, 0, &mdb_dbi_)); + return new LMDBTransaction(&mdb_dbi_, mdb_txn); +} + +void LMDBTransaction::Put(const string& key, const string& value) { + MDB_val mdb_key, mdb_value; + mdb_key.mv_data = const_cast(key.data()); + mdb_key.mv_size = key.size(); + mdb_value.mv_data = const_cast(value.data()); + mdb_value.mv_size = value.size(); + MDB_CHECK(mdb_put(mdb_txn_, *mdb_dbi_, &mdb_key, &mdb_value, 0)); +} + +DB* GetDB(DataParameter::DB backend) { + switch (backend) { + case DataParameter_DB_LEVELDB: + return new LevelDB(); + case DataParameter_DB_LMDB: + return new LMDB(); + default: + LOG(FATAL) << "Unknown database backend"; + } +} + +DB* GetDB(const string& backend) { + if (backend == "leveldb") { + return new LevelDB(); + } else if (backend == "lmdb") { + return new LMDB(); + } else { + LOG(FATAL) << "Unknown database backend"; + } +} + +} // namespace db +} // namespace caffe diff --git a/src/caffe/util/insert_splits.cpp b/src/caffe/util/insert_splits.cpp index f20efdae8c6..416f80ab3c2 100644 --- a/src/caffe/util/insert_splits.cpp +++ b/src/caffe/util/insert_splits.cpp @@ -12,7 +12,7 @@ namespace caffe { void InsertSplits(const NetParameter& param, NetParameter* param_split) { // Initialize by copying from the input NetParameter. param_split->CopyFrom(param); - param_split->clear_layers(); + param_split->clear_layer(); map > blob_name_to_last_top_idx; map, pair > bottom_idx_to_source_top_idx; map, int> top_idx_to_bottom_count; @@ -25,8 +25,8 @@ void InsertSplits(const NetParameter& param, NetParameter* param_split) { const string& blob_name = param.input(i); blob_name_to_last_top_idx[blob_name] = make_pair(-1, i); } - for (int i = 0; i < param.layers_size(); ++i) { - const LayerParameter& layer_param = param.layers(i); + for (int i = 0; i < param.layer_size(); ++i) { + const LayerParameter& layer_param = param.layer(i); layer_idx_to_layer_name[i] = layer_param.name(); for (int j = 0; j < layer_param.bottom_size(); ++j) { const string& blob_name = layer_param.bottom(j); @@ -56,22 +56,22 @@ void InsertSplits(const NetParameter& param, NetParameter* param_split) { } } } - // Create split layer for any input blobs used by other layers as bottom + // Create split layer for any input blobs used by other layer as bottom // blobs more than once. for (int i = 0; i < param.input_size(); ++i) { const int split_count = top_idx_to_bottom_count[make_pair(-1, i)]; if (split_count > 1) { const string& layer_name = layer_idx_to_layer_name[-1]; const string& blob_name = param.input(i); - LayerParameter* split_layer_param = param_split->add_layers(); + LayerParameter* split_layer_param = param_split->add_layer(); const float kZeroLossWeight = 0; ConfigureSplitLayer(layer_name, blob_name, i, split_count, kZeroLossWeight, split_layer_param); } } - for (int i = 0; i < param.layers_size(); ++i) { - LayerParameter* layer_param = param_split->add_layers(); - layer_param->CopyFrom(param.layers(i)); + for (int i = 0; i < param.layer_size(); ++i) { + LayerParameter* layer_param = param_split->add_layer(); + layer_param->CopyFrom(param.layer(i)); // Replace any shared bottom blobs with split layer outputs. for (int j = 0; j < layer_param->bottom_size(); ++j) { const pair& top_idx = @@ -84,7 +84,7 @@ void InsertSplits(const NetParameter& param, NetParameter* param_split) { blob_name, top_idx.second, top_idx_to_bottom_split_idx[top_idx]++)); } } - // Create split layer for any top blobs used by other layers as bottom + // Create split layer for any top blobs used by other layer as bottom // blobs more than once. for (int j = 0; j < layer_param->top_size(); ++j) { const pair& top_idx = make_pair(i, j); @@ -92,7 +92,7 @@ void InsertSplits(const NetParameter& param, NetParameter* param_split) { if (split_count > 1) { const string& layer_name = layer_idx_to_layer_name[i]; const string& blob_name = layer_param->top(j); - LayerParameter* split_layer_param = param_split->add_layers(); + LayerParameter* split_layer_param = param_split->add_layer(); const float loss_weight = top_idx_to_loss_weight[top_idx]; ConfigureSplitLayer(layer_name, blob_name, j, split_count, loss_weight, split_layer_param); @@ -111,7 +111,7 @@ void ConfigureSplitLayer(const string& layer_name, const string& blob_name, split_layer_param->Clear(); split_layer_param->add_bottom(blob_name); split_layer_param->set_name(SplitLayerName(layer_name, blob_name, blob_idx)); - split_layer_param->set_type(LayerParameter_LayerType_SPLIT); + split_layer_param->set_type("Split"); for (int k = 0; k < split_count; ++k) { split_layer_param->add_top( SplitBlobName(layer_name, blob_name, blob_idx, k)); diff --git a/src/caffe/util/io.cpp b/src/caffe/util/io.cpp index 4a22e18725d..b243a9804ec 100644 --- a/src/caffe/util/io.cpp +++ b/src/caffe/util/io.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -18,6 +17,8 @@ #include "caffe/proto/caffe.pb.h" #include "caffe/util/io.hpp" +const int kProtoReadBytesLimit = INT_MAX; // Max size of 2 GB minus 1 byte. + namespace caffe { using google::protobuf::io::FileInputStream; @@ -51,7 +52,7 @@ bool ReadProtoFromBinaryFile(const char* filename, Message* proto) { CHECK_NE(fd, -1) << "File not found: " << filename; ZeroCopyInputStream* raw_input = new FileInputStream(fd); CodedInputStream* coded_input = new CodedInputStream(raw_input); - coded_input->SetTotalBytesLimit(1073741824, 536870912); + coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912); bool success = proto->ParseFromCodedStream(coded_input); @@ -66,57 +67,165 @@ void WriteProtoToBinaryFile(const Message& proto, const char* filename) { CHECK(proto.SerializeToOstream(&output)); } -bool ReadImageToDatum(const string& filename, const int label, - const int height, const int width, const bool is_color, Datum* datum) { +cv::Mat ReadImageToCVMat(const string& filename, + const int height, const int width, const bool is_color) { cv::Mat cv_img; int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE); - cv::Mat cv_img_origin = cv::imread(filename, cv_read_flag); if (!cv_img_origin.data) { LOG(ERROR) << "Could not open or find file " << filename; - return false; + return cv_img_origin; } if (height > 0 && width > 0) { cv::resize(cv_img_origin, cv_img, cv::Size(width, height)); } else { cv_img = cv_img_origin; } + return cv_img; +} + +cv::Mat ReadImageToCVMat(const string& filename, + const int height, const int width) { + return ReadImageToCVMat(filename, height, width, true); +} + +cv::Mat ReadImageToCVMat(const string& filename, + const bool is_color) { + return ReadImageToCVMat(filename, 0, 0, is_color); +} - int num_channels = (is_color ? 3 : 1); - datum->set_channels(num_channels); +cv::Mat ReadImageToCVMat(const string& filename) { + return ReadImageToCVMat(filename, 0, 0, true); +} +// Do the file extension and encoding match? +static bool matchExt(const std::string & fn, + std::string en) { + size_t p = fn.rfind('.'); + std::string ext = p != fn.npos ? fn.substr(p) : fn; + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + std::transform(en.begin(), en.end(), en.begin(), ::tolower); + if ( ext == en ) + return true; + if ( en == "jpg" && ext == "jpeg" ) + return true; + return false; +} +bool ReadImageToDatum(const string& filename, const int label, + const int height, const int width, const bool is_color, + const std::string & encoding, Datum* datum) { + cv::Mat cv_img = ReadImageToCVMat(filename, height, width, is_color); + if (cv_img.data) { + if (encoding.size()) { + if ( (cv_img.channels() == 3) == is_color && !height && !width && + matchExt(filename, encoding) ) + return ReadFileToDatum(filename, label, datum); + std::vector buf; + cv::imencode("."+encoding, cv_img, buf); + datum->set_data(std::string(reinterpret_cast(&buf[0]), + buf.size())); + datum->set_label(label); + datum->set_encoded(true); + return true; + } + CVMatToDatum(cv_img, datum); + datum->set_label(label); + return true; + } else { + return false; + } +} + +bool ReadFileToDatum(const string& filename, const int label, + Datum* datum) { + std::streampos size; + + fstream file(filename.c_str(), ios::in|ios::binary|ios::ate); + if (file.is_open()) { + size = file.tellg(); + std::string buffer(size, ' '); + file.seekg(0, ios::beg); + file.read(&buffer[0], size); + file.close(); + datum->set_data(buffer); + datum->set_label(label); + datum->set_encoded(true); + return true; + } else { + return false; + } +} + +cv::Mat DecodeDatumToCVMatNative(const Datum& datum) { + cv::Mat cv_img; + CHECK(datum.encoded()) << "Datum not encoded"; + const string& data = datum.data(); + std::vector vec_data(data.c_str(), data.c_str() + data.size()); + cv_img = cv::imdecode(vec_data, -1); + if (!cv_img.data) { + LOG(ERROR) << "Could not decode datum "; + } + return cv_img; +} +cv::Mat DecodeDatumToCVMat(const Datum& datum, bool is_color) { + cv::Mat cv_img; + CHECK(datum.encoded()) << "Datum not encoded"; + const string& data = datum.data(); + std::vector vec_data(data.c_str(), data.c_str() + data.size()); + int cv_read_flag = (is_color ? CV_LOAD_IMAGE_COLOR : + CV_LOAD_IMAGE_GRAYSCALE); + cv_img = cv::imdecode(vec_data, cv_read_flag); + if (!cv_img.data) { + LOG(ERROR) << "Could not decode datum "; + } + return cv_img; +} + +// If Datum is encoded will decoded using DecodeDatumToCVMat and CVMatToDatum +// If Datum is not encoded will do nothing +bool DecodeDatumNative(Datum* datum) { + if (datum->encoded()) { + cv::Mat cv_img = DecodeDatumToCVMatNative((*datum)); + CVMatToDatum(cv_img, datum); + return true; + } else { + return false; + } +} +bool DecodeDatum(Datum* datum, bool is_color) { + if (datum->encoded()) { + cv::Mat cv_img = DecodeDatumToCVMat((*datum), is_color); + CVMatToDatum(cv_img, datum); + return true; + } else { + return false; + } +} + +void CVMatToDatum(const cv::Mat& cv_img, Datum* datum) { + CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte"; + datum->set_channels(cv_img.channels()); datum->set_height(cv_img.rows); datum->set_width(cv_img.cols); - datum->set_label(label); datum->clear_data(); datum->clear_float_data(); - string* datum_string = datum->mutable_data(); - if (is_color) { - for (int c = 0; c < num_channels; ++c) { - for (int h = 0; h < cv_img.rows; ++h) { - for (int w = 0; w < cv_img.cols; ++w) { - datum_string->push_back( - static_cast(cv_img.at(h, w)[c])); - } + datum->set_encoded(false); + int datum_channels = datum->channels(); + int datum_height = datum->height(); + int datum_width = datum->width(); + int datum_size = datum_channels * datum_height * datum_width; + std::string buffer(datum_size, ' '); + for (int h = 0; h < datum_height; ++h) { + const uchar* ptr = cv_img.ptr(h); + int img_index = 0; + for (int w = 0; w < datum_width; ++w) { + for (int c = 0; c < datum_channels; ++c) { + int datum_index = (c * datum_height + h) * datum_width + w; + buffer[datum_index] = static_cast(ptr[img_index++]); } } - } else { // Faster than repeatedly testing is_color for each pixel w/i loop - for (int h = 0; h < cv_img.rows; ++h) { - for (int w = 0; w < cv_img.cols; ++w) { - datum_string->push_back( - static_cast(cv_img.at(h, w))); - } - } } - return true; -} - -leveldb::Options GetLevelDBOptions() { - // In default, we will return the leveldb option and set the max open files - // in order to avoid using up the operating system's limit. - leveldb::Options options; - options.max_open_files = 100; - return options; + datum->set_data(buffer); } // Verifies format of data stored in HDF5 file and reshapes blob accordingly. @@ -124,6 +233,9 @@ template void hdf5_load_nd_dataset_helper( hid_t file_id, const char* dataset_name_, int min_dim, int max_dim, Blob* blob) { + // Verify that the dataset exists. + CHECK(H5LTfind_dataset(file_id, dataset_name_)) + << "Failed to find HDF5 dataset " << dataset_name_; // Verify that the number of dimensions is in the accepted range. herr_t status; int ndims; @@ -167,7 +279,7 @@ void hdf5_load_nd_dataset(hid_t file_id, const char* dataset_name_, template <> void hdf5_save_nd_dataset( - const hid_t file_id, const string dataset_name, const Blob& blob) { + const hid_t file_id, const string& dataset_name, const Blob& blob) { hsize_t dims[HDF5_NUM_DIMS]; dims[0] = blob.num(); dims[1] = blob.channels(); @@ -180,7 +292,7 @@ void hdf5_save_nd_dataset( template <> void hdf5_save_nd_dataset( - const hid_t file_id, const string dataset_name, const Blob& blob) { + const hid_t file_id, const string& dataset_name, const Blob& blob) { hsize_t dims[HDF5_NUM_DIMS]; dims[0] = blob.num(); dims[1] = blob.channels(); diff --git a/src/caffe/util/math_functions.cpp b/src/caffe/util/math_functions.cpp index c3afd2ffbc5..13e17be582b 100644 --- a/src/caffe/util/math_functions.cpp +++ b/src/caffe/util/math_functions.cpp @@ -370,9 +370,6 @@ double caffe_cpu_asum(const int n, const double* x) { return cblas_dasum(n, x, 1); } -INSTANTIATE_CAFFE_CPU_UNARY_FUNC(sign); -INSTANTIATE_CAFFE_CPU_UNARY_FUNC(sgnbit); - template <> void caffe_cpu_scale(const int n, const float alpha, const float *x, float* y) { diff --git a/src/caffe/util/math_functions.cu b/src/caffe/util/math_functions.cu index 4ae4bba6029..43e65eb9a69 100644 --- a/src/caffe/util/math_functions.cu +++ b/src/caffe/util/math_functions.cu @@ -303,6 +303,27 @@ void caffe_gpu_abs(const int N, const double* a, double* y) { } +template +__global__ void exp_kernel(const int n, const Dtype* a, Dtype* y) { + CUDA_KERNEL_LOOP(index, n) { + y[index] = exp(a[index]); + } +} + +template <> +void caffe_gpu_exp(const int N, const float* a, float* y) { + // NOLINT_NEXT_LINE(whitespace/operators) + exp_kernel<<>>( + N, a, y); +} + +template <> +void caffe_gpu_exp(const int N, const double* a, double* y) { + // NOLINT_NEXT_LINE(whitespace/operators) + exp_kernel<<>>( + N, a, y); +} + template __global__ void powx_kernel(const int n, const Dtype* a, const Dtype alpha, Dtype* y) { diff --git a/src/caffe/util/upgrade_proto.cpp b/src/caffe/util/upgrade_proto.cpp index c69c58eb340..38a06026adf 100644 --- a/src/caffe/util/upgrade_proto.cpp +++ b/src/caffe/util/upgrade_proto.cpp @@ -13,6 +13,10 @@ namespace caffe { bool NetNeedsUpgrade(const NetParameter& net_param) { + return NetNeedsV0ToV1Upgrade(net_param) || NetNeedsV1ToV2Upgrade(net_param); +} + +bool NetNeedsV0ToV1Upgrade(const NetParameter& net_param) { for (int i = 0; i < net_param.layers_size(); ++i) { if (net_param.layers(i).has_layer()) { return true; @@ -21,6 +25,10 @@ bool NetNeedsUpgrade(const NetParameter& net_param) { return false; } +bool NetNeedsV1ToV2Upgrade(const NetParameter& net_param) { + return net_param.layers_size() > 0; +} + bool UpgradeV0Net(const NetParameter& v0_net_param_padding_layers, NetParameter* net_param) { // First upgrade padding layers to padded conv layers. @@ -33,8 +41,8 @@ bool UpgradeV0Net(const NetParameter& v0_net_param_padding_layers, net_param->set_name(v0_net_param.name()); } for (int i = 0; i < v0_net_param.layers_size(); ++i) { - is_fully_compatible &= UpgradeLayerParameter(v0_net_param.layers(i), - net_param->add_layers()); + is_fully_compatible &= UpgradeV0LayerParameter(v0_net_param.layers(i), + net_param->add_layers()); } for (int i = 0; i < v0_net_param.input_size(); ++i) { net_param->add_input(v0_net_param.input(i)); @@ -61,7 +69,7 @@ void UpgradeV0PaddingLayers(const NetParameter& param, blob_name_to_last_top_idx[blob_name] = -1; } for (int i = 0; i < param.layers_size(); ++i) { - const LayerParameter& layer_connection = param.layers(i); + const V1LayerParameter& layer_connection = param.layers(i); const V0LayerParameter& layer_param = layer_connection.layer(); // Add the layer to the new net, unless it's a padding layer. if (layer_param.type() != "padding") { @@ -77,7 +85,7 @@ void UpgradeV0PaddingLayers(const NetParameter& param, if (top_idx == -1) { continue; } - LayerParameter source_layer = param.layers(top_idx); + const V1LayerParameter& source_layer = param.layers(top_idx); if (source_layer.layer().type() == "padding") { // This layer has a padding layer as input -- check that it is a conv // layer or a pooling layer and takes only one input. Also check that @@ -107,8 +115,8 @@ void UpgradeV0PaddingLayers(const NetParameter& param, } } -bool UpgradeLayerParameter(const LayerParameter& v0_layer_connection, - LayerParameter* layer_param) { +bool UpgradeV0LayerParameter(const V1LayerParameter& v0_layer_connection, + V1LayerParameter* layer_param) { bool is_fully_compatible = true; layer_param->Clear(); for (int i = 0; i < v0_layer_connection.bottom_size(); ++i) { @@ -285,6 +293,14 @@ bool UpgradeLayerParameter(const LayerParameter& v0_layer_connection, is_fully_compatible = false; } } + if (v0_layer_param.has_k()) { + if (type == "lrn") { + layer_param->mutable_lrn_param()->set_k(v0_layer_param.k()); + } else { + LOG(ERROR) << "Unknown parameter k for layer type " << type; + is_fully_compatible = false; + } + } if (v0_layer_param.has_source()) { if (type == "data") { layer_param->mutable_data_param()->set_source(v0_layer_param.source()); @@ -451,78 +467,78 @@ bool UpgradeLayerParameter(const LayerParameter& v0_layer_connection, return is_fully_compatible; } -LayerParameter_LayerType UpgradeV0LayerType(const string& type) { +V1LayerParameter_LayerType UpgradeV0LayerType(const string& type) { if (type == "accuracy") { - return LayerParameter_LayerType_ACCURACY; + return V1LayerParameter_LayerType_ACCURACY; } else if (type == "bnll") { - return LayerParameter_LayerType_BNLL; + return V1LayerParameter_LayerType_BNLL; } else if (type == "concat") { - return LayerParameter_LayerType_CONCAT; + return V1LayerParameter_LayerType_CONCAT; } else if (type == "conv") { - return LayerParameter_LayerType_CONVOLUTION; + return V1LayerParameter_LayerType_CONVOLUTION; } else if (type == "data") { - return LayerParameter_LayerType_DATA; + return V1LayerParameter_LayerType_DATA; } else if (type == "dropout") { - return LayerParameter_LayerType_DROPOUT; + return V1LayerParameter_LayerType_DROPOUT; } else if (type == "euclidean_loss") { - return LayerParameter_LayerType_EUCLIDEAN_LOSS; + return V1LayerParameter_LayerType_EUCLIDEAN_LOSS; } else if (type == "flatten") { - return LayerParameter_LayerType_FLATTEN; + return V1LayerParameter_LayerType_FLATTEN; } else if (type == "hdf5_data") { - return LayerParameter_LayerType_HDF5_DATA; + return V1LayerParameter_LayerType_HDF5_DATA; } else if (type == "hdf5_output") { - return LayerParameter_LayerType_HDF5_OUTPUT; + return V1LayerParameter_LayerType_HDF5_OUTPUT; } else if (type == "im2col") { - return LayerParameter_LayerType_IM2COL; + return V1LayerParameter_LayerType_IM2COL; } else if (type == "images") { - return LayerParameter_LayerType_IMAGE_DATA; + return V1LayerParameter_LayerType_IMAGE_DATA; } else if (type == "infogain_loss") { - return LayerParameter_LayerType_INFOGAIN_LOSS; + return V1LayerParameter_LayerType_INFOGAIN_LOSS; } else if (type == "innerproduct") { - return LayerParameter_LayerType_INNER_PRODUCT; + return V1LayerParameter_LayerType_INNER_PRODUCT; } else if (type == "lrn") { - return LayerParameter_LayerType_LRN; + return V1LayerParameter_LayerType_LRN; } else if (type == "multinomial_logistic_loss") { - return LayerParameter_LayerType_MULTINOMIAL_LOGISTIC_LOSS; + return V1LayerParameter_LayerType_MULTINOMIAL_LOGISTIC_LOSS; } else if (type == "pool") { - return LayerParameter_LayerType_POOLING; + return V1LayerParameter_LayerType_POOLING; } else if (type == "relu") { - return LayerParameter_LayerType_RELU; + return V1LayerParameter_LayerType_RELU; } else if (type == "sigmoid") { - return LayerParameter_LayerType_SIGMOID; + return V1LayerParameter_LayerType_SIGMOID; } else if (type == "softmax") { - return LayerParameter_LayerType_SOFTMAX; + return V1LayerParameter_LayerType_SOFTMAX; } else if (type == "softmax_loss") { - return LayerParameter_LayerType_SOFTMAX_LOSS; + return V1LayerParameter_LayerType_SOFTMAX_LOSS; } else if (type == "split") { - return LayerParameter_LayerType_SPLIT; + return V1LayerParameter_LayerType_SPLIT; } else if (type == "tanh") { - return LayerParameter_LayerType_TANH; + return V1LayerParameter_LayerType_TANH; } else if (type == "window_data") { - return LayerParameter_LayerType_WINDOW_DATA; + return V1LayerParameter_LayerType_WINDOW_DATA; } else { LOG(FATAL) << "Unknown layer name: " << type; - return LayerParameter_LayerType_NONE; + return V1LayerParameter_LayerType_NONE; } } bool NetNeedsDataUpgrade(const NetParameter& net_param) { for (int i = 0; i < net_param.layers_size(); ++i) { - if (net_param.layers(i).type() == LayerParameter_LayerType_DATA) { + if (net_param.layers(i).type() == V1LayerParameter_LayerType_DATA) { DataParameter layer_param = net_param.layers(i).data_param(); if (layer_param.has_scale()) { return true; } if (layer_param.has_mean_file()) { return true; } if (layer_param.has_crop_size()) { return true; } if (layer_param.has_mirror()) { return true; } } - if (net_param.layers(i).type() == LayerParameter_LayerType_IMAGE_DATA) { + if (net_param.layers(i).type() == V1LayerParameter_LayerType_IMAGE_DATA) { ImageDataParameter layer_param = net_param.layers(i).image_data_param(); if (layer_param.has_scale()) { return true; } if (layer_param.has_mean_file()) { return true; } if (layer_param.has_crop_size()) { return true; } if (layer_param.has_mirror()) { return true; } } - if (net_param.layers(i).type() == LayerParameter_LayerType_WINDOW_DATA) { + if (net_param.layers(i).type() == V1LayerParameter_LayerType_WINDOW_DATA) { WindowDataParameter layer_param = net_param.layers(i).window_data_param(); if (layer_param.has_scale()) { return true; } if (layer_param.has_mean_file()) { return true; } @@ -535,7 +551,7 @@ bool NetNeedsDataUpgrade(const NetParameter& net_param) { #define CONVERT_LAYER_TRANSFORM_PARAM(TYPE, Name, param_name) \ do { \ - if (net_param->layers(i).type() == LayerParameter_LayerType_##TYPE) { \ + if (net_param->layers(i).type() == V1LayerParameter_LayerType_##TYPE) { \ Name##Parameter* layer_param = \ net_param->mutable_layers(i)->mutable_##param_name##_param(); \ TransformationParameter* transform_param = \ @@ -567,34 +583,16 @@ void UpgradeNetDataTransformation(NetParameter* net_param) { } } -void NetParameterToPrettyPrint(const NetParameter& param, - NetParameterPrettyPrint* pretty_param) { - pretty_param->Clear(); - if (param.has_name()) { - pretty_param->set_name(param.name()); - } - if (param.has_force_backward()) { - pretty_param->set_force_backward(param.force_backward()); - } - for (int i = 0; i < param.input_size(); ++i) { - pretty_param->add_input(param.input(i)); - } - for (int i = 0; i < param.input_dim_size(); ++i) { - pretty_param->add_input_dim(param.input_dim(i)); - } - for (int i = 0; i < param.layers_size(); ++i) { - pretty_param->add_layers()->CopyFrom(param.layers(i)); - } -} - -void UpgradeNetAsNeeded(const string& param_file, NetParameter* param) { - if (NetNeedsUpgrade(*param)) { +bool UpgradeNetAsNeeded(const string& param_file, NetParameter* param) { + bool success = true; + if (NetNeedsV0ToV1Upgrade(*param)) { // NetParameter was specified using the old style (V0LayerParameter); try to // upgrade it. LOG(ERROR) << "Attempting to upgrade input file specified using deprecated " << "V0LayerParameter: " << param_file; NetParameter original_param(*param); if (!UpgradeV0Net(original_param, param)) { + success = false; LOG(ERROR) << "Warning: had one or more problems upgrading " << "V0NetParameter to NetParameter (see above); continuing anyway."; } else { @@ -616,6 +614,313 @@ void UpgradeNetAsNeeded(const string& param_file, NetParameter* param) { LOG(ERROR) << "Note that future Caffe releases will only support " << "transform_param messages for transformation fields."; } + if (NetNeedsV1ToV2Upgrade(*param)) { + LOG(ERROR) << "Attempting to upgrade input file specified using deprecated " + << "V1LayerParameter: " << param_file; + NetParameter original_param(*param); + if (!UpgradeV1Net(original_param, param)) { + success = false; + LOG(ERROR) << "Warning: had one or more problems upgrading " + << "V1LayerParameter (see above); continuing anyway."; + } else { + LOG(INFO) << "Successfully upgraded file specified using deprecated " + << "V1LayerParameter"; + } + } + return success; +} + +bool UpgradeV1Net(const NetParameter& v1_net_param, NetParameter* net_param) { + bool is_fully_compatible = true; + if (v1_net_param.layer_size() > 0) { + LOG(ERROR) << "Input NetParameter to be upgraded already specifies 'layer' " + << "fields; these will be ignored for the upgrade."; + is_fully_compatible = false; + } + net_param->CopyFrom(v1_net_param); + net_param->clear_layers(); + net_param->clear_layer(); + for (int i = 0; i < v1_net_param.layers_size(); ++i) { + if (!UpgradeV1LayerParameter(v1_net_param.layers(i), + net_param->add_layer())) { + LOG(ERROR) << "Upgrade of input layer " << i << " failed."; + is_fully_compatible = false; + } + } + return is_fully_compatible; +} + +bool UpgradeV1LayerParameter(const V1LayerParameter& v1_layer_param, + LayerParameter* layer_param) { + layer_param->Clear(); + bool is_fully_compatible = true; + for (int i = 0; i < v1_layer_param.bottom_size(); ++i) { + layer_param->add_bottom(v1_layer_param.bottom(i)); + } + for (int i = 0; i < v1_layer_param.top_size(); ++i) { + layer_param->add_top(v1_layer_param.top(i)); + } + if (v1_layer_param.has_name()) { + layer_param->set_name(v1_layer_param.name()); + } + for (int i = 0; i < v1_layer_param.include_size(); ++i) { + layer_param->add_include()->CopyFrom(v1_layer_param.include(i)); + } + for (int i = 0; i < v1_layer_param.exclude_size(); ++i) { + layer_param->add_exclude()->CopyFrom(v1_layer_param.exclude(i)); + } + if (v1_layer_param.has_type()) { + layer_param->set_type(UpgradeV1LayerType(v1_layer_param.type())); + } + for (int i = 0; i < v1_layer_param.blobs_size(); ++i) { + layer_param->add_blobs()->CopyFrom(v1_layer_param.blobs(i)); + } + for (int i = 0; i < v1_layer_param.param_size(); ++i) { + while (layer_param->param_size() <= i) { layer_param->add_param(); } + layer_param->mutable_param(i)->set_name(v1_layer_param.param(i)); + } + ParamSpec_DimCheckMode mode; + for (int i = 0; i < v1_layer_param.blob_share_mode_size(); ++i) { + while (layer_param->param_size() <= i) { layer_param->add_param(); } + switch (v1_layer_param.blob_share_mode(i)) { + case V1LayerParameter_DimCheckMode_STRICT: + mode = ParamSpec_DimCheckMode_STRICT; + break; + case V1LayerParameter_DimCheckMode_PERMISSIVE: + mode = ParamSpec_DimCheckMode_PERMISSIVE; + break; + default: + LOG(FATAL) << "Unknown blob_share_mode: " + << v1_layer_param.blob_share_mode(i); + break; + } + layer_param->mutable_param(i)->set_share_mode(mode); + } + for (int i = 0; i < v1_layer_param.blobs_lr_size(); ++i) { + while (layer_param->param_size() <= i) { layer_param->add_param(); } + layer_param->mutable_param(i)->set_lr_mult(v1_layer_param.blobs_lr(i)); + } + for (int i = 0; i < v1_layer_param.weight_decay_size(); ++i) { + while (layer_param->param_size() <= i) { layer_param->add_param(); } + layer_param->mutable_param(i)->set_decay_mult( + v1_layer_param.weight_decay(i)); + } + for (int i = 0; i < v1_layer_param.loss_weight_size(); ++i) { + layer_param->add_loss_weight(v1_layer_param.loss_weight(i)); + } + if (v1_layer_param.has_accuracy_param()) { + layer_param->mutable_accuracy_param()->CopyFrom( + v1_layer_param.accuracy_param()); + } + if (v1_layer_param.has_argmax_param()) { + layer_param->mutable_argmax_param()->CopyFrom( + v1_layer_param.argmax_param()); + } + if (v1_layer_param.has_concat_param()) { + layer_param->mutable_concat_param()->CopyFrom( + v1_layer_param.concat_param()); + } + if (v1_layer_param.has_contrastive_loss_param()) { + layer_param->mutable_contrastive_loss_param()->CopyFrom( + v1_layer_param.contrastive_loss_param()); + } + if (v1_layer_param.has_convolution_param()) { + layer_param->mutable_convolution_param()->CopyFrom( + v1_layer_param.convolution_param()); + } + if (v1_layer_param.has_data_param()) { + layer_param->mutable_data_param()->CopyFrom( + v1_layer_param.data_param()); + } + if (v1_layer_param.has_dropout_param()) { + layer_param->mutable_dropout_param()->CopyFrom( + v1_layer_param.dropout_param()); + } + if (v1_layer_param.has_dummy_data_param()) { + layer_param->mutable_dummy_data_param()->CopyFrom( + v1_layer_param.dummy_data_param()); + } + if (v1_layer_param.has_eltwise_param()) { + layer_param->mutable_eltwise_param()->CopyFrom( + v1_layer_param.eltwise_param()); + } + if (v1_layer_param.has_exp_param()) { + layer_param->mutable_exp_param()->CopyFrom( + v1_layer_param.exp_param()); + } + if (v1_layer_param.has_hdf5_data_param()) { + layer_param->mutable_hdf5_data_param()->CopyFrom( + v1_layer_param.hdf5_data_param()); + } + if (v1_layer_param.has_hdf5_output_param()) { + layer_param->mutable_hdf5_output_param()->CopyFrom( + v1_layer_param.hdf5_output_param()); + } + if (v1_layer_param.has_hinge_loss_param()) { + layer_param->mutable_hinge_loss_param()->CopyFrom( + v1_layer_param.hinge_loss_param()); + } + if (v1_layer_param.has_image_data_param()) { + layer_param->mutable_image_data_param()->CopyFrom( + v1_layer_param.image_data_param()); + } + if (v1_layer_param.has_infogain_loss_param()) { + layer_param->mutable_infogain_loss_param()->CopyFrom( + v1_layer_param.infogain_loss_param()); + } + if (v1_layer_param.has_inner_product_param()) { + layer_param->mutable_inner_product_param()->CopyFrom( + v1_layer_param.inner_product_param()); + } + if (v1_layer_param.has_lrn_param()) { + layer_param->mutable_lrn_param()->CopyFrom( + v1_layer_param.lrn_param()); + } + if (v1_layer_param.has_memory_data_param()) { + layer_param->mutable_memory_data_param()->CopyFrom( + v1_layer_param.memory_data_param()); + } + if (v1_layer_param.has_mvn_param()) { + layer_param->mutable_mvn_param()->CopyFrom( + v1_layer_param.mvn_param()); + } + if (v1_layer_param.has_pooling_param()) { + layer_param->mutable_pooling_param()->CopyFrom( + v1_layer_param.pooling_param()); + } + if (v1_layer_param.has_power_param()) { + layer_param->mutable_power_param()->CopyFrom( + v1_layer_param.power_param()); + } + if (v1_layer_param.has_relu_param()) { + layer_param->mutable_relu_param()->CopyFrom( + v1_layer_param.relu_param()); + } + if (v1_layer_param.has_sigmoid_param()) { + layer_param->mutable_sigmoid_param()->CopyFrom( + v1_layer_param.sigmoid_param()); + } + if (v1_layer_param.has_softmax_param()) { + layer_param->mutable_softmax_param()->CopyFrom( + v1_layer_param.softmax_param()); + } + if (v1_layer_param.has_slice_param()) { + layer_param->mutable_slice_param()->CopyFrom( + v1_layer_param.slice_param()); + } + if (v1_layer_param.has_tanh_param()) { + layer_param->mutable_tanh_param()->CopyFrom( + v1_layer_param.tanh_param()); + } + if (v1_layer_param.has_threshold_param()) { + layer_param->mutable_threshold_param()->CopyFrom( + v1_layer_param.threshold_param()); + } + if (v1_layer_param.has_window_data_param()) { + layer_param->mutable_window_data_param()->CopyFrom( + v1_layer_param.window_data_param()); + } + if (v1_layer_param.has_transform_param()) { + layer_param->mutable_transform_param()->CopyFrom( + v1_layer_param.transform_param()); + } + if (v1_layer_param.has_loss_param()) { + layer_param->mutable_loss_param()->CopyFrom( + v1_layer_param.loss_param()); + } + if (v1_layer_param.has_layer()) { + LOG(ERROR) << "Input NetParameter has V0 layer -- ignoring."; + is_fully_compatible = false; + } + return is_fully_compatible; +} + +const char* UpgradeV1LayerType(const V1LayerParameter_LayerType type) { + switch (type) { + case V1LayerParameter_LayerType_NONE: + return ""; + case V1LayerParameter_LayerType_ABSVAL: + return "AbsVal"; + case V1LayerParameter_LayerType_ACCURACY: + return "Accuracy"; + case V1LayerParameter_LayerType_ARGMAX: + return "ArgMax"; + case V1LayerParameter_LayerType_BNLL: + return "BNLL"; + case V1LayerParameter_LayerType_CONCAT: + return "Concat"; + case V1LayerParameter_LayerType_CONTRASTIVE_LOSS: + return "ContrastiveLoss"; + case V1LayerParameter_LayerType_CONVOLUTION: + return "Convolution"; + case V1LayerParameter_LayerType_DECONVOLUTION: + return "Deconvolution"; + case V1LayerParameter_LayerType_DATA: + return "Data"; + case V1LayerParameter_LayerType_DROPOUT: + return "Dropout"; + case V1LayerParameter_LayerType_DUMMY_DATA: + return "DummyData"; + case V1LayerParameter_LayerType_EUCLIDEAN_LOSS: + return "EuclideanLoss"; + case V1LayerParameter_LayerType_ELTWISE: + return "Eltwise"; + case V1LayerParameter_LayerType_EXP: + return "Exp"; + case V1LayerParameter_LayerType_FLATTEN: + return "Flatten"; + case V1LayerParameter_LayerType_HDF5_DATA: + return "HDF5Data"; + case V1LayerParameter_LayerType_HDF5_OUTPUT: + return "HDF5Output"; + case V1LayerParameter_LayerType_HINGE_LOSS: + return "HingeLoss"; + case V1LayerParameter_LayerType_IM2COL: + return "Im2col"; + case V1LayerParameter_LayerType_IMAGE_DATA: + return "ImageData"; + case V1LayerParameter_LayerType_INFOGAIN_LOSS: + return "InfogainLoss"; + case V1LayerParameter_LayerType_INNER_PRODUCT: + return "InnerProduct"; + case V1LayerParameter_LayerType_LRN: + return "LRN"; + case V1LayerParameter_LayerType_MEMORY_DATA: + return "MemoryData"; + case V1LayerParameter_LayerType_MULTINOMIAL_LOGISTIC_LOSS: + return "MultinomialLogisticLoss"; + case V1LayerParameter_LayerType_MVN: + return "MVN"; + case V1LayerParameter_LayerType_POOLING: + return "Pooling"; + case V1LayerParameter_LayerType_POWER: + return "Power"; + case V1LayerParameter_LayerType_RELU: + return "ReLU"; + case V1LayerParameter_LayerType_SIGMOID: + return "Sigmoid"; + case V1LayerParameter_LayerType_SIGMOID_CROSS_ENTROPY_LOSS: + return "SigmoidCrossEntropyLoss"; + case V1LayerParameter_LayerType_SILENCE: + return "Silence"; + case V1LayerParameter_LayerType_SOFTMAX: + return "Softmax"; + case V1LayerParameter_LayerType_SOFTMAX_LOSS: + return "SoftmaxWithLoss"; + case V1LayerParameter_LayerType_SPLIT: + return "Split"; + case V1LayerParameter_LayerType_SLICE: + return "Slice"; + case V1LayerParameter_LayerType_TANH: + return "TanH"; + case V1LayerParameter_LayerType_WINDOW_DATA: + return "WindowData"; + case V1LayerParameter_LayerType_THRESHOLD: + return "Threshold"; + default: + LOG(FATAL) << "Unknown V1LayerParameter layer type: " << type; + return ""; + } } void ReadNetParamsFromTextFileOrDie(const string& param_file, diff --git a/src/gtest/CMakeLists.txt b/src/gtest/CMakeLists.txt index 82a4120ca3f..ef7ff7ed14b 100644 --- a/src/gtest/CMakeLists.txt +++ b/src/gtest/CMakeLists.txt @@ -1,6 +1,5 @@ -project(gtest CXX C) -cmake_minimum_required(VERSION 2.6.2) +add_library(gtest STATIC EXCLUDE_FROM_ALL gtest.h gtest-all.cpp) +caffe_default_properties(gtest) -add_library(gtest gtest-all.cpp) -add_library(gtest_main gtest_main.cc) -target_link_libraries(gtest_main gtest) \ No newline at end of file +#add_library(gtest_main gtest_main.cc) +#target_link_libraries(gtest_main gtest) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 0da19fbadc8..02fbd5cadd8 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,19 +1,29 @@ -project( Tools ) +# Collect source files +file(GLOB_RECURSE srcs ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -# Find all source files -file(GLOB_RECURSE TOOLS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +# Build each source file independently +foreach(source ${srcs}) + get_filename_component(name ${source} NAME_WE) -# Build each source file independently -foreach(source ${TOOLS_SOURCES}) - get_filename_component(name ${source} NAME_WE) - add_executable(${name}.bin ${source}) - set_target_properties(${name}.bin PROPERTIES OUTPUT_NAME ${name}) - target_link_libraries(${name}.bin caffe) + # caffe target already exits + if(name MATCHES "caffe") + set(name ${name}.bin) + endif() -### Install ################################################################################# + # target + add_executable(${name} ${source}) + target_link_libraries(${name} ${Caffe_LINK}) + caffe_default_properties(${name}) - install(TARGETS ${name}.bin DESTINATION tools) + # set back RUNTIME_OUTPUT_DIRECTORY + caffe_set_runtime_directory(${name} "${PROJECT_BINARY_DIR}/tools") + caffe_set_solution_folder(${name} tools) + # restore output name without suffix + if(name MATCHES "caffe.bin") + set_target_properties(${name} PROPERTIES OUTPUT_NAME caffe) + endif() + # Install + install(TARGETS ${name} DESTINATION bin) endforeach(source) - diff --git a/tools/caffe.cpp b/tools/caffe.cpp index 046b38938e4..f04e28a3674 100644 --- a/tools/caffe.cpp +++ b/tools/caffe.cpp @@ -21,7 +21,7 @@ DEFINE_int32(gpu, -1, DEFINE_string(solver, "", "The solver definition protocol buffer text file."); DEFINE_string(model, "", - "The model definition protocol buffer text file."); + "The model definition protocol buffer text file.."); DEFINE_string(snapshot, "", "Optional; the snapshot solver state to resume training."); DEFINE_string(weights, "", @@ -139,8 +139,7 @@ int test() { Caffe::set_mode(Caffe::CPU); } // Instantiate the caffe net. - Caffe::set_phase(Caffe::TEST); - Net caffe_net(FLAGS_model); + Net caffe_net(FLAGS_model, caffe::TEST); caffe_net.CopyTrainedLayersFrom(FLAGS_weights); LOG(INFO) << "Running for " << FLAGS_iterations << " iterations."; @@ -205,8 +204,7 @@ int time() { Caffe::set_mode(Caffe::CPU); } // Instantiate the caffe net. - Caffe::set_phase(Caffe::TRAIN); - Net caffe_net(FLAGS_model); + Net caffe_net(FLAGS_model, caffe::TRAIN); // Do a clean forward and backward pass, so that memory allocation are done // and future iterations will be more stable. @@ -220,8 +218,8 @@ int time() { caffe_net.Backward(); const vector > >& layers = caffe_net.layers(); - vector*> >& bottom_vecs = caffe_net.bottom_vecs(); - vector*> >& top_vecs = caffe_net.top_vecs(); + const vector*> >& bottom_vecs = caffe_net.bottom_vecs(); + const vector*> >& top_vecs = caffe_net.top_vecs(); const vector >& bottom_need_backward = caffe_net.bottom_need_backward(); LOG(INFO) << "*** Benchmark begins ***"; @@ -229,38 +227,54 @@ int time() { Timer total_timer; total_timer.Start(); Timer forward_timer; - forward_timer.Start(); + Timer backward_timer; Timer timer; - for (int i = 0; i < layers.size(); ++i) { - const caffe::string& layername = layers[i]->layer_param().name(); - timer.Start(); - for (int j = 0; j < FLAGS_iterations; ++j) { + std::vector forward_time_per_layer(layers.size(), 0.0); + std::vector backward_time_per_layer(layers.size(), 0.0); + double forward_time = 0.0; + double backward_time = 0.0; + for (int j = 0; j < FLAGS_iterations; ++j) { + Timer iter_timer; + iter_timer.Start(); + forward_timer.Start(); + for (int i = 0; i < layers.size(); ++i) { + timer.Start(); // Although Reshape should be essentially free, we include it here // so that we will notice Reshape performance bugs. - layers[i]->Reshape(bottom_vecs[i], &top_vecs[i]); - layers[i]->Forward(bottom_vecs[i], &top_vecs[i]); + layers[i]->Reshape(bottom_vecs[i], top_vecs[i]); + layers[i]->Forward(bottom_vecs[i], top_vecs[i]); + forward_time_per_layer[i] += timer.MicroSeconds(); } - LOG(INFO) << layername << "\tforward: " << timer.MilliSeconds() << - " milliseconds."; - } - LOG(INFO) << "Forward pass: " << forward_timer.MilliSeconds() << - " milliseconds."; - Timer backward_timer; - backward_timer.Start(); - for (int i = layers.size() - 1; i >= 0; --i) { - const caffe::string& layername = layers[i]->layer_param().name(); - timer.Start(); - for (int j = 0; j < FLAGS_iterations; ++j) { + forward_time += forward_timer.MicroSeconds(); + backward_timer.Start(); + for (int i = layers.size() - 1; i >= 0; --i) { + timer.Start(); layers[i]->Backward(top_vecs[i], bottom_need_backward[i], - &bottom_vecs[i]); + bottom_vecs[i]); + backward_time_per_layer[i] += timer.MicroSeconds(); } - LOG(INFO) << layername << "\tbackward: " - << timer.MilliSeconds() << " milliseconds."; + backward_time += backward_timer.MicroSeconds(); + LOG(INFO) << "Iteration: " << j + 1 << " forward-backward time: " + << iter_timer.MilliSeconds() << " ms."; + } + LOG(INFO) << "Average time per layer: "; + for (int i = 0; i < layers.size(); ++i) { + const caffe::string& layername = layers[i]->layer_param().name(); + LOG(INFO) << std::setfill(' ') << std::setw(10) << layername << + "\tforward: " << forward_time_per_layer[i] / 1000 / + FLAGS_iterations << " ms."; + LOG(INFO) << std::setfill(' ') << std::setw(10) << layername << + "\tbackward: " << backward_time_per_layer[i] / 1000 / + FLAGS_iterations << " ms."; } - LOG(INFO) << "Backward pass: " << backward_timer.MilliSeconds() << - " milliseconds."; - LOG(INFO) << "Total Time: " << total_timer.MilliSeconds() << - " milliseconds."; + total_timer.Stop(); + LOG(INFO) << "Average Forward pass: " << forward_time / 1000 / + FLAGS_iterations << " ms."; + LOG(INFO) << "Average Backward pass: " << backward_time / 1000 / + FLAGS_iterations << " ms."; + LOG(INFO) << "Average Forward-Backward: " << total_timer.MilliSeconds() / + FLAGS_iterations << " ms."; + LOG(INFO) << "Total Time: " << total_timer.MilliSeconds() << " ms."; LOG(INFO) << "*** Benchmark ends ***"; return 0; } diff --git a/tools/compute_image_mean.cpp b/tools/compute_image_mean.cpp index 20f1ff81f1c..b1fc7cae38f 100644 --- a/tools/compute_image_mean.cpp +++ b/tools/compute_image_mean.cpp @@ -1,82 +1,57 @@ -#include -#include -#include #include - #include #include +#include +#include + +#include "boost/scoped_ptr.hpp" +#include "gflags/gflags.h" +#include "glog/logging.h" #include "caffe/proto/caffe.pb.h" +#include "caffe/util/db.hpp" #include "caffe/util/io.hpp" -using caffe::Datum; -using caffe::BlobProto; -using std::string; +using namespace caffe; // NOLINT(build/namespaces) + using std::max; +using std::pair; +using boost::scoped_ptr; + +DEFINE_string(backend, "lmdb", + "The backend {leveldb, lmdb} containing the images"); int main(int argc, char** argv) { ::google::InitGoogleLogging(argv[0]); - if (argc < 3 || argc > 4) { - LOG(ERROR) << "Usage: compute_image_mean input_db output_file" - << " db_backend[leveldb or lmdb]"; - return 1; - } - string db_backend = "lmdb"; - if (argc == 4) { - db_backend = string(argv[3]); - } +#ifndef GFLAGS_GFLAGS_H_ + namespace gflags = google; +#endif + + gflags::SetUsageMessage("Compute the mean_image of a set of images given by" + " a leveldb/lmdb\n" + "Usage:\n" + " compute_image_mean [FLAGS] INPUT_DB [OUTPUT_FILE]\n"); - // leveldb - leveldb::DB* db; - leveldb::Options options; - options.create_if_missing = false; - leveldb::Iterator* it = NULL; - // lmdb - MDB_env* mdb_env; - MDB_dbi mdb_dbi; - MDB_val mdb_key, mdb_value; - MDB_txn* mdb_txn; - MDB_cursor* mdb_cursor; - - // Open db - if (db_backend == "leveldb") { // leveldb - LOG(INFO) << "Opening leveldb " << argv[1]; - leveldb::Status status = leveldb::DB::Open( - options, argv[1], &db); - CHECK(status.ok()) << "Failed to open leveldb " << argv[1]; - leveldb::ReadOptions read_options; - read_options.fill_cache = false; - it = db->NewIterator(read_options); - it->SeekToFirst(); - } else if (db_backend == "lmdb") { // lmdb - LOG(INFO) << "Opening lmdb " << argv[1]; - CHECK_EQ(mdb_env_create(&mdb_env), MDB_SUCCESS) << "mdb_env_create failed"; - CHECK_EQ(mdb_env_set_mapsize(mdb_env, 1099511627776), MDB_SUCCESS); // 1TB - CHECK_EQ(mdb_env_open(mdb_env, argv[1], MDB_RDONLY, 0664), - MDB_SUCCESS) << "mdb_env_open failed"; - CHECK_EQ(mdb_txn_begin(mdb_env, NULL, MDB_RDONLY, &mdb_txn), MDB_SUCCESS) - << "mdb_txn_begin failed"; - CHECK_EQ(mdb_open(mdb_txn, NULL, 0, &mdb_dbi), MDB_SUCCESS) - << "mdb_open failed"; - CHECK_EQ(mdb_cursor_open(mdb_txn, mdb_dbi, &mdb_cursor), MDB_SUCCESS) - << "mdb_cursor_open failed"; - CHECK_EQ(mdb_cursor_get(mdb_cursor, &mdb_key, &mdb_value, MDB_FIRST), - MDB_SUCCESS); - } else { - LOG(FATAL) << "Unknown db backend " << db_backend; + gflags::ParseCommandLineFlags(&argc, &argv, true); + + if (argc < 2 || argc > 3) { + gflags::ShowUsageWithFlagsRestrict(argv[0], "tools/compute_image_mean"); + return 1; } - Datum datum; + scoped_ptr db(db::GetDB(FLAGS_backend)); + db->Open(argv[1], db::READ); + scoped_ptr cursor(db->NewCursor()); + BlobProto sum_blob; int count = 0; // load first datum - if (db_backend == "leveldb") { - datum.ParseFromString(it->value().ToString()); - } else if (db_backend == "lmdb") { - datum.ParseFromArray(mdb_value.mv_data, mdb_value.mv_size); - } else { - LOG(FATAL) << "Unknown db backend " << db_backend; + Datum datum; + datum.ParseFromString(cursor->value()); + + if (DecodeDatumNative(&datum)) { + LOG(INFO) << "Decoding Datum"; } sum_blob.set_num(1); @@ -90,81 +65,55 @@ int main(int argc, char** argv) { sum_blob.add_data(0.); } LOG(INFO) << "Starting Iteration"; - if (db_backend == "leveldb") { // leveldb - for (it->SeekToFirst(); it->Valid(); it->Next()) { - // just a dummy operation - datum.ParseFromString(it->value().ToString()); - const string& data = datum.data(); - size_in_datum = std::max(datum.data().size(), - datum.float_data_size()); - CHECK_EQ(size_in_datum, data_size) << "Incorrect data field size " << - size_in_datum; - if (data.size() != 0) { - for (int i = 0; i < size_in_datum; ++i) { - sum_blob.set_data(i, sum_blob.data(i) + (uint8_t)data[i]); - } - } else { - for (int i = 0; i < size_in_datum; ++i) { - sum_blob.set_data(i, sum_blob.data(i) + - static_cast(datum.float_data(i))); - } + while (cursor->valid()) { + Datum datum; + datum.ParseFromString(cursor->value()); + DecodeDatumNative(&datum); + + const std::string& data = datum.data(); + size_in_datum = std::max(datum.data().size(), + datum.float_data_size()); + CHECK_EQ(size_in_datum, data_size) << "Incorrect data field size " << + size_in_datum; + if (data.size() != 0) { + CHECK_EQ(data.size(), size_in_datum); + for (int i = 0; i < size_in_datum; ++i) { + sum_blob.set_data(i, sum_blob.data(i) + (uint8_t)data[i]); } - ++count; - if (count % 10000 == 0) { - LOG(ERROR) << "Processed " << count << " files."; + } else { + CHECK_EQ(datum.float_data_size(), size_in_datum); + for (int i = 0; i < size_in_datum; ++i) { + sum_blob.set_data(i, sum_blob.data(i) + + static_cast(datum.float_data(i))); } } - } else if (db_backend == "lmdb") { // lmdb - CHECK_EQ(mdb_cursor_get(mdb_cursor, &mdb_key, &mdb_value, MDB_FIRST), - MDB_SUCCESS); - do { - // just a dummy operation - datum.ParseFromArray(mdb_value.mv_data, mdb_value.mv_size); - const string& data = datum.data(); - size_in_datum = std::max(datum.data().size(), - datum.float_data_size()); - CHECK_EQ(size_in_datum, data_size) << "Incorrect data field size " << - size_in_datum; - if (data.size() != 0) { - for (int i = 0; i < size_in_datum; ++i) { - sum_blob.set_data(i, sum_blob.data(i) + (uint8_t)data[i]); - } - } else { - for (int i = 0; i < size_in_datum; ++i) { - sum_blob.set_data(i, sum_blob.data(i) + - static_cast(datum.float_data(i))); - } - } - ++count; - if (count % 10000 == 0) { - LOG(ERROR) << "Processed " << count << " files."; - } - } while (mdb_cursor_get(mdb_cursor, &mdb_key, &mdb_value, MDB_NEXT) - == MDB_SUCCESS); - } else { - LOG(FATAL) << "Unknown db backend " << db_backend; + ++count; + if (count % 10000 == 0) { + LOG(INFO) << "Processed " << count << " files."; + } + cursor->Next(); } if (count % 10000 != 0) { - LOG(ERROR) << "Processed " << count << " files."; + LOG(INFO) << "Processed " << count << " files."; } for (int i = 0; i < sum_blob.data_size(); ++i) { sum_blob.set_data(i, sum_blob.data(i) / count); } // Write to disk - LOG(INFO) << "Write to " << argv[2]; - WriteProtoToBinaryFile(sum_blob, argv[2]); - - // Clean up - if (db_backend == "leveldb") { - delete db; - } else if (db_backend == "lmdb") { - mdb_cursor_close(mdb_cursor); - mdb_close(mdb_env, mdb_dbi); - mdb_txn_abort(mdb_txn); - mdb_env_close(mdb_env); - } else { - LOG(FATAL) << "Unknown db backend " << db_backend; + if (argc == 3) { + LOG(INFO) << "Write to " << argv[2]; + WriteProtoToBinaryFile(sum_blob, argv[2]); + } + const int channels = sum_blob.channels(); + const int dim = sum_blob.height() * sum_blob.width(); + std::vector mean_values(channels, 0.0); + LOG(INFO) << "Number of channels: " << channels; + for (int c = 0; c < channels; ++c) { + for (int i = 0; i < dim; ++i) { + mean_values[c] += sum_blob.data(dim * c + i); + } + LOG(INFO) << "mean_value channel [" << c << "]:" << mean_values[c] / dim; } return 0; } diff --git a/tools/convert_imageset.cpp b/tools/convert_imageset.cpp index 09e84d6b3ef..816a91f971b 100644 --- a/tools/convert_imageset.cpp +++ b/tools/convert_imageset.cpp @@ -8,34 +8,39 @@ // subfolder1/file1.JPEG 7 // .... -#include -#include -#include -#include -#include -#include - #include #include // NOLINT(readability/streams) #include #include #include +#include "boost/scoped_ptr.hpp" +#include "gflags/gflags.h" +#include "glog/logging.h" + #include "caffe/proto/caffe.pb.h" +#include "caffe/util/db.hpp" #include "caffe/util/io.hpp" #include "caffe/util/rng.hpp" using namespace caffe; // NOLINT(build/namespaces) using std::pair; -using std::string; +using boost::scoped_ptr; DEFINE_bool(gray, false, "When this option is on, treat images as grayscale ones"); DEFINE_bool(shuffle, false, "Randomly shuffle the order of images and their labels"); -DEFINE_string(backend, "lmdb", "The backend for storing the result"); +DEFINE_string(backend, "lmdb", + "The backend {lmdb, leveldb} for storing the result"); DEFINE_int32(resize_width, 0, "Width images are resized to"); DEFINE_int32(resize_height, 0, "Height images are resized to"); +DEFINE_bool(check_size, false, + "When this option is on, check that all the datum have the same size"); +DEFINE_bool(encoded, false, + "When this option is on, the encoded image will be save in datum"); +DEFINE_string(encode_type, "", + "Optional: What type should we encode the image as ('png','jpg',...)."); int main(int argc, char** argv) { ::google::InitGoogleLogging(argv[0]); @@ -52,15 +57,19 @@ int main(int argc, char** argv) { " http://www.image-net.org/download-images\n"); gflags::ParseCommandLineFlags(&argc, &argv, true); - if (argc != 4) { + if (argc < 4) { gflags::ShowUsageWithFlagsRestrict(argv[0], "tools/convert_imageset"); return 1; } - bool is_color = !FLAGS_gray; + const bool is_color = !FLAGS_gray; + const bool check_size = FLAGS_check_size; + const bool encoded = FLAGS_encoded; + const string encode_type = FLAGS_encode_type; + std::ifstream infile(argv[2]); - std::vector > lines; - string filename; + std::vector > lines; + std::string filename; int label; while (infile >> filename >> label) { lines.push_back(std::make_pair(filename, label)); @@ -72,124 +81,71 @@ int main(int argc, char** argv) { } LOG(INFO) << "A total of " << lines.size() << " images."; - const string& db_backend = FLAGS_backend; - const char* db_path = argv[3]; + if (encode_type.size() && !encoded) + LOG(INFO) << "encode_type specified, assuming encoded=true."; int resize_height = std::max(0, FLAGS_resize_height); int resize_width = std::max(0, FLAGS_resize_width); - // Open new db - // lmdb - MDB_env *mdb_env; - MDB_dbi mdb_dbi; - MDB_val mdb_key, mdb_data; - MDB_txn *mdb_txn; - // leveldb - leveldb::DB* db; - leveldb::Options options; - options.error_if_exists = true; - options.create_if_missing = true; - options.write_buffer_size = 268435456; - leveldb::WriteBatch* batch = NULL; - - // Open db - if (db_backend == "leveldb") { // leveldb - LOG(INFO) << "Opening leveldb " << db_path; - leveldb::Status status = leveldb::DB::Open( - options, db_path, &db); - CHECK(status.ok()) << "Failed to open leveldb " << db_path - << ". Is it already existing?"; - batch = new leveldb::WriteBatch(); - } else if (db_backend == "lmdb") { // lmdb - LOG(INFO) << "Opening lmdb " << db_path; - CHECK_EQ(mkdir(db_path, 0744), 0) - << "mkdir " << db_path << "failed"; - CHECK_EQ(mdb_env_create(&mdb_env), MDB_SUCCESS) << "mdb_env_create failed"; - CHECK_EQ(mdb_env_set_mapsize(mdb_env, 1099511627776), MDB_SUCCESS) // 1TB - << "mdb_env_set_mapsize failed"; - CHECK_EQ(mdb_env_open(mdb_env, db_path, 0, 0664), MDB_SUCCESS) - << "mdb_env_open failed"; - CHECK_EQ(mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn), MDB_SUCCESS) - << "mdb_txn_begin failed"; - CHECK_EQ(mdb_open(mdb_txn, NULL, 0, &mdb_dbi), MDB_SUCCESS) - << "mdb_open failed. Does the lmdb already exist?"; - } else { - LOG(FATAL) << "Unknown db backend " << db_backend; - } + // Create new DB + scoped_ptr db(db::GetDB(FLAGS_backend)); + db->Open(argv[3], db::NEW); + scoped_ptr txn(db->NewTransaction()); // Storing to db - string root_folder(argv[1]); + std::string root_folder(argv[1]); Datum datum; int count = 0; const int kMaxKeyLength = 256; char key_cstr[kMaxKeyLength]; - int data_size; + int data_size = 0; bool data_size_initialized = false; for (int line_id = 0; line_id < lines.size(); ++line_id) { - if (!ReadImageToDatum(root_folder + lines[line_id].first, - lines[line_id].second, resize_height, resize_width, is_color, &datum)) { - continue; + bool status; + std::string enc = encode_type; + if (encoded && !enc.size()) { + // Guess the encoding type from the file name + string fn = lines[line_id].first; + size_t p = fn.rfind('.'); + if ( p == fn.npos ) + LOG(WARNING) << "Failed to guess the encoding of '" << fn << "'"; + enc = fn.substr(p); + std::transform(enc.begin(), enc.end(), enc.begin(), ::tolower); } - if (!data_size_initialized) { - data_size = datum.channels() * datum.height() * datum.width(); - data_size_initialized = true; - } else { - const string& data = datum.data(); - CHECK_EQ(data.size(), data_size) << "Incorrect data field size " - << data.size(); + status = ReadImageToDatum(root_folder + lines[line_id].first, + lines[line_id].second, resize_height, resize_width, is_color, + enc, &datum); + if (status == false) continue; + if (check_size) { + if (!data_size_initialized) { + data_size = datum.channels() * datum.height() * datum.width(); + data_size_initialized = true; + } else { + const std::string& data = datum.data(); + CHECK_EQ(data.size(), data_size) << "Incorrect data field size " + << data.size(); + } } // sequential - snprintf(key_cstr, kMaxKeyLength, "%08d_%s", line_id, + int length = snprintf(key_cstr, kMaxKeyLength, "%08d_%s", line_id, lines[line_id].first.c_str()); - string value; - datum.SerializeToString(&value); - string keystr(key_cstr); // Put in db - if (db_backend == "leveldb") { // leveldb - batch->Put(keystr, value); - } else if (db_backend == "lmdb") { // lmdb - mdb_data.mv_size = value.size(); - mdb_data.mv_data = reinterpret_cast(&value[0]); - mdb_key.mv_size = keystr.size(); - mdb_key.mv_data = reinterpret_cast(&keystr[0]); - CHECK_EQ(mdb_put(mdb_txn, mdb_dbi, &mdb_key, &mdb_data, 0), MDB_SUCCESS) - << "mdb_put failed"; - } else { - LOG(FATAL) << "Unknown db backend " << db_backend; - } + string out; + CHECK(datum.SerializeToString(&out)); + txn->Put(string(key_cstr, length), out); if (++count % 1000 == 0) { - // Commit txn - if (db_backend == "leveldb") { // leveldb - db->Write(leveldb::WriteOptions(), batch); - delete batch; - batch = new leveldb::WriteBatch(); - } else if (db_backend == "lmdb") { // lmdb - CHECK_EQ(mdb_txn_commit(mdb_txn), MDB_SUCCESS) - << "mdb_txn_commit failed"; - CHECK_EQ(mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn), MDB_SUCCESS) - << "mdb_txn_begin failed"; - } else { - LOG(FATAL) << "Unknown db backend " << db_backend; - } + // Commit db + txn->Commit(); + txn.reset(db->NewTransaction()); LOG(ERROR) << "Processed " << count << " files."; } } // write the last batch if (count % 1000 != 0) { - if (db_backend == "leveldb") { // leveldb - db->Write(leveldb::WriteOptions(), batch); - delete batch; - delete db; - } else if (db_backend == "lmdb") { // lmdb - CHECK_EQ(mdb_txn_commit(mdb_txn), MDB_SUCCESS) << "mdb_txn_commit failed"; - mdb_close(mdb_env, mdb_dbi); - mdb_env_close(mdb_env); - } else { - LOG(FATAL) << "Unknown db backend " << db_backend; - } + txn->Commit(); LOG(ERROR) << "Processed " << count << " files."; } return 0; diff --git a/tools/dump_network.cpp b/tools/dump_network.cpp deleted file mode 100644 index 90895fdc969..00000000000 --- a/tools/dump_network.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// This program takes in a trained network and an input blob, and then dumps -// all the intermediate blobs produced by the net to individual binary -// files stored in protobuffer binary formats. -// Usage: -// dump_network input_net_param trained_net_param -// input_blob output_prefix 0/1 -// if input_net_param is 'none', we will directly load the network from -// trained_net_param. If the last argv is 1, we will do a forward-backward pass -// before dumping everyting, and also dump the who network. - -#include -#include - -#include "fcntl.h" -#include "google/protobuf/text_format.h" - -#include "caffe/blob.hpp" -#include "caffe/common.hpp" -#include "caffe/filler.hpp" -#include "caffe/net.hpp" -#include "caffe/proto/caffe.pb.h" -#include "caffe/solver.hpp" -#include "caffe/util/io.hpp" - -using namespace caffe; // NOLINT(build/namespaces) - -int main(int argc, char** argv) { - Caffe::set_mode(Caffe::GPU); - Caffe::set_phase(Caffe::TEST); - - shared_ptr > caffe_net; - if (strcmp(argv[1], "none") == 0) { - // We directly load the net param from trained file - caffe_net.reset(new Net(argv[2])); - } else { - caffe_net.reset(new Net(argv[1])); - } - caffe_net->CopyTrainedLayersFrom(argv[2]); - - vector* > input_vec; - shared_ptr > input_blob(new Blob()); - if (strcmp(argv[3], "none") != 0) { - BlobProto input_blob_proto; - ReadProtoFromBinaryFile(argv[3], &input_blob_proto); - input_blob->FromProto(input_blob_proto); - input_vec.push_back(input_blob.get()); - } - - string output_prefix(argv[4]); - // Run the network without training. - LOG(ERROR) << "Performing Forward"; - caffe_net->Forward(input_vec); - if (argc > 5 && strcmp(argv[5], "1") == 0) { - LOG(ERROR) << "Performing Backward"; - Caffe::set_phase(Caffe::TRAIN); - caffe_net->Backward(); - // Dump the network - NetParameter output_net_param; - caffe_net->ToProto(&output_net_param, true); - WriteProtoToBinaryFile(output_net_param, - output_prefix + output_net_param.name()); - } - // Now, let's dump all the layers - - const vector& blob_names = caffe_net->blob_names(); - const vector > >& blobs = caffe_net->blobs(); - for (int blobid = 0; blobid < caffe_net->blobs().size(); ++blobid) { - // Serialize blob - LOG(ERROR) << "Dumping " << blob_names[blobid]; - BlobProto output_blob_proto; - blobs[blobid]->ToProto(&output_blob_proto); - WriteProtoToBinaryFile(output_blob_proto, - output_prefix + blob_names[blobid]); - } - - return 0; -} diff --git a/tools/extra/extract_seconds.py b/tools/extra/extract_seconds.py index f791afa32a2..591a51f96bd 100755 --- a/tools/extra/extract_seconds.py +++ b/tools/extra/extract_seconds.py @@ -18,18 +18,39 @@ def extract_datetime_from_line(line, year): dt = datetime.datetime(year, month, day, hour, minute, second, microsecond) return dt + +def get_log_created_year(input_file): + """Get year from log file system timestamp + """ + + log_created_time = os.path.getctime(input_file) + log_created_year = datetime.datetime.fromtimestamp(log_created_time).year + return log_created_year + + +def get_start_time(line_iterable, year): + """Find start time from group of lines + """ + + start_datetime = None + for line in line_iterable: + line = line.strip() + if line.find('Solving') != -1: + start_datetime = extract_datetime_from_line(line, year) + break + return start_datetime + + def extract_seconds(input_file, output_file): with open(input_file, 'r') as f: lines = f.readlines() - log_created_time = os.path.getctime(input_file) - log_created_year = datetime.datetime.fromtimestamp(log_created_time).year - start_time_found = False + log_created_year = get_log_created_year(input_file) + start_datetime = get_start_time(lines, log_created_year) + assert start_datetime, 'Start time not found' + out = open(output_file, 'w') for line in lines: line = line.strip() - if not start_time_found and line.find('Solving') != -1: - start_time_found = True - start_datetime = extract_datetime_from_line(line, log_created_year) if line.find('Iteration') != -1: dt = extract_datetime_from_line(line, log_created_year) elapsed_seconds = (dt - start_datetime).total_seconds() diff --git a/tools/extra/parse_log.py b/tools/extra/parse_log.py new file mode 100755 index 00000000000..16ba077aee6 --- /dev/null +++ b/tools/extra/parse_log.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python + +""" +Parse training log + +Competitor to parse_log.sh +""" + +import os +import re +import extract_seconds +import argparse +import csv + + +def get_line_type(line): + """Return either 'test' or 'train' depending on line type + """ + + line_type = None + if line.find('Train') != -1: + line_type = 'train' + elif line.find('Test') != -1: + line_type = 'test' + return line_type + + +def parse_log(path_to_log): + """Parse log file + Returns (train_dict_list, train_dict_names, test_dict_list, test_dict_names) + + train_dict_list and test_dict_list are lists of dicts that define the table + rows + + train_dict_names and test_dict_names are ordered tuples of the column names + for the two dict_lists + """ + + re_iteration = re.compile('Iteration (\d+)') + re_accuracy = re.compile('output #\d+: accuracy = ([\.\d]+)') + re_train_loss = re.compile('Iteration \d+, loss = ([\.\d]+)') + re_output_loss = re.compile('output #\d+: loss = ([\.\d]+)') + re_lr = re.compile('lr = ([\.\d]+)') + + # Pick out lines of interest + iteration = -1 + test_accuracy = -1 + learning_rate = float('NaN') + train_dict_list = [] + test_dict_list = [] + train_dict_names = ('NumIters', 'Seconds', 'TrainingLoss', 'LearningRate') + test_dict_names = ('NumIters', 'Seconds', 'TestAccuracy', 'TestLoss') + + logfile_year = extract_seconds.get_log_created_year(path_to_log) + with open(path_to_log) as f: + start_time = extract_seconds.get_start_time(f, logfile_year) + + for line in f: + iteration_match = re_iteration.search(line) + if iteration_match: + iteration = float(iteration_match.group(1)) + if iteration == -1: + # Only look for other stuff if we've found the first iteration + continue + + time = extract_seconds.extract_datetime_from_line(line, + logfile_year) + seconds = (time - start_time).total_seconds() + + lr_match = re_lr.search(line) + if lr_match: + learning_rate = float(lr_match.group(1)) + + accuracy_match = re_accuracy.search(line) + if accuracy_match and get_line_type(line) == 'test': + test_accuracy = float(accuracy_match.group(1)) + + train_loss_match = re_train_loss.search(line) + if train_loss_match: + train_loss = float(train_loss_match.group(1)) + train_dict_list.append({'NumIters': iteration, + 'Seconds': seconds, + 'TrainingLoss': train_loss, + 'LearningRate': learning_rate}) + + output_loss_match = re_output_loss.search(line) + if output_loss_match and get_line_type(line) == 'test': + test_loss = float(output_loss_match.group(1)) + # NOTE: we assume that (1) accuracy always comes right before + # loss for test data so the test_accuracy variable is already + # correctly populated and (2) there's one and only one output + # named "accuracy" for the test net + test_dict_list.append({'NumIters': iteration, + 'Seconds': seconds, + 'TestAccuracy': test_accuracy, + 'TestLoss': test_loss}) + + return train_dict_list, train_dict_names, test_dict_list, test_dict_names + + +def save_csv_files(logfile_path, output_dir, train_dict_list, train_dict_names, + test_dict_list, test_dict_names, verbose=False): + """Save CSV files to output_dir + + If the input log file is, e.g., caffe.INFO, the names will be + caffe.INFO.train and caffe.INFO.test + """ + + log_basename = os.path.basename(logfile_path) + train_filename = os.path.join(output_dir, log_basename + '.train') + write_csv(train_filename, train_dict_list, train_dict_names, verbose) + + test_filename = os.path.join(output_dir, log_basename + '.test') + write_csv(test_filename, test_dict_list, test_dict_names, verbose) + + +def write_csv(output_filename, dict_list, header_names, verbose=False): + """Write a CSV file + """ + + with open(output_filename, 'w') as f: + dict_writer = csv.DictWriter(f, header_names) + dict_writer.writeheader() + dict_writer.writerows(dict_list) + if verbose: + print 'Wrote %s' % output_filename + + +def parse_args(): + description = ('Parse a Caffe training log into two CSV files ' + 'containing training and testing information') + parser = argparse.ArgumentParser(description=description) + + parser.add_argument('logfile_path', + help='Path to log file') + + parser.add_argument('output_dir', + help='Directory in which to place output CSV files') + + parser.add_argument('--verbose', + action='store_true', + help='Print some extra info (e.g., output filenames)') + + args = parser.parse_args() + return args + + +def main(): + args = parse_args() + train_dict_list, train_dict_names, test_dict_list, test_dict_names = \ + parse_log(args.logfile_path) + save_csv_files(args.logfile_path, args.output_dir, train_dict_list, + train_dict_names, test_dict_list, test_dict_names) + + +if __name__ == '__main__': + main() diff --git a/tools/extract_features.cpp b/tools/extract_features.cpp index cb48c4796b9..f86ff96ca82 100644 --- a/tools/extract_features.cpp +++ b/tools/extract_features.cpp @@ -4,17 +4,22 @@ #include "boost/algorithm/string.hpp" #include "google/protobuf/text_format.h" -#include "leveldb/db.h" -#include "leveldb/write_batch.h" #include "caffe/blob.hpp" #include "caffe/common.hpp" #include "caffe/net.hpp" #include "caffe/proto/caffe.pb.h" +#include "caffe/util/db.hpp" #include "caffe/util/io.hpp" #include "caffe/vision_layers.hpp" -using namespace caffe; // NOLINT(build/namespaces) +using caffe::Blob; +using caffe::Caffe; +using caffe::Datum; +using caffe::Net; +using boost::shared_ptr; +using std::string; +namespace db = caffe::db; template int feature_extraction_pipeline(int argc, char** argv); @@ -27,19 +32,19 @@ int main(int argc, char** argv) { template int feature_extraction_pipeline(int argc, char** argv) { ::google::InitGoogleLogging(argv[0]); - const int num_required_args = 6; + const int num_required_args = 7; if (argc < num_required_args) { LOG(ERROR)<< "This program takes in a trained network and an input data layer, and then" " extract features of the input data produced by the net.\n" "Usage: extract_features pretrained_net_param" " feature_extraction_proto_file extract_feature_blob_name1[,name2,...]" - " save_feature_leveldb_name1[,name2,...] num_mini_batches [CPU/GPU]" - " [DEVICE_ID=0]\n" + " save_feature_dataset_name1[,name2,...] num_mini_batches db_type" + " [CPU/GPU] [DEVICE_ID=0]\n" "Note: you can extract multiple features in one pass by specifying" - " multiple feature blob names and leveldb names seperated by ','." + " multiple feature blob names and dataset names seperated by ','." " The names cannot contain white space characters and the number of blobs" - " and leveldbs must be equal."; + " and datasets must be equal."; return 1; } int arg_pos = num_required_args; @@ -59,10 +64,9 @@ int feature_extraction_pipeline(int argc, char** argv) { LOG(ERROR) << "Using CPU"; Caffe::set_mode(Caffe::CPU); } - Caffe::set_phase(Caffe::TEST); arg_pos = 0; // the name of the executable - string pretrained_binary_proto(argv[++arg_pos]); + std::string pretrained_binary_proto(argv[++arg_pos]); // Expected prototxt contains at least one data layer such as // the layer data_layer_name and one feature blob such as the @@ -91,21 +95,21 @@ int feature_extraction_pipeline(int argc, char** argv) { top: "fc7" } */ - string feature_extraction_proto(argv[++arg_pos]); + std::string feature_extraction_proto(argv[++arg_pos]); shared_ptr > feature_extraction_net( - new Net(feature_extraction_proto)); + new Net(feature_extraction_proto, caffe::TEST)); feature_extraction_net->CopyTrainedLayersFrom(pretrained_binary_proto); - string extract_feature_blob_names(argv[++arg_pos]); - vector blob_names; + std::string extract_feature_blob_names(argv[++arg_pos]); + std::vector blob_names; boost::split(blob_names, extract_feature_blob_names, boost::is_any_of(",")); - string save_feature_leveldb_names(argv[++arg_pos]); - vector leveldb_names; - boost::split(leveldb_names, save_feature_leveldb_names, + std::string save_feature_dataset_names(argv[++arg_pos]); + std::vector dataset_names; + boost::split(dataset_names, save_feature_dataset_names, boost::is_any_of(",")); - CHECK_EQ(blob_names.size(), leveldb_names.size()) << - " the number of blob names and leveldb names must be equal"; + CHECK_EQ(blob_names.size(), dataset_names.size()) << + " the number of blob names and dataset names must be equal"; size_t num_features = blob_names.size(); for (size_t i = 0; i < num_features; i++) { @@ -114,33 +118,26 @@ int feature_extraction_pipeline(int argc, char** argv) { << " in the network " << feature_extraction_proto; } - leveldb::Options options; - options.error_if_exists = true; - options.create_if_missing = true; - options.write_buffer_size = 268435456; - vector > feature_dbs; + int num_mini_batches = atoi(argv[++arg_pos]); + + std::vector > feature_dbs; + std::vector > txns; for (size_t i = 0; i < num_features; ++i) { - LOG(INFO)<< "Opening leveldb " << leveldb_names[i]; - leveldb::DB* db; - leveldb::Status status = leveldb::DB::Open(options, - leveldb_names[i].c_str(), - &db); - CHECK(status.ok()) << "Failed to open leveldb " << leveldb_names[i]; - feature_dbs.push_back(shared_ptr(db)); + LOG(INFO)<< "Opening dataset " << dataset_names[i]; + shared_ptr db(db::GetDB(argv[++arg_pos])); + db->Open(dataset_names.at(i), db::NEW); + feature_dbs.push_back(db); + shared_ptr txn(db->NewTransaction()); + txns.push_back(txn); } - int num_mini_batches = atoi(argv[++arg_pos]); - - LOG(ERROR)<< "Extracting Features"; + LOG(ERROR)<< "Extacting Features"; Datum datum; - vector > feature_batches( - num_features, - shared_ptr(new leveldb::WriteBatch())); const int kMaxKeyStrLength = 100; char key_str[kMaxKeyStrLength]; - vector*> input_vec; - vector image_indices(num_features, 0); + std::vector*> input_vec; + std::vector image_indices(num_features, 0); for (int batch_index = 0; batch_index < num_mini_batches; ++batch_index) { feature_extraction_net->Forward(input_vec); for (int i = 0; i < num_features; ++i) { @@ -148,29 +145,29 @@ int feature_extraction_pipeline(int argc, char** argv) { ->blob_by_name(blob_names[i]); int batch_size = feature_blob->num(); int dim_features = feature_blob->count() / batch_size; - Dtype* feature_blob_data; + const Dtype* feature_blob_data; for (int n = 0; n < batch_size; ++n) { datum.set_height(dim_features); datum.set_width(1); datum.set_channels(1); datum.clear_data(); datum.clear_float_data(); - feature_blob_data = feature_blob->mutable_cpu_data() + + feature_blob_data = feature_blob->cpu_data() + feature_blob->offset(n); for (int d = 0; d < dim_features; ++d) { datum.add_float_data(feature_blob_data[d]); } - string value; - datum.SerializeToString(&value); - snprintf(key_str, kMaxKeyStrLength, "%d", image_indices[i]); - feature_batches[i]->Put(string(key_str), value); + int length = snprintf(key_str, kMaxKeyStrLength, "%d", + image_indices[i]); + string out; + CHECK(datum.SerializeToString(&out)); + txns.at(i)->Put(std::string(key_str, length), out); ++image_indices[i]; if (image_indices[i] % 1000 == 0) { - feature_dbs[i]->Write(leveldb::WriteOptions(), - feature_batches[i].get()); + txns.at(i)->Commit(); + txns.at(i).reset(feature_dbs.at(i)->NewTransaction()); LOG(ERROR)<< "Extracted features of " << image_indices[i] << " query images for feature blob " << blob_names[i]; - feature_batches[i].reset(new leveldb::WriteBatch()); } } // for (int n = 0; n < batch_size; ++n) } // for (int i = 0; i < num_features; ++i) @@ -178,10 +175,11 @@ int feature_extraction_pipeline(int argc, char** argv) { // write the last batch for (int i = 0; i < num_features; ++i) { if (image_indices[i] % 1000 != 0) { - feature_dbs[i]->Write(leveldb::WriteOptions(), feature_batches[i].get()); + txns.at(i)->Commit(); } LOG(ERROR)<< "Extracted features of " << image_indices[i] << " query images for feature blob " << blob_names[i]; + feature_dbs.at(i)->Close(); } LOG(ERROR)<< "Successfully extracted the features!"; diff --git a/tools/upgrade_net_proto_binary.cpp b/tools/upgrade_net_proto_binary.cpp index d7a62e32441..8a0dd7af743 100644 --- a/tools/upgrade_net_proto_binary.cpp +++ b/tools/upgrade_net_proto_binary.cpp @@ -5,6 +5,7 @@ #include #include // NOLINT(readability/streams) #include // NOLINT(readability/streams) +#include #include "caffe/caffe.hpp" #include "caffe/util/io.hpp" @@ -23,16 +24,20 @@ int main(int argc, char** argv) { } NetParameter net_param; - if (!ReadProtoFromBinaryFile(argv[1], &net_param)) { + string input_filename(argv[1]); + if (!ReadProtoFromBinaryFile(input_filename, &net_param)) { LOG(ERROR) << "Failed to parse input binary file as NetParameter: " - << argv[1]; + << input_filename; return 2; } bool need_upgrade = NetNeedsUpgrade(net_param); bool success = true; if (need_upgrade) { - NetParameter v0_net_param(net_param); - success = UpgradeV0Net(v0_net_param, &net_param); + success = UpgradeNetAsNeeded(input_filename, &net_param); + if (!success) { + LOG(ERROR) << "Encountered error(s) while upgrading prototxt; " + << "see details above."; + } } else { LOG(ERROR) << "File already in V1 proto format: " << argv[1]; } diff --git a/tools/upgrade_net_proto_text.cpp b/tools/upgrade_net_proto_text.cpp index 2f290fc5e14..9200431bc27 100644 --- a/tools/upgrade_net_proto_text.cpp +++ b/tools/upgrade_net_proto_text.cpp @@ -5,6 +5,7 @@ #include #include // NOLINT(readability/streams) #include // NOLINT(readability/streams) +#include #include "caffe/caffe.hpp" #include "caffe/util/io.hpp" @@ -23,32 +24,31 @@ int main(int argc, char** argv) { } NetParameter net_param; - if (!ReadProtoFromTextFile(argv[1], &net_param)) { + string input_filename(argv[1]); + if (!ReadProtoFromTextFile(input_filename, &net_param)) { LOG(ERROR) << "Failed to parse input text file as NetParameter: " - << argv[1]; + << input_filename; return 2; } bool need_upgrade = NetNeedsUpgrade(net_param); bool need_data_upgrade = NetNeedsDataUpgrade(net_param); bool success = true; if (need_upgrade) { - NetParameter v0_net_param(net_param); - success = UpgradeV0Net(v0_net_param, &net_param); + success = UpgradeNetAsNeeded(input_filename, &net_param); + if (!success) { + LOG(ERROR) << "Encountered error(s) while upgrading prototxt; " + << "see details above."; + } } else { - LOG(ERROR) << "File already in V1 proto format: " << argv[1]; + LOG(ERROR) << "File already in latest proto format: " << input_filename; } if (need_data_upgrade) { UpgradeNetDataTransformation(&net_param); } - // Convert to a NetParameterPrettyPrint to print fields in desired - // order. - NetParameterPrettyPrint net_param_pretty; - NetParameterToPrettyPrint(net_param, &net_param_pretty); - // Save new format prototxt. - WriteProtoToTextFile(net_param_pretty, argv[2]); + WriteProtoToTextFile(net_param, argv[2]); LOG(ERROR) << "Wrote upgraded NetParameter text proto to " << argv[2]; return !success;