train(features, label) {
        // Require that only labels of 0 or 1 are considered.
        if (label !== 0 && label !== 1) {
            return null;
        }
        // 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 (features.length !== this.weights.length) {
            this.weights = features;
            this.bias = 1;
        }
        // Make a prediction based on current weights.
        const prediction = this.predict(features);
        // Update the weights if the prediction is wrong.
        if (typeof prediction === "number" && prediction !== label) {
            const gradient = label - prediction;
            for (let i = 0; i < this.weights.length; i++) {
                this.weights[i] += gradient * features[i];
            }
            this.bias += gradient;
        }
        return this;
    }
**Train** the classifier with a new example, which is
a numeric array of features and a 0 or 1 label.

@param {Array<number>} features an array of features as numbers
@param {number} label either 0 or 1
@returns {PerceptronModel} this