Perceptron
# 20201116 Raku programming solution
use MagickWand;
our ( \c, \runs ) = 0.00001, 2000 ;
class Trainer { has ( @.inputs, $.answer ) is rw }
sub linear(\x) { return x*0.7 + 40 }
class Perceptron {
has ( @.weights, Trainer @.training ) is rw ;
submethod BUILD(:n($n), :w($w), :h($h)) {
@!weights = [ rand*2-1 xx ^$n ];
@!training = (^runs).map: {
my (\x,\y) = rand*$w , rand*$h ;
my \a = y < linear(x) ?? 1 !! -1;
Trainer.new: inputs => (x,y,1), answer => a
}
}
method feedForward(@inputs) {
die "weights and input length mismatch" if +@inputs != +self.weights;
return ( sum( @inputs »*« self.weights ) > 0 ) ?? 1 !! -1
}
method train(@inputs, \desired) {
self.weights »+«= @inputs »*» (c*(desired - self.feedForward(@inputs)))
}
method draw(\img) {
for ^runs { self.train(self.training[$_].inputs, self.training[$_].answer) }
my $y = linear(my $x = img.width) ;
img».&{ .stroke-width(3) or .stroke('black') or .fill('none') } # C returns
img.draw-line(0.0, linear(0), $x, $y);
img.stroke-width( 1 );
for ^runs {
my $guess = self.feedForward(self.training[$_].inputs);
($x, $y) = self.training[$_].inputs[0,1] »-» 4;
$guess > 0 ?? img.stroke( 'blue' ) !! img.stroke( 'red' );
img.circle( $x, $y, $x+8, $y );
}
}
}
my ($w, $h) = 640, 360;
my $perc = Perceptron.new: n => 3, w => $w, h => $h;
my $o = MagickWand.new or die;
$o.create( $w, $h, "white" );
$perc.draw($o);
$o.write('./perceptron.png') or die
Last updated