Howto:Neural networks in Nasal

From FlightGear wiki
Jump to navigation Jump to search

Status: WIP/draft

Last update: 06/2016

Background

We've been talking about making more realistic AI opponents for Bombable, and heinkill on the ATAG forums gave a really interesting analysis of the AI pilot features of the BOB2 software that may give us some interesting and specific things to work to implement in Bombable[1]

Motivation

If you know Nasal, you could certainly teach bombable to become a bit smarter, i.e. by adding support for standard combat maneuvers. In its simplest form you could code a state machine that responds to standard situations with a randomly chosen standard response (and maybe some dynamic variation). It would be a matter of coming up with a state machine implementation in Nasal so that the AI can match up situations against suitable responses. It is definitely possible. Hooray once talked to flug about adding neural network-based AI to bombable. If there are enough people interested in this aspect of making bombable even smarter, then we could certainly come up with some more challenging AI bots.[2]


Especially if there is interest to make like a neural net type intelligent/learning system for AI fighter aircraft I think both the Bombable and Flightgear problems could rather easily be solved. The changes that would be required in FlightGear's C code are not that difficult[3]


Or train a neural network to keep a helicopter hovering in a stable position[4]

Note  The following snippet of code was ported to Nasal from JavaScript and may not have received much testing yet
# [Perceptron Classifier](http://en.wikipedia.org/wiki/Perceptron)
# http://planspace.org/20150610-a_javascript_perceptron/
# This is a single-layer perceptron classifier that takes
# arrays of numbers and predicts whether they should be classified
# as either 0 or 1 (negative or positive examples).
var perceptron = func() {
    var perceptron_model = {};
        # The weights, or coefficients of the model;
        # weights are only populated when training with data.
    var weights = [];
        # The bias term, or intercept; it is also a weight but
        # it's stored separately for convenience as it is always
        # multiplied by one.
    var bias = 0;

    # ## Predict
    # Use an array of features with the weight array and bias
    # to predict whether an example is labeled 0 or 1.
    perceptron_model.predict = func(features) {
        # Only predict if previously trained
        # on the same size feature array(s).
        if (size(features) != size(weights)) return nil;
        # Calculate the sum of features times weights,
        # with the bias added (implicitly times one).
        var score = 0;
        for (var i = 0; i < size(weights); i+=1) {
            score += weights[i] * features[i];
        }
        score += bias;
        # Classify as 1 if the score is over 0, otherwise 0.
        return score > 0 ? 1 : 0;
    };

    # ## Train
    # Train the classifier with a new example, which is
    # a numeric array of features and a 0 or 1 label.
    perceptron_model.train = func(features, label) {
        # Require that only labels of 0 or 1 are considered.
        if (label != 0 and label != 1) return nil;
        # The length of the feature array determines
        # the length of the weight array.
        # The perceptron will continue learning as long as
        # it keeps seeing feature arrays of the same length.
        # When it sees a new data shape, it initializes.
        if (size(features) != size(weights)) {
            weights = features;
            bias = 1;
        }
        # Make a prediction based on current weights.
        var prediction = perceptron_model.predict(features);
        # Update the weights if the prediction is wrong.
        if (prediction != label) {
            var gradient = label - prediction;
            for (var i = 0; i < size(weights); i+=1) {
                weights[i] += gradient * features[i];
            }
            bias += gradient;
        }
        return perceptron_model;
    };

    # Conveniently access the weights array.
    perceptron_model.weights = func() {
        return weights;
    };

    # Conveniently access the bias.
    perceptron_model.bias = func() {
        return bias;
    };

    # Return the completed model.
    return perceptron_model;
}


# Create a perceptron model:
var p = perceptron();

# Train with a feature vector [0] that has label 1,
# and a feature vector [1] that has label 0.
p.train([0], 1);
p.train([1], 0);
p.train([0], 1);

# The perceptron has learned enough to classify correctly:
print( p.predict([0]) );
# 1
print( p.predict([1]) );
# 0

topics to be covered

  • pre-requisites (matrix maths)
  • What are neural networks?
  • advantages and disadvantages
  • use cases in FlightGear (AI, bombable, combat etc)
  • model of an artificial neuron
    • number of 1-n inputs
    • each input has one associated weight (positive/negative floating point factor)
    • computation of activation value (foreach input)
    • activation threshold
    • output signal
  • layering neural networks (input layer, processing layers, output layer)
  • types of networks
    • feedforward network
  • backpropagation
  • training a network
  • types of NN training
  • Implementing Neural Networks (NN) in Nasal
    • using vectors of inputs and associated input weights
    • using a Nasal hash as a helper object
  • Mapping properties to neural inputs
  • Mapping neural network outputs to properties


Links

Vehicle control

References

References