Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/keras/utils/conv_utils.py: 10%

182 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"""Utilities used by convolution layers.""" 

16 

17import itertools 

18 

19import numpy as np 

20 

21from tensorflow.python.framework import ops 

22from tensorflow.python.framework import tensor_shape 

23from tensorflow.python.keras import backend 

24from tensorflow.python.ops import array_ops 

25 

26 

27def convert_data_format(data_format, ndim): 

28 if data_format == 'channels_last': 

29 if ndim == 3: 

30 return 'NWC' 

31 elif ndim == 4: 

32 return 'NHWC' 

33 elif ndim == 5: 

34 return 'NDHWC' 

35 else: 

36 raise ValueError('Input rank not supported:', ndim) 

37 elif data_format == 'channels_first': 

38 if ndim == 3: 

39 return 'NCW' 

40 elif ndim == 4: 

41 return 'NCHW' 

42 elif ndim == 5: 

43 return 'NCDHW' 

44 else: 

45 raise ValueError('Input rank not supported:', ndim) 

46 else: 

47 raise ValueError('Invalid data_format:', data_format) 

48 

49 

50def normalize_tuple(value, n, name): 

51 """Transforms a single integer or iterable of integers into an integer tuple. 

52 

53 Args: 

54 value: The value to validate and convert. Could an int, or any iterable of 

55 ints. 

56 n: The size of the tuple to be returned. 

57 name: The name of the argument being validated, e.g. "strides" or 

58 "kernel_size". This is only used to format error messages. 

59 

60 Returns: 

61 A tuple of n integers. 

62 

63 Raises: 

64 ValueError: If something else than an int/long or iterable thereof was 

65 passed. 

66 """ 

67 if isinstance(value, int): 

68 return (value,) * n 

69 else: 

70 try: 

71 value_tuple = tuple(value) 

72 except TypeError: 

73 raise ValueError('The `' + name + '` argument must be a tuple of ' + 

74 str(n) + ' integers. Received: ' + str(value)) 

75 if len(value_tuple) != n: 

76 raise ValueError('The `' + name + '` argument must be a tuple of ' + 

77 str(n) + ' integers. Received: ' + str(value)) 

78 for single_value in value_tuple: 

79 try: 

80 int(single_value) 

81 except (ValueError, TypeError): 

82 raise ValueError('The `' + name + '` argument must be a tuple of ' + 

83 str(n) + ' integers. Received: ' + str(value) + ' ' 

84 'including element ' + str(single_value) + ' of type' + 

85 ' ' + str(type(single_value))) 

86 return value_tuple 

87 

88 

89def conv_output_length(input_length, filter_size, padding, stride, dilation=1): 

90 """Determines output length of a convolution given input length. 

91 

92 Args: 

93 input_length: integer. 

94 filter_size: integer. 

95 padding: one of "same", "valid", "full", "causal" 

96 stride: integer. 

97 dilation: dilation rate, integer. 

98 

99 Returns: 

100 The output length (integer). 

101 """ 

102 if input_length is None: 

103 return None 

104 assert padding in {'same', 'valid', 'full', 'causal'} 

105 dilated_filter_size = filter_size + (filter_size - 1) * (dilation - 1) 

106 if padding in ['same', 'causal']: 

107 output_length = input_length 

108 elif padding == 'valid': 

109 output_length = input_length - dilated_filter_size + 1 

110 elif padding == 'full': 

111 output_length = input_length + dilated_filter_size - 1 

112 return (output_length + stride - 1) // stride 

113 

114 

115def conv_input_length(output_length, filter_size, padding, stride): 

116 """Determines input length of a convolution given output length. 

117 

118 Args: 

119 output_length: integer. 

120 filter_size: integer. 

121 padding: one of "same", "valid", "full". 

122 stride: integer. 

123 

124 Returns: 

125 The input length (integer). 

126 """ 

127 if output_length is None: 

128 return None 

129 assert padding in {'same', 'valid', 'full'} 

130 if padding == 'same': 

131 pad = filter_size // 2 

132 elif padding == 'valid': 

