From f152b3030b332a00e537d8858df5347c6579fd4d Mon Sep 17 00:00:00 2001 From: rluna Date: Fri, 4 Jan 2019 17:24:37 +0100 Subject: [PATCH] added posibility to change internal weights of the network directly assigning the values --- .settings/.gitignore | 1 + Makefile | 3 ++- src/.gitignore | 5 ++++ src/Layer.h | 24 +++++++++++++++++ src/MLP.cpp | 43 ++++++++++++++++++++++++++--- src/MLP.h | 4 +++ src/MLPTest.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++ src/Node.h | 11 +++++++- 8 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 .settings/.gitignore create mode 100644 src/.gitignore diff --git a/.settings/.gitignore b/.settings/.gitignore new file mode 100644 index 0000000..d81d4c4 --- /dev/null +++ b/.settings/.gitignore @@ -0,0 +1 @@ +/language.settings.xml diff --git a/Makefile b/Makefile index 9434e80..1131947 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,8 @@ AUXLIBS = INCLUDES = -I$(LOCALDEPSINCLUDES) -I$(AUXINCLUDES) LIBS = -L$(AUXLIBS) #LIBS += -L/usr/local/lib/ -CFLAGS = -std=gnu++11 -std=c++11 -Wall -O3 -fmessage-length=0 -fPIC $(INCLUDES) +#rlunaro: removed optimization for tests: -O3 +CFLAGS = -std=gnu++11 -std=c++11 -Wall -fmessage-length=0 -fPIC $(INCLUDES) CFLAGS += $(DEBUG) LFLAGS = $(LIBS) #For verbosity diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..f010bd6 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,5 @@ +/IrisDatasetTest.o +/LayerTest.o +/MLP.o +/MLPTest.o +/NodeTest.o diff --git a/src/Layer.h b/src/Layer.h index 462a18c..a6efa01 100644 --- a/src/Layer.h +++ b/src/Layer.h @@ -68,6 +68,14 @@ public: return m_nodes; } + /** + * Return the internal list of nodes, but modifiable. + */ + std::vector & GetNodesChangeable() { + return m_nodes; + } + + void GetOutputAfterActivationFunction(const std::vector &input, std::vector * output) const { assert(input.size() == m_num_inputs_per_node); @@ -116,6 +124,22 @@ public: }; + void SetWeights( std::vector> & weights ) + { + if( 0 <= weights.size() && weights.size() <= m_num_nodes ) + { + // traverse the list of nodes + size_t node_i = 0; + for( Node & node : m_nodes ) + { + node.SetWeights( weights[node_i] ); + node_i++; + } + } + else + throw new std::logic_error("Incorrect layer number in SetWeights call"); + }; + void SaveLayer(FILE * file) const { fwrite(&m_num_nodes, sizeof(m_num_nodes), 1, file); fwrite(&m_num_inputs_per_node, sizeof(m_num_inputs_per_node), 1, file); diff --git a/src/MLP.cpp b/src/MLP.cpp index dfdd56b..ca54320 100644 --- a/src/MLP.cpp +++ b/src/MLP.cpp @@ -48,7 +48,7 @@ void MLP::CreateMLP(const std::vector & layers_nodes, m_num_outputs = m_layers_nodes[m_layers_nodes.size() - 1]; m_num_hidden_layers = m_layers_nodes.size() - 2; - for (int i = 0; i < m_layers_nodes.size() - 1; i++) { + for (size_t i = 0; i < m_layers_nodes.size() - 1; i++) { m_layers.emplace_back(Layer(m_layers_nodes[i], m_layers_nodes[i + 1], layers_activfuncs[i], @@ -65,7 +65,7 @@ void MLP::SaveMLPNetwork(const std::string & filename)const { fwrite(&m_num_hidden_layers, sizeof(m_num_hidden_layers), 1, file); if (!m_layers_nodes.empty()) fwrite(&m_layers_nodes[0], sizeof(m_layers_nodes[0]), m_layers_nodes.size(), file); - for (int i = 0; i < m_layers.size(); i++) { + for (size_t i = 0; i < m_layers.size(); i++) { m_layers[i].SaveLayer(file); } fclose(file); @@ -83,7 +83,7 @@ void MLP::LoadMLPNetwork(const std::string & filename) { if (!m_layers_nodes.empty()) fread(&m_layers_nodes[0], sizeof(m_layers_nodes[0]), m_layers_nodes.size(), file); m_layers.resize(m_layers_nodes.size() - 1); - for (int i = 0; i < m_layers.size(); i++) { + for (size_t i = 0; i < m_layers.size(); i++) { m_layers[i].LoadLayer(file); } fclose(file); @@ -103,7 +103,7 @@ void MLP::GetOutput(const std::vector &input, std::vector temp_out(temp_size, 0.0); temp_in = input; - for (int i = 0; i < m_layers.size(); ++i) { + for (size_t i = 0; i < m_layers.size(); ++i) { if (i > 0) { //Store this layer activation if (all_layers_activations != nullptr) @@ -260,3 +260,38 @@ void MLP::Train(const std::vector &training_sample_set_with_bias }; +size_t MLP::GetNumLayers() +{ + return m_layers.size(); +} + +std::vector> MLP::GetLayerWeights( size_t layer_i ) +{ + std::vector> ret_val; + // check parameters + if( 0 <= layer_i && layer_i < m_layers.size() ) + { + Layer current_layer = m_layers[layer_i]; + for( Node & node : current_layer.GetNodesChangeable() ) + { + ret_val.push_back( node.GetWeights() ); + } + return ret_val; + } + else + throw new std::logic_error("Incorrect layer number in GetLayerWeights call"); + +} + +void MLP::SetLayerWeights( size_t layer_i, std::vector> & weights ) +{ + // check parameters + if( 0 <= layer_i && layer_i < m_layers.size() ) + { + m_layers[layer_i].SetWeights( weights ); + } + else + throw new std::logic_error("Incorrect layer number in SetLayerWeights call"); +} + + diff --git a/src/MLP.h b/src/MLP.h index 638721b..2a1cc5e 100644 --- a/src/MLP.h +++ b/src/MLP.h @@ -16,6 +16,7 @@ #include #include #include +#include class MLP { public: @@ -40,6 +41,9 @@ public: int max_iterations = 5000, double min_error_cost = 0.001, bool output_log = true); + size_t GetNumLayers(); + std::vector> GetLayerWeights( size_t layer_i ); + void SetLayerWeights( size_t layer_i, std::vector> & weights ); protected: void UpdateWeights(const std::vector> & all_layers_activations, diff --git a/src/MLPTest.cpp b/src/MLPTest.cpp index aaaf2f9..ad237f7 100644 --- a/src/MLPTest.cpp +++ b/src/MLPTest.cpp @@ -317,6 +317,70 @@ UNIT(LearnX2) { LOG(INFO) << "Trained with success." << std::endl; } + + +UNIT(GetWeightsSetWeights) { + LOG(INFO) << "Train X2 function, read internal weights" << std::endl; + + std::vector training_set = + { + { { 0, 0 },{ 0.0 } }, + { { 0, 1 },{ 1.0 } }, + { { 1, 0 },{ 0.0 } }, + { { 1, 1 },{ 1.0 } } + }; + bool bias_already_in = false; + std::vector training_sample_set_with_bias(training_set); + //set up bias + if (!bias_already_in) { + for (auto & training_sample_with_bias : training_sample_set_with_bias) { + training_sample_with_bias.AddBiasValue(1); + } + } + + size_t num_features = training_sample_set_with_bias[0].GetInputVectorSize(); + size_t num_outputs = training_sample_set_with_bias[0].GetOutputVectorSize(); + MLP my_mlp({ num_features, 2, num_outputs }, { "sigmoid", "linear" }); + //Train MLP + my_mlp.Train(training_sample_set_with_bias, 0.5, 500, 0.25); + + // get layer weights + std::vector> weights = my_mlp.GetLayerWeights( 1 ); + + // the expected value of the internal weights + // after training are 1.65693 -0.538749 + ASSERT_TRUE( 1.6 <= weights[0][0] && weights[0][0] <= 1.7 ); + ASSERT_TRUE( -0.6 <= weights[0][1] && weights[0][1] <= -0.5 ); + + // now, we are going to inject a weight value of 0.0 + // and check that the new output value is nonsense + std::vector> zeroWeights = { { 0.0, 0.0 } }; + + my_mlp.SetLayerWeights( 1, zeroWeights ); + + /* + * + * PREDICTED OUTPUT IS NOW: 0.335394 +PREDICTED OUTPUT IS NOW: 1.13887 +PREDICTED OUTPUT IS NOW: 0.180468 +PREDICTED OUTPUT IS NOW: 1.00535 + * + */ + for (const auto & training_sample : training_sample_set_with_bias) { + std::vector output; + my_mlp.GetOutput(training_sample.input_vector(), &output); + for (size_t i = 0; i < num_outputs; i++) { + bool predicted_output = output[i] > 0.5 ? true : false; + std::cout << "PREDICTED OUTPUT IS NOW: " << output[i] << std::endl; + bool correct_output = training_sample.output_vector()[i] > 0.5 ? true : false; + ASSERT_TRUE(predicted_output == correct_output); + } + } + LOG(INFO) << "Trained with success." << std::endl; +} + + + int main(int argc, char* argv[]) { START_EASYLOGGINGPP(argc, argv); microunit::UnitTester::Run(); diff --git a/src/Node.h b/src/Node.h index 3e724f4..d145d51 100644 --- a/src/Node.h +++ b/src/Node.h @@ -15,6 +15,7 @@ #include #include #include // for assert() +#include #define CONSTANT_WEIGHT_INITIALIZATION 0 @@ -81,6 +82,14 @@ public: return m_weights; } + void SetWeights( std::vector & weights ){ + // check size of the weights vector + if( weights.size() == m_num_inputs ) + m_weights = weights; + else + throw new std::logic_error("Incorrect weight size in SetWeights call"); + } + size_t GetWeightsVectorSize() const { return m_weights.size(); } @@ -146,4 +155,4 @@ protected: std::vector m_weights; }; -#endif //NODE_H \ No newline at end of file +#endif //NODE_H