Kernel PCA for Novelty Detection

Hoffmann H. Kernel PCA for novelty detection[J]. Pattern Recognition, 2007, 40(3): 863-874.

Novelty Detection: 给我的感觉有点像是奇异值检测,但是又不对,训练样本应该默认是好的样本。这个检测应该就是圈个范围,告诉我们在这个范围里的数据是这个类的,外面的不是这个类的,所以论文里也称之为:one-class classification

论文就是利用kernel PCA来寻找这么一个边界(虽然我不知道这个边界是怎么获得的),并讨论了kernel的参数以及载荷向量个数q的选取。

在这里插入图片描述

作者给出了几种方法的比较(SVM及其变种),说明了利用reconstruction error设定边界的优势。

主要内容

大部分符合和kernel PCA的符号是一致的,这里就不多赘述了,先给出reconstruction error的定义:

在这里插入图片描述

其中的行向量是特征向量(高维空间中的,这里只是如此表示,就像kernel PCA精髓,不必显示地表示出),是中心化后的。
很显然,上面的式子的第二项实际上就是在子空间中投影的部分的内积,所以就是重构的被舍弃的误差。很自然,同一类数据的值应该相对比较大。所以选择的特征向量是主特征向量(对应的特征值比较大)。

注:可以发现p(\cdot) \ge 0

这个式子如何计算呢?

在这里插入图片描述

其中\Phi_0 = 1/n \sum \limits_{i=1}^n \Phi(x_i),于是,(\tilde{\Phi}(z), \tilde{\Phi}(z))=p_S(z)可以用下式来计算:

在这里插入图片描述

注: 第二项与Parzen Windows有很大关联
令:
在这里插入图片描述

其中为第个特征向量。
易得:

所以:
在这里插入图片描述

需要注意的是(6)和(9)中有许多重复计算的地方,可以先计算这些部分再进行整理会简单很多。

注:kernel为高斯核的时候,k(x, x)的值是固定,可以理解为高维空间中向量的模大小一致,个人认为这是非常好的一个性质,这也是在数值实验中,高斯核的性能远远超出多项式核的原因(至少在这个方面是如此的)。

\sigma^2的选择

高斯核的形态为:
k(x,y) = \exp \{-\frac{\|x-y\|^2}{2\sigma^2}\}
\sigma \rightarrow0的时候,k(x,y) \rightarrow0,这时\Phi(x), \Phi(y)彼此正交,所以\sigma不应当太小。

\sigma \gg \max \|x_i -x_j\|的时候:
k(x,y) \approx 1 - \|x-y\|^2/(2\sigma^2)
可以证明,此时的reconstruction error的效用采用k(x,y)和采用(x\cdot y)的类似的。
首先:

在这里插入图片描述

在这里插入图片描述

均只差一个比例常数,又:
在这里插入图片描述

也差一个比例常数,所以此时采用和并没有太大的差别。这意味着,不能太大。

最后再提一下q的选择,显然q应当足够大,这个参数可以通过特征值来选择。

数值实验

数值实验采用的kernel均为高斯核,多项式核效果核论文中的差别有点大就不提了。

矩形框

数据是如此分布的(只是框定了范围,里面的数据点是从均匀分布中选取的):0.4 ~ 0.6, 2.3 ~ 2.6


在这里插入图片描述

q = 20, \sigma^2=0.2

在这里插入图片描述

q = 20, \sigma^2 = 0.4

在这里插入图片描述

q = 30 \sigma^2= 0.2

在这里插入图片描述

q = 30 \sigma^2=0.4

在这里插入图片描述

q = 40 \sigma^2=0.2

在这里插入图片描述

q = 40 \sigma^2=0.4

在这里插入图片描述

q = 80 \sigma^2=0.1

在这里插入图片描述

q=80
在这里插入图片描述

下面是q=50.\sigma^2=50的error在y=1.5处左右的值

在这里插入图片描述

spiral

原始数据如下(只是这个形状的):
x = (0.07t + a)cos(t) + 1.5 \\ y = (0.07t + a)sin(t) + 1.5 \\ a \in [0,0.1]

在这里插入图片描述

q = 40 \sigma^2 = 0.15

在这里插入图片描述

q = 40
在这里插入图片描述

下面是q=40,\sigma^2=0.15y=1.5左右处的error值:

在这里插入图片描述

代码

import numpy as np



