RegistrierenRegistrieren   LoginLogin   FAQFAQ    SuchenSuchen   
KI; eigenes kleines Neuronales Netz programmieren?
 
Neue Frage »
Antworten »
    Foren-Übersicht -> Off-Topic
Autor Nachricht
KI Entwickler
Gast





Beitrag KI Entwickler Verfasst am: 01. Apr 2022 18:52    Titel: KI; eigenes kleines Neuronales Netz programmieren? Antworten mit Zitat

Meine Frage:
Hi,

habe richtig Lust, ein eigenes kleines neuronales Netz zu entwickeln. Es darf auf bestehenden Funktionen aufbauen, aber den Code möchte ich schon gerne selbst schreiben.
Wie geht man da am besten vor?

Habe C-, Python- und Matlab-Grundkenntnisse. Womit macht man das am besten? Hatte an PyTorch gedacht, weil das wohl viel auf dem Arbeitsmarkt nachgefragt wird und man das super als eigenes Projekt angeben kann.

Das neuronale Netz soll vor allem selbstständig lernen (unsupervised learning?) und mit mir interagieren. Als kleines Code-Programm auf meinem Rechner existieren und stetig dazu lernen, um die Welt immer besser wahrzunehmen (Computer Vision? Bilderkennung vielleicht auch? oder auch einfach eine andere Art der Interaktion?)

Gibt es eine Möglichkeit, das ganze auch visuell darzustellen, zusätzlich zum Code? Mit anschaulichen Neuronen, sodass man es beim Lernen zusehen kann?

Meine Ideen:
Und Bonusfrage: Wenn ich das alles geschafft habe, würde ich es vielleicht eines Tages auf eine Hardware übertragen, damit es mit der physischen Welt interagieren kann. Ich hatte an eine Art kleinen Roboter mit Sensoren gedacht, vielleicht über einen Arduino? Glaubt ihr, dass funktioniert und wenn ja, wie?
Das hat erstmal keine Priorität, würde aber gerne wissen, ob es grundsätzlich möglich ist.
Danke smile
analist



Anmeldungsdatum: 23.03.2022
Beiträge: 25

Beitrag analist Verfasst am: 24. Apr 2022 03:24    Titel: Antworten mit Zitat

Hallo,
klingt ein bisschen wie ein Aprilscherz.
Falls es doch ernst gemeint war, in Python gibt es gute Werkzeuge dazu.
In
http://open.hpi.de
gibts dazu auch gute Kurse.

Nur Deine Vorstellungen von neuronalen Netzwerken musst Du wahrscheinlich etwas korrigieren.

Grafische Darstellungen kenne ich so gut wie keine. Haette ja auch wenig Sinn.
Alles was man als Grafik sehen kann sind die Lernschritte/erfolge.

Ein Arduino ist definitiv nicht geeignet fuer ANN.
Wenn Du was off-PC machen willst, probiere es wenn dann mit FPGA.
Gibt es auch gute Boards dazu, die nicht all zu teuer sind, nur Dein ANN musst Du dann schon sehr optimieren, wenn Du es in so einem Board unterbringen willst.

Da dieses Forum mir leider keine Benachrichtigungen schickt, kannst Du Dich auch direkt an physikerboard(a)for.cl wenden, wenn Du mehr Info von mir haben moechtest.

Viel Spass beim programmieren. Macht wirklich Spass (Mir zumindest)
terminus



Anmeldungsdatum: 17.10.2020
Beiträge: 555

Beitrag terminus Verfasst am: 24. Apr 2022 10:23    Titel: Antworten mit Zitat

habe ich schon gemacht, mit Arduino, und zwar einfache feed-forward-Netze als auch mehrschichtige Backpropagation-Netze (letztere sind sinnvoller, da sie auch die etwas komplexere XOR-Verknüpfung lernen können); im folgenden Fall zur Mustererkennung von Zeichen in einer 10x12 Matrix (zunächst hardcoded, man bräuchte sonst 120 GPIOs oder GUI-Buttons etc. dafür):
Code:
// Neural Network for Arduino

// Ref.:
// http://robotics.hobbizine.com/arduinoann.html
// www.cs.bham.ac.uk/~jxb/NN/nn.html

// 3-layer Backpropagation net

// modified, extended, and enhanced by terminus
// required MCUs: ESP series
// version 0.1.12-32

// (C) 2018 by terminus
// (C) of processed 3rd party code: see original sources.
// This example code is in the public domain for private use, 3rd party code not affected.
// Use for professional or business purpose only by personal written permission
// by the author.



// change log:
// 0.1.12-32: ESP32, optim learn sets
// 0.1.1a-32: ESP32, optim dyn arrays, tft
// 0.1.1-32:  ESP32
// 0.1.1: input matrix=void   => output array=void={0,0,0,0,0,0,0,0,0,0}
//        input matrix pattern="0" => output array={1,0,0,0,0,0,0,0,0,0}
// 0.1.0: debug mode

#include <SPI.h>
#include <SD.h>
#include <Adafruit_GFX.h>         // Core graphics library
#include <Adafruit_HX8357.h>      // Hardware-specific library
#include <Adafruit_ImageReader.h> // Image-reading functions


