|
a |
|
b/src/LiviaNet/LiviaNet3DConvLayer.py |
|
|
1 |
""" |
|
|
2 |
Copyright (c) 2016, Jose Dolz .All rights reserved. |
|
|
3 |
|
|
|
4 |
Redistribution and use in source and binary forms, with or without modification, |
|
|
5 |
are permitted provided that the following conditions are met: |
|
|
6 |
|
|
|
7 |
1. Redistributions of source code must retain the above copyright notice, |
|
|
8 |
this list of conditions and the following disclaimer. |
|
|
9 |
2. Redistributions in binary form must reproduce the above copyright notice, |
|
|
10 |
this list of conditions and the following disclaimer in the documentation |
|
|
11 |
and/or other materials provided with the distribution. |
|
|
12 |
|
|
|
13 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
|
14 |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
|
|
15 |
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
|
16 |
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
|
|
17 |
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
|
|
18 |
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
|
19 |
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
|
|
20 |
OTHER DEALINGS IN THE SOFTWARE. |
|
|
21 |
|
|
|
22 |
Jose Dolz. Dec, 2016. |
|
|
23 |
email: jose.dolz.upv@gmail.com |
|
|
24 |
LIVIA Department, ETS, Montreal. |
|
|
25 |
""" |
|
|
26 |
|
|
|
27 |
|
|
|
28 |
import theano |
|
|
29 |
import theano.tensor as T |
|
|
30 |
from theano.tensor.nnet import conv2d |
|
|
31 |
import theano.tensor.nnet.conv3d2d |
|
|
32 |
import pdb |
|
|
33 |
|
|
|
34 |
import sys |
|
|
35 |
import os |
|
|
36 |
import numpy as np |
|
|
37 |
import numpy |
|
|
38 |
import random |
|
|
39 |
|
|
|
40 |
from Modules.General.Utils import initializeWeights |
|
|
41 |
from Modules.NeuralNetwork.ActivationFunctions import * |
|
|
42 |
from Modules.NeuralNetwork.layerOperations import * |
|
|
43 |
|
|
|
44 |
################################################################# |
|
|
45 |
# Layer Types # |
|
|
46 |
################################################################# |
|
|
47 |
|
|
|
48 |
class LiviaNet3DConvLayer(object): |
|
|
49 |
"""Convolutional Layer of the Livia network """ |
|
|
50 |
def __init__(self, |
|
|
51 |
rng, |
|
|
52 |
layerID, |
|
|
53 |
inputSample_Train, |
|
|
54 |
inputSample_Test, |
|
|
55 |
inputToLayerShapeTrain, |
|
|
56 |
inputToLayerShapeTest, |
|
|
57 |
filterShape, |
|
|
58 |
useBatchNorm, |
|
|
59 |
numberEpochApplyRolling, |
|
|
60 |
maxPoolingParameters, |
|
|
61 |
weights_initMethodType, |
|
|
62 |
weights, |
|
|
63 |
activationType, |
|
|
64 |
dropoutRate=0.0) : |
|
|
65 |
|
|
|
66 |
self.inputTrain = None |
|
|
67 |
self.inputTest = None |
|
|
68 |
self.inputShapeTrain = None |
|
|
69 |
self.inputShapeTest = None |
|
|
70 |
|
|
|
71 |
self._numberOfFeatureMaps = 0 |
|
|
72 |
self._maxPoolingParameters = None |
|
|
73 |
self._appliedBnInLayer = None |
|
|
74 |
self.params = [] |
|
|
75 |
self.W = None |
|
|
76 |
self._gBn = None |
|
|
77 |
self._b = None |
|
|
78 |
self._aPrelu = None |
|
|
79 |
self.numberOfTrainableParams = 0 |
|
|
80 |
|
|
|
81 |
self.muBatchNorm = None |
|
|
82 |
self._varBnsArrayForRollingAverage = None |
|
|
83 |
self.numberEpochApplyRolling = numberEpochApplyRolling |
|
|
84 |
self.rollingIndex = 0 |
|
|
85 |
self._sharedNewMu_B = None |
|
|
86 |
self._sharedNewVar_B = None |
|
|
87 |
self._newMu_B = None |
|
|
88 |
self._newVar_B = None |
|
|
89 |
|
|
|
90 |
self.outputTrain = None |
|
|
91 |
self.outputTest = None |
|
|
92 |
self.outputShapeTrain = None |
|
|
93 |
self.outputShapeTest = None |
|
|
94 |
|
|
|
95 |
# === After all the parameters has been initialized, create the layer |
|
|
96 |
# Set all the inputs and parameters |
|
|
97 |
self.inputTrain = inputSample_Train |
|
|
98 |
self.inputTest = inputSample_Test |
|
|
99 |
self.inputShapeTrain = inputToLayerShapeTrain |
|
|
100 |
self.inputShapeTest = inputToLayerShapeTest |
|
|
101 |
|
|
|
102 |
self._numberOfFeatureMaps = filterShape[0] |
|
|
103 |
assert self.inputShapeTrain[1] == filterShape[1] |
|
|
104 |
self._maxPoolingParameters = maxPoolingParameters |
|
|
105 |
|
|
|
106 |
print(" --- [STATUS] --------- Creating layer {} --------- ".format(layerID)) |
|
|
107 |
|
|
|
108 |
## Process the input layer through all the steps over the block |
|
|
109 |
|
|
|
110 |
(inputToConvTrain, |
|
|
111 |
inputToConvTest) = self.passInputThroughLayerElements(inputSample_Train, |
|
|
112 |
inputToLayerShapeTrain, |
|
|
113 |
inputSample_Test, |
|
|
114 |
inputToLayerShapeTest, |
|
|
115 |
useBatchNorm, |
|
|
116 |
numberEpochApplyRolling, |
|
|
117 |
activationType, |
|
|
118 |
weights, |
|
|
119 |
dropoutRate, |
|
|
120 |
rng |
|
|
121 |
) |
|
|
122 |
# input shapes for the convolutions |
|
|
123 |
inputToConvShapeTrain = inputToLayerShapeTrain |
|
|
124 |
inputToConvShapeTest = inputToLayerShapeTest |
|
|
125 |
|
|
|
126 |
# -------------- Weights initialization ------------- |
|
|
127 |
# Initialize weights with random weights if W is empty |
|
|
128 |
# Otherwise, use loaded weights |
|
|
129 |
|
|
|
130 |
self.W = initializeWeights(filterShape, |
|
|
131 |
weights_initMethodType, |
|
|
132 |
weights) |
|
|
133 |
|
|
|
134 |
self.params = [self.W] + self.params |
|
|
135 |
self.numberOfTrainableParams += 1 |
|
|
136 |
|
|
|
137 |
##---------- Convolve -------------- |
|
|
138 |
(convolvedOutput_Train, convolvedOutputShape_Train) = convolveWithKernel(self.W, filterShape, inputToConvTrain, inputToConvShapeTrain) |
|
|
139 |
(convolvedOutput_Test, convolvedOutputShape_Test) = convolveWithKernel(self.W , filterShape, inputToConvTest, inputToConvShapeTest) |
|
|
140 |
|
|
|
141 |
self.outputTrain = convolvedOutput_Train |
|
|
142 |
self.outputTest = convolvedOutput_Test |
|
|
143 |
self.outputShapeTrain = convolvedOutputShape_Train |
|
|
144 |
self.outputShapeTest = convolvedOutputShape_Test |
|
|
145 |
|
|
|
146 |
|
|
|
147 |
def updateLayerMatricesBatchNorm(self): |
|
|
148 |
|
|
|
149 |
if self._appliedBnInLayer : |
|
|
150 |
muArrayValue = self.muBatchNorm.get_value() |
|
|
151 |
muArrayValue[self.rollingIndex] = self._sharedNewMu_B.get_value() |
|
|
152 |
self.muBatchNorm.set_value(muArrayValue, borrow=True) |
|
|
153 |
|
|
|
154 |
varArrayValue = self._varBnsArrayForRollingAverage.get_value() |
|
|
155 |
varArrayValue[self.rollingIndex] = self._sharedNewVar_B.get_value() |
|
|
156 |
self._varBnsArrayForRollingAverage.set_value(varArrayValue, borrow=True) |
|
|
157 |
self.rollingIndex = (self.rollingIndex + 1) % self.numberEpochApplyRolling |
|
|
158 |
|
|
|
159 |
def getUpdatesForBnRollingAverage(self) : |
|
|
160 |
if self._appliedBnInLayer : |
|
|
161 |
return [(self._sharedNewMu_B, self._newMu_B), |
|
|
162 |
(self._sharedNewVar_B, self._newVar_B) ] |
|
|
163 |
else : |
|
|
164 |
return [] |
|
|
165 |
|
|
|
166 |
|
|
|
167 |
def passInputThroughLayerElements(self, |
|
|
168 |
inputSample_Train, |
|
|
169 |
inputSampleShape_Train, |
|
|
170 |
inputSample_Test, |
|
|
171 |
inputSampleShape_Test, |
|
|
172 |
useBatchNorm, |
|
|
173 |
numberEpochApplyRolling, |
|
|
174 |
activationType, |
|
|
175 |
weights, |
|
|
176 |
dropoutRate, |
|
|
177 |
rndState): |
|
|
178 |
""" Through each block the following steps are applied, according to Kamnitsas: |
|
|
179 |
1 - Batch Normalization or biases |
|
|
180 |
2 - Activation function |
|
|
181 |
3 - Dropout |
|
|
182 |
4 - (Optional) Max pooling |
|
|
183 |
|
|
|
184 |
Ref: He et al "Identity Mappings in Deep Residual Networks" 2016 |
|
|
185 |
https://github.com/KaimingHe/resnet-1k-layers/blob/master/resnet-pre-act.lua """ |
|
|
186 |
|
|
|
187 |
# ________________________________________________________ |
|
|
188 |
# 1 : Batch Normalization |
|
|
189 |
# ________________________________________________________ |
|
|
190 |
""" Implemenation taken from Kamnitsas work. |
|
|
191 |
|
|
|
192 |
A batch normalization implementation in TensorFlow: |
|
|
193 |
|
|
|
194 |
http://r2rt.com/implementing-batch-normalization-in-tensorflow.html |
|
|
195 |
|
|
|
196 |
"Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift", |
|
|
197 |
Proceedings of the 32nd International Conference on Machine Learning, Lille, France, 2015. |
|
|
198 |
Journal of Machine Learning Research: W&CP volume 37 |
|
|
199 |
""" |
|
|
200 |
if useBatchNorm > 0 : |
|
|
201 |
|
|
|
202 |
self._appliedBnInLayer = True |
|
|
203 |
|
|
|
204 |
(inputToNonLinearityTrain, |
|
|
205 |
inputToNonLinearityTest, |
|
|
206 |
self._gBn, |
|
|
207 |
self._b, |
|
|
208 |
self.muBatchNorm, |
|
|
209 |
self._varBnsArrayForRollingAverage, |
|
|
210 |
self._sharedNewMu_B, |
|
|
211 |
self._sharedNewVar_B, |
|
|
212 |
self._newMu_B, |
|
|
213 |
self._newVar_B) = applyBn( numberEpochApplyRolling, |
|
|
214 |
inputSample_Train, |
|
|
215 |
inputSample_Test, |
|
|
216 |
inputSampleShape_Train) |
|
|
217 |
|
|
|
218 |
self.params = self.params + [self._gBn, self._b] |
|
|
219 |
else : |
|
|
220 |
self._appliedBnInLayer = False |
|
|
221 |
numberOfInputFeatMaps = inputSampleShape_Train[1] |
|
|
222 |
|
|
|
223 |
b_values = np.zeros( (self._numberOfFeatureMaps), dtype = 'float32') |
|
|
224 |
self._b = theano.shared(value=b_values, borrow=True) |
|
|
225 |
|
|
|
226 |
inputToNonLinearityTrain = applyBiasToFeatureMaps( self._b, inputSample_Train ) |
|
|
227 |
inputToNonLinearityTest = applyBiasToFeatureMaps( self._b, inputSample_Test ) |
|
|
228 |
|
|
|
229 |
self.params = self.params + [self._b] |
|
|
230 |
|
|
|
231 |
# ________________________________________________________ |
|
|
232 |
# 2 : Apply the corresponding activation function |
|
|
233 |
# ________________________________________________________ |
|
|
234 |
def Linear(): |
|
|
235 |
print " --- Activation function: Linear" |
|
|
236 |
self.activationFunctionType = "Linear" |
|
|
237 |
output_Train = inputToNonLinearityTrain |
|
|
238 |
output_Test = inputToNonLinearityTest |
|
|
239 |
return (output_Train, output_Test) |
|
|
240 |
|
|
|
241 |
def ReLU(): |
|
|
242 |
print " --- Activation function: ReLU" |
|
|
243 |
self.activationFunctionType = "ReLU" |
|
|
244 |
output_Train = applyActivationFunction_ReLU_v1(inputToNonLinearityTrain) |
|
|
245 |
output_Test = applyActivationFunction_ReLU_v1(inputToNonLinearityTest) |
|
|
246 |
return (output_Train, output_Test) |
|
|
247 |
|
|
|
248 |
def PReLU(): |
|
|
249 |
print " --- Activation function: PReLU" |
|
|
250 |
self.activationFunctionType = "PReLU" |
|
|
251 |
numberOfInputFeatMaps = inputSampleShape_Train[1] |
|
|
252 |
PReLU_Values = np.ones( (numberOfInputFeatMaps), dtype = 'float32' )*0.01 |
|
|
253 |
self._aPrelu = theano.shared(value=PReLU_Values, borrow=True) |
|
|
254 |
|
|
|
255 |
output_Train = applyActivationFunction_PReLU(inputToNonLinearityTrain, self._aPrelu) |
|
|
256 |
output_Test = applyActivationFunction_PReLU(inputToNonLinearityTest, self._aPrelu) |
|
|
257 |
self.params = self.params + [self._aPrelu] |
|
|
258 |
self.numberOfTrainableParams += 1 |
|
|
259 |
return (output_Train,output_Test) |
|
|
260 |
|
|
|
261 |
def LeakyReLU(): |
|
|
262 |
print " --- Activation function: Leaky ReLU " |
|
|
263 |
self.activationFunctionType = "Leky ReLU" |
|
|
264 |
leakiness = 0.2 # TODO. Introduce this value in the config.ini |
|
|
265 |
output_Train = applyActivationFunction_LeakyReLU(inputToNonLinearityTrain,leakiness) |
|
|
266 |
output_Test = applyActivationFunction_LeakyReLU(inputToNonLinearityTest,leakiness) |
|
|
267 |
return (output_Train, output_Test) |
|
|
268 |
|
|
|
269 |
optionsActFunction = {0 : Linear, |
|
|
270 |
1 : ReLU, |
|
|
271 |
2 : PReLU, |
|
|
272 |
3 : LeakyReLU} |
|
|
273 |
|
|
|
274 |
(inputToDropout_Train, inputToDropout_Test) = optionsActFunction[activationType]() |
|
|
275 |
|
|
|
276 |
# ________________________________________________________ |
|
|
277 |
# 3 : Apply Dropout |
|
|
278 |
# ________________________________________________________ |
|
|
279 |
output_Train = apply_Dropout(rndState,dropoutRate,inputSampleShape_Train,inputToDropout_Train, 0) |
|
|
280 |
output_Test = apply_Dropout(rndState,dropoutRate,inputSampleShape_Train,inputToDropout_Test, 1) |
|
|
281 |
|
|
|
282 |
# ________________________________________________________ |
|
|
283 |
# This will go as input to the convolutions |
|
|
284 |
# ________________________________________________________ |
|
|
285 |
|
|
|
286 |
return (output_Train, output_Test) |
|
|
287 |
|
|
|
288 |
|