Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/keras/src/layers/locally_connected/locally_connected1d.py: 24%

94 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2015 The TensorFlow Authors. All Rights Reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================== 

15 

16"""Locally-connected layer for 1D input.""" 

17 

18from keras.src import activations 

19from keras.src import backend 

20from keras.src import constraints 

21from keras.src import initializers 

22from keras.src import regularizers 

23from keras.src.engine.base_layer import Layer 

24from keras.src.engine.input_spec import InputSpec 

25from keras.src.layers.locally_connected import locally_connected_utils 

26from keras.src.utils import conv_utils 

27from keras.src.utils import tf_utils 

28 

29# isort: off 

30from tensorflow.python.util.tf_export import keras_export 

31 

32 

33@keras_export("keras.layers.LocallyConnected1D") 

34class LocallyConnected1D(Layer): 

35 """Locally-connected layer for 1D inputs. 

36 

37 The `LocallyConnected1D` layer works similarly to 

38 the `Conv1D` layer, except that weights are unshared, 

39 that is, a different set of filters is applied at each different patch 

40 of the input. 

41 

42 Note: layer attributes cannot be modified after the layer has been called 

43 once (except the `trainable` attribute). 

44 

45 Example: 

46 ```python 

47 # apply a unshared weight convolution 1d of length 3 to a sequence with 

48 # 10 timesteps, with 64 output filters 

49 model = Sequential() 

50 model.add(LocallyConnected1D(64, 3, input_shape=(10, 32))) 

51 # now model.output_shape == (None, 8, 64) 

52 # add a new conv1d on top 

53 model.add(LocallyConnected1D(32, 3)) 

54 # now model.output_shape == (None, 6, 32) 

55 ``` 

56 

57 Args: 

58 filters: Integer, the dimensionality of the output space (i.e. the 

59 number of output filters in the convolution). 

60 kernel_size: An integer or tuple/list of a single integer, specifying 

61 the length of the 1D convolution window. 

62 strides: An integer or tuple/list of a single integer, specifying the 

63 stride length of the convolution. 

64 padding: Currently only supports `"valid"` (case-insensitive). `"same"` 

65 may be supported in the future. `"valid"` means no padding. 

66 data_format: A string, one of `channels_last` (default) or 

67 `channels_first`. The ordering of the dimensions in the inputs. 

68 `channels_last` corresponds to inputs with shape `(batch, length, 

69 channels)` while `channels_first` corresponds to inputs with shape 

70 `(batch, channels, length)`. When unspecified, uses 

71 `image_data_format` value found in your Keras config file at 

72 `~/.keras/keras.json` (if exists) else 'channels_last'. 

73 Defaults to 'channels_last'. 

74 activation: Activation function to use. If you don't specify anything, 

75 no activation is applied (ie. "linear" activation: `a(x) = x`). 

76 use_bias: Boolean, whether the layer uses a bias vector. 

77 kernel_initializer: Initializer for the `kernel` weights matrix. 

78 bias_initializer: Initializer for the bias vector. 

79 kernel_regularizer: Regularizer function applied to the `kernel` weights 

80 matrix. 

81 bias_regularizer: Regularizer function applied to the bias vector. 

82 activity_regularizer: Regularizer function applied to the output of the 

83 layer (its "activation").. 

84 kernel_constraint: Constraint function applied to the kernel matrix. 

85 bias_constraint: Constraint function applied to the bias vector. 

86 implementation: implementation mode, either `1`, `2`, or `3`. `1` loops 

87 over input spatial locations to perform the forward pass. It is 

88 memory-efficient but performs a lot of (small) ops. `2` stores layer 

89 weights in a dense but sparsely-populated 2D matrix and implements the 

90 forward pass as a single matrix-multiply. It uses a lot of RAM but 

91 performs few (large) ops. `3` stores layer weights in a sparse tensor 

92 and implements the forward pass as a single sparse matrix-multiply. 

93 How to choose: 

94 `1`: large, dense models, 

95 `2`: small models, 

96 `3`: large, sparse models, where "large" stands for large 

97 input/output activations (i.e. many `filters`, `input_filters`, 

98 large `input_size`, `output_size`), and "sparse" stands for few 

99 connections between inputs and outputs, i.e. small ratio 

100 `filters * input_filters * kernel_size / (input_size * strides)`, 

101 where inputs to and outputs of the layer are assumed to have 

102 shapes `(input_size, input_filters)`, `(output_size, filters)` 

103 respectively. It is recommended to benchmark each in the setting 

104 of interest to pick the most efficient one (in terms of speed and 

105 memory usage). Correct choice of implementation can lead to 

106 dramatic speed improvements (e.g. 50X), potentially at the expense 

107 of RAM. Also, only `padding="valid"` is supported by 

108 `implementation=1`. 

109 Input shape: 

110 3D tensor with shape: `(batch_size, steps, input_dim)` 

111 Output shape: 

112 3D tensor with shape: `(batch_size, new_steps, filters)` `steps` value 

113 might have changed due to padding or strides. 

114 """ 

115 

116 def __init__( 

117 self, 

118 filters, 

119 kernel_size, 

120 strides=1, 

121 padding="valid", 

122 data_format=None, 

123 activation=None, 

124 use_bias=True, 

125 kernel_initializer="glorot_uniform", 

126 bias_initializer="zeros", 

127 kernel_regularizer=None, 

128 bias_regularizer=None, 

129 activity_regularizer=None, 

130 kernel_constraint=None, 

131 bias_constraint=None, 

132 implementation=1, 

133 **kwargs, 

134 ): 

135 super().__init__(**kwargs) 

136 self.filters = filters 

137 self.kernel_size = conv_utils.normalize_tuple( 

138 kernel_size, 1, "kernel_size" 

139 ) 

140 self.strides = conv_utils.normalize_tuple( 

141 strides, 1, "strides", allow_zero=True 

142 ) 

143 self.padding = conv_utils.normalize_padding(padding) 

144 if self.padding != "valid" and implementation == 1: 

145 raise ValueError( 

146 "Invalid border mode for LocallyConnected1D " 

147 '(only "valid" is supported if implementation is 1): ' + padding 

148 ) 

149 self.data_format = conv_utils.normalize_data_format(data_format) 

150 self.activation = activations.get(activation) 

151 self.use_bias = use_bias 

152 self.kernel_initializer = initializers.get(kernel_initializer) 

153 self.bias_initializer = initializers.get(bias_initializer) 

154 self.kernel_regularizer = regularizers.get(kernel_regularizer) 

155 self.bias_regularizer = regularizers.get(bias_regularizer) 

156 self.activity_regularizer = regularizers.get(activity_regularizer) 

157 self.kernel_constraint = constraints.get(kernel_constraint) 

158 self.bias_constraint = constraints.get(bias_constraint) 

159 self.implementation = implementation 

160 self.input_spec = InputSpec(ndim=3) 

161 

162 @property 

163 def _use_input_spec_as_call_signature(self): 

164 return False 

165 

166 @tf_utils.shape_type_conversion 

167 def build(self, input_shape): 

168 if self.data_format == "channels_first": 

169 input_dim, input_length = input_shape[1], input_shape[2] 

170 else: 

171 input_dim, input_length = input_shape[2], input_shape[1] 

172 

173 if input_dim is None: 

174 raise ValueError( 

175 "Axis 2 of input should be fully-defined. Found shape:", 

176 input_shape, 

177 ) 

178 self.output_length = conv_utils.conv_output_length( 

179 input_length, self.kernel_size[0], self.padding, self.strides[0] 

180 ) 

181 

182 if self.output_length <= 0: 

183 raise ValueError( 

184 "One of the dimensions in the output is <= 0 " 

185 f"due to downsampling in {self.name}. Consider " 

186 "increasing the input size. " 

187 f"Received input shape {input_shape} which would produce " 

188 "output shape with a zero or negative value in a " 

189 "dimension." 

190 ) 

191 

192 if self.implementation == 1: 

193 self.kernel_shape = ( 

194 self.output_length, 

195 self.kernel_size[0] * input_dim, 

196 self.filters, 

197 ) 

198 

199 self.kernel = self.add_weight( 

200 shape=self.kernel_shape, 

201 initializer=self.kernel_initializer, 

202 name="kernel", 

203 regularizer=self.kernel_regularizer, 

204 constraint=self.kernel_constraint, 

205 ) 

206 

207 elif self.implementation == 2: 

208 if self.data_format == "channels_first": 

209 self.kernel_shape = ( 

210 input_dim, 

211 input_length, 

212 self.filters, 

213 self.output_length, 

214 ) 

215 else: 

216 self.kernel_shape = ( 

217 input_length, 

218 input_dim, 

219 self.output_length, 

220 self.filters, 

221 ) 

222 

223 self.kernel = self.add_weight( 

224 shape=self.kernel_shape, 

225 initializer=self.kernel_initializer, 

226 name="kernel", 

227 regularizer=self.kernel_regularizer, 

228 constraint=self.kernel_constraint, 

229 ) 

230 

231 self.kernel_mask = ( 

232 locally_connected_utils.get_locallyconnected_mask( 

233 input_shape=(input_length,), 

234 kernel_shape=self.kernel_size, 

235 strides=self.strides, 

236 padding=self.padding, 

237 data_format=self.data_format, 

238 ) 

239 ) 

240 

241 elif self.implementation == 3: 

242 self.kernel_shape = ( 

243 self.output_length * self.filters, 

244 input_length * input_dim, 

245 ) 

246 

247 self.kernel_idxs = sorted( 

248 conv_utils.conv_kernel_idxs( 

249 input_shape=(input_length,), 

250 kernel_shape=self.kernel_size, 

251 strides=self.strides, 

252 padding=self.padding, 

253 filters_in=input_dim, 

254 filters_out=self.filters, 

255 data_format=self.data_format, 

256 ) 

257 ) 

258 

259 self.kernel = self.add_weight( 

260 shape=(len(self.kernel_idxs),), 

261 initializer=self.kernel_initializer, 

262 name="kernel", 

263 regularizer=self.kernel_regularizer, 

264 constraint=self.kernel_constraint, 

265 ) 

266 

267 else: 

268 raise ValueError( 

269 "Unrecognized implementation mode: %d." % self.implementation 

270 ) 

271 

272 if self.use_bias: 

273 self.bias = self.add_weight( 

274 shape=(self.output_length, self.filters), 

275 initializer=self.bias_initializer, 

276 name="bias", 

277 regularizer=self.bias_regularizer, 

278 constraint=self.bias_constraint, 

279 ) 

280 else: 

281 self.bias = None 

282 

283 if self.data_format == "channels_first": 

284 self.input_spec = InputSpec(ndim=3, axes={1: input_dim}) 

285 else: 

286 self.input_spec = InputSpec(ndim=3, axes={-1: input_dim}) 

287 self.built = True 

288 

289 @tf_utils.shape_type_conversion 

290 def compute_output_shape(self, input_shape): 

291 if self.data_format == "channels_first": 

292 input_length = input_shape[2] 

293 else: 

294 input_length = input_shape[1] 

295 

296 length = conv_utils.conv_output_length( 

297 input_length, self.kernel_size[0], self.padding, self.strides[0] 

298 ) 

299 

300 if self.data_format == "channels_first": 

301 return (input_shape[0], self.filters, length) 

302 elif self.data_format == "channels_last": 

303 return (input_shape[0], length, self.filters) 

304 

305 def call(self, inputs): 

306 if self.implementation == 1: 

307 output = backend.local_conv( 

308 inputs, 

309 self.kernel, 

310 self.kernel_size, 

311 self.strides, 

312 (self.output_length,), 

313 self.data_format, 

314 ) 

315 

316 elif self.implementation == 2: 

317 output = locally_connected_utils.local_conv_matmul( 

318 inputs, 

319 self.kernel, 

320 self.kernel_mask, 

321 self.compute_output_shape(inputs.shape), 

322 ) 

323 

324 elif self.implementation == 3: 

325 output = locally_connected_utils.local_conv_sparse_matmul( 

326 inputs, 

327 self.kernel, 

328 self.kernel_idxs, 

329 self.kernel_shape, 

330 self.compute_output_shape(inputs.shape), 

331 ) 

332 

333 else: 

334 raise ValueError( 

335 "Unrecognized implementation mode: %d." % self.implementation 

336 ) 

337 

338 if self.use_bias: 

339 output = backend.bias_add( 

340 output, self.bias, data_format=self.data_format 

341 ) 

342 

343 output = self.activation(output) 

344 return output 

345 

346 def get_config(self): 

347 config = { 

348 "filters": self.filters, 

349 "kernel_size": self.kernel_size, 

350 "strides": self.strides, 

351 "padding": self.padding, 

352 "data_format": self.data_format, 

353 "activation": activations.serialize(self.activation), 

354 "use_bias": self.use_bias, 

355 "kernel_initializer": initializers.serialize( 

356 self.kernel_initializer 

357 ), 

358 "bias_initializer": initializers.serialize(self.bias_initializer), 

359 "kernel_regularizer": regularizers.serialize( 

360 self.kernel_regularizer 

361 ), 

362 "bias_regularizer": regularizers.serialize(self.bias_regularizer), 

363 "activity_regularizer": regularizers.serialize( 

364 self.activity_regularizer 

365 ), 

366 "kernel_constraint": constraints.serialize(self.kernel_constraint), 

367 "bias_constraint": constraints.serialize(self.bias_constraint), 

368 "implementation": self.implementation, 

369 } 

370 base_config = super().get_config() 

371 return dict(list(base_config.items()) + list(config.items())) 

372