Count Me In

Whilst I have my sights very much set on creating true AI for general intelligence (yeah, I know I set my sights very high), I thought about the concept of what “counting” might mean to an AI.

For fun I created this crazy concept; yes, I must have too much time on my hands. A subsequent posting will show you a way for AI to store the number and increment/decrement it.

The source code is on GitHub.

I know I could have created a command line app that does a similar thing but it wouldn’t be anywhere near as much fun.

As this is meant to be an AI/ML blog so let’s cover the approach first.

Perceptron Neural Network: 9 inputs, 4×9 hidden, 7 outputs using back-propagation to train.

Ticking checkboxes makes the number change – it doesn’t matter which order, it truly is a count of the checkboxes ticked.

Each “input” neuron receives a “1” if the corresponding check box is ticked, or “0” if not.

Output from the neural network, I could have a floating point number that represents 0, 1/10, 2/10, 3/10…9./10. But that is aiming too low. Those of a certain age will remember 7-segment displays. Fun fact: patents for them exist in 1903! These days they can still be purchased, but are much less commonly used with all the fancy high-res screens.

To represent a number (and the letters A-F), you have to illuminate the correct segments (labelled a-g). For a “0”, you need to illuminate segments a, b, c, d, e and f, and for a “1” you illuminate b and c.

Therefore we need to associate a “count” with specific segments illuminated. To do that, I created an array of desired outputs –

/* 7 segments are annotated as follows:
 * 
 *      [-a-]                 
 *   [|]     [|]
 *   [f]     [b]
 *   [|]     [|]
 *      [-g-]              
 *   [|]     [|]
 *   [e]     [c]            
 *   [|]     [|]
 *      [-d-]              
 */

double[][] segmentsToLight = {
	           // a,b,c,d,e,f,g
	new double[]{ 1,1,1,1,1,1,0}, // 0
	new double[]{ 0,1,1,0,0,0,0}, // 1
	new double[]{ 1,1,0,1,1,0,1}, // 2
	new double[]{ 1,1,1,1,0,0,1}, // 3
	new double[]{ 0,1,1,0,0,1,1}, // 4
	new double[]{ 1,0,1,1,0,1,1}, // 5
	new double[]{ 1,0,1,1,1,1,1}, // 6
	new double[]{ 1,1,1,0,0,0,0}, // 7
	new double[]{ 1,1,1,1,1,1,1}, // 8
	new double[]{ 1,1,1,1,0,1,1}  // 9
};

We need a way to convert checkboxes into input data. I could count them in C#, but that negates the point of the blog.

I went for a very simple approach: where a box is checked I treat as 1 and unchecked as 0. Those are put in an array. I could have used inline code like (checkBox1.Checked?1:0), but chose not to.

We pass those inputs to the neural network and receive a double[] array of which segments to illuminate. The SevenSegmentDisplay class takes care of the UX.

private void AskAItoCount()
{
  double[] inputs = {
    BoolTo1or0(checkBox1.Checked),
    BoolTo1or0(checkBox2.Checked),
    BoolTo1or0(checkBox3.Checked),
    BoolTo1or0(checkBox4.Checked),
    BoolTo1or0(checkBox5.Checked),
    BoolTo1or0(checkBox6.Checked),
    BoolTo1or0(checkBox7.Checked),
    BoolTo1or0(checkBox8.Checked),
    BoolTo1or0(checkBox9.Checked)
	};

  double[] outputOfAIelementsAtoG = neuralNetwork.FeedForward(inputs);

  pictureBox7Segment.Image?.Dispose();
  pictureBox7Segment.Image = SevenSegmentDisplay.Output(outputOfAIelementsAtoG);
}

/// <summary>
/// Convert bool to 1 (true) / 0 (false).
/// </summary>
/// <param name="value">true/false.</param>
/// <returns>1 if value==true | 0 if value == false</returns>
private static double BoolTo1or0(bool value)
{
  return value ? 1 : 0;
}

By now you’re smart and maybe know where this is going – right?

Repeat

  • For each training point, back-propagate the expected output (7 segments to illuminate)
  • Test the neural network for all training points, if all pass -> trained.

Until trained.

The inputs (see code above) provide a “1” in that element of the array if checked. The neural network is to “count” the number of 1’s. So what we need to give it is things that enable it to achieve that.

This is where we do the obvious thing – feed it all patterns starting from no checkboxes ticked { 0,0,0,0,0,0,0,0,0} to all checkboxes ticked {1,1,1,1,1,1,1,1,1}. There are 2^9 permutations (512). It’s not a horrific amount of training.

// 0.511 = 2^9 (permutation 000000000 to 111111111).
for (int n = 0; n < 512; n++)
{
  double[] inputs = BinaryAmount(n, out int count);

  // associate the inputs with a 7 segment display
  neuralNetwork.BackPropagate(inputs, segmentsToLight[count] /* segments to light based on number of "1"s in input */);
}

“n” goes 0..511, and we convert it for “inputs” as the binary array of 1’s and 0’s. The “out int count” is not cheating, it’s the only way it will know which segments to light. It isn’t used by the ML, but for us to work out which segments we expect the ML to light.

If we recollect the array above, for each 0..9, it has a list of segments on or off. I am sure one smart reader will come up with a more optimal way to do this like (inputValue >> 1) & 1. Feel free to propose a better way in the comments.

/// <summary>
/// Returns 1 / 0 in each output cell based on the input number. *We use it to train AI.
/// </summary>
/// <param name="inputValue"></param>
/// <param name="countOf1sInBinaryRepresentationOfInputValue">How many 1's appear in the the inputValue when converted to "binary".</param>
/// <returns>Array of 1's & zero's from the binary representation of the input value.</returns>
private static double[] BinaryAmount(int inputValue, out int countOf1sInBinaryRepresentationOfInputValue)
{
  string binaryAmount = Convert.ToString(inputValue, 2).PadLeft(9, '0'); // turn the input into binary

  double[] result = new double[binaryAmount.Length];

  countOf1sInBinaryRepresentationOfInputValue = 0;

  for (int digitIndex = 0; digitIndex < binaryAmount.Length; digitIndex++)
  {
    result[digitIndex] = float.Parse(binaryAmount[digitIndex].ToString());

    if (result[digitIndex] != 0) ++countOf1sInBinaryRepresentationOfInputValue; // count the "1"s
  }

  return result; // how many "1"s in the binary string
}

This was a fun challenge, but it’s not going to advance AI. Behind the scenes it’s merely a mapping of 9 input variables to an array of 7 values, one didn’t need ML for that. You could create Dictionary<string,int[]> where the string contains all the checkbox values (0/1) added together, and the int[] the segments to light. You could keep a count and increment/decrement on click of checkbox and then use the array of segments by index 0..9.

If you enjoyed this post, would like something explaining better, please feel free to comment below.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *