1.PyTorch数据结构Tensor常用操作

import torch  as t
t.__version__
'2.1.1'

从接口的角度来讲,对tensor的操作可分为两类:

  1. torch.function,如torch.save等。
  2. 另一类是tensor.function,如tensor.view等。

为方便使用,对tensor的大部分操作同时支持这两类接口,如torch.sum (torch.sum(a, b))tensor.sum (a.sum(b))功能等价。

而从存储的角度来讲,对tensor的操作又可分为两类:

  1. 不会修改自身的数据,如 a.add(b), 加法的结果会返回一个新的tensor。
  2. 会修改自身的数据,如 a.add_(b), 加法的结果仍存储在a中,a被修改了。

函数名以_结尾的都是inplace方式, 即会修改调用者自己的数据,在实际应用中需加以区分。

1.1 创建Tensor

在PyTorch中新建tensor的方法有很多。

表1-1: 常见新建tensor的方法

函数功能
Tensor(*sizes)基础构造函数
tensor(data,)类似np.array的构造函数
ones(*sizes)全1Tensor
zeros(*sizes)全0Tensor
eye(*sizes)对角线为1,其他为0
arange(s,e,step)从s到e,步长为step
linspace(s,e,steps)从s到e,均匀切分成steps份
rand/randn(*sizes)均匀/标准分布
normal(mean,std)/uniform(from,to)正态分布/均匀分布
randperm(m)随机排列

这些创建方法都可以在创建的时候指定数据类型dtype和存放device(cpu/gpu).

其中使用Tensor函数新建tensor是最复杂多变的方式,它既可以接收一个list,并根据list的数据新建tensor,也能根据指定的形状新建tensor,还能传入其他的tensor,下面举几个例子。

# 指定tensor的形状
a = t.Tensor(2, 3)
a               # 数值取决于内存空间的状态,print时候可能overflow
tensor([[-1.0752245760e+09,  1.4601529998e-42,  1.4012984643e-45],[ 0.0000000000e+00,  1.4012984643e-45,  0.0000000000e+00]])
# 用list的数据创建tensor
b = t.Tensor([[1,2,3],[4,5,6]])
b
tensor([[1., 2., 3.],[4., 5., 6.]])
b.tolist()         # 把tensor转为list
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]

tensor.size()返回torch.Size对象,它是tuple的子类,但其使用方式与tuple略有区别

b_size = b.size()
b_size
torch.Size([2, 3])
b.numel()         # b中元素总个数,2*3,等价于b.nelement()
6
# 创建一个和b形状一样的tensor
c = t.Tensor(b_size)
# 创建一个元素为2和3的tensor
d = t.Tensor((2, 3))
c, d
(tensor([[1.4012984643e-45, 0.0000000000e+00, 1.4012984643e-45],[0.0000000000e+00, 1.4012984643e-45, 0.0000000000e+00]]),tensor([2., 3.]))

除了tensor.size(),还可以利用tensor.shape直接查看tensor的形状,tensor.shape等价于tensor.size()

c.shape
torch.Size([2, 3])

需要注意的是,t.Tensor(*sizes)创建tensor时,系统不会马上分配空间,只是会计算剩余的内存是否足够使用,使用到tensor时才会分配,而其它操作都是在创建完tensor之后马上进行空间分配。其它常用的创建tensor的方法举例如下。

t.ones(2, 3)
tensor([[1., 1., 1.],[1., 1., 1.]])
t.zeros(2, 3)
tensor([[0., 0., 0.],[0., 0., 0.]])
t.arange(1, 6, 2)
tensor([1, 3, 5])
t.linspace(1, 10, 3)
tensor([ 1.0000000000,  5.5000000000, 10.0000000000])
t.randn(2, 3, device=t.device('cpu'))
tensor([[-1.1603765488, -1.2580411434, -0.3872119784],[-0.9962984324, -2.3714294434, -1.5460534096]])
t.randperm(5) # 长度为5的随机排列
tensor([1, 4, 0, 2, 3])
t.eye(2, 3, dtype=t.int) # 对角线为1, 不要求行列数一致
tensor([[1, 0, 0],[0, 1, 0]], dtype=torch.int32)

