From c4d5f23cc81981da26ed6bfefb881c0397dee79e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radek=20Pu=C5=A1?= <pusradek@fit.cvut.cz>
Date: Sat, 2 Nov 2019 00:41:47 +0100
Subject: [PATCH] save to DB functionality for neural network

---
 Core/Core/Data/Model.cs                       | 25 ++++--
 Core/Core/Models/Network.cs                   | 37 ++++++++
 .../NeuralNetNamespace/Layers/ILayer.cs       |  2 +
 .../NeuralNetNamespace/Layers/InputLayer.cs   | 10 +++
 .../NeuralNetNamespace/Layers/Layer.cs        | 14 +++
 .../NeuralNetNamespace/NeuralNet.cs           | 33 +++++++
 .../NeuralNetNamespace/Neurons/INeuron.cs     |  3 +
 .../NeuralNetNamespace/Neurons/Neuron.cs      | 15 ++--
 Core/NeuralNetwork/Program.cs                 | 89 +++++++++++++++++--
 9 files changed, 209 insertions(+), 19 deletions(-)
 create mode 100644 Core/Core/Models/Network.cs

diff --git a/Core/Core/Data/Model.cs b/Core/Core/Data/Model.cs
index 75623b0..cbc2cd2 100644
--- a/Core/Core/Data/Model.cs
+++ b/Core/Core/Data/Model.cs
@@ -32,16 +32,18 @@ namespace Core.Data
         public DbSet<ForeignPayment> ForeignPayments { get; set; }
 
         public DbSet<Repetition> Repetitions { get; set; }
+        public DbSet<Network> Networks { get; set; }
 
 
         protected override void OnModelCreating(ModelBuilder modelBuilder)
         {
-            modelBuilder.Entity<Expense>().ToTable("Expense");
+            SetRelations(modelBuilder);
+            SetTableNames(modelBuilder);
+            base.OnModelCreating(modelBuilder);
+        }
 
-            modelBuilder.Entity<Account>().ToTable("Account");
-            modelBuilder.Entity<ConstantSymbol>().ToTable("ConstantSymbol");
-            modelBuilder.Entity<Person>().ToTable("Person");
-            //modelBuilder.Entity<Transaction>().ToTable("Transaction");
+        private void SetRelations(ModelBuilder modelBuilder)
+        {
             modelBuilder.Entity<Transaction>()
                 .HasOne(t => t.RecipientAccount)
                 .WithMany(a => a.RecipientTransactions)
@@ -56,8 +58,15 @@ namespace Core.Data
                 .OnDelete(DeleteBehavior.Restrict);
 
             modelBuilder.Entity<User>().HasIndex(u => u.Username).IsUnique();
-            modelBuilder.Entity<User>().ToTable("Users");
+        }
 
+        private void SetTableNames(ModelBuilder modelBuilder)
+        {
+            modelBuilder.Entity<Expense>().ToTable("Expense");
+            modelBuilder.Entity<Account>().ToTable("Account");
+            modelBuilder.Entity<ConstantSymbol>().ToTable("ConstantSymbol");
+            modelBuilder.Entity<Person>().ToTable("Person");
+            modelBuilder.Entity<User>().ToTable("Users");
             modelBuilder.Entity<AccountType>().ToTable("AccountType");
             modelBuilder.Entity<TransactionType>().ToTable("TransactionType");
             modelBuilder.Entity<Kind>().ToTable("Kind");
@@ -65,10 +74,8 @@ namespace Core.Data
             modelBuilder.Entity<MinistryPredefined>().ToTable("MinistryPredefined");
             modelBuilder.Entity<ReservedSymbol>().ToTable("ReservedSymbol");
             modelBuilder.Entity<ForeignPayment>().ToTable("ForeignPayment");
-
             modelBuilder.Entity<Repetition>().ToTable("Repetition");
-
-            base.OnModelCreating(modelBuilder);
+            modelBuilder.Entity<Network>().ToTable("Network");
         }
     }
 }
