From a04dabc22c86fd5a29ba55813251ffc52639d7b9 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Sun, 4 Oct 2020 16:22:19 +0100 Subject: [PATCH] Extended example to use logistic regression code --- .../app/src/main/cpp/CMakeLists.txt | 7 +- .../app/src/main/cpp/KomputeJniNative.cpp | 43 ++---- .../app/src/main/cpp/KomputeModelML.cpp | 131 ++++++++++++++++++ .../app/src/main/cpp/KomputeModelML.hpp | 85 ++++++++++++ .../main/res/layout/activity_kompute_jni.xml | 8 +- 5 files changed, 234 insertions(+), 40 deletions(-) create mode 100755 examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp create mode 100755 examples/android/android-simple/app/src/main/cpp/KomputeModelML.hpp diff --git a/examples/android/android-simple/app/src/main/cpp/CMakeLists.txt b/examples/android/android-simple/app/src/main/cpp/CMakeLists.txt index a4e9b3ab1..868be9ca7 100644 --- a/examples/android/android-simple/app/src/main/cpp/CMakeLists.txt +++ b/examples/android/android-simple/app/src/main/cpp/CMakeLists.txt @@ -5,7 +5,8 @@ add_subdirectory(../../../../../../../ ${CMAKE_CURRENT_BINARY_DIR}/kompute_build set(VK_ANDROID_INCLUDE_DIR ${ANDROID_NDK}/sources/third_party/vulkan/src/include) add_library(kompute-jni SHARED - KomputeJniNative.cpp) + KomputeJniNative.cpp + KomputeModelML.cpp) include_directories( ${VK_ANDROID_COMMON_DIR} @@ -18,7 +19,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 \ -DKOMPUTE_DISABLE_VK_DEBUG_LAYERS=1") target_link_libraries(kompute-jni + # Libraries from kompute build kompute - log kompute_vk_ndk_wrapper + # Libraries from android build + log android) \ No newline at end of file diff --git a/examples/android/android-simple/app/src/main/cpp/KomputeJniNative.cpp b/examples/android/android-simple/app/src/main/cpp/KomputeJniNative.cpp index d3e6d85cf..25b266229 100644 --- a/examples/android/android-simple/app/src/main/cpp/KomputeJniNative.cpp +++ b/examples/android/android-simple/app/src/main/cpp/KomputeJniNative.cpp @@ -16,10 +16,6 @@ #define RELEASE 1 #include -//#include -//#include -//#include -//#include #include #include @@ -27,19 +23,12 @@ #include "kompute/Kompute.hpp" +#include "KomputeModelML.hpp" + #ifndef KOMPUTE_VK_INIT_RETRIES #define KOMPUTE_VK_INIT_RETRIES 5 #endif -// Android log function wrappers -static const char* kTAG = "KomputeJni"; -#define LOGI(...) \ - ((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__)) -#define LOGW(...) \ - ((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__)) -#define LOGE(...) \ - ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__)) - static std::vector jfloatArrayToVector(JNIEnv *env, const jfloatArray & fromArray) { float *inCArray = env->GetFloatArrayElements(fromArray, NULL); if (NULL == inCArray) return std::vector(); @@ -61,12 +50,12 @@ extern "C" { JNIEXPORT jboolean JNICALL Java_com_ethicalml_kompute_KomputeJni_initVulkan(JNIEnv *env, jobject thiz) { - LOGI("Initialising vulkan"); + SPDLOG_INFO("Initialising vulkan"); uint32_t totalRetries = 0; while (totalRetries < KOMPUTE_VK_INIT_RETRIES) { - LOGI("VULKAN LOAD TRY NUMBER: %u", totalRetries); + SPDLOG_INFO("VULKAN LOAD TRY NUMBER: %u", totalRetries); if(InitVulkan()) { break; } @@ -86,31 +75,17 @@ Java_com_ethicalml_kompute_KomputeJni_kompute( jfloatArray xjJFloatArr, jfloatArray yJFloatArr) { - LOGI("Creating manager"); + SPDLOG_INFO("Creating manager"); std::vector xiVector = jfloatArrayToVector(env, xiJFloatArr); std::vector xjVector = jfloatArrayToVector(env, xjJFloatArr); std::vector yVector = jfloatArrayToVector(env, yJFloatArr); - kp::Manager mgr; + KomputeModelML kml; + kml.train(yVector, xiVector, xjVector); - auto tensorA = mgr.buildTensor(xiVector); - auto tensorB = mgr.buildTensor(xjVector); - auto tensorC = mgr.buildTensor(yVector); + std::vector pred = kml.predict(xiVector, xjVector); - LOGI("Result before:"); - for(const float & i : tensorC->data()) { - LOGI("%f ", i); - } - - mgr.evalOpDefault>({tensorA, tensorB, tensorC}); - mgr.evalOpDefault({tensorC}); - - LOGI("Result after:"); - for(const float & i : tensorC->data()) { - LOGI("%f ", i); - } - - return vectorToJFloatArray(env, tensorC->data()); + return vectorToJFloatArray(env, pred); } } diff --git a/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp new file mode 100755 index 000000000..3202764a2 --- /dev/null +++ b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.cpp @@ -0,0 +1,131 @@ + +#include "KomputeModelML.hpp" + +KomputeModelML::KomputeModelML() { + +} + +KomputeModelML::~KomputeModelML() { + +} + +void KomputeModelML::train(std::vector yData, std::vector xIData, std::vector xJData) { + + std::vector zerosData; + + for (size_t i = 0; i < yData.size(); i++) { + zerosData.push_back(0); + } + + uint32_t ITERATIONS = 100; + float learningRate = 0.1; + + std::shared_ptr xI{ new kp::Tensor(xIData) }; + std::shared_ptr xJ{ new kp::Tensor(xJData) }; + + std::shared_ptr y{ new kp::Tensor(yData) }; + + std::shared_ptr wIn{ new kp::Tensor({ 0.001, 0.001 }) }; + std::shared_ptr wOutI{ new kp::Tensor(zerosData) }; + std::shared_ptr wOutJ{ new kp::Tensor(zerosData) }; + + std::shared_ptr bIn{ new kp::Tensor({ 0 }) }; + std::shared_ptr bOut{ new kp::Tensor(zerosData) }; + + std::shared_ptr lOut{ new kp::Tensor(zerosData) }; + + std::vector> params = { xI, xJ, y, + wIn, wOutI, wOutJ, + bIn, bOut, lOut }; + + { + kp::Manager mgr; + + if (std::shared_ptr sq = + mgr.getOrCreateManagedSequence("createTensors").lock()) { + + sq->begin(); + + sq->record(params); + + sq->end(); + sq->eval(); + + // Record op algo base + sq->begin(); + + sq->record({ wIn, bIn }); + +#ifdef KOMPUTE_ANDROID_SHADER_FROM_STRING + // Newer versions of Android are able to use shaderc to read raw string + sq->record>( + params, std::vector(LR_SHADER.begin(), LR_SHADER.end())); +#else + // Older versions of Android require the SPIRV binary directly + sq->record>( + params, std::vector( + kp::shader_data::shaders_glsl_logisticregression_comp_spv, + kp::shader_data::shaders_glsl_logisticregression_comp_spv + + kp::shader_data::shaders_glsl_logisticregression_comp_spv_len + )); +#endif + + sq->record({ wOutI, wOutJ, bOut, lOut }); + + sq->end(); + + // Iterate across all expected iterations + for (size_t i = 0; i < ITERATIONS; i++) { + + sq->eval(); + + for (size_t j = 0; j < bOut->size(); j++) { + wIn->data()[0] -= learningRate * wOutI->data()[j]; + wIn->data()[1] -= learningRate * wOutJ->data()[j]; + bIn->data()[0] -= learningRate * bOut->data()[j]; + } + } + } + } + + this->mWeights = kp::Tensor(wIn->data()); + this->mBias = kp::Tensor(bIn->data()); +} + +std::vector KomputeModelML::predict(std::vector xI, std::vector xJ) { + assert(xI.size() == xJ.size()); + + std::vector retVector; + + // We run the inference in the CPU for simplicity + // BUt you can also implement the inference on GPU + // GPU implementation would speed up minibatching + for (size_t i = 0; i < xI.size(); i++) { + float xIVal = xI[i]; + float xJVal = xJ[i]; + float result = (xIVal * this->mWeights.data()[0] + + xJVal * this->mWeights.data()[1] + + this->mBias.data()[0]); + + // Instead of using sigmoid we'll just return full numbers + float var = result > 0 ? 1 : 0; + retVector.push_back(var); + } + + return retVector; +} + +std::vector KomputeModelML::get_params() { + std::vector retVector; + + if(this->mWeights.size() + this->mBias.size() == 0) { + return retVector; + } + + retVector.push_back(this->mWeights.data()[0]); + retVector.push_back(this->mWeights.data()[1]); + retVector.push_back(this->mBias.data()[0]); + retVector.push_back(99.0); + + return retVector; +} diff --git a/examples/android/android-simple/app/src/main/cpp/KomputeModelML.hpp b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.hpp new file mode 100755 index 000000000..335f05805 --- /dev/null +++ b/examples/android/android-simple/app/src/main/cpp/KomputeModelML.hpp @@ -0,0 +1,85 @@ + +#ifndef KOMPUTEMODELML_HPP +#define KOMPUTEMODELML_HPP + +#include +#include + +#include "kompute/Kompute.hpp" + +class KomputeModelML { + +public: + KomputeModelML(); + virtual ~KomputeModelML(); + + void train(std::vector yData, std::vector xIData, std::vector xJData); + + std::vector predict(std::vector xI, std::vector xJ); + + std::vector get_params(); + +private: + kp::Tensor mWeights; + kp::Tensor mBias; + +}; + +static std::string LR_SHADER = R"( +#version 450 + +layout (constant_id = 0) const uint M = 0; + +layout (local_size_x = 1) in; + +layout(set = 0, binding = 0) buffer bxi { float xi[]; }; +layout(set = 0, binding = 1) buffer bxj { float xj[]; }; +layout(set = 0, binding = 2) buffer by { float y[]; }; +layout(set = 0, binding = 3) buffer bwin { float win[]; }; +layout(set = 0, binding = 4) buffer bwouti { float wouti[]; }; +layout(set = 0, binding = 5) buffer bwoutj { float woutj[]; }; +layout(set = 0, binding = 6) buffer bbin { float bin[]; }; +layout(set = 0, binding = 7) buffer bbout { float bout[]; }; +layout(set = 0, binding = 8) buffer blout { float lout[]; }; + +float m = float(M); + +float sigmoid(float z) { + return 1.0 / (1.0 + exp(-z)); +} + +float inference(vec2 x, vec2 w, float b) { + // Compute the linear mapping function + float z = dot(w, x) + b; + // Calculate the y-hat with sigmoid + float yHat = sigmoid(z); + return yHat; +} + +float calculateLoss(float yHat, float y) { + return -(y * log(yHat) + (1.0 - y) * log(1.0 - yHat)); +} + +void main() { + uint idx = gl_GlobalInvocationID.x; + + vec2 wCurr = vec2(win[0], win[1]); + float bCurr = bin[0]; + + vec2 xCurr = vec2(xi[idx], xj[idx]); + float yCurr = y[idx]; + + float yHat = inference(xCurr, wCurr, bCurr); + + float dZ = yHat - yCurr; + vec2 dW = (1. / m) * xCurr * dZ; + float dB = (1. / m) * dZ; + wouti[idx] = dW.x; + woutj[idx] = dW.y; + bout[idx] = dB; + + lout[idx] = calculateLoss(yHat, yCurr); +} +)"; + +#endif //ANDROID_SIMPLE_KOMPUTEMODELML_HPP diff --git a/examples/android/android-simple/app/src/main/res/layout/activity_kompute_jni.xml b/examples/android/android-simple/app/src/main/res/layout/activity_kompute_jni.xml index 0cbb0cb97..f7ddae50d 100644 --- a/examples/android/android-simple/app/src/main/res/layout/activity_kompute_jni.xml +++ b/examples/android/android-simple/app/src/main/res/layout/activity_kompute_jni.xml @@ -9,7 +9,7 @@ tools:context="com.ethicalml.kompute.KomputeJni"> + android:text="[ 0, 1, 1, 1, 1, 1 ]" /> + android:text="[ 0, 0, 0, 1, 1, 1 ]" /> @@ -120,7 +120,7 @@ android:layout_weight="1" android:ems="10" android:inputType="textPersonName" - android:text="[ 1, 2, 3 ]" /> + android:text="[ 0, 0, 0, 1, 1, 1 ]" />