# 3.1 PyTorch 学习

本节主要是学习PyTorch相关的学习，主要是基础的学习路线，包括简单的实例笔记等。

* [x] Edit By Porter, 积水成渊,蛟龙生焉。

帮助文档见[PyTorch官网](https://pytorch.org/tutorials/)

## 一、张量(tensor)和变量(Variable)

PyTorch的官方介绍是一个拥有强力GPU加速的张量和动态构建网络的库，其主要构建是张量，所以可以把PyTorch当做Numpy来用，Pytorch的很多操作好比Numpy都是类似的，但是其能够在GPU上运行，所以有着比Numpy快很多倍的速度。

```python
import torch
import numpy as np

numpy_tensor = np.random.randn(3, 4)

pytorch_tensor1 = torch.Tensor(numpy_tensor)
pytorch_tensor2 = torch.from_numpy(numpy_tensor)

print(pytorch_tensor1)
print(pytorch_tensor2)
```

输出结果：

```python
pytorch_tensor1:

tensor([[ 1.3511,  0.2016, -0.9728,  0.7997],
        [-1.0706, -0.0768, -1.3627, -0.8809],
        [-0.6040, -0.0030,  0.4871,  0.6634]])
```

```python
pytorch_tensor2:

tensor([[ 1.3511,  0.2016, -0.9728,  0.7997],
        [-1.0706, -0.0768, -1.3627, -0.8809],
        [-0.6040, -0.0030,  0.4871,  0.6634]], dtype=torch.float64)
```

使用以上两种方法进行转换的时候，会直接将Numpy ndarray的数据类型转换为对应的Pytorch Tensor数据类型,同时我们也可以使用下面的方法将pytorch tensor转换为numpy ndarray

```python
import torch
import numpy as np

numpy_tensor = np.random.randn(3, 4)
pytorch_tensor1 = torch.Tensor(numpy_tensor)
pytorch_tensor2 = torch.from_numpy(numpy_tensor)

# 如果pytorch tensor在cpu上
numpy_array1 = pytorch_tensor1.numpy()
numpy_array2 = pytorch_tensor2.cpu().numpy()

print(numpy_array1)
print(numpy_array2)
```

```python
numpy_array1:

[[ 0.9646071   1.0680387  -1.4145772  -1.1733457 ]
 [ 0.14683424  0.15183815  0.3256755   2.5129247 ]
 [-1.0027096   0.02551154 -0.60790646 -0.22400694]]

 numpy_array2:

[[-2.09633392 -2.08986247  0.02169762  0.15833546]
 [ 1.24929483 -1.3953018   1.03153148 -0.06309232]
 [ 0.24348084 -1.42512446  1.45863934  0.92882537]]
```

需要注意GPU上的Tensor不能直接转换为Numpy ndarray，需要使用.cpu()先将GPU上的Tensor转到CPU上 PyTorch Tensor 使用GPU加速可以使用下面两种方法将Tensor放到GPU上.

```python
# 第一种方式是定义cuda数据类型
dtype = torch.cuda.FloatTensor
gpu_tensor = torch.randn(10,20).type(dtype)

# 第二种方式更简单，推荐使用
gpu_tensor = torch.randn(10,20).cuda(0) # 将tensor放到第一个GPU上
gpu_tensor = torch.randn(10,20).cuda(1) # 将tensor放到第二个GPU上
```

* 使用第一种方式将tensor放到GPU上的时候会将数据类型转换成定义的类型。
* 而使用第二种方式能够直接将tensor放到GPU上，类型跟之前保持一致。

> 推荐在定义tensor的时候就明确数据类型，然后直接使用第二种方法将tensor放到GPU上

我的测试代码：

```python
import torch
import numpy as np

numpy_tensor = np.random.randn(3, 4)
pytorch_tensor1 = torch.Tensor(numpy_tensor)
pytorch_tensor2 = torch.from_numpy(numpy_tensor)

# 如果pytorch tensor在cpu上
numpy_array1 = pytorch_tensor1.numpy()
numpy_array2 = pytorch_tensor2.cpu().numpy()

print(numpy_array1)
print(numpy_array2)

print("输出数据模式1：")
# 第一种方式是定义cuda数据类型
dtype = torch.cuda.FloatTensor
gpu_tensor1 = torch.randn(3,4).type(dtype)

print(gpu_tensor1)

gpu_tensor2 = torch.randn(3,4).cuda(0) # 将tensor放到第一个GPU上

print("输出数据模式2：")
print(gpu_tensor2)
```

## 二、Torch的数据类型

2.1 torch.Tensor

torch.Tensor是一种包含单一数据类型元素的多维矩阵。

Torch定义了[七种CPU tensor类型和八种GPU tensor类型](https://pytorch-cn.readthedocs.io/zh/latest/package_references/Tensor/)：

| Data tyoe                | CPU tensor         | GPU tensor              |
| ------------------------ | ------------------ | ----------------------- |
| 32-bit floating point    | torch.FloatTensor  | torch.cuda.FloatTensor  |
| 64-bit floating point    | torch.DoubleTensor | torch.cuda.DoubleTensor |
| 16-bit floating point    | N/A                | torch.cuda.HalfTensor   |
| 8-bit integer (unsigned) | torch.ByteTensor   | torch.cuda.ByteTensor   |
| 8-bit integer (signed)   | torch.CharTensor   | torch.cuda.CharTensor   |
| 16-bit integer (signed)  | torch.ShortTensor  | torch.cuda.ShortTensor  |
| 32-bit integer (signed)  | torch.IntTensor    | torch.cuda.IntTensor    |
| 64-bit integer (signed)  | torch.LongTensor   | torch.cuda.LongTensor   |

torch.Tensor是默认的tensor类型（torch.FlaotTensor）的简称。

一个张量tensor可以从Python的list或序列构建：

```python
>>> torch.FloatTensor([[1, 2, 3], [4, 5, 6]])
1 2 3
4 5 6
[torch.FloatTensor of size 2x3]
```

一个空张量tensor可以通过规定其大小来构建：

```python
>>> torch.IntTensor(2, 4).zero_()
0 0 0 0
0 0 0 0
[torch.IntTensor of size 2x4]
```

## 三、 Torch 的多种数学操作

### 3.1 Torch

包[torch](https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch/)包含了多维张量的数据结构以及基于其上的多种数学操作。另外，它也提供了多种工具，其中一些可以更有效地对张量和任意类型进行序列化。

它有CUDA 的对应实现，可以在NVIDIA GPU上进行张量运算(计算能力>=2.0)。

### 3.2 张量 Tensors

#### 3.2.1 torch.is\_tensor\[source]

```python
torch.is_tensor(obj)
```

如果obj 是一个pytorch张量，则返回True

> 参数： obj (Object) – 判断对象

#### 3.2.2 torch.is\_storage

```python
torch.is_storage(obj)
```

如何obj 是一个pytorch storage对象，则返回True

> 参数： input (Object) – 判断对象

#### 3.2.3 torch.set\_default\_tensor\_type\[source]

```python
torch.set_default_tensor_type(t)
```

#### 3.2.4 torch.numel

```python
torch.numel(input)->int
```

返回input 张量中的元素个数

例子:

```python
>>> a = torch.randn(1,2,3,4,5)
>>> torch.numel(a)
120
>>> a = torch.zeros(4,4)
>>> torch.numel(a)
16
```

#### 3.2.5 torch.set\_printoptions\[source]

```python
torch.set_printoptions(precision=None, threshold=None, edgeitems=None, linewidth=None, profile=None)
```

设置打印选项。 完全参考自 Numpy。

**参数:**

* precision – 浮点数输出的精度位数 (默认为8 )
* threshold – 阈值，触发汇总显示而不是完全显示(repr)的数组元素的总数 （默认为1000）
* edgeitems – 汇总显示中，每维（轴）两端显示的项数（默认值为3）
* linewidth – 用于插入行间隔的每行字符数（默认为80）。Thresholded matricies will ignore this parameter.
* profile – pretty打印的完全默认值。 可以覆盖上述所有选项 (默认为short, full)

### 3.3 创建操作 Creation Ops

#### 3.3.1 torch.eye

```python
torch.eye(n, m=None, out=None)
```

返回一个2维张量，对角线位置全1，其它位置全0

**参数:**

* n (int ) – 行数
* m (int, optional) – 列数.如果为None,则默认为n
* out (Tensor, optinal) - Output tensor

返回值: 对角线位置全1，其它位置全0的2维张量

返回值类型: Tensor

例子:

```python
>>> torch.eye(3)
 1  0  0
 0  1  0
 0  0  1
[torch.FloatTensor of size 3x3]
```

更多[参考Pytorch 文档](https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch/)
