Skip to content

Commit

Permalink
[WIP] add SVM regressor and kernel coreml exporters
Browse files Browse the repository at this point in the history
  • Loading branch information
vigsterkr committed Jan 30, 2019
1 parent 82b5f8e commit 68c0ee2
Show file tree
Hide file tree
Showing 15 changed files with 789 additions and 44 deletions.
6 changes: 6 additions & 0 deletions .ci/ci.yml
Expand Up @@ -104,6 +104,12 @@ jobs:
CXX: "clang++"
cmakeOptions: '$(commonSWIGCMakeFlags) -DINTERFACE_RUBY=ON'
interfaceName: 'ruby'
ruby:
CC: "clang"
CXX: "clang++"
cmakeOptions: '$(commonSWIGCMakeFlags) -DINTERFACE_COREML=ON'
interfaceName: 'coreml'


variables:
testRunTitle: '$(build.sourceBranchName)-debian'
Expand Down
36 changes: 36 additions & 0 deletions cmake/ShogunUtils.cmake
Expand Up @@ -315,3 +315,39 @@ function(ADD_SHOGUN_BENCHMARK REL_BENCHMARK_NAME)
set_tests_properties(${BENCHMARK_NAME} PROPERTIES ${ARGN})
endif()
endfunction()

function(ADD_SHOGUN_UNITTEST)
set(options)
set(oneValueArgs TARGET)
set(multiValueArgs LABELS)
cmake_parse_arguments(ADD_SHOGUN_UNITTEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if(NOT ENABLE_TESTING)
return()
endif()
get_filename_component(UNITTEST_NAME ${ADD_SHOGUN_UNITTEST_TARGET} NAME_WE)

if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${ADD_SHOGUN_UNITTEST_TARGET}.cc)
# This benchmark has a corresponding .cc file, set it up as an executable.
add_executable(${UNITTEST_NAME} "${ADD_SHOGUN_UNITTEST_TARGET}.cc")
set_target_properties (${UNITTEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set_target_properties (${UNITTEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin)
set_target_properties (${UNITTEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)
target_link_libraries(${UNITTEST_NAME} ${SHOGUN_UNITTEST_LINK_LIBS})
add_dependencies(${UNITTEST_NAME} GoogleMock shogun::shogun)
target_include_directories(${UNITTEST_NAME} PRIVATE ${source_dir}/googlemock/include ${source_dir}/googletest/include)
set(NO_COLOR "--color_print=false")
endif()

add_test(${UNITTEST_NAME} ${CMAKE_BINARY_DIR}/bin/${UNITTEST_NAME} ${NO_COLOR})
if (ADD_SHOGUN_UNITTEST_LABELS)
set_tests_properties(${UNITTEST_NAME} PROPERTIES LABELS ${ADD_SHOGUN_UNITTEST_LABELS})
else ()
set_tests_properties(${UNITTEST_NAME} PROPERTIES LABELS "unit")
endif()

if(ARGN)
set_tests_properties(${UNITTEST_NAME} PROPERTIES ${ARGN})
endif()
endfunction()

84 changes: 43 additions & 41 deletions cmake/external/GoogleTestNMock.cmake
@@ -1,44 +1,46 @@
MergeCFLAGS()
include(ExternalProject)
IF (NOT TARGET GoogleMock)
MergeCFLAGS()
include(ExternalProject)

IF (MSVC)
SET (CUSTOM_CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${THIRD_PARTY_DIR}/libs/gmock
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${THIRD_PARTY_DIR}/libs/gmock
-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}${CMAKE_DEFINITIONS}
-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}
-DCMAKE_CXX_FLAGS_DISTRIBUTION:STRING=${CMAKE_CXX_FLAGS_DISTRIBUTION}
-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG}
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
)
ELSE ()
SET(MERGED_CXX_FLAGS "${MERGED_CXX_FLAGS} -fPIC")
SET (CUSTOM_CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${THIRD_PARTY_DIR}/libs/gmock
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${THIRD_PARTY_DIR}/libs/gmock
-DCMAKE_CXX_FLAGS:STRING=${MERGED_CXX_FLAGS}${CMAKE_DEFINITIONS}
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
)
ENDIF()
IF (MSVC)
SET (CUSTOM_CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${THIRD_PARTY_DIR}/libs/gmock
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${THIRD_PARTY_DIR}/libs/gmock
-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}${CMAKE_DEFINITIONS}
-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}
-DCMAKE_CXX_FLAGS_DISTRIBUTION:STRING=${CMAKE_CXX_FLAGS_DISTRIBUTION}
-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG}
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
)
ELSE ()
SET(MERGED_CXX_FLAGS "${MERGED_CXX_FLAGS} -fPIC")
SET (CUSTOM_CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${THIRD_PARTY_DIR}/libs/gmock
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${THIRD_PARTY_DIR}/libs/gmock
-DCMAKE_CXX_FLAGS:STRING=${MERGED_CXX_FLAGS}${CMAKE_DEFINITIONS}
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
)
ENDIF()