#if defined(ESP8266)
#define TFT_CS   0
#define TFT_DC   15
#define SD_CS    2
#elif defined(ESP32)  // <<<<<<<<<<<<<<<<<<<<<
#define TFT_CS   15   // <<<<<<<<<<<<<<<<<<<<<
#define TFT_DC   33   // <<<<<<<<<<<<<<<<<<<<<
#define SD_CS    14   // <<<<<<<<<<<<<<<<<<<<<
#elif defined(TEENSYDUINO)
#define TFT_DC   10
#define TFT_CS   4
#define SD_CS    8
#elif defined(ARDUINO_STM32_FEATHER)
#define TFT_DC   PB4
#define TFT_CS   PA15
#define SD_CS    PC5
#elif defined(ARDUINO_NRF52_FEATHER) // BSP 0.6.5 and higher!
#define TFT_DC   11
#define TFT_CS   31
#define SD_CS    27
#elif defined(ARDUINO_MAX32620FTHR) || defined(ARDUINO_MAX32630FTHR)
#define TFT_DC   P5_4
#define TFT_CS   P5_3
#define STMPE_CS P3_3
#define SD_CS    P3_2
#else // Anything else!
#define TFT_CS   9
#define TFT_DC   10
#define SD_CS    5
#endif

Adafruit_HX8357   display = Adafruit_HX8357(TFT_CS, TFT_DC);


#include <math.h>

// debug mode: uncomment, regular mode: outcomment!
//#define  DEBUG


#define  REPORT_N   25    // for terminal log
int32_t  ReportInterval;  // for terminal log
uint32_t timestamp;       // for terminal log


/******************************************************************
   Network Configuration, customizable
 ******************************************************************/
const int NUM_INPUTS   = 120;  // <<<<< !! DON'T CHANGE THIS !!
const int NUM_HIDDEN   =  80;  // esp8266: 35
const int NUM_OUTPUTS  =  10;  // <<<<< !! DON'T CHANGE THIS !!
const int MAX_PATTERNS =  60;  // ESP32: 250, esp8266: 40

float LearningRate = 0.20;    // 0.3 vv lower oscillating
float Momentum     = 0.9;     // 0.8 ^^ lower local min
float InitialWeightMax = 0.5; // 0.5


#ifdef DEBUG
#define MAXLOOPS   3000
#define ThrSuccess 0.20
#else
#define MAXLOOPS   2147483647
#define ThrSuccess 1E-8*NUM_INPUTS*NUM_OUTPUTS*MAX_PATTERNS
#endif


int      i, j, p, q, r;
int      RandomizedIndex[MAX_PATTERNS];
uint32_t TrainingCycle;
float    Rando;
float    Error;
float    Accum;


/******************************************************************
   Artificial Neuron
 ******************************************************************/

float HiddenActiv[NUM_HIDDEN];                          // Activation factors for neurons
//float (*HiddenActiv) = new float[NUM_HIDDEN];

float OutputActiv[NUM_OUTPUTS];
//float (*OutputActiv) = new float[NUM_OUTPUTS];

//float HiddenWeights[NUM_INPUTS+1][NUM_HIDDEN];        // Weight factors for neuron inputs
float (*HiddenWeights)[NUM_HIDDEN] = new float[NUM_INPUTS+1][NUM_HIDDEN];

//float OutputWeights[NUM_HIDDEN+1][NUM_OUTPUTS];
float (*OutputWeights)[NUM_OUTPUTS] = new float[NUM_HIDDEN+1][NUM_OUTPUTS];

//float HiddenDelta[NUM_HIDDEN];                          // Delta=Divergence target/actual
float (*HiddenDelta) = new float[NUM_HIDDEN];

//float OutputDelta[NUM_OUTPUTS];
float (*OutputDelta) = new float[NUM_OUTPUTS];

//float ChangeHiddenWeights[NUM_INPUTS+1][NUM_HIDDEN];  // change buffer for Weights
float (*ChangeHiddenWeights)[NUM_HIDDEN] = new float[NUM_INPUTS+1][NUM_HIDDEN];

float ChangeOutputWeights[NUM_HIDDEN+1][NUM_OUTPUTS];
//float (*ChangeOutputWeights)[NUM_OUTPUTS] = new float[NUM_HIDDEN+1][NUM_OUTPUTS];


/******************************************************************
   Training Test Patterns
 ******************************************************************/

#define  X      1
#define  TEN_0  0,0,0,0,0,0,0,0,0,0
#define  TEN_1  1,1,1,1,1,1,1,1,1,1

//-----------------------------------------------------------------
//  Input patterns
//-----------------------------------------------------------------

