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
« 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."""
17import numpy as np
18import tensorflow.compat.v2 as tf
20from keras.src import backend
21from keras.src.utils import conv_utils
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.
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.
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`).
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`.
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"`.
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"`.
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 )
73 ndims = int(mask.ndim / 2)
75 if data_format == "channels_first":
76 mask = np.expand_dims(mask, 0)
77 mask = np.expand_dims(mask, -ndims - 1)
79 elif data_format == "channels_last":
80 mask = np.expand_dims(mask, ndims)
81 mask = np.expand_dims(mask, -1)
83 else:
84 raise ValueError("Unrecognized data_format: " + str(data_format))
86 return mask
89def local_conv_matmul(inputs, kernel, kernel_mask, output_shape):
90 """Apply N-D convolution with un-shared weights using a single matmul call.
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.
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.
120 Returns:
121 Output (N+2)-D tensor with shape `output_shape`.
122 """
123 inputs_flat = backend.reshape(inputs, (backend.shape(inputs)[0], -1))
125 kernel = kernel_mask * kernel
126 kernel = make_2d(kernel, split_dim=backend.ndim(kernel) // 2)
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
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.
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.
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.
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)
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
184def make_2d(tensor, split_dim):
185 """Reshapes an N-dimensional tensor into a 2D tensor.
187 Dimensions before (excluding) and after (including) `split_dim` are grouped
188 together.
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).
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:]
203 in_size = tf.reduce_prod(in_dims)
204 out_size = tf.reduce_prod(out_dims)
206 return tf.reshape(tensor, (in_size, out_size))