{"id":25615092,"url":"https://github.com/wvabrinskas/Neuron","last_synced_at":"2026-05-15T03:30:19.530Z","repository":{"id":43093760,"uuid":"278500312","full_name":"wvabrinskas/Neuron","owner":"wvabrinskas","description":"A neural network library for Swift ","archived":false,"fork":false,"pushed_at":"2025-02-14T13:38:46.000Z","size":32140,"stargazers_count":126,"open_issues_count":7,"forks_count":7,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-14T14:36:57.599Z","etag":null,"topics":["deep-learning","machine-learning","neural-network"],"latest_commit_sha":null,"homepage":"https://williamvabrinskas.com/Neuron/documentation/neuron/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wvabrinskas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"wvabrinskas","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2020-07-10T00:29:58.000Z","updated_at":"2025-02-06T16:28:24.000Z","dependencies_parsed_at":"2024-05-13T22:45:09.580Z","dependency_job_id":"ddf194de-e9c7-46d4-b57d-593b89008207","html_url":"https://github.com/wvabrinskas/Neuron","commit_stats":null,"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wvabrinskas%2FNeuron","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wvabrinskas%2FNeuron/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wvabrinskas%2FNeuron/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wvabrinskas%2FNeuron/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wvabrinskas","download_url":"https://codeload.github.com/wvabrinskas/Neuron/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240118380,"owners_count":19750484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deep-learning","machine-learning","neural-network"],"created_at":"2025-02-22T03:01:57.915Z","updated_at":"2026-05-15T03:30:19.263Z","avatar_url":"https://github.com/wvabrinskas.png","language":"Swift","funding_links":["https://github.com/sponsors/wvabrinskas"],"categories":["Swift"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg width=\"500\" src=\"images/neuron.png\"\u003e \n\u003c/p\u003e\n\n![](https://img.shields.io/github/v/tag/wvabrinskas/Neuron?style=flat-square)\n![](https://img.shields.io/github/license/wvabrinskas/Neuron?style=flat-square)\n![](https://img.shields.io/badge/swift-5.10.0-orange?style=flat-square)\n![](https://img.shields.io/badge/iOS-13+-darkcyan?style=flat-square)\n![](https://img.shields.io/badge/macOS-11+-darkcyan?style=flat-square)\n![](https://img.shields.io/badge/watchOS-6+-darkcyan?style=flat-square)\n![](https://img.shields.io/badge/tvOS-13+-darkcyan?style=flat-square)\n\n[![Tests](https://github.com/wvabrinskas/Neuron/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/wvabrinskas/Neuron/actions/workflows/tests.yml)\n\n\u003ca href=\"https://www.emergetools.com/app/example/ios/Neuron/release?utm_campaign=badge-data\"\u003e\u003cimg src=\"https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.emergetools.com%2Fapi%2Fv2%2Fpublic_new_build%3FexampleId%3DNeuron%26platform%3Dios%26badgeOption%3Dmax_install_size_only%26buildType%3Drelease\u0026query=$.badgeMetadata\u0026label=Neuron\u0026logo=apple\" /\u003e\u003c/a\u003e\n\n# Sponsors\nWe are incredibly grateful for the support of our sponsors\n\n\u003ca href=\"https://www.emergetools.com\"\u003e\u003cimg width=\"100\" style=\"border-radius: 15px;\" src=\"images/emerge-square.png\"\u003e\u003c/a\u003e\n\n# Become a Sponsor\nTo become a sponsor and support the development of Neuron, simply click on the \"Sponsor\" button at the top of this GitHub repository.\n\n# Support \n[\u003cimg width=\"75\" src=\"images/twitter.png\"\u003e ](https://twitter.com/wvabrinskas)\n[\u003cimg width=\"75\" src=\"images/discord.png\"\u003e](https://discord.gg/p84DYph4PW)\n[\u003cimg width=\"75\" src=\"images/www.png\"\u003e](https://williamvabrinskas.com)\n\nFeel free to send me suggestions on how to improve this. I would be delighted to learn more!! You can also feel free to assign issues here. Run the unit tests as well to learn how the project works!\n\n[Full Documentation](https://williamvabrinskas.com/Neuron/documentation/neuron/)\n\n# Important Developer Note!\n\n## When using Neuron it operates about 10X faster when you run with a `RELEASE` scheme. This is due to the compiler optimizations being set to the highest optimization value. If you find Neuron is running somewhat slowly this might be a reason why. \n\n# Before you begin developing\nRun `./scripts/onboard.sh` to install the Xcode templates that `Neuron` provides to quickly generate layer code templates.\n\n# Contribution Policies\n## Filing Issues\nFeel free to file issues about the framework here or contact me through the [Discord](https://discord.gg/p84DYph4PW). I am open to all suggestions on how to improve the framework. \n\n## Pull Requests\nThere are automated tests that run when a PR is created to the `develop` or `master` branches. These tests must pass before a PR can be merged. All PRs must merge into the `develop` branch. \n## Branching\nAll features must be branched off the `develop` branch. \n\n# Background\n\nNeuron has been a pet project of mine for years now. I set off to learn the basics of ML and I figured the best way to learn it was to implement it myself. I decided on Swift because it was the language I knew the most and I knew it would be challenging to optimize for ML as it has a lot of overhead. What you're seeing here in this repository is an accumulation of my work over the past 2 years or so. It is my baby. I decided to make this open source as I wanted to share what I've learned with the ML and Swift community. I wanted to give users of this framework the opportunity to learn and implement ML in their projects or apps. Have fun! \n\nThere is still a lot missing in this framework but with this rewrite I brought a lot more flexibity to the framework to allow for playing around with different architectures and models. There are some example models provided with the framework, like Classifier, GAN, WGAN, and WGANGP. I am always working on this project and will continue to provide updates. \n\n# Sample projects \n\n[Neuron Demo](https://github.com/wvabrinskas/NeuronDemo)\n- A \"Get Started with Neuron\" project. Should help with the learning the basics of the framework.\n\n[PokePal](https://github.com/wvabrinskas/PokePal)\n- An example project that uses a pre-trained model and your camera to identify all Pokemon currently.\n\n# Examples\n## GAN, WGAN, WGANGP\n\nGenerated 7's from a WGAN. Trained on MNIST 7's for 10 epochs. 16 - 32 kernels on the generator. \n\n\u003cimg width=\"100\" src=\"images/700.png\"\u003e\n\u003cimg width=\"90\" src=\"images/701.png\"\u003e\n\u003cimg width=\"85\" src=\"images/702.png\"\u003e\n\n# Important Note: GPU Support (WIP)\nCurrently there is no GPU execution, at least not as how I would like it. Everything runs on the CPU, with some C optimizations for certain mathematical functions. Neuron will run multithreaded on the CPU with somewhat decent speed depending on the model. However a very large model with serveral kernels and convolutions will take a while. This is something I want to get working ASAP however Metal is very difficult to work with, especially with my limited knowledge and my desire to write everything from scratch. \n\n# Quick Start Guide\nTo get started with Neuron it all begins with setting up a `Sequential` object. This object is responsible for organizing the forward and backward passes through the network. \n\n## Build a Network\nLet's build an `MNIST` classifier network. We need to build a `Sequential` object to handle our layers.\n\n```swift\nlet network = Sequential {\n  [\n    Conv2d(filterCount: 16,\n            inputSize: TensorSize(array: [28,28,1]),\n            padding: .same,\n            initializer: initializer),\n    BatchNormalize(),\n    LeakyReLu(limit: 0.2),\n    MaxPool(),\n    Conv2d(filterCount: 32,\n            padding: .same,\n            initializer: initializer),\n    BatchNormalize(),\n    LeakyReLu(limit: 0.2),\n    Dropout(0.5),\n    MaxPool(),\n    Flatten(),\n    Dense(64, initializer: initializer),\n    LeakyReLu(limit: 0.2),\n    Dense(10, initializer: initializer),\n    Softmax()\n  ]\n}\n```\n\n`Sequential` takes in one property which is block that returns an array of `Layer` types. `init(_ layers: () -\u003e [Layer])`. The order here matters. The first layer is a `Conv2d` layer with 16 filters, padding `.same`, and an initializer. The default initializer is `.heNormal`. \n\n### *First layer note*:\nYou can see here the first layer is the only layer where the `inputSize` is specified. This is because all the other layer's `inputSize` are automatically calculated when added to an `Optimizer`. \n\n## Picking an Optimizer\nNeuron uses a `protocol` that defines what's needed for an `Opitmizer`. There are currently three provided optimizers bundled with Neuron. \n- `Adam`\n- `SGD`\n- `RMSProp`\n\nAll optimizers are interchangeable. Optimizers are the \"brain\" of the network. All function calls to train the network should be called through your specific `Optimizer`. Let's build an `Adam` optimizer for this classifier. \n\n```swift\nlet optim = Adam(network,\n                 learningRate: 0.0001,\n                 l2Normalize: false)\n```\n\nThe first parameter here is the network we defined above. `learningRate` is the the size of the steps the optimizer will take when `.step()` is called. `l2Normalize` defines if the optimizer will normalize the gradients before they are applied to the weights. Default value for this is `false`. `Adam` also takes in properties for its `beta1`, `beta2`, and `epsilon` properties. \n\n### Decay Functions\nAn `Optimizer` has an optional property for setting a learning rate decay function. \n```swift\npublic protocol DecayFunction {\n  var decayedLearningRate: Tensor.Scalar { get }\n  func reset()\n  func step()\n}\n```\nCurrently there's only one available `DecayFunction` and that's `ExponentialDecay`. You can set a decay function by setting the `decayFunction` property on the `Optimizer`. Once it's set there's nothing else that needs to be done. The `Optimizer` with take care of updating and calling the function object.\n\n## Set up training model\nBy this point you are ready to train the `Optimizer` and the network. You could create your own training cycle to create the classifier or you can use the built in provided `Classifier` model. \n\nThe `Classifier` model is a completely optional class that does a lot of the heavy lifting for you when training the network. Let's use that for now.\n\n```swift\nlet classifier = Classifier(optimizer: optim,\n                            epochs: 10,\n                            batchSize: 32,\n                            threadWorkers: 8,\n                            log: false)\n```\n\nHere we create a `Classifier` object. We pass in the `Adam Optimizer` we defined earlier, the number of `epochs` to train for, the batch size, and the number of `multi-threaded` workers to use. `8` or `16` is usually a good enough number for this. This will split the batch up over multiple threads allowing for faster exectution. \n\n### Building the MNIST dataset\n\n*NOTE: Be sure to import [NeuronDatasets](https://github.com/wvabrinskas/NeuronDatasets) to get the `MNIST` and other datasets.*\n\nNext step to get the `MNIST` dataset. Neuron provides this locally to you through the `MNIST()` object.\n\n```swift\nlet data = await MNIST().build()\n```\n\nWe can use the `Async/Await` build function for simplicity. `Datasets` also support `Combine` publishers.\n\n## Time to train!\nTo train the network using the `Classifier` object just call `    classifier.fit(data.training, data.val)`. That's it! The `Classifier` will now train for the specified number of `epochs` and report to the `MetricProvider` if set. \n## Retrieving Metrics\nAll `Optimizers` support the addition of a `MetricsReporter` object. This object will track all metrics you ask it to during the initialization. If the metric isn't supported by your netowrk setup it will report a `0`. \n\n```swift\nlet reporter = MetricsReporter(frequency: 1,\n                                metricsToGather: [.loss,\n                                                  .accuracy,\n                                                  .valAccuracy,\n                                                  .valLoss])\n\noptim.metricsReporter = reporter\n\noptim.metricsReporter?.receive = { metrics in\n  let accuracy = metrics[.accuracy] ?? 0\n  let loss = metrics[.loss] ?? 0\n  //let valLoss = metrics[.valLoss] ?? 0\n  \n  print(\"training -\u003e \", \"loss: \", loss, \"accuracy: \", accuracy)\n}\n```\n\nThe `metricsToGather` array is a `Set` of `Metric` definitions. \n\n```swift\npublic enum Metric: String {\n  case loss = \"Training Loss\"\n  case accuracy = \"Accuracy\"\n  case valLoss = \"Validation Loss\"\n  case generatorLoss = \"Generator Loss\"\n  case criticLoss = \"Critic Loss\"\n  case gradientPenalty = \"Gradient Penalty\"\n  case realImageLoss = \"Real Image Loss\"\n  case fakeImageLoss = \"Fake Image Loss\"\n  case valAccuracy = \"Validation Accuracy\"\n}\n```\n\n`MetricReporter` will call `receive` when it is updated. \n\n### Remote metric logging\n\nYou can use the [NeuronRemoteLogger](https://github.com/wvabrinskas/NeuronRemoteLogger) to log to remote services like [Weights and Biases](https://wandb.ai/). Follow the instructions in the `README` in that repo on how to get started! \n\n## Exporting your model\n\nOnce the model has trained to your liking you can export the model to a `.smodel` file. This model cvan be then imported later using the `Sequential` intializer. The export will not export your `Optimizer` settings, only the `Trainable` specified in the `Optimizer`. \n\nNeuron provides a helper object for exporting called `ExportHelper`. The usage is simple: \n\n```swift\n// defined: \npublic static func getModel\u003cT: Codable\u003e(filename: String = \"model\", model: T) -\u003e URL?\n\n// usage:\nExportHelper.export(filename: \"my_model\", model: network)\n```\n\nThis will return a `URL` for you to access your `.smodel` file. \n\n### Pretty-print your network\nYou can also print your network to the console by calling `print` on the `Sequential` object. It will pretty print your network as below:\n\n\u003cimg width=\"400\" src=\"images/print-example.png\"\u003e \n\n\n## Finishing up\nKeep playing around with your new model and enjoy the network! Share your model on the Discord or ask for some other models that others have made! \n# Basics Background\n## How does Neuron work? \n## Tensor\nThe main backbone of Neuron is the `Tensor` object. This object is basically a glorified 3D array of numbers. All `Tensor` objects are 3D arrays however they can contain any type of array in-between. Its size is defined by a `TensorSize` object defining `columns`, `rows`, `depth`.\n\n```swift\npublic class Tensor: Equatable, Codable {\n  ...\n    \n  public init() {\n    self.value = []\n    self.context = TensorContext()\n  }\n  \n  public init(_ data: Scalar? = nil, context: TensorContext = TensorContext()) {\n    if let data = data {\n      self.value = [[[data]]]\n    } else {\n      self.value = []\n    }\n    \n    self.context = context\n  }\n  \n  public init(_ data: [Scalar], context: TensorContext = TensorContext()) {\n    self.value = [[data]]\n    self.context = context\n  }\n  \n  public init(_ data: [[Scalar]], context: TensorContext = TensorContext()) {\n    self.value = [data]\n    self.context = context\n  }\n  \n  public init(_ data: Data, context: TensorContext = TensorContext()) {\n    self.value = data\n    self.context = context\n  }\n}\n```\n\nAbove are the initializers that `Tensor` supports. More in-depth documentation on `Tensor` can be found [here](https://williamvabrinskas.com/Neuron/documentation/neuron/tensor). \n\n### Arithmetic\nYou can perform basic arithmetic opterations directly to a `Tensor` object as well. \n```swift\nstatic func * (Tensor, Tensor.Scalar) -\u003e Tensor\nstatic func * (Tensor, Tensor) -\u003e Tensor\nstatic func + (Tensor, Tensor) -\u003e Tensor\nstatic func + (Tensor, Tensor.Scalar) -\u003e Tensor\nstatic func - (Tensor, Tensor.Scalar) -\u003e Tensor\nstatic func - (Tensor, Tensor) -\u003e Tensor\nstatic func / (Tensor, Tensor.Scalar) -\u003e Tensor\nstatic func / (Tensor, Tensor) -\u003e Tensor\nstatic func == (Tensor, Tensor) -\u003e Bool\n```\n\n### Building a backpropagation graph\nYou can attach a `Tensor` to another `Tensor`'s graph by calling `setGraph(_ tensor: Tensor)` on the `Tensor` whose `graph` you'd like to set. \n\n```swift\nlet inputTensor = Tensor([1,2,3,4])\nvar outputTensor = Tensor([2])\n\noutputTensor.setGraph(inputTensor)\n```\n\nDoing so will set the `inputTensor` as the `graph` to the `outputTensor`. This means that when calling `.gradients` on the `outputTensor` the operation will look as such: \n\n```\ndelta -\u003e outputTensor.context(inputTensor) -\u003e gradients w.r.t to inputTensor\n```\n\nUnless you're building a graph yourself or doing something custom, you'll never have to set a graph yourself. This will be handled by the `Sequential` object.\n\n### Gradients\nMore in-depth `TensorContext` documentation can be found [here](https://williamvabrinskas.com/Neuron/documentation/neuron/tensorcontext).\n\nNeuron performs gradient descent operations using `Tensor` objects and their accompanying `TensorContext`.\n`Tensor` objects contain an internal property called `context` which is of type `TensorContext`. `TensorContext` is an object that contains the backpropagtion information for that given `Tensor`. As of right now Neuron doesn't have a full auto-grad setup yet however `Tensor` objects with their `TensorContext` provides some type of auto-grad. \n\n```swift\npublic struct TensorContext: Codable {\n  public typealias TensorBackpropResult = (input: Tensor, weight: Tensor)\n  public typealias TensorContextFunction = (_ inputs: Tensor, _ gradient: Tensor) -\u003e TensorBackpropResult\n  var backpropagate: TensorContextFunction\n  \n  public init(backpropagate: TensorContextFunction? = nil) {\n    let defaultFunction = { (input: Tensor, gradient: Tensor) in\n      return (Tensor(gradient.value), Tensor())\n    }\n    \n    self.backpropagate = backpropagate ?? defaultFunction\n  }\n  \n  public func encode(to encoder: Encoder) throws {}\n  \n  public init(from decoder: Decoder) throws {\n    self = TensorContext()\n  }\n}\n```\n\nWhen calling `.gradients(delta: SomeTensor)` on a `Tensor` that has an attached `graph` it wil automatically backpropagate all the way through the `graph` and return a `Tensor.Gradient` object. \n\n```swift\npublic struct Gradient {\n  let input: [Tensor]\n  let weights: [Tensor]\n  let biases: [Tensor]\n  \n  public init(input: [Tensor] = [],\n              weights: [Tensor] = [],\n              biases: [Tensor] = []) {\n    self.input = input\n    self.weights = weights\n    self.biases = biases\n  }\n}\n```\n\nA `Tensor.Gradient` object will contain all the gradients you'll need to perform a backpropagation step in the `Optimizer`. This object contains gradients w.r.t the `input`, w.r.t the `weights`, and w.r.t the `biases` of the graph. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwvabrinskas%2FNeuron","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwvabrinskas%2FNeuron","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwvabrinskas%2FNeuron/lists"}