From 2a81ac792e3a8e4d25a31a8127c100f30c4b3f0b Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Mon, 29 Jul 2019 14:36:52 +0100
Subject: [PATCH] Start replacing Tensorflow with Brain.js, but it's not
 finished yet

Note to self: bug them to upgrade ot GPU.js 2 for future projects
---
 package-lock.json             | 190 ++++++----------------------------
 package.json                  |   1 +
 server/settings.default.toml  |  11 +-
 server/train-ai/AITrainer.mjs |  28 ++---
 4 files changed, 44 insertions(+), 186 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 32d9a19..c36bc9e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -50,63 +50,6 @@
         "process-es6": "^0.11.2"
       }
     },
-    "@tensorflow/tfjs": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-1.2.3.tgz",
-      "integrity": "sha512-eJAAKU0Tcj84khnl3vjz1liACSsWUAegcWbsg+brG2uXOS96ayXWLGaxtjMtubpSxvaGKtJrqQTmXzRf4jaCaQ==",
-      "requires": {
-        "@tensorflow/tfjs-converter": "1.2.3",
-        "@tensorflow/tfjs-core": "1.2.3",
-        "@tensorflow/tfjs-data": "1.2.3",
-        "@tensorflow/tfjs-layers": "1.2.3"
-      }
-    },
-    "@tensorflow/tfjs-converter": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-1.2.3.tgz",
-      "integrity": "sha512-bCzkGvy6cpcl1LI8prKPz76+eRF/RjK3RLdMv5YW2LFCK2wXcaOtc5+EsKDGKJi+a3n+BeqeOFPc/QpYB/VRew=="
-    },
-    "@tensorflow/tfjs-core": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.2.3.tgz",
-      "integrity": "sha512-jLYUJYzI/b+058yKz9SjX6KwTac4hKFRh1FMI8jQpN289z9MSfKBq8BzpKd7n7ml7b55ae157Qta5pFmLwRVZw==",
-      "requires": {
-        "@types/offscreencanvas": "~2019.3.0",
-        "@types/seedrandom": "2.4.27",
-        "@types/webgl-ext": "0.0.30",
-        "@types/webgl2": "0.0.4",
-        "node-fetch": "~2.1.2",
-        "seedrandom": "2.4.3"
-      }
-    },
-    "@tensorflow/tfjs-data": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-1.2.3.tgz",
-      "integrity": "sha512-MOjsFAfiFPHGmq+WS1C3dQB2nEdeRvA0IAto/2atJUOlY41NLdGyhcnDZASLe/dPfGGARNXhOXtSSj7LY0yvIQ==",
-      "requires": {
-        "@types/node-fetch": "^2.1.2",
-        "node-fetch": "~2.1.2"
-      }
-    },
-    "@tensorflow/tfjs-layers": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-1.2.3.tgz",
-      "integrity": "sha512-qxvYbccObIXLy2c28RrOlZ+mYi7jtQrKGLB3LsOOOmEtWhuHv1d4FcSIimHy0KrnsbBxvy+v7lRG0Zvaqwn7iQ=="
-    },
-    "@tensorflow/tfjs-node-gpu": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-node-gpu/-/tfjs-node-gpu-1.2.3.tgz",
-      "integrity": "sha512-y8A1dF4WZZ+IvCCv/hrEUVV9O1ua0f5rZVzaMnJx+xv8o51DwTGk7h6tsnE/F2N6pf9mKLsY8roUBviIasVEmQ==",
-      "requires": {
-        "@tensorflow/tfjs": "~1.2.2",
-        "adm-zip": "^0.4.11",
-        "bindings": "~1.3.0",
-        "https-proxy-agent": "^2.2.1",
-        "progress": "^2.0.0",
-        "rimraf": "^2.6.2",
-        "tar": "^4.4.6"
-      }
-    },
     "@types/better-sqlite3": {
       "version": "5.4.0",
       "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-5.4.0.tgz",
@@ -152,20 +95,8 @@
     "@types/node": {
       "version": "12.6.8",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz",
-      "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg=="
-    },
-    "@types/node-fetch": {
-      "version": "2.3.7",
-      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.3.7.tgz",
-      "integrity": "sha512-+bKtuxhj/TYSSP1r4CZhfmyA0vm/aDRQNo7vbAgf6/cZajn0SAniGGST07yvI4Q+q169WTa2/x9gEHfJrkcALw==",
-      "requires": {
-        "@types/node": "*"
-      }
-    },
-    "@types/offscreencanvas": {
-      "version": "2019.3.0",
-      "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz",
-      "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q=="
+      "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==",
+      "dev": true
     },
     "@types/q": {
       "version": "1.5.2",
@@ -182,21 +113,6 @@
         "@types/node": "*"
       }
     },