byte Input[MAX_PATTERNS + 1][NUM_INPUTS] =
{
   {  0, 0, 0, 0, X, X, 0, 0, 0, 0,  // 0 ="0"
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, 0, 0, 0, 0,  // 1
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, 0, 0, 0, 0,  // 2
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, X, X, X, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, 0, 0, 0, 0,  // 3
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, X, 0, 0,  // 4
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, X, 0, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, 0, X, 0, 0, 0,
      0, 0, X, X, X, X, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, X, X, X, X, X, X, 0, 0,  // 5
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, X, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,  // 6
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, X, X, X, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, X, X, X, X, X, X, 0, 0,  // 7
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, 0, 0, 0, 0,  // 8
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, X, X, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, 0, 0, 0, 0,  // 9
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, X, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, X, X, 0, 0, 0, 0, 0,  // 10 ="0"
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, X, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, X, X, 0, 0, 0, 0, 0,  // 11
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, X, X, 0, 0, 0, 0, 0,  // 12
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, X, X, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, X, X, 0, 0, 0, 0, 0,  // 13
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, X, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, X, 0, 0, 0,  // 14
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, X, 0, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, 0, X, 0, 0, 0, 0,
      0, X, X, X, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, X, X, X, X, X, X, 0, 0, 0,  // 15
      0, X, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, X, X, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, X, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, X, X, X, 0, 0, 0, 0,  // 16
      0, 0, X, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, X, X, X, 0, 0, 0, 0, 0,
      0, X, 0, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, X, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, X, X, X, X, X, X, 0, 0, 0,  // 17
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, X, X, 0, 0, 0, 0, 0,  // 18
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, X, X, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, X, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, X, X, 0, 0, 0, 0, 0,  // 19
      0, 0, X, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, X, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, X, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, X, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, X, X, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,  // 20="0"
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 21="1"
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,  // 22="2"
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,  // 23="3"
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 24="4"
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,  // 25="5"
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,   // 26="6"
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,  // 27="7"
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,  // 28="8"
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, 0, 0, 0,  // 29="9"
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, X, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, X, X, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },



   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, X, X, 0, 0, 0, 0, 0, 0,  // 30="0"
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, X, X, X, 0, 0, 0, 0, 0, 0,
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, X, 0, 0, 0, 0, 0, 0, 0,  // 31="1"
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0,  // 32
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      X, X, X, X, X, X, 0, 0, 0, 0
   },


   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0,  // 33
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0
   },


   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,  // 34
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, X, 0, 0, 0, 0, 0,
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, X, X, X, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      X, X, X, X, X, X, 0, 0, 0, 0,  // 35
      X, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      X, X, X, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, X, X, 0, 0, 0, 0, 0,  // 36
      0, X, 0, 0, 0, X, 0, 0, 0, 0,
      X, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      X, X, X, X, 0, 0, 0, 0, 0, 0,
      X, 0, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      X, X, X, X, X, X, 0, 0, 0, 0,  // 37
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, 0, X, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, 0, 0, 0, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, 0, 0, 0, 0,
      0, X, 0, 0, 0, 0, 0, 0, 0, 0
   },


   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0,  // 38
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0,
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, X, X, 0, 0, 0, 0, 0, 0,  // 39
      0, X, 0, 0, X, 0, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, X, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, X, X, X, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      X, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, X, X, X, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0,0, 0, 0, X, X, 0, 0,     // 40 ="0"
      0, 0, 0,0, 0, X, 0, 0, X, 0,
      0, 0, 0,0, X, 0, 0, 0, 0, X,
      0, 0, 0,0, X, 0, 0, 0, 0, X,
      0, 0, 0,0, X, 0, 0, 0, 0, X,
      0, 0, 0,0, X, 0, 0, 0, 0, X,
      0, 0, 0,0, X, 0, 0, 0, 0, X,
      0, 0, 0,0, X, 0, 0, 0, 0, X,
      0, 0, 0,0, 0, X, 0, 0, X, 0,
      0, 0, 0,0, 0, 0, X, X, 0, 0,
      0, 0, 0,0, 0, 0, 0, 0, 0, 0,
      0, 0, 0,0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, X, X, 0, 0,      // 41
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, X, X, 0, 0,      // 42
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, X, X, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, X, X, 0, 0,        // 43
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, X,       // 44
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, X, 0,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, X, 0,
      0, 0, 0, 0, X, X, X, X, X, X,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, X, X, X,        // 45
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, X, X, X, 0,        // 46
      0, 0, 0, 0, 0, X, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, X, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, X, X, X, X, X, X,        // 47
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, X, X, 0, 0,        // 48
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   },

   {  0, 0, 0, 0, 0, 0, X, X, 0, 0,        // 49
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, X, X, X, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, X, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   },

   
   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,     // 50 ="0"
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0     
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,      // 51
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0
     
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,      // 52
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, X, X, X
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,        // 53
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,       // 54
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, X, 0,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, X, 0,
      0, 0, 0, 0, X, X, X, X, X, X,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, X, X, X,        // 55
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, X, X, 0,        // 56
      0, 0, 0, 0, 0, X, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, X, 0, 0,
      0, 0, 0, 0, X, 0, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, X, X, X, X, X, X,        // 57
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, 0, X, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,        // 58
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0
   },

   {  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, X, X, 0, 0,        // 59
      0, 0, 0, 0, 0, X, 0, 0, X, 0,
      0, 0, 0, 0, X, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, X, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, X, X, X, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, 0, 0, 0, 0, 0, X,
      0, 0, 0, 0, X, 0, 0, 0, X, 0,
      0, 0, 0, 0, 0, X, X, X, 0, 0
   }


};


//-----------------------------------------------------------------
//  Output patterns
//-----------------------------------------------------------------

byte Target[MAX_PATTERNS + 1][NUM_OUTPUTS] =
{
   { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //0
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, //6
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1 }, //9

   { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //10
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, //11
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, //12
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 }, //13
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, //16
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1 }, //19

   { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //20
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, //21
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, //22
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 }, //23
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, //26
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1 }, //29

   { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //30
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, //36
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1 }, //39

   { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //40
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, //46
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1 }, //49

   { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //50
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, //56
   { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1 }  //59

};



//-----------------------------------------------------------------
//  Test Inputs
//-----------------------------------------------------------------


