深度学习2
四、tensor常见操作
1、元素值
1.1、获取元素值
tensor.item() 返回tensor的元素;只能在一个元素值使用,多个报错,当存在多个元素值时需要使用索引进行获取到一个元素值时在使用 item。
1.2、元素值运算
tensor对元素值的运算:加、减、乘、除、取余、取整、幂
加减乘除:加法(+、add、add_),减法(-、sub、sub_),乘法(*、mul、mul_),除法(/、div、div_)
取整、取余、幂:取整(//)、取余(%):与python 列表操作一致,每个元素都进行单独运算。
# 运算 加减乘除
# 带_的方法基本上都是修改原数据
import torch
torch.manual_seed(666) # 设置随机种子
data1 = torch.randint(1,10,(3,3))
data2 = torch.randint(1,10,(3,3))
# 加法
print("+++++++++")
x1 = data1+100
print(x1)
x1 = data1.add(100)
print(x1)
data2.add_(200)
print(data2)
# 减法
print("----------")
x1 = data1-100
print(x1)
x1 = data1.sub(100)
print(x1)
data2.sub_(200)
print(data2)
# 乘法
print("**********")
x1 = data1*100
print(x1)
x1 = data1.mul(100)
print(x1)
data2.mul_(200)
print(data2)
# 除法
print("/")
x1 = data1/100
print(x1)
data2 =data2.type(torch.float16) # 原数据为整数,需要转为浮点数
x1 = data1.div(100)
print(x1)
data2.div_(200)
print(data2)
2、阿达玛积
.dot 点积、* 乘法 、mul 、mm、matmul、@
直接相乘或mul方法都是元素对于位置相乘(形状相同,或者一个为标量)
tensor相乘:data1.dot(data2) 点积用于一阶张量;
data1.mm(data2) 用于二阶张量;
data1@data2、data1.matmul(data2) 用于一阶及以上张量
import torch
# 相乘 * mul()
x = torch.tensor([[1,2],[3,4]])
y = torch.tensor([[11,22],[33,44]])
x1 = x*y
print(x1)
x2 = x.mul(y)
print(x2)
# dot matmul
x = torch.tensor([1,2])
y = torch.tensor([11,22])
x3 = x.dot(y)
print(x3)
x3 = x.matmul(y)
print(x3)
x4 = x@y
print(x4)
# @ matmul mm
x = torch.tensor([[1,2],[3,4]])
y = torch.tensor([[11,22],[33,44]])
x4 = x@(y)
print(x4)
x5 = x.matmul(y)
print(x5)
x6 = x.mm(y)
print(x6)
3、索引操作
3.1、下标索引
和列表操作一样,根据元素下标获取对应元素
import torch
torch.manual_seed(666)
x = torch.randint(1,10,(2,3,3))
print(x)
print(x[1])
print(x[1,2])
print(x[1,2,2])
print(x[1,2,2].item())
3.2、切片索引
与列表操作一致
import torch
torch.manual_seed(666)
x = torch.randint(1,10,(2,3,3))
print(x[:5]) # 下标超出索引界限并不会报错,给出全部数据
print(x[:2,2])
print(x[:2,2:3])
print(x[:2,2:3,1])
print(x[:2,2:3,1:2])
print(x[:2,2:3,1:2][1])
print(x[:2,2:3,1:2][1].item())
3.3、布尔索引
根据得到True返回对应位置的元素,单独索引列时,将该列数据变成一维布尔值结果,根据结果True的下标再去原函数获取对应数据
import torch
torch.manual_seed(666)
x = torch.randint(1,10,(5,5))
print(x)
print(x[x==1])
print(x[x>5])
3.4、组合索引
将前面三种索引方式组合使用
多个下标索引:
下标索引使用列表时,分别取对于数据的多行或多列:
x[[1,2]] 取第二行和第三行的所有列;
x[,[1,2]] 取所有行的第二列和第三列;
当原数据的行和列都是列表形式时,数据一一对应:也就是从前往后是 1对1,1对n,n对1,n对n,不能n对m:
错误:x[[0,1],[0,1,2]]
正确应该是:x[1,[0,1]] 表示第二行的第一列和第二列;x[[0,1],1] 表示第一行和第二行的第二列;x[[0,1],[0,1]] 表示第一行的第一列和第二行的第二列数据
多个切片索引:维度保持不变,x[:2,:2]表示行取0-1,列取0-1的数据
布尔索引和切片索引:x[x[1]==1, :2]:将x中第二行等于1的数据下标返回作为行下标索引,切0-1列数据
import torch
torch.manual_seed(15)
x = torch.randint(2000,3000,(5,5))
print(x)
x1 = x[(x[:,0]%2==0) & (x[:,1]%2==1) & (x[:,2]%4==0) & (x[:,2]%100!=0),3:5]
print(x1)
4、拼接
cat 和 stack ;
dim 表示维度
torch.cat([tensor1,tnsor2],dim =0):将tensor1和ensor2拼接;dim=0表示按照行第一维度拼接,添加行,列不变;dim=1表示按照列拼接,行不变;dim=3表示行列格式不变,将元素加维。
import torch
torch.manual_seed(666)
x = torch.randint(1,10,(2,2,2))
y = torch.randint(10,20,(2,2,2))
print(x)
print(y)
z = torch.cat([x,y],dim = 0)
print(z)
z = torch.cat([x,y],dim = 1)
print(z)
z = torch.cat([x,y],dim = 2)
print(z)
torch.stack([tensor1,tensor2],dim=0):表示将tensor1和tensor2连接;dim=0表示将两个整体放入列表的第一个元素和第二个元素的方式连接;dim=1表示扩展维度后将两个tensor的行下标相同作为同一列的不同行;dim=2表示将原本同一行的数据变成同一列不同行,相当于把第一个列拆分放入下面最接近的行,将第二个同样位置放入第二列,扩展维度后两个tensor的对应位置元素相同作为同一行的不同列
import torch
torch.manual_seed(666)
x = torch.randint(1,10,(2,2))
y = torch.randint(10,20,(2,2))
print(x)
print(y)
z = torch.stack([x,y],dim = 0)
print(z)
z1 = torch.stack([x,y],dim = 1)
print(z1)
z2 = torch.stack([x,y],dim = 2)
print(z2)
5、形状操作
5.1、形状重组
reshape(size)、view(size)
reshape(size):与数组一致,改变tensor形状
import torch
# reshape
data = torch.randint(0, 10, (4, 3))
print(data)
# 1. 使用reshape改变形状
data1 = data.reshape(2, 2, 3)
print(data1)
# 2. 使用-1表示自动计算
data2= data.reshape(2, -1)
print(data2)
view(size):将内存连续的tensor形状改变返回新的内存连续tensor,reshape内存不连续
import torch
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 使用view进行变形操作
tensor = tensor.view(2, -1)
print(tensor)
# 再次使用view操作
try:
tensor = tensor.view(3, -1)
print("可以使用view")
except:
print("不可以使用view")
# 进行转置
tensor = tensor.t()
print(tensor)
# 转置后使用view操作
try:
tensor.view(3, -1)
print("可以使用view")
except:
print("不可以使用view")
5.2、维度元素个数交换
transpose(下标)、permute(下标)
transpose(维度下标):将两个指定的维度交换
import torch
# transpose
x = torch.tensor([[[1, 2, 3], [4, 5, 6]],[[11, 22, 33], [44, 55, 66]]])
print(x,x.shape)
x1 = x.transpose(1,0) # 在不改变其他维度下进行了转置
print(x1,x1.shape)
permute(维度下标):多个下标,根据设置的顺序重新排列维度元素
import torch
# permute
torch.manual_seed(666)
x = torch.randint(0,255,(2,3,4))
print(x,x.shape)
x2 = x.permute(2,0,1)
print(x2,x2.shape)
x3 = x.permute(2,1,0)
print(x3,x3.shape)
5.3、维度展开
flatten(start_dim,end_dim)
flatten(start_dim,end_dim):默认从0到-1,所有维度都展开为一维;从start_dim维度开始,到end_dim-1维度结束展开
import torch
x = torch.randint(0,255,(2,3,2))
print(x)
x1 = x.flatten()
print(x1)
x2 = x.flatten(start_dim=0,end_dim=1)
print(x2)
5.4、降维和升维
squeeze()和unsqueeze()
squeeze:无参数默认将自动将所有元素个数为1的维度展开,参数为整数,表示维度下标
import torch
import torch
# 升维和降维
x = torch.randint(0,255,(1,3,4,1))
print(x.shape) # torch.Size([1, 3, 4, 1])
# 元素个数为1的全部降维
x1= x.squeeze()
print(x1.shape) # torch.Size([3, 4])
# 第一个维度释放
x11= x.squeeze(0) # torch.Size([3, 4, 1])
print(x11.shape)
# 释放第二个维度操作失败,因为有多个元素
x11= x.squeeze(1) # torch.Size([1, 3, 4, 1])
print(x11.shape)
# 升维度,必须填写参数
x2 = x.unsqueeze(0) # torch.Size([1, 1, 3, 4, 1])
print(x2.shape)
x2 = x.unsqueeze(2) # torch.Size([1, 3, 1, 4, 1])
print(x2.shape)
6、分割
chunk 和 split;参数都为(tensor,num,dim)
chunk 控制分割结果多少
split 控制分割数量大小
torch.chunk(x, 3,dim=0):参数1为tnsor对象;参数2为分割成多少个结果(不够数量,则值保留原大小,比如5个内容分为3份(2,2,1),2个内容分两份(1,1));参数3为dim,指定维度分隔
torch.split(x, 3,dim=0):参数1为tnsor对象;参数2为分割数量大小(不够数量,则值保留原大小,比如设置值为3,在原说内容切割3个内容分块,不够数量就只有一块);参数3为dim,指定维度分隔
import torch
x = torch.tensor([[1, 2],
[4, 5],
[7, 8],
[10, 11],
[13, 14]
])
# 分割成3块
print(torch.chunk(x, 3,dim=0))
print(torch.chunk(x, 3,dim=1))
# 按照每块大小为4进行分割
print(torch.split(x, 4,dim=0))
print(torch.split(x, 4,dim=1))
7、广播
两个tensor 直接加法或减法运算时,在某个维度的元素个数相等;某个tensor维度元素个数全是1;全部维度元素个数相等
若全部相等:直接对应元素计算
若某个维度相等,某个tenser其余维度元素个数全是1:将其广播到另一个tensor维度个数大小(复制1那个内容数据)在进行计算
若某个tensoru维度元素个数全为1,则广播值对应tensor维度个数
import torch
# 某些维度相等,剩下维度个数为1
data1d = torch.tensor([[[1,1,1,1]],[[1,1,1,1]]])
data2d = torch.tensor([[[11,11,11,11], [22,22,22,22]],[[11,11,11,11], [22,22,22,22]]])
print(data1d.shape, data2d.shape)
# 进行计算:会自动进行广播机制
print(data1d + data2d)
# 某些维度相等,剩下维度个数为1
data1d = torch.tensor([[[1,1,1,1]]])
data2d = torch.tensor([[[11,11,11,11], [22,22,22,22]],[[11,11,11,11], [22,22,22,22]]])
print(data1d.shape, data2d.shape)
# 进行计算:会自动进行广播机制
print(data1d + data2d)
# 一个rensor维度个数全为1
data1d = torch.tensor([[1]])
data2d = torch.tensor([[[11,11,11,11], [22,22,22,22]],[[11,11,11,11], [22,22,22,22]]])
print(data1d.shape, data2d.shape)
# 进行计算:会自动进行广播机制
print(data1d + data2d)
8、数据的函数运算
floor:向左取值;ceil:向右取值;round:四舍五入;trunc:保留整数;frac:保留小数;fix:向零方向取整数;abs:取绝对值;取余%:-3%2 结果为 1
import torch
data = torch.tensor(
[[1,2,-3.5]
,[4,5.1,6.5]
,[7.3,8,9.5]
,[10,11.8,-12.5]]
)
print(data)
x1 = torch.floor(data) # 向左取值
print(x1)
x1 = torch.ceil(data) # 向右取值
print(x1)
# 使用python的round()函数
# 四舍六入,五看整数位数的奇偶性,奇进偶不进
x1 = torch.round(data) # 使用python的round()函数
print(x1)
x1 = torch.trunc(data) # 只保留整数部分
print(x1)
x1 = torch.frac(data) # 只保留小数部分
print(x1)
x1 = torch.fix(data) # 向零方向保留整数(整数操作floor,负数操作ceil)
print(x1)
x1 = data%2 # 取余 整数减,负数加
print(x1)
x1 = torch.abs(data) # 取绝对值
print(x1)
9、张量保存与加载
save 和 load
import torch
x = torch.tensor([1, 2 ,3])
# save 保存到本地
torch.save(x, '../../data/tengsor_save.pt')
# load 加载本地保存文件
x = torch.load("../../data/tengsor_save.pt",torch.device('cuda') )
print(x.device)
10、并行化
get_num_threads 查看;set_num_threads 设置
线程数设置过高可能会导致线程竞争,反而降低性能;设置过低可能会导致计算资源未得到充分利用;当使用 GPU 进行计算时,线程数设置对性能影响较小,因为 GPU 计算并不依赖于 CPU 线程数
import torch
print(torch.get_num_threads())
torch.set_num_threads(4)
print(torch.get_num_threads())