Hi,
Ich bin neulich mal auf jenes Video gestoßen:
Dachte mir, ok scheint jetzt nicht so abartig kompliziert zu werden - kann man ja mal versuchen.
Das ist mein Ansatz:
NeuralHelper
NerualNetwork
Neuron
Crappy-Implementierung:
Spoiler anzeigen
Idee ist dass ich quasi bei einem ResultNeuron EvaluateActivation aufrufe und dann rekursiv durchgerechnet wird. Das funktioniert alles auch soweit wie es soll.
Jetzt habe ich allerdings Verständnis Fragen um weiter zu kommen:
1. Habe mal nur eine Layer eingefügt, die gleiche zusammengesetzte Formen erkennen soll z.B. TOPLOOP als oberer Kringel von einer 8.
Da es allerdings nahezu unzumutbar ist da von Hand Gewichte ranzubauen - (28*28)<Bildgröße>*(6)<Anzahl der Subformen>*(10)<Anzahl möglicher Ziffern> habe ich eine Methode geschrieben die die Dinger zufällig wählt. Was die Frage bei mir aufwirft, ob man sich über solche Unterteilungsgeschichten überhaupt Gedanken machen muss, oder ob man einfach beliebig viele Neuronen in die Zwischenlayers haut und hofft, dass sie sich halt nach langem Training anpassen?
2. Wie ziehe ich aufgrund der Fehlerrate (Quadratsumme der Abweichung) Rückschlüsse darauf wie ich die Gewichte nachjustieren muss?
3. Ist die Vorgehensweise für ein allgemeines Konzept eines Neuralen Netzwerkes annehmbar?
Ich bin neulich mal auf jenes Video gestoßen:
Dachte mir, ok scheint jetzt nicht so abartig kompliziert zu werden - kann man ja mal versuchen.
Das ist mein Ansatz:
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace NeuralNumberNetwork {
- class NeuralHelper {
- public static double SigmoidFunction(double x) {
- double eX = Math.Pow(Math.E, x);
- return eX / (1 + eX);
- }
- public static String getRandomName(int length) {
- StringBuilder sb = new StringBuilder();
- Random rand = new Random();
- for (int i = 0; i < length; i++) {
- var ch = Convert.ToChar(49+rand.Next(20));
- sb.Append(ch);
- }
- return sb.ToString();
- }
- }
- }
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace NeuralNumberNetwork {
- abstract class NeuralNetwork {
- private List<Neuron> _InputNeurons = new List<Neuron>();
- public List<Neuron> InputNeurons { get { return _InputNeurons; } }
- private List<Neuron> _ResultNeurons = new List<Neuron>();
- public List<Neuron> ResultNeurons { get { return _ResultNeurons; } }
- public abstract void InitializeInputNeurons(Object inputData);
- public abstract void IntializeHiddenLayers();
- public abstract void IntializeResultNeurons();
- public Neuron runNetwork() {
- Neuron result = new Neuron() {Activation = 0 };
- for (int i = 0; i < ResultNeurons.Count; i++) {
- double neuronResult = ResultNeurons[i].EvaluateActivation();
- if (neuronResult > result.Activation) {
- result = ResultNeurons[i];
- }
- }
- return result;
- }
- public double evaluateFailure(int indexExpectedResult) {
- double sqaureSum = 0;
- for (int i = 0; i < ResultNeurons.Count; i++) {
- if (i == indexExpectedResult) {
- sqaureSum += Math.Pow(1 - ResultNeurons[i].Activation, 2);
- } else {
- sqaureSum += Math.Pow(0 - ResultNeurons[i].Activation, 2);
- }
- }
- return sqaureSum;
- }
- }
- }
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace NeuralNumberNetwork {
- class Neuron {
- public Neuron(String Name) {
- this.Name = Name;
- }
- public Neuron() {
- this.Name = NeuralHelper.getRandomName(20);
- }
- public Neuron(double Activation, bool isBase) {
- this.Activation = Activation;
- this.Name = NeuralHelper.getRandomName(20);
- this._IsBaseNeuron = isBase;
- }
- /// <summary>
- /// Name of the Neuron
- /// </summary>
- public String Name { get; }
- /// <summary>
- /// Is the degree of Activation of the Neuron
- /// </summary>
- private double _Activation = 0 ;
- public double Activation {
- get {
- return _Activation;
- }
- set {
- if (value > 1 || value < 0) {
- throw new ArgumentException("Neuron Activation has to be between 0 and 1.");
- }
- _Activation = value;
- }
- }
- /// <summary>
- /// The Bias of the Neuron
- /// </summary>
- public double Bias { get; set; }
- /// <summary>
- /// Determines if Neuron is in the starter layer of the network
- /// </summary>
- private readonly bool _IsBaseNeuron = false;
- public bool IsBaseNeuron { get { return _IsBaseNeuron; } }
- /// <summary>
- /// List of all the Predeccessor Neurons connected to the Neuron
- /// </summary>
- private List<Neuron> _PredeccessorNeurons = new List<Neuron>();
- public List<Neuron> PredeccessorNeurons { get { return _PredeccessorNeurons; } }
- /// <summary>
- /// List of all the Weights of the Predeccessor Neurons connected to this Neuron
- /// </summary>
- private List<double> _PredeccessorWeights = new List<double>();
- public List<double> PredeccessorWeights { get { return _PredeccessorWeights; } }
- /// <summary>
- /// Calculation of the Activation using recursion
- /// </summary>
- /// <returns></returns>
- public double EvaluateActivation() {
- //Evalutation only on inner Neurons need when they have not been evaluated before
- if (!this.IsBaseNeuron) {
- double weightedSum = 0;
- for (int i = 0; i < this.PredeccessorNeurons.Count; i++) {
- weightedSum += this.PredeccessorNeurons[i].EvaluateActivation() * this.PredeccessorWeights[i];
- }
- //Subtract the Bias
- weightedSum = weightedSum - Bias;
- //Calculate Sigmoid function (e^x)/(1+e^x), optimization possible
- this.Activation = NeuralHelper.SigmoidFunction(weightedSum);
- }
- return this.Activation;
- }
- public void randomizeWeights() {
- Random rand = new Random();
- //Order of weight to Neuron is mixed but does not matter since it is random anyways
- for (int i = 0; i < this.PredeccessorNeurons.Count; i++) {
- this.PredeccessorWeights.Add(rand.NextDouble());
- }
- }
- public override string ToString() {
- return this.Name + " :" + this.Activation;
- }
- }
- }
Crappy-Implementierung:
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace NeuralNumberNetwork.NumberNeuralNetwork {
- class NumberNeuralNetwork : NeuralNetwork {
- /// <summary>
- /// inputData is an Image
- /// </summary>
- /// <param name="inputData"></param>
- public override void InitializeInputNeurons(object inputData) {
- Bitmap bmp = (Bitmap)inputData;
- for (int x = 0; x < bmp.Size.Width; x++) {
- for (int y = 0; y < bmp.Size.Height; y++) {
- var activation = getActivation(bmp.GetPixel(x,y));
- this.InputNeurons.Add(new Neuron(activation,true));
- }
- }
- }
- public double getActivation(Color c) {
- return (c.R + c.B + c.G) / (3 * 255);
- }
- public override void IntializeResultNeurons() {
- for (int i = 0; i < 10; i++) {
- ResultNeurons.Add(new Neuron("Number_"+i+"_Neuron"));
- }
- }
- public override void IntializeHiddenLayers() {
- List<Neuron> Shapes = new List<Neuron>();
- Shapes.Add(new Neuron("TOPLOOP") );
- Shapes.Add(new Neuron("BOTTOMLOOP"));
- Shapes.Add(new Neuron("TOPLEFTEDGE"));
- Shapes.Add(new Neuron("TOPRIGHTEDGE"));
- Shapes.Add(new Neuron("BOTTOMLEFTEDGE"));
- Shapes.Add(new Neuron("BOTTOMTIGHTEDGE"));
- Shapes.Add(new Neuron("MIDDLELINE"));
- foreach (var item in ResultNeurons) {
- foreach (var middle in Shapes) {
- item.PredeccessorNeurons.Add(middle);
- }
- item.randomizeWeights();
- }
- foreach (var item in Shapes) {
- foreach (var input in InputNeurons) {
- item.PredeccessorNeurons.Add(input);
- }
- item.randomizeWeights();
- }
- }
- }
- }
Idee ist dass ich quasi bei einem ResultNeuron EvaluateActivation aufrufe und dann rekursiv durchgerechnet wird. Das funktioniert alles auch soweit wie es soll.
Jetzt habe ich allerdings Verständnis Fragen um weiter zu kommen:
1. Habe mal nur eine Layer eingefügt, die gleiche zusammengesetzte Formen erkennen soll z.B. TOPLOOP als oberer Kringel von einer 8.
Da es allerdings nahezu unzumutbar ist da von Hand Gewichte ranzubauen - (28*28)<Bildgröße>*(6)<Anzahl der Subformen>*(10)<Anzahl möglicher Ziffern> habe ich eine Methode geschrieben die die Dinger zufällig wählt. Was die Frage bei mir aufwirft, ob man sich über solche Unterteilungsgeschichten überhaupt Gedanken machen muss, oder ob man einfach beliebig viele Neuronen in die Zwischenlayers haut und hofft, dass sie sich halt nach langem Training anpassen?
2. Wie ziehe ich aufgrund der Fehlerrate (Quadratsumme der Abweichung) Rückschlüsse darauf wie ich die Gewichte nachjustieren muss?
3. Ist die Vorgehensweise für ein allgemeines Konzept eines Neuralen Netzwerkes annehmbar?
faxe1008