byte TestInputC01[NUM_INPUTS] =
{  // "1"
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

byte TestInputC02[NUM_INPUTS] =
{  // "2"
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, X, 0, 0, 0, 0, 0, 0,
   0, 0, 0, X, 0, 0, 0, 0, 0, 0,
   0, 0, 0, X, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

byte TestInputC03[NUM_INPUTS] = {  // "3"
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

byte TestInputC06[NUM_INPUTS] = {   //  "6"
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, X, 0, 0, 0, 0, 0, 0,
   0, 0, 0, X, 0, 0, 0, 0, 0, 0,
   0, 0, 0, X, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, X, 0, 0, 0, X, 0, 0,
   0, 0, 0, X, 0, 0, 0, X, 0, 0,
   0, 0, 0, X, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};



byte TestInputT1[NUM_INPUTS] = {
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Test pattern T1 1, untrained
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, X, 0, 0, 0, 0, 0, 0, 0, 0,
   0, X, 0, 0, 0, 0, 0, 0, 0, 0,
   0, X, 0, 0, 0, 0, 0, 0, 0, 0,
   0, X, 0, 0, 0, 0, 0, 0, 0, 0,
   0, X, 0, 0, 0, 0, 0, 0, 0, 0,
   0, X, 0, 0, 0, 0, 0, 0, 0, 0,
   0, X, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

byte TestInputT9[NUM_INPUTS] =  // Test pattern T9 9, untrained
{
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, X, 0, 0, 0, X, 0, 0,
   0, 0, 0, X, 0, 0, 0, X, 0, 0,
   0, 0, 0, X, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, 0, 0, 0, X, 0, 0,
   0, 0, 0, 0, X, X, X, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};


/*  ^^^^^^^^^^^^^^^     End NetConfiguration      ^^^^^^^^^^^^^^^^
 *****************************************************************/



/******************************************************************
   Tools
 ******************************************************************/

const char* lnUp = "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^";
const char* lnDn = "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv";

//-----------------------------------------------------------------
//  tool: millis to cstring
//-----------------------------------------------------------------
char * millis_to_strF(uint32_t ms, char * str) {
   const uint32_t d = 86400000; // 86400000 milliseconds in a day
   const uint32_t h = 3600000; // 3600000 milliseconds in an hour
   const uint32_t m = 60000; // 60000 milliseconds in a minute
   const uint32_t s =  1000; // 1000 milliseconds in a second
   uint32_t  Days = 0;
   uint32_t  Hours = 0;
   uint32_t  Mins = 0;
   uint32_t  Secs = 0;

   Days  = ms / d ;
   Hours = (ms % d) / h;
   Mins  = ((ms % d) % h) / m ;
   Secs  = (((ms % d) % h) % m) / s;

   sprintf(str, "%ld.%02ld:%02ld:%02ld", Days, Hours, Mins, Secs);
   return str;
}


//-----------------------------------------------------------------
// tool: NN: calc Activation + Net-Error
//-----------------------------------------------------------------

void computeActErr() {
   //--------------------------------------------------
   //  Compute hidden layer activations
   //--------------------------------------------------

   for ( i = 0 ; i < NUM_HIDDEN ; i++ ) {
      Accum = HiddenWeights[NUM_INPUTS][i] ;
      for ( j = 0 ; j < NUM_INPUTS ; j++ ) {
         Accum += Input[p][j] * HiddenWeights[j][i] ;
      }
      HiddenActiv[i] = 1.0 / (1.0 + exp(-Accum)) ;
   }

   //--------------------------------------------------
   //  Compute output layer activations +  errors
   //--------------------------------------------------

   for ( i = 0 ; i < NUM_OUTPUTS ; i++ ) {
      Accum = OutputWeights[NUM_HIDDEN][i] ;
      for ( j = 0 ; j < NUM_HIDDEN ; j++ ) {
         Accum += HiddenActiv[j] * OutputWeights[j][i] ;
      }
      OutputActiv[i] = 1.0 / (1.0 + exp(-Accum)) ;
   }
}


//-----------------------------------------------------------------
// tool: NN statistics log to terminal monitor
//-----------------------------------------------------------------

void PrintStatistixx() {
   char str[30];

   Serial.print ("TrainingCycle: "); Serial.print (TrainingCycle);
   if (TrainingCycle > 0) {
      Serial.print ("  Error=");      Serial.print (Error, 5);
      Serial.print ("  ThrSuccess="); Serial.print (ThrSuccess, 5);
      Serial.print ("  runtime (d.h:m:s)=");
      Serial.print ( millis_to_strF(millis() - timestamp, str) );
   }
}



//-----------------------------------------------------------------
// tool: NN Inputs/Outputs to terminal monitor
//-----------------------------------------------------------------

void PrintNetPattern()
{
   char buf[10];

   for ( p = 0 ; p < MAX_PATTERNS ; p++ ) {
      Serial.println();
      //Serial.print ("  Training Pattern: ");

      sprintf(buf, "%3d", p);
      Serial.print (buf);
      Serial.print (" In:");
      for (int i = 0 ; i < NUM_INPUTS ; i++ ) {
         if (!(i % 10)) {
            yield();
            Serial.println();
            Serial.print("    ");
         }
         Serial.print (Input[p][i], DEC);
      }
      Serial.print (" Targ: ");
      for (int i = 0 ; i < NUM_OUTPUTS ; i++ ) {
         Serial.print (Target[p][i], DEC);
         Serial.print ("");
      }

      computeActErr();

      Serial.print (" Out: ");
      for (int i = 0 ; i < NUM_OUTPUTS ; i++ ) {
         Serial.print (OutputActiv[i], 3);
         Serial.print (" ");
      }
      Serial.println();
      Serial.print ("        ROUNDED OUT: ");
      int OutputBin[NUM_OUTPUTS];
      for (int i = 0 ; i < NUM_OUTPUTS ; i++ ) {
         OutputBin[i]=(int)round(OutputActiv[i]);
         Serial.print(OutputBin[i]);
      }
      Serial.print (" Dec: ");
      int Dec=0;
      for (int i = 0 ; i < NUM_OUTPUTS ; i++ ) {
         Dec=Dec+(OutputBin[i] << (NUM_OUTPUTS - i - 1));
      }
      Serial.print(Dec);
   }
   Serial.println();
   Serial.println(lnUp);
   PrintStatistixx();

   Serial.println();
#ifdef DEBUG
   Serial.println ("DEBUG MODE: ACTIVE! ");
#endif

}

/******************************************************************
   HAL (Heuristic Algorithmic Layer): Initialize
 ******************************************************************/

int initializeWeights() {
   //--------------------------------------------------
   //  Initialize HiddenWeights and ChangeHiddenWeights
   //--------------------------------------------------
   for ( i = 0 ; i < NUM_HIDDEN ; i++ ) {
      for ( j = 0 ; j <= NUM_INPUTS ; j++ ) {
         ChangeHiddenWeights[j][i] = 0.0 ;
         Rando = float(random(100)) / 100;
         HiddenWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
      }
   }

   //--------------------------------------------------
   //  Initialize OutputWeights and ChangeOutputWeights
   //--------------------------------------------------
   for ( i = 0 ; i < NUM_OUTPUTS ; i ++ ) {
      for ( j = 0 ; j <= NUM_HIDDEN ; j++ ) {
         ChangeOutputWeights[j][i] = 0.0 ;
         Rando = float(random(100)) / 100;
         OutputWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;
      }
   }
}

/******************************************************************
   HAL: Backpropagation Learning
 ******************************************************************/



//******************************************************************
int BP_Learning() {

RESTART:
   initializeWeights();

   Serial.println();
   Serial.println("Initial/Untrained Outputs: ");

   PrintNetPattern();

   //--------------------------------------------------
   //  Begin training
   //--------------------------------------------------
   for ( TrainingCycle = 1 ; TrainingCycle < MAXLOOPS ; TrainingCycle++) {

      //--------------------------------------------------
      //  Randomize order of training patterns
      //--------------------------------------------------

      for ( p = 0 ; p < MAX_PATTERNS ; p++) {
         q = random(MAX_PATTERNS);
         r = RandomizedIndex[p] ;
         RandomizedIndex[p] = RandomizedIndex[q] ;
         RandomizedIndex[q] = r ;
      }
      Error = 0.0 ;

      //--------------------------------------------------
      //  Cycle through each training pattern in the randomized order
      //--------------------------------------------------
      for ( q = 0 ; q < MAX_PATTERNS ; q++ ) {
         p = RandomizedIndex[q];

         //--------------------------------------------------
         //  Compute hidden layer activations
         //--------------------------------------------------

         for ( i = 0 ; i < NUM_HIDDEN ; i++ ) {

            Accum = HiddenWeights[NUM_INPUTS][i] ;
            for ( j = 0 ; j < NUM_INPUTS ; j++ ) {
               Accum += Input[p][j] * HiddenWeights[j][i] ;
            }
            HiddenActiv[i] = 1.0 / (1.0 + exp(-Accum)) ;
         }

         //--------------------------------------------------
         //  Compute output layer activations + calculate errors
         //--------------------------------------------------
         for ( i = 0 ; i < NUM_OUTPUTS ; i++ ) {

            Accum = OutputWeights[NUM_HIDDEN][i] ;
            for ( j = 0 ; j < NUM_HIDDEN ; j++ ) {
               Accum += HiddenActiv[j] * OutputWeights[j][i] ;
            }
            OutputActiv[i] = 1.0 / (1.0 + exp(-Accum)) ;
            OutputDelta[i] = (Target[p][i] - OutputActiv[i]) * OutputActiv[i] * (1.0 - OutputActiv[i]) ;
            Error += 0.5 * (Target[p][i] - OutputActiv[i]) * (Target[p][i] - OutputActiv[i]) ;
         }

         //--------------------------------------------------
         //  Backpropagate errors to hidden layer
         //--------------------------------------------------

         for ( i = 0 ; i < NUM_HIDDEN ; i++ ) {
            Accum = 0.0 ;
            //yield();  // esp8266: yield() !
            for ( j = 0 ; j < NUM_OUTPUTS ; j++ ) {
               Accum += OutputWeights[i][j] * OutputDelta[j] ;
            }
            HiddenDelta[i] = Accum * HiddenActiv[i] * (1.0 - HiddenActiv[i]) ;
         }


         //--------------------------------------------------
         //  Update Inner --> Hidden Weights
         //--------------------------------------------------

         for ( i = 0 ; i < NUM_HIDDEN ; i++ ) {
            //yield();  // esp8266: yield() !
            ChangeHiddenWeights[NUM_INPUTS][i] = LearningRate * HiddenDelta[i] + Momentum * ChangeHiddenWeights[NUM_INPUTS][i] ;
            HiddenWeights[NUM_INPUTS][i] += ChangeHiddenWeights[NUM_INPUTS][i] ;
            for ( j = 0 ; j < NUM_INPUTS ; j++ ) {
               ChangeHiddenWeights[j][i] = LearningRate * Input[p][j] * HiddenDelta[i] + Momentum * ChangeHiddenWeights[j][i];
               HiddenWeights[j][i] += ChangeHiddenWeights[j][i] ;
            }
         }

         //--------------------------------------------------
         //  Update Hidden --> Output Weights
         //--------------------------------------------------

         for ( i = 0 ; i < NUM_OUTPUTS ; i ++ ) {

            ChangeOutputWeights[NUM_HIDDEN][i] = LearningRate * OutputDelta[i] + Momentum * ChangeOutputWeights[NUM_HIDDEN][i] ;
            OutputWeights[NUM_HIDDEN][i] += ChangeOutputWeights[NUM_HIDDEN][i] ;
            for ( j = 0 ; j < NUM_HIDDEN ; j++ ) {
               ChangeOutputWeights[j][i] = LearningRate * HiddenActiv[j] * OutputDelta[i] + Momentum * ChangeOutputWeights[j][i] ;
               OutputWeights[j][i] += ChangeOutputWeights[j][i] ;
            }
         }
      }

      //--------------------------------------------------
      //  Every (n) cycles send to terminal for display
      //--------------------------------------------------

      ReportInterval = ReportInterval - 1;
      if (ReportInterval == 0)
      {
         Serial.println();
         Serial.println(lnDn);
         PrintStatistixx();

         PrintNetPattern();

         if (TrainingCycle == 1)
         {
            ReportInterval = REPORT_N - 1;
         }
         else
         {
            ReportInterval = REPORT_N;
         }
      }


      //--------------------------------------------------
      // If (error rate < pre-determined threshold) => end
      //--------------------------------------------------

      // if caught in local minimum or oscillating:
      if ( Error > 1.0 && TrainingCycle > 3000 ) {
         initializeWeights();
         TrainingCycle = 2;
         goto RESTART;
      }
      if ( Error > 0.6 && TrainingCycle > 20000 ) {
         initializeWeights();
         TrainingCycle = 3;
         goto RESTART;
      }


      // success?
      if ( Error < ThrSuccess ) return 0 ;  // 0:  training OK: no err
   }
   return -1;              // -1: loop limit reached: no success
}





/******************************************************************
   HAL (Heuristic Algorithmic Layer): Recognize arbitrary patterns
 ******************************************************************/

void InputPatternRecognition(byte *TestInput ) {  // TestInput[NUM_INPUTS]
   char buf[10];

   for (int i = 0; i < NUM_INPUTS; i++) {
      Input[MAX_PATTERNS][i] = TestInput[i];  // store inputs in extra pattern array slot
   }

   Serial.println();
   Serial.print ("  Test Pattern ");

   Serial.print (" In:");
   for (int i = 0 ; i < NUM_INPUTS ; i++ ) {
      if (!(i % 10)) {
         yield();
         Serial.println();
         Serial.print("    ");
      }
      Serial.print (Input[MAX_PATTERNS][i], DEC);
   }

   computeActErr();

   Serial.print (" Out: ");
   for (int i = 0 ; i < NUM_OUTPUTS ; i++ ) {
      Serial.print (OutputActiv[i], 3);
      Serial.print (" ");
   }
   Serial.println();
   Serial.print ("       ROUNDED OUT: ");
   int OutputBin[NUM_OUTPUTS];
   for (int i = 0 ; i < NUM_OUTPUTS ; i++ ) {
      OutputBin[i]=(int)round(OutputActiv[i]);
      Serial.print(OutputBin[i]);
   }
   Serial.print (" Dec: ");
   int Dec=0;
   for (int i = 0 ; i < NUM_OUTPUTS ; i++ ) {
      Dec=Dec+(OutputBin[i] << (NUM_OUTPUTS - i - 1));
   }
   Serial.print(Dec);
   Serial.println();
   Serial.println(lnUp);

}




volatile static int8_t StateMode, ModeLearn = 1, ModeDetect = 0, ModePause = 0;

/******************************************************************
   setup
 ******************************************************************/

void setup() {
   Serial.begin(230400); // <<<<<<<<<<<<<<<<< !!
   display.begin();          // Initialize screen
   display.fillScreen(HX8357_BLACK);

   delay(1000);
   timestamp = millis();

   randomSeed(analogRead(A0));
   ReportInterval = 1;
   for ( p = 0 ; p < MAX_PATTERNS ; p++ ) {
      RandomizedIndex[p] = p ;
   }
}



/******************************************************************
   loop
 ******************************************************************/
void loop() {
   volatile static int8_t result = -1;

   //--------------------------------------------------
   // start Backpropagation Learning (BP)
   //--------------------------------------------------
   result = BP_Learning();

#ifdef DEBUG
   Serial.println ("DEBUG MODE: ACTIVE! ");
#endif

   if (result == 0) {               // 0:  training OK: no err
      // Error < ThrSuccess

      Serial.println ();
      Serial.println(lnDn);
      PrintStatistixx();
      PrintNetPattern();
      Serial.println ();
      Serial.println ();
      Serial.println ("Training Set Solved! ");
      Serial.println ("-------------------- ");
      Serial.println ();
      Serial.println ();

   }

   else if (result == -1) {      // -1: loop limit reached: no success

      Serial.println(lnDn);
      PrintStatistixx();
      PrintNetPattern();
      Serial.println ();
      Serial.println ();
      Serial.println ("loop limit reached - Training aborted! ");
      Serial.println ("-------------------------------------- ");
      Serial.println ();
      Serial.println ();

   }


   // Recognize test patterns:

   Serial.println ("TestInputC01 \"1\" ");  // Test, debug
   InputPatternRecognition( TestInputC01 );
   Serial.println ();
   Serial.println ();

   Serial.println ("TestInputC06 \"6\" ");  // Test, debug
   InputPatternRecognition( TestInputC06 );
   Serial.println ();
   Serial.println ();

   Serial.println ("TestInputT1, untrained 1");  // Test T1, debug
   InputPatternRecognition( TestInputT1 );
   Serial.println ();
   Serial.println ();

   Serial.println ("TestInputT9, untrained 9");  // Test T9, debug
   InputPatternRecognition( TestInputT9 );
   Serial.println ();
   Serial.println ();

#ifdef DEBUG
   Serial.println ("DEBUG MODE: ACTIVE! ");
#endif


   while (true) {        // debug: loop forever
      delay(1000); // training finished
   }

}

http://robotics.hobbizine.com/pics/nn3layer.png
https://datasolut.com/wp-content/uploads/2019/11/neuronale_netze_aktivierung-1024x485.png
http://robotics.hobbizine.com/arduinoann.html
www.cs.bham.ac.uk/~jxb/NN/nn.html
https://novustat.com/statistik-blog/kuenstliches-neuronales-netz-einfach-erklaert.html
https://datasolut.com/neuronale-netzwerke-einfuehrung/

Das eigentlich Aufwändige ist aber die Zusammenstellung von Lern-Sets und deren Training, nicht die Skalierung des Arduino-Grundprogramms für mehr Inputs und Outputs, bei mir:
const int NUM_INPUTS = 120; // <<<<< !!
const int NUM_HIDDEN = 80; // esp8266: 35
const int NUM_OUTPUTS = 10; // <<<<< !!
const int MAX_PATTERNS = 60; // ESP32: max. 250, esp8266: 40

Je mehr Reaktionsweisen bzw. I/O-Verknüpfungen gelernt werden sollen, desto schneller steigt auch der Speicherbedarf (überproportional, fast exponentiell) und die Lern/Trainingszeit ebenfalls (teilw. jetzt schon einige Stunden).
Wenn du daher anfangen willst, fang am besten mit einem Raspberry Pi 3 oder 4 an, ich würde als GUI IDE den qt creator mit C++ dafür verwenden, samt full-HD-color-HDMI-Monitor.
Dann hättest du auch jede Menge Platz auf dem Bildschirm für die GUI form und für jede Menge virtuelle buttons, grids, gauges, lists, text boxes, und eine graphic canvas für Bilder oder Video, falls du so etwas brauchst.

Und klar kannst du auch Roboterprogramme damit füttern, ich selber habe auch schon Lego-Mindstorms NXT (per NXC) damit programmiert, für einen einfachen Hindernis-Parcour oder Linienfolger.

_________________
αΓγΔδεζηΘθικλµνΞξΠπϱΣστΦφχΨψΩω∫∂ħ⋅⟨⟩√≤≥∞∈


Zuletzt bearbeitet von terminus am 25. Apr 2022 13:23, insgesamt einmal bearbeitet
analist



Anmeldungsdatum: 23.03.2022
Beiträge: 25

Beitrag analist Verfasst am: 25. Apr 2022 02:37    Titel: Antworten mit Zitat

Hallo @terminus
ist ja echt toll.
Aber auf meinen alten Arduino haette ich nicht mal dieses ganze Prg. laden koennen.
Die neueren scheinen da besser zu sein.

Ich habe eine Schwierigkeit mit meinem Lernalgorithmus.
Kannst Du mir da helfen?

PS
Ein FPGA-Board ist guenstiger als ein Ras und auch leistungsfaehiger.
Ich habe Eins von denen
https://www.xilinx.com/products/silicon-devices/fpga/what-is-an-fpga.html
Aber leider noch nicht zum laufen gebracht.
terminus



Anmeldungsdatum: 17.10.2020
Beiträge: 555

Beitrag terminus Verfasst am: 25. Apr 2022 09:44    Titel: Antworten mit Zitat

hallo,
ja, das stimmt, es ist IMO mindestens ein Arduino Due nötig (den gibts ja auch schon bald 20 Jahre). Geht zwar auch kleiner, hat dann aber zu wenig KI.
Oder eben einen der neuen MCUs mit M4 Kern oder ESP32: letzteren nehme ich am liebsten, weil es ihn
a) als Adafruit Feather gibt, mit großen, einfach aufsteckbaren Featherwing Touch-Displays,
b) der ESP32 ein 32-bit Dual-Core ist und unterstützt pre-emptives MT mit abgespecktem, verändertem pthread/std::thread per freeRTOS (SEEEHR wichtig!!),
c) er einen fp-Coprozessor hat,
d) unterstützt mehr Standard-C/C++-Libs wie aus stdio.h oder std::, und
e) weil er eingebautes WiFi für Web-Apps und IoT hat (auch zum Roboter steuern per Web-App).
Nachteil bei Due/M4: eben KEIN preemptives MT.
Vorteil bei RPi: viel schneller, viel mehr RAM, 4 Kerne, volles, komplettes POSIX-C und C++ (nicht abgespeckt, inkl. vollem pthread + std::thread), ein kompletter Multiuser-Multitasking-Mini-PC eben.

