<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 <img src="../Pierian-Data-Logo.PNG">
<br>
<strong><center>Copyright 2019. Created by Jose Marcial Portilla.</center></strong>
Tensor Basics
This section covers
Step1:  Confirm you're using PyTorch version 1.1.0
Step2:  Converting NumPy arrays to PyTorch tensors
A <a href='https
Step3:  Here <tt>torch.DoubleTensor</tt> refers to 64-bit floating point data.
<h2><a href='https
Step4:  Class constructors
<a href='https
Step5:  Creating tensors from scratch
Uninitialized tensors with <tt>.empty()</tt>
<a href='https
Step6:  Initialized tensors with <tt>.zeros()</tt> and <tt>.ones()</tt>
<a href='https
Step7:  Tensors from ranges
<a href='https
Step8:  Tensors from data
<tt>torch.tensor()</tt> will choose the dtype based on incoming data
Step9:  Alternatively you can set the type by the tensor method used.
For a list of tensor types visit https
Step10:  You can also pass the dtype in as an argument. For a list of dtypes visit https
Step11:  Changing the dtype of existing tensors
Don't be tempted to use <tt>x = torch.tensor(x, dtype=torch.type)</tt> as it will raise an error about improper use of tensor cloning.<br>
Instead, use the tensor <tt>.type()</tt> method.
Step12:  Random number tensors
<a href='https
Step13:  Random number tensors that follow the input size
<a href='https
Step14:  The same syntax can be used with<br>
<a href='https
Step15:  Setting the random seed
<a href='https
Step16:  Tensor attributes
Besides <tt>dtype</tt>, we can look at other <a href='https
Step17:  PyTorch supports use of multiple <a href='https
<ASSISTANT_TASK:>
Python Code:
import torch
import numpy as np
Explanation: <img src="../Pierian-Data-Logo.PNG">
<br>
<strong><center>Copyright 2019. Created by Jose Marcial Portilla.</center></strong>
Tensor Basics
This section covers:
* Converting NumPy arrays to PyTorch tensors
* Creating tensors from scratch
Perform standard imports
End of explanation
torch.__version__
Explanation: Confirm you're using PyTorch version 1.1.0
End of explanation
arr = np.array([1,2,3,4,5])
print(arr)
print(arr.dtype)
print(type(arr))
x = torch.from_numpy(arr)
# Equivalent to x = torch.as_tensor(arr)
print(x)
# Print the type of data held by the tensor
print(x.dtype)
# Print the tensor object type
print(type(x))
print(x.type()) # this is more specific!
arr2 = np.arange(0.,12.).reshape(4,3)
print(arr2)
x2 = torch.from_numpy(arr2)
print(x2)
print(x2.type())
Explanation: Converting NumPy arrays to PyTorch tensors
A <a href='https://pytorch.org/docs/stable/tensors.html'><strong><tt>torch.Tensor</tt></strong></a> is a multi-dimensional matrix containing elements of a single data type.<br>
Calculations between tensors can only happen if the tensors share the same dtype.<br>
In some cases tensors are used as a replacement for NumPy to use the power of GPUs (more on this later).
End of explanation
# Using torch.from_numpy()
arr = np.arange(0,5)
t = torch.from_numpy(arr)
print(t)
arr[2]=77
print(t)
# Using torch.tensor()
arr = np.arange(0,5)
t = torch.tensor(arr)
print(t)
arr[2]=77
print(t)
Explanation: Here <tt>torch.DoubleTensor</tt> refers to 64-bit floating point data.
<h2><a href='https://pytorch.org/docs/stable/tensors.html'>Tensor Datatypes</a></h2>
<table style="display: inline-block">
<tr><th>TYPE</th><th>NAME</th><th>EQUIVALENT</th><th>TENSOR TYPE</th></tr>
<tr><td>32-bit integer (signed)</td><td>torch.int32</td><td>torch.int</td><td>IntTensor</td></tr>
<tr><td>64-bit integer (signed)</td><td>torch.int64</td><td>torch.long</td><td>LongTensor</td></tr>
<tr><td>16-bit integer (signed)</td><td>torch.int16</td><td>torch.short</td><td>ShortTensor</td></tr>
<tr><td>32-bit floating point</td><td>torch.float32</td><td>torch.float</td><td>FloatTensor</td></tr>
<tr><td>64-bit floating point</td><td>torch.float64</td><td>torch.double</td><td>DoubleTensor</td></tr>
<tr><td>16-bit floating point</td><td>torch.float16</td><td>torch.half</td><td>HalfTensor</td></tr>
<tr><td>8-bit integer (signed)</td><td>torch.int8</td><td></td><td>CharTensor</td></tr>
<tr><td>8-bit integer (unsigned)</td><td>torch.uint8</td><td></td><td>ByteTensor</td></tr></table>
Copying vs. sharing
<a href='https://pytorch.org/docs/stable/torch.html#torch.from_numpy'><strong><tt>torch.from_numpy()</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.as_tensor'><strong><tt>torch.as_tensor()</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.tensor'><strong><tt>torch.tensor()</tt></strong></a><br>
There are a number of different functions available for <a href='https://pytorch.org/docs/stable/torch.html#creation-ops'>creating tensors</a>. When using <a href='https://pytorch.org/docs/stable/torch.html#torch.from_numpy'><strong><tt>torch.from_numpy()</tt></strong></a> and <a href='https://pytorch.org/docs/stable/torch.html#torch.as_tensor'><strong><tt>torch.as_tensor()</tt></strong></a>, the PyTorch tensor and the source NumPy array share the same memory. This means that changes to one affect the other. However, the <a href='https://pytorch.org/docs/stable/torch.html#torch.tensor'><strong><tt>torch.tensor()</tt></strong></a> function always makes a copy.
End of explanation
data = np.array([1,2,3])
a = torch.Tensor(data)  # Equivalent to cc = torch.FloatTensor(data)
print(a, a.type())
b = torch.tensor(data)
print(b, b.type())
c = torch.tensor(data, dtype=torch.long)
print(c, c.type())
Explanation: Class constructors
<a href='https://pytorch.org/docs/stable/tensors.html'><strong><tt>torch.Tensor()</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/tensors.html'><strong><tt>torch.FloatTensor()</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/tensors.html'><strong><tt>torch.LongTensor()</tt></strong></a>, etc.<br>
There's a subtle difference between using the factory function <font color=black><tt>torch.tensor(data)</tt></font> and the class constructor <font color=black><tt>torch.Tensor(data)</tt></font>.<br>
The factory function determines the dtype from the incoming data, or from a passed-in dtype argument.<br>
The class constructor <tt>torch.Tensor()</tt>is simply an alias for <tt>torch.FloatTensor(data)</tt>. Consider the following:
End of explanation
x = torch.empty(4, 3)
print(x)
Explanation: Creating tensors from scratch
Uninitialized tensors with <tt>.empty()</tt>
<a href='https://pytorch.org/docs/stable/torch.html#torch.empty'><strong><tt>torch.empty()</tt></strong></a> returns an <em>uninitialized</em> tensor. Essentially a block of memory is allocated according to the size of the tensor, and any values already sitting in the block are returned. This is similar to the behavior of <tt>numpy.empty()</tt>.
End of explanation
x = torch.zeros(4, 3, dtype=torch.int64)
print(x)
Explanation: Initialized tensors with <tt>.zeros()</tt> and <tt>.ones()</tt>
<a href='https://pytorch.org/docs/stable/torch.html#torch.zeros'><strong><tt>torch.zeros(size)</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.ones'><strong><tt>torch.ones(size)</tt></strong></a><br>
It's a good idea to pass in the intended dtype.
End of explanation
x = torch.arange(0,18,2).reshape(3,3)
print(x)
x = torch.linspace(0,18,12).reshape(3,4)
print(x)
Explanation: Tensors from ranges
<a href='https://pytorch.org/docs/stable/torch.html#torch.arange'><strong><tt>torch.arange(start,end,step)</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.linspace'><strong><tt>torch.linspace(start,end,steps)</tt></strong></a><br>
Note that with <tt>.arange()</tt>, <tt>end</tt> is exclusive, while with <tt>linspace()</tt>, <tt>end</tt> is inclusive.
End of explanation
x = torch.tensor([1, 2, 3, 4])
print(x)
print(x.dtype)
print(x.type())
Explanation: Tensors from data
<tt>torch.tensor()</tt> will choose the dtype based on incoming data:
End of explanation
x = torch.FloatTensor([5,6,7])
print(x)
print(x.dtype)
print(x.type())
Explanation: Alternatively you can set the type by the tensor method used.
For a list of tensor types visit https://pytorch.org/docs/stable/tensors.html
End of explanation
x = torch.tensor([8,9,-3], dtype=torch.int)
print(x)
print(x.dtype)
print(x.type())
Explanation: You can also pass the dtype in as an argument. For a list of dtypes visit https://pytorch.org/docs/stable/tensor_attributes.html#torch.torch.dtype<br>
End of explanation
print('Old:', x.type())
x = x.type(torch.int64)
print('New:', x.type())
Explanation: Changing the dtype of existing tensors
Don't be tempted to use <tt>x = torch.tensor(x, dtype=torch.type)</tt> as it will raise an error about improper use of tensor cloning.<br>
Instead, use the tensor <tt>.type()</tt> method.
End of explanation
x = torch.rand(4, 3)
print(x)
x = torch.randn(4, 3)
print(x)
x = torch.randint(0, 5, (4, 3))
print(x)
Explanation: Random number tensors
<a href='https://pytorch.org/docs/stable/torch.html#torch.rand'><strong><tt>torch.rand(size)</tt></strong></a> returns random samples from a uniform distribution over [0, 1)<br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.randn'><strong><tt>torch.randn(size)</tt></strong></a> returns samples from the "standard normal" distribution [σ = 1]<br>
&nbsp;&nbsp;&nbsp;&nbsp;Unlike <tt>rand</tt> which is uniform, values closer to zero are more likely to appear.<br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.randint'><strong><tt>torch.randint(low,high,size)</tt></strong></a> returns random integers from low (inclusive) to high (exclusive)
End of explanation
x = torch.zeros(2,5)
print(x)
x2 = torch.randn_like(x)
print(x2)
Explanation: Random number tensors that follow the input size
<a href='https://pytorch.org/docs/stable/torch.html#torch.rand_like'><strong><tt>torch.rand_like(input)</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.randn_like'><strong><tt>torch.randn_like(input)</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.randint_like'><strong><tt>torch.randint_like(input,low,high)</tt></strong></a><br> these return random number tensors with the same size as <tt>input</tt>
End of explanation
x3 = torch.ones_like(x2)
print(x3)
Explanation: The same syntax can be used with<br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.zeros_like'><strong><tt>torch.zeros_like(input)</tt></strong></a><br>
<a href='https://pytorch.org/docs/stable/torch.html#torch.ones_like'><strong><tt>torch.ones_like(input)</tt></strong></a>
End of explanation
torch.manual_seed(42)
x = torch.rand(2, 3)
print(x)
torch.manual_seed(42)
x = torch.rand(2, 3)
print(x)
Explanation: Setting the random seed
<a href='https://pytorch.org/docs/stable/torch.html#torch.manual_seed'><strong><tt>torch.manual_seed(int)</tt></strong></a> is used to obtain reproducible results
End of explanation
x.shape
x.size()  # equivalent to x.shape
x.device
Explanation: Tensor attributes
Besides <tt>dtype</tt>, we can look at other <a href='https://pytorch.org/docs/stable/tensor_attributes.html'>tensor attributes</a> like <tt>shape</tt>, <tt>device</tt> and <tt>layout</tt>
End of explanation
x.layout
Explanation: PyTorch supports use of multiple <a href='https://pytorch.org/docs/stable/tensor_attributes.html#torch-device'>devices</a>, harnessing the power of one or more GPUs in addition to the CPU. We won't explore that here, but you should know that operations between tensors can only happen for tensors installed on the same device.
End of explanation
<END_TASK>