Torch

Torch is a package for scientific calculations written in lua. A quick introduction to lua can be found here.

Tensors

At torch's heart is the tensor. A tensor is generalization of vectors and matrices to n-dimensions.

To define any tensor, we use the torch.Tensor function. For example, the following defines a vector with 4 elements:


In [1]:
a = torch.Tensor(4)

Torch will usually initialize your vector with garbage values. So, you'll see this when you print out the vector's value.


In [2]:
print(a)


Out[2]:
1e-313 *
 0.0017
 2.3342
 0.4244
 0.0000
[torch.DoubleTensor of size 4]

In itorch, you need not use print to print out the value of a variable. You can just type the name of the variable as follows:


In [3]:
a


Out[3]:
1e-313 *
 0.0017
 2.3342
 0.4244
 0.0000
[torch.DoubleTensor of size 4]

To declare a matrix, you would do:


In [4]:
b = torch.Tensor(2,2)

In [5]:
b


Out[5]:
1e-313 *
  0.0000  2.3342
  0.4244  0.0000
[torch.DoubleTensor of size 2x2]

For a 3-dimensional tensor, you would go:


In [6]:
c = torch.Tensor(2,2,2)

In [7]:
c


Out[7]:
(1,.,.) = 
   0.0000e+00   9.6079e-71
  1.2782e+165   1.6582e-76

(2,.,.) = 
  2.3583e+184   9.1666e-72
   5.4006e-66  2.0408e+184
[torch.DoubleTensor of size 2x2x2]

And so on ....

Size

If you are dealing with a tensor of unknown size, it is possible to quickly check its size. This can be done like this:


In [8]:
a:size()


Out[8]:
 4
[torch.LongStorage of size 1]


In [9]:
b:size()


Out[9]:
 2
 2
[torch.LongStorage of size 2]


In [10]:
c:size()


Out[10]:
 2
 2
 2
[torch.LongStorage of size 3]

Fill your tensor

As mentioned earlier, if you create a simple Tensor, you'll end up with garbage values. There are easy ways to initialize your tensor with values from certain distributions. For example to fill up your tensor with values from a gaussian/normal distribution you would do:


In [11]:
c:normal()


Out[11]:
(1,.,.) = 
 -1.0242  0.6681
 -0.5173  0.7760

(2,.,.) = 
 -1.0229  0.9319
 -0.7053 -0.5492
[torch.DoubleTensor of size 2x2x2]

Obviously, since this is a randomly initialized variable you should see a different output everytime.

You can fill your tensor with any value like this:


In [12]:
c:fill(3.14)


Out[12]:
(1,.,.) = 
  3.1400  3.1400
  3.1400  3.1400

(2,.,.) = 
  3.1400  3.1400
  3.1400  3.1400
[torch.DoubleTensor of size 2x2x2]

A note on : and .
a:function() is just syntactic sugar for a.function(a).

So, c:fill(2.14) can be written as c.fill(c, 2.14).


In [13]:
c.fill(c, 2.14)


Out[13]:
(1,.,.) = 
  2.1400  2.1400
  2.1400  2.1400

(2,.,.) = 
  2.1400  2.1400
  2.1400  2.1400
[torch.DoubleTensor of size 2x2x2]

So, this actually uses the passed object, c in this case. So, if you look at the contents of c, it must be changed.


In [14]:
c


Out[14]:
(1,.,.) = 
  2.1400  2.1400
  2.1400  2.1400

(2,.,.) = 
  2.1400  2.1400
  2.1400  2.1400
[torch.DoubleTensor of size 2x2x2]

Other ways
There are other ways to create tensors with randomly generated values. rand returns values in the range [0,1) from uniform distribution whereas randn returns values from a normal distribution with mean zero and variance 1 .


In [115]:
torch.rand(2,2)


Out[115]:
 0.3867  0.4407
 0.1780  0.0595
[torch.DoubleTensor of size 2x2]


In [116]:
torch.randn(2,2)


Out[116]:
 0.2117  1.5497
 1.9520 -0.0990
[torch.DoubleTensor of size 2x2]

Slicing and Indexing

There are various ways to only look at a smaller section of a tensor (a process called slicing). Let's first randomly initialize our tensor.


In [16]:
b:normal()


Out[16]:
-0.2930  0.4985
-1.2227  0.8963
[torch.DoubleTensor of size 2x2]

To peek through at the 1st row and 2nd column of b, we do this:


In [21]:
b[{1,2}]


Out[21]:
0.49846197897873	

or this:


In [20]:
b[1][2]


Out[20]:
0.49846197897873	

The [m][n][p] method is a less general form of slicing and can only point to scalars. If you want to slice out a smaller tensor from a bigger tensor, you should use the {} method.

Here are some examples of {} at work.


In [29]:
b[{{1}}]


Out[29]:
-0.2930  0.4985
[torch.DoubleTensor of size 1x2]

This selects the first row of b.


In [31]:
b[{{1,2}}]


Out[31]:
-0.2930  0.4985
-1.2227  0.8963
[torch.DoubleTensor of size 2x2]

This selects the 1st and 2nd rows of b which is just the full matrix b.


In [34]:
b[{1,{2}}]


Out[34]:
 0.4985
[torch.DoubleTensor of size 1]


In [35]:
b[{{1},2}]


Out[35]:
 0.4985
[torch.DoubleTensor of size 1]


In [36]:
b[{{1},{2}}]


Out[36]:
 0.4985
[torch.DoubleTensor of size 1x1]

Notice the difference between b[{1,2}], b[{1,{2}}], b[{{1},2}], and b[{{1},{2}}]. Pay attention to the size printed below the output. If you add an extra {} inside the outer {}, usually get back a tensor for that dimension. Always pay attention to the dimensions of your output. A lot of bugs happen because of this.