diff --git a/Core/Core/Models/Network.cs b/Core/Core/Models/Network.cs
new file mode 100644
index 0000000..7ae69fa
--- /dev/null
+++ b/Core/Core/Models/Network.cs
@@ -0,0 +1,37 @@
+using Core.Data;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Core.Models
+{
+    public class Network
+    {
+        [Key]
+        public long ID { get; set; }
+        public ICollection<Layer> Layer { get; set; }
+    }
+
+    public class Layer
+    {
+        [Key]
+        public long ID { get; set; }
+        public ICollection<Neuron> Neuron { get; set; }
+    }
+
+    public class Neuron
+    {
+        [Key]
+        public long ID { get; set; }
+        public ICollection<Weight> Weight { get; set; }
+    }
+
+    public class Weight
+    {
+        [Key]
+        public long ID { get; set; }
+        public double Value { get; set; }
+    }
+}
diff --git a/Core/NeuralNetwork/NeuralNetNamespace/Layers/ILayer.cs b/Core/NeuralNetwork/NeuralNetNamespace/Layers/ILayer.cs
index b0b5a74..4e9d824 100644
--- a/Core/NeuralNetwork/NeuralNetNamespace/Layers/ILayer.cs
+++ b/Core/NeuralNetwork/NeuralNetNamespace/Layers/ILayer.cs
@@ -14,5 +14,7 @@ namespace NeuralNetwork.NeuralNetNamespace.Layers
         void Evaluate(DenseVector inputVector);
         void Propagate(DenseVector error);
         void AdjustWeights();
+        List<List<double>> Export();
+        void Import(List<List<double>> list);
     }
 }
diff --git a/Core/NeuralNetwork/NeuralNetNamespace/Layers/InputLayer.cs b/Core/NeuralNetwork/NeuralNetNamespace/Layers/InputLayer.cs
index 98c506d..6b96ea9 100644
--- a/Core/NeuralNetwork/NeuralNetNamespace/Layers/InputLayer.cs
+++ b/Core/NeuralNetwork/NeuralNetNamespace/Layers/InputLayer.cs
@@ -37,6 +37,16 @@ namespace NeuralNetwork.NeuralNetNamespace.Layers
             NextLayer.Evaluate(inputVector);
         }
 
+        public List<List<double>> Export()
+        {
+            return new List<List<double>> { new List<double> { Size } };
+        }
+
+        public void Import(List<List<double>> list)
+        {
+            throw new NotImplementedException();
+        }
+
         public void Propagate(DenseVector error)
         {
             return;
diff --git a/Core/NeuralNetwork/NeuralNetNamespace/Layers/Layer.cs b/Core/NeuralNetwork/NeuralNetNamespace/Layers/Layer.cs
index 085f199..274d74a 100644
--- a/Core/NeuralNetwork/NeuralNetNamespace/Layers/Layer.cs
+++ b/Core/NeuralNetwork/NeuralNetNamespace/Layers/Layer.cs
@@ -74,5 +74,19 @@ namespace NeuralNetwork.NeuralNetNamespace.Layers
             if (PrevLayer != null)
                 PrevLayer.Propagate(layerError);
         }
+
+        public List<List<double>> Export()
+        {
+            List<List<double>> layer = new List<List<double>> (Neurons.Count);
+            for (int i = 0; i < Neurons.Count; i++)
+                layer.Add(Neurons[i].Export());
+            return layer;
+        }
+
+        public void Import(List<List<double>> layers)
+        {
+            for (int i = 0; i < layers.Count; i++)
+                Neurons[i].Import(layers[i]);
+        }
     }
 }
