Replacing a for loop with indexing

username: radek
cooked: <p>I have a 3-dimensional tensor. 10_000 examples x 10 predicted labels x outputs from 3 models. <code>(10_000, 10, 3)</code></p>
<p>I also have a tensor with ids of the model outputs I would like to use for each label. It has 10 values between 0 and 2.</p>
<p>Is there any way I could index into the 3d tensor picking an output for a label from a specified model? At the end I would like to have a 10_000 examples x 10 labels tensor where for each of the labels I picked predictions from a model of my choosing.</p>
<p>I am currently doing this via permuting the original tensor to be of shape <code>(10, 3, 10_000)</code> and looping over the labels. I store the outputs in a list and concatenate them into a tensor at the end.</p>
<p>I tried using <code>tensor.gather</code> and <code>tensor.index_select</code> but couldn’t get either to work. Intuitively I feel there must a better way of doing this.</p>
<p>This is the code I have:</p>
<pre><code class="lang-auto">labels = []
for label, idx in zip(preds.permute(2, 0, 1), best_model_idx_per_label):
    labels.append(label[idx])

torch.stack(labels)
</code></pre>
<p>Would be grateful for any help. Thank you!</p>

username: ptrblck
cooked: <p>If I understand you correctly, you would like to select from the last dimension using an index tensor with values in <code>[0, 2]</code>.<br>
The same operation should be performed for all examples?</p>
<p>If so, you could try the following code:</p>
<pre><code class="lang-auto">a = torch.randn(20, 10, 3)

idx = torch.zeros(10).random_(3).long()
idx = idx.unsqueeze(0).repeat(20, 1)
 
b = a.gather(2, idx.view(20, 10, 1))
print(b.shape)
</code></pre>
<p>I’ve just used 20 for the batch dimension.<br>
Let me know, if this works for you or if I misunderstood your question.</p>

username: radek
cooked: <p>Thank you very much for your help, really appreciate it <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=5" title=":slight_smile:" class="emoji" alt=":slight_smile:"> This works perfectly.</p>
<p>Really neat to see how you went about achieving this via growing the idx tensor to a shape that was needed and then using gather. Quite a learning experience!</p>
<p>Thank you!</p>

username: abd
cooked: <p>Hi <a class="mention" href="/u/ptrblck">@ptrblck</a></p>
<p>hawo can I replace this loop:<br>
import torch<br>
d=torch.randn(20,10,3)<br>
for i in range(d.size(0)):<br>
d[i]=d[i]*2+20<br>
print(d.shape)</p>
<p>thanks</p>

username: Eta_C
cooked: <p><a class="mention" href="/u/abd">@abd</a> quite simple.</p>
<pre><code class="lang-python">import torch
d = torch.randn(20, 10, 3)
d = d * 2 + 20
print(d)
</code></pre>

username: alex_gilabert
cooked: <p>Hello <a class="mention" href="/u/ptrblck">@ptrblck</a></p>
<p>I have a efficient issue with some tensor for loop.</p>
<p>I’m extracting the features from the last layer of a CNN through a image data loader (I’m using batch size 8). Im  getting the euclidean distance of the batch tensor and a table with previous features.</p>
<p>I want to add to the table a tensor every time all the tensors in the table are above a threshole. I have implemented a successful running code but the loop i use its not efficient and im wondering how i could do something similar using something more efficient rather than this secuential way.</p>
<p>for i, data in enumerate(dataloader, 0):<br>
input, label = data<br>
input, label = input.to(device), label.to(device)<br>
n,c h,w = input.size()<br>
outputs = model(input)<br>
if (i == 0):<br>
features_list = torch.cat( (features_list, outputs[0].view(1,-1)), 0)<br>
dist_tensores = torch.cdist(outputs, features_list, p=2.0)<br>
activation = torch.gt(dist_tensores, AVG, out=torch.cuda.FloatTensor(len(outputs), len(features_list)))<br>
counter = len(features_list)<br>
activation_list = torch.sum(activation, dim=0)<br>
for x in range(len(activation)):<br>
if (torch.sum(activation[x], dim=0) == counter):<br>
features_list = torch.cat( (features_list, outputs[x].view(1,-1)), 0)</p>
<p>Any ideas?</p>

