Teach an artificial neuron to tell light from dark

Build the smallest possible artificial intelligence: one neuron on the UNO. Teach it with two buttons what "light" and "dark" mean, and watch its numbers change as it learns.
  • Level:Beginner
  • Duration: 50 min
  • Ages 10+
  • Platform: Arduino

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Teach an artificial neuron to tell light from dark – EEMENTOR STEM™ lesson

Your brain is made of about 86 billion nerve cells called neurons. Each one adds up the signals it gets and decides whether to "fire". Artificial intelligence copies that idea with numbers. In this lesson the UNO runs one artificial neuron that looks at the light sensor. At first it knows nothing and guesses badly. Then you teach it: hold the sensor in the light and press "light", cover it and press "dark". After a few examples the neuron lights the right LED by itself. You'll see the two numbers it learns, its weight and its bias, change with every lesson you give it.

What you'll learn

  • What an artificial neuron is: inputs, a weight, a bias and an output
  • How a neuron learns from examples with the perceptron rule
  • What training, an epoch and an error are
  • Why the same neuron can learn the opposite if you teach it differently

What you need

  • UNO R3 board and USB cable (UNO R3 Starter Kit)
  • Photoresistor module (45-in-1 Sensor Kit)
  • 2 push buttons (UNO R3 Starter Kit)
  • Green LED, red LED and 2 resistors, 220 Ω (UNO R3 Starter Kit)
  • The printed parts from the 3D printing box below (neuron model), or the cardboard version described there
  • Breadboard and jumper wires (UNO R3 Starter Kit)

Wiring

Breadboard wiring diagram: Teach an artificial neuron to tell light from dark
Breadboard view. Check each connection against the wiring table below.
From To
Photoresistor module S UNO pin A0
Photoresistor module + and − 5V and GND
"Light" button, one leg UNO pin 2
"Dark" button, one leg UNO pin 3
Both buttons, the diagonally opposite leg GND
UNO pin 9 220 Ω resistor, then the green LED's long leg
UNO pin 10 220 Ω resistor, then the red LED's long leg
Both LED short legs GND

Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.

The sketch

const int LIGHT_PIN = A0;
const int LIGHT_BUTTON = 2, DARK_BUTTON = 3;
const int GREEN_PIN = 9, RED_PIN = 10;
const float RATE = 0.3;               // learning rate: how big each correction is
const int MAX_EXAMPLES = 20;

float weight = 0;                     // the neuron starts knowing nothing
float bias = 0;
float examples[MAX_EXAMPLES];         // what the sensor saw
int answers[MAX_EXAMPLES];            // what you said it was: 1 light, 0 dark
int count = 0;

// The input: the light reading as a number from 0 to 1
float sense() {
  long sum = 0;
  for (int i = 0; i < 10; i++) sum += analogRead(LIGHT_PIN);
  return sum / 10 / 1023.0;
}

// The neuron: multiply, add, decide. 1 means "light", 0 means "dark"
int think(float x) {
  return weight * x + bias > 0 ? 1 : 0;
}

void learn(float x, int answer) {
  examples[count % MAX_EXAMPLES] = x;  // remember this example
  answers[count % MAX_EXAMPLES] = answer;
  count++;
  int stored = min(count, MAX_EXAMPLES);
  for (int epoch = 1; epoch <= 50; epoch++) {      // go through all examples again and again
    int mistakes = 0;
    for (int i = 0; i < stored; i++) {
      int error = answers[i] - think(examples[i]);  // -1, 0 or +1
      if (error != 0) {
        weight += RATE * error * examples[i];        // the perceptron rule
        bias += RATE * error;
        mistakes++;
      }
    }
    if (mistakes == 0) {
      Serial.print("Learned it in ");
      Serial.print(epoch);
      Serial.println(epoch == 1 ? " epoch." : " epochs.");
      break;
    }
  }
  Serial.print("weight = ");
  Serial.print(weight, 3);
  Serial.print("   bias = ");
  Serial.println(bias, 3);
  if (weight != 0) {
    Serial.print("It now calls everything ");
    Serial.print(weight > 0 ? "above " : "below ");
    Serial.print(-bias / weight, 2);                 // where weight * x + bias = 0
    Serial.println(" \"light\".");
  }
}

bool pressed(int pin) {
  if (digitalRead(pin) == HIGH) return false;
  delay(30);
  while (digitalRead(pin) == LOW) {}                 // wait until you let go
  return true;
}

void setup() {
  Serial.begin(9600);
  pinMode(LIGHT_BUTTON, INPUT_PULLUP);
  pinMode(DARK_BUTTON, INPUT_PULLUP);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(RED_PIN, OUTPUT);
  Serial.println("Teach me! Press LIGHT or DARK to tell me what the sensor sees.");
}

void loop() {
  float x = sense();
  int guess = think(x);
  digitalWrite(GREEN_PIN, guess == 1);
  digitalWrite(RED_PIN, guess == 0);

  if (pressed(LIGHT_BUTTON)) {
    Serial.print("You said: LIGHT at ");
    Serial.println(x, 2);
    learn(x, 1);
  }
  if (pressed(DARK_BUTTON)) {
    Serial.print("You said: DARK at ");
    Serial.println(x, 2);
    learn(x, 0);
  }
  delay(50);
}

Teach it

  1. Open the Serial Monitor at 9600 baud. The neuron starts with weight 0 and bias 0, so it always says "dark": the red LED is on.
  2. Hold the sensor under a lamp and press light. The numbers change. Cover it with your hand and press dark.
  3. Give it three or four examples of each. Now move between light and shadow: the LEDs follow by themselves.
  4. Press the reset button on the UNO and teach it the opposite: dark is "light" and light is "dark". It learns that just as happily. It has no idea what light is; it only learns what you show it.

How it works

An artificial neuron does one small sum: it multiplies its input by a weight, adds a bias and checks whether the result is above zero. The weight says how important the input is and in which direction; the bias moves the point where the answer flips. That flip point, -bias / weight, is the neuron's idea of where "light" begins.

Learning means finding a weight and a bias that give the right answers. The perceptron rule, invented in 1958, does it with a simple correction: for every example the neuron gets wrong, nudge the weight and the bias a little towards the right answer. Going through all the examples once is an epoch. The neuron repeats epochs until it makes no more mistakes.

Big AI systems like the ones behind chatbots work on the same idea, with billions of weights instead of one, and a cleverer way of nudging them. But the heart of it is the same: examples, errors and small corrections.

Try this next

  • Give the neuron a second input, the potentiometer, and teach it "light AND knob up".
  • Make RATE very small (0.01) and count how many epochs it needs now.
  • Teach it with a wrong example on purpose. What happens to the flip point?

Troubleshooting

  • It always says the same thing: give it at least one example of each kind.
  • It says "50 epochs" and still makes mistakes: you gave it examples that contradict each other, like "light" and "dark" at the same reading. Press reset and teach it again.
  • A button does nothing: use the diagonally opposite legs of the button.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

UNO R3 Starter Kit | Arduino IDE Compatible | Free Tutorials $56.00

Shop the kit