-
增加维度torch_Pytorch-tensor的维度变化
2020-12-22 18:51:17view/reshape:大小不变的条件下,转变shapesqueeze/unsqueeze:减少/增加维度transpose/t/permute:转置,单次/多次交换expand/repeat:维度扩展view reshape在pytorch0.3的时候,默认是view .为了与numpy一致0.4...引言
本篇介绍tensor的维度变化。
维度变化改变的是数据的理解方式!
view/reshape:大小不变的条件下,转变shape
squeeze/unsqueeze:减少/增加维度
transpose/t/permute:转置,单次/多次交换
expand/repeat:维度扩展
view reshape
在pytorch0.3的时候,默认是view .为了与numpy一致0.4以后增加了reshape。
损失维度信息,如果不额外存储/记忆的话,恢复时会出现问题。
执行view/reshape是有一定的物理意义的,不然不会这样做。
保证tensor的size不变即可/numel()一致/元素个数不变。
数据的存储/维度顺序非常非常非常重要
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In[4]: a = torch.rand(4,1,28,28)
In[5]: a.shape
Out[5]: torch.Size([4, 1, 28, 28])
In[6]: a.view(4,28*28) # 4, 1*28*28 将后面的进行合并/合并通道,长宽,忽略了通道信息,上下左右的空间信息。适合全连接层。
Out[6]:
tensor([[0.1483, 0.6321, 0.8864, ..., 0.0646, 0.4791, 0.0892],
[0.5868, 0.5278, 0.8514, ..., 0.0682, 0.7815, 0.2724],
[0.4945, 0.4957, 0.0047, ..., 0.4253, 0.4135, 0.1234],
[0.0618, 0.4257, 0.1960, ..., 0.1377, 0.5776, 0.4071]])
In[7]: a.view(4,28*28).shape
Out[7]: torch.Size([4, 784])
In[8]: a.view(4*28, 28).shape # 合并batch,channel,行合并 放在一起为N [N,28] 每个N,刚好有28个像素点,只关心一行数据
Out[8]: torch.Size([112, 28])
In[9]: a.view(4*1,28,28).shape# 4张叠起来了
Out[9]: torch.Size([4, 28, 28])
In[10]: b = a.view(4,784) # a原来的维度信息是[b,c,h,w],但a这样赋值后,它是恢复不到原来的
In[11]: b.view(4,28,28,1) # logic Bug # 语法上没有问题,但逻辑上 [b h w c] 与以前是不对应的。
a.view(4,783)
RuntimeError: shape '[4, 783]' is invalid for input of size 3136
squeeze 与 unsqueeze
unsqueeze
unsqueeze(index) 拉伸(增加一个维度) (增加一个组别)
参数的范围是 [-a.dim()-1, a.dim()+1) 如下面例子中范围是[-5,5)
-5 –> 0 … -1 –> 4 这样的话,0表示在前面插入,-1表示在后面插入,正负会有些混乱,所以推荐用正数。
0与正数,就是在xxx前面插入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
In[17]: a.shape
Out[17]: torch.Size([4, 1, 28, 28])
In[18]: a.unsqueeze(0).shape# 在0的前面插入一个维度
Out[18]: torch.Size([1, 4, 1, 28, 28]) # 理解上就是在batch的基础上增加了组。
In[19]: a.unsqueeze(-1).shape# 在-1之后插入一个维度
Out[19]: torch.Size([4, 1, 28, 28, 1]) # 理解上可能增加一个方差之类的
In[20]: a.unsqueeze(4).shape
Out[20]: torch.Size([4, 1, 28, 28, 1])
In[21]: a.unsqueeze(-4).shape
Out[21]: torch.Size([4, 1, 1, 28, 28])
In[22]: a.unsqueeze(-5).shape
Out[22]: torch.Size([1, 4, 1, 28, 28])
In[23]: a.unsqueeze(-6).shape
IndexError: Dimension out of range (expected to be in range of [-5, 4], but got -6)
In[24]: a = torch.tensor([1.2,2.3])
In[27]: a.shape
Out[27]: torch.Size([2])
In[25]: a.unsqueeze(-1) # 维度变成 [2,1] 2行1列
Out[25]:
tensor([[1.2000],
[2.3000]])
In[26]: a.unsqueeze(0)
Out[26]: tensor([[1.2000, 2.3000]]) # 维度变成 [1,2] 1行2列
实际案例
给一个bias(偏置),bias相当于给每个channel上的所有像素增加一个偏置
为了做到 f+b 我们需要改变b的维度
1
2
3
4
5
In[28]: b = torch.rand(32)
In[29]: f = torch.rand(4,32,14,14)
In[30]: b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
In[31]: b.shape
Out[31]: torch.Size([1, 32, 1, 1])
后面进一步扩张到 [4,32,14,14]
queeze
squeeze(index) 当index对应的dim为1,就产生作用。
不写参数,会挤压所有维度为1的。
1
2
3
4
5
6
7
8
9
10
11
12
In[38]: b.shape
Out[38]: torch.Size([1, 32, 1, 1])
In[39]: b.squeeze().shape # 默认将所有维度为1的进行挤压 这32个channel,每个channel有一个值
Out[39]: torch.Size([32])
In[40]: b.squeeze(0).shape
Out[40]: torch.Size([32, 1, 1])
In[41]: b.squeeze(-1).shape
Out[41]: torch.Size([1, 32, 1])
In[42]: b.squeeze(1).shape
Out[42]: torch.Size([1, 32, 1, 1])
In[43]: b.squeeze(-4).shape
Out[43]: torch.Size([32, 1, 1])
expand / repeat
Expand:broadcasting (推荐)
只是改变了理解方式,并没有增加数据
在需要的时候复制数据
Reapeat:memory copied
会实实在在的增加数据
上面提到的b [1, 32, 1, 1] f[ 4, 32, 14, 14 ]
目标是将b的维度变成与f相同的维度。
expand
扩展(expand)张量不会分配新的内存,只是在存在的张量上创建一个新的视图(view)
1
2
3
4
5
6
7
8
9
In[44]: a = torch.rand(4,32,14,14)
In[45]: b.shape
Out[45]: torch.Size([1, 32, 1, 1]) # 只有1-->N才是可行的, 3 -> N 是需要规则的
In[46]: b.expand(4,32,14,14).shape
Out[46]: torch.Size([4, 32, 14, 14])
In[47]: b.expand(-1,32,-1,-1).shape# -1表示这个维度不变
Out[47]: torch.Size([1, 32, 1, 1])
In[48]: b.expand(-1,32,-1,-4).shape# -4这里是一个bug,没有意义,最新版已经修复了
Out[48]: torch.Size([1, 32, 1, -4])
repeat
主动复制原来的。
参数表示的是要拷贝的次数/是原来维度的倍数
沿着特定的维度重复这个张量,和expand()不同的是,这个函数拷贝张量的数据。
1
2
3
4
5
6
7
8
9
10
11
In[49]: b.shape
Out[49]: torch.Size([1, 32, 1, 1])
In[50]: b.repeat(4,32,1,1).shape
Out[50]: torch.Size([4, 1024, 1, 1])
In[51]: b.repeat(4,1,1,1).shape
Out[51]: torch.Size([4, 32, 1, 1])
In[52]: b.repeat(4,1,32,32)
In[53]: b.repeat(4,1,32,32).shape
Out[53]: torch.Size([4, 32, 32, 32])
In[55]: b.repeat(4,1,14,14).shape# 这样就达到目标了
Out[55]: torch.Size([4, 32, 14, 14])
转置
.t
转置操作
.t 只针对 2维矩阵
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a = torch.randn(3,4)
a.t().shape
Out[58]: torch.Size([4, 3])
In[60]: a
Out[60]:
tensor([[ 0.5629, -0.5085, -0.3371, 1.2387],
[ 0.2142, -1.7846, 0.2297, 1.7797],
[-0.3197, 0.6116, 0.3791, 0.9218]])
In[61]: a.t()
Out[61]:
tensor([[ 0.5629, 0.2142, -0.3197],
[-0.5085, -1.7846, 0.6116],
[-0.3371, 0.2297, 0.3791],
[ 1.2387, 1.7797, 0.9218]])
b.t()
RuntimeError: t() expects a tensor with <= 2 dimensions, but self is 4D
transpose
在结合view使用的时候,view会导致维度顺序关系变模糊,所以需要人为跟踪。
错误的顺序,会导致数据污染
一次只能两两交换
contiguous
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 由于交换了1,3维度,就会变得不连续,所以需要用contiguous,来吧数据变得连续。
In[17]: a1 = a.transpose(1,3).view(4,3*32*32).view(4,3,32,32)
RuntimeError: invalid argument 2: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Call .contiguous() before .view()
In[8]: a = torch.randn(4,3,32,32)
In[9]: a.shape
Out[9]: torch.Size([4, 3, 32, 32])
In[10]: a1 = a.transpose(1,3).contiguous().view(4,3*32*32).view(4,3,32,32)
#[b c h w] 交换1,3维度的数据 [b w h c],再把后面的三个连在一起,展开后变为 [b c w h] 导致和原来的顺序不同,造成数据污染!!!
In[11]: a1.shape
Out[11]: torch.Size([4, 3, 32, 32])
In[12]: a2 = a.transpose(1,3).contiguous().view(4,3*32*32).view(4,32,32,3).transpose(1,3)
# [b c h w] -> [b w h c] -> [b w h c] -> [b c h w] 和原来顺序相同。
In[13]: a2.shape
Out[13]: torch.Size([4, 3, 32, 32])
# 验证向量一致性
In[14]: torch.all(torch.eq(a,a1))
Out[14]: tensor(0, dtype=torch.uint8)
In[15]: torch.all(torch.eq(a,a2))
Out[15]: tensor(1, dtype=torch.uint8)
permute
会打乱内存顺序,待补充!!!
由于transpose一次只能两两交换,所以变换后在变回去至少需要两次操作,而permute一次就好。例如对于[b,h,w,c]
[b,h,w,c]是numpy存储图片的格式,需要这一步才能导出numpy
1
2
3
4
5
6
7
8
9
10
In[18]: a = torch.rand(4,3,28,28)
In[19]: a.transpose(1,3).shape# [b c h w] -> [b w h c] h与w的顺序发生了变换,导致图像发生了变化
Out[19]: torch.Size([4, 28, 28, 3])
In[20]: b = torch.rand(4,3,28,32)
In[21]: b.transpose(1,3).shape
Out[21]: torch.Size([4, 32, 28, 3])
In[22]: b.transpose(1,3).transpose(1,2).shape
Out[22]: torch.Size([4, 28, 32, 3]) # [b,h,w,c]是numpy存储图片的格式,需要这一步才能导出numpy
In[23]: b.permute(0,2,3,1).shape
Out[23]: torch.Size([4, 28, 32, 3])
Broadcast
自动扩展:
维度扩展,自动调用expand
without copying data ,不需要拷贝数据。
核心思想
在前面插入1维
将size 1 扩展成相同 size 的维度
例子:
对于 feature maps : [4, 32, 14, 14],想给它添加一个偏置Bias
Bias:[32] –> [32, 1 , 1] (这里是手动的) => [1, 32, 1, 1] => [4, 32, 14, 14]
目标:当Bias和feature maps的size一样时,才能执行叠加操作!!!
Why broadcasting?
就像下图表示的一样:我们希望进行如下的几种计算,但需要满足数学上的约束(size相同),为了节省人们为满足数学上的约束而手动复制的过程,而产生的Broadcast,它节省了大量的内容消耗。
第二行数据中 [3] => [1, 3] => [4, 3] (行复制了4次)
第三行数据中
[4,1] => [4, 3] (列复制了3次)
[1,3] => [4, 3] (行复制了4次)
broadcast = unsqueze(插入新维度) + expand(将1dim变成相同维度)
例子:
有这样的数据 [class, students, scores],具体是4个班,每个班32人,每人8门课程[4, 32, 8] 。
考试不理想,对于这组数据我们需要为每一位同学的成绩加5分
要求: [4, 32, 8] + [4, 32, 8]
实际上:[4, 32, 8] + [5.0]
操作上:[1] =>(unsqueeze) [1, 1, 1] =>(expand_as) [4, 32, 8],这样需要写3个接口。
所以才会有 broadcast!!
内存分析:
[4, 32, 8] => 1024
[5.0] => 1 如果是手动复制的话,内存消耗将变为原来的1024倍
使用条件?
A [ 大维度 —> 小维度 ]
从最后一位(最小维度)开始匹配,如果维度上的size是0,1或相同,则满足条件,看下一个维度,直到都满足条件为止。
如果当前维度是1,扩张到相同维度
如果没有维度,插入一个维度并扩张到相同维度
当最小维度不匹配的时候是没法使用broadcastiong,如共有8门课程,但只给了4门课程的变化,这样就会产生歧义。
note:小维度指定,大维度随意
小维度指定:假如英语考难了,只加英语成绩 [0 0 5 0 0 0 0 0]
案例
情况一
A[4, 32, 14, 14]
B[1, 32, 1, 1] => [4,,32, 14, 14]
情况二
A[4, 32, 14, 14]
B[14, 14] => [1, 1, 14, 14] => [4, 32, 14, 14]
情况三
不符合条件
A[4, 32, 14, 14]
B[2, 32, 14, 14]
理解这种行为
小维度指定,大维度随意。小维度设定规则(加5分),大维度默认按照这个规则(通用)。
维度为1才满足条件,是为了保证公平(统一的规则)
常见使用情景
A [4, 3, 32, 32] b,c,h,w
+[32, 32] 叠加一个相同的feature map,做一些平移变换。相当于一个base(基底),
+[3, 1, 1] 针对 RGB 进行不同的补充,如R 0.5 、G 0 、B 0.3
+[1, 1, 1, 1] 对于所有的都加一个数值,抬高一下,如加0.5.
-
multi task训练torch_Pytorch + Apex 训练流程记录
2021-01-01 22:08:42内容包括有:Pytorch自带的分布式训练API,以及Nvidia的apex附带的API,最后再额外写一下Nvidia的DALI数据读取与处理加速的方法。文末是一些查看后觉得有用的链接,可以先看看里面的内容,本文只是对其做一些补充。...最近尝试用Pytorch的分布式训练API进行一系列实验,踩了不少坑,也看了很多资料。在这里简单做一下总结。内容包括有:Pytorch自带的分布式训练API,以及Nvidia的apex附带的API,最后再额外写一下Nvidia的DALI数据读取与处理加速的方法。文末是一些查看后觉得有用的链接,可以先看看里面的内容,本文只是对其做一些补充。
在一些大的视觉任务例如语义分割和目标检测中由于数据IO不是瓶颈,所以DALI其实不太需要,我是在ImageNet预训练阶段会用到DALI,提速确实明显。
首先是Pytorch自带的分布式API:
import argparse parse = argparse.ArgumentParser() # 在训练的开始要设置一个叫local_rank的参数到环境变量中,用argparse模块。 # 当使用分布式训练时Pytorch会开多进程,这个参数负责表示具体进程。 # 通常我们只需要其中一个进程输出结果就可以了。 parse.add_argument('--local_rank', type=int, default=0) args = parse.parse_args() local_rank = args.local_rank # 设置并初始化环境,后端通常用nccl就可以。 torch.cuda.set_device(local_rank) torch.distributed.init_process_group(backend="nccl", init_method='env://') device = torch.device("cuda:{}".format(local_rank)) # 加载数据集,注意加载如DataLoader时,要使用分布式sampler的API,然后把shuffle设置为False。 # 具体对训练数据shuffle操作放到了每一轮中额外设置。 # 开启pin_memory是方便后续做数据IO的加速,用到Pytorch自带的一些性质。 # 通常不是数据IO不是瓶颈时用这个就足够 traindataset = train_set() from torch.utils.data.distributed import DistributedSampler from torch.utils import data datasampler = DistributedSampler(dataset=traindataset) trainloader = data.DataLoader(traindataset, sampler=datasampler, batch_size=xxx, shuffle=False, pin_memory=True, num_workers=xxx)
以上是环境初始化以及数据读取部分的设置,无论使用apex还是Pytorch自带的分布式API,上面都是必须做的,以下模型读取的话Pytorch的API和apex有所不同,主要是apex不仅可以分布式训练,还可以使用混合精度模式以及同步bn(Sync-BN)。参考代码:
from apex.parallel import DistributedDataParallel as DDP from apex.parallel import convert_syncbn_model, SyncBatchNorm from apex.fp16_utils import * from apex import amp, optimizers from apex.multi_tensor_apply import multi_tensor_applier model = XXXNet() model.train() model.to(device) # device是上面设置好的device。 # Sync-BatchNorm # 使用apex自带的同步BN层。 # 调用函数后会自动遍历model的层,将Batchnorm层替换。 model = convert_syncbn_model(model) optimizer = optim.SGD(model.parameters(), lr=.., decay=.., ...) # 用于记录训练过程中的loss变化。 def reduce_tensor(tensor, world_size=1): dist.all_reduce(tensor, op=dist.ReduceOp.SUM) tensor /= world_size return tensor # 通过调整下面的opt_level实现半精度训练。 # opt_level选项有:'O0', 'O1', 'O2', 'O3'. # 其中'O0'是fp32常规训练,'O1'、'O2'是fp16训练,'O3'则可以用来推断但不适合拿来训练(不稳定) # 注意,当选用fp16模式进行训练时,keep_batchnorm默认是None,无需设置; # scale_loss是动态模式,可以设置也可以不设置。 model, optimizer = amp.initialize(model, optimizer, opt_level=opt_level, keep_batchnorm_fp32=keep_bn_32, scale_loss=scale_loss) # Pytorch自带的分布式API model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[local_rank]) # Apex的分布式API model = DDP(model) # 训练过程中: while training: output = model(input) loss = criterion(output, target) # 方便记录损失下降 log_loss = reduce_tensor(log_loss.clone().detach_()) optimizer.zero_grad() # 假如设置了fp16训练模式,则要将反传过程替换为以下。 with amp.scale_loss(loss, optimizer) as scaled_loss: scaled_loss.backward() optimizer.step()
关于模型保存和重用:
# Save checkpoint # 注意,在保存权重时要注意判断local_rank。 # 因为是多进程任务,假如多个进程同时写同一个文件在同一个地址会损坏文件。 # 通常是local_rank 为默认值时保存权重,由于多个进程之间的保持GPU通信,所以每个进程的权重都是一样的。 if local_rank == 0: checkpoint = { 'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'amp': amp.state_dict() } torch.save(checkpoint, 'amp_checkpoint.pt') ... # 模型重用,把权重放在‘cpu’模式上,然后读取,再设置分布式。 net = XXXNet() net.train() resume = 'XXXNet.pth' checkpoint = torch.load(resume, map_location='cpu') # CPU mode net.load_state_dict(checkpoint['model']) optimizer.load_state_dict(checkpoint['optimizer']) model, optimizer = amp.initialize(model, optimizer, opt_level=opt_level) amp.load_state_dict(checkpoint['amp'])
注意,由于我自己还没有尝试过模型重用,所以具体流程还不是特别清楚,所以这里遇到什么问题可以交流下。
最后一步就是开启训练:
# 在终端输入: NCCL_DEBUG=INFO CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 train.py # NCCL_DEBUG=INFO 表示打印nccl初始化信息,可选,用于debug。 # CUDA_VISIBLE_DEVICES=0,1 用来指定训练用GPU # python -m torch.distributed.launch 必须加,用来初始化local_rank到每一个进程 # --nproc_per_node=2 通常一台机器上用多少GPU就设置多少,每个GPU在一个进程上效率较好。
最后提一些遇到的比较常见的问题:
- apex编译出错:
- 有可能是Pytorch的cuda版本和nvcc(NVIDIA (R) Cuda compiler driver)版本不一致。查看nvcc:命令行输入nvcc --version;查看Pytorch的cuda:Python下输入torch.version.cuda 查看。不对应的话建议更换,一般需要cuda 9.2 及以上,Pytorch最好升到1.1以上。
- C++文件编译出错。准确来说是Python的C++编译文件,像我的话我的g++在anaconda路径下,和C++其他标准库路径不一致,要在Python代码(通常是setup.py文件中)的Extension部分添加extra_link_args=['-L/usr/lib/x86_64-linux-gnu/']。这个问题一般是出在各种涉及到C++编译例如目标检测的NMS或者ROI操作。之前编译目标检测包如mmdetection和Simpledet等时就是用这种解决办法,一试一个准。
- 其他问题应该google就可以搜得到。
- 数据加速:
- 使用训练小网络时,梯度更新都很快,数据IO的时间消耗是训练时长的瓶颈。特别是训练Mobilenet,Shufflenet,IGCV等小网络时,这时候就要通过一些手段来加速。
- 例子一:ImageNet预训练,可以使用Nvidia的DALI库。通过将预处理手段放在GPU上进行来加速数据IO,会占用一部分显存。值得注意的是,DALI支持Pytorch、Mxnet和tensorflow。但是Mxnet的话只能读取record文件,所以还是先将数据生成record再调用DALI,而Pytorch则可以直接调用。详细的API在文末[1]。
- 例子二:我们可以用data_prefetcher类来加速,通过预读文件到GPU上来加速,具体做法在[2]。值得注意的是,链接里的类定义了DataLoader的数据输出格式,并对输出的数据做归一化,注意是否需要去除。
参考:
- DALI文档
- data_prefetcher类
lbin:pytorch 1.0 分布式zhuanlan.zhihu.com尼古拉·瓦砾:【PyTorch】唯快不破:基于Apex的混合精度加速zhuanlan.zhihu.comlbin:pytorch + apex 生活变得更美好zhuanlan.zhihu.com尼古拉·瓦砾:【分布式训练】单机多卡的正确打开方式(三):PyTorchzhuanlan.zhihu.comMrTian:给训练踩踩油门 —— Pytorch 加速数据读取zhuanlan.zhihu.com - apex编译出错:
-
dann的alpha torch_pytorch BiLSTM+CRF代码详解
2021-01-26 21:15:40一. BILSTM + CRF介绍1....1.1开始之前我们假设我们的数据集中有两类实体——人名和地名,与之相对应在我们的训练数据集中,有五类标签:B-Person, I- Person,B-Organization,I-Organization假...一. BILSTM + CRF介绍
1.介绍
基于神经网络的方法,在命名实体识别任务中非常流行和普遍。 如果你不知道Bi-LSTM和CRF是什么,你只需要记住他们分别是命名实体识别模型中的两个层。
1.1开始之前
我们假设我们的数据集中有两类实体——人名和地名,与之相对应在我们的训练数据集中,有五类标签:
B-Person, I- Person,B-Organization,I-Organization
假设句子x由五个字符w1,w2,w3,w4,w5组成,其中【w1,w2】为人名类实体,【w3】为地名类实体,其他字符标签为“O”。
1.2BiLSTM-CRF模型
以下将给出模型的结构:
第一,句子x中的每一个单元都代表着由字嵌入或词嵌入构成的向量。其中,字嵌入是随机初始化的,词嵌入是通过数据训练得到的。所有的嵌入在训练过程中都会调整到最优。
第二,这些字或词嵌入为BiLSTM-CRF模型的输入,输出的是句子x中每个单元的标签。
Bi-LSTM结构图
尽管一般不需要详细了解BiLSTM层的原理,但是为了更容易知道CRF层的运行原理,我们需要知道BiLSTM的输出层。
图2.Bi-LSTM标签预测原理图
如上图所示,BiLSTM层的输出为每一个标签的预测分值,例如,对于单元w0,BiLSTM层输出的是
1.5 (B-Person), 0.9 (I-Person), 0.1 (B-Organization), 0.08 (I-Organization) 0.05 (O).
这些分值将作为CRF的输入。
1.3 如果没有CRF层会怎样
你也许已经发现了,即使没有CRF层,我们也可以训练一个BiLSTM命名实体识别模型,如图所示:
图3.去除CRF的BiLSTM命名实体识别模型
由于BiLSTM的输出为单元的每一个标签分值,我们可以挑选分值最高的一个作为该单元的标签。例如,对于单元w0,“B-Person”有最高分值—— 1.5,因此我们可以挑选“B-Person”作为w0的预测标签。同理,我们可以得到w1——“I-Person”,w2—— “O” ,w3——“B-Organization”,w4——“O”。
虽然我们可以得到句子x中每个单元的正确标签,但是我们不能保证标签每次都是预测正确的。例如,图4.中的例子,标签序列是“I-Organization I-Person” and “B-Organization I-Person”,很显然这是错误的。
在这里插入图片描述
1.4 CRF层能从训练数据中获得约束性的规则
CRF层可以为最后预测的标签添加一些约束来保证预测的标签是合法的。在训练数据训练过程中,这些约束可以通过CRF层自动学习到。
这些约束可以是:
I:句子中第一个词总是以标签“B-“ 或 “O”开始,而不是“I-”
II:标签“B-label1 I-label2 I-label3 I-…”,label1, label2, label3应该属于同一类实体。例如,“B-Person I-Person” 是合法的序列, 但是“B-Person I-Organization” 是非法标签序列.
III:标签序列“O I-label” is 非法的.实体标签的首个标签应该是 “B-“ ,而非 “I-“, 换句话说,有效的标签序列应该是“O B-label”。
有了这些约束,标签序列预测中非法序列出现的概率将会大大降低。
二. 标签的score和损失函数的定义
Bi-LSTM layer的输出维度是tag size,这就相当于是每个词 wi 映射到tag的发射概率值,设Bi-LSTM的输出矩阵为P,其中Pi,j代表词wi映射到tagj的非归一化概率。对于CRF来说,我们假定存在一个转移矩阵A,则Ai,j代表tagi转移到tagj的转移概率。
对于输入序列 X 对应的输出tag序列 y,定义分数为
在这里插入图片描述
利用Softmax函数,我们为每一个正确的tag序列y定义一个概率值(YX代表所有的tag序列,包括不可能出现的)
在这里插入图片描述
因而在训练中,我们只需要最大化似然概率p(y|X)即可,这里我们利用对数似然
在这里插入图片描述
所以我们将损失函数定义为-log(p(y|X)),就可以利用梯度下降法来进行网络的学习了。
loss function:
在这里插入图片描述
在对损失函数进行计算的时候,S(X,y)的计算很简单,而
在这里插入图片描述(下面记作logsumexp)的计算稍微复杂一些,因为需要计算每一条可能路径的分数。这里用一种简便的方法,对于到词wi+1的路径,可以先把到词wi的logsumexp计算出来,因为
在这里插入图片描述
因此先计算每一步的路径分数和直接计算全局分数相同,但这样可以大大减少计算的时间。
三. 对于损失函数的详细解释
这篇文章对于理解十分有用
举例说 【我 爱 中国人民】对应标签【N V N】那这个标签就是一个完整的路径,也就对应一个Score值。
接下来我想讲的是这个公式:
在这里插入图片描述
这个公式成立是很显然的,动笔算一算就知道了,代码里其实就是用了这个公式的原理。
def _forward_alg(self, feats):
# Do the forward algorithm to compute the partition function
init_alphas = torch.full((1, self.tagset_size), -10000.)
# START_TAG has all of the score.
init_alphas[0][self.tag_to_ix[START_TAG]] = 0.
# Wrap in a variable so that we will get automatic backprop
forward_var = init_alphas
# Iterate through the sentence
for feat in feats:
alphas_t = [] # The forward tensors at this timestep
for next_tag in range(self.tagset_size):
# broadcast the emission score: it is the same regardless of
# the previous tag
emit_score = feat[next_tag].view(
1, -1).expand(1, self.tagset_size)
# the ith entry of trans_score is the score of transitioning to
# next_tag from i
trans_score = self.transitions[next_tag].view(1, -1)
# The ith entry of next_tag_var is the value for the
# edge (i -> next_tag) before we do log-sum-exp
next_tag_var = forward_var + trans_score + emit_score
# The forward variable for this tag is log-sum-exp of all the
# scores.
alphas_t.append(log_sum_exp(next_tag_var).view(1))
forward_var = torch.cat(alphas_t).view(1, -1)
terminal_var = forward_var + self.transitions[self.tag_to_ix[STOP_TAG]]
alpha = log_sum_exp(terminal_var)
return alpha
我们看到有这么一段代码 :
next_tag_var = forward_var + trans_score + emit_score
我们主要就是来讲讲他。
首先这个算法的思想是:假设我们要做一个词性标注的任务,对句子【我 爱 中华人民】,我们要对这个句子做
在这里插入图片描述
意思就是 对这个句子所有可能的标注,都算出来他们的Score,然后按照指数次幂加起来,再取对数。一般来说取所有可能的标注情况比较复杂,我们这里举例是长度为三,但是实际过程中,可能比这个要大得多,所以我们需要有一个简单高效得算法。也就是我们程序中得用得算法, 他是这么算得:
先算出【我, 爱】可能标注得所有情况,取 log_sum_exp 然后加上 转换到【中国人民】得特征值 再加上【中国人民】对应得某个标签得特征值。其等价于【我,爱,中国人民】所有可能特征值指数次幂相加,然后取对数.
接下来我们来验证一下是不是这样
首先我们假设词性一共只有两种 名词N 和 动词 V
那么【我,爱】得词性组合一共有四种 N + N,N + V, V + N, V + V
那么【爱】标注为N时得log_sum_exp 为
在这里插入图片描述
【爱】 标注为 V时的 log_sum_exp为
在这里插入图片描述
我们的forward列表里就是存在着这两个值,即:
在这里插入图片描述
假设【中华人民】得词性为N,我们按照代码来写一下公式,在forward列表对应位置相加就是这样
在这里插入图片描述
在这里插入图片描述
四. 代码块详细说明:
先说明两个重要的矩阵:
feats: 发射矩阵(emit score)是sentence 在embedding后,再经过LSTM后得到的矩阵(也就是LSTM的输出), 维度为11 * 5 (11为sentence 的length,5是标签数)。这个矩阵表示经过LSTM后sentence的每个word对应的每个labels的得分)。 表示发射概率。
self.transitions:转移矩阵,维度为55,transitions[i][j]表示label j转移到label i的概率。transtion[i]维度为15,表示每个label转移到label i的概率。 表示概率矩阵
1. def log_sum_exp(vec)
# compute log sum exp in numerically stable way for the forward algorithm
def log_sum_exp(vec): #vec是1*5, type是Variable
max_score = vec[0, argmax(vec)]
# max_score维度是1, max_score.view(1,-1)维度是1*1,
# max_score.view(1, -1).expand(1, vec.size()[1])的维度是1*5
max_score_broadcast = max_score.view(1, -1).expand(1, vec.size()[1])
# 里面先做减法,减去最大值可以避免e的指数次,计算机上溢
return max_score + \
torch.log(torch.sum(torch.exp(vec - max_score_broadcast)))
你可能会疑问return 的结果为什么先减去max_score.其实这是一个小技巧,因为一上来就做指数运算可能会引起计算结果溢出,先减去score,经过log_sum_exp后,再把max_score给加上。
其实就等同于:
return torch.log(torch.sum(torch.exp(vec)))
2. def neg_log_likelihood(self, sentence, tags)
如果你完整地把代码读完,你会发现neg_log_likelihood()这个函数是loss function.
loss = model.neg_log_likelihood(sentence_in, targets)
我们来分析一下neg_log_likelihood()函数代码:
def neg_log_likelihood(self, sentence, tags):
# feats: 11*5 经过了LSTM+Linear矩阵后的输出,之后作为CRF的输入。
feats = self._get_lstm_features(sentence)
forward_score = self._forward_alg(feats)
gold_score = self._score_sentence(feats, tags)
return forward_score - gold_score
你在这里可能会有疑问:问什么forward_score - gold_score可以作为loss呢。
这里,我们回顾一下我们在上文中说明的loss function函数公式:
在这里插入图片描述
你就会发现forward_score和gold_score分别表示上述等式右边的两项。
3. def _forward_alg(self, feats):
我们通过上一个函数的分析得知这个函数就是用来求forward_score的,也就是loss function等式右边的第一项:
在这里插入图片描述
# 预测序列的得分
# 只是根据随机的transitions,前向传播算出的一个score
#用到了动态规划的思想,但因为用的是随机的转移矩阵,算出的值很大score>20
def _forward_alg(self, feats):
# do the forward algorithm to compute the partition function
init_alphas = torch.full((1, self.tagset_size), -10000.) # 1*5 而且全是-10000
# START_TAG has all of the score
# 因为start tag是4,所以tensor([[-10000., -10000., -10000., 0., -10000.]]),
# 将start的值为零,表示开始进行网络的传播,
init_alphas[0][self.tag_to_ix[START_TAG]] = 0
# warp in a variable so that we will get automatic backprop
forward_var = init_alphas # 初始状态的forward_var,随着step t变化
# iterate through the sentence
# 会迭代feats的行数次
for feat in feats: #feat的维度是5 依次把每一行取出来~
alphas_t = [] # the forward tensors at this timestep
for next_tag in range(self.tagset_size): #next tag 就是简单 i,从0到len
# broadcast the emission(发射) score:
# it is the same regardless of the previous tag
# 维度是1*5 LSTM生成的矩阵是emit score
emit_score = feat[next_tag].view(
1, -1).expand(1, self.tagset_size)
# the i_th entry of trans_score is the score of transitioning
# to next_tag from i
trans_score = self.transitions[next_tag].view(1, -1) # 维度是1*5
# The ith entry of next_tag_var is the value for the
# edge (i -> next_tag) before we do log-sum-exp
# 第一次迭代时理解:
# trans_score所有其他标签到B标签的概率
# 由lstm运行进入隐层再到输出层得到标签B的概率,emit_score维度是1*5,5个值是相同的
next_tag_var = forward_var + trans_score + emit_score
# The forward variable for this tag is log-sum-exp of all the scores
alphas_t.append(log_sum_exp(next_tag_var).view(1))
# 此时的alphas t 是一个长度为5,例如:
# [tensor(0.8259), tensor(2.1739), tensor(1.3526), tensor(-9999.7168), tensor(-0.7102)]
forward_var = torch.cat(alphas_t).view(1, -1) #到第(t-1)step时5个标签的各自分数
# 最后只将最后一个单词的forward var与转移 stop tag的概率相加
# tensor([[ 21.1036, 18.8673, 20.7906, -9982.2734, -9980.3135]])
terminal_var = forward_var + self.transitions[self.tag_to_ix[STOP_TAG]]
alpha = log_sum_exp(terminal_var) # alpha是一个0维的tensor
return alpha
4. def _score_sentence(self, feats, tags)
由2的函数分析,我们知道这个函数就是求gold_score,即loss function的第二项
# 根据真实的标签算出的一个score,
# 这与上面的def _forward_alg(self, feats)共同之处在于:
# 两者都是用的随机转移矩阵算的score
# 不同地方在于,上面那个函数算了一个最大可能路径,但实际上可能不是真实的各个标签转移的值
# 例如:真实标签是N V V,但是因为transitions是随机的,所以上面的函数得到其实是N N N这样,
# 两者之间的score就有了差距。而后来的反向传播,就能够更新transitions,使得转移矩阵逼近
#真实的“转移矩阵”
# 得到gold_seq tag的score 即根据真实的label 来计算一个score,
# 但是因为转移矩阵是随机生成的,故算出来的score不是最理想的值
def _score_sentence(self, feats, tags): #feats 11*5 tag 11 维
# gives the score of a provied tag sequence
score = torch.zeros(1)
# 将START_TAG的标签3拼接到tag序列最前面,这样tag就是12个了
tags = torch.cat([torch.tensor([self.tag_to_ix[START_TAG]], dtype=torch.long), tags])
for i, feat in enumerate(feats):
# self.transitions[tags[i + 1], tags[i]] 实际得到的是从标签i到标签i+1的转移概率
# feat[tags[i+1]], feat是step i 的输出结果,有5个值,
# 对应B, I, E, START_TAG, END_TAG, 取对应标签的值
# transition【j,i】 就是从i ->j 的转移概率值
score = score + \
self.transitions[tags[i+1], tags[i]] + feat[tags[i + 1]]
score = score + self.transitions[self.tag_to_ix[STOP_TAG], tags[-1]]
return score
5. def _viterbi_decode(self, feats):
# 维特比解码, 实际上就是在预测的时候使用了, 输出得分与路径值
# 预测序列的得分
def _viterbi_decode(self, feats):
backpointers = []
# initialize the viterbi variables in long space
init_vvars = torch.full((1, self.tagset_size), -10000.)
init_vvars[0][self.tag_to_ix[START_TAG]] = 0
# forward_var at step i holds the viterbi variables for step i-1
forward_var = init_vvars
for feat in feats:
bptrs_t = [] # holds the backpointers for this step
viterbivars_t = [] # holds the viterbi variables for this step
for next_tag in range(self.tagset_size):
# next-tag_var[i] holds the viterbi variable for tag i
# at the previous step, plus the score of transitioning
# from tag i to next_tag.
# we don't include the emission scores here because the max
# does not depend on them(we add them in below)
# 其他标签(B,I,E,Start,End)到标签next_tag的概率
next_tag_var = forward_var + self.transitions[next_tag]
best_tag_id = argmax(next_tag_var)
bptrs_t.append(best_tag_id)
viterbivars_t.append(next_tag_var[0][best_tag_id].view(1))
# now add in the emssion scores, and assign forward_var to the set
# of viterbi variables we just computed
# 从step0到step(i-1)时5个序列中每个序列的最大score
forward_var = (torch.cat(viterbivars_t) + feat).view(1, -1)
backpointers.append(bptrs_t) # bptrs_t有5个元素
# transition to STOP_TAG
# 其他标签到STOP_TAG的转移概率
terminal_var = forward_var + self.transitions[self.tag_to_ix[STOP_TAG]]
best_tag_id = argmax(terminal_var)
path_score = terminal_var[0][best_tag_id]
# follow the back pointers to decode the best path
best_path = [best_tag_id]
for bptrs_t in reversed(backpointers):
best_tag_id = bptrs_t[best_tag_id]
best_path.append(best_tag_id)
# pop off the start tag
# we don't want to return that ti the caller
start = best_path.pop()
assert start == self.tag_to_ix[START_TAG] # Sanity check
best_path.reverse() # 把从后向前的路径正过来
return path_score, best_path
如果对于该函数还没有太理解,可以参考这篇博客:
总结
以上就是我结合了几篇比较不错的博客后的总结,欢迎大家提问。
-
python interpreter 中没有torch_PyTorch扩展自定义PyThon/C++(CUDA)算子的若干方法总结
2020-11-19 20:18:13点上方蓝字人工智能算法与Python大数据获取更多干货在右上方···设为星标★,第一时间获取资源仅做学术分享,如有侵权,联系删除转载于 :奔腾的黑猫@知乎来源丨https://zhuanlan.zhihu.com/p/158643792在做毕设的...点上方蓝字人工智能算法与Python大数据获取更多干货
在右上方 ··· 设为星标 ★,第一时间获取资源
仅做学术分享,如有侵权,联系删除
转载于 :奔腾的黑猫@知乎
来源丨https://zhuanlan.zhihu.com/p/158643792在做毕设的时候,需要实现一个PyTorch原生代码中没有的并行算子,所以用到了这部分的知识,再不总结就要忘光了= =本文内容主要是PyTorch的官方教程的各种传送门,这些官方教程写的都很好,以后就可以不用再浪费时间在百度上了。由于图神经网络计算框架PyG的代码实现也是采用了扩展的方法,因此也可以当成下面总结PyG源码文章的前导知识吧 。第一种情况:使用PyThon扩展PyTorch
使用PyThon扩展PyTorch准确的来说是在PyTorch的Python前端实现自定义算子或者模型,不涉及底层C++的实现。这种扩展方式是所有扩展方式中最简单的,也是官方首先推荐的,这是因为PyTorch在NVIDIA cuDNN,Intel MKL或NNPACK之类的库的支持下已经对可能出现的CPU和GPU操作进行了高度优化,因此用Python扩展的代码通常足够快。比如要扩展一个新的PyThon算子(torch.nn
)只需要继承torch.nn.Module并实现其forward方法即可。详细的过程请参考官方教程传送门:https://pytorch.org/docs/master/notes/extending.html第二种情况:使用pybind11构建共享库形式的C++和CUDA扩展
但是如果我们想对代码进行进一步优化,比如对自己的算子添加并行的CUDA实现或者连接个OpenCV的库什么的,那么仅仅使用Python进行扩展就不能满足需求;其次如果我们想序列化模型,在一个没有Python环境的生产环境下部署,也需要我们使用C++重写算法;最后考虑到考虑到多线程执行和性能原因,一般Python代码也并不适合做部署。因此在对性能有要求或者需要序列化模型的场景下我们还是会用到C++扩展。下面我先把官方教程传送门放在这里:https://pytorch.org/tutorials/advanced/cpp_extension.html对于一种典型的扩展情况,比如我们要设计一个全新的C++底层算子,其过程其实就三步:第一步:使用C++编写算子的forward函数和backward函数第二步:将该算子的forward函数和backward函数使用**pybind11**绑定到python上第三步:使用setuptools/JIT/CMake编译打包C++工程为so文件注意到在第一步中,我们不仅仅要实现forward函数也要实现backward函数,这是因为在C++端PyTorch目前不支持自动根据forward函数推导出backward函数,所以我们必须要对自己算子的反向传播过程完全清楚。一个需要注意的地方是,你可以选择直接在C++中继承torch::autograd类进行扩展;也可以像官方教程中那样在C++代码中实现forward和backward的核心过程,而在python端继承PyTorch的torch.autograd.Function类。在C++端扩展forward函数和backward函数的需要注意以下规则:(1)首先无论是forward函数还是backward函数都需要声明为静态函数。(2)forward
函数可以接受任意多的参数并且应该返回一个 variable list或者variable;forward函数需要将[torch::autograd::AutogradContext](https://link.zhihu.com/?target=https%3A//pytorch.org/cppdocs/api/structtorch_1_1autograd_1_1_autograd_context.html%23structtorch_1_1autograd_1_1_autograd_context)
作为自己的第一个参数。Variables可以被使用ctx->save_for_backward保存,而其他数据类型可以使用ctx->saved_data以<:string>
pairs的形式保存在一个map中。(3)backward
函数第一个参数同样需要为torch::autograd::AutogradContext,其余的参数是一个variable_list,包含的变量数量与forward
输出的变量数量相等。它应该返回和forward
输入一样多的变量。保存在forward中的Variable变量可以通过ctx->get_saved_variables而其他的数据类型可以通过ctx->saved_data获取。请注意,backward的输入参数是自动微分系统反传回来的参数梯度值,其需要和forward函数的返回值位置一一对应的;而backward的返回值是对各参数根据自动微分规则求导后的梯度值,其需要和forward函数的输入参数位置一一对应,对于不需要求导的参数也需要使用空Variable占位。
由于涉及到在C++环境下操作张量和反向传播等操作,因此我们需要对PyTorch的C++后端的库有所了解,主要就是Torch和Aten这两个库,下面我简要介绍一下这两兄弟。// PyG的C++扩展就选择的是直接继承PyTorch的C++端的torch::autograd类进行扩展
// 下面是PyG的一个ScatterSum算子的扩展示例
// 不用纠结这个算子的具体内容,对扩展的算子的结构有一个大致了解即可
class ScatterSum : public torch::autograd::Function {
public:
// AutogradContext *ctx指针可以操作
static variable_list forward(AutogradContext *ctx, Variable src,
Variable index, int64_t dim,
torch::optional optional_out,
torch::optional dim_size) {
dim = dim < 0 ? src.dim() + dim : dim;
ctx->saved_data["dim"] = dim;
ctx->saved_data["src_shape"] = src.sizes();
index = broadcast(index, src, dim);
auto result = scatter_fw(src, index, dim, optional_out, dim_size, "sum");
auto out = std::get<0>(result);
ctx->save_for_backward({index});
// 如果在扩展的C++代码中使用非Aten内建操作修改了tensor的值,需要对其进行脏标记
if (optional_out.has_value())
ctx->mark_dirty({optional_out.value()});
return {out};
}
// grad_outs是out参数反传回来的梯度值
static variable_list backward(AutogradContext *ctx, variable_list grad_outs) {
auto grad_out = grad_outs[0];
auto saved = ctx->get_saved_variables();
auto index = saved[0];
auto dim = ctx->saved_data["dim"].toInt();
auto src_shape = list2vec(ctx->saved_data["src_shape"].toIntList());
auto grad_in = torch::gather(grad_out, dim, index, false);
// 不需要求导的参数需要空Variable占位
return {grad_in, Variable(), Variable(), Variable(), Variable()};
}
};
其中Torch是PyTorch的C++底层实现(PS:其实是先有的Torch后有的PyTorch,从名字也能看出来),FB在编码PyTorch的时候就有意将PyTorch的接口和Torch的接口设计的十分类似,因此如果你对PyTorch很熟悉的话那么你也会很快的对Torch上手。Torch官方文档传送门:https://pytorch.org/cppdocs/frontend.html安装PyTorch的C++前端的官方教程:https://pytorch.org/cppdocs/installing.html而Aten是ATen从根本上讲是一个张量库,在PyTorch中几乎所有其他Python和C ++接口都在其上构建。它提供了一个核心Tensor
类,在其上定义了数百种操作。这些操作大多数都具有CPU和GPU实现,Tensor
该类将根据其类型向其动态调度。和Torch相比Aten更接近底层和核心逻辑。Aten源代码传送门:https://github.com/zdevito/ATen/tree/master/aten/srcgithub.com使用Aten声明和操作张量的教程:https://pytorch.org/cppdocs/notes/tensor_basics.html由于Pyorch的C++后端文档比较少,因此要多参考官方的例子,尝试去模仿官方教程的代码,同时可以通过Python前端的接口猜测后端接口的功能,如果没有文档了就读一读源码,还是有不少注释的,还能理解实现的逻辑。第三种情况:为TORCHSCRIPT添加C++和CUDA扩展
首先简单解释一下TorchScript是什么,如果用官方的定义来说:“TorchScript是一种从PyTorch代码创建可序列化和可优化模型的方法。任何TorchScript程序都可以从一个Python进程中保存并可以在一个没有Python环境的进程中被加载。”通俗来说TorchScript就是一个序列化模型(即Inference)的工具,它可以让你的PyTorch代码方便的在生产环境中部署,同时在将PyTorch代码转化TorchScript代码时还会对你的模型进行一些性能上的优化。使用TorchScript完成模型的部署要比我们之前提到的使用C++重写要简单的多,因为是自动生成的。TorchScript包含两种序列化模型的方法:tracing和script,两种方法各有其适用场景,由于和本文关系不大就不详细展开了,具体的官方教程传送门在此:https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html但是,TorchScript只能自动化的构造PyTorch的原生代码,如果我们需要序列化自定义的C++扩展算子,则需要我们显式的将这些自定义算子注册到TorchScript中,所幸的是,这一过程其实非常简单,整个过程和第二小节中使用pybind11构建共享库的形式的C++和CUDA扩展十分类似。官方教程传送门如下:https://pytorch.org/tutorials/advanced/torch_script_custom_ops.html而对于自定义的C++类,如果要注册到TorchScript要稍微复杂一些,官方教程传送门如下:https://pytorch.org/tutorials/advanced/torch_script_custom_classes.html?highlight=registeroperators另外需要注意的是,如果想要编写能够被TorchScript编译器理解的代码,需要注意在C++自定义扩展算子参数中的数据类型,目前被TorchScript支持的参数数据类型有torch::Tensor,torch::Scalar(标量类型),double,int64_t和std::vector,而像float,int,short这些是不能作为自定义扩展算子的参数数据类型的。目前就先总结这么多吧~---------♥---------
声明:本内容来源网络,版权属于原作者
图片来源网络,不代表本公众号立场。如有侵权,联系删除
AI博士私人微信,还有少量空位
如何画出漂亮的深度学习模型图?
如何画出漂亮的神经网络图?
一文读懂深度学习中的各种卷积
点个在看支持一下吧
-
matmul torch 详解_pytorch 乘法运算汇总与解析
2020-12-24 01:45:27pytorch 有多种乘法运算,在这里做一次全面的总结。元素一一相乘该操作又称作 "哈达玛积", 简单来说就是 tensor 元素逐个相乘。这个操作,是通过 也就是常规的乘号操作符定义的操作结果。torch.mul 是等价的。import... -
tensor torch 构造_Pytorch:Tensor
2021-03-07 04:33:31从接口的角度来讲,对tensor的...为方便使用,对tensor的大部分操作同时支持这两类接口,在此不做具体区分,如torch.sum (torch.sum(a, b))与tensor.sum (a.sum(b))功能等价。而从存储的角度来讲,对tensor的操作... -
Pytorch与Torch的关系与对比
2021-02-07 10:31:26pytorch可以说是torch的python版,然后增加了很多新的特性 torch是火炬的意思 上面的对比图来源于官网,官方认为,这两者最大的区别就是Pytorch重新设计了model模型和intermediate中间变量的关系,在Pytorch中... -
Pytorch torch.cat与torch.chunk
2019-11-08 15:26:13部分内容摘自pytorch中文文档 torch.cat torch.cat(inputs, dimension=0) → Tensor ,在给定维度上对输入的张量序列seq 进行连接操作。 torch.cat()可以看做 torch.split() 和 torch.chunk()的反操作, cat() 函数... -
python中如何导入torch_[PyTorch入门]之数据导入与处理
2020-12-19 13:00:58数据导入与处理来自这里。在解决任何机器学习问题时,都需要在处理数据上花费大量的努力。PyTorch提供了很多工具来简化数据加载,希望使代码更具可读性。在本教程中,我们将学习如何从繁琐的数据中加载、预处理数据... -
pytorch与torch的关系
2018-09-23 22:15:41分享一篇关于pytorch和torch关系的文章,讲的很清楚。 本文转载至:https://m.oldpan.me/archives/pytorch-torch-relation, 对pytorch感兴趣的可以关注Oldpan博客微信公众号,干货多多,同步更新博客深度学习文章... -
pytorch中深度拷贝_Pytorch深度指南-torch与Tensor常用操作方法
2020-12-19 01:04:27pytorch深度指南-torch与Tensor常用操作方法import torchtorch.Tensor会继承某些torch的某些数学运算,例如sort, min/max....不需要调用相应的torch.funciton进行处理,下文中如果是torch/Tensor即表示该函数可以直接... -
PyTorch中torch.tensor与torch.Tensor的区别详解
2020-09-16 20:53:49主要介绍了PyTorch中torch.tensor与torch.Tensor的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 -
Pytorch安装方法与Pytorch module ‘torch‘ has no attribute ‘FloatTensor‘等错误出现的奇怪原因解决法
2021-03-29 18:08:31Pytorch安装方法与Pytorch module ‘torch’ has no attribute 'FloatTensor’等错误出现的奇怪原因解决法 Pytorch安装方法(我将国内一些开源的安装链接附在最后供你们使用): ①Anaconda3使用Anaconda Prompt打出... -
PyTorch中 torch.nn与torch.nn.functional的区别
2019-05-22 17:40:32pytorch中文文档:torch.nn 在__init__()函数里定义,定义的是一个类: torch.nn.functional pytorch中文文档:torch.nn.functional 在__forward()__函数里定义,定义的是一个函数: 两者的区别: nn中是定义... -
9/6 Pytorch与Torch的关系
2020-09-06 09:30:33pytorch可以说是torch的python版 Pytorch采用python语言接口来实现编程,而torch是采用lua语言,Lua相当于一个小型加强版的C,支持类和面向对象 -
浅谈Pytorch与Torch的关系
2019-12-25 11:10:06那么Pytorch是怎么来的,追根溯源,pytorch可以说是torch的python版,然后增加了很多新的特性,那么pytorch和torch的具体区别是什么,这篇文章大致对两者进行一下简要分析,有一个宏观的了解。 上面的对比图来源... -
Pytorch torch.cat与torch.stack的区别
2019-10-24 12:36:35torch.cat()函数可以将多个张量拼接成一个张量。torch.cat()有两个参数,第一个是要拼接的张量的列表或是元组;第二个参数是拼接的维度。 torch.cat()的示例如下图1所示 ...stack与cat的区别在于,torch.stack()函... -
torch标准化_PyTorch 学习笔记(二):PyTorch的数据增强与数据标准化
2020-12-21 03:05:28加入极市专业CV交流群,与6000+来自腾讯,...点击文末“阅读原文”立刻申请入群~作者| 余霆嵩来源专栏 |PyTorch学习笔记已获作者原创授权,请勿二次转发本文截取自一个github上千星的火爆教程——《PyTorch 模型训... -
add函数 pytorch_博特智能|浅谈Pytorch与Torch的关系
2021-01-03 14:06:26那么Pytorch是怎么来的,追根溯源,pytorch可以说是torch的python版,然后增加了很多新的特性,那么pytorch和torch的具体区别是什么,这篇文章大致对两者进行一下简要分析,有一个宏观的了解。上面的对比图来源于... -
专访田渊栋 | Torch升级版PyTorch开源,Python为先,强GPU加速
2017-08-01 09:42:00专访Facebook研究员田渊栋和PyTorch作者Soumith 新智元 :田博士,关于PyTorch的发布,请问...新智元 :请问 PyTorch 除了是基于 Python,其它架构是否与 Torch 一样? 田渊栋:基本C/C++这边都是用的torch原来的函... -
PyTorch中torch.tensor与torch.Tensor的区别
2020-05-17 18:08:27PyTorch最近几年可谓大火。相比于TensorFlow,PyTorch对于Python初学者更为友好,更易上手。 众所周知,numpy作为Python中数据分析的专业第三方库,比Python自带的Math库速度更快。同样的,在PyTorch中,有一个... -
tensorflow与pytorch
2019-04-22 16:57:33tensorflow中与pytorch同等作用的函数: tf.reshape(input, shape) -> input.view(shape) tf.minium(input, min) -> torch.clamp(input, max) tf.gather(input1, input2) -> input1[input2] tf....