Efficiently multiplying tensor elements in Keras - python

I have a Tensor in Keras with the following shape
x = (None, 14, 14, 32)
This is the weight of the convolution layer from my network.
I need to multiply elements of the tensor with each other i.e. self multiplication and then sum all the values together.
Let us consider a simpler example, if I have the following tensor
x = Tensor([1,2,3],[4,5,6])
Then I need to compute x*x and the output should be
1*4 + 1*5 + 1*6 + 2*4 + 2*5 + 2*6 + 3*4 + 3*5 + 3*6
As a naive implementation, I tried the following
flattened_unpacked = tf.unstack(tf.reshape(tf.gather(x,0), [-1]))
list1 = []
list2 = []
for elem in flattened_unpacked:
list1.append(elem)
list2.append(elem)
res = [i * j for j in list1 for i in list2]
sum_res = sum(res)
But it quickly ran out of memory on Google Colab. Is there an efficient way to perform this multiplication ?

You can use broadcast_to to make your array size compatible for matrix multiplication. See the documentation here.
Looking at the example given you are trying to do a matrix multiplication od these two matrices.
Matrix 1: shape 3x3
array([[1, 1, 1],
[2, 2, 2],
[3, 3, 3]])
Matrix 2: shape 3x1
array([[4],
[5],
[6]])
And calculate the sum of the resultant 3 x 1 matrix.
This can be achieved by:
Step 1: Matrix multiplication:
x = tf.matmul(tf.broadcast_to(tf.constant([[1], [2], [3]]), shape = (3,3)), tf.constant([[4], [5], [6]]))
Output:
array([[15],
[30],
[45]])
Step 2: Adding the elements
tf.reduce_sum(x)
Output:
90

Related

Lorentzian inner product in matrix form using Pytorch

I want to compute the Lorentzian inner product, that is <x,y> = -x1y1 + x2y2 + x3y3 +...
I have the code
res = torch.sum(x * y, dim=-1) - 2 * x[..., 0] * y[..., 0]
But this fails to work, because I keep getting this error -
RuntimeError: The size of tensor a (450) must match the size of tensor b (30) at non-singleton dimension 0
I need the inner product in the matrix form. So I did this -
res = torch.matmul(x,torch.transpose(y,0,1))
-2*torch.matmul(x[...,0],torch.transpose(y[...,0],0,0))
But I get a new error
RuntimeError: inconsistent tensor size, expected tensor [450] and src [30] to have the same number of elements, but got 450 and 30 elements respectively.
I have tried this on a simple toy example -
x = torch.tensor([[1, 2, 3]])
y = torch.tensor([[2, 2, 2]])
prod = torch.matmul(x,torch.transpose(y,0,1))-2*torch.matmul(x[...,0],torch.transpose(y[...,0],0,0))
print(prod)
Output : tensor([[8]]) which is right. But somehow doesn't seem to work in the application I am working on.
I am not sure how to solve this. Any insights are welcome please!
So I did this -
import torch
x = torch.tensor([[1, 2, 3]])
y = torch.tensor([[2, 2, 2]])
x[...,0] *= -1
res = torch.matmul(x,torch.transpose(y,0,1))
print (res)
It started working for my application.

how to add list of arrays (tensors)

