Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow_addons/layers/polynomial.py: 23%
52 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 2020 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"""Implements Polynomial Crossing Layer."""
17import tensorflow as tf
18from typeguard import typechecked
20from tensorflow_addons.utils import types
23@tf.keras.utils.register_keras_serializable(package="Addons")
24class PolynomialCrossing(tf.keras.layers.Layer):
25 """Layer for Deep & Cross Network to learn explicit feature interactions.
27 A layer that applies feature crossing in learning certain explicit
28 bounded-degree feature interactions more efficiently. The `call` method
29 accepts `inputs` as a tuple of size 2 tensors. The first input `x0` should be
30 the input to the first `PolynomialCrossing` layer in the stack, or the input
31 to the network (usually after the embedding layer), the second input `xi`
32 is the output of the previous `PolynomialCrossing` layer in the stack, i.e.,
33 the i-th `PolynomialCrossing` layer.
35 The output is `x[i+1] = x0 .* (W * x[i] + diag_scale * x[i]) + bias + x[i]`, where .* designates elementwise
36 multiplication, W could be a full rank matrix, or a low rank matrix `U*V` to reduce the computational cost,
37 and diag_scale increases the diagonal of W to improve training stability (especially for the low rank case).
39 See [Deep & Cross Network for Ad Click Predictions](https://arxiv.org/pdf/1708.05123.pdf).
41 Example:
43 >>> input = np.random.randint(10, size=(10, 5))
44 >>> x0 = tf.keras.layers.Embedding(input_dim=10, output_dim=3)(input)
45 >>> x1 = PolynomialCrossing(projection_dim=None)((x0, x0))
46 >>> x2 = PolynomialCrossing(projection_dim=None)((x0, x1))
47 >>> logits = tf.keras.layers.Dense(units=10)(x2)
48 >>> model = tf.keras.Model(logits)
50 Args:
51 projection_dim: project dimension to reduce the computational cost.
52 Default is `None` such that a full (`input_dim` by `input_dim`)
53 matrix W is used. If enabled, a low-rank matrix W = U*V will be used,
54 where U is of size `input_dim` by `projection_dim` and V is of size
55 `projection_dim` by `input_dim`. `projection_dim` need to be smaller
56 than `input_dim`/2 to improve the model efficiency.
57 diag_scale: a non-negative float used to increase the diagonal of the
58 kernel W by `diag_scale`.
59 use_bias: whether to calculate the bias/intercept for this layer. If set to
60 False, no bias/intercept will be used in calculations, e.g., the data is
61 already centered.
62 kernel_initializer: Initializer instance to use on the kernel matrix.
63 bias_initializer: Initializer instance to use on the bias vector.
64 kernel_regularizer: Regularizer instance to use on the kernel matrix.
65 bias_regularizer: Regularizer instance to use on bias vector.
67 Input shape:
68 A tuple of 2 `(batch_size, input_dim)` dimensional inputs.
70 Output shape:
71 A single `(batch_size, input_dim)` dimensional output.
72 """
74 @typechecked
75 def __init__(
76 self,
77 projection_dim: int = None,
78 diag_scale: float = 0.0,
79 use_bias: bool = True,
80 kernel_initializer: types.Initializer = "truncated_normal",
81 bias_initializer: types.Initializer = "zeros",
82 kernel_regularizer: types.Regularizer = None,
83 bias_regularizer: types.Regularizer = None,
84 **kwargs,
85 ):
86 super(PolynomialCrossing, self).__init__(**kwargs)
88 self.projection_dim = projection_dim
89 self.diag_scale = diag_scale
90 self.use_bias = use_bias
91 self.kernel_initializer = tf.keras.initializers.get(kernel_initializer)
92 self.bias_initializer = tf.keras.initializers.get(bias_initializer)
93 self.kernel_regularizer = tf.keras.regularizers.get(kernel_regularizer)
94 self.bias_regularizer = tf.keras.regularizers.get(bias_regularizer)
96 self.supports_masking = True
98 def build(self, input_shape):
99 if not isinstance(input_shape, (tuple, list)) or len(input_shape) != 2:
100 raise ValueError(
101 "Input shapes must be a tuple or list of size 2, "
102 "got {}".format(input_shape)
103 )
104 last_dim = input_shape[-1][-1]
105 if self.projection_dim is None:
106 self.kernel = self.add_weight(
107 "kernel",
108 shape=[last_dim, last_dim],
109 initializer=self.kernel_initializer,
110 regularizer=self.kernel_regularizer,
111 dtype=self.dtype,
112 trainable=True,
113 )
114 else:
115 if self.projection_dim < 0 or self.projection_dim > last_dim / 2:
116 raise ValueError(
117 "`projection_dim` should be smaller than last_dim / 2 to improve"
118 "the model efficiency, and should be positive. Got "
119 "`projection_dim` {}, and last dimension of input {}".format(
120 self.projection_dim, last_dim
121 )
122 )
123 self.kernel_u = self.add_weight(
124 "kernel_u",
125 shape=[last_dim, self.projection_dim],
126 initializer=self.kernel_initializer,
127 regularizer=self.kernel_regularizer,
128 dtype=self.dtype,
129 trainable=True,
130 )
131 self.kernel_v = self.add_weight(
132 "kernel_v",
133 shape=[self.projection_dim, last_dim],
134 initializer=self.kernel_initializer,
135 regularizer=self.kernel_regularizer,
136 dtype=self.dtype,
137 trainable=True,
138 )
139 if self.use_bias:
140 self.bias = self.add_weight(
141 "bias",
142 shape=[last_dim],
143 initializer=self.bias_initializer,
144 regularizer=self.bias_regularizer,
145 dtype=self.dtype,
146 trainable=True,
147 )
148 self.built = True
150 def call(self, inputs):
151 if not isinstance(inputs, (tuple, list)) or len(inputs) != 2:
152 raise ValueError(
153 "Inputs to the layer must be a tuple or list of size 2, "
154 "got {}".format(inputs)
155 )
156 x0, x = inputs
157 if self.projection_dim is None:
158 prod_output = tf.matmul(x, self.kernel)
159 else:
160 prod_output = tf.matmul(x, self.kernel_u)
161 prod_output = tf.matmul(prod_output, self.kernel_v)
162 if self.diag_scale:
163 prod_output = tf.add(prod_output, self.diag_scale * x)
164 outputs = x0 * prod_output + x
165 if self.use_bias:
166 outputs = tf.add(outputs, self.bias)
167 return outputs
169 def get_config(self):
170 config = {
171 "projection_dim": self.projection_dim,
172 "diag_scale": self.diag_scale,
173 "use_bias": self.use_bias,
174 "kernel_initializer": tf.keras.initializers.serialize(
175 self.kernel_initializer
176 ),
177 "bias_initializer": tf.keras.initializers.serialize(self.bias_initializer),
178 "kernel_regularizer": tf.keras.regularizers.serialize(
179 self.kernel_regularizer
180 ),
181 "bias_regularizer": tf.keras.regularizers.serialize(self.bias_regularizer),
182 }
183 base_config = super(PolynomialCrossing, self).get_config()
184 return dict(list(base_config.items()) + list(config.items()))
186 def compute_output_shape(self, input_shape):
187 if not isinstance(input_shape, (tuple, list)):
188 raise ValueError(
189 "A `PolynomialCrossing` layer should be called " "on a list of inputs."
190 )
191 return input_shape[0]