def neural_graph_fingerprints(radius, fingerprint_len, feature_size): input_graph = layers.Input(shape=(None, None), name="input_graph") input_layer_rep = layers.Input(shape=(None, feature_size), name="input_layer_rep") graph = layers.Lambda(lambda x: k.temporal_padding(x, (0, 80)))( input_graph) layer_rep = layers.Lambda(lambda x: k.temporal_padding(x, (0, 80)))( input_layer_rep) all_outputs = [] for i in range(1, radius): layer_rep, layer_fp = fingerprint_model_layer(graph, layer_rep, fingerprint_len, feature_size) layer_fp = layers.Lambda(lambda x: k.sum(x, axis=1))(layer_fp) all_outputs.append(layer_fp) #final_fp = layers.merge(all_outputs, mode='sum', name="final_merge") final_fp = layers.Lambda(lambda x: k.sum(x, axis=1))(all_outputs) neural_fp = models.Model(inputs=[input_graph, input_layer_rep], outputs=final_fp) neural_fp.compile(optimizer=optimizers.Adam(), loss='categorical_crossentropy') return neural_fp
def call(self, inputs): if isinstance(inputs, list): x, x_mask = inputs else: x, x_mask = inputs, None seq_dim = K.int_shape(x)[-1] # 补足长度,保证可以reshape seq_len = K.shape(x)[1] pad_len = self.rate - seq_len % self.rate x = K.temporal_padding(x, (0, pad_len)) if x_mask is not None: x_mask = K.temporal_padding(x_mask, (0, pad_len)) new_seq_len = K.shape(x)[1] # 变换shape x = K.reshape(x, (-1, new_seq_len // self.rate, self.rate, seq_dim)) x = K.permute_dimensions(x, (0, 2, 1, 3)) x = K.reshape(x, (-1, new_seq_len // self.rate, seq_dim)) if x_mask is not None: x_mask = K.reshape(x_mask, (-1, new_seq_len // self.rate, self.rate, 1)) x_mask = K.permute_dimensions(x_mask, (0, 2, 1, 3)) x_mask = K.reshape(x_mask, (-1, new_seq_len // self.rate, 1)) # 做attention if x_mask is not None: x = self.reuse(self.attention, [x, x, x, x_mask, x_mask]) else: x = self.reuse(self.attention, [x, x, x]) # 恢复shape x = K.reshape(x, (-1, self.rate, new_seq_len // self.rate, self.out_dim)) x = K.permute_dimensions(x, (0, 2, 1, 3)) x = K.reshape(x, (-1, new_seq_len, self.out_dim)) x = x[:, :-pad_len] return x
def loss(y_true, y_pred): sent_rels = K.expand_dims(_sent_rels, axis=-1) # (*, doc_maxsentlen, 1) virtual_pred1 = K.temporal_padding(y_pred, [1, 0]) # (*, doc_maxsentlen+1, 1) virtual_pred2 = K.temporal_padding(y_pred, [0, 1]) # (*, doc_maxsentlen+1, 1) virtual_pred_diff = virtual_pred1 - virtual_pred2 # (*, doc_maxsentlen+1, 1) virtual_pred_diff = virtual_pred_diff[:, :-1, :] # (*, doc_maxsentlen, 1) virtual_pred_diff = K.squeeze(virtual_pred_diff, axis=-1) # (*, doc_maxsentlen) virtual_pred_diff = K.square(virtual_pred_diff) # (*, doc_maxsentlen) sent_rels = K.squeeze(sent_rels, axis=-1) # (*, doc_maxsentlen) loss_sum = K.batch_dot(K.square(virtual_pred_diff), sent_rels, axes=(1, 1)) # (*, 1) loss_value = K.mean(K.squeeze(loss_sum, axis=-1)) return loss_value
def call(self, inputs): # For each caption, remove the last word vector and append a zero-vector to the beginning. _inputs = K.temporal_padding(inputs[:, 0:-1, :], padding=(1, 0)) # _inputs[:, 0, idxBOS] += 1. scatter = K.tf.scatter_nd([[0, self.idxBOS]], K.tf.constant([1.]), K.tf.constant(_inputs.shape.as_list()[1:])) return _inputs + scatter
def call(self, x, mask=None): #i_ shape = [none, 600, 300) t_ shape = [4,300] x_, t_ = x t_ = K.transpose(t_) g = K.dot(x_, t_) normal_g = K.dot(K.l2_normalize(x_, axis=-1), K.l2_normalize(t_)) g /= normal_g print(g.shape) #g = K.exp(g) #g /= K.cast(K.sum(g, axis=-1, keepdims=True)+K.epsilon(), K.floatx()) g_padding = K.temporal_padding(g, padding=(self.r, self.r)) print(g_padding.shape) g_padding = tf.transpose(g_padding, perm=[0, 2, 1]) gw = [] g_array = tf.unstack(g_padding, axis=-1) for i in range(self.r, g_padding.shape[-1] - self.r): g_pad = K.stack(g_array[i - self.r:i + self.r], axis=-1) gw_e = K.squeeze(K.dot(g_pad, K.expand_dims(self.W)), axis=-1) gw.append(K.max(gw_e, axis=-1)) gw = K.stack(gw, axis=-1) print(gw.shape) ul = K.relu(gw + self.b) bate = K.exp(ul) bate /= K.sum(bate, axis=-1, keepdims=True) + K.epsilon() return x_ * K.expand_dims(bate)
def extract_seq_patches(x, kernel_size, rate): """x.shape = [None, seq_len, seq_dim] 滑动地把每个窗口的x取出来,为做局部attention作准备。 Args: x:需要分窗口的变量 kernel_size:窗口长度,会padding kernel_size-1个空白,(类似CNN中的same padding) Exampe: kvar = K.variable(value=np.array([[[1,1],[3,4]]])) # 最里层为一个句子 K.eval((extract_seq_patches(kvar, 3, 1))) Out[135]: array([[[[0., 0.], [1., 1.], [3., 4.]], [[1., 1.], [3., 4.], [0., 0.]]]], dtype=float32) """ # 获取句子维数和句子长度,用于最后的reshape seq_dim = K.int_shape(x)[-1] seq_len = K.shape(x)[1] # 由kernel_size决定temporal_padding的多少,p_left==p_left # 左右两侧都padding kernel_size-1个空白, k_size = kernel_size + (rate - 1) * (kernel_size - 1) p_right = (k_size - 1) // 2 p_left = k_size - 1 - p_right # 进行padding x = K.temporal_padding(x, (p_left, p_right)) # 按句子的单词进行划分,只选取其中一部分单词 xs = [x[:, i:i + seq_len] for i in range(0, k_size, rate)] # 按照axis = 2连接 x = K.concatenate(xs, 2) # 每个句子中分为若干个窗口,所以多了一个维度,输出为4维数,np.array,第一个维数为batch_size return K.reshape(x, (-1, seq_len, kernel_size, seq_dim))
def call(self, x): new_vec_list = [] shape = K.get_value(self.floor(K.int_shape(x)[1])) for l in self.ovl: temp_vec_list = [] for i in range(l): self.window_size = K.get_value(self.ceil( K.int_shape(x)[1] / l)) self.stride = K.get_value(self.floor(K.int_shape(x)[1] / l)) if (self.stride * i + self.window_size) <= shape: temp = x[:, self.stride * i:self.stride * i + self.window_size] else: temp = x[:, self.stride * i:, :] temp = K.temporal_padding( temp, (0, self.window_size - K.get_value(self.floor(K.int_shape(temp)[1])))) temp = K.max(temp, axis=1, keepdims=True) temp_vec_list.append(temp) if l == 1: new_vec_list = temp_vec_list else: new_vec_list.append( keras.layers.concatenate(temp_vec_list, axis=1)) if len(self.ovl) == 1: return new_vec_list ret = keras.layers.concatenate(new_vec_list, axis=1) # print(self.compute_output_shape(x.shape)) return ret
def get_keras_pad(cls, x, pads, dim, data_format=None): """ implement pad in conv or pool operator :param x: input tensor :param pads: pads attribute in conv or pool operator :param dim: the pad dim :param data_format: data format of x :return: the result tensor of input tensor implementing padding operation """ if sum(pads) == 0: return x if len(pads) == dim * 2: pads = list( np.transpose( np.array(pads).reshape([2, dim]).astype(np.int32))) pads = tuple(tuple(i) for i in pads) elif len(pads) == dim: pads = tuple((i, i) for i in pads) if dim == 1: return Lambda(lambda _x: K.temporal_padding(_x, pads))(x) elif dim == 2: return Lambda( lambda _x: K.spatial_2d_padding(_x, pads, data_format))(x) elif dim == 3: return Lambda( lambda _x: K.spatial_3d_padding(_x, pads, data_format))(x) else: raise NotImplementedError( "padding with dim {} is not implemented.".format(dim))
def preprocess_input(self, inputs, training=None): if self.window_size > 1: inputs = K.temporal_padding(inputs, (self.window_size - 1, 0)) inputs = K.expand_dims(inputs, 2) # add a dummy dimension output = K.conv2d( inputs, self.kernel, strides=self.strides, padding="valid", data_format="channels_last", ) output = K.squeeze(output, 2) # remove the dummy dimension if self.use_bias: output = K.bias_add(output, self.bias, data_format="channels_last") if self.dropout is not None and 0.0 < self.dropout < 1.0: z = output[:, :, :self.units] f = output[:, :, self.units:2 * self.units] o = output[:, :, 2 * self.units:] f = K.in_train_phase(1 - _dropout(1 - f, self.dropout), f, training=training) return K.concatenate([z, f, o], -1) else: return output
def concat(x, x_concat): """ """ diff = K.shape(x_concat)[1] - K.shape(x)[1] x = K.temporal_padding(x, padding=(diff // 2, diff - diff // 2)) return x
def call(self, inputs, mask=None): if self.input_type == 'sentence': # inputs are untokenized sentences embeddings = self.elmo(inputs=K.squeeze(K.cast(inputs, tf.string), axis=1), signature="default", as_dict=True)[self.output_mode] elmo_max_length = K.int_shape(embeddings)[1] if self.max_length > elmo_max_length: embeddings = K.temporal_padding(embeddings, padding=(0, self.max_length - elmo_max_length)) elif elmo_max_length > self.max_length: # embeddings = tf.slice(embeddings, begin=[0, 0, 0], size=[-1, self.max_length, -1]) embeddings = embeddings[:, : self.max_length, :] # more pythonic else: # inputs are tokenized word id sequence # convert inputs to word sequence inputs = tf.cast(inputs, dtype=tf.int64) sequence_lengths = tf.cast(tf.count_nonzero(inputs, axis=1), dtype=tf.int32) embeddings = self.elmo(inputs={ 'tokens': self.lookup_table.lookup(inputs), 'sequence_len': sequence_lengths }, signature="tokens", as_dict=True)[self.output_mode] if self.output_mode != 'defalut': output_mask = K.expand_dims(K.cast(K.not_equal(inputs, 0), tf.float32), axis=-1) embeddings *= output_mask return embeddings
def call(self, x, mask=None): if self.causal: x = K.temporal_padding(x, padding=(self.atrous_rate * (self.filter_length - 1), 0)) # return super(CausalAtrousConvolution1D, self).call(x, mask) return super(CausalDilatedConv1D, self).call(x)
def _add_context(x, context_frames, pad_frames): x = K.temporal_padding(x, (pad_frames, pad_frames)) to_concat = [ x[:, offset:-(context_frames - offset - 1), :] for offset in range(context_frames - 1) ] to_concat.append(x[:, (context_frames - 1):, :]) x = K.concatenate(to_concat, axis=2) return x
def same_padding_second_dim(x, padding_length, name): if (padding_length % 2 == 0): l, r = padding_length / 2, padding_length / 2 - 1 else: l, r = padding_length / 2, padding_length / 2 + 1 l, r = int(l), int(r) # x = Lambda(lambda x: K.temporal_padding(x, padding=(l,r)), name='same_padding_second_dim' + str(name)) x = Lambda(lambda x: K.temporal_padding(x, padding=(l, r))) # x = K.temporal_padding(x, padding=(l,r)) return x
def call(self, x): new_vec_list = [] for i in range(self.splits): if (self.stride * i + self.window_size) <= int(self.in_shape[1]): temp = x[:, self.stride * i:self.stride * i + self.window_size] else: temp = x[:, self.stride * i:int(self.in_shape[1])] temp = K.temporal_padding( temp, (0, self.window_size - K.int_shape(temp)[1])) new_vec_list.append(temp) return new_vec_list
def same_padding_second_dim(x, padding_length, name): if(padding_length % 2 == 0 ): l , r = padding_length/2 , padding_length/2 - 1 else: l , r = padding_length/2 , padding_length/2 + 1 l , r = int(l), int(r) if(l < 0 ):l = 0 if(r < 0): r = 0 x = Lambda(lambda x: K.temporal_padding(x, padding=(l, r))) # x = K.temporal_padding(x, padding=(l,r)) return x
def extract_seq_patches(x, kernel_size, rate): """x.shape = [None, seq_len, seq_dim] 滑动地把每个窗口的x取出来,为做局部attention作准备。 """ seq_dim = K.int_shape(x)[-1] seq_len = K.shape(x)[1] k_size = kernel_size + (rate - 1) * (kernel_size - 1) p_right = (k_size - 1) // 2 p_left = k_size - 1 - p_right x = K.temporal_padding(x, (p_left, p_right)) xs = [x[:, i:i + seq_len] for i in range(0, k_size, rate)] x = K.concatenate(xs, 2) return K.reshape(x, (-1, seq_len, kernel_size, seq_dim))
def special_cube_conv(in_tensor, filter_size): """ Takes in a None (samples) x 54 x ? (filters) tensor. It embedds it into 5 x 5 grid, and does a 3D convolution using only the nodes in the orginal embedding. To speed things up, it actually does the folowing: - pads the end with a zero (in the last dimension): None (samples) x 55 x ? (filters) (neighbors) - align neighbors to get an output of dim: None (samples) x 54 x 27 x ? (filters) (neighbors) - 2d convolution with filter (1, 27) and no padding to get an output of dim: None (samples) x 54 x filter_size - reshape to remove last dimension: None (samples) x filter_size x 54 """ assert in_tensor.shape[1] == 54, in_tensor.shape # pad (output dim: None x 55 x ?) padded = Lambda(lambda x: K.temporal_padding(x, (0, 1)))( in_tensor) # just pad end assert padded.shape[1] == 55, padded.shape # align neighbors (output dim: None x 54 x 27 x ?) #aligned = K.gather(padded, neighbors) #aligned = padded[ neighbors[np.newaxis].astype(np.int32), :] aligned = Lambda(lambda x: tf.gather(x, neighbors, axis=1))(padded) assert aligned.shape[1:3] == (54, 27), aligned.shape # 2D convolution in one axis (output dim: None x 54 x 1 x filter_size) conv = Conv2D(filter_size, kernel_size=(1, 27), strides=(1, 1), padding='valid', data_format="channels_last", kernel_regularizer=l2(0.001), bias_regularizer=l2(0.001))(aligned) assert conv.shape[1:3] == (54, 1), conv.shape # reshape (output dim: None x 54 x filter_size) out_tensor = Lambda(lambda x: K.squeeze(x, axis=2))(conv) assert out_tensor.shape[1] == 54, out_tensor.shape return out_tensor
def special_cube_conv(in_tensor, filter_size): """ Takes in a None (samples) x 54 x ? (filters) tensor. It embedds it into 5 x 5 grid, and does a 3D convolution using only the nodes in the orginal embedding. To speed things up, it actually does the folowing: - pads the end with a zero (in the last dimension): None (samples) x 55 x ? (filters) (neighbors) - align neighbors to get an output of dim: None (samples) x 54 x 27 x ? (filters) (neighbors) - 2d convolution with filter (1, 27) and no padding to get an output of dim: None (samples) x 54 x filter_size - reshape to remove last dimension: None (samples) x filter_size x 54 """ assert in_tensor.shape[1] == 54, in_tensor.shape # pad (output dim: None x 55 x ?) padded = Lambda(lambda x: K.temporal_padding(x, (0, 1)))(in_tensor) # just pad end assert padded.shape[1] == 55, padded.shape # align neighbors (output dim: None x 54 x 27 x ?) #aligned = K.gather(padded, neighbors) #aligned = padded[ neighbors[np.newaxis].astype(np.int32), :] aligned = Lambda(lambda x: tf.gather(x, neighbors, axis=1))(padded) assert aligned.shape[1:3] == (54, 27), aligned.shape # 2D convolution in one axis (output dim: None x 54 x 1 x filter_size) conv = Conv2D(filter_size, kernel_size=(1, 27), strides=(1, 1), padding='valid', data_format="channels_last", kernel_regularizer=l2(0.001), bias_regularizer=l2(0.001))(aligned) assert conv.shape[1:3] == (54, 1), conv.shape # reshape (output dim: None x 54 x filter_size) out_tensor = Lambda(lambda x: K.squeeze(x, axis=2))(conv) assert out_tensor.shape[1] == 54, out_tensor.shape return out_tensor
def call(self, x): self.new_layers_list = [] out_vecs = [] for window in self.windows: window = int(window) new_vec_list = [] i = 0 while K.int_shape(x)[1] > window * (i + 1): temp = x[:, window * i:window * (i + 1)] new_vec_list.append(temp) i += 1 temp = x[:, window * i:] temp = K.temporal_padding(temp, padding=(0, window * (i + 1) - K.int_shape(x)[1])) new_vec_list.append(temp) temp = concatenate(new_vec_list, axis=-1) temp = K.mean(temp, axis=-1, keepdims=True) out_vecs.append(temp) return out_vecs
def stdev_pooling(inputs, stride): import keras.backend as K data = inputs padding = K.shape(data)[1] % stride data = K.switch( padding > 0, K.temporal_padding(data, padding=(0, stride - padding)), data) num_windows = K.shape(data)[1] / stride idxs = K.arange(num_windows) * stride windows = K.map_fn(lambda w: data[:, w:(w + stride), :], idxs, dtype=K.floatx()) windows = K.permute_dimensions(windows, (1, 0, 2, 3)) stds = K.map_fn(lambda w: K.std(w, axis=1), windows) return stds
def call(self, x): # input shape: (nb_samples, time (padded with zeros), input_dim) # note that the .build() method of subclasses MUST define # self.input_spec with a complete input shape. input_shape = self.input_spec[0].shape if self.window_size > 1: x = K.temporal_padding(x, (self.window_size-1, 0)) x = K.expand_dims(x, 2) # add a dummy dimension # z, g output = K.conv2d(x, self.kernel, strides=self.strides, padding='valid', data_format='channels_last') output = K.squeeze(output, 2) # remove the dummy dimension if self.use_bias: output = K.bias_add(output, self.bias, data_format='channels_last') z = output[:, :, :self.output_dim] g = output[:, :, self.output_dim:] return self.activation(z) * K.sigmoid(g)
def get_keras_pad(cls, x, pads, dim, data_format=None): if sum(pads) == 0: return x if len(pads) == dim * 2: pads = list( np.transpose( np.array(pads).reshape([2, dim]).astype(np.int32))) pads = tuple(tuple(i) for i in pads) elif len(pads) == dim: pads = tuple((i, i) for i in pads) if dim == 1: return Lambda(lambda _x: K.temporal_padding(_x, pads))(x) elif dim == 2: return Lambda( lambda _x: K.spatial_2d_padding(_x, pads, data_format))(x) elif dim == 3: return Lambda( lambda _x: K.spatial_3d_padding(_x, pads, data_format))(x) else: raise NotImplementedError( "padding with dim {} is not implemented.".format(dim))
def preprocess_input(self, x): if self.window_size > 1: x = K.temporal_padding(x, (self.window_size - 1, 0)) x = K.expand_dims(x, 2) # add a dummy dimension output = K.conv2d(x, self.W, strides=self.subsample, padding='valid', data_format='channels_last') output = K.squeeze(output, 2) # remove the dummy dimension if self.bias: output += K.reshape(self.b, (1, 1, self.output_dim * 3)) if self.dropout is not None and 0. < self.dropout < 1.: z = output[:, :, :self.output_dim] f = output[:, :, self.output_dim:2 * self.output_dim] o = output[:, :, 2 * self.output_dim:] f = K.in_train_phase(1 - _dropout(1 - f, self.dropout), f) return K.concatenate([z, f, o], -1) else: return output
def call(self, inputs): if isinstance(inputs, list): x, x_mask = inputs else: x, x_mask = inputs, None seq_dim = K.int_shape(x)[-1] # 补足长度,保证可以reshape seq_len = K.shape(x)[1] pad_len = self.rate - seq_len % self.rate x = K.temporal_padding(x, (0, pad_len)) if x_mask is not None: x_mask = K.temporal_padding(x_mask, (0, pad_len)) new_seq_len = K.shape(x)[1] x = K.reshape(x, (-1, new_seq_len, seq_dim)) # 经过padding后shape可能变为None,所以重新声明一下shape # 线性变换 qw = self.reuse(self.q_dense, x) kw = self.reuse(self.k_dense, x) vw = self.reuse(self.v_dense, x) # 提取局部特征 kernel_size = 1 + 2 * self.neighbors kwp = extract_seq_patches( kw, kernel_size, self.rate) # shape=[None, seq_len, kernel_size, out_dim] vwp = extract_seq_patches( vw, kernel_size, self.rate) # shape=[None, seq_len, kernel_size, out_dim] if x_mask is not None: xp_mask = extract_seq_patches(x_mask, kernel_size, self.rate) # 形状变换 qw = K.reshape(qw, (-1, new_seq_len // self.rate, self.rate, self.heads, self.key_size)) kw = K.reshape(kw, (-1, new_seq_len // self.rate, self.rate, self.heads, self.key_size)) vw = K.reshape(vw, (-1, new_seq_len // self.rate, self.rate, self.heads, self.size_per_head)) kwp = K.reshape(kwp, (-1, new_seq_len // self.rate, self.rate, kernel_size, self.heads, self.key_size)) vwp = K.reshape(vwp, (-1, new_seq_len // self.rate, self.rate, kernel_size, self.heads, self.size_per_head)) if x_mask is not None: x_mask = K.reshape(x_mask, (-1, new_seq_len // self.rate, self.rate, 1, 1)) xp_mask = K.reshape( xp_mask, (-1, new_seq_len // self.rate, self.rate, kernel_size, 1, 1)) # 维度置换 qw = K.permute_dimensions( qw, (0, 3, 2, 1, 4)) # shape=[None, heads, r, seq_len // r, size] kw = K.permute_dimensions(kw, (0, 3, 2, 1, 4)) vw = K.permute_dimensions(vw, (0, 3, 2, 1, 4)) qwp = K.expand_dims(qw, 4) kwp = K.permute_dimensions( kwp, (0, 4, 2, 1, 3, 5)) # shape=[None, heads, r, seq_len // r, kernel_size, out_dim] vwp = K.permute_dimensions(vwp, (0, 4, 2, 1, 3, 5)) if x_mask is not None: x_mask = K.permute_dimensions(x_mask, (0, 3, 2, 1, 4)) xp_mask = K.permute_dimensions(xp_mask, (0, 4, 2, 1, 3, 5)) # Attention1 a = K.batch_dot(qw, kw, [4, 4]) / self.key_size**0.5 a = K.permute_dimensions(a, (0, 1, 2, 4, 3)) a = to_mask(a, x_mask, 'add') a = K.permute_dimensions(a, (0, 1, 2, 4, 3)) if self.mask_right: ones = K.ones_like(a[:1, :1, :1]) mask = (ones - K.tf.matrix_band_part(ones, -1, 0)) * 1e10 a = a - mask # Attention2 ap = K.batch_dot(qwp, kwp, [5, 5]) / self.key_size**0.5 ap = K.permute_dimensions(ap, (0, 1, 2, 3, 5, 4)) if x_mask is not None: ap = to_mask(ap, xp_mask, 'add') ap = K.permute_dimensions(ap, (0, 1, 2, 3, 5, 4)) if self.mask_right: mask = np.ones((1, kernel_size)) mask[:, -self.neighbors:] = 0 mask = (1 - K.constant(mask)) * 1e10 for _ in range(4): mask = K.expand_dims(mask, 0) ap = ap - mask ap = ap[..., 0, :] # 合并两个Attention A = K.concatenate([a, ap], -1) A = K.softmax(A) a, ap = A[..., :K.shape(a)[-1]], A[..., K.shape(a)[-1]:] # 完成输出1 o1 = K.batch_dot(a, vw, [4, 3]) # 完成输出2 ap = K.expand_dims(ap, -2) o2 = K.batch_dot(ap, vwp, [5, 4]) o2 = o2[..., 0, :] # 完成输出 o = o1 + o2 o = to_mask(o, x_mask, 'mul') o = K.permute_dimensions(o, (0, 3, 2, 1, 4)) o = K.reshape(o, (-1, new_seq_len, self.out_dim)) o = o[:, :-pad_len] return o
def divisible_temporal_padding(x, n): """将一维向量序列右padding到长度能被n整除 """ r_len = K.shape(x)[1] % n p_len = K.switch(r_len > 0, n - r_len, 0) return K.temporal_padding(x, (0, p_len))
def _delay(self, x): return K.temporal_padding(x, (1, 0))[:, :-1]
def call(self, inputs): return K.temporal_padding(inputs, padding=self.padding)
def neighbour_lookup(atoms, edges, maskvalue=0, include_self=False): ''' Looks up the features of an all atoms neighbours, for a batch of molecules. # Arguments: atoms (K.tensor): of shape (batch_n, max_atoms, num_atom_features) edges (K.tensor): of shape (batch_n, max_atoms, max_degree) with neighbour indices and -1 as padding value maskvalue (numerical): the maskingvalue that should be used for empty atoms or atoms that have no neighbours (does not affect the input maskvalue which should always be -1!) include_self (bool): if True, the featurevector of each atom will be added to the list feature vectors of its neighbours # Returns: neigbour_features (K.tensor): of shape (batch_n, max_atoms(+1), max_degree, num_atom_features) depending on the value of include_self # Todo: - make this function compatible with Tensorflow, it should be quite trivial because there is an equivalent of `T.arange` in tensorflow. ''' # The lookup masking trick: We add 1 to all indices, converting the # masking value of -1 to a valid 0 index. masked_edges = edges + 1 # We then add a padding vector at index 0 by padding to the left of the # lookup matrix with the value that the new mask should get # masked_atoms = temporal_padding(atoms, (1,0), padvalue=maskvalue) # OLD code. Use keras.backend.temporal_padding masked_atoms = K.temporal_padding(atoms, padding=(1, 0)) # Import dimensions atoms_shape = K.shape(masked_atoms) # shape (batch_n, max_atoms + 1, num_atom_features) +1 from padding batch_n = atoms_shape[0] # number of molecules in batch aka number of examples per batch lookup_size = atoms_shape[1] # number of atoms in one molecule aka atoms in one example num_atom_features = atoms_shape[2] # number of atom features edges_shape = K.shape(masked_edges) max_atoms = edges_shape[1] # number of atoms in one molecule max_degree = edges_shape[2] # max number of connections an atom can have, usually is 5 # create broadcastable offset offset_shape = (batch_n, 1, 1) # print(K.dtype(masked_edges)) # equals int32 # changed T.arange to K.arange offset = K.reshape(K.arange(batch_n, dtype=K.dtype(masked_edges)), offset_shape) offset *= lookup_size # apply offset to account for the fact that after reshape, all individual # batch_n indices will be combined into a single big index flattened_atoms = K.reshape(masked_atoms, (-1, num_atom_features)) flattened_edges = K.reshape(masked_edges + offset, (batch_n, -1)) # Gather flattened flattened_result = K.gather(flattened_atoms, flattened_edges) # Unflatten result output_shape = (batch_n, max_atoms, max_degree, num_atom_features) # output = T.reshape(flattened_result, output_shape) # OLD output = K.reshape(x=flattened_result, shape=output_shape) if include_self: # EDIT: changed dim=2 to axis=2 return K.concatenate([K.expand_dims(atoms, axis=2), output], axis=2) return output