username: ptrblck
cooked: <p>Could you post the shapes of all necessary tensors in order to create an executable code snippet, please?<br>
PS: you can post code snippets by wrapping them into three backticks ```, which makes debugging easier <img src="https://discuss.pytorch.org/images/emoji/apple/wink.png?v=9" title=":wink:" class="emoji" alt=":wink:"></p>

username: alex_gilabert
cooked: <p>First of all thanks for anwering!</p>
<pre><code class="lang-auto">features_list = torch.empty( (0, 1000), dtype=torch.float ).cuda()
</code></pre>
<p>Firstly we create an empty tensor of 1000 colummns (output of VGG16 conv_layer5_3).</p>
<p>Batch size of the dataloader = 8</p>
<pre><code class="lang-auto">    counter = 0                                                     
    model.cuda()
    for i, data in enumerate(dataloader, 0):
        #Extraccion de Tensores
        input, label = data                                             
        input, label = input.to(device), label.to(device)              
        n,c ,h,w = input.size()                                         
        outputs = model(input)   
</code></pre>
<p>this outputs torch tensor has dims: torch.Size([8, 1000])</p>
<pre><code class="lang-auto">        if (i == 0):                                                    
            features_list = torch.cat( (features_list, outputs[0].view(1,-1)), 0)

</code></pre>
<p>For the very first iteration we add the first tensor that comes in the mini-batch.</p>
<pre><code class="lang-auto">        dist_tensores = torch.cdist(outputs, features_list, p=2.0) 
</code></pre>
<p>This distance dims depends on the iteration but firstly are:<br>
1º torch.Size([8, 1])<br>
2º torch.Size([8, 6])<br>
3º torch.Size([8, 11])<br>
…</p>
<pre><code class="lang-auto">AVG = 60
</code></pre>
<pre><code class="lang-auto">        activation = torch.gt(dist_tensores, AVG, out=torch.cuda.FloatTensor(len(outputs), len(features_list)))
</code></pre>
<p>Depends on the iteration, always [8, X] being X the number of tensors in <code>feature_list</code></p>
<pre><code class="lang-auto">        counter = len(features_list)
        activation_list = torch.sum(activation, dim=0)

</code></pre>
<pre><code class="lang-auto">        for x in range(len(activation)):
          if (torch.sum(activation[x], dim=0) == counter):
            features_list = torch.cat( (features_list, outputs[x].view(1,-1)), 0)
</code></pre>
<p>If the add of all the positions in dim 0 equals the number of rows of <code>feature_list</code> we proceed to add the tensor via <code>torch.cat</code>.</p>
<p>My issue is that i don`t know how to add the tensor <code>outputs[x]</code> if im not ussing a loop. I think there are “for loops” created for this or maybe its just a vectorial comparision.</p>
<p>Once again, thanks for your answer. <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>

username: alex_gilabert
cooked: <p><a class="mention" href="/u/ptrblck">@ptrblck</a> Here is the code i hope that better this way <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>

username: ptrblck
cooked: <p>You could probably avoid the last loop using:</p>
<pre><code class="lang-python">idx = activation.sum(1) == counter
features_list = torch.cat((features_list, outputs[idx]), 0)
</code></pre>
<p>However, as I don’t have dummy inputs and the expected results, I couldn’t really test it, so you would need to verify it.<br>
I don’t think you could avoid the <code>dist_tensor</code> calculation, as it would be recalculated for the current output sample, if I understand the use case correctly.</p>

username: alex_gilabert
cooked: <p><a class="mention" href="/u/ptrblck">@ptrblck</a> That is exactly what i was looking for! Thanks for the help!</p>