Neuronales Netzwerk

minzee

Bekanntes Mitglied
Hi 🙂

Ich hatte heute mit jemanden zusammengesessen, um unser erstes neuronales Netzwerk zu programmieren. Das scheint doch ziemlich komplex zu sein. In einer allerersten ungetesteten Version sind wir zu dem folgenden Ergebnis gekommen:

Java:
class Neuron
{
   private double o;
   private Neuron[] neurons;
   private double[] weights;
   private Func propagationFunc;
   private Func activationFunc;
   private Func outputFunc;

   /**
    * Input-Neuron
    */
   public Neuron(Func propagationFunc, Func activationFunc, Func outputFunc)
   {
      this.propagationFunc = propagationFunc;
      this.activationFunc = activationFunc;
      this.outputFunc = outputFunc;
   }
   /**
    * Output-Neuron
    * @param Neuron[] neurons Eingehende Neuronen
    * @param double[] weights Gewichte der eigehenden Verbindungen
    */
   public Neuron(Neuron[] neurons, double[] weights, Func propagationFunc, Func activationFunc, Func outputFunc)
   {
      this.neurons = neurons;
      this.weights = weights;
      this.propagationFunc = propagationFunc;
      this.activationFunc = activationFunc;
      this.outputFunc = outputFunc;
   }
   /**
    * Wert für ein Input-Neuron.
    * @param double value
    */
   public void in(double value)
   {
      this.o = value;
   }
   /**
    * Ausgabe des Neurons.
    * @return double
    */
   public double out()
   {
      if(neurons != null)
      {
         // Propagierungsfunktion => net
         double net = 0.0;
         for(int i = 0; i < neurons.length; ++i)
         {
            net += weights[i] * neurons[i].out();
         }
         if(propagationFunc != null)
         {
            net = propagationFunc.calc(net);
         }
         
         // Aktivierungsfunktion => a
         double a = (activationFunc != null)
            ?  activationFunc.calc(net)
            :  net;
         
         // Outputfunktion => o
         double o = (outputFunc != null)
            ?  outputFunc.calc(a)
            :  a;
      }
      return o;
   }
}
class Main
{
   public static void main(String[] args)
   {
      double[] values = {1.0, 0.0, 1.0};
      double[] weights = {0.6, 0.6, 0.6};
      
      double threshold = 0.5;
      double value = 1.0;
      Func f = new ThresholdValueFunc(threshold, value); // 1
      
      // Schicht 0:
      Neuron n0 = new Neuron(f, null, null);
      Neuron n1 = new Neuron(f, null, null);
      
      // Schicht 1:
      Neuron[] level0 = {n0, n1};
      Neuron n2 = new Neuron(level0, new double[]{0.02, 0.12}, f, null, null);
      Neuron n3 = new Neuron(level0, new double[]{0.03, 0.13}, f, null, null);
      
      // Schicht 2:
      Neuron n4 = new Neuron(new Neuron[]{n0, n1, n2, n3}, new double[]{0.04, 0.14, 0.24, 0.34}, f, null, null);
      
      n0.in(1.0);
      n1.in(1.0);
      System.out.println(n4.out());
   }
}
ThresholdValueFunc und andere mögliche Func-Klassen gibts hier:
Java:
interface Func
{
   public double calc(double value);
}
class ThresholdValueFunc implements Func
{
   private double threshold;
   private double thresholdValue;
   
   /**
    * @param double value Der Wert, der ausgegeben wird, wenn calc einen Wert > threshold berechnet.
    */
   public ThresholdValueFunc(double threshold, double thresholdValue)
   {
      this.threshold = threshold;
      this.thresholdValue = thresholdValue;
   }
   /**
    * Gleiche Anzahl Elemente!
    * @return double
    */
   public double calc(double value)
   {
      if(value > threshold)
      {
         return thresholdValue;
      }
      return 0.0;
   }
}
class ThresholdValuesFunc implements Func
{
   private double threshold;
   private double upperThresholdValue;
   private double lowerThresholdValue;
   
   /**
    * @param double value Der Wert, der ausgegeben wird, wenn calc einen Wert > threshold berechnet.
    */
   public ThresholdValuesFunc(double threshold, double upperThresholdValue, double lowerThresholdValue)
   {
      this.threshold = threshold;
      this.upperThresholdValue = upperThresholdValue;
      this.lowerThresholdValue = lowerThresholdValue;
   }
   /**
    * Gleiche Anzahl Elemente!
    * @return double
    */
   public double calc(double value)
   {
      if(value > threshold)
      {
         return upperThresholdValue;
      }
      return lowerThresholdValue;
   }
}
class SignumFunc implements Func
{
   private double threshold;
   
   public SignumFunc(double threshold)
   {
      this.threshold = threshold;
   }
   /**
    * @return double
    */
   public double calc(double value)
   {
      if(value > threshold)
      {
         return 1.0;
      }
      return -1.0;
   }
}
class LinearFunc implements Func
{
   private double upperThreshold;
   private double lowerThreshold;
   private double upperThresholdValue;
   private double lowerThresholdValue;
   
   /**
    * Garantiert, dass es keine Sprünge in der Kurve gibt.
    */
   public LinearFunc(double upperThreshold, double lowerThreshold)
   {
      this.upperThresholdValue = this.upperThreshold = upperThreshold;
      this.lowerThresholdValue = this.lowerThreshold = lowerThreshold;
   }
   /**
    * Sprünge in der Kurve möglich.
    */
   public LinearFunc(double upperThreshold, double lowerThreshold, double upperThresholdValue, double lowerThresholdValue)
   {
      this.upperThreshold = upperThreshold;
      this.lowerThreshold = lowerThreshold;
      this.upperThresholdValue = upperThresholdValue;
      this.lowerThresholdValue = lowerThresholdValue;
   }
   /**
    * @return double
    */
   public double calc(double value)
   {
      if(value >= upperThreshold)
      {
         return upperThresholdValue;
      }
      if(value <= lowerThreshold)
      {
         return lowerThresholdValue;
      }
      return value;
   }
}
class SigmoidFunc implements Func
{
   private double delta;
   
   public SigmoidFunc(double delta)
   {
      this.delta = delta;
   }
   /**
    * @return double
    */
   public double calc(double value)
   {
      return 1.0 / (1.0 + Math.exp(-value) / delta); // e^(-sum)
   }
}
class TanHFunc implements Func
{
   /**
    * @return double
    */
   public double calc(double value)
   {
      return Math.tanh(value);
   }
}
Wir haben zuvor noch nie ein objektorientiert programmiertes neuronales Netzwerk gesehen. Darum würde mich interssieren, ob wir auf dem richtigen Weg sind. Ist die Programmierung soweit in Ordnung?
 
Sehr ruhig in diesem Thema. Hattet ihr noch nie mit neuronalen Netzen zu tun? Oder habe ich das nur irgendwie schlecht hier reingepostet?

Hier auch unser Versuch, einem Neuron das Lernen beizubringen:
Java:
class Neuron
{
   private double o;
   private Neuron[] neurons;
   private double[] weights;
   private Func propagationFunc;
   private Func activationFunc;
   private Func outputFunc;

