正文开始:
目录:
Var:计图的基本数据类型
在计图 (Jittor) - 开源深度学习框架中,Var 类型是最基本的数据类型。计图的所有操作,都将基于 Var 进行。所以,学习并掌握 Var 是使用计图的第一步。请不要感到畏难,我们之所以引入 Var 类型,是为了优化深度学习的计算能力与效率,目的是方便您更加高效地进行开发,请您尽情享受。
简单来说,Var 是一种特殊的数据结构,它与数组和矩阵非常相似。您可简单理解为,一维 Var 即是数组,二维 Var 即是矩阵。在计图中,我们使用 Var 来编码模型的输入和输出,以及模型的参数。
计图的 Var 与 NumPy 的 ndarray 极其相似,但 Var 可以在 GPU 或其他专用硬件上运行,以加速计算。如果您熟悉 ndarray,您就会十分熟悉 Var 的相关操作。如果没有,请按照本教程,对 Var 类型进行快速演练。本教程已覆盖 Var 的基础操作,可以基本满足您的开发需求。如您想要更加深入了解并灵活使用 Var,敬请查阅我们的 API 文档。
首先,我们在 Python 中导入计图:
import jittor as jt
[i 0202 22:49:45.609290 80 compiler.py:847] Jittor(1.2.2.23) src: /home/llt/.local/lib/python3.7/site-packages/jittor [i 0202 22:49:45.611377 80 compiler.py:848] g++ at /usr/bin/g++ [i 0202 22:49:45.612691 80 compiler.py:849] cache_path: /home/llt/.cache/jittor/default/g++ [i 0202 22:49:45.626682 80 __init__.py:257] Found /usr/local/cuda/bin/nvcc(10.2.89) at /usr/local/cuda/bin/nvcc. [i 0202 22:49:45.697894 80 __init__.py:257] Found gdb(8.1.0) at /usr/bin/gdb. [i 0202 22:49:45.715477 80 __init__.py:257] Found addr2line(2.30) at /usr/bin/addr2line. [i 0202 22:49:45.768340 80 compiler.py:889] pybind_include: -I/usr/include/python3.7m -I/usr/local/lib/python3.7/dist-packages/pybind11/include [i 0202 22:49:45.792263 80 compiler.py:891] extension_suffix: .cpython-37m-x86_64-linux-gnu.so [i 0202 22:49:45.974687 80 __init__.py:169] Total mem: 62.78GB, using 16 procs for compiling. [i 0202 22:49:46.131834 80 jit_compiler.cc:21] Load cc_path: /usr/bin/g++ [i 0202 22:49:46.304843 80 init.cc:54] Found cuda archs: [75,] [i 0202 22:49:46.491697 80 __init__.py:257] Found mpicc(2.1.1) at /usr/bin/mpicc. [i 0202 22:49:46.549639 80 compiler.py:654] handle pyjt_include/home/llt/.local/lib/python3.7/site-packages/jittor/extern/mpi/inc/mpi_warper.h [i 0202 22:49:46.596179 80 compile_extern.py:287] Downloading nccl... [i 0202 22:49:46.683976 80 compile_extern.py:16] found /usr/local/cuda/include/cublas.h [i 0202 22:49:46.686093 80 compile_extern.py:16] found /usr/lib/x86_64-linux-gnu/libcublas.so [i 0202 22:49:47.293398 80 compile_extern.py:16] found /usr/local/cuda/include/cudnn.h [i 0202 22:49:47.294725 80 compile_extern.py:16] found /usr/local/cuda/lib64/libcudnn.so [i 0202 22:49:47.332204 80 compiler.py:654] handle pyjt_include/home/llt/.local/lib/python3.7/site-packages/jittor/extern/cuda/cudnn/inc/cudnn_warper.h [i 0202 22:49:49.192471 80 compile_extern.py:16] found /usr/local/cuda/include/curand.h [i 0202 22:49:49.193815 80 compile_extern.py:16] found /usr/local/cuda/lib64/libcurand.so
从 “普通数据” 导入生成 Var
data = [[1, 2],[3, 4],[5, 6]] # 生成一个普通的列表
print("data: \n", data)
var_data = jt.array(data) # 通过列表生成 Var
print("var_data: \n", var_data)
print("var_data的类型是:", type(var_data)) # 打印类型,可以发现生成的是jittor.Var
var_data = jt.Var(data) # 当然,jittor.Var也可以直接使用
data: [[1, 2], [3, 4], [5, 6]] var_data: jt.Var([[1 2] [3 4] [5 6]], dtype=int32) var_data的类型是: <class 'jittor.jittor_core.Var'>
从 “NumPy 数组” 导入生成 Var
import numpy as np
np_array = np.array([[1, 2],[3, 4],[5, 6]]) # 生成一个 NumPy 的数组
print("np_array: \n", np_array)
var_np_array=jt.array(np_array) # 通过 NumPy 的数组生成 Var
print("var_np_array: \n", var_np_array)
np_array: [[1 2] [3 4] [5 6]] var_np_array: jt.Var([[1 2] [3 4] [5 6]], dtype=int64)
从 “其他 Var 数据” 导入生成新的 Var
# 准备一个原始的 Var
var_data = jt.Var([[1, 2],[3, 4],[5, 6]])
# 生成一个形状相同,元素全为 0 的 Var
var_zeros = jt.zeros_like(var_data)
print("Zeros Var: \n", var_zeros)
# 生成一个形状相同,元素全为 1 的 Var
var_ones = jt.ones_like(var_data)
print("Ones Var: \n", var_ones)
# 生成一个形状相同,并指定元素值的 Var
var_full = jt.full_like(var_data, 5)
print("full Var: \n", var_full)
# 生成一个形状相同,元素为随机数的 Var
var_rand = jt.rand(var_data.shape, dtype=jt.float) # 重写元素数据类型
print("Random Var: \n", var_rand)
Zeros Var: jt.Var([[0 0] [0 0] [0 0]], dtype=int32) Ones Var: jt.Var([[1 1] [1 1] [1 1]], dtype=int32) full Var: jt.Var([[5 5] [5 5] [5 5]], dtype=int32) Random Var: jt.Var([[0.27817145 0.22768587] [0.71629596 0.78609395] [0.8813336 0.5737404 ]], dtype=float32)
生成一些特定的 Var
shape = (3,4,) # 指定将要生成 Var 的形状
rand_var = jt.rand(shape) # 生成元素为随机数的 Var
ones_var = jt.ones(shape) # 生成元素全为 1 的 Var
zeros_var = jt.zeros(shape) # 生成元素全为 0 的 Var
print("Random Var: \n", rand_var)
print("Ones Var: \n", ones_var)
print("Zeros Var: \n", zeros_var)
arange_var1 = jt.arange(9) # 生成元素为等差数组的一维 Var (只指定一个结束参数)
arange_var2 = jt.arange(0, 9, 2) # 生成元素为等差数组的一维 Var (指定起始、结束、步长)
print("Arange Var 1: \n", arange_var1)
print("Arange Var 2: \n", arange_var2)
Random Var: jt.Var([[0.8556649 0.16008338 0.5214482 0.98024756] [0.02092005 0.6033252 0.08662996 0.9897186 ] [0.20087309 0.07411052 0.57556623 0.54145 ]], dtype=float32) Ones Var: jt.Var([[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]], dtype=float32) Zeros Var: jt.Var([[0. 0. 0. 0.] [0. 0. 0. 0.] [0. 0. 0. 0.]], dtype=float32) Arange Var 1: jt.Var([0 1 2 3 4 5 6 7 8], dtype=int32) Arange Var 2: jt.Var([0 2 4 6 8], dtype=int32)
Var 的形状: 通过 var.shape 查看
var = jt.array([[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]])
print("Var: \n", var)
print("Shape of Var: \n", var.shape)
Var: jt.Var([[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]], dtype=int32) Shape of Var: [3,4,]
Var 的数据类型: 通过 var.dtype 查看
var = jt.ones((3,4))
print("Var: \n", var)
print("Datatype of Var: \n", var.dtype)
Var: jt.Var([[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]], dtype=float32) Datatype of Var: float32
重塑形状 Reshape
var1 = jt.arange(27) # 创建一个元素为 0-26 的一维 Var1
print("Var1: \n", var1)
print("Shape of Var1: \n", var1.shape)
var2 = var1.reshape(3,3,3) # 将 Var1 重塑形状为 3*3*3 的 Var2
print("Var2: \n", var2)
print("Shape of Var2: \n", var2.shape)
var3 = var2.reshape(3,-1) # 参数值为 -1,代表将根据其他形状参数自动计算出合适的值,再进行重塑形状
print("Var3: \n", var3)
print("Shape of Var3: \n", var3.shape)
Var1: jt.Var([ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26], dtype=int32) Shape of Var1: [27,] Var2: jt.Var([[[ 0 1 2] [ 3 4 5] [ 6 7 8]] [[ 9 10 11] [12 13 14] [15 16 17]] [[18 19 20] [21 22 23] [24 25 26]]], dtype=int32) Shape of Var2: [3,3,3,] Var3: jt.Var([[ 0 1 2 3 4 5 6 7 8] [ 9 10 11 12 13 14 15 16 17] [18 19 20 21 22 23 24 25 26]], dtype=int32) Shape of Var3: [3,9,]
转置 Transpose
# (提示:这里我们用 arange 初始化一个 0 到 11 的一维 Var,然后通过 reshape 将其转化为 4*3 的 Var1。)
var1 = jt.arange(12).reshape(4,3) # 创建一个 4*3 的二维矩阵 Var1
print("Var1: \n", var1)
print("Shape of Var1: \n", var1.shape)
var1_t = var1.transpose() # 矩阵转置成 3*4 的二维矩阵 Var1 Transpose
print("Var1 Transpose: \n", var1_t)
print("Shape of Var1 Transpose: \n", var1_t.shape)
Var1: jt.Var([[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11]], dtype=int32) Shape of Var1: [4,3,] Var1 Transpose: jt.Var([[ 0 3 6 9] [ 1 4 7 10] [ 2 5 8 11]], dtype=int32) Shape of Var1 Transpose: [3,4,]
var1 = jt.arange(24).reshape(2,3,4) # 创建一个 2*3*4 的三维 Var1
print("Var1: \n", var1) # 其含义为,第一个轴上 2 个元素;第二个轴上 3 个元素;第三个轴上 4 个元素。
print("Shape of Var1: \n", var1.shape)
var1_t = var1.transpose(0,2,1) # 根据轴索引转置。三维轴索引为 0,1,2,调整索引顺序即代表对应轴调整顺序。
print("Var1 Transpose: \n", var1_t) # 从默认的“0,1,2”调整为“0,2,1”,即代表后两轴进行互换。
print("Shape of Var1 Transpose: \n", var1_t.shape) # 互换后,第一个轴上 2 个元素;第二个轴上 4 个元素;第三个轴上 3 个元素。
Var1: jt.Var([[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [[12 13 14 15] [16 17 18 19] [20 21 22 23]]], dtype=int32) Shape of Var1: [2,3,4,] Var1 Transpose: jt.Var([[[ 0 4 8] [ 1 5 9] [ 2 6 10] [ 3 7 11]] [[12 16 20] [13 17 21] [14 18 22] [15 19 23]]], dtype=int32) Shape of Var1 Transpose: [2,4,3,]
切片和索引
var = jt.arange(9).reshape(3,3) # 初始化一个 3*3 的 Var
print(1, "Var: \n", var)
var[:,1] = 0 # 利用切片,将任意行的第二个元素(即第二列)重置为 0
print(2, "Var: \n", var)
line_2 = var[1] # 利用索引,拿到第二行元素
print(3, "Line 2 in Var: \n", line_2)
1 Var: jt.Var([[0 1 2] [3 4 5] [6 7 8]], dtype=int32) 2 Var: jt.Var([[0 0 2] [3 0 5] [6 0 8]], dtype=int32) 3 Line 2 in Var: jt.Var([3 0 5], dtype=int32)
原地操作
在计图中,我们使用 assign() 函数实现在原变量上直接操作。
var0 = jt.arange(6).reshape(2,3) # 初始化一个 2*3 的 Var0
print(0, "Var0: \n", var0) # 打印查看 Var0
var1 = var0.add(1) # 使 Var1 等于 Var0 上每个元素加 1
print(1, "Var1: \n", var1) # 打印发现 Var1 赋值成功
print(1, "Var0: \n", var0) # 但 Var0 并没有发生变化
var2 = var0.assign(var0.add(1)) # 利用 assign() 函数,使 Var2 等于 Var0 上每个元素加 1
print(2, "Var2: \n", var2) # 打印发现 Var2 赋值成功
print(2, "Var0: \n", var0) # 同时 Var0 也发生了改变
var3 = var0.transpose() # 使 Var3 等于 Var0 的转置
print(3, "Var3: \n", var3) # 打印发现 Var3 赋值成功
print(3, "Var0: \n", var0) # 但 Var0 并没有发生变化
var4 = var0.assign(var0.transpose()) # 利用 assign() 函数,使 Var4 等于 Var0 的转置
print(4, "Var4: \n", var4) # 打印发现 Var4 赋值成功
print(4, "Var0: \n", var0) # 同时 Var0 也发生了改变
0 Var0: jt.Var([[0 1 2] [3 4 5]], dtype=int32) 1 Var1: jt.Var([[1 2 3] [4 5 6]], dtype=int32) 1 Var0: jt.Var([[0 1 2] [3 4 5]], dtype=int32) 2 Var2: jt.Var([[1 2 3] [4 5 6]], dtype=int32) 2 Var0: jt.Var([[1 2 3] [4 5 6]], dtype=int32) 3 Var3: jt.Var([[1 4] [2 5] [3 6]], dtype=int32) 3 Var0: jt.Var([[1 2 3] [4 5 6]], dtype=int32) 4 Var4: jt.Var([[1 4] [2 5] [3 6]], dtype=int32) 4 Var0: jt.Var([[1 4] [2 5] [3 6]], dtype=int32)
开启 GPU 加速
在计图中,开启 GPU 加速的方式非常简单。我们使用 jt.flags.use_cuda 表示是否使用 GPU 训练。
jt.flags.use_cuda = 1 # 使用 GPU 训练
[i 0130 19:00:46.379159 88 cuda_flags.cc:26] CUDA enabled.
Var 的加法和连接
# 初始化两个Var
var1 = jt.arange(9).reshape(3,3)
var2 = jt.ones((3,3))
print("var1: \n", var1)
print("var2: \n", var2)
var1: jt.Var([[0 1 2] [3 4 5] [6 7 8]], dtype=int32) var2: jt.Var([[1. 1. 1.] [1. 1. 1.] [1. 1. 1.]], dtype=float32)
# Var 与实数相加
var3 = var1 + 3
print("var3: \n", var3)
# 两个 Var 相加
var4 = var1 + var2
print("var4: \n", var4)
# 连接两个 Var
var5 = jt.concat([var1, var2], dim=1)
print("var5: \n", var5)
var3: jt.Var([[ 3 4 5] [ 6 7 8] [ 9 10 11]], dtype=int32) var4: jt.Var([[1. 2. 3.] [4. 5. 6.] [7. 8. 9.]], dtype=float32) var5: jt.Var([[0. 1. 2. 1. 1. 1.] [3. 4. 5. 1. 1. 1.] [6. 7. 8. 1. 1. 1.]], dtype=float32)
Var 的乘法:标量相乘 / 矩阵相乘
# 初始化两个Var
var1 = jt.arange(9).reshape(3,3)
var2 = jt.ones((3,3))
print("var1: \n", var1)
print("var2: \n", var2)
var1: jt.Var([[0 1 2] [3 4 5] [6 7 8]], dtype=int32) var2: jt.Var([[1. 1. 1.] [1. 1. 1.] [1. 1. 1.]], dtype=float32)
# Var 与实数相乘
var3 = var1 * 3
print("var3: \n", var3)
var3: jt.Var([[ 0 3 6] [ 9 12 15] [18 21 24]], dtype=int32)
# 标量相乘 方式一
var4 = var1 * var2
print("var4: \n", var4)
# 标量相乘 方式二
var5 = var1.multiply(var2)
print("var5: \n", var5)
# 标量相乘 方式三
var6 = jt.multiply(var1, var2)
print("var6: \n", var6)
var4: jt.Var([[0. 1. 2.] [3. 4. 5.] [6. 7. 8.]], dtype=float32) var5: jt.Var([[0. 1. 2.] [3. 4. 5.] [6. 7. 8.]], dtype=float32) var6: jt.Var([[0. 1. 2.] [3. 4. 5.] [6. 7. 8.]], dtype=float32)
# 矩阵相乘 方式一
var7 = var1 @ var2
print("var7: \n", var7)
# 矩阵相乘 方式二
var8 = var1.matmul(var2)
print("var8: \n", var8)
# 矩阵相乘 方式三
var9 = jt.matmul(var1, var2)
print("var9: \n", var9)
var7: jt.Var([[ 3. 3. 3.] [12. 12. 12.] [21. 21. 21.]], dtype=float32) var8: jt.Var([[ 3. 3. 3.] [12. 12. 12.] [21. 21. 21.]], dtype=float32) var9: jt.Var([[ 3. 3. 3.] [12. 12. 12.] [21. 21. 21.]], dtype=float32)
从 Var 转化成 NumPy 数组
# 新建一个 Var
var = jt.ones((4,5))
print("var: \n", var)
print("type(var):", type(var))
# 将其转化为 NumPy 数组
ndarray = var.numpy()
print("ndarray: \n", ndarray)
print("type(ndarray):", type(ndarray))
var: jt.Var([[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]], dtype=float32) type(var): <class 'jittor.jittor_core.Var'> ndarray: [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]] type(ndarray): <class 'numpy.ndarray'>
从 NumPy 数组转化成 Var
# 新建一个 NumPy 数组
ndarray = np.ones((4,5))
print("ndarray: \n", ndarray)
print("type(ndarray):", type(ndarray))
# 将其转化为 Var 类型
var = jt.array(ndarray)
print("var: \n", var)
print("type(var):", type(var))
ndarray: [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]] type(ndarray): <class 'numpy.ndarray'> var: jt.Var([[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]], dtype=float64) type(var): <class 'jittor.jittor_core.Var'>
至此,第一章 - 基本概念: Var - 结束!
恭喜您!已成功解锁了 Jittor (计图) 最核心、最基本的数据类型 - Var 类型的基本使用。
在掌握了 Var 类型的基本操作之后,您只需要进一步熟悉神经网络训练的一般模式,便可以利用 Jittor 解决更高阶、更实战的问题。
请不要停下脚步,只差一点点,你就可以轻松地用 Jittor 来进行神经网络的训练啦!