-    "@types/seedrandom": {
-      "version": "2.4.27",
-      "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.27.tgz",
-      "integrity": "sha1-nbVjk33YaRX2kJK8QyWdL0hXjkE="
-    },
-    "@types/webgl-ext": {
-      "version": "0.0.30",
-      "resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz",
-      "integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg=="
-    },
-    "@types/webgl2": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.4.tgz",
-      "integrity": "sha512-PACt1xdErJbMUOUweSrbVM7gSIYm1vTncW2hF6Os/EeWi6TXYAYMPp+8v6rzHmypE5gHrxaxZNXgMkJVIdZpHw=="
-    },
     "abstract-leveldown": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz",
@@ -213,24 +129,11 @@
       "integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw==",
       "dev": true
     },
-    "adm-zip": {
-      "version": "0.4.13",
-      "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz",
-      "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw=="
-    },
     "aes-js": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz",
       "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ=="
     },
-    "agent-base": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
-      "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
-      "requires": {
-        "es6-promisify": "^5.0.0"
-      }
-    },
     "alphanum-sort": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
@@ -393,11 +296,6 @@
       "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
       "dev": true
     },
-    "bindings": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz",
-      "integrity": "sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew=="
-    },
     "bl": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
@@ -455,6 +353,15 @@
         }
       }
     },
+    "brain.js": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/brain.js/-/brain.js-1.6.1.tgz",
+      "integrity": "sha512-c+qrOGoW9VvIohTf70MQCjNzrunXVWI09CT27BZ/lZb6nObGxkdP10c4g9Ztnd8WFJijGdh3veh1A1vk/daIUA==",
+      "requires": {
+        "gpu.js": "^1.10.4",
+        "thaw.js": "^2.0.0"
+      }
+    },
     "brorand": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
@@ -1401,19 +1308,6 @@
         "event-emitter": "~0.3.5"
       }
     },
-    "es6-promise": {
-      "version": "4.2.8",
-      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
-      "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
-    },
-    "es6-promisify": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
-      "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
-      "requires": {
-        "es6-promise": "^4.0.3"
-      }
-    },
     "es6-set": {
       "version": "0.1.5",
       "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
@@ -1756,6 +1650,21 @@
         "unique-stream": "^2.0.2"
       }
     },
+    "gpu.js": {
+      "version": "1.10.4",
+      "resolved": "https://registry.npmjs.org/gpu.js/-/gpu.js-1.10.4.tgz",
+      "integrity": "sha512-qh5O1LXwwkhGOpEicR9SnQB7xduUDY5rKGPWj9yOFLNI90bX+mReooGQjnL3iZ08btBOT0zQGu9wKA+gGy2BhQ==",
+      "requires": {
+        "acorn": "^5.1.1"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "5.7.3",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
+          "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
+        }
+      }
+    },
     "has": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -1885,25 +1794,6 @@
       "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
       "dev": true
     },
-    "https-proxy-agent": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz",
-      "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==",
-      "requires": {
-        "agent-base": "^4.3.0",
-        "debug": "^3.1.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.2.6",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
-          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        }
-      }
-    },
     "icss-replace-symbols": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
@@ -2854,11 +2744,6 @@
         "lower-case": "^1.1.1"
       }
     },
-    "node-fetch": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
-      "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
-    },
     "node-releases": {
       "version": "1.1.25",
       "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.25.tgz",
@@ -3580,11 +3465,6 @@
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
     },
-    "progress": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
-      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
-    },
     "promise.series": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz",
@@ -3798,14 +3678,6 @@
       "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
       "dev": true
     },