133 pad = 0 

134 elif padding == 'full': 

135 pad = filter_size - 1 

136 return (output_length - 1) * stride - 2 * pad + filter_size 

137 

138 

139def deconv_output_length(input_length, 

140 filter_size, 

141 padding, 

142 output_padding=None, 

143 stride=0, 

144 dilation=1): 

145 """Determines output length of a transposed convolution given input length. 

146 

147 Args: 

148 input_length: Integer. 

149 filter_size: Integer. 

150 padding: one of `"same"`, `"valid"`, `"full"`. 

151 output_padding: Integer, amount of padding along the output dimension. Can 

152 be set to `None` in which case the output length is inferred. 

153 stride: Integer. 

154 dilation: Integer. 

155 

156 Returns: 

157 The output length (integer). 

158 """ 

159 assert padding in {'same', 'valid', 'full'} 

160 if input_length is None: 

161 return None 

162 

163 # Get the dilated kernel size 

164 filter_size = filter_size + (filter_size - 1) * (dilation - 1) 

165 

166 # Infer length if output padding is None, else compute the exact length 

167 if output_padding is None: 

168 if padding == 'valid': 

169 length = input_length * stride + max(filter_size - stride, 0) 

170 elif padding == 'full': 

171 length = input_length * stride - (stride + filter_size - 2) 

172 elif padding == 'same': 

173 length = input_length * stride 

174 

175 else: 

176 if padding == 'same': 

177 pad = filter_size // 2 

178 elif padding == 'valid': 

179 pad = 0 

180 elif padding == 'full': 

181 pad = filter_size - 1 

182 

183 length = ((input_length - 1) * stride + filter_size - 2 * pad + 

184 output_padding) 

185 return length 

186 

187 

188def normalize_data_format(value): 

189 if value is None: 

190 value = backend.image_data_format() 

191 data_format = value.lower() 

192 if data_format not in {'channels_first', 'channels_last'}: 

193 raise ValueError('The `data_format` argument must be one of ' 

194 '"channels_first", "channels_last". Received: ' + 

195 str(value)) 

196 return data_format 

197 

198 

199def normalize_padding(value): 

200 if isinstance(value, (list, tuple)): 

201 return value 

202 padding = value.lower() 

203 if padding not in {'valid', 'same', 'causal'}: 

204 raise ValueError('The `padding` argument must be a list/tuple or one of ' 

205 '"valid", "same" (or "causal", only for `Conv1D). ' 

206 'Received: ' + str(padding)) 

207 return padding 

208 

209 

210def conv_kernel_mask(input_shape, kernel_shape, strides, padding): 

211 """Compute a mask representing the connectivity of a convolution operation. 

212 

213 Assume a convolution with given parameters is applied to an input having N 

214 spatial dimensions with `input_shape = (d_in1, ..., d_inN)` to produce an 

215 output with shape `(d_out1, ..., d_outN)`. This method returns a boolean array 

216 of shape `(d_in1, ..., d_inN, d_out1, ..., d_outN)` with `True` entries 

217 indicating pairs of input and output locations that are connected by a weight. 

218 

219 Example: 

220 

221 >>> input_shape = (4,) 

222 >>> kernel_shape = (2,) 

223 >>> strides = (1,) 

224 >>> padding = "valid" 

225 >>> conv_kernel_mask(input_shape, kernel_shape, strides, padding) 

226 array([[ True, False, False], 

227 [ True, True, False], 

228 [False, True, True], 

229 [False, False, True]]) 

230 

231 where rows and columns correspond to inputs and outputs respectively. 

232 

233 

234 Args: 

235 input_shape: tuple of size N: `(d_in1, ..., d_inN)`, spatial shape of the 

236 input. 

237 kernel_shape: tuple of size N, spatial shape of the convolutional kernel / 

238 receptive field. 

239 strides: tuple of size N, strides along each spatial dimension. 

240 padding: type of padding, string `"same"` or `"valid"`. 

241 `"valid"` means no padding. `"same"` results in padding evenly to 

242 the left/right or up/down of the input such that output has the same 

243 height/width dimension as the input. 

244 

245 Returns: 

246 A boolean 2N-D `np.ndarray` of shape 

247 `(d_in1, ..., d_inN, d_out1, ..., d_outN)`, where `(d_out1, ..., d_outN)` 

248 is the spatial shape of the output. `True` entries in the mask represent 

249 pairs of input-output locations that are connected by a weight. 

250 

251 Raises: 

252 ValueError: if `input_shape`, `kernel_shape` and `strides` don't have the 

253 same number of dimensions. 

254 NotImplementedError: if `padding` is not in {`"same"`, `"valid"`}. 

255 """ 

256 if padding not in {'same', 'valid'}: 

257 raise NotImplementedError('Padding type %s not supported. ' 

258 'Only "valid" and "same" ' 

259 'are implemented.' % padding) 

260 

261 in_dims = len(input_shape) 

262 if isinstance(kernel_shape, int): 

263 kernel_shape = (kernel_shape,) * in_dims 

264 if isinstance(strides, int): 

265 strides = (strides,) * in_dims 

266 

267 kernel_dims = len(kernel_shape) 

268 stride_dims = len(strides) 

269 if kernel_dims != in_dims or stride_dims != in_dims: 

270 raise ValueError('Number of strides, input and kernel dimensions must all ' 

271 'match. Received: %d, %d, %d.' % 

272 (stride_dims, in_dims, kernel_dims)) 

273 

274 output_shape = conv_output_shape(input_shape, kernel_shape, strides, padding) 

275 

276 mask_shape = input_shape + output_shape 

277 mask = np.zeros(mask_shape, np.bool_) 

278 

279 output_axes_ticks = [range(dim) for dim in output_shape] 

280 for output_position in itertools.product(*output_axes_ticks): 

281 input_axes_ticks = conv_connected_inputs(input_shape, kernel_shape, 

282 output_position, strides, padding) 

283 for input_position in itertools.product(*input_axes_ticks): 

284 mask[input_position + output_position] = True 

285 

286 return mask 

287 

288 

289def conv_kernel_idxs(input_shape, kernel_shape, strides, padding, filters_in, 

290 filters_out, data_format): 

291 """Yields output-input tuples of indices in a CNN layer. 

292 

293 The generator iterates over all `(output_idx, input_idx)` tuples, where 

294 `output_idx` is an integer index in a flattened tensor representing a single 

295 output image of a convolutional layer that is connected (via the layer 

296 weights) to the respective single input image at `input_idx` 

297 

298 Example: 

299 

300 >>> input_shape = (2, 2) 

301 >>> kernel_shape = (2, 1) 

302 >>> strides = (1, 1) 

303 >>> padding = "valid" 

304 >>> filters_in = 1 

305 >>> filters_out = 1 

306 >>> data_format = "channels_last" 

307 >>> list(conv_kernel_idxs(input_shape, kernel_shape, strides, padding, 

308 ... filters_in, filters_out, data_format)) 

309 [(0, 0), (0, 2), (1, 1), (1, 3)] 

310 

311 Args: 

312 input_shape: tuple of size N: `(d_in1, ..., d_inN)`, spatial shape of the 

313 input. 

314 kernel_shape: tuple of size N, spatial shape of the convolutional kernel / 

315 receptive field. 

316 strides: tuple of size N, strides along each spatial dimension. 

317 padding: type of padding, string `"same"` or `"valid"`. 

318 `"valid"` means no padding. `"same"` results in padding evenly to 

319 the left/right or up/down of the input such that output has the same 

320 height/width dimension as the input. 

321 filters_in: `int`, number if filters in the input to the layer. 

322 filters_out: `int', number if filters in the output of the layer. 

323 data_format: string, "channels_first" or "channels_last". 

324 

325 Yields: 

326 The next tuple `(output_idx, input_idx)`, where 

327 `output_idx` is an integer index in a flattened tensor representing a single 

328 output image of a convolutional layer that is connected (via the layer 

329 weights) to the respective single input image at `input_idx`. 

330 

331 Raises: 

332 ValueError: if `data_format` is neither 

333 `"channels_last"` nor `"channels_first"`, or if number of strides, input, 

334 and kernel number of dimensions do not match. 

335 

336 NotImplementedError: if `padding` is neither `"same"` nor `"valid"`. 

337 """ 

338 if padding not in ('same', 'valid'): 

339 raise NotImplementedError('Padding type %s not supported. ' 

340 'Only "valid" and "same" ' 

341 'are implemented.' % padding) 

342 

343 in_dims = len(input_shape) 

344 if isinstance(kernel_shape, int): 

345 kernel_shape = (kernel_shape,) * in_dims 

346 if isinstance(strides, int): 

347 strides = (strides,) * in_dims 

348 

349 kernel_dims = len(kernel_shape) 

350 stride_dims = len(strides) 

351 if kernel_dims != in_dims or stride_dims != in_dims: 

352 raise ValueError('Number of strides, input and kernel dimensions must all ' 

353 'match. Received: %d, %d, %d.' % 

354 (stride_dims, in_dims, kernel_dims)) 

355 

356 output_shape = conv_output_shape(input_shape, kernel_shape, strides, padding) 

357 output_axes_ticks = [range(dim) for dim in output_shape] 

358 

359 if data_format == 'channels_first': 

360 concat_idxs = lambda spatial_idx, filter_idx: (filter_idx,) + spatial_idx 

361 elif data_format == 'channels_last': 

362 concat_idxs = lambda spatial_idx, filter_idx: spatial_idx + (filter_idx,) 

363 else: 

364 raise ValueError('Data format %s not recognized.' 

365 '`data_format` must be "channels_first" or ' 

366 '"channels_last".' % data_format) 

367 

368 for output_position in itertools.product(*output_axes_ticks): 

369 input_axes_ticks = conv_connected_inputs(input_shape, kernel_shape, 

370 output_position, strides, padding) 

371 for input_position in itertools.product(*input_axes_ticks): 

372 for f_in in range(filters_in): 

373 for f_out in range(filters_out): 

374 out_idx = np.ravel_multi_index( 

375 multi_index=concat_idxs(output_position, f_out), 

376 dims=concat_idxs(output_shape, filters_out)) 

377 in_idx = np.ravel_multi_index( 

378 multi_index=concat_idxs(input_position, f_in), 

379 dims=concat_idxs(input_shape, filters_in)) 

380 yield (out_idx, in_idx) 

381 

382 

383def conv_connected_inputs(input_shape, kernel_shape, output_position, strides, 

384 padding): 

385 """Return locations of the input connected to an output position. 

386 

387 Assume a convolution with given parameters is applied to an input having N 

388 spatial dimensions with `input_shape = (d_in1, ..., d_inN)`. This method 

389 returns N ranges specifying the input region that was convolved with the 

390 kernel to produce the output at position 

391 `output_position = (p_out1, ..., p_outN)`. 

392 

393 Example: 

394 

395 >>> input_shape = (4, 4) 

396 >>> kernel_shape = (2, 1) 

397 >>> output_position = (1, 1) 

398 >>> strides = (1, 1) 

399 >>> padding = "valid" 

400 >>> conv_connected_inputs(input_shape, kernel_shape, output_position, 

401 ... strides, padding) 

402 [range(1, 3), range(1, 2)] 

403 

404 Args: 

405 input_shape: tuple of size N: `(d_in1, ..., d_inN)`, spatial shape of the 

406 input. 

407 kernel_shape: tuple of size N, spatial shape of the convolutional kernel / 

408 receptive field. 

409 output_position: tuple of size N: `(p_out1, ..., p_outN)`, a single position 

410 in the output of the convolution. 

411 strides: tuple of size N, strides along each spatial dimension. 

412 padding: type of padding, string `"same"` or `"valid"`. 

413 `"valid"` means no padding. `"same"` results in padding evenly to 

414 the left/right or up/down of the input such that output has the same 

415 height/width dimension as the input. 

416 

417 Returns: 

418 N ranges `[[p_in_left1, ..., p_in_right1], ..., 

419 [p_in_leftN, ..., p_in_rightN]]` specifying the region in the 

420 input connected to output_position. 

421 """ 

