Grundlagen Java

Funktionen als Parameter

Sigmoid Funktion als Parameter für ein Neuron


/**
 * @(#)ISigmoidFunction.java
 * Copyright (c) 2020 Uwe Hennig
 * All rights reserved.
 */
package de.uh.neuron;

/**
 * ISigmoidFunction
 * @author Uwe Hennig
 */
@FunctionalInterface
public interface ISigmoidFunction {
   T calc(T x, T x0, T minY, T maxY, T gradient);
}

Sigmoid Functions

Diese Klasse enthält eine Sammlung von Sigmoid Funktionen mit passender Signatur zum Interface ISigmoidFunction. Die Methoden sind statisch, weil das Neuron eine Parameterklasse enthält, die dann die Attribute minY, maxY und gradient enthält. In der Parameterklasse ist dann die Funktion selbst als Attribut enthalten. SigmoidFunctions ist somit nur ein Sammelcontainer mit diversen Sigmoid Funktionen. Siehe auch Funktionen

                
/**
 * @(#)SigmoidFunctions.java
 * Copyright (c) 2020 Uwe Hennig
 * All rights reserved.
 */
package de.uh.neuron.impl;

/**
 * SigmoidFunctions
 * @author Uwe Hennig
 */
public class SigmoidFunctions {
	
   // Jump : 0 if x < 0 else 1
   public static Double jump(Double x, Double x0, Double minY, Double maxY, Double gradient) {
      return x < x0? minY : maxY;
   }
	
   // Fermi : 1 + (1 + e^(-x)
   public static Double fermi(Double x, Double x0, Double minY, Double maxY, Double gradient) {
      return minY + (maxY - minY) / (1.0 + Math.exp(-gradient * (x - x0)));
   }
    
   // more functions ...
}

Setzen und Aufruf der Fermi Funktion am Beispiel

package de.uh.test.nn;

public class Test {
   private ISigmoidFunction<Double> function;
   private Double x0, minY, maxY, gradient;
	
   public void setFunction(ISigmoidFunction<Double> f) {
      this.function = f;
   }
	
   public Double calc(Double x) {
      return function.calc(x, this.x0, this.minY, this.maxY, this.gradient);
   }
	
   public static void main(String ... args) {
      Test t = new Test();
        
      // ... set x0, minY, maxY, gradient values
                
      t.setFunction(SigmoidFunctions::fermi);
      t.calc(5.0D);
   }	
}
Asynchrone Signalverarbeitung der Neuronen

In diesem Beispiel wird die asynchrone Verarbeitung mit Hilfe einer Producer - Consumer - Queue beschrieben.

Die Neuronen kommunizieren nicht direkt miteinander, sondern senden ihre Signale (gewichtetes Potential) an eine zentrale Verarbeitungsqueue. Ein Signal enthält alle notwendigen Informationen. Das Neuron selbst muss nur den Identifier des Ausgangsneuron mit der passenden Gewichtung kennen.

Die Queue, genauer der "StimulusTransmitter" - Thread, muss für jedes Signal das Neuron laden und die Neuronfunktion "addPotential" aufrufen. Damit die Queue das Neuron schnell laden kann, werden alle Neuronen in einer "Registry" (Gehirn) gespeichert.

Folgende Codefragmente zeigen die Klasse Neuron und die Queue-Verarbeitung "StimulusTransmitter".

Neuron Klasse

/**
 * @(#)Neuron.java
 * Copyright (c) 2020 Uwe Hennig
 * All rights reserved.
 */
package de.uh.neuron.impl;


/**
 * Neuron
 * @author Uwe Hennig
 */
public class Neuron {
   private String id;
	
   private Map<String, Double> outNodes = new HashMap<>(4); // Node - weight
	
   private double sumWeight = 0.0;
   private SigmoidFunctionParameters fParams;
	
   private StimuliQueue  queue;

   private ReadWriteLock lock = new ReentrantReadWriteLock();
                
   public Neuron(StimuliQueue queue, SigmoidFunctionParameters fParams) {
      // ... init values
   }
	
	
   public void addPotential(Stimulus message) {
      lock.writeLock().lock();
                
      try {
         potential += message.getValue();
			
         if (!outNodes.isEmpty()) {
            double y = fParams.calculate(potential) * potential;
            if (y > 0) {
               for(String cid : outNodes.keySet()) {
                  Stimulus m = new Stimulus(id, cid, y * outNodes.get(cid) / sumWeight);
                  queue.put(m);
               }
               potential -= y;  
            }
         }
      } finally {
            lock.writeLock().unlock();
      }
   }
}



Queue Klasse

/**
 * @(#)StimuliQueue.java
 * Copyright (c) 2020 Uwe Hennig
 * All rights reserved.
 */
package de.uh.neuron.impl;

/**
 * StimuliQueue
 * Wrapper of Producer Consumer Queue ArrayBlockingQueue
 * Producer: Neuron
 * Consumer: StimulusTransmitter
 * @author Uwe Hennig
 */
public class StimuliQueue {
   private static final int THREADS = 16;
	
   private ArrayBlockingQueue sharedQueue = new ArrayBlockingQueue(1000);
   private ExecutorService executor; 
	
   public StimuliQueue() {
      executor = Executors.newFixedThreadPool(THREADS);
      // create and start consumer StimulusTransmitter.
      for (int i = 0; i < THREADS; i++) {
         executor.execute(new StimulusTransmitter(this) );
      }
   }
	
   // called from Neuron
   public void put(Stimulus stimulus) {
      sharedQueue.offer(stimulus);
   }
	
   // called from StimulusTransmitter
   public boolean isEmpty() {
      return sharedQueue.isEmpty();
   }
	
   // called from StimulusTransmitter
   public Stimulus get() {
      try {
         return sharedQueue.poll(500, TimeUnit.MILLISECONDS);
      } catch (InterruptedException e) {
         return null;
      }
   }
	
   // called from main process
   public void shutdown(long secondsToWaitUntilShutdown) throws InterruptedException {
      Thread.sleep(secondsToWaitUntilShutdown * 1000L);
      // Stop Signal
      put(new Stimulus(null, null, 0.0D));
      executor.shutdown();
      executor.awaitTermination(10, TimeUnit.MINUTES);
   }
}



Transmitter Klasse

/**
 * @(#)StimulusTransmitter.java
 * Copyright (c) 2020 Uwe Hennig
 * All rights reserved.
 */
package de.uh.neuron.impl;

/**
 * StimulusTransmitter
 * Thread, which transmits one stimulus. 
 * @author Uwe Hennig
 */
public class StimulusTransmitter implements Runnable {
   private static AtomicBoolean notDone = new AtomicBoolean(true);
   private static AtomicLong startTime = new AtomicLong(0L);
   private static AtomicLong timeout = new AtomicLong(0L);
	
   private StimuliQueue queue;
	
   public StimulusTransmitter(StimuliQueue queue) {
      this.queue = queue;
   }
	
   @Override
   public void run() {
      while((!queue.isEmpty() || notDone.get()) && !timedOut()) {
         Stimulus message = queue.get();
			
         if (message != null) {
            String targetId = message.getTarget();
				
            if (targetId != null) {
               Neuron clientSoma=NeuronPool.getInstance().get(targetId);
               if  (clientSoma != null) {
                  clientSoma.addPotential(message);
               } 
            } else {
               notDone.set(false);
               startTime.set(System.currentTimeMillis());
            }
         }
      }
   }
   // ... more
}