Reputation: 3327
I want to do the following normalization in tensorflow, but I'm not sure how to.
I have a tensor a
, which is of shape (c, c) and let's say c=16 in this case. I have another tensor of shape b
, which is of shape (w, w, c) and w = 32, c=16 (so b
's dimensions are height w, width w, and depth/channels c). I'm ignoring the batch dimensions in both cases, but you can assume the a
and b
have the same batch dimensions/number of samples.
I would like to do something like the following, ideally using tensor notation/operations and not using this for loop:
for i in range(c): # loop over the channels
y[:,:,i] = b[:,:,i]/(sum_j[a[j,i] * b[:,:,j]])
In other words, we're normalizing b
by a weighted sum over j a[j,i]*b[:,:,j]
, where j is indexing the channel. We do this for each channel i to produce y[:,:,i]
. I would greatly appreciate help on how to do this using tensorflow functionality. Thanks!
Upvotes: 1
Views: 510
Reputation: 17585
Let c = 2 and w = 3 for illustration.
import tensorflow as tf
a = tf.constant([
[1, 2],
[3, 4]], dtype=tf.float32)
b = tf.constant([
[[11, 15],
[4, 3],
[3, 7]],
[[22, 5],
[15, 20],
[1, 4]],
[[16, 14],
[3, 22],
[2, 8]]], tf.float32)
a = a[:, None, None]
print(a.shape)
# (2, 1, 1, 2)
b_extended = tf.tile(b[None], [2, 1, 1, 1])
print(b_extended.shape)
# (2, 3, 3, 2)
norm = tf.math.reduce_sum(a * b_extended, -1)
norm = tf.transpose(norm, (1, 2, 0))
out = b / norm
print(out)
# tf.Tensor(
# [[[0.2682927 0.16129032]
# [0.4 0.125 ]
# [0.1764706 0.1891892 ]]
#
# [[0.6875 0.05813954]
# [0.27272728 0.16 ]
# [0.11111111 0.21052632]]
#
# [[0.36363637 0.13461539]
# [0.06382979 0.22680412]
# [0.11111111 0.21052632]]], shape=(3, 3, 2), dtype=float32)
Let's test with numpy and see if this is indeed what we want.
import numpy as np
a = a.numpy()
b = b.numpy()
y = np.zeros((3, 3, 2))
for i in range(2):
y[:, :, i] = b[:, :, i] / (a[i] * b).sum(-1)
np.testing.assert_array_almost_equal(y, out.numpy())
# passes
You can just wrap it in a tensorflow.keras.layers.Layer
and make your own layer and you'll need to account for batches now when you are expanding the inputs.
import tensorflow as tf
from tensorflow.keras import layers
class CustomNormLayer(layers.Layer):
"""``CustomNormLayer``."""
def __init__(self):
super().__init__()
def call(self, x, y):
# extract `c`
c = tf.shape(x)[1]
# expand x
x = x[:, :, None, None]
# ----^ (batch)
# tile y
y_extended = tf.tile(y[:, None], (1, c, 1, 1, 1))
# ---------------------^ (batch)
# sum norm
norm = tf.math.reduce_sum(x * y_extended, -1)
# permute
norm = tf.transpose(norm, (0, 2, 3, 1))
return y / norm
Let's make a model and try it out
a = tf.constant([
[1, 2],
[3, 4]], dtype=tf.float32)
b = tf.constant([
[[11, 15],
[4, 3],
[3, 7]],
[[22, 5],
[15, 20],
[1, 4]],
[[16, 14],
[3, 22],
[2, 8]]], tf.float32)
# put data into a `tf.data` object
batch_size = 5
dataset = tf.data.Dataset.from_tensors((a, b))
dataset = dataset.repeat()
dataset = dataset.batch(batch_size)
train_iter = iter(dataset)
# define layers
xa = tf.keras.Input((2, 2))
xb = tf.keras.Input((3, 3, 2))
out = CustomNormLayer()(xa, xb)
# define model
model = tf.keras.Model([xa, xb], out)
# put some data in
data = next(train_iter)
model(data)[0]
# <tf.Tensor: shape=(3, 3, 2), dtype=float32, numpy=
# array([[[0.2682927 , 0.16129032],
# [0.4 , 0.125 ],
# [0.1764706 , 0.1891892 ]],
# ...
Upvotes: 1