In tutorial 1, we reviewed basics of Python and how Numpy extends vanilla Python for many tasks in scientific computing.

In this tutorial, we will go over two libraries, Matplotlib for data visualization and PyTorch for machine learning.

Matplotlib is a plotting library. This section gives a brief introduction to the `matplotlib.pyplot`

module, which provides a plotting system similar to that of MATLAB.

In [1]:

```
import numpy as np
import matplotlib.pyplot as plt
```

The most important function in `matplotlib.pyplot`

is `plot`

, which allows you to plot 2D data. Here is a simple example:

In [2]:

```
# Compute the x and y coordinates for points on a sine curve
x = np.arange(0, 3 * np.pi, 0.1)
y = np.sin(x)
# Plot the points using matplotlib
plt.plot(x, y)
```

Out[2]:

With just a little bit of extra work we can easily plot multiple lines at once, and add a title, legend, and axis labels:

In [3]:

```
y_sin = np.sin(x)
y_cos = np.cos(x)
# Can plot multiple graphs
plt.plot(x, y_sin)
plt.plot(x, y_cos)
# Set x and y label
plt.xlabel('x axis label')
plt.ylabel('y axis label')
# Set title and legend
plt.title('Sine and Cosine')
plt.legend(['Sine', 'Cosine'])
```

Out[3]:

You can plot different things in the same figure using the subplot function. Here is an example:

In [4]:

```
# Compute the x and y coordinates for points on sine and cosine curves
x = np.arange(0, 3 * np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)
# Set up a subplot grid that has height 2 and width 1.
# This sets the first such subplot as active.
plt.subplot(2, 1, 1)
# Make the first plot
plt.plot(x, y_sin)
plt.title('Sine')
# Set the second subplot as active
plt.subplot(2, 1, 2)
# Make the second plot.
plt.plot(x, y_cos)
plt.title('Cosine')
# Show the figure.
plt.show()
```

`imshow`

function from `pyplot`

module can be used to show images. For example:

In [5]:

```
img = plt.imread('cute-kittens.jpg')
print(img)
```

In [6]:

```
# Show the original image
plt.imshow(img) # Similar to plt.plot but for image
plt.show()
```

Note that each cells in an image is composed of 3 color channels (i.e. RGB color). Often the last axis is used for color channels, in the order of red, green, and blue.

In [7]:

```
print(img.shape) # 460 width x 276 height x RGB (3 channels)
```

In [8]:

```
# Displaying only red color channel
plt.imshow(img[:, :, 0])
plt.show()
```

PyTorch is a Python-based scientific computing package. PyTorch is currently, along with Tensorflow, one of the most popular machine learning library.

PyTorch, at its core, is similar to Numpy in a sense that they both

- try to make it easier to write codes for scientific computing
- achieve improved performance over vanilla Python by leveraging highly optimized C back-end.

However, compare to Numpy, PyTorch offers much better GPU support and provides many high-level features for machine learning. Technically, Numpy can be used to perform almost every thing PyTorch does. However, Numpy would be a lot slower than PyTorch, especially with CUDA GPU, and it would take more effort to write machine learning related code compared to using PyTorch.

Mathematically speaking, tensor is a mathematical object for representing multi-dimensional arrays and tensor can be thought of as generalization of vectors and matrices. Tensor extends vector(1-D grid of numbers) and matrix(2-D grid of numbers) to represent any dimensional structure.

In PyTorch, `tensor`

is similar to Numpy's `ndarray`

but can be used on a GPU to accelerate computing.

`tensor`

can be created using initialization functions, similar to ones for `ndarray`

.

In [9]:

```
import torch
```

In [10]:

```
x = torch.empty(5, 3)
print(x)
```

In [11]:

```
x = torch.rand(5, 3)
print(x)
```

In [12]:

```
x = torch.zeros(5, 3, dtype=torch.long) # explicitely specify data type
print(x)
```

`tensor`

can also be created from array-like data such as `ndarray`

or other `tensors`

In [13]:

```
x = torch.tensor([5.5, 3]) # From Python list
print(x)
```

In [14]:

```
np_array = np.arange(6).reshape((2, 3))
torch_tensor = torch.from_numpy(np_array) # From ndarray
print(np_array)
print(torch_tensor)
np_array_2 = torch_tensor.numpy() # Back to ndarray from tensor
print(np_array_2)
```

Operations on `tensor`

use similar syntax as in Numpy

In [15]:

```
x = torch.ones(5, 3)
print(x)
x *= 2
print(x)
```

In [16]:

```
y = torch.rand(5, 3)
print(y)
print(x + y)
print(x * y)
```

In [17]:

```
# Using different syntax for the same operations above
print(torch.add(x, y))
```

In [18]:

```
# Inplace operation
x.add_(y)
print(x)
```

In [19]:

```
# Using the same indexing syntax from Python list and Numpy
print(x[1:4, :])
```

In [20]:

```
print(x.shape) # Similar to Numpy
```