Für kleinere Roboter-Projekte ohne extrem viele Einzelmuster reichen solche 32-bit Arduinos ab ca. 80MHz und 80KB per C++ aber absolut aus.
(Anm.: ein Rpi 2B (!) ist im Durchschnitt etwa 4-7x schneller als ein ESP32 oder ein M4, gemessen mit einem gemischten Benchmark, und hat ~3000x so viel RAM)

Zu den Algorithmen kann ich dir nicht mehr sagen als auch in den verlinkten Tutorials (und etlichen anderen) steht, und es würde darauf hinauslaufen, im Prinzip alles abzuschreiben bzw. hier rein zu kopieren - was wenig Sinn macht. Ich habe mir seinerzeit auch 2 komplette Bücher zu dem Thema KI und NN gekauft.
Das Thema ist schlicht zu kompliziert, um zu versuchen, es hier noch kürzer darzustellen, da musst du dich also durchwühlen.

Zu FPGAs kann ich nichts sagen, sie sind aber IMO nicht geeigneter als die M4 oder ESP32 oder ggf. ein RPi 3 oder 4, es geht ja auch um GPIO/Device-Libs, Programmier-Tools und Example-Code (und am besten mit Wechsel-Display: ein 15-20"er zur stationären Entwicklung, ein optionaler 10"er für den mobilen Einsatz). Den RPi würde ich auch immer lokal programmieren, nicht über externe PCs.

