├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── BiasNeuron.swift ├── Functions.swift ├── Layer.swift ├── Network.swift └── Neuron.swift ├── SwiftSimpleNeuralNetwork.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── SwiftSimpleNeuralNetwork ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── MainMenu.xib ├── Info.plist └── MNIST Data │ ├── t10k-images.idx3-ubyte │ ├── t10k-labels.idx1-ubyte │ ├── train-images.idx3-ubyte │ └── train-labels.idx1-ubyte ├── SwiftSimpleNeuralNetworkTests ├── Info.plist ├── IrisTest.swift ├── SinTest.swift ├── WineTest.swift ├── iris.csv └── wine.csv └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .Trashes 3 | *.swp 4 | # Xcode 5 | # 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | 23 | # CocoaPods 24 | # 25 | # We recommend against adding the Pods directory to your .gitignore. However 26 | # you should judge for yourself, the pros and cons are mentioned at: 27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 28 | # 29 | # Pods/ 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "SwiftSimpleNeuralNetwork" 5 | ) 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftSimpleNeuralNetwork 2 | A simple multi-layer feed-forward neural network with backpropagation built in Swift. 3 | 4 | ## Philosophy 5 | This *teaching* project is proclaimed *simple* for two reasons: 6 | - The code aims to be simple to understand (even at the expense of performance). I built this project to learn more about implementing neural networks. It does not aim to be state of the art or feature complete, but instead approachable. 7 | - The type of neural network targetted is very specific - only multi-layer feed-forward backpropagation networks. Why? Because we're keeping it simple, 8 | 9 | Contributions to the project will be measured not only by their functional aspects (improved performance, more features) but also by how much they stick to the philosophy. 10 | 11 | ## Installation 12 | 13 | **The project requires Xcode 10.2 and Swift 5.** 14 | 15 | ### Manual 16 | 17 | For the present, the best way to try the project out is through the wine and iris Xcode unit tests. Just download or clone the repository and run them from within Xcode. 18 | 19 | ### SPM 20 | 21 | You can also install the project's main files (but not the unit tests) through SPM via this repository. 22 | 23 | ## Mac Example App 24 | 25 | The example app for macOS that comes with SwiftSimpleNeuralNetwork trains on 60,000 MNIST sample images and then predicts another 10,000 testing images. In my testing it reaches as high as 94% accuracy without much tuning (just many batches of training on the full dataset). This is definitely not state of the art, but for a *simple* neural network, it's a decent demonstration. 26 | 27 | ![MNIST Example App Screenshot](screenshot.png) 28 | 29 | ## Unit Tests 30 | 31 | A check indicates a test is passing/working. 32 | - [x] `IrisTest.swift` uses the classic data set (contained in `iris.csv`) to classify 150 irises by four attributes. 33 | - [x] `WineTest.swift` uses a data set of 178 wines across thirteen attributes (contained in `wine.csv`) to classify wines by cultivar (three cultivars total). The test trains on the first 150 and then validates itself by classifying the remaining 28. 34 | - [ ] `SinTest.swift` tries to learn to approximate the sin() function. ~80% of predictions come close to correct values. 35 | 36 | ## Book Chapter 37 | 38 | Chapter 7 of [Classic Computer Science Problems in Swift](https://github.com/davecom/ClassicComputerScienceProblemsInSwift) is based on this project. It contains it a step-by-step tutorial, explaining how a slightly more primitive version of the project works. 39 | 40 | ## License, Contributions, and Attributions 41 | 42 | SwiftSimpleNeuralNetwork is Copyright 2016-2019 David Kopec and licensed under the Apache License 2.0 (see LICENSE). As per the Apache license, contributions are also Apache licensed by default. And contributions are welcome! 43 | 44 | The wine and iris datasets in the unit tests are provided curtosy of the UCI Machine Learning Repository which should be cited as: 45 | > Lichman, M. (2013). UCI Machine Learning Repository [http://archive.ics.uci.edu/ml]. Irvine, CA: University of California, School of Information and Computer Science. 46 | 47 | The MNIST dataset is from [LeCun, Cortes, and Burges](http://yann.lecun.com/exdb/mnist/). 48 | 49 | The overall neural network algorithm implemented throughout the project was derived primarily from Chapter 18 of Artificial Intelligence: A Modern Approach (Third Edition) by Stuart Russell and Peter Norvig. 50 | 51 | A few small individual utility functions in `Functions.swift` are from third party sources and cited appropriately in-source. 52 | 53 | ## Future Directions 54 | 55 | - Improved in-source documentation 56 | - Improved documentation in this README 57 | - More unit tests 58 | - More activation functions 59 | - Utility function to archive (serialize) and recreate (deserialize) trained neural networks 60 | - Better testing of networks with more than one hidden layer 61 | - Improved performance 62 | -------------------------------------------------------------------------------- /Sources/BiasNeuron.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BiasNeuron.swift 3 | // SwiftSimpleNeuralNetwork 4 | // 5 | // Created by David Kopec on 3/13/18. 6 | // Copyright © 2018-2019 Oak Snow Consulting. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BiasNeuron: Neuron { 12 | // weights are dummies, so other algorithms don't have to change 13 | init(weights: [Double]) { 14 | super.init(weights: weights, activationFunction: { _ in return 0.0 }, derivativeActivationFunction: { _ in return 0.0 }) 15 | } 16 | override func output(inputs: [Double]) -> Double { 17 | return 1.0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Functions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Functions.swift 3 | // SwiftSimpleNeuralNetwork 4 | // 5 | // Copyright 2016-2019 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | 20 | import Accelerate 21 | import Foundation 22 | 23 | // MARK: Randomization & Statistical Helpers 24 | 25 | /// Create *number* of random Doubles between -1.0 and 1.0 26 | func randomWeights(number: Int) -> [Double] { 27 | return (0.. [Double] { 32 | return (0.. Double { 59 | return 1.0 / (1.0 + exp(-x)) 60 | } 61 | 62 | // as derived at http://www.ai.mit.edu/courses/6.892/lecture8-html/sld015.htm 63 | func derivativeSigmoid(_ x: Double) -> Double { 64 | let sigRes = sigmoid(x) 65 | return sigRes * (1 - sigRes) 66 | } 67 | 68 | func ReLU(_ x: Double) -> Double { 69 | return max(0, x) 70 | } 71 | 72 | func derivativeReLU(_ x: Double) -> Double { 73 | if x > 0 { return 1 } 74 | return 0 75 | } 76 | 77 | func tanh(_ x: Double) -> Double { 78 | return (exp(x) - exp(-x)) / (exp(x) + exp(-x)) 79 | } 80 | 81 | func derivativeTanh(_ x: Double) -> Double { 82 | return 1 - pow(tanh(x), 2) 83 | } 84 | 85 | // MARK: SIMD Accelerated Math 86 | 87 | // Based on example from Surge project 88 | // https://github.com/mattt/Surge/blob/master/Source/Arithmetic.swift 89 | /// Find the dot product of two vectors 90 | /// assuming that they are of the same length 91 | /// using SIMD instructions to speed computation 92 | func dotProduct(_ xs: [Double], _ ys: [Double]) -> Double { 93 | var answer: Double = 0.0 94 | vDSP_dotprD(xs, 1, ys, 1, &answer, vDSP_Length(xs.count)) 95 | return answer 96 | } 97 | 98 | // Based on example from Surge project 99 | // https://github.com/mattt/Surge/blob/master/Source/Arithmetic.swift 100 | /// Subtract one vector from another 101 | /// assuming that they are of the same length 102 | /// using SIMD instructions to speed computation 103 | public func sub(x: [Double], y: [Double]) -> [Double] { 104 | var results = [Double](y) 105 | catlas_daxpby(Int32(x.count), 1.0, x, 1, -1, &results, 1) 106 | 107 | return results 108 | } 109 | 110 | // Another Surge example, see above citation 111 | public func mul(x: [Double], y: [Double]) -> [Double] { 112 | var results = [Double](repeating: 0.0, count: x.count) 113 | vDSP_vmulD(x, 1, y, 1, &results, 1, vDSP_Length(x.count)) 114 | 115 | return results 116 | } 117 | 118 | // Another Surge example, see above citation 119 | public func sum(x: [Double]) -> Double { 120 | var result: Double = 0.0 121 | vDSP_sveD(x, 1, &result, vDSP_Length(x.count)) 122 | 123 | return result 124 | } 125 | 126 | // MARK: Random Number Generation 127 | 128 | // this struct & the randomFractional() function 129 | // based on http://stackoverflow.com/a/35919911/281461 130 | struct Math { 131 | nonisolated(unsafe) private static var seeded = false 132 | 133 | static func randomFractional() -> Double { 134 | 135 | if !Math.seeded { 136 | let time = Int(NSDate().timeIntervalSinceReferenceDate) 137 | srand48(time) 138 | Math.seeded = true 139 | } 140 | 141 | return drand48() 142 | } 143 | 144 | // addition, just multiplies random number by *limit* 145 | static func randomTo(limit: Double) -> Double { 146 | 147 | if !Math.seeded { 148 | let time = Int(NSDate().timeIntervalSinceReferenceDate) 149 | srand48(time) 150 | Math.seeded = true 151 | } 152 | 153 | return drand48() * limit 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Sources/Layer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Layer.swift 3 | // SwiftSimpleNeuralNetwork 4 | // 5 | // Copyright 2016-2019 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | class Layer { 20 | let previousLayer: Layer? 21 | var neurons: [Neuron] 22 | var outputCache: [Double] 23 | var hasBias: Bool = false 24 | 25 | // for future use in deserializing networks 26 | init(previousLayer: Layer? = nil, neurons: [Neuron] = [Neuron]()) { 27 | self.previousLayer = previousLayer 28 | self.neurons = neurons 29 | self.outputCache = Array(repeating: 0.0, count: neurons.count) 30 | } 31 | 32 | // main init 33 | init(previousLayer: Layer? = nil, numNeurons: Int, activationFunction: @escaping (Double) -> Double, derivativeActivationFunction: @escaping (Double)-> Double, learningRate: Double, hasBias: Bool = false) { 34 | self.previousLayer = previousLayer 35 | self.neurons = Array() 36 | self.hasBias = hasBias 37 | for _ in 0..(repeating: 0.0, count: neurons.count) 44 | } 45 | 46 | func outputs(inputs: [Double]) -> [Double] { 47 | if previousLayer == nil { // input layer (first layer) 48 | outputCache = hasBias ? inputs + [1.0] : inputs 49 | } else { // hidden layer or output layer 50 | outputCache = neurons.map { $0.output(inputs: inputs) } 51 | } 52 | return outputCache 53 | } 54 | 55 | // should only be called on an output layer 56 | func calculateDeltasForOutputLayer(expected: [Double]) { 57 | for n in 0.. Layers -> Neurons 23 | class Network { 24 | var layers: [Layer] 25 | 26 | init(layerStructure:[Int], activationFunction: @escaping (Double) -> Double = sigmoid, derivativeActivationFunction: @escaping (Double) -> Double = derivativeSigmoid, learningRate: Double = 0.25, hasBias: Bool = false) { 27 | if (layerStructure.count < 3) { 28 | print("Error: Should be at least 3 layers (1 input, 1 hidden, 1 output)") 29 | } 30 | layers = [Layer]() 31 | // input layer 32 | layers.append(Layer(numNeurons: layerStructure[0], activationFunction: activationFunction, derivativeActivationFunction: derivativeActivationFunction, learningRate: learningRate, hasBias: hasBias)) 33 | 34 | // hidden layers 35 | for x in layerStructure.enumerated() where x.offset != 0 && x.offset != layerStructure.count - 1 { 36 | layers.append(Layer(previousLayer: layers[x.offset - 1], numNeurons: x.element, activationFunction: activationFunction, derivativeActivationFunction: derivativeActivationFunction, learningRate: learningRate, hasBias: hasBias)) 37 | } 38 | 39 | // output layer (can't have bias node) 40 | layers.append(Layer(previousLayer: layers[layerStructure.count - 2], numNeurons: layerStructure.last!, activationFunction: activationFunction, derivativeActivationFunction: derivativeActivationFunction, learningRate: learningRate, hasBias: false)) 41 | } 42 | 43 | /// pushes input data to the first layer 44 | /// then output from the first as input to the second 45 | /// second to the third, etc. 46 | func outputs(input: [Double]) -> [Double] { 47 | return layers.reduce(input) { $1.outputs(inputs: $0) } 48 | } 49 | 50 | /// Figure out each neuron's changes based on the errors 51 | /// of the output versus the expected outcome 52 | func backPropagate(expected: [Double]) { 53 | //calculate delta for output layer neurons 54 | layers.last?.calculateDeltasForOutputLayer(expected: expected) 55 | //calculate delta for prior layers 56 | for l in (1..(inputs:[[Double]], expecteds:[T], interpretOutput: ([Double]) -> T) -> (correct: Int, total: Int, percentage: Double) { 96 | var correct = 0 97 | for (input, expected) in zip(inputs, expecteds) { 98 | let result = interpretOutput(outputs(input: input)) 99 | if result == expected { 100 | correct += 1 101 | } 102 | } 103 | let percentage = Double(correct) / Double(inputs.count) 104 | return (correct, inputs.count, percentage) 105 | } 106 | 107 | // for when result is a single neuron 108 | func validate(inputs:[[Double]], expecteds:[Double], accuracy: Double) -> (correct: Int, total: Int, percentage: Double) { 109 | var correct = 0 110 | for (input, expected) in zip(inputs, expecteds) { 111 | let result = outputs(input: input)[0] 112 | let difference = abs(expected - result) 113 | let percentOff = expected != 0 ? difference / expected : difference 114 | if percentOff < accuracy { 115 | correct += 1 116 | } 117 | } 118 | let percentage = Double(correct) / Double(inputs.count) 119 | return (correct, inputs.count, percentage) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Sources/Neuron.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Neuron.swift 3 | // SwiftSimpleNeuralNetwork 4 | // 5 | // Copyright 2016-2019 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | 20 | /// An individual node in a layer 21 | class Neuron { 22 | var weights: [Double] 23 | var activationFunction: (Double) -> Double 24 | var derivativeActivationFunction: (Double) -> Double 25 | var inputCache: Double = 0.0 26 | var delta: Double = 0.0 27 | var learningRate: Double 28 | 29 | init(weights: [Double], activationFunction: @escaping (Double) -> Double, derivativeActivationFunction: @escaping (Double) -> Double, learningRate: Double = 0.25) { 30 | self.weights = weights 31 | self.activationFunction = activationFunction 32 | self.derivativeActivationFunction = derivativeActivationFunction 33 | self.learningRate = learningRate 34 | } 35 | 36 | /// The output that will be going to the next layer 37 | /// or the final output if this is an output layer 38 | func output(inputs: [Double]) -> Double { 39 | inputCache = dotProduct(inputs, weights) 40 | return activationFunction(inputCache) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 550F28AE1D0C966300B11A8F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28AD1D0C966300B11A8F /* AppDelegate.swift */; }; 11 | 550F28B01D0C966300B11A8F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 550F28AF1D0C966300B11A8F /* Assets.xcassets */; }; 12 | 550F28B31D0C966300B11A8F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 550F28B11D0C966300B11A8F /* MainMenu.xib */; }; 13 | 550F28BC1D0C96CB00B11A8F /* Neuron.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28BB1D0C96CB00B11A8F /* Neuron.swift */; }; 14 | 550F28BE1D0C972200B11A8F /* Layer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28BD1D0C972200B11A8F /* Layer.swift */; }; 15 | 550F28C01D0C975600B11A8F /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28BF1D0C975600B11A8F /* Network.swift */; }; 16 | 550F28C21D0CA5E600B11A8F /* Functions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28C11D0CA5E600B11A8F /* Functions.swift */; }; 17 | 553E02871D22E41500E1C3B3 /* IrisTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553E02861D22E41500E1C3B3 /* IrisTest.swift */; }; 18 | 553E02891D22E55200E1C3B3 /* iris.csv in Resources */ = {isa = PBXBuildFile; fileRef = 553E02881D22E55200E1C3B3 /* iris.csv */; }; 19 | 555015B91D224DCB004D3F18 /* SinTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555015B81D224DCB004D3F18 /* SinTest.swift */; }; 20 | 555015C01D224E33004D3F18 /* Functions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28C11D0CA5E600B11A8F /* Functions.swift */; }; 21 | 555015C11D224E33004D3F18 /* Neuron.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28BB1D0C96CB00B11A8F /* Neuron.swift */; }; 22 | 555015C21D224E33004D3F18 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28BF1D0C975600B11A8F /* Network.swift */; }; 23 | 555015C31D224E33004D3F18 /* Layer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550F28BD1D0C972200B11A8F /* Layer.swift */; }; 24 | 5565EEEB2058E57F000C2E2F /* t10k-images.idx3-ubyte in Resources */ = {isa = PBXBuildFile; fileRef = 5565EEE72058E57E000C2E2F /* t10k-images.idx3-ubyte */; }; 25 | 5565EEEC2058E57F000C2E2F /* t10k-labels.idx1-ubyte in Resources */ = {isa = PBXBuildFile; fileRef = 5565EEE82058E57E000C2E2F /* t10k-labels.idx1-ubyte */; }; 26 | 5565EEED2058E57F000C2E2F /* train-images.idx3-ubyte in Resources */ = {isa = PBXBuildFile; fileRef = 5565EEE92058E57F000C2E2F /* train-images.idx3-ubyte */; }; 27 | 5565EEEE2058E57F000C2E2F /* train-labels.idx1-ubyte in Resources */ = {isa = PBXBuildFile; fileRef = 5565EEEA2058E57F000C2E2F /* train-labels.idx1-ubyte */; }; 28 | 5585848F1D25C3D400978802 /* wine.csv in Resources */ = {isa = PBXBuildFile; fileRef = 5585848E1D25C3D400978802 /* wine.csv */; }; 29 | 558584911D25C3FE00978802 /* WineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558584901D25C3FE00978802 /* WineTest.swift */; }; 30 | 559C37582058BB5400814909 /* BiasNeuron.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559C37572058BB5400814909 /* BiasNeuron.swift */; }; 31 | 559C37592058BF2000814909 /* BiasNeuron.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559C37572058BB5400814909 /* BiasNeuron.swift */; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXContainerItemProxy section */ 35 | 555015BB1D224DCB004D3F18 /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 550F28A21D0C966300B11A8F /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 550F28A91D0C966300B11A8F; 40 | remoteInfo = SwiftSimpleNeuralNetwork; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 550F28AA1D0C966300B11A8F /* SwiftSimpleNeuralNetwork.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSimpleNeuralNetwork.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 550F28AD1D0C966300B11A8F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | 550F28AF1D0C966300B11A8F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 550F28B21D0C966300B11A8F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 49 | 550F28B41D0C966300B11A8F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 550F28BB1D0C96CB00B11A8F /* Neuron.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Neuron.swift; path = Sources/Neuron.swift; sourceTree = ""; }; 51 | 550F28BD1D0C972200B11A8F /* Layer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Layer.swift; path = Sources/Layer.swift; sourceTree = ""; }; 52 | 550F28BF1D0C975600B11A8F /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Network.swift; path = Sources/Network.swift; sourceTree = ""; }; 53 | 550F28C11D0CA5E600B11A8F /* Functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Functions.swift; path = Sources/Functions.swift; sourceTree = ""; }; 54 | 553E02861D22E41500E1C3B3 /* IrisTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IrisTest.swift; sourceTree = ""; }; 55 | 553E02881D22E55200E1C3B3 /* iris.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iris.csv; sourceTree = ""; }; 56 | 555015B61D224DCB004D3F18 /* SwiftSimpleNeuralNetworkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftSimpleNeuralNetworkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 555015B81D224DCB004D3F18 /* SinTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SinTest.swift; sourceTree = ""; }; 58 | 555015BA1D224DCB004D3F18 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 5565EEE72058E57E000C2E2F /* t10k-images.idx3-ubyte */ = {isa = PBXFileReference; lastKnownFileType = file; path = "t10k-images.idx3-ubyte"; sourceTree = ""; }; 60 | 5565EEE82058E57E000C2E2F /* t10k-labels.idx1-ubyte */ = {isa = PBXFileReference; lastKnownFileType = file; path = "t10k-labels.idx1-ubyte"; sourceTree = ""; }; 61 | 5565EEE92058E57F000C2E2F /* train-images.idx3-ubyte */ = {isa = PBXFileReference; lastKnownFileType = file; path = "train-images.idx3-ubyte"; sourceTree = ""; }; 62 | 5565EEEA2058E57F000C2E2F /* train-labels.idx1-ubyte */ = {isa = PBXFileReference; lastKnownFileType = file; path = "train-labels.idx1-ubyte"; sourceTree = ""; }; 63 | 5585848E1D25C3D400978802 /* wine.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wine.csv; sourceTree = ""; }; 64 | 558584901D25C3FE00978802 /* WineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WineTest.swift; sourceTree = ""; }; 65 | 559C37572058BB5400814909 /* BiasNeuron.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BiasNeuron.swift; path = Sources/BiasNeuron.swift; sourceTree = ""; }; 66 | /* End PBXFileReference section */ 67 | 68 | /* Begin PBXFrameworksBuildPhase section */ 69 | 550F28A71D0C966300B11A8F /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | 555015B31D224DCB004D3F18 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | 550F28A11D0C966300B11A8F = { 87 | isa = PBXGroup; 88 | children = ( 89 | 550F28BA1D0C96A500B11A8F /* Sources */, 90 | 550F28AC1D0C966300B11A8F /* DemoApp */, 91 | 555015B71D224DCB004D3F18 /* SwiftSimpleNeuralNetworkTests */, 92 | 550F28AB1D0C966300B11A8F /* Products */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 550F28AB1D0C966300B11A8F /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 550F28AA1D0C966300B11A8F /* SwiftSimpleNeuralNetwork.app */, 100 | 555015B61D224DCB004D3F18 /* SwiftSimpleNeuralNetworkTests.xctest */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 550F28AC1D0C966300B11A8F /* DemoApp */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 5565EEEF2058E58C000C2E2F /* MNIST Data */, 109 | 550F28AD1D0C966300B11A8F /* AppDelegate.swift */, 110 | 550F28AF1D0C966300B11A8F /* Assets.xcassets */, 111 | 550F28B11D0C966300B11A8F /* MainMenu.xib */, 112 | 550F28B41D0C966300B11A8F /* Info.plist */, 113 | ); 114 | name = DemoApp; 115 | path = SwiftSimpleNeuralNetwork; 116 | sourceTree = ""; 117 | }; 118 | 550F28BA1D0C96A500B11A8F /* Sources */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 550F28C11D0CA5E600B11A8F /* Functions.swift */, 122 | 550F28BB1D0C96CB00B11A8F /* Neuron.swift */, 123 | 559C37572058BB5400814909 /* BiasNeuron.swift */, 124 | 550F28BF1D0C975600B11A8F /* Network.swift */, 125 | 550F28BD1D0C972200B11A8F /* Layer.swift */, 126 | ); 127 | name = Sources; 128 | sourceTree = ""; 129 | }; 130 | 555015B71D224DCB004D3F18 /* SwiftSimpleNeuralNetworkTests */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 5585848E1D25C3D400978802 /* wine.csv */, 134 | 553E02881D22E55200E1C3B3 /* iris.csv */, 135 | 555015B81D224DCB004D3F18 /* SinTest.swift */, 136 | 553E02861D22E41500E1C3B3 /* IrisTest.swift */, 137 | 558584901D25C3FE00978802 /* WineTest.swift */, 138 | 555015BA1D224DCB004D3F18 /* Info.plist */, 139 | ); 140 | path = SwiftSimpleNeuralNetworkTests; 141 | sourceTree = ""; 142 | }; 143 | 5565EEEF2058E58C000C2E2F /* MNIST Data */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 5565EEE72058E57E000C2E2F /* t10k-images.idx3-ubyte */, 147 | 5565EEE82058E57E000C2E2F /* t10k-labels.idx1-ubyte */, 148 | 5565EEE92058E57F000C2E2F /* train-images.idx3-ubyte */, 149 | 5565EEEA2058E57F000C2E2F /* train-labels.idx1-ubyte */, 150 | ); 151 | path = "MNIST Data"; 152 | sourceTree = ""; 153 | }; 154 | /* End PBXGroup section */ 155 | 156 | /* Begin PBXNativeTarget section */ 157 | 550F28A91D0C966300B11A8F /* SwiftSimpleNeuralNetwork */ = { 158 | isa = PBXNativeTarget; 159 | buildConfigurationList = 550F28B71D0C966300B11A8F /* Build configuration list for PBXNativeTarget "SwiftSimpleNeuralNetwork" */; 160 | buildPhases = ( 161 | 550F28A61D0C966300B11A8F /* Sources */, 162 | 550F28A71D0C966300B11A8F /* Frameworks */, 163 | 550F28A81D0C966300B11A8F /* Resources */, 164 | ); 165 | buildRules = ( 166 | ); 167 | dependencies = ( 168 | ); 169 | name = SwiftSimpleNeuralNetwork; 170 | productName = SwiftSimpleNeuralNetwork; 171 | productReference = 550F28AA1D0C966300B11A8F /* SwiftSimpleNeuralNetwork.app */; 172 | productType = "com.apple.product-type.application"; 173 | }; 174 | 555015B51D224DCB004D3F18 /* SwiftSimpleNeuralNetworkTests */ = { 175 | isa = PBXNativeTarget; 176 | buildConfigurationList = 555015BF1D224DCB004D3F18 /* Build configuration list for PBXNativeTarget "SwiftSimpleNeuralNetworkTests" */; 177 | buildPhases = ( 178 | 555015B21D224DCB004D3F18 /* Sources */, 179 | 555015B31D224DCB004D3F18 /* Frameworks */, 180 | 555015B41D224DCB004D3F18 /* Resources */, 181 | ); 182 | buildRules = ( 183 | ); 184 | dependencies = ( 185 | 555015BC1D224DCB004D3F18 /* PBXTargetDependency */, 186 | ); 187 | name = SwiftSimpleNeuralNetworkTests; 188 | productName = SwiftSimpleNeuralNetworkTests; 189 | productReference = 555015B61D224DCB004D3F18 /* SwiftSimpleNeuralNetworkTests.xctest */; 190 | productType = "com.apple.product-type.bundle.unit-test"; 191 | }; 192 | /* End PBXNativeTarget section */ 193 | 194 | /* Begin PBXProject section */ 195 | 550F28A21D0C966300B11A8F /* Project object */ = { 196 | isa = PBXProject; 197 | attributes = { 198 | BuildIndependentTargetsInParallel = YES; 199 | LastSwiftUpdateCheck = 0800; 200 | LastUpgradeCheck = 1600; 201 | ORGANIZATIONNAME = "Oak Snow Consulting"; 202 | TargetAttributes = { 203 | 550F28A91D0C966300B11A8F = { 204 | CreatedOnToolsVersion = 7.3.1; 205 | LastSwiftMigration = 1020; 206 | }; 207 | 555015B51D224DCB004D3F18 = { 208 | CreatedOnToolsVersion = 8.0; 209 | DevelopmentTeam = 59VX8JQ7R6; 210 | DevelopmentTeamName = "David Kopec"; 211 | LastSwiftMigration = 1020; 212 | ProvisioningStyle = Automatic; 213 | TestTargetID = 550F28A91D0C966300B11A8F; 214 | }; 215 | }; 216 | }; 217 | buildConfigurationList = 550F28A51D0C966300B11A8F /* Build configuration list for PBXProject "SwiftSimpleNeuralNetwork" */; 218 | compatibilityVersion = "Xcode 3.2"; 219 | developmentRegion = en; 220 | hasScannedForEncodings = 0; 221 | knownRegions = ( 222 | en, 223 | Base, 224 | ); 225 | mainGroup = 550F28A11D0C966300B11A8F; 226 | productRefGroup = 550F28AB1D0C966300B11A8F /* Products */; 227 | projectDirPath = ""; 228 | projectRoot = ""; 229 | targets = ( 230 | 550F28A91D0C966300B11A8F /* SwiftSimpleNeuralNetwork */, 231 | 555015B51D224DCB004D3F18 /* SwiftSimpleNeuralNetworkTests */, 232 | ); 233 | }; 234 | /* End PBXProject section */ 235 | 236 | /* Begin PBXResourcesBuildPhase section */ 237 | 550F28A81D0C966300B11A8F /* Resources */ = { 238 | isa = PBXResourcesBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | 5565EEEC2058E57F000C2E2F /* t10k-labels.idx1-ubyte in Resources */, 242 | 5565EEEE2058E57F000C2E2F /* train-labels.idx1-ubyte in Resources */, 243 | 550F28B01D0C966300B11A8F /* Assets.xcassets in Resources */, 244 | 550F28B31D0C966300B11A8F /* MainMenu.xib in Resources */, 245 | 5565EEED2058E57F000C2E2F /* train-images.idx3-ubyte in Resources */, 246 | 5565EEEB2058E57F000C2E2F /* t10k-images.idx3-ubyte in Resources */, 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | 555015B41D224DCB004D3F18 /* Resources */ = { 251 | isa = PBXResourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | 553E02891D22E55200E1C3B3 /* iris.csv in Resources */, 255 | 5585848F1D25C3D400978802 /* wine.csv in Resources */, 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | /* End PBXResourcesBuildPhase section */ 260 | 261 | /* Begin PBXSourcesBuildPhase section */ 262 | 550F28A61D0C966300B11A8F /* Sources */ = { 263 | isa = PBXSourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | 550F28BE1D0C972200B11A8F /* Layer.swift in Sources */, 267 | 559C37582058BB5400814909 /* BiasNeuron.swift in Sources */, 268 | 550F28BC1D0C96CB00B11A8F /* Neuron.swift in Sources */, 269 | 550F28C01D0C975600B11A8F /* Network.swift in Sources */, 270 | 550F28C21D0CA5E600B11A8F /* Functions.swift in Sources */, 271 | 550F28AE1D0C966300B11A8F /* AppDelegate.swift in Sources */, 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | 555015B21D224DCB004D3F18 /* Sources */ = { 276 | isa = PBXSourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | 555015B91D224DCB004D3F18 /* SinTest.swift in Sources */, 280 | 559C37592058BF2000814909 /* BiasNeuron.swift in Sources */, 281 | 555015C11D224E33004D3F18 /* Neuron.swift in Sources */, 282 | 553E02871D22E41500E1C3B3 /* IrisTest.swift in Sources */, 283 | 555015C31D224E33004D3F18 /* Layer.swift in Sources */, 284 | 555015C21D224E33004D3F18 /* Network.swift in Sources */, 285 | 558584911D25C3FE00978802 /* WineTest.swift in Sources */, 286 | 555015C01D224E33004D3F18 /* Functions.swift in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXSourcesBuildPhase section */ 291 | 292 | /* Begin PBXTargetDependency section */ 293 | 555015BC1D224DCB004D3F18 /* PBXTargetDependency */ = { 294 | isa = PBXTargetDependency; 295 | target = 550F28A91D0C966300B11A8F /* SwiftSimpleNeuralNetwork */; 296 | targetProxy = 555015BB1D224DCB004D3F18 /* PBXContainerItemProxy */; 297 | }; 298 | /* End PBXTargetDependency section */ 299 | 300 | /* Begin PBXVariantGroup section */ 301 | 550F28B11D0C966300B11A8F /* MainMenu.xib */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 550F28B21D0C966300B11A8F /* Base */, 305 | ); 306 | name = MainMenu.xib; 307 | sourceTree = ""; 308 | }; 309 | /* End PBXVariantGroup section */ 310 | 311 | /* Begin XCBuildConfiguration section */ 312 | 550F28B51D0C966300B11A8F /* Debug */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ALWAYS_SEARCH_USER_PATHS = NO; 316 | CLANG_ANALYZER_NONNULL = YES; 317 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 318 | CLANG_CXX_LIBRARY = "libc++"; 319 | CLANG_ENABLE_MODULES = YES; 320 | CLANG_ENABLE_OBJC_ARC = YES; 321 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 322 | CLANG_WARN_BOOL_CONVERSION = YES; 323 | CLANG_WARN_COMMA = YES; 324 | CLANG_WARN_CONSTANT_CONVERSION = YES; 325 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INFINITE_RECURSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 333 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 336 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 337 | CLANG_WARN_STRICT_PROTOTYPES = YES; 338 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 339 | CLANG_WARN_UNREACHABLE_CODE = YES; 340 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 341 | CODE_SIGN_IDENTITY = "-"; 342 | COPY_PHASE_STRIP = NO; 343 | DEAD_CODE_STRIPPING = YES; 344 | DEBUG_INFORMATION_FORMAT = dwarf; 345 | ENABLE_STRICT_OBJC_MSGSEND = YES; 346 | ENABLE_TESTABILITY = YES; 347 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 348 | GCC_C_LANGUAGE_STANDARD = gnu99; 349 | GCC_DYNAMIC_NO_PIC = NO; 350 | GCC_NO_COMMON_BLOCKS = YES; 351 | GCC_OPTIMIZATION_LEVEL = 0; 352 | GCC_PREPROCESSOR_DEFINITIONS = ( 353 | "DEBUG=1", 354 | "$(inherited)", 355 | ); 356 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 357 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 358 | GCC_WARN_UNDECLARED_SELECTOR = YES; 359 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 360 | GCC_WARN_UNUSED_FUNCTION = YES; 361 | GCC_WARN_UNUSED_VARIABLE = YES; 362 | MACOSX_DEPLOYMENT_TARGET = 10.11; 363 | MTL_ENABLE_DEBUG_INFO = YES; 364 | ONLY_ACTIVE_ARCH = YES; 365 | SDKROOT = macosx; 366 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 367 | SWIFT_VERSION = 6.0; 368 | }; 369 | name = Debug; 370 | }; 371 | 550F28B61D0C966300B11A8F /* Release */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ALWAYS_SEARCH_USER_PATHS = NO; 375 | CLANG_ANALYZER_NONNULL = YES; 376 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 377 | CLANG_CXX_LIBRARY = "libc++"; 378 | CLANG_ENABLE_MODULES = YES; 379 | CLANG_ENABLE_OBJC_ARC = YES; 380 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 381 | CLANG_WARN_BOOL_CONVERSION = YES; 382 | CLANG_WARN_COMMA = YES; 383 | CLANG_WARN_CONSTANT_CONVERSION = YES; 384 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 385 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 386 | CLANG_WARN_EMPTY_BODY = YES; 387 | CLANG_WARN_ENUM_CONVERSION = YES; 388 | CLANG_WARN_INFINITE_RECURSION = YES; 389 | CLANG_WARN_INT_CONVERSION = YES; 390 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 391 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 392 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 394 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 395 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 396 | CLANG_WARN_STRICT_PROTOTYPES = YES; 397 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 398 | CLANG_WARN_UNREACHABLE_CODE = YES; 399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 400 | CODE_SIGN_IDENTITY = "-"; 401 | COPY_PHASE_STRIP = NO; 402 | DEAD_CODE_STRIPPING = YES; 403 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 404 | ENABLE_NS_ASSERTIONS = NO; 405 | ENABLE_STRICT_OBJC_MSGSEND = YES; 406 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 407 | GCC_C_LANGUAGE_STANDARD = gnu99; 408 | GCC_NO_COMMON_BLOCKS = YES; 409 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 410 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 411 | GCC_WARN_UNDECLARED_SELECTOR = YES; 412 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 413 | GCC_WARN_UNUSED_FUNCTION = YES; 414 | GCC_WARN_UNUSED_VARIABLE = YES; 415 | MACOSX_DEPLOYMENT_TARGET = 10.11; 416 | MTL_ENABLE_DEBUG_INFO = NO; 417 | SDKROOT = macosx; 418 | SWIFT_VERSION = 6.0; 419 | }; 420 | name = Release; 421 | }; 422 | 550F28B81D0C966300B11A8F /* Debug */ = { 423 | isa = XCBuildConfiguration; 424 | buildSettings = { 425 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 426 | COMBINE_HIDPI_IMAGES = YES; 427 | DEAD_CODE_STRIPPING = YES; 428 | INFOPLIST_FILE = SwiftSimpleNeuralNetwork/Info.plist; 429 | LD_RUNPATH_SEARCH_PATHS = ( 430 | "$(inherited)", 431 | "@executable_path/../Frameworks", 432 | ); 433 | MACOSX_DEPLOYMENT_TARGET = 11.0; 434 | PRODUCT_BUNDLE_IDENTIFIER = com.oaksnow.SwiftSimpleNeuralNetwork; 435 | PRODUCT_NAME = "$(TARGET_NAME)"; 436 | SWIFT_VERSION = 6.0; 437 | }; 438 | name = Debug; 439 | }; 440 | 550F28B91D0C966300B11A8F /* Release */ = { 441 | isa = XCBuildConfiguration; 442 | buildSettings = { 443 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 444 | COMBINE_HIDPI_IMAGES = YES; 445 | DEAD_CODE_STRIPPING = YES; 446 | INFOPLIST_FILE = SwiftSimpleNeuralNetwork/Info.plist; 447 | LD_RUNPATH_SEARCH_PATHS = ( 448 | "$(inherited)", 449 | "@executable_path/../Frameworks", 450 | ); 451 | MACOSX_DEPLOYMENT_TARGET = 11.0; 452 | PRODUCT_BUNDLE_IDENTIFIER = com.oaksnow.SwiftSimpleNeuralNetwork; 453 | PRODUCT_NAME = "$(TARGET_NAME)"; 454 | SWIFT_COMPILATION_MODE = wholemodule; 455 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 456 | SWIFT_VERSION = 6.0; 457 | }; 458 | name = Release; 459 | }; 460 | 555015BD1D224DCB004D3F18 /* Debug */ = { 461 | isa = XCBuildConfiguration; 462 | buildSettings = { 463 | BUNDLE_LOADER = "$(TEST_HOST)"; 464 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 465 | COMBINE_HIDPI_IMAGES = YES; 466 | DEAD_CODE_STRIPPING = YES; 467 | INFOPLIST_FILE = SwiftSimpleNeuralNetworkTests/Info.plist; 468 | LD_RUNPATH_SEARCH_PATHS = ( 469 | "$(inherited)", 470 | "@executable_path/../Frameworks", 471 | "@loader_path/../Frameworks", 472 | ); 473 | MACOSX_DEPLOYMENT_TARGET = 11.0; 474 | PRODUCT_BUNDLE_IDENTIFIER = com.oaksnow.SwiftSimpleNeuralNetworkTests; 475 | PRODUCT_NAME = "$(TARGET_NAME)"; 476 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 477 | SWIFT_VERSION = 5.0; 478 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftSimpleNeuralNetwork.app/Contents/MacOS/SwiftSimpleNeuralNetwork"; 479 | }; 480 | name = Debug; 481 | }; 482 | 555015BE1D224DCB004D3F18 /* Release */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | BUNDLE_LOADER = "$(TEST_HOST)"; 486 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 487 | COMBINE_HIDPI_IMAGES = YES; 488 | DEAD_CODE_STRIPPING = YES; 489 | INFOPLIST_FILE = SwiftSimpleNeuralNetworkTests/Info.plist; 490 | LD_RUNPATH_SEARCH_PATHS = ( 491 | "$(inherited)", 492 | "@executable_path/../Frameworks", 493 | "@loader_path/../Frameworks", 494 | ); 495 | MACOSX_DEPLOYMENT_TARGET = 11.0; 496 | PRODUCT_BUNDLE_IDENTIFIER = com.oaksnow.SwiftSimpleNeuralNetworkTests; 497 | PRODUCT_NAME = "$(TARGET_NAME)"; 498 | SWIFT_COMPILATION_MODE = wholemodule; 499 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 500 | SWIFT_VERSION = 5.0; 501 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftSimpleNeuralNetwork.app/Contents/MacOS/SwiftSimpleNeuralNetwork"; 502 | }; 503 | name = Release; 504 | }; 505 | /* End XCBuildConfiguration section */ 506 | 507 | /* Begin XCConfigurationList section */ 508 | 550F28A51D0C966300B11A8F /* Build configuration list for PBXProject "SwiftSimpleNeuralNetwork" */ = { 509 | isa = XCConfigurationList; 510 | buildConfigurations = ( 511 | 550F28B51D0C966300B11A8F /* Debug */, 512 | 550F28B61D0C966300B11A8F /* Release */, 513 | ); 514 | defaultConfigurationIsVisible = 0; 515 | defaultConfigurationName = Release; 516 | }; 517 | 550F28B71D0C966300B11A8F /* Build configuration list for PBXNativeTarget "SwiftSimpleNeuralNetwork" */ = { 518 | isa = XCConfigurationList; 519 | buildConfigurations = ( 520 | 550F28B81D0C966300B11A8F /* Debug */, 521 | 550F28B91D0C966300B11A8F /* Release */, 522 | ); 523 | defaultConfigurationIsVisible = 0; 524 | defaultConfigurationName = Release; 525 | }; 526 | 555015BF1D224DCB004D3F18 /* Build configuration list for PBXNativeTarget "SwiftSimpleNeuralNetworkTests" */ = { 527 | isa = XCConfigurationList; 528 | buildConfigurations = ( 529 | 555015BD1D224DCB004D3F18 /* Debug */, 530 | 555015BE1D224DCB004D3F18 /* Release */, 531 | ); 532 | defaultConfigurationIsVisible = 0; 533 | defaultConfigurationName = Release; 534 | }; 535 | /* End XCConfigurationList section */ 536 | }; 537 | rootObject = 550F28A21D0C966300B11A8F /* Project object */; 538 | } 539 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftSimpleNeuralNetwork 4 | // 5 | // Copyright 2018 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | //MNIST data set curtosy http://yann.lecun.com/exdb/mnist/ 20 | 21 | import Cocoa 22 | 23 | typealias Byte = UInt8 24 | 25 | @main 26 | class AppDelegate: NSObject, NSApplicationDelegate, @unchecked Sendable { 27 | 28 | @IBOutlet weak var window: NSWindow! 29 | @IBOutlet weak var characterImageView: NSImageView! 30 | @IBOutlet weak var currentCharacterIndexLabel: NSTextField! 31 | @IBOutlet weak var currentCharacterLabel: NSTextField! 32 | @IBOutlet weak var testingImageView: NSImageView! 33 | @IBOutlet weak var testingCharacterIndexLabel: NSTextField! 34 | @IBOutlet weak var testingCharacterLabel: NSTextField! 35 | @IBOutlet weak var singleTestPredictionLabel: NSTextField! 36 | @IBOutlet weak var allTestAccuracyLabel: NSTextField! 37 | @IBOutlet weak var trainProgress: NSProgressIndicator! 38 | @IBOutlet weak var batchLabel: NSTextField! 39 | 40 | @IBOutlet weak var testButton: NSButton! 41 | @IBOutlet weak var testAllButton: NSButton! 42 | @IBOutlet weak var trainButton: NSButton! 43 | 44 | var trainingImages = [[Byte]]() 45 | var trainingLabels = [Byte]() 46 | var testingImages = [[Byte]]() 47 | var testingLabels = [Byte]() 48 | var currentTrainingImageShown: Int = 0 49 | var currentTestingImageShown: Int = 0 50 | var numBatches: Int = 1 51 | let network: Network = Network(layerStructure: [784,20,10], activationFunction: sigmoid, derivativeActivationFunction: derivativeSigmoid, learningRate: 0.006, hasBias: true) 52 | 53 | //read the mnist training images (48 MBs, 60000 images, 28x28 grayscale) 54 | func loadTrainingImages() { 55 | 56 | if let path = Bundle.main.path(forResource: "train-images", ofType: "idx3-ubyte"), let stream = InputStream(fileAtPath: path) { 57 | stream.open() 58 | defer { stream.close() } 59 | var dummy: [Byte] = [Byte](repeating: 0, count: 16) 60 | // skip over header 61 | stream.read(&dummy, maxLength: 16) 62 | var image: [Byte] = [Byte](repeating: 0, count: 784) // 28 x 28 = 784 63 | while stream.read(&image, maxLength: 784) == 784 { 64 | trainingImages.append(image) 65 | } 66 | } 67 | 68 | } 69 | 70 | //read the mnist training images (60000 labels, 1 byte each) 71 | func loadTrainingLabels() { 72 | 73 | if let path = Bundle.main.path(forResource: "train-labels", ofType: "idx1-ubyte"), let stream = InputStream(fileAtPath: path) { 74 | stream.open() 75 | defer { stream.close() } 76 | var dummy: [Byte] = [Byte](repeating: 0, count: 8) 77 | // skip over header 78 | stream.read(&dummy, maxLength: 8) 79 | var label: Byte = 0 // 1 byte 0-10 80 | while stream.read(&label, maxLength: 1) == 1 { 81 | trainingLabels.append(label) 82 | } 83 | } 84 | 85 | } 86 | 87 | //read the mnist testing images 88 | func loadTestingImages() { 89 | 90 | if let path = Bundle.main.path(forResource: "t10k-images", ofType: "idx3-ubyte"), let stream = InputStream(fileAtPath: path) { 91 | stream.open() 92 | defer { stream.close() } 93 | var dummy: [Byte] = [Byte](repeating: 0, count: 16) 94 | // skip over header 95 | stream.read(&dummy, maxLength: 16) 96 | var image: [Byte] = [Byte](repeating: 0, count: 784) // 28 x 28 = 784 97 | while stream.read(&image, maxLength: 784) == 784 { 98 | testingImages.append(image) 99 | } 100 | } 101 | 102 | } 103 | 104 | //read the mnist testing labels 105 | func loadTestingLabels() { 106 | 107 | if let path = Bundle.main.path(forResource: "t10k-labels", ofType: "idx1-ubyte"), let stream = InputStream(fileAtPath: path) { 108 | stream.open() 109 | defer { stream.close() } 110 | var dummy: [Byte] = [Byte](repeating: 0, count: 8) 111 | // skip over header 112 | stream.read(&dummy, maxLength: 8) 113 | var label: Byte = 0 // 1 byte 0-10 114 | while stream.read(&label, maxLength: 1) == 1 { 115 | testingLabels.append(label) 116 | } 117 | } 118 | 119 | } 120 | 121 | // based on https://stackoverflow.com/a/34677134/281461 122 | func imageFromGrayscaleBytes(pixelValues: [Byte]) -> NSImage { 123 | var pixelValues = pixelValues 124 | let width = 28 125 | let height = 28 126 | let bitsPerComponent = 8 127 | let bytesPerPixel = 1 128 | let bitsPerPixel = bytesPerPixel * bitsPerComponent 129 | let bytesPerRow = bytesPerPixel * width 130 | let totalBytes = height * bytesPerRow 131 | let providerRef = CGDataProvider(dataInfo: nil, data: &pixelValues, size: totalBytes) { (info, data, size) in 132 | return 133 | } 134 | 135 | let colorSpaceRef = CGColorSpaceCreateDeviceGray() 136 | let bitmapInfo = CGBitmapInfo() 137 | let imageRef = CGImage(width: width, 138 | height: height, 139 | bitsPerComponent: bitsPerComponent, 140 | bitsPerPixel: bitsPerPixel, 141 | bytesPerRow: bytesPerRow, 142 | space: colorSpaceRef, 143 | bitmapInfo: bitmapInfo, 144 | provider: providerRef!, 145 | decode: nil, 146 | shouldInterpolate: false, 147 | intent: CGColorRenderingIntent.defaultIntent) 148 | return NSImage(cgImage: imageRef!, size: NSSize(width: 28, height: 28)) 149 | } 150 | 151 | @MainActor func showTrainingImage(index: Int) { 152 | characterImageView.image = imageFromGrayscaleBytes(pixelValues: trainingImages[index]) 153 | currentCharacterIndexLabel.stringValue = "Index: \(index)" 154 | currentCharacterLabel.stringValue = "Label: \(trainingLabels[index])" 155 | } 156 | 157 | @MainActor func showTestingImage(index: Int) { 158 | testingImageView.image = imageFromGrayscaleBytes(pixelValues: testingImages[index]) 159 | testingCharacterIndexLabel.stringValue = "Index: \(index)" 160 | testingCharacterLabel.stringValue = "Label: \(testingLabels[index])" 161 | } 162 | 163 | func applicationDidFinishLaunching(_ aNotification: Notification) { 164 | // Insert code here to initialize your application 165 | //print(randomWeights(number: 10)) 166 | loadTrainingImages() 167 | loadTrainingLabels() 168 | loadTestingImages() 169 | loadTestingLabels() 170 | // show the first one 171 | showTrainingImage(index: currentTrainingImageShown) 172 | showTestingImage(index: currentTestingImageShown) 173 | } 174 | 175 | func applicationWillTerminate(_ aNotification: Notification) { 176 | // Insert code here to tear down your application 177 | } 178 | 179 | @IBAction func nextTrainingImage(sender: AnyObject) { 180 | currentTrainingImageShown = (currentTrainingImageShown + 1) % trainingImages.count 181 | showTrainingImage(index: currentTrainingImageShown) 182 | } 183 | 184 | @IBAction func previousTrainingImage(sender: AnyObject) { 185 | currentTrainingImageShown -= 1 186 | if currentTrainingImageShown < 0 { currentTrainingImageShown = trainingImages.count - 1 } 187 | showTrainingImage(index: currentTrainingImageShown) 188 | } 189 | 190 | @IBAction func nextTestingImage(sender: AnyObject) { 191 | currentTestingImageShown = (currentTestingImageShown + 1) % testingImages.count 192 | showTestingImage(index: currentTestingImageShown) 193 | } 194 | 195 | @IBAction func previousTestingImage(sender: AnyObject) { 196 | currentTestingImageShown -= 1 197 | if currentTestingImageShown < 0 { currentTestingImageShown = testingImages.count - 1 } 198 | showTestingImage(index: currentTestingImageShown) 199 | } 200 | 201 | @IBAction func train(sender: AnyObject) { 202 | // original images are made of bytes from 0-255, we need these intensity values as Doubles 203 | let imageData = trainingImages.map{ return $0.map{ return Double($0) / 255 }} 204 | let expecteds: [[Double]] = trainingLabels.map{ 205 | switch $0 { 206 | case 0: 207 | return [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 208 | case 1: 209 | return [0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 210 | case 2: 211 | return [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 212 | case 3: 213 | return [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 214 | case 4: 215 | return [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0] 216 | case 5: 217 | return [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0] 218 | case 6: 219 | return [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0] 220 | case 7: 221 | return [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0] 222 | case 8: 223 | return [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0] 224 | default: 225 | return [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0] 226 | } 227 | } 228 | // batches 229 | // for _ in 0..<100 { 230 | // network.train(inputs: Array>(imageData.prefix(50)), expecteds: Array>(expecteds.prefix(50)), printError: true) 231 | // } 232 | // all 233 | DispatchQueue.main.async { [unowned self] in 234 | self.trainButton.isEnabled = false 235 | self.testAllButton.isEnabled = false 236 | self.testButton.isEnabled = false 237 | self.trainProgress.doubleValue = 0.0 238 | } 239 | DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async { [weak self, numBatches = numBatches] in 240 | for i in 1...numBatches { 241 | self?.network.train(inputs: imageData, expecteds: expecteds, printError: false) 242 | print("Finished batch \(i) of \(numBatches)") 243 | DispatchQueue.main.async { [weak self] in 244 | self?.trainProgress.doubleValue = Double(i) 245 | } 246 | } 247 | print("Done Training") 248 | DispatchQueue.main.async { [weak self] in 249 | self?.trainButton.isEnabled = true 250 | self?.testAllButton.isEnabled = true 251 | self?.testButton.isEnabled = true 252 | } 253 | } 254 | } 255 | 256 | // helper function finds the highest in an Array of Double and returns its index 257 | func interpretOutput(output: [Double]) -> Int { 258 | return output.firstIndex(of: output.max()!)! 259 | } 260 | 261 | @IBAction func batchSizeChanged(sender: NSStepper) { 262 | numBatches = sender.integerValue 263 | trainProgress.maxValue = sender.doubleValue 264 | batchLabel.stringValue = "\(numBatches)" 265 | } 266 | 267 | @IBAction func testCurrent(sender: AnyObject) { 268 | let imageData = testingImages[currentTestingImageShown].map{ return Double($0)} 269 | let output = network.outputs(input: imageData) 270 | print(output) 271 | let result = interpretOutput(output: output) 272 | singleTestPredictionLabel.stringValue = "Prediction: \(result)" 273 | } 274 | 275 | @IBAction func testAll(sender: AnyObject) { 276 | let imageData = testingImages.map{ return $0.map{ return Double($0) / 255 }} 277 | let expecteds: [Int] = testingLabels.map{ Int($0) } 278 | let (_, _, percentage) = network.validate(inputs: imageData, expecteds: expecteds, interpretOutput: interpretOutput) 279 | allTestAccuracyLabel.stringValue = "Accuracy: \(percentage * 100)%" 280 | } 281 | } 282 | 283 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | Default 555 | 556 | 557 | 558 | 559 | 560 | 561 | Left to Right 562 | 563 | 564 | 565 | 566 | 567 | 568 | Right to Left 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | Default 580 | 581 | 582 | 583 | 584 | 585 | 586 | Left to Right 587 | 588 | 589 | 590 | 591 | 592 | 593 | Right to Left 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 722 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 788 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2016 Oak Snow Consulting. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork/MNIST Data/t10k-images.idx3-ubyte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davecom/SwiftSimpleNeuralNetwork/62143986d4b02acb927aa51fcf61947bd956b790/SwiftSimpleNeuralNetwork/MNIST Data/t10k-images.idx3-ubyte -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork/MNIST Data/t10k-labels.idx1-ubyte: -------------------------------------------------------------------------------- 1 | '                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork/MNIST Data/train-images.idx3-ubyte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davecom/SwiftSimpleNeuralNetwork/62143986d4b02acb927aa51fcf61947bd956b790/SwiftSimpleNeuralNetwork/MNIST Data/train-images.idx3-ubyte -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetwork/MNIST Data/train-labels.idx1-ubyte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davecom/SwiftSimpleNeuralNetwork/62143986d4b02acb927aa51fcf61947bd956b790/SwiftSimpleNeuralNetwork/MNIST Data/train-labels.idx1-ubyte -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetworkTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetworkTests/IrisTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IrisTest.swift 3 | // SwiftSimpleNeuralNetwork 4 | // 5 | // Copyright 2016-2019 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | 20 | // Iris data set courtsey of: 21 | // Lichman, M. (2013). UCI Machine Learning Repository [http://archive.ics.uci.edu/ml]. Irvine, CA: University of California, School of Information and Computer Science. 22 | 23 | import XCTest 24 | import Foundation 25 | 26 | /// Tests against the classic iris data set. 27 | /// Uses the entire data set both for training 28 | /// and for validation (yes, bad probably). 29 | class IrisTest: XCTestCase { 30 | 31 | var network: Network = Network(layerStructure: [4,20,3], learningRate: 0.3, hasBias: true) 32 | var irisParameters: [[Double]] = [[Double]]() 33 | var irisClassifications: [[Double]] = [[Double]]() 34 | var irisSpecies: [String] = [String]() 35 | 36 | func parseIrisCSV() { 37 | let myBundle = Bundle.init(for: IrisTest.self) 38 | let urlpath = myBundle.path(forResource: "iris", ofType: "csv") 39 | let url = URL(fileURLWithPath: urlpath!) 40 | let csv = try! String.init(contentsOf: url) 41 | let lines = csv.components(separatedBy: "\n") 42 | 43 | let shuffledLines = lines.shuffled 44 | for line in shuffledLines { 45 | if line == "" { continue } 46 | let items = line.components(separatedBy: ",") 47 | let parameters = items[0...3].map{ Double($0)! } 48 | irisParameters.append(parameters) 49 | let species = items[4] 50 | if species == "Iris-setosa" { 51 | irisClassifications.append([1.0, 0.0, 0.0]) 52 | } else if species == "Iris-versicolor" { 53 | irisClassifications.append([0.0, 1.0, 0.0]) 54 | } else { 55 | irisClassifications.append([0.0, 0.0, 1.0]) 56 | } 57 | irisSpecies.append(species) 58 | } 59 | normalizeByColumnMax(dataset: &irisParameters) 60 | } 61 | 62 | func interpretOutput(output: [Double]) -> String { 63 | if output.max()! == output[0] { 64 | return "Iris-setosa" 65 | } else if output.max()! == output[1] { 66 | return "Iris-versicolor" 67 | } else { 68 | return "Iris-virginica" 69 | } 70 | } 71 | 72 | override func setUp() { 73 | super.setUp() 74 | // Put setup code here. This method is called before the invocation of each test method in the class. 75 | parseIrisCSV() 76 | // train over entire data set 10 times 77 | for _ in 0..<1000 { 78 | network.train(inputs: irisParameters, expecteds: irisClassifications, printError: false) 79 | } 80 | } 81 | 82 | override func tearDown() { 83 | // Put teardown code here. This method is called after the invocation of each test method in the class. 84 | super.tearDown() 85 | } 86 | 87 | func testSamples() { 88 | // This is an example of a functional test case. 89 | // Use XCTAssert and related functions to verify your tests produce the correct results. 90 | let results = network.validate(inputs: irisParameters, expecteds: irisSpecies, interpretOutput: interpretOutput) 91 | print("\(results.correct) correct of \(results.total) = \(results.percentage * 100)%") 92 | XCTAssertEqual(results.percentage, 1.00, accuracy: 0.05, "Did not come within a 95% confidence interval") 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetworkTests/SinTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftSimpleNeuralNetworkTests.swift 3 | // SwiftSimpleNeuralNetworkTests 4 | // 5 | // Copyright 2016-2019 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | 20 | import XCTest 21 | 22 | class SwiftSimpleNeuralNetworkTests: XCTestCase { 23 | 24 | var network: Network = Network(layerStructure: [1,24,1], activationFunction: sigmoid, derivativeActivationFunction: derivativeSigmoid, learningRate: 0.6, hasBias: true) 25 | 26 | override func setUp() { 27 | super.setUp() 28 | // Put setup code here. This method is called before the invocation of each test method in the class. 29 | let xos = randomNums(number: 1000000, limit: Double.pi) 30 | let ys = xos.map{[sin($0)]} 31 | let xs = xos.map{[$0]} 32 | network.train(inputs: xs, expecteds: ys, printError: true) 33 | } 34 | 35 | override func tearDown() { 36 | // Put teardown code here. This method is called after the invocation of each test method in the class. 37 | super.tearDown() 38 | } 39 | 40 | func testSin() { 41 | // This is an example of a functional test case. 42 | // Use XCTAssert and related functions to verify your tests produce the correct results. 43 | let xos = randomNums(number: 1000000, limit: Double.pi) 44 | let ys = xos.map{ sin($0)} 45 | let xs = xos.map{[$0]} 46 | let results = network.validate(inputs: xs, expecteds: ys, accuracy: 0.05) 47 | print("\(results.correct) correct of \(results.total) = \(results.percentage * 100)%") 48 | XCTAssertEqual(results.percentage, 1.00, accuracy: 0.05, "Did not come within a 95% confidence interval") 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetworkTests/WineTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WineTest.swift 3 | // SwiftSimpleNeuralNetwork 4 | // 5 | // Copyright 2016-2019 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | // Wine data set courtsey of: 20 | // Lichman, M. (2013). UCI Machine Learning Repository [http://archive.ics.uci.edu/ml]. Irvine, CA: University of California, School of Information and Computer Science. 21 | 22 | import XCTest 23 | import Foundation 24 | 25 | /// Train on 150 wines and then successfully classify 26 | /// 28 previously unseen wines. 27 | class WineTest: XCTestCase { 28 | 29 | var network: Network = Network(layerStructure: [13,7,3], learningRate: 0.3, hasBias: true) 30 | // for training 31 | var wineParameters: [[Double]] = [[Double]]() 32 | var wineClassifications: [[Double]] = [[Double]]() 33 | // for testing/validation 34 | var wineSamples: [[Double]] = [[Double]]() 35 | var wineCultivars: [Int] = [Int]() 36 | 37 | func parseWineCSV() { 38 | let myBundle = Bundle.init(for: WineTest.self) 39 | let urlpath = myBundle.path(forResource: "wine", ofType: "csv") 40 | let url = URL(fileURLWithPath: urlpath!) 41 | let csv = try! String.init(contentsOf: url) 42 | let lines = csv.components(separatedBy: "\n") 43 | 44 | let shuffledLines = lines.shuffled 45 | for line in shuffledLines { 46 | if line == "" { continue } 47 | let items = line.components(separatedBy: ",") 48 | let parameters = items[1...13].map{ Double($0)! } 49 | wineParameters.append(parameters) 50 | let species = Int(items[0])! 51 | if species == 1 { 52 | wineClassifications.append([1.0, 0.0, 0.0]) 53 | } else if species == 2 { 54 | wineClassifications.append([0.0, 1.0, 0.0]) 55 | } else { 56 | wineClassifications.append([0.0, 0.0, 1.0]) 57 | } 58 | wineCultivars.append(species) 59 | } 60 | normalizeByColumnMax(dataset: &wineParameters) 61 | wineSamples = Array(wineParameters.dropFirst(150)) 62 | wineCultivars = Array(wineCultivars.dropFirst(150)) 63 | wineParameters = Array(wineParameters.dropLast(28)) 64 | } 65 | 66 | func interpretOutput(output: [Double]) -> Int { 67 | if output.max()! == output[0] { 68 | return 1 69 | } else if output.max()! == output[1] { 70 | return 2 71 | } else { 72 | return 3 73 | } 74 | } 75 | 76 | override func setUp() { 77 | super.setUp() 78 | parseWineCSV() 79 | // train over entire data set 200 times 80 | for _ in 0..<200 { 81 | network.train(inputs: wineParameters, expecteds: wineClassifications, printError: false) 82 | } 83 | } 84 | 85 | override func tearDown() { 86 | super.tearDown() 87 | } 88 | 89 | func testSamples() { 90 | let results = network.validate(inputs: wineSamples, expecteds: wineCultivars, interpretOutput: interpretOutput) 91 | print("\(results.correct) correct of \(results.total) = \(results.percentage * 100)%") 92 | XCTAssertEqual(results.percentage, 1.00, accuracy: 0.05, "Did not come within a 95% confidence interval") 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetworkTests/iris.csv: -------------------------------------------------------------------------------- 1 | 5.1,3.5,1.4,0.2,Iris-setosa 2 | 4.9,3.0,1.4,0.2,Iris-setosa 3 | 4.7,3.2,1.3,0.2,Iris-setosa 4 | 4.6,3.1,1.5,0.2,Iris-setosa 5 | 5.0,3.6,1.4,0.2,Iris-setosa 6 | 5.4,3.9,1.7,0.4,Iris-setosa 7 | 4.6,3.4,1.4,0.3,Iris-setosa 8 | 5.0,3.4,1.5,0.2,Iris-setosa 9 | 4.4,2.9,1.4,0.2,Iris-setosa 10 | 4.9,3.1,1.5,0.1,Iris-setosa 11 | 5.4,3.7,1.5,0.2,Iris-setosa 12 | 4.8,3.4,1.6,0.2,Iris-setosa 13 | 4.8,3.0,1.4,0.1,Iris-setosa 14 | 4.3,3.0,1.1,0.1,Iris-setosa 15 | 5.8,4.0,1.2,0.2,Iris-setosa 16 | 5.7,4.4,1.5,0.4,Iris-setosa 17 | 5.4,3.9,1.3,0.4,Iris-setosa 18 | 5.1,3.5,1.4,0.3,Iris-setosa 19 | 5.7,3.8,1.7,0.3,Iris-setosa 20 | 5.1,3.8,1.5,0.3,Iris-setosa 21 | 5.4,3.4,1.7,0.2,Iris-setosa 22 | 5.1,3.7,1.5,0.4,Iris-setosa 23 | 4.6,3.6,1.0,0.2,Iris-setosa 24 | 5.1,3.3,1.7,0.5,Iris-setosa 25 | 4.8,3.4,1.9,0.2,Iris-setosa 26 | 5.0,3.0,1.6,0.2,Iris-setosa 27 | 5.0,3.4,1.6,0.4,Iris-setosa 28 | 5.2,3.5,1.5,0.2,Iris-setosa 29 | 5.2,3.4,1.4,0.2,Iris-setosa 30 | 4.7,3.2,1.6,0.2,Iris-setosa 31 | 4.8,3.1,1.6,0.2,Iris-setosa 32 | 5.4,3.4,1.5,0.4,Iris-setosa 33 | 5.2,4.1,1.5,0.1,Iris-setosa 34 | 5.5,4.2,1.4,0.2,Iris-setosa 35 | 4.9,3.1,1.5,0.2,Iris-setosa 36 | 5.0,3.2,1.2,0.2,Iris-setosa 37 | 5.5,3.5,1.3,0.2,Iris-setosa 38 | 4.9,3.6,1.4,0.1,Iris-setosa 39 | 4.4,3.0,1.3,0.2,Iris-setosa 40 | 5.1,3.4,1.5,0.2,Iris-setosa 41 | 5.0,3.5,1.3,0.3,Iris-setosa 42 | 4.5,2.3,1.3,0.3,Iris-setosa 43 | 4.4,3.2,1.3,0.2,Iris-setosa 44 | 5.0,3.5,1.6,0.6,Iris-setosa 45 | 5.1,3.8,1.9,0.4,Iris-setosa 46 | 4.8,3.0,1.4,0.3,Iris-setosa 47 | 5.1,3.8,1.6,0.2,Iris-setosa 48 | 4.6,3.2,1.4,0.2,Iris-setosa 49 | 5.3,3.7,1.5,0.2,Iris-setosa 50 | 5.0,3.3,1.4,0.2,Iris-setosa 51 | 7.0,3.2,4.7,1.4,Iris-versicolor 52 | 6.4,3.2,4.5,1.5,Iris-versicolor 53 | 6.9,3.1,4.9,1.5,Iris-versicolor 54 | 5.5,2.3,4.0,1.3,Iris-versicolor 55 | 6.5,2.8,4.6,1.5,Iris-versicolor 56 | 5.7,2.8,4.5,1.3,Iris-versicolor 57 | 6.3,3.3,4.7,1.6,Iris-versicolor 58 | 4.9,2.4,3.3,1.0,Iris-versicolor 59 | 6.6,2.9,4.6,1.3,Iris-versicolor 60 | 5.2,2.7,3.9,1.4,Iris-versicolor 61 | 5.0,2.0,3.5,1.0,Iris-versicolor 62 | 5.9,3.0,4.2,1.5,Iris-versicolor 63 | 6.0,2.2,4.0,1.0,Iris-versicolor 64 | 6.1,2.9,4.7,1.4,Iris-versicolor 65 | 5.6,2.9,3.6,1.3,Iris-versicolor 66 | 6.7,3.1,4.4,1.4,Iris-versicolor 67 | 5.6,3.0,4.5,1.5,Iris-versicolor 68 | 5.8,2.7,4.1,1.0,Iris-versicolor 69 | 6.2,2.2,4.5,1.5,Iris-versicolor 70 | 5.6,2.5,3.9,1.1,Iris-versicolor 71 | 5.9,3.2,4.8,1.8,Iris-versicolor 72 | 6.1,2.8,4.0,1.3,Iris-versicolor 73 | 6.3,2.5,4.9,1.5,Iris-versicolor 74 | 6.1,2.8,4.7,1.2,Iris-versicolor 75 | 6.4,2.9,4.3,1.3,Iris-versicolor 76 | 6.6,3.0,4.4,1.4,Iris-versicolor 77 | 6.8,2.8,4.8,1.4,Iris-versicolor 78 | 6.7,3.0,5.0,1.7,Iris-versicolor 79 | 6.0,2.9,4.5,1.5,Iris-versicolor 80 | 5.7,2.6,3.5,1.0,Iris-versicolor 81 | 5.5,2.4,3.8,1.1,Iris-versicolor 82 | 5.5,2.4,3.7,1.0,Iris-versicolor 83 | 5.8,2.7,3.9,1.2,Iris-versicolor 84 | 6.0,2.7,5.1,1.6,Iris-versicolor 85 | 5.4,3.0,4.5,1.5,Iris-versicolor 86 | 6.0,3.4,4.5,1.6,Iris-versicolor 87 | 6.7,3.1,4.7,1.5,Iris-versicolor 88 | 6.3,2.3,4.4,1.3,Iris-versicolor 89 | 5.6,3.0,4.1,1.3,Iris-versicolor 90 | 5.5,2.5,4.0,1.3,Iris-versicolor 91 | 5.5,2.6,4.4,1.2,Iris-versicolor 92 | 6.1,3.0,4.6,1.4,Iris-versicolor 93 | 5.8,2.6,4.0,1.2,Iris-versicolor 94 | 5.0,2.3,3.3,1.0,Iris-versicolor 95 | 5.6,2.7,4.2,1.3,Iris-versicolor 96 | 5.7,3.0,4.2,1.2,Iris-versicolor 97 | 5.7,2.9,4.2,1.3,Iris-versicolor 98 | 6.2,2.9,4.3,1.3,Iris-versicolor 99 | 5.1,2.5,3.0,1.1,Iris-versicolor 100 | 5.7,2.8,4.1,1.3,Iris-versicolor 101 | 6.3,3.3,6.0,2.5,Iris-virginica 102 | 5.8,2.7,5.1,1.9,Iris-virginica 103 | 7.1,3.0,5.9,2.1,Iris-virginica 104 | 6.3,2.9,5.6,1.8,Iris-virginica 105 | 6.5,3.0,5.8,2.2,Iris-virginica 106 | 7.6,3.0,6.6,2.1,Iris-virginica 107 | 4.9,2.5,4.5,1.7,Iris-virginica 108 | 7.3,2.9,6.3,1.8,Iris-virginica 109 | 6.7,2.5,5.8,1.8,Iris-virginica 110 | 7.2,3.6,6.1,2.5,Iris-virginica 111 | 6.5,3.2,5.1,2.0,Iris-virginica 112 | 6.4,2.7,5.3,1.9,Iris-virginica 113 | 6.8,3.0,5.5,2.1,Iris-virginica 114 | 5.7,2.5,5.0,2.0,Iris-virginica 115 | 5.8,2.8,5.1,2.4,Iris-virginica 116 | 6.4,3.2,5.3,2.3,Iris-virginica 117 | 6.5,3.0,5.5,1.8,Iris-virginica 118 | 7.7,3.8,6.7,2.2,Iris-virginica 119 | 7.7,2.6,6.9,2.3,Iris-virginica 120 | 6.0,2.2,5.0,1.5,Iris-virginica 121 | 6.9,3.2,5.7,2.3,Iris-virginica 122 | 5.6,2.8,4.9,2.0,Iris-virginica 123 | 7.7,2.8,6.7,2.0,Iris-virginica 124 | 6.3,2.7,4.9,1.8,Iris-virginica 125 | 6.7,3.3,5.7,2.1,Iris-virginica 126 | 7.2,3.2,6.0,1.8,Iris-virginica 127 | 6.2,2.8,4.8,1.8,Iris-virginica 128 | 6.1,3.0,4.9,1.8,Iris-virginica 129 | 6.4,2.8,5.6,2.1,Iris-virginica 130 | 7.2,3.0,5.8,1.6,Iris-virginica 131 | 7.4,2.8,6.1,1.9,Iris-virginica 132 | 7.9,3.8,6.4,2.0,Iris-virginica 133 | 6.4,2.8,5.6,2.2,Iris-virginica 134 | 6.3,2.8,5.1,1.5,Iris-virginica 135 | 6.1,2.6,5.6,1.4,Iris-virginica 136 | 7.7,3.0,6.1,2.3,Iris-virginica 137 | 6.3,3.4,5.6,2.4,Iris-virginica 138 | 6.4,3.1,5.5,1.8,Iris-virginica 139 | 6.0,3.0,4.8,1.8,Iris-virginica 140 | 6.9,3.1,5.4,2.1,Iris-virginica 141 | 6.7,3.1,5.6,2.4,Iris-virginica 142 | 6.9,3.1,5.1,2.3,Iris-virginica 143 | 5.8,2.7,5.1,1.9,Iris-virginica 144 | 6.8,3.2,5.9,2.3,Iris-virginica 145 | 6.7,3.3,5.7,2.5,Iris-virginica 146 | 6.7,3.0,5.2,2.3,Iris-virginica 147 | 6.3,2.5,5.0,1.9,Iris-virginica 148 | 6.5,3.0,5.2,2.0,Iris-virginica 149 | 6.2,3.4,5.4,2.3,Iris-virginica 150 | 5.9,3.0,5.1,1.8,Iris-virginica 151 | -------------------------------------------------------------------------------- /SwiftSimpleNeuralNetworkTests/wine.csv: -------------------------------------------------------------------------------- 1 | 1,14.23,1.71,2.43,15.6,127,2.8,3.06,.28,2.29,5.64,1.04,3.92,1065 2 | 1,13.2,1.78,2.14,11.2,100,2.65,2.76,.26,1.28,4.38,1.05,3.4,1050 3 | 1,13.16,2.36,2.67,18.6,101,2.8,3.24,.3,2.81,5.68,1.03,3.17,1185 4 | 1,14.37,1.95,2.5,16.8,113,3.85,3.49,.24,2.18,7.8,.86,3.45,1480 5 | 1,13.24,2.59,2.87,21,118,2.8,2.69,.39,1.82,4.32,1.04,2.93,735 6 | 1,14.2,1.76,2.45,15.2,112,3.27,3.39,.34,1.97,6.75,1.05,2.85,1450 7 | 1,14.39,1.87,2.45,14.6,96,2.5,2.52,.3,1.98,5.25,1.02,3.58,1290 8 | 1,14.06,2.15,2.61,17.6,121,2.6,2.51,.31,1.25,5.05,1.06,3.58,1295 9 | 1,14.83,1.64,2.17,14,97,2.8,2.98,.29,1.98,5.2,1.08,2.85,1045 10 | 1,13.86,1.35,2.27,16,98,2.98,3.15,.22,1.85,7.22,1.01,3.55,1045 11 | 1,14.1,2.16,2.3,18,105,2.95,3.32,.22,2.38,5.75,1.25,3.17,1510 12 | 1,14.12,1.48,2.32,16.8,95,2.2,2.43,.26,1.57,5,1.17,2.82,1280 13 | 1,13.75,1.73,2.41,16,89,2.6,2.76,.29,1.81,5.6,1.15,2.9,1320 14 | 1,14.75,1.73,2.39,11.4,91,3.1,3.69,.43,2.81,5.4,1.25,2.73,1150 15 | 1,14.38,1.87,2.38,12,102,3.3,3.64,.29,2.96,7.5,1.2,3,1547 16 | 1,13.63,1.81,2.7,17.2,112,2.85,2.91,.3,1.46,7.3,1.28,2.88,1310 17 | 1,14.3,1.92,2.72,20,120,2.8,3.14,.33,1.97,6.2,1.07,2.65,1280 18 | 1,13.83,1.57,2.62,20,115,2.95,3.4,.4,1.72,6.6,1.13,2.57,1130 19 | 1,14.19,1.59,2.48,16.5,108,3.3,3.93,.32,1.86,8.7,1.23,2.82,1680 20 | 1,13.64,3.1,2.56,15.2,116,2.7,3.03,.17,1.66,5.1,.96,3.36,845 21 | 1,14.06,1.63,2.28,16,126,3,3.17,.24,2.1,5.65,1.09,3.71,780 22 | 1,12.93,3.8,2.65,18.6,102,2.41,2.41,.25,1.98,4.5,1.03,3.52,770 23 | 1,13.71,1.86,2.36,16.6,101,2.61,2.88,.27,1.69,3.8,1.11,4,1035 24 | 1,12.85,1.6,2.52,17.8,95,2.48,2.37,.26,1.46,3.93,1.09,3.63,1015 25 | 1,13.5,1.81,2.61,20,96,2.53,2.61,.28,1.66,3.52,1.12,3.82,845 26 | 1,13.05,2.05,3.22,25,124,2.63,2.68,.47,1.92,3.58,1.13,3.2,830 27 | 1,13.39,1.77,2.62,16.1,93,2.85,2.94,.34,1.45,4.8,.92,3.22,1195 28 | 1,13.3,1.72,2.14,17,94,2.4,2.19,.27,1.35,3.95,1.02,2.77,1285 29 | 1,13.87,1.9,2.8,19.4,107,2.95,2.97,.37,1.76,4.5,1.25,3.4,915 30 | 1,14.02,1.68,2.21,16,96,2.65,2.33,.26,1.98,4.7,1.04,3.59,1035 31 | 1,13.73,1.5,2.7,22.5,101,3,3.25,.29,2.38,5.7,1.19,2.71,1285 32 | 1,13.58,1.66,2.36,19.1,106,2.86,3.19,.22,1.95,6.9,1.09,2.88,1515 33 | 1,13.68,1.83,2.36,17.2,104,2.42,2.69,.42,1.97,3.84,1.23,2.87,990 34 | 1,13.76,1.53,2.7,19.5,132,2.95,2.74,.5,1.35,5.4,1.25,3,1235 35 | 1,13.51,1.8,2.65,19,110,2.35,2.53,.29,1.54,4.2,1.1,2.87,1095 36 | 1,13.48,1.81,2.41,20.5,100,2.7,2.98,.26,1.86,5.1,1.04,3.47,920 37 | 1,13.28,1.64,2.84,15.5,110,2.6,2.68,.34,1.36,4.6,1.09,2.78,880 38 | 1,13.05,1.65,2.55,18,98,2.45,2.43,.29,1.44,4.25,1.12,2.51,1105 39 | 1,13.07,1.5,2.1,15.5,98,2.4,2.64,.28,1.37,3.7,1.18,2.69,1020 40 | 1,14.22,3.99,2.51,13.2,128,3,3.04,.2,2.08,5.1,.89,3.53,760 41 | 1,13.56,1.71,2.31,16.2,117,3.15,3.29,.34,2.34,6.13,.95,3.38,795 42 | 1,13.41,3.84,2.12,18.8,90,2.45,2.68,.27,1.48,4.28,.91,3,1035 43 | 1,13.88,1.89,2.59,15,101,3.25,3.56,.17,1.7,5.43,.88,3.56,1095 44 | 1,13.24,3.98,2.29,17.5,103,2.64,2.63,.32,1.66,4.36,.82,3,680 45 | 1,13.05,1.77,2.1,17,107,3,3,.28,2.03,5.04,.88,3.35,885 46 | 1,14.21,4.04,2.44,18.9,111,2.85,2.65,.3,1.25,5.24,.87,3.33,1080 47 | 1,14.38,3.59,2.28,16,102,3.25,3.17,.27,2.19,4.9,1.04,3.44,1065 48 | 1,13.9,1.68,2.12,16,101,3.1,3.39,.21,2.14,6.1,.91,3.33,985 49 | 1,14.1,2.02,2.4,18.8,103,2.75,2.92,.32,2.38,6.2,1.07,2.75,1060 50 | 1,13.94,1.73,2.27,17.4,108,2.88,3.54,.32,2.08,8.90,1.12,3.1,1260 51 | 1,13.05,1.73,2.04,12.4,92,2.72,3.27,.17,2.91,7.2,1.12,2.91,1150 52 | 1,13.83,1.65,2.6,17.2,94,2.45,2.99,.22,2.29,5.6,1.24,3.37,1265 53 | 1,13.82,1.75,2.42,14,111,3.88,3.74,.32,1.87,7.05,1.01,3.26,1190 54 | 1,13.77,1.9,2.68,17.1,115,3,2.79,.39,1.68,6.3,1.13,2.93,1375 55 | 1,13.74,1.67,2.25,16.4,118,2.6,2.9,.21,1.62,5.85,.92,3.2,1060 56 | 1,13.56,1.73,2.46,20.5,116,2.96,2.78,.2,2.45,6.25,.98,3.03,1120 57 | 1,14.22,1.7,2.3,16.3,118,3.2,3,.26,2.03,6.38,.94,3.31,970 58 | 1,13.29,1.97,2.68,16.8,102,3,3.23,.31,1.66,6,1.07,2.84,1270 59 | 1,13.72,1.43,2.5,16.7,108,3.4,3.67,.19,2.04,6.8,.89,2.87,1285 60 | 2,12.37,.94,1.36,10.6,88,1.98,.57,.28,.42,1.95,1.05,1.82,520 61 | 2,12.33,1.1,2.28,16,101,2.05,1.09,.63,.41,3.27,1.25,1.67,680 62 | 2,12.64,1.36,2.02,16.8,100,2.02,1.41,.53,.62,5.75,.98,1.59,450 63 | 2,13.67,1.25,1.92,18,94,2.1,1.79,.32,.73,3.8,1.23,2.46,630 64 | 2,12.37,1.13,2.16,19,87,3.5,3.1,.19,1.87,4.45,1.22,2.87,420 65 | 2,12.17,1.45,2.53,19,104,1.89,1.75,.45,1.03,2.95,1.45,2.23,355 66 | 2,12.37,1.21,2.56,18.1,98,2.42,2.65,.37,2.08,4.6,1.19,2.3,678 67 | 2,13.11,1.01,1.7,15,78,2.98,3.18,.26,2.28,5.3,1.12,3.18,502 68 | 2,12.37,1.17,1.92,19.6,78,2.11,2,.27,1.04,4.68,1.12,3.48,510 69 | 2,13.34,.94,2.36,17,110,2.53,1.3,.55,.42,3.17,1.02,1.93,750 70 | 2,12.21,1.19,1.75,16.8,151,1.85,1.28,.14,2.5,2.85,1.28,3.07,718 71 | 2,12.29,1.61,2.21,20.4,103,1.1,1.02,.37,1.46,3.05,.906,1.82,870 72 | 2,13.86,1.51,2.67,25,86,2.95,2.86,.21,1.87,3.38,1.36,3.16,410 73 | 2,13.49,1.66,2.24,24,87,1.88,1.84,.27,1.03,3.74,.98,2.78,472 74 | 2,12.99,1.67,2.6,30,139,3.3,2.89,.21,1.96,3.35,1.31,3.5,985 75 | 2,11.96,1.09,2.3,21,101,3.38,2.14,.13,1.65,3.21,.99,3.13,886 76 | 2,11.66,1.88,1.92,16,97,1.61,1.57,.34,1.15,3.8,1.23,2.14,428 77 | 2,13.03,.9,1.71,16,86,1.95,2.03,.24,1.46,4.6,1.19,2.48,392 78 | 2,11.84,2.89,2.23,18,112,1.72,1.32,.43,.95,2.65,.96,2.52,500 79 | 2,12.33,.99,1.95,14.8,136,1.9,1.85,.35,2.76,3.4,1.06,2.31,750 80 | 2,12.7,3.87,2.4,23,101,2.83,2.55,.43,1.95,2.57,1.19,3.13,463 81 | 2,12,.92,2,19,86,2.42,2.26,.3,1.43,2.5,1.38,3.12,278 82 | 2,12.72,1.81,2.2,18.8,86,2.2,2.53,.26,1.77,3.9,1.16,3.14,714 83 | 2,12.08,1.13,2.51,24,78,2,1.58,.4,1.4,2.2,1.31,2.72,630 84 | 2,13.05,3.86,2.32,22.5,85,1.65,1.59,.61,1.62,4.8,.84,2.01,515 85 | 2,11.84,.89,2.58,18,94,2.2,2.21,.22,2.35,3.05,.79,3.08,520 86 | 2,12.67,.98,2.24,18,99,2.2,1.94,.3,1.46,2.62,1.23,3.16,450 87 | 2,12.16,1.61,2.31,22.8,90,1.78,1.69,.43,1.56,2.45,1.33,2.26,495 88 | 2,11.65,1.67,2.62,26,88,1.92,1.61,.4,1.34,2.6,1.36,3.21,562 89 | 2,11.64,2.06,2.46,21.6,84,1.95,1.69,.48,1.35,2.8,1,2.75,680 90 | 2,12.08,1.33,2.3,23.6,70,2.2,1.59,.42,1.38,1.74,1.07,3.21,625 91 | 2,12.08,1.83,2.32,18.5,81,1.6,1.5,.52,1.64,2.4,1.08,2.27,480 92 | 2,12,1.51,2.42,22,86,1.45,1.25,.5,1.63,3.6,1.05,2.65,450 93 | 2,12.69,1.53,2.26,20.7,80,1.38,1.46,.58,1.62,3.05,.96,2.06,495 94 | 2,12.29,2.83,2.22,18,88,2.45,2.25,.25,1.99,2.15,1.15,3.3,290 95 | 2,11.62,1.99,2.28,18,98,3.02,2.26,.17,1.35,3.25,1.16,2.96,345 96 | 2,12.47,1.52,2.2,19,162,2.5,2.27,.32,3.28,2.6,1.16,2.63,937 97 | 2,11.81,2.12,2.74,21.5,134,1.6,.99,.14,1.56,2.5,.95,2.26,625 98 | 2,12.29,1.41,1.98,16,85,2.55,2.5,.29,1.77,2.9,1.23,2.74,428 99 | 2,12.37,1.07,2.1,18.5,88,3.52,3.75,.24,1.95,4.5,1.04,2.77,660 100 | 2,12.29,3.17,2.21,18,88,2.85,2.99,.45,2.81,2.3,1.42,2.83,406 101 | 2,12.08,2.08,1.7,17.5,97,2.23,2.17,.26,1.4,3.3,1.27,2.96,710 102 | 2,12.6,1.34,1.9,18.5,88,1.45,1.36,.29,1.35,2.45,1.04,2.77,562 103 | 2,12.34,2.45,2.46,21,98,2.56,2.11,.34,1.31,2.8,.8,3.38,438 104 | 2,11.82,1.72,1.88,19.5,86,2.5,1.64,.37,1.42,2.06,.94,2.44,415 105 | 2,12.51,1.73,1.98,20.5,85,2.2,1.92,.32,1.48,2.94,1.04,3.57,672 106 | 2,12.42,2.55,2.27,22,90,1.68,1.84,.66,1.42,2.7,.86,3.3,315 107 | 2,12.25,1.73,2.12,19,80,1.65,2.03,.37,1.63,3.4,1,3.17,510 108 | 2,12.72,1.75,2.28,22.5,84,1.38,1.76,.48,1.63,3.3,.88,2.42,488 109 | 2,12.22,1.29,1.94,19,92,2.36,2.04,.39,2.08,2.7,.86,3.02,312 110 | 2,11.61,1.35,2.7,20,94,2.74,2.92,.29,2.49,2.65,.96,3.26,680 111 | 2,11.46,3.74,1.82,19.5,107,3.18,2.58,.24,3.58,2.9,.75,2.81,562 112 | 2,12.52,2.43,2.17,21,88,2.55,2.27,.26,1.22,2,.9,2.78,325 113 | 2,11.76,2.68,2.92,20,103,1.75,2.03,.6,1.05,3.8,1.23,2.5,607 114 | 2,11.41,.74,2.5,21,88,2.48,2.01,.42,1.44,3.08,1.1,2.31,434 115 | 2,12.08,1.39,2.5,22.5,84,2.56,2.29,.43,1.04,2.9,.93,3.19,385 116 | 2,11.03,1.51,2.2,21.5,85,2.46,2.17,.52,2.01,1.9,1.71,2.87,407 117 | 2,11.82,1.47,1.99,20.8,86,1.98,1.6,.3,1.53,1.95,.95,3.33,495 118 | 2,12.42,1.61,2.19,22.5,108,2,2.09,.34,1.61,2.06,1.06,2.96,345 119 | 2,12.77,3.43,1.98,16,80,1.63,1.25,.43,.83,3.4,.7,2.12,372 120 | 2,12,3.43,2,19,87,2,1.64,.37,1.87,1.28,.93,3.05,564 121 | 2,11.45,2.4,2.42,20,96,2.9,2.79,.32,1.83,3.25,.8,3.39,625 122 | 2,11.56,2.05,3.23,28.5,119,3.18,5.08,.47,1.87,6,.93,3.69,465 123 | 2,12.42,4.43,2.73,26.5,102,2.2,2.13,.43,1.71,2.08,.92,3.12,365 124 | 2,13.05,5.8,2.13,21.5,86,2.62,2.65,.3,2.01,2.6,.73,3.1,380 125 | 2,11.87,4.31,2.39,21,82,2.86,3.03,.21,2.91,2.8,.75,3.64,380 126 | 2,12.07,2.16,2.17,21,85,2.6,2.65,.37,1.35,2.76,.86,3.28,378 127 | 2,12.43,1.53,2.29,21.5,86,2.74,3.15,.39,1.77,3.94,.69,2.84,352 128 | 2,11.79,2.13,2.78,28.5,92,2.13,2.24,.58,1.76,3,.97,2.44,466 129 | 2,12.37,1.63,2.3,24.5,88,2.22,2.45,.4,1.9,2.12,.89,2.78,342 130 | 2,12.04,4.3,2.38,22,80,2.1,1.75,.42,1.35,2.6,.79,2.57,580 131 | 3,12.86,1.35,2.32,18,122,1.51,1.25,.21,.94,4.1,.76,1.29,630 132 | 3,12.88,2.99,2.4,20,104,1.3,1.22,.24,.83,5.4,.74,1.42,530 133 | 3,12.81,2.31,2.4,24,98,1.15,1.09,.27,.83,5.7,.66,1.36,560 134 | 3,12.7,3.55,2.36,21.5,106,1.7,1.2,.17,.84,5,.78,1.29,600 135 | 3,12.51,1.24,2.25,17.5,85,2,.58,.6,1.25,5.45,.75,1.51,650 136 | 3,12.6,2.46,2.2,18.5,94,1.62,.66,.63,.94,7.1,.73,1.58,695 137 | 3,12.25,4.72,2.54,21,89,1.38,.47,.53,.8,3.85,.75,1.27,720 138 | 3,12.53,5.51,2.64,25,96,1.79,.6,.63,1.1,5,.82,1.69,515 139 | 3,13.49,3.59,2.19,19.5,88,1.62,.48,.58,.88,5.7,.81,1.82,580 140 | 3,12.84,2.96,2.61,24,101,2.32,.6,.53,.81,4.92,.89,2.15,590 141 | 3,12.93,2.81,2.7,21,96,1.54,.5,.53,.75,4.6,.77,2.31,600 142 | 3,13.36,2.56,2.35,20,89,1.4,.5,.37,.64,5.6,.7,2.47,780 143 | 3,13.52,3.17,2.72,23.5,97,1.55,.52,.5,.55,4.35,.89,2.06,520 144 | 3,13.62,4.95,2.35,20,92,2,.8,.47,1.02,4.4,.91,2.05,550 145 | 3,12.25,3.88,2.2,18.5,112,1.38,.78,.29,1.14,8.21,.65,2,855 146 | 3,13.16,3.57,2.15,21,102,1.5,.55,.43,1.3,4,.6,1.68,830 147 | 3,13.88,5.04,2.23,20,80,.98,.34,.4,.68,4.9,.58,1.33,415 148 | 3,12.87,4.61,2.48,21.5,86,1.7,.65,.47,.86,7.65,.54,1.86,625 149 | 3,13.32,3.24,2.38,21.5,92,1.93,.76,.45,1.25,8.42,.55,1.62,650 150 | 3,13.08,3.9,2.36,21.5,113,1.41,1.39,.34,1.14,9.40,.57,1.33,550 151 | 3,13.5,3.12,2.62,24,123,1.4,1.57,.22,1.25,8.60,.59,1.3,500 152 | 3,12.79,2.67,2.48,22,112,1.48,1.36,.24,1.26,10.8,.48,1.47,480 153 | 3,13.11,1.9,2.75,25.5,116,2.2,1.28,.26,1.56,7.1,.61,1.33,425 154 | 3,13.23,3.3,2.28,18.5,98,1.8,.83,.61,1.87,10.52,.56,1.51,675 155 | 3,12.58,1.29,2.1,20,103,1.48,.58,.53,1.4,7.6,.58,1.55,640 156 | 3,13.17,5.19,2.32,22,93,1.74,.63,.61,1.55,7.9,.6,1.48,725 157 | 3,13.84,4.12,2.38,19.5,89,1.8,.83,.48,1.56,9.01,.57,1.64,480 158 | 3,12.45,3.03,2.64,27,97,1.9,.58,.63,1.14,7.5,.67,1.73,880 159 | 3,14.34,1.68,2.7,25,98,2.8,1.31,.53,2.7,13,.57,1.96,660 160 | 3,13.48,1.67,2.64,22.5,89,2.6,1.1,.52,2.29,11.75,.57,1.78,620 161 | 3,12.36,3.83,2.38,21,88,2.3,.92,.5,1.04,7.65,.56,1.58,520 162 | 3,13.69,3.26,2.54,20,107,1.83,.56,.5,.8,5.88,.96,1.82,680 163 | 3,12.85,3.27,2.58,22,106,1.65,.6,.6,.96,5.58,.87,2.11,570 164 | 3,12.96,3.45,2.35,18.5,106,1.39,.7,.4,.94,5.28,.68,1.75,675 165 | 3,13.78,2.76,2.3,22,90,1.35,.68,.41,1.03,9.58,.7,1.68,615 166 | 3,13.73,4.36,2.26,22.5,88,1.28,.47,.52,1.15,6.62,.78,1.75,520 167 | 3,13.45,3.7,2.6,23,111,1.7,.92,.43,1.46,10.68,.85,1.56,695 168 | 3,12.82,3.37,2.3,19.5,88,1.48,.66,.4,.97,10.26,.72,1.75,685 169 | 3,13.58,2.58,2.69,24.5,105,1.55,.84,.39,1.54,8.66,.74,1.8,750 170 | 3,13.4,4.6,2.86,25,112,1.98,.96,.27,1.11,8.5,.67,1.92,630 171 | 3,12.2,3.03,2.32,19,96,1.25,.49,.4,.73,5.5,.66,1.83,510 172 | 3,12.77,2.39,2.28,19.5,86,1.39,.51,.48,.64,9.899999,.57,1.63,470 173 | 3,14.16,2.51,2.48,20,91,1.68,.7,.44,1.24,9.7,.62,1.71,660 174 | 3,13.71,5.65,2.45,20.5,95,1.68,.61,.52,1.06,7.7,.64,1.74,740 175 | 3,13.4,3.91,2.48,23,102,1.8,.75,.43,1.41,7.3,.7,1.56,750 176 | 3,13.27,4.28,2.26,20,120,1.59,.69,.43,1.35,10.2,.59,1.56,835 177 | 3,13.17,2.59,2.37,20,120,1.65,.68,.53,1.46,9.3,.6,1.62,840 178 | 3,14.13,4.1,2.74,24.5,96,2.05,.76,.56,1.35,9.2,.61,1.6,560 179 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davecom/SwiftSimpleNeuralNetwork/62143986d4b02acb927aa51fcf61947bd956b790/screenshot.png --------------------------------------------------------------------------------