以估计正态分布为例,
高斯核函数:
核函数估计方法:
clear alln=100
%生成一些正态分布的随机数
x=normrnd(0,1,1,n)
minx = min(x)
maxx = max(x)
dx = (maxx-minx)/n
x1 = minx:dx:maxx-dx
h=0.5
f=zeros(1,n)
for j = 1:n
for i=1:n
f(j)=f(j)+exp(-(x1(j)-x(i))^2/2/h^2)/sqrt(2*pi)
end
f(j)=f(j)/n/h
end
plot(x1,f)
%用系统函数计算比较
[f2,x2] = ksdensity(x)
hold on
plot(x2,f2,'r') %红色为参考
09 SVM - 线性不可分模型
假设: 函数Ф是一个从低维特征空间到高维特征空间的一个映射,那么如果存在函数K(x,z), 对于任意的低维特征向量x和z,都有:
称函数K(x,z)为核函数(kernal function);
核函数在解决线性不可分问题的时候,采取的方式是:使用低维特征空间上的计算来避免在高维特征空间中向量内积的恐怖计算量;也就是说此时SVM模型可以应用在高维特征空间中数据可线性分割的优点,同时又避免了引入这个高维特征空间恐怖的内积计算量。
本质: 核函数是一个低纬的计算结果,并没有采用低纬到高维的映射。只不过核函数低纬运算的结果 等价于 映射到高维时向量点积的值。
不妨还是从最开始的简单例子出发,设两个向量x 1 = (μ 1 + μ 2 ) T 和x 2 = (η 1 + η 2 ) T ,两个向量的点积是五维空间的映射,因此映射过后的内积为:
而同时我们可以发现有以下公式:
可以发现两者之间非常相似,所以我们只要乘上一个相关的系数,就可以让这两个式子的值相等,这样不就将五维空间的一个内积转换为两维空间的内积的运算。
现有有两个两维的向量,进行二阶多项式扩展,然后进行内积计算,这个时候映射高高维后计算的计算量为:11次乘法+4次加法;采用近似计算的计算量为:3次乘法+2次加法;采用加系数后的近似计算的计算量为:4次乘法+2次加法;
线性核函数(Linear Kernel): 即原函数,不做映射。
多项式核函数(Polynomial Kernel):其中γ、r、d属于超参,需要调参定义;
类似上面的函数,上面的0.8476是调参出来的结果。
重点:
高斯核函数(Gaussian Kernel): 其中γ属于超参,要求大于0,需要调参定义;
高斯核在实际运用中特别多 ,不仅仅是因为需要调的参数比较少。
最重要的原因是:
在sklearn中,核函数是rbf,即Radial basis functionfuntion 径向基 ;其中真正用到的核函数算法是高斯核。
PS:之前在讲加权线性回归中提过相似度的度量,其中用到的就是类似高斯核的函数。
Sigmoid核函数(Sigmoid Kernel):其中γ、r属于超参,需要调参定义;
了解即可,这个核函数别去用它,垃圾得一塌糊涂。
该算法大致上就是把Sigmoid函数变成了tan函数。
将原始数据映射到高维,然后找一个超曲面来分割它们。差不多就是我上一章一开始画的那个图。
1、 核函数可以自定义;核函数必须是正定核函数,即Gram矩阵是半正定矩阵;
2、核函数的价值在于它虽然也是将特征进行从低维到高维的转换,但核函数它事先在低维上进行计算,而将实质上的分类效果表现在了高维上,也就如上文所说的避免了直接在高维空间中的复杂计算;
3、 通过核函数,可以将非线性可分的数据转换为线性可分数据;
令z=x;那么进行多维变换后,应该是同一个向量,从而可以得到以下公式:
了解核函数的构造方式,尤其是高斯核。
11 SVM - 序列最小优化算法 SMO
由于CUDA调试工具的不完善、CUDA调试工具上手难度较高,并行思想本身就难调试等因素,CUDA调试一直都是一件很蛋疼的事情。写CUDA也有三四年了,前段时间在群里见别人问CUDA调试的问题,突然有想法写个CUDA调试的博客。自己经验尚浅,希望各位大大看过后能够在评论里指点一二,共同完善这篇博客。
本博客只针对逻辑bug。
1 定位bug
出现bug的第一想法自然是定位bug。cuda比较奇特的地方在于,有时报错bug在500行,但500行出的代码没有错误,而是在1000行的地方逻辑错了,十分头疼。
下面介绍三种我总结的定位bug方法:
1.1 二分法
一半一半的注释代码,定位bug。比较笨拙和麻烦,但是十分好用。
1.2 输出定位法
将整体代码分为几个模块,正常的CUDA代码大概可以分为数据初始化,内存申请,内存拷贝,核函数执行,结果拷贝等模块。在每个模块结束后输出标志,示例如图1。这样在调试时就可以根据输出快速定位bug大约在什么位置。如下图:
1.3 调试工具
对于部分bug,可以用调试工具更快速的定位。
在linux下,对于访存越界等问题,cuda gdb可以直接定位在崩溃那一行。
win下是Nsight,我不熟悉nsight,求大神补充。
2 解决bug
比较简单的bug,定位后基本就一眼就解决了。但对于复杂的bug,还是比较费劲的。
2.1 调试工具
单步调试,打断点。无论是cuda gdb还是Nsight,都可以定位到某一个线程上进行调试,可以说是非常强大。cuda gdb和nsight都有英文官方文档,建议大家都学一学,熟练后调试事半功倍。
但因为大量线程随机并行执行,有时并不知道该定位到哪个线程上;线程调试不容易控制;定位到单线程调试比较费劲,费时间;教程少(虽然有官方文档),上手难度较大一些。这些都是CUDA调试工具没有被广泛接受的原因。
2.2 缩小数据量或线程数并在核函数中打印
大量线程并行是导致CUDA调试难度大的最大原因,尽量的减少并行量是一个非常好的降低调试难度的办法。“小并行”甚至“串行”能够大大方便调试。
在Fermi以后的架构中,可以在核函数中使用printf。
在合理范围内缩小数据量,进而减少线程数,比如输入图像大小改为16*16。或者修改线程为<<<1,1>>>,printf打印,看是否与预期结果相同。
3 预防bug
每一个写CUDAer大概都有花几个小时甚至几天调一个bug的经历。既然bug这么难调,那么预防bug就显得尤其重要了。
3.1 写代码前一定要完全构思好架构
社会快速发展,人的心也变得着急了。写CUDA代码之前,一定要沉得住气,多花点时间在纸上构思代码,将代码模块化,哪里容易出问题,哪里该写输出,哪里该检查。
3.2 函数返回结果检查
函数返回结果检查能够非常好的定位bug,是基本的编程意识。
虽然代码可能看起来会比较冗余,示例如下,也可以参考cuda sample里的代码。
3.3 函数输入检查
在调用比较重要的函数时,建议用assert检查输入参数与预期值是否相同。
3.4 核函数内检查
举个例子,遇到的情况是在拷贝shred memory时,拷贝逻辑比较复杂。此时可以写一个检查函数,以保证拷贝的正确性。
核函数内代码如下:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)