Tangent Distance

简介

切空间距离可以用在KNN方法中度量距离,其解决的是图像经过有限变换之后还能否被分类正确,例如。对一张数字为5的手写数字图片,将其膨胀后得到图像p1,此时KNN还应认为p1与原图接近,即距离较近,而不是距离其他类别较近。而Tangent Distance较好的解决了经过图像变换后距离度量的问题,其通过梯度下降算法在切空间中优化求得被分类向量和原始图像及其经过变换后图像的空间中最近的点。

Question

对于上面这张图,我们使用传统的欧式距离,极有可能将左边的图分类成4,而不是9。这暴露出一个问题,传统的欧式距离对旋转、缩放、平移等等变换是不鲁棒的,这就告诉我们我们需要一个新的对于常规变换鲁棒的距离评估标准。

Tangent Vector

切空间向量是如何定义的呢?简单的说,切空间向量的切是指在参数变化方向上的切,即输入图像/向量关于参数的微分。要理解这个,首先我们要先将图像变换数学化(为将问题简单化,我们认为只有一种变换,即旋转变换):

\[x_{t} = F(x;a)
\]

上式表示输入图像x旋转a角度变为xt,我们怎么求旋转变换关于角度a的微分呢?很简单:

\[T = \lim_{a \to 0}x_t - x = \lim_{a \to 0} F(x;a) - x
\]

这个定义,也就是图像关于a的微分,也就是切空间向量的定义。

那么这个切空间向量有什么用呢,很有用!

我们可以在切线方向也就是切空间里去搜索一种最合适的变换,也就是得到一个最合适的a,使得变换后的图像与原图的欧式距离最小。

一个简单的想法就是穷举出所有的经过变换后的图像,我们将图片每隔1度旋转增广一张,那么肯定能找到一张与所需要分类的图片距离最近的图像。

当然,切空间距离可不是这么简单,上面的想法是让a离散化的想法,实际上a是连续的,我们可以通过梯度下降法来求得一个最为合适的参数a,从而找到最合适的距离。

Tangent Distance

上面我们定义了切空间向量,下面是切空间距离的定义:

\[D_{tan}(x,y) = \min_a [||x + Ta - y||]
\]

这个定义可以这么理解,T由于是参数方向上的微分,乘以系数a然后加上x就是在参数方向上的变换,a取0度,就是不旋转,a取15度就是旋转15度。而a是参数,通过最小化与需要判别图像的欧式距离来得到参数a,继而得到切空间距离。

显然这里优化过程是需要用到梯度下降算法的。

将上式扩展为多种变换,就是对T的定义扩展为矩阵,比如有r种变换,图像像素维度d,则T矩阵就是r x d的。a就是d x 1维度。

Gradient Descent

简答推导一下这里梯度下降的公式:

\[\frac {\partial(||x + Ta - y||)}{\partial a} = \frac{\partial(x + Ta - y)^T(x + Ta - y)}{\partial a}\\ = \frac{\partial}{\partial a}(x^TTa + a^TT^Tx + a^TT^TTa - A^TT^Ty - y^TTa)\\ =2T^Tx + 2T^TTa - 2T^Ty\\ =2T^T(x + Ta - y)
\]

好了,上面推导完了梯度,可以开始写代码了。

Coding

import cv2 as cv
import numpy as np
import copy
class GradientDescent():
def __init__(self,T):
self.T = T #(2,784)
def __call__(self,x,y):
r,d = self.T.shape # (2,784)
a = np.ones(shape = (r,1)) # (2,1)
t = 0
while True:
b = copy.copy(a)
# (784,2).dot (2,1) -> (784,1) -> (2,1)
a = a - 0.0005 * self.T.dot(x + self.T.T.dot(a) - y)
t += 1
#print(a,b)
if np.sqrt(np.mean((b-a)**2)) < 0.0001 or t > 5000:
break
return a,self.T class TanhDistance():
def __init__(self,frame,transforms = None):
self.vectors = []
h,w = frame.shape
self.hw = h*w
if transforms is not None:
for transform in transforms:
t = transform(frame) # (h,w)
self.vectors.append(np.reshape(t,(h*w,)) - np.reshape(frame,(h*w,)))
self.gradientDescent = GradientDescent(np.array(self.vectors)) # r,28*28
def __call__(self,x,y):
x = np.reshape(x,(self.hw,1))
y = np.reshape(y,(self.hw,1))
a,T = self.gradientDescent(x,y) # (28*28,1) return np.sqrt(np.mean((x + T.T.dot(a) - y)**2))
def get_transforms(frame):
h,w = frame.shape
transformations = []
# rotate
delta_theta = 5
M = cv.getRotationMatrix2D(((w-1)/2.0,(h-1)/2.0),delta_theta,1)
transformations.append(lambda x:cv.warpAffine(x,M,(w,h))) # shift
delta_x = 2
delta_y = 0
M = np.float32([[1,0,delta_x],[0,1,delta_y]])
transformations.append(lambda x:cv.warpAffine(x,M,(w,h))) return transformations if __name__ == "__main__":
img = cv.imread("/home/xueaoru/图片/0000.jpg")
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
gray = cv.resize(gray,(28,28))/255
transforms = get_transforms(gray)
metric = TanhDistance(gray,transforms) img2 = cv.imread("/home/xueaoru/图片/000.jpg")
gray2 = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
gray2 = cv.resize(gray2,(28,28))/255 print("tan distance:{}".format(metric(gray,gray2)))
print("l2 distance:{}".format(np.sqrt(np.mean((gray - gray2)**2))))
#for transform in transforms:
# print(transform(gray))