422 ranges = [] 

423 

424 ndims = len(input_shape) 

425 for d in range(ndims): 

426 left_shift = int(kernel_shape[d] / 2) 

427 right_shift = kernel_shape[d] - left_shift 

428 

429 center = output_position[d] * strides[d] 

430 

431 if padding == 'valid': 

432 center += left_shift 

433 

434 start = max(0, center - left_shift) 

435 end = min(input_shape[d], center + right_shift) 

436 

437 ranges.append(range(start, end)) 

438 

439 return ranges 

440 

441 

442def conv_output_shape(input_shape, kernel_shape, strides, padding): 

443 """Return the output shape of an N-D convolution. 

444 

445 Forces dimensions where input is empty (size 0) to remain empty. 

446 

447 Args: 

448 input_shape: tuple of size N: `(d_in1, ..., d_inN)`, spatial shape of the 

449 input. 

450 kernel_shape: tuple of size N, spatial shape of the convolutional kernel / 

451 receptive field. 

452 strides: tuple of size N, strides along each spatial dimension. 

453 padding: type of padding, string `"same"` or `"valid"`. 

454 `"valid"` means no padding. `"same"` results in padding evenly to 

455 the left/right or up/down of the input such that output has the same 

456 height/width dimension as the input. 

457 

458 Returns: 

459 tuple of size N: `(d_out1, ..., d_outN)`, spatial shape of the output. 

460 """ 

461 dims = range(len(kernel_shape)) 

462 output_shape = [ 

463 conv_output_length(input_shape[d], kernel_shape[d], padding, strides[d]) 

464 for d in dims 

465 ] 

466 output_shape = tuple( 

467 [0 if input_shape[d] == 0 else output_shape[d] for d in dims]) 

468 return output_shape 

469 

470 

471def squeeze_batch_dims(inp, op, inner_rank): 

472 """Returns `unsqueeze_batch(op(squeeze_batch(inp)))`. 

473 

474 Where `squeeze_batch` reshapes `inp` to shape 

475 `[prod(inp.shape[:-inner_rank])] + inp.shape[-inner_rank:]` 

476 and `unsqueeze_batch` does the reverse reshape but on the output. 

477 

478 Args: 

479 inp: A tensor with dims `batch_shape + inner_shape` where `inner_shape` 

480 is length `inner_rank`. 

481 op: A callable that takes a single input tensor and returns a single. 

482 output tensor. 

483 inner_rank: A python integer. 

484 

485 Returns: 

486 `unsqueeze_batch_op(squeeze_batch(inp))`. 

487 """ 

488 with ops.name_scope_v2('squeeze_batch_dims'): 

489 shape = inp.shape 

490 

491 inner_shape = shape[-inner_rank:] 

492 if not inner_shape.is_fully_defined(): 

493 inner_shape = array_ops.shape(inp)[-inner_rank:] 

494 

495 batch_shape = shape[:-inner_rank] 

496 if not batch_shape.is_fully_defined(): 

497 batch_shape = array_ops.shape(inp)[:-inner_rank] 

498 

499 if isinstance(inner_shape, tensor_shape.TensorShape): 

500 inp_reshaped = array_ops.reshape(inp, [-1] + inner_shape.as_list()) 

501 else: 

502 inp_reshaped = array_ops.reshape( 

503 inp, array_ops.concat(([-1], inner_shape), axis=-1)) 

504 

505 out_reshaped = op(inp_reshaped) 

506 

507 out_inner_shape = out_reshaped.shape[-inner_rank:] 

508 if not out_inner_shape.is_fully_defined(): 

509 out_inner_shape = array_ops.shape(out_reshaped)[-inner_rank:] 

510 

511 out = array_ops.reshape( 

512 out_reshaped, array_ops.concat((batch_shape, out_inner_shape), axis=-1)) 

513 

514 out.set_shape(inp.shape[:-inner_rank] + out.shape[-inner_rank:]) 

515 return out