class KernelPCA:

    def __init__(self, data, kernel=2, pra=0.4):
        self.__n, self.__d = data.shape
        self.__data = np.array(data, dtype=float)
        self.kernel = self.kernels(kernel, pra)
        self.__K = self.center()

    @property
    def shape(self):
        return self.__n, self.__d

    @property
    def data(self):
        return self.data

    @property
    def K(self):
        return self.__K

    @property
    def alpha(self):
        return self.__alpha

    @property
    def eigenvalue(self):
        return self.__value

    def kernels(self, label, pra):
        """
        数据是一维的时候可能有Bug
        :param label: 1:多项式;2:exp
        :param pra:1: d; 2: sigma
        :return: 函数 或报错
        """
        if label is 1:
            return lambda x, y: (x @ y) ** pra
        elif label is 2:
            return lambda x, y: \
                np.exp(-(x-y) @ (x-y) / (2 * pra ** 2))
        else:
            raise TypeError("No such kernel...")

    def center(self):
        """中心化"""
        oldK = np.zeros((self.__n, self.__n), dtype=float)
        one_n = np.ones((self.__n, self.__n), dtype=float)
        for i in range(self.__n):
            for j in range(i, self.__n):
                x = self.__data[i]
                y = self.__data[j]
                oldK[i, j] = oldK[j, i] = self.kernel(x, y)
        return oldK - 2 * one_n @ oldK + one_n @ oldK @ one_n

    def ordereig(self, A):
        """晕了,没想到linalg.eig出来的特征值不一定是按序的"""
        value, vector = np.linalg.eig(A)
        order = np.argsort(value)[::-1]
        value = value[order]
        vector = vector.T[order].T
        return value, vector

    def processing(self):
        """实际上就是K的特征分解,再对alpha的大小进行一下调整"""
        value, alpha = self.ordereig(self.__K)
        index = value > 0
        value = value[index]
        alpha = alpha[:, index] * (1 / np.sqrt(value))
        self.__alpha = alpha
        self.__value = value / self.__n

    def score(self, x):
        """来了一个新的样本,我们进行得分"""
        k = np.zeros(self.__n)
        for i in range(self.__n):
            y = self.__data[i]
            k[i] = self.kernel(x, y)
        return k @ self.__alpha


class NoveltyDetection:

    def __init__(self, data, q, kernel=2, pra=0.4):
        self.kernel = self.kernels(kernel, pra) #选择kernel
        kernelPCA = KernelPCA(data, kernel, pra) #进行kernel PCA
        kernelPCA.processing() #进行kernel PCA
        self.__alpha = kernelPCA.alpha[:, :q] #获取相应的alpha
        self.__K = kernelPCA.K #获取K
        self.basedata() #用于计算reconstruction error
        self.__data = np.array(data, dtype=float)
        self.__shape = kernelPCA.shape
        del kernelPCA #我不知道这么做是否有意义

    @property
    def alpha(self):
        return self.__alpha

    @property
    def K(self):
        return self.__K

    @property
    def data(self):
        return self.__data

    @property
    def shape(self):
        return self.__shape

    def kernels(self, label, pra):
        """
        数据是一维的时候可能有Bug
        :param label: 1:多项式;2:exp
        :param pra:1: d; 2: sigma
        :return: 函数 或报错
        """
        if label is 1:
            return lambda x, y: (x @ y) ** pra
        elif label is 2:
            return lambda x, y: \
                np.exp(-(x-y) @ (x-y) / (2 * pra ** 2))
        else:
            raise TypeError("No such kernel...")

    def basedata(self):
        self.unitmean = np.mean(self.K, 1)
        self.allmean = np.mean(self.K)

    def detection(self, z):
        n = self.shape[0]
        phiz = np.zeros(n)
        for i in range(n):
            phiz[i] = self.kernel(z, self.data[i])
        psz = self.kernel(z, z) - np.mean(phiz) * 2 + \
            self.allmean
        temp1 = -np.mean(phiz) + self.allmean
        temp2 = np.array([
            phiz[i] - self.unitmean[i] + temp1
            for i in range(n)
        ], dtype=float)
        pz = psz - np.sum((self.alpha.T @ temp2) ** 2)

        return pz
"""
import matplotlib.pyplot as plt

x = (np.random.rand(1000) + 0.4 / 2.2) * 2.2
y = (np.random.rand(1000) + 0.4 / 2.2) * 2.2

func_and = lambda x: x[0] and x[1]
func_or = lambda x: x[0] or x[1]
def findindex(x, x1, x2, x3, x4):
    index1 = zip(x > x1, x < x2)
    index2 = zip(x > x3, x < x4)
    index3 = list(map(func_and, index1))
    index4 = list(map(func_and, index2))
    index = list(map(func_or, zip(index3, index4)))
    return index

xindex = findindex(x, 0.4, 0.7, 2.3, 2.6)
yindex = findindex(y, 0.4, 0.7, 2.3, 2.6)
index = list(map(func_or, zip(xindex, yindex)))
x = x[index]
y = y[index]
data = np.column_stack((x, y))

test = NoveltyDetection(data, 40, kernel=2, pra=0.2)
Y, X = np.mgrid[0:3:100j, 0:3:100j]
newdata = np.zeros(X.shape)
for i in range(X.shape[0]):
    for j in range(X.shape[1]):
        z = np.array([X[i, j], Y[i, j]])
        newdata[i, j] = test.detection(z)

plt.scatter(X, Y, c=newdata)
plt.xlim(0, 3)
plt.ylim(0, 3)
plt.show()
"""
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容