More detailed examples of this can be found here. I would suggest you to try these examples in another itorch notebook.

Here is a very general example:


In [37]:
d = torch.randn(3,4,5)

In [43]:
d


Out[43]:
(1,.,.) = 
 -0.8461  0.3194 -0.8264  0.3670 -1.1910
  1.0196 -1.2663  1.6300  0.2113  0.9769
 -0.6217 -0.4479  1.2800  2.4759  1.7844
  2.2250  1.4622  0.0158 -0.0034 -1.2053

(2,.,.) = 
  0.7008 -0.0110 -1.6457  0.3076  0.3965
  0.3776  0.0581 -0.0073 -0.7872  0.7750
  0.2884 -1.6259  3.1138  0.1392 -0.9459
 -0.4032  0.0462  0.8212  0.0091  0.0718

(3,.,.) = 
 -0.0116  0.0431 -1.6378  0.5006  0.3774
  1.1158  1.1870  2.1269  0.2587 -0.9945
  0.3300  0.8079  0.3424  0.2584 -2.0348
 -0.6764 -0.1062 -2.0644  0.1323 -0.4932
[torch.DoubleTensor of size 3x4x5]


In [38]:
d[{1,{2,3},{3,5}}]


Out[38]:
 1.6300  0.2113  0.9769
 1.2800  2.4759  1.7844
[torch.DoubleTensor of size 2x3]

Notice how this is different from d[{{1},{2,3},{3,5}}] (Look at the size line).


In [39]:
d[{{1},{2,3},{3,5}}]


Out[39]:
(1,.,.) = 
  1.6300  0.2113  0.9769
  1.2800  2.4759  1.7844
[torch.DoubleTensor of size 1x2x3]

Both of them select the 1st element of 1st dimension, 2nd and 3rd elements of 2nd dimension and 3rd to 5th elements of 3rd dimension but the second piece of code returns a 1x2x3 tensor and the first one returns a 2x3 tensor.

Another way to slice is the select command but it isn't as general as the {} method. You can reduce one dimension from a tensor using this. Therefore, it doesn't work on 1D tensors. It's documentation is here.

For example:


In [52]:
-- tensor_name:select(dimension, index)
d:select(1,2)


Out[52]:
 0.7008 -0.0110 -1.6457  0.3076  0.3965
 0.3776  0.0581 -0.0073 -0.7872  0.7750
 0.2884 -1.6259  3.1138  0.1392 -0.9459
-0.4032  0.0462  0.8212  0.0091  0.0718
[torch.DoubleTensor of size 4x5]

This selects 2nd element of the 1st dimension which is a 4x5 matrix. Each element of the 1st dimension is actually a 4x5 matrix as can be seen in the output for d above.

Try to understand what this might be doing:


In [53]:
d:select(2,2)


Out[53]:
 1.0196 -1.2663  1.6300  0.2113  0.9769
 0.3776  0.0581 -0.0073 -0.7872  0.7750
 1.1158  1.1870  2.1269  0.2587 -0.9945
[torch.DoubleTensor of size 3x5]

There are many methods of extracting a smaller tensor from a bigger tensor and they are described here.

Math operations

Let's first create two tensors. First one contains just ones and in the second we fill 2s.


In [98]:
e = torch.ones(2,2)

In [99]:
f = torch.Tensor(2,2):fill(2)

In [100]:
e


Out[100]:
 1  1
 1  1
[torch.DoubleTensor of size 2x2]


In [101]:
f


Out[101]:
 2  2
 2  2
[torch.DoubleTensor of size 2x2]

Addition
This is one simple way to add tensors:


In [102]:
e+f


Out[102]:
 3  3
 3  3
[torch.DoubleTensor of size 2x2]

This doesn't affect the values of the addends themselves as can be seen below.


In [103]:
e


Out[103]:
 1  1
 1  1
[torch.DoubleTensor of size 2x2]


In [104]:
f


Out[104]:
 2  2
 2  2
[torch.DoubleTensor of size 2x2]

Another way to write this is:


In [105]:
torch.add(e,f)


Out[105]:
 3  3
 3  3
[torch.DoubleTensor of size 2x2]

The following however does affect the value of e.


In [106]:
e:add(f)


Out[106]:
 3  3
 3  3
[torch.DoubleTensor of size 2x2]


In [107]:
e


Out[107]:
 3  3
 3  3
[torch.DoubleTensor of size 2x2]


In [108]:
f


Out[108]:
 2  2
 2  2
[torch.DoubleTensor of size 2x2]

Adding scalars
To add scalars to tensors use the same functions.


In [109]:
e+1


Out[109]:
 4  4
 4  4
[torch.DoubleTensor of size 2x2]


In [110]:
e


Out[110]:
 3  3
 3  3
[torch.DoubleTensor of size 2x2]


In [111]:
torch.add(e,1)


Out[111]:
 4  4
 4  4
[torch.DoubleTensor of size 2x2]


In [112]:
e


Out[112]:
 3  3
 3  3
[torch.DoubleTensor of size 2x2]


In [113]:
e:add(1)


Out[113]:
 4  4
 4  4
[torch.DoubleTensor of size 2x2]


In [114]:
e


Out[114]:
 4  4
 4  4
[torch.DoubleTensor of size 2x2]

Some miscellaneous function

norm This function returns norm of a tensor. This norm is the L2 norm.


In [118]:
g = torch.ones(2,2)

In [120]:
g


Out[120]:
 1  1
 1  1
[torch.DoubleTensor of size 2x2]


In [121]:
-- sqrt(1^2 + 1^2 + 1^2 + 1^2) = 2
g:norm()


Out[121]:
2	

In [122]:
g


Out[122]:
 1  1
 1  1
[torch.DoubleTensor of size 2x2]


In [ ]: