Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import org.scalatest.FunSuite
- import MathUtils._
- import scala.util.Random
- /**
- * Created by 'Jakub Dziworski' on 05.12.16
- */
- class PerceptronTest extends FunSuite {
- case class Pattern(inputs: List[Double], expectedResult: List[Double])
- test("Should chuj") {
- val patterns = List(
- Pattern(List(1, 0, 0, 0), List(1, 0, 0, 0))
- // Pattern(List(0, 1, 0, 0), List(0, 1, 0, 0))
- // Pattern(List(0, 0, 1, 0), List(0, 0, 1, 0))
- // Pattern(List(0, 0, 0, 1), List(0, 0, 0, 1))
- )
- val trainingResult = Perceptron.train(patterns)
- println(trainingResult)
- }
- object Perceptron {
- val Step = 0.1
- val Delta = 0.1
- val Bias = 1.0
- type Weights = List[Double]
- type Inputs = List[Double]
- type Layer = List[Neuron]
- type ErrorSignal = Double
- case class Neuron(weights: Weights)
- case class NeuronResult(inputs: Perceptron.Inputs, weights: Perceptron.Weights, weightedSum: Double, output: Double)
- case class TrainingResult(hiddenLayer: Layer, outputLayer: Layer)
- def train(patterns: List[Pattern]): TrainingResult = {
- def trainLoop(hiddenLayer: Layer, outputLayer: Layer): TrainingResult = {
- val pattern = patterns(Random.nextInt(patterns.length))
- val inputs: Inputs = pattern.inputs
- val hiddenLayerResult = calcLayer(inputs, hiddenLayer)
- val outputLayerResult = calcLayer(hiddenLayerResult.map(_.output), outputLayer)
- val networkResult = calcNetwork(inputs, hiddenLayer, outputLayer)
- if (isTrained(patterns, networkResult)) {
- TrainingResult(hiddenLayer, outputLayer)
- } else {
- val outputLayerErrorSignals = calcOutputLayerSignals(pattern, outputLayerResult)
- val hiddenLayerErrorSignals = calcHiddenLayerSignals(hiddenLayerResult, outputLayer, outputLayerErrorSignals)
- val hiddenLayerNewNeurons = newNeurons(hiddenLayerResult, hiddenLayerErrorSignals)
- val outputLayerNewNeurons = newNeurons(outputLayerResult, outputLayerErrorSignals)
- trainLoop(hiddenLayerNewNeurons, outputLayerNewNeurons)
- }
- }
- val freshHiddenLayer: List[Neuron] = List.fill(2)(Neuron(List.fill(5)(randomWeight())))
- val freshOutputLayer: List[Neuron] = List.fill(4)(Neuron(List.fill(3)(randomWeight())))
- trainLoop(freshHiddenLayer, freshOutputLayer)
- }
- def calcHiddenLayerSignals(hiddenLayerResult: List[NeuronResult], outputLayer: Layer, outputLayerErrorSignals: List[ErrorSignal]): List[ErrorSignal] = {
- hiddenLayerResult.zipWithIndex.map { case (neuronResult, neuronIndex) =>
- val correspondingOutputWeights = outputLayer.map(_.weights(neuronIndex))
- activationDerivative(neuronResult.weightedSum) * weightedSum(outputLayerErrorSignals, correspondingOutputWeights)
- }
- }
- def calcOutputLayerSignals(pattern: Pattern, outputLayerResult: List[NeuronResult]): List[ErrorSignal] = {
- outputLayerResult.zip(pattern.expectedResult).map { case (actualOutput, desiredOutput) =>
- activationDerivative(actualOutput.weightedSum) * (desiredOutput - actualOutput.output)
- }
- }
- def isTrained(patterns: List[Pattern], result: List[NeuronResult]): Boolean = patterns.forall { p =>
- val expected = p.expectedResult
- result.zip(expected).forall { case (actual, expected) => Math.abs(actual.output - expected) < Delta }
- }
- def calcNeuron(inputs: Inputs, neuron: Neuron): NeuronResult = {
- val weighSum = weightedSum(inputs :+ Bias, neuron.weights)
- NeuronResult(inputs, neuron.weights, weighSum, activationFunction(weighSum))
- }
- def calcLayer(inputs: Inputs, neurons: List[Neuron]): List[NeuronResult] = neurons.map(n => calcNeuron(inputs, n))
- def calcNetwork(inputs: Inputs, hiddenLayer: List[Neuron], outputLayer: List[Neuron]): List[NeuronResult] = {
- val hiddenLayerResult = calcLayer(inputs, hiddenLayer)
- val outputLayerResult = calcLayer(hiddenLayerResult.map(_.output), outputLayer)
- outputLayerResult
- }
- def newNeurons(results: List[NeuronResult], errorSignals: List[ErrorSignal]): List[Neuron] = {
- results.zip(errorSignals).map { case (result, signal) =>
- result.weights.map(weight => weight + Step * signal * result.output)
- }.map(Neuron)
- }
- }
- }
- object MathUtils {
- def activationFunction(x: Double): Double = {
- 1.0 / (1.0 + Math.exp(-x))
- }
- def activationDerivative(x: Double): Double = {
- Math.exp(x) / Math.pow(Math.exp(x) + 1.0, 2)
- }
- def weightedSum(input: List[Double], weights: List[Double]): Double = {
- input.zip(weights).map { case (value, weight) => value * weight }.sum
- }
- def randomWeight(): Double = {
- (Random.nextDouble() - 0.5) / 2.0
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement