Giter VIP home page Giter VIP logo

neuralnet's Introduction

Banner

NeuralNet

This is the NeuralNet module for the Swift AI project. Full details on the project can be found in the main repo.

The NeuralNet class contains a fully connected, feed-forward artificial neural network. This neural net offers support for deep learning, and is designed for flexibility and use in performance-critical applications.

Importing

Swift Package Manager

SPM makes it easy to import the package into your own project. Just add this line to the dependencies in package.swift:

.Package(url: "https://github.com/Swift-AI/NeuralNet.git", majorVersion: 0, minor: 3)

Manually

Since iOS and Cocoa applications aren't supported by SPM yet, you may need to import the package manually. To do this, simply drag and drop the files from Sources into your project.

This isn't as elegant as using a package manager, but we anticipate SPM support for these platforms soon. For this reason we've decided not to use alternatives like CocoaPods.

Initialization

NeuralNet relies on a helper class called Structure for setting up the neural network. As its name implies, this class defines the overall structure of the network. Its initializer accepts six arguments:

  • nodes: An array [Int] designating the number of nodes (neurons) in each layer of the neural network. The first entry in the array corresponds to the number of inputs in the network; the lasy entry is the number of outputs. All other entries are hidden layers.
  • hiddenActivation: An ActivationFunction to apply to all hidden layers during inference. Several defaults are provided; you may also provide a custom function if desired.
  • outputActivation: An ActivationFunction to apply to the network's output layer.
  • batchSize: The number of input sets in each batch that will be fed into the neural network. Default 1.
  • learningRate: A learning rate to apply during backpropagation. Default 0.5.
  • momentum: A momentum factor to apply during backpropagation. Default 0.9.

Note: If you intend to use the network for inference only (no training), you may omit learningRate and momentum. Default values will be used, but they will not affect the network's output.

let structure = try NeuralNet.Structure(nodes: [784, 500, 10],
                                        hiddenActivation: .rectifiedLinear, outputActivation: .softmax,
                                        batchSize: 100, learningRate: 0.8, momentum: 0.9)

Once you've defined the structure, you're ready to create your NeuralNet:

let nn = try NeuralNet(structure: structure)

From Storage

A trained NeuralNet can also be persisted to disk for later use:

let url = URL(fileURLWithPath: "/path/to/file")
try nn.save(to: url)

And can likewise be initialized from a URL:

let nn = try NeuralNet(url: url)

Inference

You perform inference using the infer method, which accepts either an array [Float] or 2D array [[Float]]. The method chosen depends on the structure of your data:

Single Set

If you set batchSize to 1 during initialization, you will most likely want to feed inputs using a 1-dimensional array [Float]. The size of the array must equal the number of inputs to the neural network.

let input: [Float] = [1, 2, 3, 4] // ...
let output = try nn.infer(input)

Minibatch

For convenience, the infer method may also accept a 2D array [[Float]] for minibatch inference. Each inner array [Float] is a single set of inputs for the neural network, and the outer array is a full batch. The size of the outer array must equal the batchSize defined during initialization.

let inputBatch: [[Float]] = [
    [1, 2, 3, 4],
    [4, 3, 2, 1]
]
let outputs = try nn.infer(inputBatch)

Note: Either method may always be used, regardless of batchSize, as long as the data is structured appropriately. For example, if batchSize is > 1 you may still feed inputs as a 1-dimensional array [Float] by serializing all inputs into a single array. Likewise, if batchSize is 1 you may still organize your inputs into a 2D array [[Float]] with only a single inner array. The result is the same; it is simply a matter of convenience.

Training

What good is a neural network that hasn't been trained? You have two options for training your net:

Automatic Training

Your net comes with a train method that attempts to perform all training steps automatically. This method is recommended for simple applications and newcomers, but might be too limited for advanced users.

In order to train automatically you must first create a Dataset, which is a simple container for all training and validation data. The object accepts 5 parameters:

  • trainInputs: A 2D array [[Float]] containing all sets of training inputs. Each set must be equal in size to your network's inputs.
  • trainLabels: A 2D array [[Float]] containing all labels corresponding to each input set. Each set must be equal in size to your network's outputs, and the number of sets must match trainInputs.
  • validationInputs: Same as trainInputs, but a unique set of data used for network validation.
  • validationLabels: Same as trainLabels, but a unique validation set corresponding to validationInputs.
  • structure: This should be the same Structure object used to create your network. If you initialized the network from disk, or don't have access to the original Structure, you can access it as a property on your net: nn.structure. Providing this parameter allows Dataset to perform some preliminary checks on your data, to help avoid issues later during training.

Note: The validation data will NOT be used to train the network, but will be used to test the network's progress periodically. Once the desired error threshold on the validation data has been reached, the training will cease. Ideally, the validation data should be randomly selected and representative of the full search space.

let dataset = try NeuralNet.Dataset(trainInputs: myTrainingData,
                                    trainLabels: myTrainingLabels,
                                    validationInputs: myValidationData,
                                    validationLabels: myValidationLabels,
                                    structure: structure)

One you have a dataset, you're ready to train the network. One more parameter is required to kick off the training process:

  • errorThreshold: The minimum average error to achieve before training is allowed to stop. This error is calculated using your network's cost function, and is averaged across all validation sets. Error must be positive and nonzero.
try nn.train(dataset, errorThreshold: 0.001)

Note: Training will continue until the average error drops below the provided threshold. Be careful not to provide too low of a value, or training may take a very long time or get stuck in a local minimum.

Manual Training

You have the option to train your network manually using a combination of inference and backpropagation. This method is ideal if you require fine-grained control over the training process.

The backpropagate method accepts the single set of output labels corresponding to the most recent call to infer. Internally, the network will compare the labels to its actual output, and apply stochastic gradient descent using the learningRate and momentum values provided earlier. Over many cycles, this will shift the network's weights closer to the "true" answer.

The backpropagate method does not return a value.

let err = try nn.backpropagate([myLabels])

A full training routine might look something like this:

let trainInputs: [[Float]] = [[/* Training data here */]]
let trainLabels: [[Float]] = [[/* Training labels here */]]
let validationInputs: [[Float]] = [[/* Validation data here */]]
let validationLabels: [[Float]] = [[/* Validation labels here */]]

// Loop forever until desired network accuracy is met
while true {
    // Perform one training epoch on training data
    for (inputs, labels) in zip(trainInputs, trainLabels) {
        try nn.infer(inputs)
        try nn.backpropagate(labels)
    }
    // After each epoch, check progress on validation data
    var error: Float = 0
    for (inputs, labels) in zip(validationInputs, validationLabels) {
        let outputs = try nn.infer(inputs)
        // Sum the error of each output node
        error += nn.costFunction.cost(real: outputs, target: labels)
    }
    // Calculate average error
    error /= Float(validationInputs.count)
    if error < DESIRED_ERROR {
        // SUCCESS
        break
    }
}

Note from this example that your network's cost function is a public property. This allows you to calculate error using the same function that's used for backpropagation, if desired. In addition, you have the ability to tune the network's learningRate and momentum parameters during training to achieve fine-tuned results.

Reading and Modifying

A few methods and properties are provided to access or modify the state of the neural network:

  • allWeights - Returns a serialized array of the network's current weights at any point:
let weights = nn.allWeights()
  • setWeights - Allows the user to reset the network with custom weights at any time. Accepts a serialized array [Float], as returned by the allWeights method:
try nn.setWeights(weights)

Additinally, learningRate and momentum are mutable properties on NeuralNet that may be safely tuned at any time.

Additional Information

To achieve nonlinearity, NeuralNet uses a one of several activation functions for hidden and output nodes, as configured during initialization. Because of this property, you will achieve better results if the following points are taken into consideration:

  • Input data should generally be normalized to have a mean of 0 and standard deviation of 1.
  • For most activation functions, outputs will reside in the range (0, 1). For regression problems, a wider range is often needed and thus the outputs must be scaled accordingly.
  • When providing labels for backpropagation, you may also need to scale the data in reverse (for applicable activations).

neuralnet's People

Contributors

albanperli avatar collinhundley avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

neuralnet's Issues

file formats / accuracy / relevant readings / enhancements

Hi @collinhundley
I acknowledge your efforts in bringing the maths / machine learning to these libraries. The code looks well commented - and also quite neat in it's implementation. I'm studying ML in spare time so been reviewing linear algebra / calculus / to get head around the maths before taking on bringing tensorflow swift port in due course.

