diff --git a/content/10_nn.html b/content/10_nn.html index 0c375922..31b31b51 100644 --- a/content/10_nn.html +++ b/content/10_nn.html @@ -227,7 +227,7 @@

The Perceptron Code

The constructor can receive an argument indicating the number of inputs (in this case, three: x_0, x_1, and a bias) and size the weights array accordingly, filling it with random values to start:

-
  // The argument n determines the number of inputs (including the bias).
+  
	// The argument n determines the number of inputs (including the bias).
   constructor(n) {
     this.weights = [];
     for (let i = 0; i < n; i++) {
@@ -321,7 +321,7 @@ 

The Perceptron Code

A high learning constant causes the weight to change more drastically. This may help the perceptron arrive at a solution more quickly, but it also increases the risk of overshooting the optimal weights. A small learning constant will adjust the weights more slowly and require more training time, but will allow the network to make small adjustments that could improve overall accuracy.

Assuming the addition of a learningConstant property to the Perceptronclass, I can now write a training method for the perceptron following the steps I outlined earlier:

  // Step 1: Provide the inputs and known answer.
-  // These are passed in as arguments to train().
+  // These are passed in as arguments to train().
   train(inputs, desired) {
     // Step 2: Guess according to those inputs.
     let guess = this.feedforward(inputs);
@@ -386,12 +386,12 @@ 

The Perceptron Code

Figure 10.8: A graph of y = \frac{1}2x - 1

I’ll arbitrarily choose that as the equation for my line, and write a function accordingly:

-
// A function to calculate y based on x along a line
+
// A function to calculate y based on x along a line
 function f(x) {
   return 0.5 * x - 1;
 }

Now there’s the matter of the p5.js canvas defaulting to (0, 0) in the top-left corner with the y-axis pointing down. For this discussion, I’ll assume I’ve built the following into the code to reorient the canvas to match a more traditional Cartesian space:

-
// Move the origin (0, 0) to the center.
+
// Move the origin (0, 0) to the center.
 translate(width / 2, height / 2);
 // Flip the y-axis orientation (positive points up!).
 scale(1, -1);
@@ -463,7 +463,7 @@

Example 10.1: The Perceptron

strokeWeight(2); line(-width / 2, f(-width / 2), width / 2, f(width / 2)); - // Get the current (x, y) of the training data. + // Get the current (x, y) of the training data. let x = training[count][0]; let y = training[count][1]; // What is the desired output? @@ -799,7 +799,7 @@

Training the Model

classifier.normalizeData();

In this case, the handcoded data was limited to a range of –1 to +1 from the get-go, so calling normalizeData() here is likely redundant. Still, this function call is important to demonstrate. Normalizing your data ahead of time as part of the preprocessing step will absolutely work, but the auto-normalization feature of ml5.js is a big help!

Now for the heart of the machine learning process: actually training the model. Here’s the code:

-
// The train() method initiates the training process.
+
// The train() method initiates the training process.
 classifier.train(finishedTraining);
 
 // A callback function for when the training is complete
@@ -874,7 +874,7 @@ 

Deploying the Model

]

In this example output, the model is highly confident (approximately 96.7 percent) that the correct label is "right", while it has minimal confidence (0.03 percent) in the "left" label. The confidence values are normalized and add up to 100 percent.

All that remains now is to fill out the sketch with code so the model can receive live input from the mouse. The first step is to signal the completion of the training process so the user knows the model is ready. I’ll include a global status variable to track the training process and ultimately display the predicted label on the canvas. The variable is initialized to "training" but updated to "ready" through the finishedTraining() callback:

-
// When the sketch starts, it will show a status of training.
+
// When the sketch starts, it will show a status of training.
 let status = "training";
 
 function draw() {
@@ -884,7 +884,7 @@ 

Deploying the Model

text(status, width / 2, height / 2); } -// This is the callback for when training is complete, and the message changes to ready. +// This is the callback for when training is complete, and the message changes to ready. function finishedTraining() { status = "ready"; }
diff --git a/content/11_nn_ga.html b/content/11_nn_ga.html index 008e0a29..5ceaff1c 100644 --- a/content/11_nn_ga.html +++ b/content/11_nn_ga.html @@ -85,7 +85,7 @@

Coding Flappy Bird

To simplify the code even further, I’ll add the forces directly to the bird’s velocity instead of accumulating them into an acceleration variable. In addition to the usual update(), I’ll include a flap() method for the bird to fly upward. The show() method isn’t included here as it only draws a circle. Here’s the code:

class Bird {
   constructor() {
-    // The bird’s position (x will be constant)
+    // The bird’s position (x will be constant)
     this.x = 50
     this.y = 120;
 
@@ -323,7 +323,7 @@ 

Selection: Flappy Bird Fitness

}

I’ll assign the fitness a numeric value that increases by one every cycle through draw(), as long as the bird remains alive. The birds that survive longer should have a higher fitness value. This mechanism mirrors the reinforcement learning technique of rewarding good decisions. In reinforcement learning, however, an agent receives immediate feedback for every decision it makes, allowing it to adjust its policy accordingly. Here, the bird’s fitness is a cumulative measure of its overall success and will be applied only during the selection step of the GA:

  update() {
-    //{!1} Increment the fitness each time through update().
+    //{!1} Increment the fitness each time through update().
     this.fitness++;
   }

The alive property is a Boolean flag that’s initially set to true. When a bird collides with a pipe, this property is set to false. Only birds that are still alive are updated and drawn to the canvas:

@@ -658,9 +658,9 @@

Example 11.4: Dynamic Ne } } - /* seek() predicts a steering force as described previously. */ + /* seek() predicts a steering force as described previously. */ - /* update() increments the fitness if the glow is reached as described previously. */ + /* update() increments the fitness if the glow is reached as described previously. */ }

It’s hard to believe, but this book has been a journey well over 10 years in the making. Thank you, dear reader, for sticking with it. I promise it’s not an infinite loop. However meandering it might have seemed, like a random walk, I’m finally using an arrival steering behavior to reach the final piece of the puzzle, an attempt to bring together all my past explorations in my own version of the Ecosystem Project.

@@ -784,7 +784,7 @@

Example 11.5: A Bloop with Sensors

sense() method for each sensor. sense(food) { for (let sensor of this.sensors) { sensor.sense(this.position, food); diff --git a/content/examples/06_libraries/6_5_compound_bodies_error/lollipop.js b/content/examples/06_libraries/6_5_compound_bodies_error/lollipop.js index 756c87e6..d2ea762f 100644 --- a/content/examples/06_libraries/6_5_compound_bodies_error/lollipop.js +++ b/content/examples/06_libraries/6_5_compound_bodies_error/lollipop.js @@ -25,48 +25,48 @@ class Lollipop { // Drawing the lollipop show() { - if (mouseIsPressed) { - // The angle comes from the compound body - let angle = this.body.angle; + // if (mouseIsPressed) { + // // The angle comes from the compound body + // let angle = this.body.angle; - //{!2} Get the position for each part - let position1 = this.part1.position; - let position2 = this.part2.position; + // //{!2} Get the position for each part + // let position1 = this.part1.position; + // let position2 = this.part2.position; - fill(127); - stroke(0); - strokeWeight(1); + // fill(127); + // stroke(0); + // strokeWeight(1); - // Translate and rotate the rectangle (part1) - push(); - translate(position1.x, position1.y); - rotate(angle); - rectMode(CENTER); - rect(0, 0, this.w, this.h); - pop(); + // // Translate and rotate the rectangle (part1) + // push(); + // translate(position1.x, position1.y); + // rotate(angle); + // rectMode(CENTER); + // rect(0, 0, this.w, this.h); + // pop(); - // Translate and rotate the circle (part2) - push(); - translate(position2.x, position2.y); - rotate(angle); - fill(200); - circle(0, 0, this.r * 2); - pop(); - } else { - let position = this.body.position; - let angle = this.body.angle; - rectMode(CENTER); - fill(127); - stroke(0); - strokeWeight(1); - push(); - translate(position.x, position.y); - rotate(angle); - rect(0, 0, this.w, this.h); - fill(200); - circle(this.w / 2, 0, this.r * 2); - pop(); - } + // // Translate and rotate the circle (part2) + // push(); + // translate(position2.x, position2.y); + // rotate(angle); + // fill(200); + // circle(0, 0, this.r * 2); + // pop(); + // } else { + let position = this.body.position; + let angle = this.body.angle; + rectMode(CENTER); + fill(127); + stroke(0); + strokeWeight(1); + push(); + translate(position.x, position.y); + rotate(angle); + rect(0, 0, this.w, this.h); + fill(200); + circle(this.w / 2, 0, this.r * 2); + pop(); + // } } checkEdge() { diff --git a/content/examples/06_libraries/6_5_compound_bodies_error/screenshot.png b/content/examples/06_libraries/6_5_compound_bodies_error/screenshot.png index 78e88fe1..3d9ef4c9 100644 Binary files a/content/examples/06_libraries/6_5_compound_bodies_error/screenshot.png and b/content/examples/06_libraries/6_5_compound_bodies_error/screenshot.png differ diff --git a/content/examples/06_libraries/6_5_compound_bodies_error/sketch.js b/content/examples/06_libraries/6_5_compound_bodies_error/sketch.js index 4a2c6b09..420a8a00 100644 --- a/content/examples/06_libraries/6_5_compound_bodies_error/sketch.js +++ b/content/examples/06_libraries/6_5_compound_bodies_error/sketch.js @@ -5,8 +5,7 @@ // TODO: Why is body.pos different from part1.pos? // Why is there body.angle but no part1.angle? -const { Engine, Bodies, Composite, Body, Vector } = Matter; - +const { Engine, Bodies, Composite, Body, Vector } = Matter; // A reference to the matter physics engine let engine; @@ -27,7 +26,7 @@ function setup() { boundaries.push( new Boundary((3 * width) / 4, height - 50, width / 2 - 50, 10) ); - + for (let i = 0; i < 100; i++) { let lolli = new Lollipop(random(width), 0); lollipops.push(lolli); @@ -40,8 +39,6 @@ function draw() { // Update the engine! Engine.update(engine); - - // Iterate over the boxes backwards for (let i = lollipops.length - 1; i >= 0; i--) { lollipops[i].show();