diff --git a/Core/NeuralNetwork/NeuralNetNamespace/NeuralNet.cs b/Core/NeuralNetwork/NeuralNetNamespace/NeuralNet.cs
index 2086545..724cb03 100644
--- a/Core/NeuralNetwork/NeuralNetNamespace/NeuralNet.cs
+++ b/Core/NeuralNetwork/NeuralNetNamespace/NeuralNet.cs
@@ -16,6 +16,8 @@ namespace NeuralNetwork.NeuralNetNamespace
 
         public NeuralNet CreateEmpty(List<int> layerSizes)
         {
+            layers = new List<ILayer>(layerSizes.Count);
+
             ILayer prevLayer = new InputLayer(layerSizes[0]);
             layers.Add(prevLayer);
 
@@ -35,6 +37,37 @@ namespace NeuralNetwork.NeuralNetNamespace
             return this;
         }
 
+        /// <summary>
+        /// weights for neurons in corresponding layers
+        /// (net 1:N > layer 1:N > neuron 1:N > weight)
+        /// </summary>
+        /// <returns>weigts for individual neurons (+layers)</returns>
+        public List<List<List<double>>> ExportNet()
+        {
+            List<List<List<double>>> net = new List<List<List<double>>>(layers.Count);
+            for (int i = 0; i < layers.Count; i++)
+                net.Add(layers[i].Export());
+
+            return net;
+        }
+
+        public void Import(List<List<List<double>>> net)
+        {
+            //instance initialization first
+            List<int> shape = new List<int>(net.Count);
+
+            shape.Add((int)net[0][0][0]);
+            for (int i = 1; i < net.Count; i++)
+                shape.Add(net[i].Count);
+
+            CreateEmpty(shape);
+
+            //fill with data
+
+            for (int i = 1; i < net.Count; i++)
+                layers[i].Import(net[i]);
+        }
+
         public double LastResult { get; private set; }
 
         public bool EvaluateInput(List<double> input)
diff --git a/Core/NeuralNetwork/NeuralNetNamespace/Neurons/INeuron.cs b/Core/NeuralNetwork/NeuralNetNamespace/Neurons/INeuron.cs
index 8703e11..82ebadf 100644
--- a/Core/NeuralNetwork/NeuralNetNamespace/Neurons/INeuron.cs
+++ b/Core/NeuralNetwork/NeuralNetNamespace/Neurons/INeuron.cs
@@ -12,5 +12,8 @@ namespace NeuralNetwork.NeuralNetNamespace.Neurons
         DenseVector Propagate(double error);
         void AdjustWeights();
         double Output { get; }
+
+        List<double> Export();
+        void Import(List<double> list);
     }
 }
diff --git a/Core/NeuralNetwork/NeuralNetNamespace/Neurons/Neuron.cs b/Core/NeuralNetwork/NeuralNetNamespace/Neurons/Neuron.cs
index d8c36f2..7bad697 100644
--- a/Core/NeuralNetwork/NeuralNetNamespace/Neurons/Neuron.cs
+++ b/Core/NeuralNetwork/NeuralNetNamespace/Neurons/Neuron.cs
@@ -7,7 +7,6 @@ using System.Text;
 
 namespace NeuralNetwork.NeuralNetNamespace.Neurons
 {
-    //TODO: Abs() pro WeightTotal - buÄŹ ano nebo ne
     //TODO: komentáře
     //TODO: dekompozice
     class Neuron : INeuron
@@ -21,10 +20,6 @@ namespace NeuralNetwork.NeuralNetNamespace.Neurons
         private int PropagationIterator;
 
         public DenseVector Weights { get; private set; }
-        /// <summary>
-        /// sum of all weights in Weights vector
-        /// </summary>
-        private double WeightTotal;
         
 
         private DenseVector Corrections;
@@ -108,5 +103,15 @@ namespace NeuralNetwork.NeuralNetNamespace.Neurons
 
             return $"({Output.ToString()} # {str})";
         }
+
+        public List<double> Export()
+        {
+            return new List<double>(Weights.ToArray());
+        }
+
+        public void Import(List<double> list)
+        {
+            Weights.SetValues(list.ToArray());
+        }
     }
 }
diff --git a/Core/NeuralNetwork/Program.cs b/Core/NeuralNetwork/Program.cs
index 04c9d66..bd7ff27 100644
--- a/Core/NeuralNetwork/Program.cs
+++ b/Core/NeuralNetwork/Program.cs
@@ -9,7 +9,85 @@ namespace NeuralNetwork
         private static int startSize;
         static void Main(string[] args)
         {
-            XORGenerator();
+            NeuralNet ann = TestTrain(infinite: false);
+
+            Console.WriteLine("evaluate trained:");
+            EvaluationTest(ann);
+
+            Console.WriteLine("evaluate trained 2:");
+            EvaluationTest(ann);
+
+            Console.WriteLine("exporting:");
+            List<List<List<double>>> export = ann.ExportNet();
+            Console.WriteLine("done");
+
+            Console.WriteLine("evaluate trained 3:");
+            EvaluationTest(ann);
+
+            Console.WriteLine("importing to new net:");
+            NeuralNet import = new NeuralNet();
+            import.Import(export);
+            Console.WriteLine("done");
+
+            Console.WriteLine("test:");
+            EvaluationTest(import);
+            Console.WriteLine("done");
+
+            Console.WriteLine("importing to old net:");
+            ann.Import(export);
+            Console.WriteLine("done");
+
+            Console.WriteLine("test:");
+            EvaluationTest(ann);
+            Console.WriteLine("done");
+        }
+
+        private static KeyValuePair<bool, List<double>> GetGenerator()
+        {
+            return OddEvenGenerator();
+        }
+
+        private static void EvaluationTest(NeuralNet ann)
+        {
+            int passed = 0;
+            int failed = 0;
+            double yes = 0;
+            double predictionYes = 0;
+            double no = 0;
+            double predictionNo = 0;
+            const int iterations=1000;
+            for (int i = 0; i < iterations; i++)
+            {
+                var data = GetGenerator();
+                bool guess = ann.EvaluateInput(data.Value);
+                bool result = data.Key;
+
+                if (guess == result)
+                    passed++;
+                else
+                    failed++;
+
+                if (result)
+                    yes++;
+                else
+                    no++;
+
+                if (guess)
+                    predictionYes++;
+                else
+                    predictionNo++;
+            }
+
+            double localResult = ((double)passed / iterations) * 100;
+            Console.WriteLine($"succes rate: {localResult}% (p:{passed} vs. f:{failed})");
+            Console.WriteLine($"Actualyes: {yes}, Actualno:{no}");
+            Console.WriteLine($"predictionYes: {predictionYes}, predictionNo:{predictionNo}");
+            Console.WriteLine("_-_-_-_-_-_-_-_-_-_-_-_-_");
+        }
+
+        private static NeuralNet TestTrain(bool infinite)
+        {
+            GetGenerator();
             List<int> widths = new List<int>() { startSize, 3, 3, 1 };
             NeuralNet ANN = new NeuralNet().CreateEmpty(widths);
 
@@ -29,9 +107,9 @@ namespace NeuralNetwork
             const int adjust = 100;
             //const int step = 20;
             //const int adjust = 3;
-            for (int i = 0; true || i < 1000; i++)
+            for (int i = 0; infinite || i < 10000; i++)
             {
-                var data = XORGenerator();
+                var data = GetGenerator();
                 bool guess = ANN.EvaluateInput(data.Value);
                 bool result = data.Key;
 
@@ -54,7 +132,7 @@ namespace NeuralNetwork
                 if (i % adjust == 0 && i > adjust)
                     ANN.AdjustWeights();
 
-                if (i % step == 0)
+                if (i>1 && i % step == 0)
                 {
                     Console.WriteLine(ANN.ToString());
                     Console.WriteLine("_-_-_-_-_-_-_-_-_-_-_-_-_");
@@ -73,7 +151,8 @@ namespace NeuralNetwork
                 }
 
             }
-            Console.WriteLine("Hello World!");
+
+            return ANN;
         }
 
         private static KeyValuePair<bool, List<double>> XORGenerator()
-- 
GitLab