虽然我在前面写到没有必要去阅读整个官方文档,但是在开发过程中发现,如果对整个文档特别是关于tensor的操作和函数有一定的了解,那么实际运用起来是事半功倍的。只能说一句“真香”来概括我现在的心情了。所以后面还是会详细的把官方文档中所有的函数做一个学习和总结。这一部分工程量很大,不知道啥时候可以完成。。。。
Tensors
属性相关
torch.is_tensor(obj)
用来判断obj是否为一个tensor变量。同样的还有torch.is_storage(obj)用于判断变量是否为一个storage变量。
torch.set_default_dtype(d)
用于改变tensor中数据的默认类型,如下面的例子:
>>> torch.tensor([1.2, 3]).dtype # initial default for floating point is torch.float32torch.float32>>> torch.set_default_dtype(torch.float64)>>> torch.tensor([1.2, 3]).dtype # a new floating point tensortorch.float64
所有支持的数据类型如下表所示:
tensor数据类型
想要得到tensor中默认的数据类型,可以使用:torch.get_default_dtype(),如下面的这个例子:
>>> torch.get_default_dtype() # initial default for floating point is torch.float32torch.float32>>> torch.set_default_dtype(torch.float64)>>> torch.get_default_dtype() # default is now changed to torch.float64torch.float64>>> torch.set_default_tensor_type(torch.FloatTensor) # setting tensor type also affects this>>> torch.get_default_dtype() # changed to torch.float32, the dtype for torch.FloatTensortorch.float32
同时也提供了设置默认的tensor的数据类型的函数,但是我没看懂这个和前面的做法有什么区别:
torch.set_default_tensor_type(t)
例子如下:
>>> torch.tensor([1.2, 3]).dtype # initial default for floating point is torch.float32torch.float32>>> torch.set_default_tensor_type(torch.DoubleTensor) >>> torch.tensor([1.2, 3]).dtype # a new floating point tensortorch.float64
为了得到tensor中所有的元素个数可以使用:
torch.numel(input)
函数返回一个int型的数据,例子如下:
>>> a = torch.randn(1, 2, 3, 4, 5)>>> torch.numel(a)120>>> a = torch.zeros(4,4)>>> torch.numel(a)16
当你需要输出tensor查看的时候,或许需要设置一下默认的输出选项:
torch.set_printoptions(precision=None, threshold=None, edgeitems=None, linewidth=None, profile=None)
其中precision是每一个元素的输出精度,默认是八位;threshold是输出时的阈值,当tensor中元素的个数大于该值时,进行缩略输出,默认时1000;edgeitems是输出的维度,默认是3;linewidth字面意思,每一行输出的长度;profile=None,修正默认设置(不太懂,感兴趣的可以试试))
为了防止一些不正常的元素产生,比如特别小的数,pytorch支持如下设置:
torch.set_flush_denormal(mode)
mode中可以填true或者false
例子如下:
>>> torch.set_flush_denormal(True)True>>> torch.tensor([1e-323], dtype=torch.float64) tensor([ 0.], dtype=torch.float64)>>> torch.set_flush_denormal(False)True>>> torch.tensor([1e-323], dtype=torch.float64) tensor(9.88131e-324 * [ 1.0000], dtype=torch.float64)
可以看出设置了之后,当出现极小数时,直接置为0了。文档中提出该功能必须要系统支持。
Creation Ops
这一部分主要介绍tensor的生成。首先直接赋值:
torch.tensor(data, dtype=None, device=None, requires_grad=False)
具体的结果如下例子所示:
>>> torch.tensor([[0.1, 1.2], [2.2, 3.1], [4.9, 5.2]]) tensor([[ 0.1000, 1.2000], [ 2.2000, 3.1000], [ 4.9000, 5.2000]])>>> torch.tensor([0, 1]) # Type inference on datatensor([ 0, 1])>>> torch.tensor([[0.11111, 0.222222, 0.3333333]], dtype=torch.float64, device=torch.device('cuda:0')) # creates a torch.cuda.DoubleTensortensor([[ 0.1111, 0.2222, 0.3333]], dtype=torch.float64, device='cuda:0')>>> torch.tensor(3.14159) # Create a scalar (zero-dimensional tensor)tensor(3.1416)>>> torch.tensor([]) # Create an empty tensor (of size (0,))tensor([])
当然不能忘了tensor和numpy之间可以相互转换,这个在前面基础部分就有介绍,使用torch.from_numpy即可。
>>> a = numpy.array([1, 2, 3])>>> t = torch.from_numpy(a)>>> t tensor([ 1, 2, 3])>>> t[0] = -1>>> a array([-1, 2, 3])
如果要生成全为0的tensor,我们使用torch.zeros(),参数如下:
torch.zeros(*sizes, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
其中size可以是任意维度的list或tuple。如下所示:
>>> torch.zeros(2, 3)tensor([[ 0., 0., 0.], [ 0., 0., 0.]]) >>> torch.zeros(5)tensor([ 0., 0., 0., 0., 0.])
另外还有一个zeros_like函数,从函数名不难猜到,这个函数是用于生成和输入tensor大小相同的全零tensor的。
处理生成全零的tensor,还有one()函数,用于生成全为1的tensor。也有one_like函数。
下面这个函数和python中的range类似,用于产生一个一维的tensor,在给定的区间下依据给定的步长。
torch.arange(start=0, end, step=1, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
例子如下所示:
>>> torch.arange(5)tensor([ 0., 1., 2., 3., 4.]) >>> torch.arange(1, 4)tensor([ 1., 2., 3.]) >>> torch.arange(1, 2.5, 0.5)tensor([ 1.0000, 1.5000, 2.0000])
注意这里生成的tensor中是不包含上界的,如果要包含上界,可以使用range替代。当然也可以通过设定上下界和元素的个数来避免步长的设定。使用linspace就行了:
>>> torch.linspace(3, 10, steps=5) tensor([ 3.0000, 4.7500, 6.5000, 8.2500, 10.0000])>>> torch.linspace(-10, 10, steps=5) tensor([-10., -5., 0., 5., 10.])>>> torch.linspace(start=-10, end=10, steps=5) tensor([-10., -5., 0., 5., 10.])
既然有线性空间,那么log空间自然也是支持的。(虽然我暂时不知道这个可以用来干嘛?画图吗?)
将上面的linspace换为logspace即可:
>>> torch.logspace(start=-10, end=10, steps=5) tensor([ 1.0000e-10, 1.0000e-05, 1.0000e+00, 1.0000e+05, 1.0000e+10])>>> torch.logspace(start=0.1, end=1.0, steps=5) tensor([ 1.2589, 2.1135, 3.5481, 5.9566, 10.0000])
单位矩阵在矩阵运算中起到了很关键的作用,需要生成一个单位阵,可以使用如下语句:
>>> torch.eye(3)tensor([[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]])
当然如果特殊需要,单位阵也是支持设置宽的:
>>> torch.eye(3,7)tensor([[ 1., 0., 0., 0., 0., 0., 0.], [ 0., 1., 0., 0., 0., 0., 0.], [ 0., 0., 1., 0., 0., 0., 0.]])
除了单位阵,还可以生成未初始化的矩阵,调用empty即可,数字是随机的:
>>> torch.empty(2, 3)tensor(1.00000e-08 * [[ 6.3984, 0.0000, 0.0000], [ 0.0000, 0.0000, 0.0000]])
该函数同样有兄弟:empty_like()
前面介绍了可以生成全为0和1的tensor,那么如果我要生成全为2的呢?
首先你可以拿全为1的乘以一个常数,其次,你可以使用full()函数:
>>> torch.full((2, 3), 3.141592)tensor([[ 3.1416, 3.1416, 3.1416], [ 3.1416, 3.1416, 3.1416]])
同样full函数有基友full_like()。
以上是pytorch目前支持的所有tensor生成方法,下面介绍关于tensor的一系列“矩阵操作”。
Indexing, Slicing, Joining, Mutating Ops
这一部分将介绍目前pytorch支持的所有关于tensor的各种变换操作。首先是多个tensor的连接,这里和caffe的concat layer作用应该类似,但是不得不感叹,pytorch的实现简洁太多了啊。函数如下:
torch.cat(seq, dim=0, out=None)
例子如下,做的事二维的,当然高维的同样也可以,不过你必须保证连接的维度上的长度事匹配的。
>>> x = torch.randn(2, 3)>>> x tensor([[ 0.6580, -1.0969, -0.4614], [-0.1034, -0.5790, 0.1497]])>>> torch.cat((x, x, x), 0) tensor([[ 0.6580, -1.0969, -0.4614], [-0.1034, -0.5790, 0.1497], [ 0.6580, -1.0969, -0.4614], [-0.1034, -0.5790, 0.1497], [ 0.6580, -1.0969, -0.4614], [-0.1034, -0.5790, 0.1497]])>>> torch.cat((x, x, x), 1) tensor([[ 0.6580, -1.0969, -0.4614, 0.6580, -1.0969, -0.4614, 0.6580, -1.0969, -0.4614], [-0.1034, -0.5790, 0.1497, -0.1034, -0.5790, 0.1497, -0.1034, -0.5790, 0.1497]])
通过设置参数dim来选择按哪一个维度相连。
还有另外一种连接的方法:
torch.stack(seq, dim=0, out=None)
该函数跟cat基本相同。不过stack要求连接的tensor每一个维度都相同。
同样可以将一个tensor分割开来,函数如下:
torch.chunk(tensor, chunks, dim=0)
例子如下:
>>> print(a)tensor([[ 0.6253, 0.8666, -0.1230, 0.3984, 0.2968], [-1.1441, 1.1067, -0.0283, 0.4503, -0.4435], [ 0.4108, -0.2321, 0.2295, 0.2917, 0.1316], [ 1.4066, -0.2489, 0.2258, -0.5783, -0.6589], [-1.9384, 0.8134, 0.2353, -0.1845, -1.1675], [-0.7617, 0.6622, 0.6844, 0.0229, -0.7072], [ 0.7110, -0.8292, -0.1205, 1.3795, -1.3677], [-0.0562, 1.6998, -0.2817, -0.7298, 0.2130], [ 0.4300, 0.8207, -1.1832, 0.9723, -0.0193], [-0.3227, 0.1291, -0.1117, -0.2469, -0.5320]]) >>> torch.chunk(a,2,0) (tensor([[ 0.6253, 0.8666, -0.1230, 0.3984, 0.2968], [-1.1441, 1.1067, -0.0283, 0.4503, -0.4435], [ 0.4108, -0.2321, 0.2295, 0.2917, 0.1316], [ 1.4066, -0.2489, 0.2258, -0.5783, -0.6589], [-1.9384, 0.8134, 0.2353, -0.1845, -1.1675]]), tensor([[-0.7617, 0.6622, 0.6844, 0.0229, -0.7072], [ 0.7110, -0.8292, -0.1205, 1.3795, -1.3677], [-0.0562, 1.6998, -0.2817, -0.7298, 0.2130], [ 0.4300, 0.8207, -1.1832, 0.9723, -0.0193], [-0.3227, 0.1291, -0.1117, -0.2469, -0.5320]])) >>> torch.chunk(a,2,1) (tensor([[ 0.6253, 0.8666, -0.1230], [-1.1441, 1.1067, -0.0283], [ 0.4108, -0.2321, 0.2295], [ 1.4066, -0.2489, 0.2258], [-1.9384, 0.8134, 0.2353], [-0.7617, 0.6622, 0.6844], [ 0.7110, -0.8292, -0.1205], [-0.0562, 1.6998, -0.2817], [ 0.4300, 0.8207, -1.1832], [-0.3227, 0.1291, -0.1117]]), tensor([[ 0.3984, 0.2968], [ 0.4503, -0.4435], [ 0.2917, 0.1316], [-0.5783, -0.6589], [-0.1845, -1.1675], [ 0.0229, -0.7072], [ 1.3795, -1.3677], [-0.7298, 0.2130], [ 0.9723, -0.0193], [-0.2469, -0.5320]]))
返回的值是一个tuple。将其依次复制给需要的变量即可。
同样还有另外一个划分的方法,重写的split函数,功能相似:
torch.split(tensor, split_size_or_sections, dim=0)
其中split_size_or_sections用于设定划分规则,dim用于设定划分的维度。如下例:
>>> x = torch.randn(5, 5, 5)>>> x tensor([[[-0.8347, 2.2014, 0.2768, 0.8642, 0.7517], [-1.0237, 1.1800, -1.9238, 0.7537, -0.7155], [-0.5800, -0.0527, 1.1536, 0.8828, 0.0136], [ 0.1452, -0.5878, -1.9840, 0.8288, 1.1804], [-0.0514, 0.6633, -1.0233, -1.6492, 0.2422]], [[-0.1809, 0.0115, -0.1156, 0.4199, -0.6424], [ 1.0098, 0.2440, 0.2432, 1.5793, -0.4407], [-0.5316, 1.6012, -0.4609, 2.3206, 1.1053], [ 0.0999, -0.3938, -1.4438, 0.4003, -0.6948], [-1.5991, -0.2188, 0.8460, 1.2603, -0.4484]], [[ 0.1010, 1.8040, 0.3617, 0.5746, -0.0840], [-0.0948, -0.7579, 1.4936, -1.1720, -0.0606], [ 0.9900, -0.8400, 0.5351, -0.5388, -1.7400], [-0.5397, -0.1141, -0.3179, -0.2871, -0.8846], [ 0.3530, 0.5373, 0.2718, 1.2432, -1.3468]], [[ 0.5830, 0.4154, 0.9897, 1.6300, 1.5950], [ 0.4810, -1.1237, -0.2133, 0.5659, -0.4047], [ 0.0257, -0.3569, 1.4560, -0.8150, -0.3167], [ 1.8086, 0.1829, -0.7073, -1.1966, 0.3805], [ 0.0532, -0.2976, 0.3080, -1.3165, -1.0769]], [[ 1.8177, -0.6848, 0.1681, -1.0104, 0.9661], [-1.1784, -1.7252, 0.5589, -1.5597, 1.1723], [ 0.5254, -1.3278, 2.0289, -1.6005, -0.9900], [-0.2772, -0.4890, 1.1362, 0.9137, 1.0255], [-0.3162, -0.6244, 0.9933, -1.7472, 0.5968]]])>>> torch.split(x,2,0) (tensor([[[-0.8347, 2.2014, 0.2768, 0.8642, 0.7517], [-1.0237, 1.1800, -1.9238, 0.7537, -0.7155], [-0.5800, -0.0527, 1.1536, 0.8828, 0.0136], [ 0.1452, -0.5878, -1.9840, 0.8288, 1.1804], [-0.0514, 0.6633, -1.0233, -1.6492, 0.2422]], [[-0.1809, 0.0115, -0.1156, 0.4199, -0.6424], [ 1.0098, 0.2440, 0.2432, 1.5793, -0.4407], [-0.5316, 1.6012, -0.4609, 2.3206, 1.1053], [ 0.0999, -0.3938, -1.4438, 0.4003, -0.6948], [-1.5991, -0.2188, 0.8460, 1.2603, -0.4484]]]), tensor([[[ 0.1010, 1.8040, 0.3617, 0.5746, -0.0840], [-0.0948, -0.7579, 1.4936, -1.1720, -0.0606], [ 0.9900, -0.8400, 0.5351, -0.5388, -1.7400], [-0.5397, -0.1141, -0.3179, -0.2871, -0.8846], [ 0.3530, 0.5373, 0.2718, 1.2432, -1.3468]], [[ 0.5830, 0.4154, 0.9897, 1.6300, 1.5950], [ 0.4810, -1.1237, -0.2133, 0.5659, -0.4047], [ 0.0257, -0.3569, 1.4560, -0.8150, -0.3167], [ 1.8086, 0.1829, -0.7073, -1.1966, 0.3805], [ 0.0532, -0.2976, 0.3080, -1.3165, -1.0769]]]), tensor([[[ 1.8177, -0.6848, 0.1681, -1.0104, 0.9661], [-1.1784, -1.7252, 0.5589, -1.5597, 1.1723], [ 0.5254, -1.3278, 2.0289, -1.6005, -0.9900], [-0.2772, -0.4890, 1.1362, 0.9137, 1.0255], [-0.3162, -0.6244, 0.9933, -1.7472, 0.5968]]]))>>> torch.split(x,(3,2),0) (tensor([[[-0.8347, 2.2014, 0.2768, 0.8642, 0.7517], [-1.0237, 1.1800, -1.9238, 0.7537, -0.7155], [-0.5800, -0.0527, 1.1536, 0.8828, 0.0136], [ 0.1452, -0.5878, -1.9840, 0.8288, 1.1804], [-0.0514, 0.6633, -1.0233, -1.6492, 0.2422]], [[-0.1809, 0.0115, -0.1156, 0.4199, -0.6424], [ 1.0098, 0.2440, 0.2432, 1.5793, -0.4407], [-0.5316, 1.6012, -0.4609, 2.3206, 1.1053], [ 0.0999, -0.3938, -1.4438, 0.4003, -0.6948], [-1.5991, -0.2188, 0.8460, 1.2603, -0.4484]], [[ 0.1010, 1.8040, 0.3617, 0.5746, -0.0840], [-0.0948, -0.7579, 1.4936, -1.1720, -0.0606], [ 0.9900, -0.8400, 0.5351, -0.5388, -1.7400], [-0.5397, -0.1141, -0.3179, -0.2871, -0.8846], [ 0.3530, 0.5373, 0.2718, 1.2432, -1.3468]]]), tensor([[[ 0.5830, 0.4154, 0.9897, 1.6300, 1.5950], [ 0.4810, -1.1237, -0.2133, 0.5659, -0.4047], [ 0.0257, -0.3569, 1.4560, -0.8150, -0.3167], [ 1.8086, 0.1829, -0.7073, -1.1966, 0.3805], [ 0.0532, -0.2976, 0.3080, -1.3165, -1.0769]], [[ 1.8177, -0.6848, 0.1681, -1.0104, 0.9661], [-1.1784, -1.7252, 0.5589, -1.5597, 1.1723], [ 0.5254, -1.3278, 2.0289, -1.6005, -0.9900], [-0.2772, -0.4890, 1.1362, 0.9137, 1.0255], [-0.3162, -0.6244, 0.9933, -1.7472, 0.5968]]]))>>> torch.split(x,(3,2),1) (tensor([[[-0.8347, 2.2014, 0.2768, 0.8642, 0.7517], [-1.0237, 1.1800, -1.9238, 0.7537, -0.7155], [-0.5800, -0.0527, 1.1536, 0.8828, 0.0136]], [[-0.1809, 0.0115, -0.1156, 0.4199, -0.6424], [ 1.0098, 0.2440, 0.2432, 1.5793, -0.4407], [-0.5316, 1.6012, -0.4609, 2.3206, 1.1053]], [[ 0.1010, 1.8040, 0.3617, 0.5746, -0.0840], [-0.0948, -0.7579, 1.4936, -1.1720, -0.0606], [ 0.9900, -0.8400, 0.5351, -0.5388, -1.7400]], [[ 0.5830, 0.4154, 0.9897, 1.6300, 1.5950], [ 0.4810, -1.1237, -0.2133, 0.5659, -0.4047], [ 0.0257, -0.3569, 1.4560, -0.8150, -0.3167]], [[ 1.8177, -0.6848, 0.1681, -1.0104, 0.9661], [-1.1784, -1.7252, 0.5589, -1.5597, 1.1723], [ 0.5254, -1.3278, 2.0289, -1.6005, -0.9900]]]), tensor([[[ 0.1452, -0.5878, -1.9840, 0.8288, 1.1804], [-0.0514, 0.6633, -1.0233, -1.6492, 0.2422]], [[ 0.0999, -0.3938, -1.4438, 0.4003, -0.6948], [-1.5991, -0.2188, 0.8460, 1.2603, -0.4484]], [[-0.5397, -0.1141, -0.3179, -0.2871, -0.8846], [ 0.3530, 0.5373, 0.2718, 1.2432, -1.3468]], [[ 1.8086, 0.1829, -0.7073, -1.1966, 0.3805], [ 0.0532, -0.2976, 0.3080, -1.3165, -1.0769]], [[-0.2772, -0.4890, 1.1362, 0.9137, 1.0255], [-0.3162, -0.6244, 0.9933, -1.7472, 0.5968]]]))
下面这个函数应该是到目前为止,第一个不太好理解的。
torch.gather(input, dim, index, out=None)
直接上例子吧,详细的解释和应用可以去这个博客,因为我确实没有用到过,所以只能从算法上简单的解释一下了。
这个函数的意义在于按照一个给定的轴收集原tensor中的值,并得到一个新的tensor,其中dim = 0 是按照y轴,dim = 1是按照x轴,按照哪个轴在原tensor和index对应的tensor中都是按照相同的轴读取。输出也按照该轴输出。这一点是没法一眼看懂这个函数的原因所在。例子如下:
>>> t = torch.tensor([[1,2],[3,4]])>>> torch.gather(t, 1, torch.tensor([[0,0],[1,0]])) tensor([[ 1, 1], [ 4, 3]])>>> torch.gather(t, 0, torch.tensor([[0,0],[1,0]])) tensor([[ 1, 2], [ 3, 2]])
当dim为1时,按照x轴(行)读取,index对应的第一行为0,0所以连续在原tensor中读取两次第一个位置,保存在结果tenser的第一行。第二行同理。
当dim为0时,按照y轴(列)读取,index对应的第一列为0,1所以分别读取原tensor的第一列的第一个第二个数1和3存在结果tensor的第一列。
和上面挑tensor中元素的情况类似,我们还能挑选tensor中的一整行和一整列。函数如下:
torch.index_select(input, dim, index, out=None)
这个例子还是很好理解的,我觉得我不需要过多的解释了。直接上结果:
>>> x = torch.randn(3, 4)>>> x tensor([[ 0.1427, 0.0231, -0.5414, -1.0009], [-0.4664, 0.2647, -0.1228, -1.1068], [-1.1734, -0.6571, 0.7230, -0.6004]])>>> indices = torch.tensor([0, 2])>>> torch.index_select(x, 0, indices) tensor([[ 0.1427, 0.0231, -0.5414, -1.0009], [-1.1734, -0.6571, 0.7230, -0.6004]])>>> torch.index_select(x, 1, indices) tensor([[ 0.1427, -0.5414], [-0.4664, -0.1228], [-1.1734, 0.7230]])
如果想要挑选出tensor中所有大于一个阈值的量要怎么做呢?
这里要结合两个函数,ge()和mask_select()这里首先介绍mask_select()函数,ge函数在后面的tensor运算章节会讲。这里只需要知道他讲输入tensor按照一个阈值01化即可(大于阈值设为1)。mask_select函数将按照输入的mask挑选出原tensor中mask上面为1的元素,生成一个一维的向量。
结果如下:
>>> x = torch.randn(3, 4)>>> x tensor([[ 0.3552, -2.3825, -0.8297, 0.3477], [-1.2035, 1.2252, 0.5002, 0.6248], [ 0.1307, -2.0608, 0.1244, 2.0139]])>>> mask = x.ge(0.5)>>> mask tensor([[ 0, 0, 0, 0], [ 0, 1, 1, 1], [ 0, 0, 0, 1]], dtype=torch.uint8)>>> torch.masked_select(x, mask) tensor([ 1.2252, 0.5002, 0.6248, 2.0139])
下面这个函数用于得到tensor中非零元素的位置坐标,返回值的每一行代表一个坐标。如果输入tensor是n维的,其中非零元素个数为k,那么返回值是一个k×n的tensor。函数和示例如下所示:
torch.nonzero(input, out=None)
>>> torch.nonzero(torch.tensor([1, 1, 1, 0, 1]))tensor([[ 0], [ 1], [ 2], [ 4]]) >>> torch.nonzero(torch.tensor([[0.6, 0.0, 0.0, 0.0], [0.0, 0.4, 0.0, 0.0], [0.0, 0.0, 1.2, 0.0], [0.0, 0.0, 0.0,-0.4]]))tensor([[ 0, 0], [ 1, 1], [ 2, 2], [ 3, 3]])
接下来介绍一个必定经常用到的函数,reshape。该函数用于改变tensor的形状,和其他的reshape一样,参数设为-1时,该维度的元素个数由其他维度计算得到。直接上示例:
>>> a = torch.arange(4)>>> torch.reshape(a, (2, 2)) tensor([[ 0., 1.], [ 2., 3.]])>>> b = torch.tensor([[0, 1], [2, 3]])>>> torch.reshape(b, (-1,)) tensor([ 0, 1, 2, 3])
有些时候不需要分割tensor,但是需要压缩tensor,pytorch提供了一个自动去掉通道是1的维度的函数:
torch.squeeze(input, dim=None, out=None)
直接上例子吧,比如我们生成一个维度为3,1,2的tensor
>>> x = torch.randn(3, 1, 2)>>> x tensor([[[-0.2863, 0.8594]], [[-0.4789, 0.9160]], [[ 1.0955, -1.2205]]])
可以发现实际上他的第二个维度没有什么意义。调用squeeze函数:
>>> torch.squeeze(x)tensor([[-0.2863, 0.8594], [-0.4789, 0.9160], [ 1.0955, -1.2205]])
如果一个tensor中通道数为1的维度有很多,但是又不想全部去掉,那么可以在函数中通过设定dim参数,选择去掉某一个维度。
有的时候又需要增加维度,这时可以使用unsqueeze函数:
torch.unsqueeze(input, dim, out=None)
该函数将在制定的维度增加一维:
>>> x = torch.tensor([1, 2, 3, 4])>>> torch.unsqueeze(x, 0) tensor([[ 1, 2, 3, 4]])>>> torch.unsqueeze(x, 1) tensor([[ 1], [ 2], [ 3], [ 4]])
squeeze和unsqueeze函数产生的输出和输入都是共享存储空间的,改变其中一个另外一个也会改变。
既然tensor是矩阵操作,那么肯定少不了矩阵的转置:
torch.t(input, out=None)
例子很简单,函数也只有一个参数,那就是输入tensor:
>>> x = torch.randn(2, 3)>>> x tensor([[ 0.4875, 0.9158, -0.5872], [ 0.3938, -0.6929, 0.6932]])>>> torch.t(x) tensor([[ 0.4875, 0.3938], [ 0.9158, -0.6929], [-0.5872, 0.6932]])
前面介绍过取部分元素的函数,这里再增加一个,用起来比较简单,但是在使用时可能人为需要计算的就多一点了:
torch.take(input, indices)
该函数将输入tensor转换为一个一维的向量,然后在该向量上依据给出的坐标,返回元素的tensor:
>>> src = torch.tensor([[4, 3, 5], [6, 7, 8]])>>> torch.take(src, torch.tensor([0, 2, 5])) tensor([ 4, 5, 8])
pytorch除了提供了.t()这种简单的矩阵转置方式,还提供了另外一个函数:
torch.transpose(input, dim0, dim1, out=None)
这里通过指定两个维度dim0和dim1,来将其做转换,如果设为0和1则和t的效果等价。不过需要注意的是transpose函数转置前后的到的tensor是共享底层存储空间的,如果对其中一个的元素做更改,另外一个也会发生变化:
>>> x = torch.randn(3, 1, 2)>>> x tensor([[[-0.2863, 0.8594]], [[-0.4789, 0.9160]], [[ 1.0955, -1.2205]]])>>> torch.squeeze(x) tensor([[-0.2863, 0.8594], [-0.4789, 0.9160], [ 1.0955, -1.2205]])>>> x = torch.randn(2, 3)>>> x tensor([[ 0.6525, 0.1151, -0.0437], [ 0.2640, -1.2813, 1.3332]])>>> y = torch.transpose(x, 0, 1)>>> y tensor([[ 0.6525, 0.2640], [ 0.1151, -1.2813], [-0.0437, 1.3332]])>>> y[0][0] = 1>>> y tensor([[ 1.0000, 0.2640], [ 0.1151, -1.2813], [-0.0437, 1.3332]])>>> x tensor([[ 1.0000, 0.1151, -0.0437], [ 0.2640, -1.2813, 1.3332]])
如果需要删除一个维度,可以用下面的操作:
torch.unbind(tensor, dim=0)
直接上示例吧:
>>> xtensor([[ 1.0000, 0.1151, -0.0437], [ 0.2640, -1.2813, 1.3332]]) >>> torch.unbind(x, 1) (tensor([ 1.0000, 0.2640]), tensor([ 0.1151, -1.2813]), tensor([-0.0437, 1.3332]))
可以看出,当我们删除了维度1之后,原tensor被分为了几个小的只有维度0的子tensor,所以这个函数可以简答的理解为按给定的维度将原tensor展开,上面是按列展开。再比如我们按行展开:
>>> torch.unbind(x, 0) (tensor([ 1.0000, 0.1151, -0.0437]), tensor([ 0.2640, -1.2813, 1.3332]))
终于要把这一部分写完了,还剩最后一个函数:
torch.where(condition, x, y)
这个函数的功能是依据一个判断语句,来从tensor中挑选语句,官方文档给的例子是:
>>> x = torch.randn(3, 2)>>> y = torch.ones(3, 2)>>> x tensor([[-0.4620, 0.3139], [ 0.3898, -0.7197], [ 0.0478, -0.1657]])>>> torch.where(x > 0, x, y) tensor([[ 1.0000, 0.3139], [ 0.3898, 1.0000], [ 0.0478, 1.0000]])
那么能不能依据x的值在y和z之中挑选呢?是可以的,甚至其他的条件都可以,但是有一个条件是你所使用的条件和变量是广播的。
>>> x = torch.randn(3, 2)>>> y = torch.ones(3, 2)>>> z = torch.zeros(3, 2)>>> x tensor([[ 0.9749, 0.2215], [-0.3449, 2.2324], [ 0.8020, 0.7957]])>>> y tensor([[ 1., 1.], [ 1., 1.], [ 1., 1.]])>>> z tensor([[ 0., 0.], [ 0., 0.], [ 0., 0.]])>>> torch.where(x>0,x,y) tensor([[ 0.9749, 0.2215], [ 1.0000, 2.2324], [ 0.8020, 0.7957]])
Random sampling
写到这里,我这一篇博客才写了四分之一(T_T)
随机数是深度学习中很关键的基础,为什么?自行百度吧。pytorch中设置随机数生成的种子点,查看种子点的函数分别为:
torch.manual_seed(seed) #设置torch.initial_seed() #查看
示例如下:
>>> torch.initial_seed()16846182053669541300>>> torch.manual_seed(1) <torch._C.Generator object at 0x7fadc8d9afb0>>>> torch.initial_seed()1
因为这篇博客里不打算探究pytorch随机数的生成原理,所以以下几个函数就不详细介绍了:get_rng_state();set_rng_state(new_state);torch.default_generator = <torch._C.Generator object>。
下面来介绍一系列随机tensor的生成方法
torch.bernoulli(input, out=None)
该函数按照伯努利分布依据输入的tensor来随机的生成0,1二值的tensor。要求输入tensor的元素值在0和1之间。
>>> a = torch.empty(3, 3).uniform_(0, 1) # generate a uniform random matrix with range [0, 1]>>> a tensor([[ 0.1737, 0.0950, 0.3609], [ 0.7148, 0.0289, 0.2676], [ 0.9456, 0.8937, 0.7202]])>>> torch.bernoulli(a) tensor([[ 1., 0., 0.], [ 0., 0., 0.], [ 1., 1., 1.]])>>> a = torch.ones(3, 3) # probability of drawing "1" is 1>>> torch.bernoulli(a) tensor([[ 1., 1., 1.], [ 1., 1., 1.], [ 1., 1., 1.]])>>> a = torch.zeros(3, 3) # probability of drawing "1" is 0>>> torch.bernoulli(a) tensor([[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]])
torch.multinomial(input, num_samples, replacement=False, out=None)
这个函数比较有意思,他的输入tensor表示了每一个位置的概率,这里并不要求他是大于1的,但是所有元素的和不能是0。num_samples为挑选出的位置的编号,实际上就是一个随机数,第三个参数指的是是否运行重复挑选。官方文档给的例子似乎不太好体现,给一个二值的输入tensor演示一下吧:
>>> weights = torch.tensor([0, 10], dtype=torch.float)>>> torch.multinomial(weights, 4, replacement=True) tensor([ 1, 1, 1, 1])
当讲两个位置的概率设为相同时:
>>> weights = torch.tensor([10, 10], dtype=torch.float)>>> torch.multinomial(weights, 4, replacement=True) tensor([ 1, 1, 0, 0])
注意如果后面的replacement设为false,那么输出的元素个数必须是不大于输入的size的。
下面一个方法生成的是服从正太分布的随机数:
有三种种方式:
torch.normal(mean, std, out=None) torch.normal(mean=0.0, std, out=None) torch.normal(mean, std=1.0, out=None)
如果输入的mean和std是tensor,那么生成的tensor将按照mean的形状,元素的个数和输入相同,也就是说mean和std可以有不同的形状,但是必须有相同的元素个数:
>>> torch.normal(mean=torch.arange(1, 11), std=torch.arange(1, 0, -0.1)) tensor([ 1.0425, 3.5672, 2.7969, 4.2925, 4.7229, 6.2134, 8.0505, 8.1408, 9.0563, 10.0566])
另外也可以固定其中一个,也就是第二种和第三种方式:
>>> torch.normal(mean=0.5, std=torch.arange(1, 6)) tensor([-1.2793, -1.0732, -2.0687, 5.1177, -1.2303])>>> torch.normal(mean=torch.arange(1, 6)) tensor([ 1.1552, 2.6148, 2.6535, 5.8318, 4.2361])
下面几个功能类似,都是按照一定得规则生成随机tensor的,这里统一整理一下:
#生成[0,1)的随机数torch.rand(*sizes, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)#按照输入的tensor的尺寸生成torch.rand_like(input, dtype=None, layout=None, device=None, requires_grad=False) #在一个范围内生成整型的随机torch.randint(low=0, high, size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) #不解释torch.randint_like(input, low=0, high, dtype=None, layout=torch.strided, device=None, requires_grad=False)#返回01正太分布torch.randn(*sizes, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)#不解释torch.randn_like(input, dtype=None, layout=None, device=None, requires_grad=False)#返回0到输入n的之间整数随机排列不含ntorch.randperm(n, out=None, dtype=torch.int64, layout=torch.strided, device=None, requires_grad=False)
另外在torch.Tensor下还定义了一些in-place的函数:
torch.Tensor.bernoulli_()
- in-place version oftorch.bernoulli()
,伯努利分布torch.Tensor.cauchy_()
- numbers drawn from the Cauchy distribution,柯西分布torch.Tensor.exponential_()
- numbers drawn from the exponential distribution,指数分布torch.Tensor.geometric_()
- elements drawn from the geometric distribution,几何分布torch.Tensor.log_normal_()
- samples from the log-normal distribution,对数正太分布torch.Tensor.normal_()
- in-place version oftorch.normal()
,正太分布torch.Tensor.random_()
- numbers sampled from the discrete uniform distribution,均匀分布torch.Tensor.uniform_()
- numbers sampled from the continuous uniform distribution,连续均匀分布
Serialization
这里主要介绍如何将得到的tensor保存到本地,或者从本地读取tensor,这也是很关键的步骤。
torch.save(obj, f, pickle_module=<module 'pickle' from '/private/home/soumith/anaconda3/lib/python3.6/pickle.py'>, pickle_protocol=2)
这一部分在Recommended approach for saving a model
有更详细的介绍。这里就放几个简单的例子吧:
>>> # Save to file>>> x = torch.tensor([0, 1, 2, 3, 4])>>> torch.save(x, 'tensor.pt')>>> # Save to io.BytesIO buffer>>> buffer = io.BytesIO()>>> torch.save(x, buffer)
同样load函数:
>>> torch.load('tensors.pt')# Load all tensors onto the CPU>>> torch.load('tensors.pt', map_location='cpu')# Load all tensors onto the CPU, using a function>>> torch.load('tensors.pt', map_location=lambda storage, loc: storage)# Load all tensors onto GPU 1>>> torch.load('tensors.pt', map_location=lambda storage, loc: storage.cuda(1))# Map tensors from GPU 1 to GPU 0>>> torch.load('tensors.pt', map_location={'cuda:1':'cuda:0'})# Load tensor from io.BytesIO object>>> with open('tensor.pt') as f: buffer = io.BytesIO(f.read())>>> torch.load(buffer)
这一部分可能后续会专门写一篇博客介绍。这篇博客主要介绍tensor的相关操作函数吧。
Parallelism
这里主要是和CPU线程相关的两个函数:
torch.get_num_threads()torch.set_num_threads()
第一个用于的到目前的CPU线程,第二个用于设定使用的线程,但是在GPU训练和测试时基本没有什么用了。
Locally disabling gradient computation
前面有提到过,pytorch会默认的给tensor计算梯度,这样在实际使用时,会增加一些不必要的资源开销,可以通过设置torch.no_grad(), torch.enable_grad()和torch.set_grad_enabled()三个值来设定tensor的梯度计算:
直接上例子吧:
>>> x = torch.zeros(1, requires_grad=True)>>> with torch.no_grad():... y = x * 2>>> y.requires_gradFalse>>> is_train = False>>> with torch.set_grad_enabled(is_train):... y = x * 2>>> y.requires_gradFalse>>> torch.set_grad_enabled(True) # this can also be used as a function>>> y = x * 2>>> y.requires_gradTrue>>> torch.set_grad_enabled(False)>>> y = x * 2>>> y.requires_gradFalse
以上基本介绍完了tensor的各种基本的函数。下面步入这篇博客的正题。。。。写了快八千字了才步入正题。。。。
Math operations
Pointwise Ops
这一块将介绍在tensor上可以使用的各种数学计算。首先是最简单的各种运算,输入都是一个tensor,所以就不详细写了,还是直接整理一下:
求绝对值
torch.abs(input, out=None) >>> torch.abs(torch.tensor([-1, -2, 3])) tensor([ 1, 2, 3])
求反三角函数
torch.acos(input, out=None) >>> a = torch.randn(4)>>> a tensor([ 0.3348, -0.5889, 0.2005, -0.1584])>>> torch.acos(a) tensor([ 1.2294, 2.2004, 1.3690, 1.7298]) torch.asin(input, out=None) >>> a = torch.randn(4)>>> a tensor([-0.5962, 1.4985, -0.4396, 1.4525])>>> torch.asin(a) tensor([-0.6387, nan, -0.4552, nan]) torch.atan(input, out=None) >>> a = torch.randn(4)>>> a tensor([ 0.2341, 0.2539, -0.6256, -0.6448])>>> torch.atan(a) tensor([ 0.2299, 0.2487, -0.5591, -0.5727]) torch.atan2(input1, input2, out=None) #输入为两个tensor,求他们对应的反正切>>> a = torch.randn(4)>>> a tensor([ 0.9041, 0.0196, -0.3108, -2.4423])>>> torch.atan2(a, torch.randn(4)) tensor([ 0.9833, 0.0811, -1.9743, -1.4151])
所有元素加上一个定值
torch.add()>>> a = torch.randn(4)>>> a tensor([ 0.0202, 1.0985, 1.3506, -0.6056])>>> torch.add(a, 20) tensor([ 20.0202, 21.0985, 21.3506, 19.3944])
输入tensor加上一个常数乘以另一个tensor,如果两个维度不同,会扩展:
>>> a = torch.randn(4)>>> a tensor([-0.9732, -0.3497, 0.6245, 0.4022])>>> b = torch.randn(4, 1)>>> b tensor([[ 0.3743], [-1.7724], [-0.5811], [-0.8017]])>>> torch.add(a, 10, b) tensor([[ 2.7695, 3.3930, 4.3672, 4.1450], [-18.6971, -18.0736, -17.0994, -17.3216], [ -6.7845, -6.1610, -5.1868, -5.4090], [ -8.9902, -8.3667, -7.3925, -7.6147]])
对tensor1和tensor2做数除,然后乘以一个变量之后加到输入tensor上。
>>> t = torch.randn(1, 3)>>> t1 = torch.randn(3, 1)>>> t2 = torch.randn(1, 3)>>> torch.addcdiv(t, 0.1, t1, t2) tensor([[-0.2312, -3.6496, 0.1312], [-1.0428, 3.4292, -0.1030], [-0.5369, -0.9829, 0.0430]])
类似的也有乘法:
torch.addcmul(tensor, value=1, tensor1, tensor2, out=None)
还有一个减法的,但是好像物理意义不太一样,公式如下:
lerp
torch.lerp(start, end, weight, out=None)>>> start = torch.arange(1, 5)>>> end = torch.empty(4).fill_(10)>>> start tensor([ 1., 2., 3., 4.])>>> end tensor([ 10., 10., 10., 10.])>>> torch.lerp(start, end, 0.5) tensor([ 5.5000, 6.0000, 6.5000, 7.0000])
对tensor中的元素做向上取整?
torch.ceil(input, out=None) >>> a = torch.randn(4)>>> a tensor([-0.6341, -1.4208, -1.0900, 0.5826])>>> torch.ceil(a) tensor([-0., -1., -1., 1.])
向下取整:
torch.floor(input, out=None) >>> a = torch.randn(4)>>> a tensor([-0.8166, 1.5308, -0.2530, -0.2091])>>> torch.floor(a) tensor([-1., 1., -1., -1.])
x大于或小于阈值时将其截断:
torch.clamp(input, min, max, out=None) >>> a = torch.randn(4)>>> a tensor([-1.7120, 0.1734, -0.0478, -0.0922])>>> torch.clamp(a, min=-0.5, max=0.5) tensor([-0.5000, 0.1734, -0.0478, -0.0922])#当然也可以只设置一边>>> a = torch.randn(4)>>> a tensor([-0.0299, -2.3184, 2.1593, -0.8883])>>> torch.clamp(a, min=0.5) tensor([ 0.5000, 0.5000, 2.1593, 0.5000])>>> a = torch.randn(4)>>> a tensor([ 0.0753, -0.4702, -0.4599, 0.1899])>>> torch.clamp(a, max=0.5) tensor([ 0.0753, -0.4702, -0.4599, 0.1899])
三角函数
#余弦torch.cos(input, out=None)>>> a = torch.randn(4)>>> a tensor([ 1.4309, 1.2706, -0.8562, 0.9796])>>> torch.cos(a) tensor([ 0.1395, 0.2957, 0.6553, 0.5574])#双曲余弦torch.cosh(input, out=None) >>> a = torch.randn(4)>>> a tensor([ 0.1632, 1.1835, -0.6979, -0.7325])>>> torch.cosh(a) tensor([ 1.0133, 1.7860, 1.2536, 1.2805])
元素数除法,可以除数也可以除tensor
torch.div()>>> a = torch.randn(5)>>> a tensor([ 0.3810, 1.2774, -0.2972, -0.3719, 0.4637])>>> torch.div(a, 0.5) tensor([ 0.7620, 2.5548, -0.5944, -0.7439, 0.9275])>>> a = torch.randn(4, 4)>>> a tensor([[-0.3711, -1.9353, -0.4605, -0.2917], [ 0.1815, -1.0111, 0.9805, -1.5923], [ 0.1062, 1.4581, 0.7759, -1.2344], [-0.1830, -0.0313, 1.1908, -1.4757]])>>> b = torch.randn(4)>>> b tensor([ 0.8032, 0.2930, -0.8113, -0.2308])>>> torch.div(a, b) tensor([[-0.4620, -6.6051, 0.5676, 1.2637], [ 0.2260, -3.4507, -1.2086, 6.8988], [ 0.1322, 4.9764, -0.9564, 5.3480], [-0.2278, -0.1068, -1.4678, 6.3936]])
计算元素除法的各项余数:
torch.fmod(input, divisor, out=None) >>> torch.fmod(torch.tensor([-3., -2, -1, 1, 2, 3]), 2) tensor([-1., -0., -1., 1., 0., 1.])>>> torch.fmod(torch.tensor([1., 2, 3, 4, 5]), 1.5) tensor([ 1.0000, 0.5000, 0.0000, 1.0000, 0.5000])
作者:shaozi_ss
链接:https://www.jianshu.com/p/cf7adeff2a05
共同學習,寫下你的評論
評論加載中...
作者其他優質文章