It's quite an achievement. I been looking through code and shopping around for best implementation for machine learning in swift. you maybe interested to know of this sibling MNIST trainer in swift too.
I liked the way donald implemented a wrapper around linear algebra for accelerate platform.
https://github.com/donald-pinckney/SwiftNum/tree/master/Sources/Linear

I came across this interesting codebase -
https://github.com/hollance/BNNS-vs-MPSCNN

Important to note - Apple's showcase AI example here
https://developer.apple.com/library/content/samplecode/MPSCNNHelloWorld/Introduction/Intro.html

Digging through the code - Apple have this format h5 format to save their models.
When I was comparing SwiftLearn with NeuralNet - it was obvious that there was differences in how each library is importing data / - so hard to compare accuracy of trainers. Got me thinking -there's got to be a way to swap out the data between frameworks. Did you consider going down Apple's route ? it may not be best answer as they're targeting gpu or cpu.
screen shot 2017-05-14 at 17 39 36

http://stackoverflow.com/questions/40522224/mpscnn-weight-ordering

https://github.com/kazoo-kmt/MPSCNNfeeder

I also came across this other code that has similar implementation
https://github.com/kazoo-kmt/test_MPSCNNfeeder

img_0183

screen shot 2017-05-14 at 17 13 19

Naturally - getting ai models on par with tensorflow would take a lot of resources / but it would be neat to see better / comparable results. It seems there's some large gap in accuracy of the training when compared against test_MPSCNNfeeder.

Saving/Loading network doesn’t work properly

Hi @collinhundley,
the network is not saved properly, so it’s not loaded properly.
The costKey is not filled,

public func save(to url: URL) throws {

        let json: [String : Any] = [
            NeuralNet.inputsKey : structure.inputs,
            NeuralNet.hiddenKey : structure.hidden,
            NeuralNet.outputsKey : structure.outputs,
            NeuralNet.momentumKey : momentumFactor,
            NeuralNet.learningKey : learningRate,
            NeuralNet.hiddenActivationKey : hiddenActivation.stringValue(),
            NeuralNet.outputActivationKey : outputActivation.stringValue(),
            NeuralNet.weightsKey : allWeights(),
        ]
   
         // ...
}

but it’s mandatory in the init function

guard let inputs = array[NeuralNet.inputsKey] as? Int,
            let hidden = array[NeuralNet.hiddenKey] as? Int,
            let outputs = array[NeuralNet.outputsKey] as? Int,
            let momentum = array[NeuralNet.momentumKey] as? Float,
            let lr = array[NeuralNet.learningKey] as? Float,
            let hiddenActivationStr = array[NeuralNet.hiddenActivationKey] as? String,
            let outputActivationStr = array[NeuralNet.outputActivationKey] as? String,
            let costStr = array[NeuralNet.costKey] as? String,
            let weights = array[NeuralNet.weightsKey] as? [Float]
            else {
                throw Error.initialization("One or more required NeuralNet properties are missing.")
        }

Fixed by adding:
NeuralNet.costKey : costFunction.stringValue() in the saved json array.

Thx for the good work!

Alban

No biases saved in Storage.swift

Hi, I noted different output values (with same input) after saving and restoring a neural network in url. I discovered that in saving methods of neural network (Storage.swift), only weights are saved but not biases. Little modifications to save and restore both weights and biases correct this issue.

Mean-square error calculation

Hi there,
I think the mean-square error calculation might have a problem. It doesn't seem to be accumulating values. That swift syntax is a little unfamiliar to me, so I tried typing it out verbosely:

var errorSum = Float(0)
                for x in 0 ..< real.count {
                    let error = (real[x] - target[x]) * (real[x] - target[x])
                    errorSum += error
                }
                errorSum /= Float(real.count)

... and the result was different.
Hope this helps your project, apologies if I have got it wrong, best wishes,
Simon.

Cocoapods distribution?

I've read in README.md that You would rather like to use SPM, even though It would take a while until It would be supported.
If someone would make Pull Request that would allow to distribute Your magnificent Library through Cocoapods, would You re-consider using NeuralNet with Cocoapods?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.