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

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.""" 

16 

17import tensorflow as tf 

18from typeguard import typechecked 

19 

20from tensorflow_addons.utils import types 

21 

22 

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. 

26 

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. 

34 

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). 

38 

39 See [Deep & Cross Network for Ad Click Predictions](https://arxiv.org/pdf/1708.05123.pdf). 

40 

41 Example: 

42 

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) 

49 

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. 

66 

67 Input shape: 

68 A tuple of 2 `(batch_size, input_dim)` dimensional inputs. 

69 

70 Output shape: 

71 A single `(batch_size, input_dim)` dimensional output. 

72 """ 

73 

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) 

87 

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) 

95 

96 self.supports_masking = True 

97 

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 

149 

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 

168 

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())) 

185 

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]