_________________
αΓγΔδεζηΘθικλµνΞξΠπϱΣστΦφχΨψΩω∫∂ħ⋅⟨⟩√≤≥∞∈
analist



Anmeldungsdatum: 23.03.2022
Beiträge: 25

Beitrag analist Verfasst am: 26. Apr 2022 10:07    Titel: Antworten mit Zitat

Hallo,
ich hatte ein FPGA ja auch nur vorgeschlagen, um die aufwendigen Matrixoperationen "auszulagern".
Damit koennte man "echte" und nicht nur "virtuelle" Neuronen arbeiten lassen.
Das wuerde einen sehr grossen Vorteil in der Schnelligkeit auch mit wenig CPU-Leitung ergeben.

Das geht allerdings nur fuer nicht sehr grosse Netzwerke, weil sonst das FPGA einfach zu teuer wird.

Und die neuen Analogcomputer sind dafuer auch noch zu teuer.
terminus



Anmeldungsdatum: 17.10.2020
Beiträge: 555

Beitrag terminus Verfasst am: 26. Apr 2022 10:39    Titel: Antworten mit Zitat

wie schon in einer PN geschrieben, müssen Netzarchitekturen für Training und Betrieb identisch sein.
Das Erstellen der Architektur ist vergleichsweise schnell zu machen, auch die Programmierung der Sensor-Inputs und Outputs ist wschl überschaubar (aber Plattform-abhängig).
Dann kommt aber das Erstellen von Lernsets und anschl. Training, und das ist in jedem Falle die Basis für das Funktionieren und auch das bei weitem Aufwändigste - die Ausführung hinterher geht vergleichsweise quasi mit einem Wimpernschlag.
Wenn Trainingsdaten existieren, muss man sie speichern und zurücklesen können, und dann kann man sie auch auf rein ausführende, bislang noch nicht trainierte Systeme übertragen, hier sind nur die Realtime-Bedingungen limitierend (ob die Reaktion auf einen Reiz zwingend innerhalb von sec, ms, µs, oder ns erfolgen muss), was wiederum die Anforderungen an die Ausführungshardware bestimmt: ein RPi3B mit pthread highPrio-RR- Threads (und notfalls reservierten cpu-Kernen, damit der Linux-Kern nicht dazwischenfunkt) reicht IMO für Hobbyanwendungen dicke aus. Auch ein ESP32 kann dicke ausreichend sein. Muss man beide ntl auch erst mal richtig programmieren.