-    "rimraf": {
-      "version": "2.6.3",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
-      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
-      "requires": {
-        "glob": "^7.1.3"
-      }
-    },
     "ripemd160": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
@@ -3930,11 +3802,6 @@
       "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
       "dev": true
     },
-    "seedrandom": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",
-      "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw="
-    },
     "semver": {
       "version": "5.7.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
@@ -4362,6 +4229,11 @@
         "source-map-support": "~0.5.12"
       }
     },
+    "thaw.js": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/thaw.js/-/thaw.js-2.0.0.tgz",
+      "integrity": "sha1-RSvF1+4s4bb5IDewW1BsvWVikdA="
+    },
     "through2": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
diff --git a/package.json b/package.json
index 1ef466f..35be201 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
 		"async-mqtt": "^2.3.0",
 		"awilix": "^4.2.2",
 		"better-sqlite3": "^5.4.0",
+		"brain.js": "^1.6.1",
 		"chroma-js": "^2.0.4",
 		"debug": "^4.1.1",
 		"haversine-distance": "^1.1.4",
diff --git a/server/settings.default.toml b/server/settings.default.toml
index 148a9ca..2f30918 100644
--- a/server/settings.default.toml
+++ b/server/settings.default.toml
@@ -59,13 +59,12 @@ devices = [
 rssi_min = -150
 rssi_max = 0
 
-# Data is streamed from the SQLite databse. The batch size specifies how many 
-# rows to gather for training at once.
-# Note that the entire available dataset will eventually end up being used - 
-# this setting just controls how much of ti is in memory at once.
-batch_size = 32
+# The architecture of the neural network, as an arary of integers.
+# Each integer represents the number of nodes in a layer of the neural network.
+network_arch = [ 64 ]
+
 # The number of epochs to train for.
-epochs = 5
+epochs = 1000
 # The percentage, between 0 and 1, of data that should be set aside for validation.
 validation_split = 0.1
 
diff --git a/server/train-ai/AITrainer.mjs b/server/train-ai/AITrainer.mjs
index db66797..fd28356 100644
--- a/server/train-ai/AITrainer.mjs
+++ b/server/train-ai/AITrainer.mjs
@@ -3,7 +3,7 @@
 import path from 'path';
 import fs from 'fs';
 
-import tf from '@tensorflow/tfjs-node-gpu';
+import brain from 'brain.js';
 
 class AITrainer {
 	constructor({ ansi, settings, log, root_dir, GatewayRepo, DatasetFetcher }) {
@@ -15,28 +15,14 @@ class AITrainer {
 		this.repo_gateway = GatewayRepo;
 	}
 	
-	generate_model() {
-		let model = tf.sequential();
-		model.add(tf.layers.dense({
-			units: 64, // 64 nodes
-			activation: "sigmoid", // Sigmoid activation function
-			inputShape: [3], // 3 inputs - lat, long, and distance from gateway
-		}))
-		model.add(tf.layers.dense({
-			units: 1, // 1 output value - RSSI
-			activation: "sigmoid" // The example code uses softmax, but this is generally best used for classification tasks
-		}));
-		
-		model.compile({
-			optimizer: tf.train.adam(),
-			loss: tf.losses.absoluteDifference,
-			metrics: [ tf.metrics.meanSquaredError ]
+	generate_neural_net() {
+		let net = new brain.NeuralNetwork({
+			hiddenLayers: this.settings.ai.network_arch,
+			activation: "sigmoid"
 		});
 		
-		this.l.log(`Model:`);
-		model.summary();
 		
-		return model;
+		return net;
 	}
 	
 	async train_all() {
@@ -84,7 +70,7 @@ class AITrainer {
 	 */
 	async train_gateway(gateway_id, destination_filename) {
 		this.l.log(`${this.a.fgreen}${this.a.hicol}Training AI for gateway ${gateway_id}${this.a.reset}`);
-		let model = this.generate_model();
+		let model = this.generate_neural_net();
 		
 		// let dataset_input = tf.data.generator(
 		// 	this.dataset_fetcher.fetch_input.bind(this.dataset_fetcher, gateway_id)
