REIGNITE:重学 PyTorch¶
安装¶
首先在安装好 CUDA 11.x 的 Linux 电脑上安装 docker 和 nvidia-docker 和 docker-compose v2。
使用 docker 的好处是可以连 CUDA 版本一起放进虚拟环境。
然后随便在哪新建一个文件夹,随便起名,比如 torch_env
然后 cd torch_env,将以下内容写进 Dockerfile
FROM pytorch/pytorch:1.11.0-cuda11.3-cudnn8-runtime
# apt 换源
USER root
RUN sed -i "s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list &&\
sed -i "s@http://.*security.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list
# 接收 cudatools 的公钥,信任 CUDA 更新包,如果出现因为 cuda 更新的错误就将这句反注释掉
# RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A4B469963BF863CC
# 安装 openssh
RUN apt-get update && apt-get install -y openssh-server --fix-missing
# pip 换源
RUN pip config set global.index-url https://mirrors.bfsu.edu.cn/pypi/web/simple
# 添加 SSH 公钥
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config &&\
echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config &&\
echo "AuthorizedKeysFile .ssh/authorized_keys" >> /etc/ssh/sshd_config &&\
/etc/init.d/ssh restart &&\
mkdir -p ~/.ssh &&\
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMYZoO4AcJo32+7D8n7/JRWOMEU0KB/J8w4HuJ01GVSD min@DESKTOP-7BOCHVM" > ~/.ssh/authorized_keys
# >>> 在这里可以进行 pip install 等软件包安装
# 开启容器内 SSH 访问
ENTRYPOINT ["/usr/sbin/sshd", "-D"]
再将以下内容写进 docker-compose.yml
version: "3"
services:
jupyter:
restart: always
# image: ufoym/deepo:all-jupyter-py36-cu111
build: "."
container_name: jupyter-all
ports:
- "8822:22"
shm_size: "32gb" # PyTorch 多线程加载数据
volumes:
- "$HOME:$HOME"
deploy:
resources:
reservations:
devices:
- capabilities: ["gpu"] # NVIDIA GPU支持
最后
docker compose up -d
定义¶
array_like 指 list, tuple, NumPy ndarray, 标量等数据类型
- a tensor
- a NumPy array
- a DLPack capsule
- an object that implements Python’s buffer protocol
- a scalar
- a sequence of scalars
重新认识 Tensor¶
Tensor 是什么¶
创建一个 Tensor¶
- 使用
torch.tensor()从已有数据创建 Tensor - 使用
torch.*Tensor Creation Ops 创建特定形状、初始内容和数据类型的 Tensor (see Creation Ops). - 使用
torch.*_like创建形状等同于当前某个 Tensor 的具有特定内容和数据类型的 Tensor (see Creation Ops). - 使用
tensor.new_*创建与当前某 Tensor 具有相同数据类型的 Tensor
torch.tensor¶
创建一个没有 Autograd 记录的 leaf tensor,不会与原对象共享内存。
接收一个 data: array_like 参数,作为原始数据。如果没有附加参数,从 data 中推断类型信息。
但从 tensor 创建 tensor 应该使用 Tensor.clone()、Tensor.detach()(返回一个从当前计算图中脱离的 tensor,但返回的 tensor 与之共享 storage,所以不应该使用 inplace 操作进行改变)、Tensor.requires_grad_()。
torch.asarray¶
与 torch.tensor 不同的是 asarray ,如果原对象是 ndarray、DLPack capsule、实现了 Python buffer protocol 的对象,则新 Tensor 会与原对象共享内存。
torch.as_tensor¶
与上述不同的是,如果原对象是 tensor,则会最大程度保留 autograd 历史。
torch.from_numpy¶
一定会与原对象共享内存,不支持 resize 操作
其他不太常用的还有 from_dlpack、frombuffer 等。
统一初始化¶
zerosonesempty
以上三者 API 原型相同
a = torch.zeros(1, 2, 3, 4, 5)
a.shape # torch.Size([1, 2, 3, 4, 5])
full
def torch.full(size, fill_value, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) -> Tensor: ...
第一个参数是一个 shape_like 对象,第二个是统一的初始值。
a = torch((1, 2, 3), 42)
eye
单位矩阵,谐音 Identity 里的 I。
def torch.eye(n, m=None, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) -> Tensor
看原型应该能看懂是啥意思
a = torch.eye(3)
b = torch.eye(3, 4)
创建类似现有 Tensor 的 Tensor¶
zeros_likeones_likeempty_likefull_likeheaviside
前四个怎么用猜都猜得出来
第五个,
范围创建¶
在 PyTorch 1.11 被 depreciate 了,不要用了range:创建有 \(\left\lfloor \frac{\text { end-start }}{\text { step }}\right\rfloor+1\) 个元素的一维 Tensor,返回 Tensor 的 dtype 为float32arange:创建有 \(\left\lceil\frac{\text { end-start }}{\text { step }}\right\rceil\) 个元素的一维 Tensor,返回 Tensor 的 dtype 为int64linspace:创建有 \(\mathrm{step}\) 个元素的 Tensor,和 MATLAB 中同名函数同义logspace:\((\mathrm{base} ^{\text {start }}, \mathrm{base} ^{\left(\text {start+ } \frac{\text { end -start }}{\text { steps-1 }}\right)}, \ldots, \mathrm{base} ^{\left(\text {start+(steps-2)* } \frac{\text { end }-\text { start }}{\text { steps }^{-1}}\right)}, \mathrm{base} \left.^{\text {end }}\right)\)
特殊的 Tensor¶
meta tensor只有形状而没有内容,用于???- sparce tensor 基于稀疏矩阵,类似
scipy的sparce_matrix,可用于图神经网络 - quantized tensor 将 float32 量化为 uint8 而提高推理性能
- complex tensor 复数 Tensor,提供了
real和image两个属性 - named tensor 带有命名的 Tensor,设计的初衷是用“命名”来跟踪维度,在建图时发现维度不匹配问题,而不是像现在一样
RuntimeException。
用 Tensor 的 new_* 方法创建 Tensor¶
new_tensornew_fullnew_emptynew_onesnew_zeros
new 出的 Tensor 与原 Tensor 有相同的 dtype 和 device。
tensor = torch.ones((2,), dtype=torch.float64)
tensor.new_full((3, 4), 3.141592)
一生二二生三三生万物
随机初始化¶
torch.rand()值初始化为 \([0, 1)\) 之间均匀分布torch.rand_like()torch.randn()值初始化为服从 \(\mathcal{N}(0, 1)\) 的正态分布torch.randn_like()torch.randint()值为[low, high)之间的随机torch.int64torch.randint_like()torch.randperm()返回一个 \(1\ldots n\) 的随机排列的一维 Tensor,dtype默认torch.int64
Tensor 的基本属性(从实现的角度)¶
Tensor 是线性计算中最常见的数据结构。
- 维数:
ndim:返回一个Pythonint对象,表示维度 - 形状:
shape:返回一个torch.Shape对象,表示每个维度的大小 - 访问步长:
stride():返回一个tuple,为维度的stride。 - storage:
storage():返回一个torch.Storage的子类,为torch的底层一维存储 - 数据类型:
dtype:返回一个torch.dtype对象,表示这个tensor中数据的类型 - 数据内容:直接访问或者
b.data,可以像数组一样操作,具体访问方式在后文介绍。 - 设备:
device:返回一个torch.device对象 - 梯度:
grad:如果这个 Tensor 有 grad 返回之;如果没有返回 None,具体会在 Autograd 一节中详细介绍。 layout(beta)对于 dense tensor 等价于stride,对于sparse COO tensor处于测试阶段memory_format:规定数据的存储和访问方式,主要有contiguous_format和channel_last两种。torch.contiguous_format:默认表现,Tensor is or will be allocated in dense non-overlapping memory. Strides represented by values in decreasing order.torch.channels_last: Tensor is or will be allocated in dense non-overlapping memory. Strides represented by values instrides[0] > strides[2] > strides[3] > strides[1] == 1aka NHWC order.torch.preserve_format:Used in functions likecloneto preserve the memory format of the input tensor. If input tensor is allocated in dense non-overlapping memory, the output tensor strides will be copied from the input. Otherwise output strides will followtorch.contiguous_format
目前能想到的就是这些。
Tensor 的数据类型¶
直接摘抄 PyTorch 官网的解释如下:难得认真地分个类画个表
| Data type | dtype | CPU tensor | GPU tensor |
|---|---|---|---|
| 32-bit floating point | torch.float32 or torch.float |
torch.FloatTensor |
torch.cuda.FloatTensor |
| 64-bit floating point | torch.float64 or torch.double |
torch.DoubleTensor |
torch.cuda.DoubleTensor |
| 16-bit floating point 1 | torch.float16 or torch.half |
torch.HalfTensor |
torch.cuda.HalfTensor |
| 16-bit floating point 2 | torch.bfloat16 |
torch.BFloat16Tensor |
torch.cuda.BFloat16Tensor |
| 32-bit complex | torch.complex32 or torch.chalf |
||
| 64-bit complex | torch.complex64 or torch.cfloat |
||
| 128-bit complex | torch.complex128 or torch.cdouble |
||
| 8-bit integer (unsigned) | torch.uint8 |
torch.ByteTensor |
torch.cuda.ByteTensor |
| 8-bit integer (signed) | torch.int8 |
torch.CharTensor |
torch.cuda.CharTensor |
| 16-bit integer (signed) | torch.int16 or torch.short |
torch.ShortTensor |
torch.cuda.ShortTensor |
| 32-bit integer (signed) | torch.int32 or torch.int |
torch.IntTensor |
torch.cuda.IntTensor |
| 64-bit integer (signed) | torch.int64 or torch.long |
torch.LongTensor |
torch.cuda.LongTensor |
| Boolean | torch.bool |
torch.BoolTensor |
torch.cuda.BoolTensor |
| quantized 8-bit integer (unsigned) | torch.quint8 |
torch.ByteTensor |
/ |
| quantized 8-bit integer (signed) | torch.qint8 |
torch.CharTensor |
/ |
| quantized 32-bit integer (signed) | torch.qint32 |
torch.IntTensor |
/ |
| quantized 4-bit integer (unsigned) 3 | torch.quint4x2 |
torch.ByteTensor |
/ |
相同设备上,不同数据类型的实数 Tensor 进行计算时遵循类似 C 语言的变量类型提升规则,而不同设备上的 Tensor 不能直接计算。
Tensor 的操作¶
半天才到这一步,好戏还在后头呢。