Wenn du andererseits den KI-Teil "auslagern" willst, brauchst du auch eine schnelle Kommunikation, um die I/O-Daten zwischen Robot-Basisplattform und "KI-Chip" hin und her zu übertragen, das wäre dann schon wieder ein doppelter Aufwand (und kostet auch wieder zusätzliche Zeit).

Auch professionelle KI-Deeplearning-Systeme (IBM etc) machen Training und Ausführung u.U. mit 2 verschiedenen, jew. dafür optimierten Chips: das wäre für mich als Hobbyist aber zu aufwändig, ich würde alles auf 1 und demselben System nachen, um Programmierarbeit und Entwicklungszeit zu sparen.

_________________
αΓγΔδεζηΘθικλµνΞξΠπϱΣστΦφχΨψΩω∫∂ħ⋅⟨⟩√≤≥∞∈
analist



Anmeldungsdatum: 23.03.2022
Beiträge: 25

Beitrag analist Verfasst am: 26. Apr 2022 21:09    Titel: Antworten mit Zitat

Das hier und mein privates Projekt haben nichts mit einander zu tun.

Das board das ich habe, hat einen CPU-Teil und einen FPGA-Teil und Kommunikation ist wirklich nicht so wichtig.

In der Lernfase gibt es normaler weise keine zeitkritischen Sachen und wenn das uebertragen der ganzen Werte von den Gewichten laenger dauert, dann macht das gar nichts.

In der Produktion "muss" die CPU fasst gar nichts mehr machen, weil der FPGA-Teil Ein- und Ausgaenge selber bedient.

Aber leider gibt es dafuer noch sehr wenig "Tutorials". Ich hab zumindest keine gefunden und der Werkzeugkasten ist auch sehr begrenzt.

Also wenn Du mehr als Copy-Paste machen willst, koennte das die Herausforderung sein.
Nur dafuer brauch man eine recht hohe Frust-toleranz und Ausdauer.

Deswegen zum Anfangen nicht unbedingt gut geeignet.
Neue Frage »
Antworten »
    Foren-Übersicht -> Off-Topic