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

36 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"""Private utilities for locally-connected layers.""" 

16 

17import numpy as np 

18import tensorflow.compat.v2 as tf 

19 

20from keras.src import backend 

21from keras.src.utils import conv_utils 

22 

23 

24def get_locallyconnected_mask( 

25 input_shape, kernel_shape, strides, padding, data_format 

26): 

27 """Return a mask representing connectivity of a locally-connected operation. 

28 

29 This method returns a masking numpy array of 0s and 1s (of type 

30 `np.float32`) that, when element-wise multiplied with a fully-connected 

31 weight tensor, masks out the weights between disconnected input-output pairs 

32 and thus implements local connectivity through a sparse fully-connected 

33 weight tensor. 

34 

35 Assume an unshared convolution with given parameters is applied to an input 

36 having N spatial dimensions with `input_shape = (d_in1, ..., d_inN)` 

37 to produce an output with spatial shape `(d_out1, ..., d_outN)` (determined 

38 by layer parameters such as `strides`). 

39 

40 This method returns a mask which can be broadcast-multiplied (element-wise) 

41 with a 2*(N+1)-D weight matrix (equivalent to a fully-connected layer 

42 between (N+1)-D activations (N spatial + 1 channel dimensions for input and 

43 output) to make it perform an unshared convolution with given 

44 `kernel_shape`, `strides`, `padding` and `data_format`. 

45 

46 Args: 

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

48 input. 

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

50 receptive field. 

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

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

53 data_format: a string, `"channels_first"` or `"channels_last"`. 

54 

55 Returns: 

56 a `np.float32`-type `np.ndarray` of shape 

57 `(1, d_in1, ..., d_inN, 1, d_out1, ..., d_outN)` 

58 if `data_format == `"channels_first"`, or 

59 `(d_in1, ..., d_inN, 1, d_out1, ..., d_outN, 1)` 

60 if `data_format == "channels_last"`. 

61 

62 Raises: 

63 ValueError: if `data_format` is neither `"channels_first"` nor 

64 `"channels_last"`. 

65 """ 

66 mask = conv_utils.conv_kernel_mask( 

67 input_shape=input_shape, 

68 kernel_shape=kernel_shape, 

69 strides=strides, 

70 padding=padding, 

71 ) 

72 

73 ndims = int(mask.ndim / 2) 

74 

75 if data_format == "channels_first": 

76 mask = np.expand_dims(mask, 0) 

77 mask = np.expand_dims(mask, -ndims - 1) 

78 

79 elif data_format == "channels_last": 

80 mask = np.expand_dims(mask, ndims) 

81 mask = np.expand_dims(mask, -1) 

82 

83 else: 

84 raise ValueError("Unrecognized data_format: " + str(data_format)) 

85 

86 return mask 

87 

88 

89def local_conv_matmul(inputs, kernel, kernel_mask, output_shape): 

90 """Apply N-D convolution with un-shared weights using a single matmul call. 

91 

92 This method outputs `inputs . (kernel * kernel_mask)` 

93 (with `.` standing for matrix-multiply and `*` for element-wise multiply) 

94 and requires a precomputed `kernel_mask` to zero-out weights in `kernel` and 

95 hence perform the same operation as a convolution with un-shared 

96 (the remaining entries in `kernel`) weights. It also does the necessary 

97 reshapes to make `inputs` and `kernel` 2-D and `output` (N+2)-D. 

98 

99 Args: 

100 inputs: (N+2)-D tensor with shape `(batch_size, channels_in, d_in1, ..., 

101 d_inN)` or `(batch_size, d_in1, ..., d_inN, channels_in)`. 

102 kernel: the unshared weights for N-D convolution, 

103 an (N+2)-D tensor of shape: `(d_in1, ..., d_inN, channels_in, 

104 d_out2, ..., d_outN, channels_out)` or `(channels_in, d_in1, ..., 

105 d_inN, channels_out, d_out2, ..., d_outN)`, with the ordering of 

106 channels and spatial dimensions matching that of the input. Each 

107 entry is the weight between a particular input and output location, 

108 similarly to a fully-connected weight matrix. 

109 kernel_mask: a float 0/1 mask tensor of shape: `(d_in1, ..., d_inN, 1, 

110 d_out2, ..., d_outN, 1)` or `(1, d_in1, ..., d_inN, 1, d_out2, ..., 

111 d_outN)`, with the ordering of singleton and spatial dimensions 

112 matching that of the input. Mask represents the connectivity pattern 

113 of the layer and is precomputed elsewhere based on layer parameters: 

114 stride, padding, and the receptive field shape. 

115 output_shape: a tuple of (N+2) elements representing the output shape: 

116 `(batch_size, channels_out, d_out1, ..., d_outN)` or `(batch_size, 

117 d_out1, ..., d_outN, channels_out)`, with the ordering of channels and 

118 spatial dimensions matching that of the input. 

119 

120 Returns: 

121 Output (N+2)-D tensor with shape `output_shape`. 

122 """ 

123 inputs_flat = backend.reshape(inputs, (backend.shape(inputs)[0], -1)) 

124 

125 kernel = kernel_mask * kernel 

126 kernel = make_2d(kernel, split_dim=backend.ndim(kernel) // 2) 

127 

128 output_flat = tf.matmul(inputs_flat, kernel, b_is_sparse=True) 

129 output = backend.reshape( 

130 output_flat, 

131 [ 

132 backend.shape(output_flat)[0], 

133 ] 

134 + output_shape.as_list()[1:], 

135 ) 

136 return output 

137 

138 

139def local_conv_sparse_matmul( 

140 inputs, kernel, kernel_idxs, kernel_shape, output_shape 

141): 

142 """Apply N-D convolution with unshared weights using a single sparse matmul. 

143 

144 This method outputs `inputs . tf.sparse.SparseTensor(indices=kernel_idxs, 

145 values=kernel, dense_shape=kernel_shape)`, with `.` standing for 

146 matrix-multiply. It also reshapes `inputs` to 2-D and `output` to (N+2)-D. 

147 

148 Args: 

149 inputs: (N+2)-D tensor with shape `(batch_size, channels_in, d_in1, ..., 

150 d_inN)` or `(batch_size, d_in1, ..., d_inN, channels_in)`. 

151 kernel: a 1-D tensor with shape `(len(kernel_idxs),)` containing all the 

152 weights of the layer. 

153 kernel_idxs: a list of integer tuples representing indices in a sparse 

154 matrix performing the un-shared convolution as a matrix-multiply. 

155 kernel_shape: a tuple `(input_size, output_size)`, where `input_size = 

156 channels_in * d_in1 * ... * d_inN` and `output_size = channels_out * 

157 d_out1 * ... * d_outN`. 

158 output_shape: a tuple of (N+2) elements representing the output shape: 

159 `(batch_size, channels_out, d_out1, ..., d_outN)` or `(batch_size, 

160 d_out1, ..., d_outN, channels_out)`, with the ordering of channels and 

161 spatial dimensions matching that of the input. 

162 

163 Returns: 

164 Output (N+2)-D dense tensor with shape `output_shape`. 

165 """ 

166 inputs_flat = backend.reshape(inputs, (backend.shape(inputs)[0], -1)) 

167 output_flat = tf.sparse.sparse_dense_matmul( 

168 sp_a=tf.SparseTensor(kernel_idxs, kernel, kernel_shape), 

169 b=inputs_flat, 

170 adjoint_b=True, 

171 ) 

172 output_flat_transpose = backend.transpose(output_flat) 

173 

174 output_reshaped = backend.reshape( 

175 output_flat_transpose, 

176 [ 

177 backend.shape(output_flat_transpose)[0], 

178 ] 

179 + output_shape.as_list()[1:], 

180 ) 

181 return output_reshaped 

182 

183 

184def make_2d(tensor, split_dim): 

185 """Reshapes an N-dimensional tensor into a 2D tensor. 

186 

187 Dimensions before (excluding) and after (including) `split_dim` are grouped 

188 together. 

189 

190 Args: 

191 tensor: a tensor of shape `(d0, ..., d(N-1))`. 

192 split_dim: an integer from 1 to N-1, index of the dimension to group 

193 dimensions before (excluding) and after (including). 

194 

195 Returns: 

196 Tensor of shape 

197 `(d0 * ... * d(split_dim-1), d(split_dim) * ... * d(N-1))`. 

198 """ 

199 shape = tf.shape(tensor) 

200 in_dims = shape[:split_dim] 

201 out_dims = shape[split_dim:] 

202 

203 in_size = tf.reduce_prod(in_dims) 

204 out_size = tf.reduce_prod(out_dims) 

205 

206 return tf.reshape(tensor, (in_size, out_size)) 

207