@@ -57,21 +57,9 @@ func randomWeights(number: Int) -> [Double] {
57
57
return ( 0 ..< number) . map { _ in Random . double ( from: 0.0 , to: 1.0 ) }
58
58
}
59
59
60
- // MARK: Activation Functions and Their Derivatives
61
-
62
- /// the classic sigmoid activation function
63
- func sigmoid( _ x: Double ) -> Double {
64
- return 1.0 / ( 1.0 + exp( - x) )
65
- }
66
-
67
- // as derived at http://www.ai.mit.edu/courses/6.892/lecture8-html/sld015.htm
68
- func derivativeSigmoid( _ x: Double ) -> Double {
69
- return sigmoid ( x) * ( 1 - sigmoid( x) )
70
- }
71
-
72
60
// MARK: SIMD Accelerated Math
73
61
74
- // Based on example from Surge project
62
+ // The next four functions are based on example from Surge project
75
63
// https://github.com/mattt/Surge/blob/master/Source/Arithmetic.swift
76
64
/// Find the dot product of two vectors
77
65
/// assuming that they are of the same length
@@ -82,44 +70,49 @@ func dotProduct(_ xs: [Double], _ ys: [Double]) -> Double {
82
70
return answer
83
71
}
84
72
85
- // Based on example from Surge project
86
- // https://github.com/mattt/Surge/blob/master/Source/Arithmetic.swift
87
73
/// Subtract one vector from another
88
- /// assuming that they are of the same length
89
- /// using SIMD instructions to speed computation
90
- public func sub( x: [ Double ] , y: [ Double ] ) -> [ Double ] {
74
+ public func sub( _ x: [ Double ] , _ y: [ Double ] ) -> [ Double ] {
91
75
var results = [ Double] ( y)
92
76
catlas_daxpby ( Int32 ( x. count) , 1.0 , x, 1 , - 1 , & results, 1 )
93
-
94
77
return results
95
78
}
96
79
97
- // Another Surge example, see above citation
98
- public func mul( x: [ Double ] , y: [ Double ] ) -> [ Double ] {
80
+ /// Multiply two vectors together
81
+ public func mul( _ x: [ Double ] , _ y: [ Double ] ) -> [ Double ] {
99
82
var results = [ Double] ( repeating: 0.0 , count: x. count)
100
83
vDSP_vmulD ( x, 1 , y, 1 , & results, 1 , vDSP_Length ( x. count) )
101
-
102
84
return results
103
85
}
104
86
105
- // Another Surge example, see above citation
106
- public func sum( x: [ Double ] ) -> Double {
87
+ /// Sum a vector
88
+ public func sum( _ x: [ Double ] ) -> Double {
107
89
var result : Double = 0.0
108
90
vDSP_sveD ( x, 1 , & result, vDSP_Length ( x. count) )
109
-
110
91
return result
111
92
}
112
93
94
+ // MARK: Activation Functions and Their Derivatives
95
+
96
+ /// the classic sigmoid activation function
97
+ func sigmoid( _ x: Double ) -> Double {
98
+ return 1.0 / ( 1.0 + exp( - x) )
99
+ }
100
+
101
+ // as derived at http://www.ai.mit.edu/courses/6.892/lecture8-html/sld015.htm
102
+ func derivativeSigmoid( _ x: Double ) -> Double {
103
+ return sigmoid ( x) * ( 1 - sigmoid( x) )
104
+ }
105
+
113
106
/// An individual node in a layer
114
107
class Neuron {
115
108
var weights : [ Double ]
116
109
var activationFunction : ( Double ) -> Double
117
110
var derivativeActivationFunction : ( Double ) -> Double
118
- var inputCache : Double = 0.0
111
+ var outputCache : Double = 0.0
119
112
var delta : Double = 0.0
120
113
var learningRate : Double
121
114
122
- init ( weights: [ Double ] , activationFunction: @escaping ( Double ) -> Double , derivativeActivationFunction: @escaping ( Double ) -> Double , learningRate: Double = 0.25 ) {
115
+ init ( weights: [ Double ] , activationFunction: @escaping ( Double ) -> Double , derivativeActivationFunction: @escaping ( Double ) -> Double , learningRate: Double ) {
123
116
self . weights = weights
124
117
self . activationFunction = activationFunction
125
118
self . derivativeActivationFunction = derivativeActivationFunction
@@ -129,8 +122,8 @@ class Neuron {
129
122
/// The output that will be going to the next layer
130
123
/// or the final output if this is an output layer
131
124
func output( inputs: [ Double ] ) -> Double {
132
- inputCache = dotProduct ( inputs, weights)
133
- return activationFunction ( inputCache )
125
+ outputCache = dotProduct ( inputs, weights)
126
+ return activationFunction ( outputCache )
134
127
}
135
128
136
129
}
@@ -140,14 +133,6 @@ class Layer {
140
133
var neurons : [ Neuron ]
141
134
var outputCache : [ Double ]
142
135
143
- // for future use in deserializing networks
144
- init ( previousLayer: Layer ? = nil , neurons: [ Neuron ] = [ Neuron] ( ) ) {
145
- self . previousLayer = previousLayer
146
- self . neurons = neurons
147
- self . outputCache = Array < Double > ( repeating: 0.0 , count: neurons. count)
148
- }
149
-
150
- // main init
151
136
init ( previousLayer: Layer ? = nil , numNeurons: Int , activationFunction: @escaping ( Double ) -> Double , derivativeActivationFunction: @escaping ( Double ) -> Double , learningRate: Double ) {
152
137
self . previousLayer = previousLayer
153
138
self . neurons = Array < Neuron > ( )
@@ -169,7 +154,7 @@ class Layer {
169
154
// should only be called on an output layer
170
155
func calculateDeltasForOutputLayer( expected: [ Double ] ) {
171
156
for n in 0 ..< neurons. count {
172
- neurons [ n] . delta = neurons [ n] . derivativeActivationFunction ( neurons [ n] . inputCache ) * ( expected [ n] - outputCache[ n] )
157
+ neurons [ n] . delta = neurons [ n] . derivativeActivationFunction ( neurons [ n] . outputCache ) * ( expected [ n] - outputCache[ n] )
173
158
}
174
159
}
175
160
@@ -179,19 +164,17 @@ class Layer {
179
164
let nextWeights = nextLayer. neurons. map { 0ドル. weights [ index] }
180
165
let nextDeltas = nextLayer. neurons. map { 0ドル. delta }
181
166
let sumOfWeightsXDeltas = dotProduct ( nextWeights, nextDeltas)
182
- neuron. delta = neuron. derivativeActivationFunction ( neuron. inputCache ) * sumOfWeightsXDeltas
167
+ neuron. delta = neuron. derivativeActivationFunction ( neuron. outputCache ) * sumOfWeightsXDeltas
183
168
}
184
169
}
185
-
186
-
187
170
}
188
171
189
172
/// Represents an entire neural network. From largest to smallest we go
190
173
/// Network -> Layers -> Neurons
191
174
class Network {
192
175
var layers : [ Layer ]
193
176
194
- init ( layerStructure: [ Int ] , activationFunction: @escaping ( Double ) -> Double = sigmoid, derivativeActivationFunction: @escaping ( Double ) -> Double = derivativeSigmoid, learningRate: Double = 0.25 ) {
177
+ init ( layerStructure: [ Int ] , activationFunction: @escaping ( Double ) -> Double = sigmoid, derivativeActivationFunction: @escaping ( Double ) -> Double = derivativeSigmoid, learningRate: Double ) {
195
178
if ( layerStructure. count < 3 ) {
196
179
print ( " Error: Should be at least 3 layers (1 input, 1 hidden, 1 output) " )
197
180
}
@@ -214,7 +197,7 @@ class Network {
214
197
215
198
/// Figure out each neuron's changes based on the errors
216
199
/// of the output versus the expected outcome
217
- func backPropagate ( expected: [ Double ] ) {
200
+ func backpropagate ( expected: [ Double ] ) {
218
201
//calculate delta for output layer neurons
219
202
layers. last? . calculateDeltasForOutputLayer ( expected: expected)
220
203
//calculate delta for prior layers
@@ -223,32 +206,32 @@ class Network {
223
206
}
224
207
}
225
208
226
- /// backPropagate () doesn't actually change any weights
227
- /// this function uses the deltas calculated in backPropagate ()
209
+ /// backpropagate () doesn't actually change any weights
210
+ /// this function uses the deltas calculated in backpropagate ()
228
211
/// to actually make changes to the weights
229
212
func updateWeights( ) {
230
- for layer in layers{
213
+ for layer in layers. dropFirst ( ) { // skip input layer
231
214
for neuron in layer. neurons {
232
215
for w in 0 ..< neuron. weights. count {
233
- neuron. weights [ w] = neuron. weights [ w] + ( neuron. learningRate * ( layer. previousLayer? . outputCache [ w] ) ! * neuron. delta)
216
+ neuron. weights [ w] = neuron. weights [ w] + ( neuron. learningRate * ( layer. previousLayer? . outputCache [ w] ) ! * neuron. delta)
234
217
}
235
218
}
236
219
}
237
220
}
238
221
239
222
/// train() uses the results of outputs() run over
240
223
/// many *inputs* and compared against *expecteds* to feed
241
- /// backPropagate () and updateWeights()
224
+ /// backpropagate () and updateWeights()
242
225
func train( inputs: [ [ Double ] ] , expecteds: [ [ Double ] ] , printError: Bool = false , threshold: Double ? = nil ) {
243
226
for (location, xs) in inputs. enumerated ( ) {
244
227
let ys = expecteds [ location]
245
228
let outs = outputs ( input: xs)
246
229
if ( printError) {
247
- let diff = sub ( x : outs, y : ys)
248
- let error = sqrt ( sum ( x : mul ( x : diff, y : diff) ) )
230
+ let diff = sub ( outs, ys)
231
+ let error = sqrt ( sum ( mul ( diff, diff) ) )
249
232
print ( " \( error) error in run \( location) " )
250
233
}
251
- backPropagate ( expected: ys)
234
+ backpropagate ( expected: ys)
252
235
updateWeights ( )
253
236
}
254
237
}
@@ -286,7 +269,7 @@ func normalizeByColumnMax( dataset:inout [[Double]]) {
286
269
287
270
// MARK: Iris Test
288
271
289
- var network : Network = Network ( layerStructure: [ 4 , 5 , 3 ] , learningRate: 0.3 )
272
+ var network : Network = Network ( layerStructure: [ 4 , 6 , 3 ] , learningRate: 0.3 )
290
273
var irisParameters : [ [ Double ] ] = [ [ Double] ] ( )
291
274
var irisClassifications : [ [ Double ] ] = [ [ Double] ] ( )
292
275
var irisSpecies : [ String ] = [ String] ( )
0 commit comments