torch.tensor是在0.4版本新增加的一个新版本的创建tensor方法,使用的方法,和参数几乎和np.array完全一致

scalar = t.tensor(3.14159) 
print('scalar: %s, shape of sclar: %s' %(scalar, scalar.shape))
scalar: tensor(3.1415901184), shape of sclar: torch.Size([])
vector = t.tensor([1, 2])
print('vector: %s, shape of vector: %s' %(vector, vector.shape))
vector: tensor([1, 2]), shape of vector: torch.Size([2])
tensor = t.Tensor(1,2) # 注意和t.tensor([1, 2])的区别
tensor.shape
torch.Size([1, 2])
matrix = t.tensor([[0.1, 1.2], [2.2, 3.1], [4.9, 5.2]])
matrix,matrix.shape
(tensor([[0.1000000015, 1.2000000477],[2.2000000477, 3.0999999046],[4.9000000954, 5.1999998093]]),torch.Size([3, 2]))
t.tensor([[0.11111, 0.222222, 0.3333333]],dtype=t.float64,device=t.device('cpu'))
tensor([[0.1111100000, 0.2222220000, 0.3333333000]], dtype=torch.float64)
empty_tensor = t.tensor([])
empty_tensor.shape
torch.Size([0])

1.2 常用Tensor操作

通过tensor.view方法可以调整tensor的形状,但必须保证调整前后元素总数一致。view不会修改自身的数据,返回的新tensor与源tensor共享内存,也即更改其中的一个,另外一个也会跟着改变。在实际应用中可能经常需要添加或减少某一维度,这时候squeezeunsqueeze两个函数就派上用场了。

a = t.arange(0, 6)
a.view(2, 3)
tensor([[0, 1, 2],[3, 4, 5]])
b = a.view(-1, 3) # 当某一维为-1的时候,会自动计算它的大小
b.shape
torch.Size([2, 3])
b.unsqueeze(1) # 注意形状,在第1维(下标从0开始)上增加“1” 
#等价于 b[:,None]
b[:, None].shape
torch.Size([2, 1, 3])
b.unsqueeze(-2) # -2表示倒数第二个维度
tensor([[[0, 1, 2]],[[3, 4, 5]]])
c = b.view(1, 1, 1, 2, 3)
c.squeeze(0) # 压缩第0维的“1”
tensor([[[[0, 1, 2],[3, 4, 5]]]])
c.squeeze() # 把所有维度为“1”的压缩
tensor([[0, 1, 2],[3, 4, 5]])
a[1] = 100
b # a修改,b作为view之后的,也会跟着修改
tensor([[  0, 100,   2],[  3,   4,   5]])

resize是另一种可用来调整size的方法,但与view不同,它可以修改tensor的大小。如果新大小超过了原大小,会自动分配新的内存空间,而如果新大小小于原大小,则之前的数据依旧会被保存,看一个例子。

b.resize_(1, 3)
b
tensor([[  0, 100,   2]])
b.resize_(3, 3) # 旧的数据依旧保存着,多出的大小会分配新空间
b
tensor([[  0, 100,   2],[  3,   4,   5],[  0,   0,   0]])

1.3 索引操作

Tensor支持与numpy.ndarray类似的索引操作,语法上也类似,下面通过一些例子,讲解常用的索引操作。如无特殊说明,索引出来的结果与原tensor共享内存,也即修改一个,另一个会跟着修改。

a = t.randn(3, 4)
a
tensor([[-0.2404960245, -0.1326600760, -0.0196023099,  1.7071028948],[ 0.7344398499,  1.9282346964,  1.7709469795,  0.4925989807],[-0.2727401257, -0.0743735656, -1.0019408464, -0.6791635156]])
a[0] # 第0行(下标从0开始)
tensor([-0.2404960245, -0.1326600760, -0.0196023099,  1.7071028948])
a[:, 0] # 第0列
tensor([-0.2404960245,  0.7344398499, -0.2727401257])
a[0][2] # 第0行第2个元素,等价于a[0, 2]
tensor(-0.0196023099)
a[0, -1] # 第0行最后一个元素
tensor(1.7071028948)
a[:2] # 前两行
tensor([[-0.2404960245, -0.1326600760, -0.0196023099,  1.7071028948],[ 0.7344398499,  1.9282346964,  1.7709469795,  0.4925989807]])
a[:2, 0:2] # 前两行,第0,1列
tensor([[-0.2404960245, -0.1326600760],[ 0.7344398499,  1.9282346964]])
print(a[0:1, :2]) # 第0行,前两列 
print(a[0, :2]) # 注意两者的区别:形状不同
tensor([[-0.2404960245, -0.1326600760]])
tensor([-0.2404960245, -0.1326600760])
# None类似于np.newaxis, 为a新增了一个轴
# 等价于a.view(1, a.shape[0], a.shape[1])
a[None].shape
torch.Size([1, 3, 4])
a[None].shape # 等价于a[None,:,:]
torch.Size([1, 3, 4])
a[:,None,:].shape
torch.Size([3, 1, 4])
a[:,None,:,None,None].shape
torch.Size([3, 1, 4, 1, 1])
a > 1 # 返回一个ByteTensor
tensor([[False, False, False,  True],[False,  True,  True, False],[False, False, False, False]])
a[a>1] # 等价于a.masked_select(a>1)
# 选择结果与原tensor不共享内存空间
tensor([1.7071028948, 1.9282346964, 1.7709469795])
a[t.LongTensor([0,1])] # 第0行和第1行
tensor([[-0.2404960245, -0.1326600760, -0.0196023099,  1.7071028948],[ 0.7344398499,  1.9282346964,  1.7709469795,  0.4925989807]])

其它常用的选择函数如表3-2所示。

表3-2常用的选择函数

函数功能
index_select(input, dim, index)在指定维度dim上选取,比如选取某些行、某些列
masked_select(input, mask)例子如上,a[a>0],使用ByteTensor进行选取
non_zero(input)非0元素的下标
gather(input, dim, index)根据index,在dim维度上选取数据,输出的size与index一样

gather是一个比较复杂的操作,对一个2维tensor,输出的每个元素如下:

out[i][j] = input[index[i][j]][j]  # dim=0
out[i][j] = input[i][index[i][j]]  # dim=1

三维tensor的gather操作同理,下面举几个例子。

a = t.arange(0, 16).view(4, 4)
a
tensor([[ 0,  1,  2,  3],[ 4,  5,  6,  7],[ 8,  9, 10, 11],[12, 13, 14, 15]])
# 选取对角线的元素
index = t.LongTensor([[0,1,2,3]])
a.gather(0, index)
tensor([[ 0,  5, 10, 15]])
# 选取反对角线上的元素
index = t.LongTensor([[3,2,1,0]]).t()
a.gather(1, index)
tensor([[ 3],[ 6],[ 9],[12]])
# 选取反对角线上的元素,注意与上面的不同
index = t.LongTensor([[3,2,1,0]])
a.gather(0, index)
tensor([[12,  9,  6,  3]])
# 选取两个对角线上的元素
index = t.LongTensor([[0,1,2,3],[3,2,1,0]]).t()
b = a.gather(1, index)
b
tensor([[ 0,  3],[ 5,  6],[10,  9],[15, 12]])

gather相对应的逆操作是scatter_gather把数据从input中按index取出,而scatter_是把取出的数据再放回去。注意scatter_函数是inplace操作。

out = input.gather(dim, index)
-->近似逆操作
out = Tensor()
out.scatter_(dim, index)
# 把两个对角线元素放回去到指定位置
c = t.zeros(4,4)
c.scatter_(1, index, b.float())
tensor([[ 0.,  0.,  0.,  3.],[ 0.,  5.,  6.,  0.],[ 0.,  9., 10.,  0.],[12.,  0.,  0., 15.]])

对tensor的任何索引操作仍是一个tensor,想要获取标准的python对象数值,需要调用tensor.item(), 这个方法只对包含一个元素的tensor适用

a[0,0] #依旧是tensor)
tensor(0)
a[0,0].item() # python float
0
d = a[0:1, 0:1, None]
print(d.shape)
d.item() # 只包含一个元素的tensor即可调用tensor.item,与形状无关
torch.Size([1, 1, 1])0
# a[0].item()  ->
# raise ValueError: only one element tensors can be converted to Python scalars

1.4 高级索引

PyTorch在0.2版本中完善了索引操作,目前已经支持绝大多数numpy的高级索引1。高级索引可以看成是普通索引操作的扩展,但是高级索引操作的结果一般不和原始的Tensor共享内存。

x = t.arange(0,27).view(3,3,3)
x
tensor([[[ 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]]])
x[[1, 2], [1, 2], [2, 0]] # x[1,1,2]和x[2,2,0]
tensor([14, 24])
x[[2, 1, 0], [0], [1]] # x[2,0,1],x[1,0,1],x[0,0,1]
tensor([19, 10,  1])
x[[0, 2], ...] # x[0] 和 x[2]
tensor([[[ 0,  1,  2],[ 3,  4,  5],[ 6,  7,  8]],[[18, 19, 20],[21, 22, 23],[24, 25, 26]]])

1.5 Tensor类型

Tensor有不同的数据类型,如表1-3所示,每种类型分别对应有CPU和GPU版本(HalfTensor除外)。默认的tensor是FloatTensor,可通过t.set_default_tensor_type 来修改默认tensor类型(如果默认类型为GPU tensor,则所有操作都将在GPU上进行)。Tensor的类型对分析内存占用很有帮助。例如对于一个size为(1000, 1000, 1000)的FloatTensor,它有1000*1000*1000=10^9个元素,每个元素占32bit/8 = 4Byte内存,所以共占大约4GB内存/显存。HalfTensor是专门为GPU版本设计的,同样的元素个数,显存占用只有FloatTensor的一半,所以可以极大缓解GPU显存不足的问题,但由于HalfTensor所能表示的数值大小和精度有限2,所以可能出现溢出等问题。

表1-3: tensor数据类型

Data typedtypeCPU tensorGPU tensor
32-bit floating pointtorch.float32 or torch.floattorch.FloatTensortorch.cuda.FloatTensor
64-bit floating pointtorch.float64 or torch.doubletorch.DoubleTensortorch.cuda.DoubleTensor
16-bit floating pointtorch.float16 or torch.halftorch.HalfTensortorch.cuda.HalfTensor
8-bit integer (unsigned)torch.uint8torch.ByteTensortorch.cuda.ByteTensor
8-bit integer (signed)torch.int8torch.CharTensortorch.cuda.CharTensor
16-bit integer (signed)torch.int16 or torch.shorttorch.ShortTensortorch.cuda.ShortTensor
32-bit integer (signed)torch.int32 or torch.inttorch.IntTensortorch.cuda.IntTensor
64-bit integer (signed)torch.int64 or torch.longtorch.LongTensortorch.cuda.LongTensor

各数据类型之间可以互相转换,type(new_type)是通用的做法,同时还有floatlonghalf等快捷方法。CPU tensor与GPU tensor之间的互相转换通过tensor.cudatensor.cpu方法实现,此外还可以使用tensor.to(device)。Tensor还有一个new方法,用法与t.Tensor一样,会调用该tensor对应类型的构造函数,生成与当前tensor类型一致的tensor。torch.*_like(tensora) 可以生成和tensora拥有同样属性(类型,形状,cpu/gpu)的新tensor。 tensor.new_*(new_shape) 新建一个不同形状的tensor。

# 设置默认tensor,注意参数是字符串
t.set_default_tensor_type('torch.DoubleTensor')
a = t.Tensor(2,3)
a.dtype # 现在a是DoubleTensor,dtype是float64
torch.float64
# 恢复之前的默认设置
t.set_default_tensor_type('torch.FloatTensor')
# 把a转成FloatTensor,等价于b=a.type(t.FloatTensor)
b = a.float() 
b.dtype
torch.float32
c = a.type_as(b)
c
tensor([[0.0000000000e+00, 8.8863861561e-01, 4.0106318193e-04],[1.0096810608e-14, 3.7590373540e-04, 2.0494908676e-04]])
a.new(2,3) # 等价于torch.DoubleTensor(2,3),建议使用a.new_tensor
tensor([[0.1000000015, 1.2000000477, 2.2000000477],[3.0999999046, 4.9000000954, 5.1999998093]], dtype=torch.float64)
t.zeros_like(a) #等价于t.zeros(a.shape,dtype=a.dtype,device=a.device)
tensor([[0., 0., 0.],[0., 0., 0.]], dtype=torch.float64)
t.zeros_like(a, dtype=t.int16) #可以修改某些属性
tensor([[0, 0, 0],[0, 0, 0]], dtype=torch.int16)
t.rand_like(a)
tensor([[0.4587583271, 0.0597746880, 0.3043038499],[0.8112906815, 0.1569333205, 0.7714216456]], dtype=torch.float64)
a.new_ones(4,5, dtype=t.int)
tensor([[1, 1, 1, 1, 1],[1, 1, 1, 1, 1],[1, 1, 1, 1, 1],[1, 1, 1, 1, 1]], dtype=torch.int32)
a.new_tensor([3,4]) # 
tensor([3., 4.], dtype=torch.float64)

1.6 逐元素操作

这部分操作会对tensor的每一个元素(point-wise,又名element-wise)进行操作,此类操作的输入与输出形状一致。常用的操作如表1-4所示。

表1-4: 常见的逐元素操作

函数功能
abs/sqrt/div/exp/fmod/log/pow…绝对值/平方根/除法/指数/求余/求幂…
cos/sin/asin/atan2/cosh…相关三角函数
ceil/round/floor/trunc上取整/四舍五入/下取整/只保留整数部分
clamp(input, min, max)超过min和max部分截断
sigmod/tanh…激活函数

对于很多操作,例如div、mul、pow、fmod等,PyTorch都实现了运算符重载,所以可以直接使用运算符。如a ** 2 等价于torch.pow(a,2), a * 2等价于torch.mul(a,2)

其中clamp(x, min, max)的输出满足以下公式:
y i = { m i n , if  x i < m i n x i , if  m i n ≤ x i ≤ m a x m a x , if  x i > m a x y_i = \begin{cases} min, & \text{if } x_i \lt min \\ x_i, & \text{if } min \le x_i \le max \\ max, & \text{if } x_i \gt max\\ \end{cases} yi= min,xi,max,if xi<minif minximaxif xi>max
clamp常用在某些需要比较大小的地方,如取一个tensor的每个元素与另一个数的较大值。

a = t.arange(0, 6).view(2, 3).float()
t.cos(a)
tensor([[ 1.0000000000,  0.5403023362, -0.4161468446],[-0.9899924994, -0.6536436081,  0.2836622000]])
a % 3 # 等价于t.fmod(a, 3)
tensor([[0., 1., 2.],[0., 1., 2.]])
a ** 2 # 等价于t.pow(a, 2)
tensor([[ 0.,  1.,  4.],[ 9., 16., 25.]])
# 取a中的每一个元素与3相比较大的一个 (小于3的截断成3)
print(a)
t.clamp(a, min=3)
tensor([[0., 1., 2.],[3., 4., 5.]])tensor([[3., 3., 3.],[3., 4., 5.]])
b = a.sin_() # 效果同 a = a.sin();b=a ,但是更高效节省显存
a
tensor([[ 0.0000000000,  0.8414709568,  0.9092974067],[ 0.1411200017, -0.7568024993, -0.9589242935]])

1.7 归并操作

此类操作会使输出形状小于输入形状,并可以沿着某一维度进行指定操作。如加法sum,既可以计算整个tensor的和,也可以计算tensor中每一行或每一列的和。常用的归并操作如表1-5所示。

表1-5: 常用归并操作

函数功能
mean/sum/median/mode均值/和/中位数/众数
norm/dist范数/距离
std/var标准差/方差
cumsum/cumprod累加/累乘

以上大多数函数都有一个参数**dim**,用来指定这些操作是在哪个维度上执行的。关于dim(对应于Numpy中的axis)的解释众说纷纭,这里提供一个简单的记忆方式:

假设输入的形状是(m, n, k)

  • 如果指定dim=0,输出的形状就是(1, n, k)或者(n, k)
  • 如果指定dim=1,输出的形状就是(m, 1, k)或者(m, k)
  • 如果指定dim=2,输出的形状就是(m, n, 1)或者(m, n)

size中是否有"1",取决于参数keepdimkeepdim=True会保留维度1。注意,以上只是经验总结,并非所有函数都符合这种形状变化方式,如cumsum

b = t.ones(2, 3)
b.sum(dim = 0, keepdim=True)
tensor([[2., 2., 2.]])
# keepdim=False,不保留维度"1",注意形状
b.sum(dim=0, keepdim=False)
tensor([2., 2., 2.])
b.sum(dim=1)
tensor([3., 3.])
a = t.arange(0, 6).view(2, 3)
print(a)
a.cumsum(dim=1) # 沿着行累加
tensor([[0, 1, 2],[3, 4, 5]])tensor([[ 0,  1,  3],[ 3,  7, 12]])

1.8 比较

比较函数中有一些是逐元素比较,操作类似于逐元素操作,还有一些则类似于归并操作。常用比较函数如表1-6所示。

表1-6: 常用比较函数

函数功能
gt/lt/ge/le/eq/ne大于/小于/大于等于/小于等于/等于/不等
topk最大的k个数
sort排序
max/min比较两个tensor最大最小值

表中第一行的比较操作已经实现了运算符重载,因此可以使用a>=ba>ba!=ba==b,其返回结果是一个ByteTensor,可用来选取元素。max/min这两个操作比较特殊,以max来说,它有以下三种使用情况:

  • t.max(tensor):返回tensor中最大的一个数
  • t.max(tensor,dim):指定维上最大的数,返回tensor和下标
  • t.max(tensor1, tensor2): 比较两个tensor相比较大的元素

至于比较一个tensor和一个数,可以使用clamp函数。下面举例说明。

a = t.linspace(0, 15, 6).view(2, 3)
a
tensor([[ 0.,  3.,  6.],[ 9., 12., 15.]])
b = t.linspace(15, 0, 6).view(2, 3)
b
tensor([[15., 12.,  9.],[ 6.,  3.,  0.]])
a>b
tensor([[False, False, False],[ True,  True,  True]])
a[a>b] # a中大于b的元素
tensor([ 9., 12., 15.])
t.max(a)
tensor(15.)
t.max(b, dim=1) 
# 第一个返回值的15和6分别表示第0行和第1行最大的元素
# 第二个返回值的0和0表示上述最大的数是该行第0个元素
torch.return_types.max(
values=tensor([15.,  6.]),
indices=tensor([0, 0]))
t.max(a,b)
tensor([[15., 12.,  9.],[ 9., 12., 15.]])
# 比较a和10较大的元素
t.clamp(a, min=10)
tensor([[10., 10., 10.],[10., 12., 15.]])

1.9 线性代数

PyTorch的线性函数主要封装了Blas和Lapack,其用法和接口都与之类似。常用的线性代数函数如表1-7所示。

表1-7: 常用的线性代数函数

函数功能
trace对角线元素之和(矩阵的迹)
diag对角线元素
triu/tril矩阵的上三角/下三角,可指定偏移量
mm/bmm矩阵乘法,batch的矩阵乘法
addmm/addbmm/addmv/addr/badbmm…矩阵运算
t转置
dot/cross内积/外积
inverse求逆矩阵
svd奇异值分解

具体使用说明请参见官方文档3,需要注意的是,矩阵的转置会导致存储空间不连续,需调用它的.contiguous方法将其转为连续。

b = a.t()
b.is_contiguous()
False
b.contiguous()
tensor([[ 0.,  9.],[ 3., 12.],[ 6., 15.]])

  1. https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#advanced-indexing ↩︎

  2. https://stackoverflow.com/questions/872544/what-range-of-numbers-can-be-represented-in-a-16-32-and-64-bit-ieee-754-syste ↩︎

  3. http://pytorch.org/docs/torch.html#blas-and-lapack-operations ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/205914.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET 8 中 Android 资源生成的改进和变化

作者&#xff1a;Dean Ellis 排版&#xff1a;Alan Wang 随着 .NET 8 的发布&#xff0c;我们引入了一个新系统&#xff0c;用于生成访问 Android 资源的 C# 代码。 在 Xamarin.Android、.NET 6 和 .NET 7 中生成 Resource.designer.cs 文件的系统已经被弃用。 新系统生成一个名…

No Chromedriver found that can automate Chrome ‘x.x.xxxx‘的解决办法

一、前置说明 在使用Appium对Android设备自动化测试时&#xff0c;切换WebView时抛出异常&#xff1a; selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occurred while processing the command. Original error: No Chromedriver foun…

分布式训练类的定义以及创建分布式模型

一 、分布式训练类的定义 from ..modules import Module from typing import Any, Optional from .common_types import _devices_t, _device_tclass DistributedDataParallel(Module):process_group: Any ...dim: int ...module: Module ...device_ids: _devices_t ...ou…

iOS(swiftui)——网络连接(Moya)

Moya 是一个流行的 Swift 网络抽象层&#xff0c;被用于简化 iOS 应用程序中的网络请求。使用 Moya&#xff0c;可以定义网络请求的方式&#xff0c;增加类型安全性&#xff0c;因为所有的网络请求都是经过 Swift 类型系统检查的&#xff0c;并且 Moya 提供了一种很好的方式来将…

利用 Python 进行数据分析实验(五)

一、实验目的 使用Python解决问题 二、实验要求 自主编写并运行代码&#xff0c;按照模板要求撰写实验报告 三、实验步骤 1 爬取并下载当当网某一本书的网页内容&#xff0c;并保存为html格式 2 在豆瓣网上爬取某本书的前50条短评内容并计算评分的平均值(自学正则表达式) …

交叉验证以及scikit-learn实现

交叉验证 交叉验证既可以解决数据集的数据量不够大问题&#xff0c;也可以解决参数调优的问题。 主要有三种方式&#xff1a; 简单交叉验证&#xff08;HoldOut检验&#xff09;、k折交叉验证&#xff08;k-fold交叉验证&#xff09;、自助法。 本文仅针对k折交叉验证做详细解…

ZooKeeper学习一

一、概念 ZooKeeper是一个开放源码的分布式协调服务&#xff0c;它是集群的管理者&#xff0c;监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作&#xff0c;最终将简单易用的接口和性能高效、功能稳定的系统提供给用户。 分布式应用程序可以基于ZooKeeper实现…

GO设计模式——4、单例模式(创建型)

目录 单例模式&#xff08;Singleton Pattern&#xff09; 优缺点 使用场景 饿汉式和懒汉式单例模式 单例模式&#xff08;Singleton Pattern&#xff09; 单例模式&#xff08;Singleton Pattern&#xff09;是一个类只允许创建一个对象&#xff08;或者实例&#xff…

基于ssm vue个人需求和地域特色的外卖推荐系统源码和论文

首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要包罗软件架构模式、整体功能模块、数据库设计。本项…

利用管道、信号量、信号、共享内存和消息队列进行多进程通信

一.管道&#xff08;分为命名管道和匿名管道&#xff09; 管道的特点&#xff1a; ①无论是命名管道还是匿名管道&#xff0c;写入管道的数据都存放在内存之中。 ②管道是一种半双工的通信方式&#xff08;半双工是指终端A能发信号给终端B&#xff0c;终端B也能发信号给终端…

css的4种引入方式--内联样式(标签内style)、内部样式表(<style>)、外部样式表(<link>、@import)

1.内联样式&#xff08;Inline Styles&#xff09;&#xff1a;可以直接在HTML元素的style属性中定义CSS样式。 例如&#xff1a; <p style"color: red; font-size: 16px;">这是一段红色的文本</p>内联样式适用于对单个元素应用特定的样式&#xff0c;…

软件开发安全指南

2.1.应用系统架构安全设计要求 2.2.应用系统软件功能安全设计要求 2.3.应用系统存储安全设计要求 2.4.应用系统通讯安全设计要求 2.5.应用系统数据库安全设计要求 2.6.应用系统数据安全设计要求 软件开发全资料获取&#xff1a;点我获取

Linux 网络协议

1 网络基础 1.1 网络概念 网络是一组计算机或者网络设备通过有形的线缆或者无形的媒介如无线&#xff0c;连接起来&#xff0c;按照一定的规则&#xff0c;进行通讯的集合( 缺一不可 )。 5G的来临以及IPv6的不断普及&#xff0c;能够进行联网的设备将会是越来越多&#xff08…

ERP数据仓库模型

ERP数据仓库模型建设是一个复杂的过程&#xff0c;涉及到多个主题域。以下是一个详细的设计方案&#xff1a; 确定业务需求和目标 在开始设计数据仓库模型之前&#xff0c;需要了解企业的业务需求和目标。这包括了解企业的运营模式、业务流程、关键绩效指标等。通过与业务部门…

vue 商品列表案例

my-tag 标签组件的封装 1. 创建组件 - 初始化 2. 实现功能 (1) 双击显示&#xff0c;并且自动聚焦 v-if v-else dbclick 操作 isEdit 自动聚焦&#xff1a; 1. $nextTick > $refs 获取到dom&#xff0c;进行focus获取焦点 2. 封装v-focus指令 (2) 失去焦点&#xff0c;隐藏…

Unity 程序运行后的日志信息路径

Unity 游戏程序运行后&#xff0c;在后台有个路径文件专门用于日志信息记录。 当运行程序发生错误时&#xff0c;我们可以通过查用该日志&#xff0c;获取相关有用信息&#xff0c;对我们处理Bug会有很大帮助。 在Windows平台上&#xff0c;该路径是&#xff1a; C:\Users\&…

用Rust刷LeetCode之66 加一

66. 加一[1] 难度: 简单 func plusOne(digits []int) []int { length : len(digits) // 从最低位开始遍历&#xff0c;逐位加一 for i : length - 1; i > 0; i-- { if digits[i] < 9 { digits[i] return digits } d…

【Mac】brew提示arch -arm64 brew以及uname返回x86_64的问题

背景 使用MacBook 14 M1 Pro两年了&#xff0c;自从使用了第三方Shell工具WindTerm后&#xff0c;使用brew时会提示我使用arch -arm64 brew安装&#xff0c;一开始没太在意&#xff0c;直到今天朋友问我uname -a返回的是什么架构&#xff0c;我才惊讶的发现竟然返回的是x86_64…

优化系统性能:深入性能测试的重要性与最佳实践

目录 引言 1. 为什么性能测试重要&#xff1f; 1.1 用户体验 1.2 系统稳定性 1.3 成本节约 1.4 品牌声誉 2. 性能测试的关键步骤 2.1 制定性能测试计划 2.2 确定性能测试类型 2.3 设计性能测试用例 2.4 配置性能测试环境 2.5 执行性能测试 2.6 分析和优化 2.7 回…

QT----Visual Studio打开.ui文件报错无法打开

问题 在我安装完qt后将它嵌入vs&#xff0c;后新建的文件无法打开ui文件 解决方法 右击ui文件打开方式,添加,程序找到你qt的安装目录里的designer.exe。点击确定再次双击就能够打开。