   /**
    * Input-Neuron
    */
   public Neuron(Func propagationFunc, Func activationFunc, Func outputFunc)
   {
      this.propagationFunc = propagationFunc;
      this.activationFunc = activationFunc;
      this.outputFunc = outputFunc;
   }
   /**
    * Output-Neuron
    * @param Neuron[] neurons Eingehende Neuronen
    * @param double[] weights Gewichte der eigehenden Verbindungen
    */
   public Neuron(Neuron[] neurons, double[] weights, Func propagationFunc, Func activationFunc, Func outputFunc)
   {
      this.neurons = neurons;
      this.weights = weights;
      this.propagationFunc = propagationFunc;
      this.activationFunc = activationFunc;
      this.outputFunc = outputFunc;
   }
   /**
    * Wert für ein Input-Neuron.
    * @param double value
    */
   public void in(double value)
   {
      this.o = value;
   }
   /**
    * Ausgabe des Neurons.
    * @return double
    */
   public double out()
   {
      if(neurons != null)
      {
         // Propagierungsfunktion => net
         double net = 0.0;
         for(int i = 0; i < neurons.length; ++i)
         {
            net += weights[i] * neurons[i].out();
         }
         if(propagationFunc != null)
         {
            net = propagationFunc.calc(net);
         }
         
         // Aktivierungsfunktion => a
         double a = (activationFunc != null)
            ?  activationFunc.calc(net)
            :  net;
         
         // Outputfunktion => o
         double o = (outputFunc != null)
            ?  outputFunc.calc(a)
            :  a;
      }
      return o;
   }
   /**
    * Versucht die Gewichte so einzustellen, dass die Ausgabe bei allen möglichen Eingängen immer den Mustern entspricht.
    * Anzahl Elemente von patterns muss mit patternOs übereinstimmen.
    * @param int nMax maximale Anzahl an Lern-Durchläufen
    * @param double eta Lernrate
    * @param double[][] patterns Muster-Eingänge
    * @param double[] patternOs erwartete Muster-Ausgänge
    */
   public void learn(int nMax, double eta, double[][] patterns, double[] patternOs)
   {      
      int l = patterns[0].length;
      Random r = new Random();
      for(int i = 0; i < nMax; ++i)
      {
         // Wie im Buch wird hier per Zufall ein Muster ausgesucht:
         int k = r.nextInt(patternOs.length); // [0, length)
         double target = patternOs[k]; // Sollwert
         
         // Propagierungsfunktion => net
         double net = 0.0;
         for(int j = 0; j < l; ++j)
         {
            net += weights[j] * patterns[k][j];
         }
         if(propagationFunc != null)
         {
            net = propagationFunc.calc(net);
         }
         
         // Aktivierungsfunktion => a
         double a = (activationFunc != null)
            ?  activationFunc.calc(net)
            :  net;
         
         // Outputfunktion => o
         double o = (outputFunc != null)
            ?  outputFunc.calc(a)
            :  a;
            
         // Sollwert mit Istwert vergleichen:
         if(target != o)
         {
            // Abweichung:
            double delta = target - o;
            
            // Gewichte aller aktiven Eingänge anpassen:
            for(int j = 0; j < l; ++j)
            {
               if(patterns[k][j] > 0.0) // wenn ein aktiver Eingang
               {
                  weights[j] += weights[j] * eta * delta; // Gewicht zu einem bestimmten Eingang anpassen
               }
            }
         }

         // zum Testen:
         System.out.print("i = " + i + ", k = " + k + ": ");
         for(int x = 0; x < weights.length; ++x)
         {
            System.out.print(weights[x] + " ");
         }
         System.out.println();
      }
   }
}
class Main
{
   public static void main(String[] args)
   {      
      double threshold = 0.5;
      double value = 1.0;
      Func f = new ThresholdValueFunc(threshold, value);
           
      // Schicht 0:
      Neuron n0 = new Neuron(f, null, null);
      Neuron n1 = new Neuron(f, null, null);
      Neuron n2 = new Neuron(f, null, null);
      
      // Schicht 1:
      Neuron n3 = new Neuron(new Neuron[]{n0, n1, n2}, new double[]{0.6, 0.6, 0.6}, f, null, null);
      
      int nMax = 20;
      double eta = 0.5;
      double[][] patterns =
      {
         // 0    1    2
         {0.0, 0.0, 0.0},
         {1.0, 0.0, 0.0},
         {0.0, 1.0, 0.0},
         {1.0, 1.0, 0.0},
         {0.0, 0.0, 1.0},
         {1.0, 0.0, 1.0},
         {0.0, 1.0, 1.0},
         {1.0, 1.0, 1.0}
      };
      double[] patternOs = {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0};
      n3.learn(nMax, eta, patterns, patternOs);
   }
}
Würden uns über Rückmeldung freuen, ob das korrekt ist.
 
Zuletzt bearbeitet:
Vielleicht solltest du darüber nachdenken
  • dass das hier ein Anfänger-Forum ist; diesem Status entwächst du wohl so langsam (denke ich).
  • dass dies ein Java-Forum ist; fachliche Fragen zu Mathematik, Informatik, Fußball oder Steuerrecht solltest du vielleicht in einem entsprechenden Fachforum stellen. (Auch wenn sie manchmal ~ durch "Zufall" ~ hier ebenfalls beantwortet werden können.)
 
Ich kenn mich hier im Forum noch nicht aus. Aber mittlerweile habe ich die Vermutung, dass solche Themen in "Codeschnipsel u. Projekte" besser aufgehoben sind.

Ich glaube übrigens, dass die Neuron-Klasse nicht stimmt. Ich vermute, hier muss viel stärker zwischen Input- und Output-Neuronen unterschieden werden. Total fehlt mir der Durchblick bei Neuronen in Zwischenschichten. Keine Ahnung, ob man sie zu Input-, Output- oder ganz anderen Neuronen zuordnen kann bzw. muss. Könnt ihr mir dabei weiterhelfen?
 
Zuletzt bearbeitet:

Zurück
Oben