I am defining a simple conv2d function to calculate the cross-correlation between input and kernel (both 2D tensor) as below:
import torch
def conv2D(X, K):
h = K.shape[0]
w = K.shape[1]
ĥ = X.shape[0] - h + 1
ŵ = X.shape[1] - w + 1
Y = torch.zeros((ĥ, ŵ))
for i in range (ĥ):
for j in range (ŵ):
Y[i, j] = (X[i: i+h, j: j+w]*K).sum()
return Y
When X and K are of rank-3 tensor, I calculate the conv2d for each channel and then add them together as below:
def conv2D_multiple(X, K):
cross = []
result = 0
for x, k in zip(X, K):
cross.append(conv2D(x,k))
for t in cross:
result += t
return result
To test my function:
X_2 = torch.tensor([[[0, 1, 2], [3, 4, 5], [6, 7, 8]],
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]], dtype=torch.float32)
K_2 = torch.tensor([[[0, 1], [2, 3]], [[1, 2], [3, 4]]], dtype=torch.float32)
conv2D_multiple(X_2, K_2)
The results is:
tensor([[ 56., 72.],
[104., 120.]])
The result is as expected, however, I believe my second
for loop inside conv2D_multiple(X, K) function is redundant. My question is how to sum (element wise)
tensors (arrays) in the list so I omit the second for loop.
Since your conv2D operates on a per slice behaviour, what you can do is allocate a 3D tensor so that when you use the first for loop, you store the results by taking each result and populating each slice. You can then sum along the dimension of the slices using PyTorch's built-in torch.sum operator on the tensor to get the same result. To make it palatable, I'll make the slice dimension dim=0. Therefore, replace cross from being an initial empty list to a Torch tensor that is 3D to allow you to store the intermediate results, then compress along the slice dimension by summing. We can get away with doing this as your initial implementation stored the intermediate results as a list of 2D tensors. To make it easier, go to 3D and allow PyTorch to sum along the slice axis.
This will require that you define the correct dimensions for this 3D tensor first prior to looping:
def conv2D_multiple(X, K):
h = K.shape[1]
w = K.shape[2]
ĥ = X.shape[1] - h + 1
ŵ = X.shape[2] - w + 1
c = X.shape[0]
cross = torch.zeros((c, ĥ, ŵ), dtype=torch.float32)
for i, (x, k) in enumerate(zip(X, K)):
cross[i] = conv2D(x,k)
result = cross.sum(dim=0)
return result
Notice that for each slice you're iterating over between the input and kernel, instead of appending to a new list we directly place this into a slice in the intermediate tensor. Once you store these results, sum along the slice axis to finally compress it into what you expect. Running the new function above with your example inputs generates the same result.
If this isn't a desired result for you, another way is to simply take the list of tensors you created, build the intermediate tensor out of that by stacking them all together using torch.stack and sum. By default it stacks along the first axis (dim=0):
def conv2D_multiple(X, K):
cross = []
result = 0
for x, k in zip(X, K):
cross.append(conv2D(x,k))
cross = torch.stack(cross)
result = cross.sum(dim=0)
return result

An equivalent but differentiable argmax expression in Tensorflow

I need a one-hot representation for the maximum value in a tensor.
For example, consider a tensor 2 x 3:
[ [1, 5, 2],
[0, 3, 7] ]
The one-hot-argmax representation I am aiming for looks like this:
[ [0, 1, 0],
[0, 0, 1] ]
I can do it as follows, where my_tensor is a N x 3 tensor:
position = tf.argmax(my_tensor, axis=1). # Shape (N x )
one_hot_pos = tf.one_hot(position, depth=3) # Shape (N x 3)
But this part of the code need be differentiable since I'm training over it.
My workaround was as follows, where EPSILON = 1e-3 is a small constant:
max_value = tf.reduce_max(my_tensor, axis=1, keepdims=True)
clip_min = max_value - EPSILON
one_hot_pos = (tf.clip_by_value(my_tensor, clip_min, max_value) - clip_min) / (max_value - clip_min)
The workaround works most of the time, but - as expected - it has some issues:
Sensible to EPSILON: if it is too small, a division by zero might happen
Can't solve ties: argmax only chooses one even in a tie situation
Do you know any better way of simulating the argmax followed by one_hot situation, while fixing the two mentioned issues, but using only differentiable Tensorflow functions?
Do some maximum, tile and multiplication operations. Like:
a = tf.Variable([ [1, 5, 2], [0, 3, 7] ]) # your tensor
m = tf.reduce_max(a, axis=1) # [5,7]
m = tf.expand_dims(m, -1) # [[5],[7]]
m = tf.tile(m, [1,3]) # [[5,5,5],[7,7,7]]
y = tf.cast(tf.equal(a,m), tf.float32)) # [[0,1,0],[0,0,1]]
This is a tricky multiplication operation that is differentiable.

Why does a linear regression placeholder have shape [1, 1] in tensorflow?

