【PyTorch】计算局部相似矩阵
计算局部相似矩阵
代码文档:https://github.com/lartpang/mypython/blob/master/2019-09-25计算局部相关性矩阵/计算局部相关性.ipynb
问题说明
对于给定的数据,其尺寸为N,C,H,W,现在想要计算其局部的相关性,也就是说特定尺寸范围内,例如2*2大小的区域内任意两点之间的点积。
试写出相关的代码。
问题分析
计算局部相关性,而且这里也提到是说使用局部的区域的任意两点之间的点积来计算,所以实际上也就是需要就算对应的2*2范围内的任意两个C维矢量的点积,最终得到一个4*4的关系矩阵。若是在矢量点积的时候,除以各自的模,那么实际上计算的就是两个矢量的余弦距离。
余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。相比距离度量,余弦相似度更加注重两个向量在方向上的差异,而非距离或长度上。
https://blog.csdn.net/weixin_38659482/article/details/85045537
\]
所以“除以模”这个归一化操作放在点积之前。实际上也就是除以沿着C维度计算的L2范数。
最直接的思路是简单的遍历计算,但是不实际,太耗时。如何能够利用GPU并行计算的优势,那自然是使用矩阵操作。
对于已有的N,C,H,W的数据,我们需要计算点积,对于三维以上的点积,可以使用torch.matmul,这时,乘法发生在最右侧的几个维度上。
可以构想,我们最终得到的结果应该是N * NumOfRegions * 4 * 4大小的一个张量。而这里的NumOfRegions表示总的计算了的区域的数量。对于pytorch,我所知道的可以收集区域数据,而且没有其他多余操作的方法只有torch.nn.Unfold。所以这里使用它来实现这个过程。
实现过程
对于矩阵乘法,思考的最简单的方式就是维度匹配。
import torch
import torch.nn as nn
a = torch.rand(1, 2, 3, 4)
b = torch.rand(1, 2, 3, 4)
print("a=>\n", a)
print("b=>\n", b)
a=>
tensor([[[[0.4818, 0.9888, 0.8039, 0.7089],
[0.7667, 0.2273, 0.9956, 0.4739],
[0.9515, 0.1896, 0.7928, 0.0173]],
[[0.1723, 0.8767, 0.4832, 0.6515],
[0.9487, 0.6301, 0.5711, 0.7781],
[0.2017, 0.9220, 0.2793, 0.2675]]]])
b=>
tensor([[[[0.1417, 0.3510, 0.1170, 0.1698],
[0.4311, 0.1535, 0.6087, 0.6646],
[0.1880, 0.4103, 0.0289, 0.1094]],
[[0.3398, 0.8751, 0.8299, 0.3514],
[0.0333, 0.2831, 0.8086, 0.0514],
[0.3168, 0.2895, 0.5107, 0.4949]]]])
这里先定义两个tensor,二者实际上没有关系,后面的计算也没有关系,只是为了多展示一点。
unfold_func = nn.Unfold(2, 1, 0, 1)
unfold_a = unfold_func(a)
print("unfold_a=>\n", unfold_a)
unfold_b = unfold_func(b)
print("unfold_b=>\n", unfold_b)
unfold_a=>
tensor([[[0.4818, 0.9888, 0.8039, 0.7667, 0.2273, 0.9956],
[0.9888, 0.8039, 0.7089, 0.2273, 0.9956, 0.4739],
[0.7667, 0.2273, 0.9956, 0.9515, 0.1896, 0.7928],
[0.2273, 0.9956, 0.4739, 0.1896, 0.7928, 0.0173],
[0.1723, 0.8767, 0.4832, 0.9487, 0.6301, 0.5711],
[0.8767, 0.4832, 0.6515, 0.6301, 0.5711, 0.7781],
[0.9487, 0.6301, 0.5711, 0.2017, 0.9220, 0.2793],
[0.6301, 0.5711, 0.7781, 0.9220, 0.2793, 0.2675]]])
unfold_b=>
tensor([[[0.1417, 0.3510, 0.1170, 0.4311, 0.1535, 0.6087],
[0.3510, 0.1170, 0.1698, 0.1535, 0.6087, 0.6646],
[0.4311, 0.1535, 0.6087, 0.1880, 0.4103, 0.0289],
[0.1535, 0.6087, 0.6646, 0.4103, 0.0289, 0.1094],
[0.3398, 0.8751, 0.8299, 0.0333, 0.2831, 0.8086],
[0.8751, 0.8299, 0.3514, 0.2831, 0.8086, 0.0514],
[0.0333, 0.2831, 0.8086, 0.3168, 0.2895, 0.5107],
[0.2831, 0.8086, 0.0514, 0.2895, 0.5107, 0.4949]]])
这里使用fold和unfold操作之后可以看出来,外侧的括号从原来的四层变为了现在的三层,实际上表示的就是从原来的N,C,H,W变成了现在的N,C*4,H/2*W/2的样子。
而对于H/2*W/2的维度上,在滑窗处理时,也是基于行主序调整成一行的。
unfold_a_reshape = unfold_a.transpose(1, 2).view(1, (3-1)*(4-1), 2, 4) # N,H'W',C,2*2
print("unfold_a_reshape=>\n", unfold_a_reshape)
unfold_b_reshape = unfold_b.transpose(1, 2).view(1, (3-1)*(4-1), 2, 4)
print("unfold_b_reshape=>\n", unfold_b_reshape)
unfold_a_reshape=>
tensor([[[[0.4818, 0.9888, 0.7667, 0.2273],
[0.1723, 0.8767, 0.9487, 0.6301]],
[[0.9888, 0.8039, 0.2273, 0.9956],
[0.8767, 0.4832, 0.6301, 0.5711]],
[[0.8039, 0.7089, 0.9956, 0.4739],
[0.4832, 0.6515, 0.5711, 0.7781]],
[[0.7667, 0.2273, 0.9515, 0.1896],
[0.9487, 0.6301, 0.2017, 0.9220]],
[[0.2273, 0.9956, 0.1896, 0.7928],
[0.6301, 0.5711, 0.9220, 0.2793]],
[[0.9956, 0.4739, 0.7928, 0.0173],
[0.5711, 0.7781, 0.2793, 0.2675]]]])
unfold_b_reshape=>
tensor([[[[0.1417, 0.3510, 0.4311, 0.1535],
[0.3398, 0.8751, 0.0333, 0.2831]],
[[0.3510, 0.1170, 0.1535, 0.6087],
[0.8751, 0.8299, 0.2831, 0.8086]],
[[0.1170, 0.1698, 0.6087, 0.6646],
[0.8299, 0.3514, 0.8086, 0.0514]],
[[0.4311, 0.1535, 0.1880, 0.4103],
[0.0333, 0.2831, 0.3168, 0.2895]],
[[0.1535, 0.6087, 0.4103, 0.0289],
[0.2831, 0.8086, 0.2895, 0.5107]],
[[0.6087, 0.6646, 0.0289, 0.1094],
[0.8086, 0.0514, 0.5107, 0.4949]]]])
这里调整一下形状,这里可以根据维度匹配的思想进行连接,这里就是为了方便通过后面的矩阵乘法实现对于区域内任意点关系的描述矩阵的构造。
mm_unfold_a = torch.matmul(unfold_a_reshape.transpose(2, 3), unfold_a_reshape) # N,H'W',2*2,2*2
print("mm_unfold_a=>\n", mm_unfold_a)
mm_unfold_b = torch.matmul(unfold_b_reshape.transpose(2, 3), unfold_b_reshape)
print("mm_unfold_b=>\n", mm_unfold_b)
mm_unfold_a=>
tensor([[[[0.2619, 0.6275, 0.5329, 0.2181],
[0.6275, 1.7462, 1.5898, 0.7771],
[0.5329, 1.5898, 1.4878, 0.7720],
[0.2181, 0.7771, 0.7720, 0.4487]],
[[1.7462, 1.2184, 0.7771, 1.4851],
[1.2184, 0.8796, 0.4871, 1.0763],
[0.7771, 0.4871, 0.4487, 0.5862],
[1.4851, 1.0763, 0.5862, 1.3174]],
[[0.8796, 0.8847, 1.0763, 0.7569],
[0.8847, 0.9270, 1.0779, 0.8429],
[1.0763, 1.0779, 1.3174, 0.9163],
[0.7569, 0.8429, 0.9163, 0.8301]],
[[1.4878, 0.7720, 0.9209, 1.0200],
[0.7720, 0.4487, 0.3433, 0.6240],
[0.9209, 0.3433, 0.9459, 0.3664],
[1.0200, 0.6240, 0.3664, 0.8860]],
[[0.4487, 0.5862, 0.6240, 0.3562],
[0.5862, 1.3174, 0.7153, 0.9488],
[0.6240, 0.7153, 0.8860, 0.4078],
[0.3562, 0.9488, 0.4078, 0.7065]],
[[1.3174, 0.9163, 0.9488, 0.1700],
[0.9163, 0.8301, 0.5930, 0.2164],
[0.9488, 0.5930, 0.7065, 0.0884],
[0.1700, 0.2164, 0.0884, 0.0719]]]])
mm_unfold_b=>
tensor([[[[0.1355, 0.3471, 0.0724, 0.1180],
[0.3471, 0.8891, 0.1805, 0.3017],
[0.0724, 0.1805, 0.1869, 0.0756],
[0.1180, 0.3017, 0.0756, 0.1037]],
[[0.8891, 0.7674, 0.3017, 0.9213],
[0.7674, 0.7025, 0.2530, 0.7424],
[0.3017, 0.2530, 0.1037, 0.3224],
[0.9213, 0.7424, 0.3224, 1.0244]],
[[0.7025, 0.3115, 0.7424, 0.1204],
[0.3115, 0.1523, 0.3875, 0.1309],
[0.7424, 0.3875, 1.0244, 0.4461],
[0.1204, 0.1309, 0.4461, 0.4443]],
[[0.1869, 0.0756, 0.0916, 0.1865],
[0.0756, 0.1037, 0.1186, 0.1450],
[0.0916, 0.1186, 0.1357, 0.1689],
[0.1865, 0.1450, 0.1689, 0.2522]],
[[0.1037, 0.3224, 0.1450, 0.1490],
[0.3224, 1.0244, 0.4839, 0.4306],
[0.1450, 0.4839, 0.2522, 0.1597],
[0.1490, 0.4306, 0.1597, 0.2616]],
[[1.0244, 0.4461, 0.4306, 0.4668],
[0.4461, 0.4443, 0.0455, 0.0982],
[0.4306, 0.0455, 0.2616, 0.2559],
[0.4668, 0.0982, 0.2559, 0.2569]]]])
这里计算了乘法,实际上结果计算出来的就是对应的关系矩阵。这里结果的尺寸为N, NumOfRegion, 2*2, 2*2。(这里没有计算范数,实际上应该除以范数)
a_ = a[0, :2, :2, :2]
b_ = b[0, :2, :2, :2]
print(a_.shape, b_.shape)
a_ = a_.reshape(1, 2, 2*2) # N,C,2*2
b_ = b_.reshape(1, 2, 2*2)
print("torch.matmul(a_.t, a_)=>\n", torch.matmul(a_.transpose(1, 2), a_))
print("torch.matmul(b_.t, b_)=>\n", torch.matmul(b_.transpose(1, 2), b_))
print(torch.matmul(a_.transpose(1, 2), a_)[0] == mm_unfold_a[0, 0])
print(torch.matmul(b_.transpose(1, 2), b_)[0] == mm_unfold_b[0, 0])
torch.Size([2, 2, 2]) torch.Size([2, 2, 2])
torch.matmul(a_.t, a_)=>
tensor([[[0.2619, 0.6275, 0.5329, 0.2181],
[0.6275, 1.7462, 1.5898, 0.7771],
[0.5329, 1.5898, 1.4878, 0.7720],
[0.2181, 0.7771, 0.7720, 0.4487]]])
torch.matmul(b_.t, b_)=>
tensor([[[0.1355, 0.3471, 0.0724, 0.1180],
[0.3471, 0.8891, 0.1805, 0.3017],
[0.0724, 0.1805, 0.1869, 0.0756],
[0.1180, 0.3017, 0.0756, 0.1037]]])
tensor([[True, True, True, True],
[True, True, True, True],
[True, True, True, True],
[True, True, True, True]])
tensor([[True, True, True, True],
[True, True, True, True],
[True, True, True, True],
[True, True, True, True]])
从这里可以看出来,通过fold、reshape(view)、matmul实现了对于N,C,H,W形状的数据的局部(这里对应为滑窗操作的kernel_size)关联矩阵的计算,而且速度又快(相较于最原始朴素的“滑窗式”计算方法)。
对于运算过程代码的书写,这里验证了一个想法,简单的按照矩阵的维度匹配的原则,是可以直接写出来这个局部关系矩阵的:
N,C,H,W --(Ws*Ws)-->
N,C*Ws*Ws,H/Ws*W/Ws -->
N,H/Ws*W/Ws,C*Ws*Ws -->
N,H/Ws*W/Ws,C*Ws*Ws -->
N,H/Ws*W/Ws,C,Ws*Ws -->
N,H/Ws*W/Ws,Ws*Ws,Ws*Ws
这里的H/Ws*W/Ws实际上反映出来的是分块的数量,这里直接使用除法对应的是滑窗大小正好可以被数据长宽整除,同时步长等于滑窗大小,没有padding的情况。
前面给出的代码中可以看出来,这里的值对于步长为1的时候,是需要进行调整的。
unfold_func = nn.Unfold(2, 1, 0, 1)
...
unfold_a_reshape = unfold_a.transpose(1, 2).view(1, (3-1)*(4-1), 2, 4)
【PyTorch】计算局部相似矩阵的更多相关文章
- pytorch 计算图像数据集的均值和标准差
在使用 torchvision.transforms进行数据处理时我们经常进行的操作是: transforms.Normalize((0.485,0.456,0.406), (0.229,0.224, ...
- PyTorch 实战:计算 Wasserstein 距离
PyTorch 实战:计算 Wasserstein 距离 2019-09-23 18:42:56 This blog is copied from: https://mp.weixin.qq.com/ ...
- 机器翻译注意力机制及其PyTorch实现
前面阐述注意力理论知识,后面简单描述PyTorch利用注意力实现机器翻译 Effective Approaches to Attention-based Neural Machine Translat ...
- [源码解析] PyTorch分布式(5) ------ DistributedDataParallel 总述&如何使用
[源码解析] PyTorch 分布式(5) ------ DistributedDataParallel 总述&如何使用 目录 [源码解析] PyTorch 分布式(5) ------ Dis ...
- 任意半径局部直方图类算法在PC中快速实现的框架。
在图像处理中,局部算法一般来说,在很大程度上会获得比全局算法更为好的效果,因为他考虑到了图像领域像素的信息,而很多局部算法可以借助于直方图获得加速.同时,一些常规的算法,比如中值滤波.最大值滤波.最小 ...
- matlab练习程序(局部加权线性回归)
通常我们使用的最小二乘都需要预先设定一个模型,然后通过最小二乘方法解出模型的系数. 而大多数情况是我们是不知道这个模型的,比如这篇博客中z=ax^2+by^2+cxy+dx+ey+f 这样的模型. 局 ...
- 第十五节、韦伯局部描述符(WLD,附源码)
纹理作为一种重要的视觉线索,是图像中普遍存在而又难以描述的特征,图像的纹理特征一般是指图像上地物重复排列造成的灰度值有规则的分布.纹理特征的关键在于纹理特征的提取方法.目前,用于纹理特征提取的方法有很 ...
- PyTorch 数据集类 和 数据加载类 的一些尝试
最近在学习PyTorch, 但是对里面的数据类和数据加载类比较迷糊,可能是封装的太好大部分情况下是不需要有什么自己的操作的,不过偶然遇到一些自己导入的数据时就会遇到一些问题,因此自己对此做了一些小实 ...
- 图像处理之优化---任意半径局部直方图类算法在PC中快速实现的框架
在图像处理中,局部算法一般来说,在很大程度上会获得比全局算法更为好的效果,因为他考虑到了图像领域像素的信息,而很多局部算法可以借助于直方图获得加速.同时,一些常规的算法,比如中值滤波.最大值滤波.最小 ...
随机推荐
- JAVA学习第二周课后作业
Java 的基本运行单位是类.类由数据成员和函数成员组成.变量之间可以相互转换.String是一个类.static是静态.全局的意思.经过测试,Java的枚举类型定义的Size与String一样都不是 ...
- 自定义Vue组件
自定义Vue组件的三步骤 1.创建组件 2.注册组件 3.使用组件 创建组件 //创建组件 var myclock = { data(){ return { clock: new Date().toL ...
- 依赖jquery的select皮肤2
这个下拉菜单存在于body中,不会受select父级overflow的影响,同样依赖于jquery. 缺陷是如果select上的样式不是定义在class上的,不能完全获取select上的样式. 不过, ...
- 【LuoguP4887】第十四分块(前体)
题目链接 题意 区间两数异或在二进制下有 \(k\) 个 \(1\) 的对数. Sol 普通莫队的话,如果要实时维护好区间内的答案需要支持区间对一个数求答案. 直接做不是很好做,容易发现其实这也就是一 ...
- JQuery的deferred.promise()
jQuery提供的deferred.promise()方法的作用是,在原来的Deferred 对象上返回另一个 Deferred 对象,即受限制的 Promise 对象,受限制的 Promise 对象 ...
- 【CF963C】Cutting Rectangle(数论,构造,map)
题意: 思路:考虑构造最小的单位矩形然后平铺 单位矩形中每种矩形的数量可以根据比例算出来,为c[i]/d,其中d是所有c[i]的gcd,如果能构造成功答案即为d的因子个数 考虑如果要将两种矩形放在同一 ...
- CF1213E Two Small Strings
题目链接 问题分析 由于三个字母是等价的,所以大致可以分为如下几种情况: aa, ab ab, ac ab, ba ab, bc 不难发现,第\(3\)中情况可能造成无解(\(n>1\)时),而 ...
- HDU4587--TWO NODES(无向图割点,暴力出奇迹)这是我见过的时间最长的题。。。
TWO NODES Time Limit: 24000/12000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Total ...
- 分布式-信息方式-JMS信息结构
JMS的消息结构JMS消息由以下几部分组成:消息头,属性和消息体消息头包含消息的识别信息和路由信息,消息头包含一些标准的属性如下:1: JMSDestination:由send方法设置2: JMSDe ...
- Java常考面试题整理(六)
101.HTTP相应的结构是怎么样的? 参考答案: HTTP相应由三个部分组成: 1.状态码(status code):描述了相应的状态,可以用来检查是否成功的完成了请求.请求失败的情况下,状态码可以 ...