-
Notifications
You must be signed in to change notification settings - Fork 87
/
DeepLayer.swift
342 lines (293 loc) · 12 KB
/
DeepLayer.swift
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
//
// DeepLayer.swift
// AIToolbox
//
// Created by Kevin Coble on 6/25/16.
// Copyright © 2016 Kevin Coble. All rights reserved.
//
import Foundation
import Accelerate
/// Class for a single layer of a deep network
/// A deep layer contains multiple data channels - all of which can be computed synchronously
final public class DeepLayer : DeepNetworkInputSource, DeepNetworkOutputDestination, MLPersistence
{
var channels : [DeepChannel] = []
public init() {
}
public init?(fromDictionary: [String: AnyObject])
{
// Get the array of channels
let channelArray = fromDictionary["channels"] as? NSArray
if (channelArray == nil) { return nil }
for item in channelArray! {
let element = item as? [String: AnyObject]
if (element == nil) { return nil }
let channel = DeepChannel(fromDictionary: element!)
if (channel == nil) { return nil }
channels.append(channel!)
}
}
/// Function to get the number of defined channels
public var numChannels: Int {
get { return channels.count }
}
/// Function to get the channel for the specified index
public func getChannel(atIndex: Int) -> DeepChannel
{
return channels[atIndex]
}
/// Function to add a channel to the layer
public func addChannel(_ newChannel: DeepChannel)
{
channels.append(newChannel)
}
/// Functions to remove a channel from the layer
public func removeChannel(_ channelIndex: Int)
{
if (channelIndex >= 0 && channelIndex < channels.count) {
channels.remove(at: channelIndex)
}
}
public func removeChannel(_ channelID: String)
{
if let index = getChannelIndex(channelID) {
removeChannel(index)
}
}
/// Function to get the size of a channel output (as input for next layer)
public func getInputDataSize(_ inputIDs : [String]) -> DeepChannelSize?
{
var currentSize : DeepChannelSize?
// Get the size of each input
for sourceID in inputIDs {
if let channelIndex = getChannelIndex(sourceID) {
if (currentSize == nil) {
// First input - start with this size
currentSize = channels[channelIndex].resultSize
}
else {
// Not first input, add sizes together
let extendedCurrentSize = currentSize!.dimensions + [1, 1, 1]
let extendedInputSize = channels[channelIndex].resultSize.dimensions + [1, 1, 1]
for index in 0..<4 {
if (index <= currentSize!.numDimensions && index <= channels[channelIndex].resultSize.numDimensions) {
if (extendedCurrentSize[index] != extendedInputSize[index]) { return nil }
continue
}
if (index == currentSize!.numDimensions && index == channels[channelIndex].resultSize.numDimensions) {
var newDimensions = currentSize!.dimensions
newDimensions.append(extendedCurrentSize[index] + extendedInputSize[index])
currentSize = DeepChannelSize(dimensionCount : index + 1, dimensionValues : newDimensions)
break
}
else if (index < currentSize!.numDimensions) {
var newDimensions = currentSize!.dimensions
newDimensions[index] = (extendedCurrentSize[index] + extendedInputSize[index])
currentSize = DeepChannelSize(dimensionCount : index + 1, dimensionValues : newDimensions)
break
}
else {
var newDimensions = channels[channelIndex].resultSize.dimensions
newDimensions[index] = (extendedCurrentSize[index] + extendedInputSize[index])
currentSize = DeepChannelSize(dimensionCount : index + 1, dimensionValues : newDimensions)
break
}
}
}
}
else {
// The input source wasn't found
return nil
}
}
return currentSize
}
/// Function to add a network operator to a channel in the layer
public func addNetworkOperator(_ channelIndex: Int, newOperator: DeepNetworkOperator)
{
if (channelIndex >= 0 && channelIndex < channels.count) {
channels[channelIndex].addNetworkOperator(newOperator)
}
}
/// Function to get a network operator at the specified index
public func getNetworkOperator(_ channelIndex: Int, operatorIndex: Int) ->DeepNetworkOperator?
{
if (channelIndex >= 0 && channelIndex < channels.count) {
return channels[channelIndex].getNetworkOperator(operatorIndex)
}
return nil
}
/// Function to get a replace operator at the specified index
public func replaceNetworkOperator(_ channelIndex: Int, operatorIndex: Int, newOperator: DeepNetworkOperator)
{
if (channelIndex >= 0 && channelIndex < channels.count) {
return channels[channelIndex].replaceNetworkOperator(operatorIndex, newOperator: newOperator)
}
}
/// Function to remove a network operator from a channel in the layer
public func removeNetworkOperator(_ channelIndex: Int, operatorIndex: Int)
{
if (channelIndex >= 0 && channelIndex < channels.count) {
channels[channelIndex].removeNetworkOperator(operatorIndex)
}
}
/// Function to find a channel index from an ID
public func getChannelIndex(_ channelID: String) -> Int?
{
for index in 0..<channels.count {
if (channels[index].idString == channelID) { return index }
}
return nil
}
func validateAgainstPreviousLayer(_ prevLayer: DeepNetworkInputSource, layerIndex: Int) ->[String]
{
var errors : [String] = []
// Check each channel
for channel in channels {
errors += channel.validateAgainstPreviousLayer(prevLayer, layerIndex: layerIndex)
}
return errors
}
func getResultRange() ->(minimum: Float, maximum: Float)
{
if let lastChannel = channels.last {
return lastChannel.getResultRange()
}
return (minimum: 0.0, maximum: 1.0)
}
public func initializeParameters()
{
for channel in channels {
channel.initializeParameters()
}
}
// Method to feed values forward through the layer
func feedForward(_ prevLayer: DeepNetworkInputSource)
{
// Get a concurrent GCD queue to run this all in
let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
// Get a GCD group so we can know when all channels are done
let group = DispatchGroup();
// Process each channel concurrently
for channel in channels {
queue.async(group: group) {
channel.feedForward(prevLayer)
}
}
// Wait for the channels to finish calculating before the next layer is allowed to start
group.wait()
}
// Function to clear weight-change accumulations for the start of a batch
public func startBatch()
{
// Get a concurrent GCD queue to run this all in
let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
// Get a GCD group so we can know when all channels are done
let group = DispatchGroup();
// Process each channel concurrently
for channel in channels {
queue.async(group: group) {
channel.startBatch()
}
}
// Wait for the channels to finish calculating before the next layer is allowed to start
group.wait()
}
func backPropagate(_ gradientSource: DeepNetworkOutputDestination)
{
// Get a concurrent GCD queue to run this all in
let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
// Get a GCD group so we can know when all channels are done
let group = DispatchGroup();
// Process each channel concurrently
for channel in channels {
queue.async(group: group) {
channel.backPropagate(gradientSource)
}
}
// Wait for the channels to finish calculating before the previous layer is allowed to start
group.wait()
}
public func updateWeights(_ trainingRate : Float, weightDecay: Float)
{
// Get a concurrent GCD queue to run this all in
let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
// Get a GCD group so we can know when all channels are done
let group = DispatchGroup();
// Process each channel concurrently
for channel in channels {
queue.async(group: group) {
channel.updateWeights(trainingRate, weightDecay: weightDecay)
}
}
// Wait for the channels to finish calculating before the previous layer is allowed to start
group.wait()
}
public func gradientCheck(ε: Float, Δ: Float, network: DeepNetwork) -> Bool
{
// Have each channel check - we can't do this concurrently, as the result values for the whole network must be used for each check
var result = true
for channel in 0..<channels.count {
if (!channels[channel].gradientCheck(ε: ε, Δ: Δ, network: network)) { result = false }
}
return result
}
public func getValuesForIDs(_ inputIDs : [String]) -> [Float]
{
var combinedInputs : [Float] = []
// Get the index
for sourceID in inputIDs {
if let index = getChannelIndex(sourceID) {
combinedInputs += channels[index].getFinalResult()
}
else {
return []
}
}
return combinedInputs
}
public func getAllValues() -> [Float]
{
var result : [Float] = []
for channel in channels {
result += channel.getFinalResult()
}
return result
}
func getGradientForSource(_ sourceID : String) -> [Float]
{
// Sum the gradient from each channel that uses the source
var result : [Float] = []
for channel in channels {
if (channel.usesInputID(sourceID)) {
//!!ktc - getGradient need to indicate sourceID?
let channelGradient = channel.getGradient()
if (result.count == 0) {
result = channelGradient
}
else {
vDSP_vadd(result, 1, channelGradient, 1, &result, 1, vDSP_Length(result.count))
}
}
}
return result
}
public func getResultOfItem(_ channelIndex: Int, operatorIndex: Int) ->(values : [Float], size: DeepChannelSize)?
{
if (channelIndex >= 0 && channelIndex < channels.count) {
return channels[channelIndex].getResultOfItem(operatorIndex)
}
return nil
}
public func getPersistenceDictionary() -> [String: AnyObject]
{
var resultDictionary : [String: AnyObject] = [:]
// Set the array of channels
var channelArray : [[String: AnyObject]] = []
for channel in channels {
channelArray.append(channel.getPersistenceDictionary())
}
resultDictionary["channels"] = channelArray as AnyObject?
return resultDictionary
}
}