I've been reading this guide on tensorflow: https://medium.com/all-of-us-are-belong-to-machines/the-gentlest-introduction-to-tensorflow-248dc871a224
...and mostly, I see what's happening.
However, the linear model in the example code defines the linear model like this:
# Model linear regression y = Wx + b
x = tf.placeholder(tf.float32, [None, 1])
W = tf.Variable(tf.zeros([1,1]))
b = tf.Variable(tf.zeros([1]))
product = tf.matmul(x,W)
y = product + b
y_ = tf.placeholder(tf.float32, [None, 1])
# Cost function sum((y_-y)**2)
cost = tf.reduce_mean(tf.square(y_-y))
# Training using Gradient Descent to minimize cost
train_step = tf.train.GradientDescentOptimizer(0.0000001).minimize(cost)
The question is: Why is Wx + b represented with these values:
x = tf.placeholder(tf.float32, [None, 1])
W = tf.Variable(tf.zeros([1,1]))
b = tf.Variable(tf.zeros([1]))
? [None, 1], [1, 1]? Why [None, 1] for x and [1, 1] for W?
If [1, 1] is 1 element of size 1, then why is b just [1], what does that mean? 1 element of size 0?
For W = tf.Variable, the first '1' is feature, house size, and the 2nd '1' is output, house price.
Does that mean if I was trying to represent the model, say:
y = Ax + Bz
That means I have two 'features' (x and z) and that my A and B values should be shaped [2, 1]? It doesn't seem right...
This seems utterly unlike what is done in polynomial regression, where weight factors are shape [1]. Why is this different?
I think maybe you should learn something like linear algebra.
Let's start with this line # Model linear regression y = Wx + b which is the first line in the code you post. Actually, it means two matrix operations.
First one is Wx, that means matrix X matrix multiply x. In your case, means:
[x11, x21, x31, ..., xn1]T * [w] = [x11*w, x21*w, x31*w, ..., xn1*w]T
Let Wx as R(Result), we can rewrite Wx + B into R + B. This is the second matrix operation. In your case, means:
[x11*w, x21*w, x31*w, ..., xn1*w]T + [b] = [x11*w + b, x21*w + b, x31*w + b, ..., xn1*w + b]T
So if you have more than one features in your input, and want to output multiple results, the definition of model should be:
x = tf.placeholder(tf.float32, [None, your_input_features])
W = tf.Variable(tf.zeros([your_input_features, your_output_features]))
b = tf.Variable(tf.zeros([your_output_features]))
product = tf.matmul(x,W)
y = product + b
The original author should have chosen the shape as [1, 1] because she/he wanted to have a more general function than plain scalar product.
This way, you can change the shape to [1, d] to have d features for each sample.
Of course one should also change the shape of x to d then.
Are you familiar with linear algebra ?
A placeholder of shape [None, 1] means unlimited rows and 1 column.
A placeholder of shape [1, 1] means 1 row and 1 column.
Shape [1, 1] and [1] are different in that sense:
[1] => plh = [x]
[1, 1] => plh = [[x]]
Then tf.matmul compute the dot product: x.W and add b.
In order for tensorflow to work, the tensors must be of similar shape, that's why W is of shape [1, 1] and not just [1].
Let us have:
x = [[1], [2], [3]]
W = [[10]]
b = [[9], [8], [7]]
Then:
tf.matmul(x, W) = [[10], [20], [30]]
tf.matmul(x, W) + b = [[19], [28], [27]]
I hope this answer your question

Tensorflow - pick values from indicies, what is the operation called?

An example
Suppose I have a tensor values with shape (2,2,2)
values = [[[0, 1],[2, 3]],[[4, 5],[6, 7]]]
And a tensor indicies with shape (2,2) which describes what values to be selected in the innermost dimension
indicies = [[1,0],[0,0]]
Then the result will be a (2,2) matrix with these values
result = [[1,2],[4,6]]
What is this operation called in tensorflow and how to do it?
General
Note that the above shape (2,2,2) is only an example, it can be any dimension. Some conditions for this operation:
ndim(values) -1 = ndim(indicies)
values.shape[:-1] == indicies.shape == result.shape
indicies.max() < values.shape[-1] -1
I think you can emulate this with tf.gather_nd. You will just have to convert "your" indices to a representation that is suitable for tf.gather_nd. The following example here is tied to your specific example, i.e. input tensors of shape (2, 2, 2) but I think this gives you an idea how you could write the conversion for input tensors with arbitrary shape, although I am not sure how easy it would be to implement this (haven't thought about it too long). Also, I'm not claiming that this is the easiest possible solution.
import tensorflow as tf
import numpy as np
values = np.array([[[0, 1], [2, 3]], [[4, 5], [6, 7]]])
values_tf = tf.constant(values)
indices = np.array([[1, 0], [0, 0]])
converted_idx = []
for k in range(values.shape[0]):
outer = []
for l in range(values.shape[1]):
inds = [k, l, indices[k][l]]
outer.append(inds)
print(inds)
converted_idx.append(outer)
with tf.Session() as sess:
result = tf.gather_nd(values_tf, converted_idx)
print(sess.run(result))
This prints
[[1 2]
[4 6]]
Edit: To handle arbitrary shapes here is a recursive solution that should work (only tested on your example):
def convert_idx(last_dim_vals, ori_indices, access_to_ori, depth):
if depth == len(last_dim_vals.shape) - 1:
inds = access_to_ori + [ori_indices[tuple(access_to_ori)]]
return inds
outer = []
for k in range(ori_indices.shape[depth]):
inds = convert_idx(last_dim_vals, ori_indices, access_to_ori + [k], depth + 1)
outer.append(inds)
return outer
You can use this together with the original code I posted like so:
...
converted_idx = convert_idx(values, indices, [], 0)
with tf.Session() as sess:
result = tf.gather_nd(values_tf, converted_idx)
print(sess.run(result))

Categories