diff --git a/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp b/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp index 172cde15d..ff717b1a3 100644 --- a/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp +++ b/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.cpp @@ -5,73 +5,138 @@ #include "KomputeModelMLNode.h" KomputeModelMLNode::KomputeModelMLNode() { + std::cout << "CALLING CONSTRUCTOR" << std::endl; this->_init(); } -void KomputeModelMLNode::add(float value) { - // Set the new data in the local device - this->mSecondaryTensor->setData({value}); - // Execute recorded sequence - if (std::shared_ptr sq = this->mSequence.lock()) { - sq->eval(); +void KomputeModelMLNode::train(Array yArr, Array xIArr, Array xJArr) { + + assert(yArr.size() == xIArr.size()); + assert(xIArr.size() == xJArr.size()); + + std::vector yData; + std::vector xIData; + std::vector xJData; + std::vector zerosData; + + for (size_t i = 0; i < yArr.size(); i++) { + yData.push_back(yArr[i]); + xIData.push_back(xIArr[i]); + xJData.push_back(xJArr[i]); + zerosData.push_back(0); } - else { - throw std::runtime_error("Sequence pointer no longer available"); + + 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 }); + + sq->record>( + params, std::vector(LR_SHADER.begin(), LR_SHADER.end())); + + 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]; + } + } + } } + + SPDLOG_INFO("RESULT: <<<<<<<<<<<<<<<<<<<"); + SPDLOG_INFO(wIn->data()[0]); + SPDLOG_INFO(wIn->data()[1]); + SPDLOG_INFO(bIn->data()[0]); + + this->mWeights = kp::Tensor(wIn->data()); + this->mBias = kp::Tensor(bIn->data()); } -void KomputeModelMLNode::reset() { +Array KomputeModelMLNode::predict(Array xI, Array xJ) { + assert(xI.size() == xJ.size()); + + Array retArray; + + // 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 + Variant var = result > 0 ? 1 : 0; + retArray.push_back(var); + } + + return retArray; } -float KomputeModelMLNode::get_total() const { - return this->mPrimaryTensor->data()[0]; +Array KomputeModelMLNode::get_params() { + Array retArray; + + SPDLOG_INFO(this->mWeights.size() + this->mBias.size()); + + if(this->mWeights.size() + this->mBias.size() == 0) { + return retArray; + } + + retArray.push_back(this->mWeights.data()[0]); + retArray.push_back(this->mWeights.data()[1]); + retArray.push_back(this->mBias.data()[0]); + retArray.push_back(99.0); + + return retArray; } void KomputeModelMLNode::_init() { std::cout << "CALLING INIT" << std::endl; - this->mPrimaryTensor = this->mManager.buildTensor({ 0.0 }); - this->mSecondaryTensor = this->mManager.buildTensor({ 0.0 }); - this->mSequence = this->mManager.getOrCreateManagedSequence("AdditionSeq"); - - // We now record the steps in the sequence - if (std::shared_ptr sq = this->mSequence.lock()) - { - - std::string shader(R"( - #version 450 - - layout (local_size_x = 1) in; - - layout(set = 0, binding = 0) buffer a { float pa[]; }; - layout(set = 0, binding = 1) buffer b { float pb[]; }; - - void main() { - uint index = gl_GlobalInvocationID.x; - pa[index] = pb[index] + pa[index]; - } - )"); - - sq->begin(); - - // First we ensure secondary tensor loads to GPU - // No need to sync the primary tensor as it should not be changed - sq->record( - { this->mSecondaryTensor }); - - // Then we run the operation with both tensors - sq->record>( - { this->mPrimaryTensor, this->mSecondaryTensor }, - std::vector(shader.begin(), shader.end())); - - // We map the result back to local - sq->record( - { this->mPrimaryTensor }); - - sq->end(); - } - else { - throw std::runtime_error("Sequence pointer no longer available"); - } } void KomputeModelMLNode::_process(float delta) { @@ -82,8 +147,8 @@ void KomputeModelMLNode::_bind_methods() { ClassDB::bind_method(D_METHOD("_process", "delta"), &KomputeModelMLNode::_process); ClassDB::bind_method(D_METHOD("_init"), &KomputeModelMLNode::_init); - ClassDB::bind_method(D_METHOD("add", "value"), &KomputeModelMLNode::add); - ClassDB::bind_method(D_METHOD("reset"), &KomputeModelMLNode::reset); - ClassDB::bind_method(D_METHOD("get_total"), &KomputeModelMLNode::get_total); + ClassDB::bind_method(D_METHOD("train", "yArr", "xIArr", "xJArr"), &KomputeModelMLNode::train); + ClassDB::bind_method(D_METHOD("predict", "xI", "xJ"), &KomputeModelMLNode::predict); + ClassDB::bind_method(D_METHOD("get_params"), &KomputeModelMLNode::get_params); } diff --git a/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.h b/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.h index ba2b39432..85c3310f9 100644 --- a/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.h +++ b/examples/godot_logistic_regression/custom_module/kompute_model_ml/KomputeModelMLNode.h @@ -12,9 +12,11 @@ class KomputeModelMLNode : public Node { public: KomputeModelMLNode(); - void add(float value); - void reset(); - float get_total() const; + void train(Array y, Array xI, Array xJ); + + Array predict(Array xI, Array xJ); + + Array get_params(); void _process(float delta); void _init(); @@ -23,9 +25,64 @@ protected: static void _bind_methods(); private: - kp::Manager mManager; - std::weak_ptr mSequence; - std::shared_ptr mPrimaryTensor; - std::shared_ptr mSecondaryTensor; + 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); +} +)"; + diff --git a/examples/godot_logistic_regression/custom_module/kompute_model_ml/register_types.cpp b/examples/godot_logistic_regression/custom_module/kompute_model_ml/register_types.cpp index 8bf6c2356..a85141263 100644 --- a/examples/godot_logistic_regression/custom_module/kompute_model_ml/register_types.cpp +++ b/examples/godot_logistic_regression/custom_module/kompute_model_ml/register_types.cpp @@ -5,10 +5,10 @@ #include "core/class_db.h" #include "KomputeModelMLNode.h" -void register_kompute_summator_types() { +void register_kompute_model_ml_types() { ClassDB::register_class(); } -void unregister_kompute_summator_types() { +void unregister_kompute_model_ml_types() { // Nothing to do here in this example. } diff --git a/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp b/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp index fef940ed7..c4969ab7d 100644 --- a/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp +++ b/examples/godot_logistic_regression/gdnative_shared/src/KomputeModelML.cpp @@ -1,4 +1,4 @@ -/* summator.cpp */ +#pragma once #include #include @@ -15,8 +15,8 @@ KomputeModelML::KomputeModelML() { void KomputeModelML::train(Array yArr, Array xIArr, Array xJArr) { - assert(y.size() == xI.size()); - assert(xI.size() == xJ.size()); + assert(yArr.size() == xIArr.size()); + assert(xIArr.size() == xJArr.size()); std::vector yData; std::vector xIData; diff --git a/examples/godot_logistic_regression/godot_resources/assets/CustomModuleExampleScene.tscn b/examples/godot_logistic_regression/godot_resources/assets/CustomModuleExampleScene.tscn index a98375112..ea32697a7 100755 --- a/examples/godot_logistic_regression/godot_resources/assets/CustomModuleExampleScene.tscn +++ b/examples/godot_logistic_regression/godot_resources/assets/CustomModuleExampleScene.tscn @@ -5,4 +5,4 @@ [node name="Parent" type="Node2D"] script = ExtResource( 1 ) -[node name="CustomKomputeNode" type="KomputeSummatorNode" parent="."] +[node name="EditorKomputeModelMLNode" type="KomputeModelMLNode" parent="."] diff --git a/examples/godot_logistic_regression/godot_resources/scripts/CustomModuleExampleScene.gd b/examples/godot_logistic_regression/godot_resources/scripts/CustomModuleExampleScene.gd index 45f68783e..30849545f 100755 --- a/examples/godot_logistic_regression/godot_resources/scripts/CustomModuleExampleScene.gd +++ b/examples/godot_logistic_regression/godot_resources/scripts/CustomModuleExampleScene.gd @@ -2,27 +2,27 @@ extends Node2D # Called when the node enters the scene tree for the first time. func _ready(): - print("hello") + var xi = [0, 1, 1, 1, 1, 1] + var xj = [0, 0, 0, 0, 1, 1] + var y = [0, 0, 0, 0, 1, 1] - # Use existing node - print($CustomKomputeNode.get_total()) + print("Running training and predict on existing node") - $CustomKomputeNode.add(10) - print($CustomKomputeNode.get_total()) + $EditorKomputeModelMLNode.train(y, xi, xj) - $CustomKomputeNode.add(10) - print($CustomKomputeNode.get_total()) + var preds = $EditorKomputeModelMLNode.predict(xi, xj) + + print(preds) + + print("Running training and predict on new instance") # Create new instance - var s = KomputeSummatorNode.new() + var s = KomputeModelMLNode.new() - # This will print 0 as it's a new instance - print(s.get_total()) + s.train(y, xi, xj) + print("") - # Now we can again send further commands - s.add(10) - print(s.get_total()) + preds = s.predict(xi, xj) - s.add(10) - print(s.get_total()) + print(preds)