Task: Perceptron




Go: package main

import (
    "github.com/fogleman/gg"
    "math/rand"
    "time"
)

const c = 0.00001

func linear(x float64) float64 {
    return x*0.7 + 40
}

type trainer struct {
    inputs []float64
    answer int
}

func newTrainer(x, y float64, a int) *trainer {
    return &trainer{[]float64{x, y, 1}, a}
}

type perceptron struct {
    weights  []float64
    training []*trainer
}

func newPerceptron(n, w, h int) *perceptron {
    weights := make([]float64, n)
    for i := 0; i < n; i++ {
        weights[i] = rand.Float64()*2 - 1
    }

    training := make([]*trainer, 2000)
    for i := 0; i < 2000; i++ {
        x := rand.Float64() * float64(w)
        y := rand.Float64() * float64(h)
        answer := 1
        if y < linear(x) {
            answer = -1
        }
        training[i] = newTrainer(x, y, answer)
    }
    return &perceptron{weights, training}
}

func (p *perceptron) feedForward(inputs []float64) int {
    if len(inputs) != len(p.weights) {
        panic("weights and input length mismatch, program terminated")
    }
    sum := 0.0
    for i, w := range p.weights {
        sum += inputs[i] * w
    }
    if sum > 0 {
        return 1
    }
    return -1
}

func (p *perceptron) train(inputs []float64, desired int) {
    guess := p.feedForward(inputs)
    err := float64(desired - guess)
    for i := range p.weights {
        p.weights[i] += c * err * inputs[i]
    }
}

func (p *perceptron) draw(dc *gg.Context, iterations int) {
    le := len(p.training)
    for i, count := 0, 0; i < iterations; i, count = i+1, (count+1)%le {
        p.train(p.training[count].inputs, p.training[count].answer)
    }
    x := float64(dc.Width())
    y := linear(x)
    dc.SetLineWidth(2)
    dc.SetRGB255(0, 0, 0) 
    dc.DrawLine(0, linear(0), x, y)
    dc.Stroke()
    dc.SetLineWidth(1)
    for i := 0; i < le; i++ {
        guess := p.feedForward(p.training[i].inputs)
        x := p.training[i].inputs[0] - 4
        y := p.training[i].inputs[1] - 4
        if guess > 0 {
            dc.SetRGB(0, 0, 1) 
        } else {
            dc.SetRGB(1, 0, 0) 
        }
        dc.DrawCircle(x, y, 8)
        dc.Stroke()
    }
}

func main() {
    rand.Seed(time.Now().UnixNano())
    w, h := 640, 360
    perc := newPerceptron(3, w, h)
    dc := gg.NewContext(w, h)
    dc.SetRGB(1, 1, 1) 
    dc.Clear()
    perc.draw(dc, 2000)
    dc.SavePNG("perceptron.png")
}


Java: import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

public class Perceptron extends JPanel {

    class Trainer {
        double[] inputs;
        int answer;

        Trainer(double x, double y, int a) {
            inputs = new double[]{x, y, 1};
            answer = a;
        }
    }

    Trainer[] training = new Trainer[2000];
    double[] weights;
    double c = 0.00001;
    int count;

    public Perceptron(int n) {
        Random r = new Random();
        Dimension dim = new Dimension(640, 360);
        setPreferredSize(dim);
        setBackground(Color.white);

        weights = new double[n];
        for (int i = 0; i < weights.length; i++) {
            weights[i] = r.nextDouble() * 2 - 1;
        }

        for (int i = 0; i < training.length; i++) {
            double x = r.nextDouble() * dim.width;
            double y = r.nextDouble() * dim.height;

            int answer = y < f(x) ? -1 : 1;

            training[i] = new Trainer(x, y, answer);
        }

        new Timer(10, (ActionEvent e) -> {
            repaint();
        }).start();
    }

    private double f(double x) {
        return x * 0.7 + 40;
    }

    int feedForward(double[] inputs) {
        assert inputs.length == weights.length : "weights and input length mismatch";

        double sum = 0;
        for (int i = 0; i < weights.length; i++) {
            sum += inputs[i] * weights[i];
        }
        return activate(sum);
    }

    int activate(double s) {
        return s > 0 ? 1 : -1;
    }

    void train(double[] inputs, int desired) {
        int guess = feedForward(inputs);
        double error = desired - guess;
        for (int i = 0; i < weights.length; i++) {
            weights[i] += c * error * inputs[i];
        }
    }

    @Override
    public void paintComponent(Graphics gg) {
        super.paintComponent(gg);
        Graphics2D g = (Graphics2D) gg;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        
        int x = getWidth();
        int y = (int) f(x);
        g.setStroke(new BasicStroke(2));
        g.setColor(Color.orange);
        g.drawLine(0, (int) f(0), x, y);

        train(training[count].inputs, training[count].answer);
        count = (count + 1) % training.length;

        g.setStroke(new BasicStroke(1));
        g.setColor(Color.black);
        for (int i = 0; i < count; i++) {
            int guess = feedForward(training[i].inputs);

            x = (int) training[i].inputs[0] - 4;
            y = (int) training[i].inputs[1] - 4;

            if (guess > 0)
                g.drawOval(x, y, 8, 8);
            else
                g.fillOval(x, y, 8, 8);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setTitle("Perceptron");
            f.setResizable(false);
            f.add(new Perceptron(3), BorderLayout.CENTER);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });
    }
}