所用图片:

距离结果:

tangent distance:0.3062723225969733

l2 distance:0.336102326896069Q

[学习笔记] Tangent Distance的更多相关文章

  1. ArcGIS JS 学习笔记1 用ArcGIS JS 实现仿百度地图的距离量测和面积量测

    一.开篇 在博客注册了三年,今天才决定写第一篇博客,警告自己不要懒!!! 二.关于ArcGIS JS 版本选择 在写这篇博客时ArcGIS JS 4.0正式版已经发布.它和3.x版本的不同是,Map不 ...

  2. <老友记>学习笔记

    这是六个人的故事,从不服输而又有强烈控制欲的monica,未经世事的千金大小姐rachel,正直又专情的ross,幽默风趣的chandle,古怪迷人的phoebe,花心天真的joey——六个好友之间的 ...

  3. 【Unity Shaders】学习笔记——SurfaceShader(十一)光照模型

    [Unity Shaders]学习笔记——SurfaceShader(十一)光照模型 转载请注明出处:http://www.cnblogs.com/-867259206/p/5664792.html ...

  4. 【Unity Shaders】学习笔记——SurfaceShader(九)Cubemap

    [Unity Shaders]学习笔记——SurfaceShader(九)Cubemap 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以 ...

  5. 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图

    [Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...

  6. DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)

            本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com         注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...

  7. osgEarth学习笔记(转载)

    osgEarth学习笔记1.        通过earth文件创建图层时,可以指定多个影像数据源和多个高程数据源,数据源的顺序决定渲染顺序,在earth文件中处于最前的在渲染时处于最底层渲染:所以如果 ...

  8. Unity3D 骨骼动画原理学习笔记

    最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭哧吭哧的~ 首先列出学习参考的前人的文章,本文较多的参考了其中的表述: 1. ...

  9. ArcGIS API for JavaScript 4.2学习笔记[21] 对3D场景上的3D要素进行点击查询【Query类学习】

    有人问我怎么这个系列没有写自己做的东西呢? 大哥大姐,这是"学习笔记"啊!当然主要以解读和笔记为主咯. 也有人找我要实例代码(不是示例),我表示AJS尚未成熟,现在数据编辑功能才简 ...

随机推荐

  1. Idea破解2019

    转自:https://blog.csdn.net/qq_36622149/article/details/88910952 Idea破解,亲测有效,轻量快捷高效更新记录:首次:Idea破解,亲测有效2 ...

  2. count(*),count(1),count(字段)

    如果null参与聚集运算,则除count(*)之外其它聚集函数都忽略null. 如:    ID     DD     1      e     2    null    select  count( ...

  3. 5、vim编辑器

    1.什么是VIM? 理解为windows下面的文本编辑器,比如记事本,比如word文档 2.为什么要学? 因为在后面我们配置的服务,都需要人为修改配置,以便让程序按照我们修改后的指示运行. 1.修改配 ...

  4. raise与raise······from

    在python中,如果想手动引发一个异常,我们一般都会使用raise # -*- coding:utf-8 -*- # @Author: WanMingZhu # @Date: 2019/10/22 ...

  5. python面向编程;类的绑定与非绑定方法、反射、内置方法

    一.类的绑定与非绑定方法 ''' 类中定义函数分为了两大类: 1. 绑定方法 特殊之处: 绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数自动传入 绑定给对象的方法: 在类中定义函数没有被任何 ...

  6. 基础数据 补充 set() 集合 深浅拷贝

    一  对字符串的操作 li = ["张曼玉", "朱茵", "关之琳", "刘嘉玲"] s = "_" ...

  7. Hadoop_11_HDFS的流式 API 操作

    对于MapReduce等框架来说,需要有一套更底层的API来获取某个指定文件中的一部分数据,而不是一整个文件 因此使用流的方式来操作 HDFS上的文件,可以实现读取指定偏移量范围的数据 1.客户端测试 ...

  8. Perl环境安装

    在我们开始学习 Perl 语言前,我们需要先安装 Perl 的执行环境. Perl 可以在以下平台下运行: Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, Sun ...

  9. IDEA利用mybatis-generator自动生成dao和mapper

    pom.xml配置 <properties> <java.version>1.8</java.version> <mybatis-generator-core ...

  10. jenkins+docker+docker-compose持续集成

    一.前期准备 1.宿主机安装docker,传送门 2.宿主机安装JDK,传送门 3.宿主机安装maven,传送门 4.宿主机安装git yum install git 5.宿主机安装jenkins,传 ...