IF(EXISTS /usr/src/googletest)
ExternalProject_Add(
GoogleMock
DOWNLOAD_COMMAND ""
SOURCE_DIR /usr/src/googletest
PREFIX ${CMAKE_BINARY_DIR}/GoogleMock
INSTALL_COMMAND ""
CMAKE_ARGS ${CUSTOM_CMAKE_ARGS}
)
ELSE()
ExternalProject_Add(
GoogleMock
URL https://github.com/google/googletest/archive/release-1.8.1.tar.gz
URL_MD5 2e6fbeb6a91310a16efe181886c59596
TIMEOUT 10
PREFIX ${CMAKE_BINARY_DIR}/GoogleMock
DOWNLOAD_DIR ${THIRD_PARTY_DIR}/GoogleMock
INSTALL_COMMAND ""
CMAKE_ARGS ${CUSTOM_CMAKE_ARGS}
)
IF(EXISTS /usr/src/googletest)
ExternalProject_Add(
GoogleMock
DOWNLOAD_COMMAND ""
SOURCE_DIR /usr/src/googletest
PREFIX ${CMAKE_BINARY_DIR}/GoogleMock
INSTALL_COMMAND ""
CMAKE_ARGS ${CUSTOM_CMAKE_ARGS}
)
ELSE()
ExternalProject_Add(
GoogleMock
URL https://github.com/google/googletest/archive/release-1.8.1.tar.gz
URL_MD5 2e6fbeb6a91310a16efe181886c59596
TIMEOUT 10
PREFIX ${CMAKE_BINARY_DIR}/GoogleMock
DOWNLOAD_DIR ${THIRD_PARTY_DIR}/GoogleMock
INSTALL_COMMAND ""
CMAKE_ARGS ${CUSTOM_CMAKE_ARGS}
)
ENDIF()
ENDIF()
33 changes: 30 additions & 3 deletions src/interfaces/coreml/CMakeLists.txt
@@ -1,11 +1,17 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_library(shogun-coreml SHARED CoreMLModel.cc CoreMLConverter.cc KernelConverter.cc SVMConverter.cc)
set_property(TARGET shogun-coreml PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(shogun-coreml shogun::shogun)

IF (PROTOBUF_FOUND AND ENABLE_PROTOBUF)
FILE(GLOB COREML_SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.proto")
FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
SET(COREML_SOURCES)
FOREACH (SRC_FILE ${COREML_SRC})
GET_FILENAME_COMPONENT(SRC_FILE_WE ${SRC_FILE} NAME_WE)
SET(COMPILED_SRC "${CMAKE_CURRENT_BINARY_DIR}/coreml/${SRC_FILE_WE}")
SET(COMPILED_SRC "${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE_WE}")
SET(COMPILED_SRC_H "${COMPILED_SRC}.pb.h")
SET(COMPILED_SRC_CPP "${COMPILED_SRC}.pb.cpp")
SET(COMPILED_SRC_CPP "${COMPILED_SRC}.pb.cc")
ADD_CUSTOM_COMMAND(
OUTPUT "${COMPILED_SRC_H}" "${COMPILED_SRC_CPP}"
COMMAND "${PROTOBUF_PROTOC_EXECUTABLE}"
Expand All @@ -16,8 +22,29 @@ IF (PROTOBUF_FOUND AND ENABLE_PROTOBUF)
)
SET(PROTO_TARGET_NAME "CoreML_${SRC_FILE_WE}")
ADD_CUSTOM_TARGET("${PROTO_TARGET_NAME}" DEPENDS "${COMPILED_SRC_H}" "${COMPILED_SRC_CPP}")
ADD_DEPENDENCIES(libshogun "${PROTO_TARGET_NAME}")
LIST(APPEND COREML_SOURCES ${COMPILED_SRC_CPP})
ENDFOREACH()
add_library(coreml STATIC ${COREML_SOURCES})
target_link_libraries(shogun-coreml coreml ${Protobuf_LITE_LIBRARIES})
ELSE()
MESSAGE(FATAL_ERROR "Protobuf is required for CoreML")
ENDIF()

IF (ENABLE_TESTING)
include(external/GoogleTestNMock)
ExternalProject_Get_Property(GoogleMock source_dir)
LINK_DIRECTORIES(${THIRD_PARTY_DIR}/libs/gmock)

enable_testing()
add_library(coreml_unittest_main ${CMAKE_CURRENT_SOURCE_DIR}/unittest_main.cc)
target_link_libraries(coreml_unittest_main gmock gtest shogun::shogun)
add_dependencies(coreml_unittest_main GoogleMock shogun::shogun)
target_include_directories(coreml_unittest_main
PRIVATE
${source_dir}/googlemock/include
${source_dir}/googletest/include)
set(SHOGUN_UNITTEST_LINK_LIBS coreml_unittest_main shogun::shogun shogun-coreml gmock gtest)

ADD_SHOGUN_UNITTEST(TARGET SVMConverter_test LABELS coreml-unit)
ADD_SHOGUN_UNITTEST(TARGET KernelConverter_test LABELS coreml-unit)
ENDIF()
27 changes: 27 additions & 0 deletions src/interfaces/coreml/CoreMLConverter.cc
@@ -0,0 +1,27 @@
#include "CoreMLConverter.h"
#include "SVMConverter.h"

#include <shogun/lib/exception/NotFittedException.h>


using namespace shogun;
using namespace shogun::coreml;

std::shared_ptr<CoreMLModel> shogun::coreml::convert(const CMachine* m)
{
/*
auto converter_registry = ConverterFactory::instance();
std::string machine_name(m->get_name());
if (!m->is_trained())
throw NotFittedException("The supplied machine is not trained!");
auto model = std::make_shared<CoreMLModel>();
auto spec = model->get_specification();
//(*converter_registry)(machine_name)(m, spec);
return model;
*/
return nullptr;
}


93 changes: 93 additions & 0 deletions src/interfaces/coreml/CoreMLConverter.h
@@ -0,0 +1,93 @@
#ifndef __COREML_CONVERTER_H__
#define __COREML_CONVERTER_H__

#include <functional>
#include <unordered_map>
#include <unordered_set>

#include <shogun/machine/Machine.h>

#include "CoreMLModel.h"

namespace shogun
{
namespace coreml
{
static constexpr int32_t SPECIFICATION_VERSION = 1;

struct visitor;

struct ICoreMLConverter {
virtual void convert(visitor& v, const CSGObject* o) const = 0;
};

template <class, class>
struct CoreMLConverter;

struct visitor {
template<typename T, typename I>
void visit(T*, I*);
};

template <class I, class O>
struct CoreMLConverter: ICoreMLConverter {
typedef I input_type;
typedef O output_type;

std::shared_ptr<CoreML::Specification::Model> m_spec;
CoreMLConverter(std::shared_ptr<CoreML::Specification::Model> spec): m_spec(spec) {}

void convert(visitor& c, const CSGObject* o) const override
{
auto x = o->as<const I>();
c.visit(this, x);
}

static O* convert(const I* o);
static const std::unordered_set<std::string> supported_types;
};
/*
class ConverterFactory
{
typedef std::function<std::shared_ptr<ICoreMLConverter>(std::shared_ptr<CoreML::Specification::Model>)> ConverterFactoryFunction;
public:
bool register_converter(const std::string& machine_name, ConverterFunction f)
{
return m_registry.emplace(std::make_pair(machine_name, f)).second;
}
ConverterFunction& operator()(const std::string& m)
{
auto f = m_registry.find(m);
if (f == m_registry.end())
throw std::runtime_error("The provided machine cannot be converted to CoreML format!");
return f->second;
}
static ConverterFactory* instance()
{
static ConverterFactory* f = new ConverterFactory();
return f;
}
private:
std::unordered_map<std::string, ConverterFactoryFunction> m_registry;
};
*/

std::shared_ptr<CoreMLModel> convert(const CMachine* m);

#define REGISTER_COREML_CONVERTER(factory, classname, machines, function) \
static int register_converter##classname = []() { \
for (auto m = machines.cbegin(); m != machines.cend(); ++m) \
factory->register_converter(*m, function); \
return factory->size(); \
}();

#define REGISTER_CONVERTER(classname, machines, function) \
REGISTER_COREML_CONVERTER(ConverterFactory::instance(), classname, machines, function)
}
}

#endif
33 changes: 33 additions & 0 deletions src/interfaces/coreml/CoreMLModel.cc
@@ -0,0 +1,33 @@
#include "CoreMLModel.h"

#include <fstream>

#include <google/protobuf/io/zero_copy_stream_impl.h>

#include "Model.pb.h"

using namespace shogun::coreml;

CoreMLModel::CoreMLModel():
m_spec(std::make_shared<CoreML::Specification::Model>())
{
}

CoreMLModel::~CoreMLModel()
{
m_spec.reset();
}

void CoreMLModel::save(const std::string& filename) const
{
std::fstream out(filename, std::ios::binary | std::ios::out);
this->save(out);
out.close();
}

void CoreMLModel::save(std::ostream& out) const
{
::google::protobuf::io::OstreamOutputStream pb_out(&out);
if (!m_spec->SerializeToZeroCopyStream(&pb_out))
throw std::runtime_error("could not save");
}
39 changes: 39 additions & 0 deletions src/interfaces/coreml/CoreMLModel.h
@@ -0,0 +1,39 @@
#ifndef __COREML_MODEL_H__
#define __COREML_MODEL_H__

#include <memory>
#include <ostream>
#include <string>

namespace CoreML
{
namespace Specification
{
class Model;
}
}

namespace shogun
{
namespace coreml
{
class CoreMLModel
{
public:
CoreMLModel();
~CoreMLModel();

void save(const std::string& filename) const;
void save(std::ostream& out) const;

std::shared_ptr<CoreML::Specification::Model> get_specification() const
{
return m_spec;
}

private:
std::shared_ptr<CoreML::Specification::Model> m_spec;
};
}
}
#endif

0 comments on commit 68c0ee2

Please sign in to comment.