精华内容
下载资源
问答
  • 提供给新手学习的 PHP新手教程,是一比较有价值的PHP新手教程! 一、PHP简介 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一真正交互的WEB站点。本教程并不想让你...
  • 并发编程简介并发编程式Java语言重要特性之一,当然也是最难以掌握内容。编写可靠并发程序是一项不小挑战。但是,作为程序员我们,要变得更有价值,就需要啃一些硬骨头了。因此,理解并发编程基础理论和...

    并发编程简介

    并发编程式Java语言的重要特性之一,当然也是最难以掌握的内容。编写可靠的并发程序是一项不小的挑战。但是,作为程序员的我们,要变得更有价值,就需要啃一些硬骨头了。因此,理解并发编程的基础理论和编程实践,让自己变得更值钱吧。

    使用并发编程的优势

    1、充分利用多核CPU的处理能力

    现在,多核CPU已经非常普遍了,普通的家用PC基本都双核、四核的,何况企业用的服务器了。如果程序中只有一个线程在运行,则最多也只能利用一个CPU资源啊,如果是一个四核的系统,岂不是最多只利用了25%的CPU资源吗?严重的浪费啊!

    另外如果存在I/O操作的话,单线程的程序在I/O完成之前只能等着了,处理器完成处于空闲状态,这样能处理的请求数量就很低了。换成多线程就不一样了,一个线程在I/O的时候,另一个线程可以继续运行,处理请求啊,这样,吞吐量就上来了。

    2、方便业务建模

    如果在程序中只包含一种类型的任务,那么比包含多种不同类型的任务的程序要更易于编写、错误更少,也更容易测试。如果在业务建模中,有多种类型的任务场景。我们可以使用多线程来解决,让每个线程专门负责一种类型的任务。

    通过使用线程,可以将负责并且一步的工作流进一步分解为一组简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置进行交互。

    并发编程带来的风险

    虽然并发编程帮助我们提高了程序的性能,同时也提高对我们程序员的要求,因为在编写并发程序的过程中,一不小心就面临着多线程带来的风险。这些风险主要是安全性问题、活跃性问题和性能问题。

    1、安全性问题

    安全性问题可能是非常复杂的,在多线程场景中,如果没有正确地使用同步机制,会导致程序结果的不确定性,这是非常危险的。

    比如我们熟知的 count++ 问题

    public 

    上面的代码,在单线程环境中没有问题。但是如果是多个线程同时访问getCount方法,则不会得到期望的正确结果。

    原因在于count ++ 不是CPU级别的原子指令,我们写了一条语句,但是在底层实际上包含了三个独立的操作:读取count,将count加1,将计算结果再写会主内存。而这多个线程有机会在其中任何一个操作时发生切换,这样便有可能两个线程拿到了同样的值,让后执行加1的操作。

    2、活跃性问题

    当某个操作无法继续执行下去的时候,就会发生活跃性问题。在串行程序中,活跃性问题形式之一可能是无意中造成的无限循环。在多线程场景中,如果有线程A在等待线程B释放其持有的资源,而线程B永远都不释放该资源,那么线程A将永远地等待下去。

    多线程中的活跃性问题一般指的就是死锁、饥饿、活锁等。

    3、性能问题

    本来是用多线程是为了提高程序性能的,结果却产生了性能问题。性能问题包括多个方面,例如服务时间过长,响应不灵敏,吞吐量过地、资源消耗过高等。

    使用多线程而产生性能问题的根本原因就是,创建线程、切换线程都是要带来某种运行时开销的。如果我们的程序在频繁的创建线程,那很快创建线程的消耗将增加,拖累程序整体性能。同时频繁的线程切换,也会产生性能问题。

    创建线程的几种方法

    在使用Java开始编写并发程序时,我们首先要知道在Java中应该如何创建线程,至少有下面的三种方法。通过线程池创建线程留到后面线程池章节单独说明。

    实现Runnable接口

    我们通过实现一个Runnable接口,将线程要执行的任务封装起来。

    public 

    使用Thread对象启动线程

    public 

    实现Callable接口

    可以看到实现Runnable接口启动的线程是没有返回值的。而Callable接口可以实现有返回值地启动线程。

    public 

    通过FutureTask 我们可以获取到返回值

    public 

    继承Thread类

    继承Thread类,重写run方法。

    public 

    一般不建议通过这种方式创建线程,因为:

    • Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
    • 类可能只要求可执行就行,继承整个 Thread 类开销过大。

    线程安全性问题

    我们编写并发程序,最先要考虑的就是安全性问题,要保证在多线程执行条件下程序运行结果的正确性。

    要编写线程安全的代码,其核心就是要对状态访问的操作进行管理,特别是对共享的和可变的状态的访问。

    当多个线程访问某个状态变量并且其中有一个线程执行写入操作时,必须采用同步机制来协同这些线程对变量的访问。Java中的主要同步机制是关键字synchronized,它提供了一种独占的加锁方式,但是“同步”这个术语还包括volatile类型的变量,显示锁(Lock)以及原子变量。

    如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误。有三种方式可以修复这个问题:

    • 不在线程之间共享该状态变量
    • 将状态变量修改为不可变的变量
    • 在访问状态变量时使用同步

    原子性

    这里的原子性其实和数据库事务中的原子性意义是相同的。我们把不可分割的一组操作叫做原子操作,这种不能中断的特性叫做原子性。

    例如上面提到的 count++ 的问题,在java语法上,这看上去是一条指令,其实在CPU层面,至少需要三条CPU指令,因此,对CPU而言,count++的操作不是一个原子操作。

    65adf55d0b5760ee298b76fb72f37e80.png

    CPU能保证的原子操作是CPU指令级别的,而不是高级语言的一条语句。

    竞态条件

    在并发编程中,因为线程切换,导致不恰当的执行时序而出现不正确结果的情况,叫做竞态条件。上面的count++ 的例子中就存在着竞态条件。

    最常见的竞态条件类型就是“先检查后执行”操作,即通过一个可能失效的观测结果来决定下一步的动作。

    因此在并发编程实践中,要避免竞态条件的发生,才能保证线程安全性。

    加锁机制

    原子性问题的源头是线程切换,而操作系统做线程切换是依赖CPU中断的,所以禁止CPU中断就能够禁止线程切换。但是禁止线程切换就能保证原子性吗?

    答案是并不能,例如在多核32位操作系统下,执行long类型变量的写操作。因为long类型变量是64位,在32位CPU上执行写操作会被拆分成两次写操作(写高32位和写低32位)。

    5b0c5ea23a35bdf64c78daea4e95642e.png

    可能会出现,在同一时刻,一个线程A在CPU-1上执行写高32位指令,另一个线程B在CPU-2上也在执行写高32位指令,这样就会出现诡异的bug。此时,禁止CPU中断并不能保证同一时刻只有一个线程执行。

    9537978ec74622e30ff2cf5bdd04b3a9.png

    因此,我们要有一种机制,保证同一时刻只有一个线程执行。我们称之为“互斥”。

    简易锁模型

    根据互斥特性,我们可以尝试构建一种简易的锁模型。

    71fd1d203c73e6dc48a8b0857bb993a2.png

    通过加锁的操作,使得同一时刻,只有一个线程在执行临界区的代码。

    Java语言提供的内置锁技术:sychronized

    Java语言提供了关键字synchronized,就是一种锁的实现。准确的来说,这种实现是JVM帮我们实现的。

    synchronized关键字可以用来修饰方法,也可以用来修饰代码块。基本的使用如下:

    public 

    这里有一个 类锁和对象锁的概念,比如上面修饰静态方法的synchronized,是以SyncDemo.class 类为锁对象的,而修饰普通实例方法的synchronized,是以当前实例对象为锁对象的。

    synchronized是一种内置锁,线程在进入同步代码块之前会自动获得锁,并且在退出同步代码块时自动释放锁。另外synchronized还是一种互斥锁,互斥意味着当线程A尝试获取一个由线程B持有的锁时,线程A必须等待或者阻塞,直到线程B释放这个锁。如果线程B永远不释放锁,那么线程A也将永远地等待下去。

    内置锁synchronized是可以重(chong)入的。 可重入的意思是如果某个线程在尝试获取一个已经有它自己持有的锁时,这个请求就会成功。

    如果内置锁不是可重入的,那下面的代码将会发生死锁。因为Payment和AliPayment的doService()方法都是synchronized的,因此每个方法在执行前都会获取Payment上的锁,如果内置不是可重入的,那么在执行super.doService()方法时,将无法获得锁,因为这个锁已经被持有,从而AliPayment方法就不会结束,从而也不会释放锁,线程将永远等待下去。

    public 

    基础线程机制

    线程的生命周期(6种状态)

    通过查看Thread源码,我们可以知道线程总共有6中状态。

    cc6edb7264af2ee266afedd55b201405.png

    一个线程只能处于一种状态,线程在这几种状态之间的转换便构成了线程的生命周期。

    52f27117f05e5f2415ea3db37da60689.png

    这张图需要熟记于胸,面试高频题。

    sleep和join,yield

    sleep是Thread类的一个静态方法,它让当前正在执行的线程睡眠指定的毫秒数。

    public 

    需要注意的是sleep方法会抛出InterruptedException 中断异常。

    join方法:

    一个线程可以在其他线程之上调用join方法,其作用是等待一段时间直到第二个线程运行结束才继续执行。

    通过看join的源码可以知道,join的底层其实是在调用wait方法实现线程协作的。

    public 

    yield方法:

    是一种给线程调度器的暗示:让当前线程让出CPU的使用权,让其他线程执行一会。不过这种暗示没有任何机制保证它将会被采纳。

    关于sleep、join、yield,可以移步下面这篇进一步了解。 Java多线程中join、yield、sleep方法详解

    线程的优先级

    通过setPriority方法,我们可以设置线程的优先级。线程的优先级将该线程的重要性传递给了调度器。尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先级高的线程先执行。然而,这并不是意味着优先级低的线程将得不到执行。优先级低的线程仅仅是执行的频率较低而已。

    需要注意的是试图通过控制线程的优先级来控制线程的执行顺序,这是完全错误的做法。

    对象的共享

    只有正确地共享和发布对象,才能保证多线程同时访问的安全性。

    可见性问题(Volatile变量)

    什么是可见性问题。举个例子,当读操作和写操作在不同的线程中执行时,我们无法保证执行读操作的线程能够及时地看到写线程写入的值。这就是可见性问题。

    下面的程序说明了当多个线程在没有同步的情况下共享数据会出现的问题。

    public 

    在代码中,主线程和读线程都在访问共享的变量ready和number。主线程启动读线程,然后将number设置为42,并将ready设置为true。读线程则一直循环直到发现ready为true时,然后输出number的值。我们期望是输出42。但事实上有可能输出0,或者程序根本无法终止。这是因为代码中没有使用足够的同步机制,无法保证主线程修改的number和ready值对于读线程来说是可见的。

    重排序:

    上面程序可能会输出0,即读线程看到了ready的值,但却没有看到之后写入的number的值(代码中却是先写入number,再写入ready,顺序变了),这种现象叫做“重排序”。

    在没有同步的情况下,编译器、处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整。

    volatile变量:

    volatile变量是java语言提供的一种稍弱的同步机制(比起synchronized锁而言),用来确保将变量的更新操作通知到其他线程。

    当把变量申明成volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

    在访问volatile类型的变量时不会执行加锁的操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比synchronized关键字更轻量级的同步机制。

    加锁机制既可以保证可见性又可以保证原子性,但是volatile变量只能确保可见性。

    当且仅当满足一下所有条件时,才应该使用volatile变量:

    • 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
    • 该变量不会与其他状态变量一起纳入不变形条件中。
    • 在访问变量时不需要加锁。

    发布和逸出

    “发布”一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。例如,将一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一个非私有的方法中返回该引用,或者将引用传递到其他类的方法中。

    在许多情况下,我们需要确保对象及其内容状态不被发布。而在某些情况下,我们又需要发布某个对象,但如果在发布时要确保线程安全性,则可能需要同步。

    当某个不应该发布的对象被发布时,这种情况就被称为逸出。

    下面是一个发布的例子:

    public 

    线程封闭

    前面说到,当访问共享的可变数据时,通常需要使用同步。一种避免使用同步的方式就是不共享数据。如果仅在但线程内访问数据,就不需要使用同步。这种技术被称为线程封闭

    当某个对象封闭在一个线程中时,这种用法将自动实现线程安全性,即使被封闭的对象本身不会线程安全的。

    那在具体的编程实践中,该如何实现线程封闭呢,其实可以通过局部变量或ThreadLocal类等。

    不变性(Final域)

    满足同步需求的另一种方法是使用不可变对象。我们目前为止探讨的所有原子性和可见性的问题,都和多线程访问可变的状态相关。如果这个对象本身的状态不会发生任何改变,那这些复杂性都消失了。我们也不需要同步机制了。

    如果某个对象在被创建后其状态就不能被修改,那么这个对象就称为不可变对象。而不可变对象一定是线程安全的。

    不可变性并不等于将对象中的所有域都声明为final的,即使都声明为final类型的,这个对象也仍然是可变的,因为在final域中可以保存对可变对象的引用。

    当满足以下条件时,对象才是不可变的:

    • 对象创建以后其状态就不能修改。
    • 对象的所有域都是final类型。
    • 对象是正确创建的(在对象的创建期间,this引用没有逸出)。

    final域

    final域是不能被修改的(但如果final域引用的是可变对像,那么这些被引用的对象是可以修改的)。然而,在Java内存模型中,final域还有这特殊的语义。final域能确保初始化过程的安全性,从而可以不受限制地访问不可变对象,并在共享这些对象时无需同步。

    一种好的编程习惯是,除非需要某个域是可变的,否则应将其声明为final域。

    取消与关闭

    一般来说,我们启动一个线程,然后等着它自然 运行结束就完了。但是可能存在这样一种需求,我们有时候想提前结束任务或者线程。比如用户点了取消按钮,需要快速关闭某个应用等。

    取消某个操作的原因可能有很多:

    • 用户请求取消。比如点击了图形界面的取消按钮。
    • 有时间限制的操作。当达到超时时间设置时必须取消正在进行的任务。
    • 因为产生错误了,需要取消正在进行的任务。

    Java语言早期版本中可能存在Thread.stop和suspend等方法去终止一个线程,但这些方法因为安全性问题都已经被废弃了。

    我们一般能想到的去终止一个线程的方法,可能是去检查一个volatile类型的boolean值,通过改变boolean值让线程停下来。像下面这样:

    public 

    如果任务中调用了一些阻塞方法,比如从磁盘或是网络读取字节流。则通过检查标志位的方式取消或者结束任务将变得不可行了,因为存在可能永远不会检查标志位的情况,这样任务任务永远不会结束了。

    在Java线程中提供了一种中断机制,能够使一个线程终止另一个线程的当前工作。

    中断

    线程中断是一种协作机制,线程可以通过这种机制来通知另一个线程,告诉它在合适的或者可能的情况下停止当前的工作,并转而执行其他的工作。

    每个线程都有一个boolean类型的中断状态。当中断线程时,这个线程的中断状态将被设置为true。

    Thread类中包含了和线程中断相关的3个方法:

    public 

    阻塞方法,比如Thread.sleep和Object.wait()等,都会检查线程何时中断,并在发现中断时提前返回。它们在响应中断时执行的操作包括:清理中断状态,抛出InterruptedException,表示阻塞操作由于中断而提前结束。因此,这些阻塞方法抛出InterruptedException就是提供给程序员一种让程序停止的入口。

    public 

    JVM关闭

    JVM可以正常关闭,也可以强行关闭。正常关闭的方式主要有:当最后一个“正常(非守护)”线程结束时,或者当调用了System.exit时,或者通过其他特定平台的方法关闭。强行关闭方式可以是通过调用Runtime.halt或者在操作系统中“杀死”JVM进程。

    关闭钩子

    在正常关闭中,JVM首先会调用所有已注册的关闭钩子(Shutdown Hook)。关闭钩子是指通过Runtime.addShutdownHook注册的但尚未开始的线程。 JVM并不能保证关闭钩子的调用顺序。

    在关闭应用程序线程时,如果有(守护或非守护)线程仍然在运行,那么这些线程接下来将与关闭进程并发执行。当所有的关闭钩子都执行结束时,如果runFinalizersOnExit为true,那么JVM将运行终结器,然后在停止。当被强行关闭时,只是关闭JVM,而不会运行关闭钩子。

    守护线程

    有时候后你希望创建一个线程来执行一些辅助工作,但是又不希望这个线程阻碍JVM的正常关闭。这种情况下可以使用守护线程。

    线程分为两种:普通线程和守护线程。JVM在启动时创建的所有线程中,除了主线程以外,其他的线程都是守护线程(比如垃圾回收线程)。我们平时在代码中创建的线程是普通线程,因为新建的线程会继承创建它的线程的守护状态。

    普通线程和守护线程的主要区别在当线程退出的时候发生的操作。当JVM停止时,所有仍然存在的守护线程都将抛弃——既不会执行finally代码块,也不会执行回卷栈,而JVM只是直接退出。

    线程协作

    当使用多线程同时运行多个任务时,我们通过加锁(互斥锁)的方式实现了多个任务的同步,解决了任务之间干涉问题,其本质是在解决安全性问题。线程协作则是在同步基础上要多个任务之间有协调,也就是说有些任务之间是有先后执行顺序的,一个任务结束了,或者一些任务准备好了,才能执行接下来的任务。

    这种协作,首先是建立在互斥的基础上的。这也就是为什么wait()和notify()方法必须要在同步代码块之中了。

    wait和notify

    当一个任务在方法里遇到了对wait()的调用的时候,当前执行的线程将被挂起,对象上的锁被释放,因为wait()方法释放了锁,这就意味着另一个任务可以获得锁,因此在该对象中的其他synchronized方法可以在wait()期间被调用。而其他方法中一般会使用notify()或者notifyAll()来重新唤起等待的线程。

    wait()有两种形式,其中一种是带毫秒参数的重载方法,含义和sleep()方法里参数的意思相同,都是指“在此期间暂停”。但和sleep不同的是,对于wait()方法而言:

    • 在wait()期间对象锁是释放的。
    • 可以通过notify、notifyAll或者时间到期了,从wait()中恢复执行。

    这里引用《Java编程思想》中的给汽车打蜡的例子来做说明。

    WaxOMatic.java有两个过程:一个是将蜡涂到Car上,一个是抛光它。涂蜡之前要先抛光。即抛光-->涂蜡 -->抛光-->涂蜡.....。这样一个交替的过程。

    public 

    运行结果如下:

    Wax 

    notify和notifyAll的区别

    可能有多个任务在单个Car对象上处于wait状态,因此调用notifyAll()比只调用notify()要更安全。在使用notify()时,在众多等待的线程中只能有一个被唤醒。如果所有这些任务在等待不同的条件,那么你就不会知道是否唤醒了恰当的任务。因此应尽量多地使用notifyAll,它总是没错的。

    另外需要注意的是,当notifyAll()因某个特定锁而被调用时,只有等待这个锁的任务才会被唤醒。

    生产者-消费者模型

    下面的例子是通过wait和notify协作机制实现的生产者-消费者模型。示例中生产者生产面包,消费者消费面包。

    public 

    运行结果:

    生产了一个面包Bread: 

    未完待续

    展开全文
  • 、基于相似度方法 1、概述 “异常”通常是一主观判断,什么样数据被认为是...在普通数据处理中,我们常常需要保留正常数据,而对噪声和异常值的特性基本忽略。但在异常检测中,我们弱化了“噪声”和

    四、基于相似度的方法

    1、概述

    “异常”通常是一个主观的判断,什么样的数据被认为是“异常”的,需要结合业务背景和环境来具体分析确定。

    实际上,数据通常嵌入在大量的噪声中,而我们所说的“异常值”通常指具有特定业务意义的那一类特殊的异常值。噪声可以视作特性较弱的异常值,没有被分析的价值。噪声和异常之间、正常数据和噪声之间的边界都是模糊的。异常值通常具有更高的离群程度分数值,同时也更具有可解释性。

    在普通的数据处理中,我们常常需要保留正常数据,而对噪声和异常值的特性则基本忽略。但在异常检测中,我们弱化了“噪声”和“正常数据”之间的区别,专注于那些具有有价值特性的异常值。在基于相似度的方法中,主要思想是异常点的表示与正常点不同。

    2、基于距离的度量

    基于距离的方法是一种常见的适用于各种数据域的异常检测算法,它基于最近邻距离来定义异常值。此类方法不仅适用于多维数值数据,在其他许多领域,例如分类数据,文本数据,时间序列数据和序列数据等方面也有广泛的应用。

    基于距离的异常检测有这样一个前提假设,即异常点的近邻距离要远大于正常点。解决问题的最简单方法是使用嵌套循环。 第一层循环遍历每个数据,第二层循环进行异常判断,需要计算当前点与其他点的距离,一旦已识别出多于 个数据点与当前点的距离在 之内,则将该点自动标记为非异常值。

    这样计算的时间复杂度为O(N^2),当数据量比较大时,这样计算是及不划算的。 因此,需要修剪方法以加快距离计算。

    2.1 基于单元的方法

    在基于单元格的技术中,数据空间被划分为单元格,单元格的宽度是阈值D和数据维数的函数。具体地说,每个维度被划分成宽度最多为 (D/(2sqrt(d)))单元格。在给定的单元以及相邻的单元中存在的数据点满足某些特性,这些特性可以让数据被更有效的处理。
    在这里插入图片描述
    以二维情况为例,此时网格间的距离为(D/(2
    sqrt(d))),需要记住的一点是,网格单元的数量基于数据空间的分区,并且与数据点的数量无关。这是决定该方法在低维数据上的效率的重要因素,在这种情况下,网格单元的数量可能不多。 另一方面,此方法不适用于更高维度的数据。对于给定的单元格,其L1邻居被定义为通过最多1个单元间的边界可从该单元到达的单元格的集合。请注意,在一个角上接触的两个单元格也是L1邻居。L2邻居是通过跨越2个或3个边界而获得的那些单元格。 上图中显示了标记为X 的特定单元格及其L1和L2邻居集。 显然,内部单元具有8个L1邻居和40个L2邻居。 然后,可以立即观察到以下性质:

    • 单元格中两点之间的距离最多为D/2 。
    • 一个点与L1邻接点之间的距离最大为D。
    • 一个点与它的 Lr邻居(其中r > 2)中的一个点之间的距离至少为F。

    唯一无法直接得出结论的是L2中的单元格。 这表示特定单元中数据点的不确定性区域。对于这些情况,需要明确执行距离计算。同时,可以定义许多规则,以便立即将部分数据点确定为异常值或非异常值。 规则如下:

    • 如果一个单元格中包含超过k 个数据点及其L1 邻居,那么这些数据点都不是异常值。
    • 如果单元A 及其相邻L1 和L2中包含少于k个数据点,则单元A中的所有点都是异常值。

    此过程的第一步是将部分数据点直接标记为非异常值(如果由于第一个规则而导致它们的单元格包含k个点以上)。 此外,此类单元格的所有相邻单元格仅包含非异常值。 为了充分利用第一条规则的修剪能力,确定每个单元格及其L1邻居中点的总和。 如果总数大于k,则所有这些点也都标记为非离群值。

    在这里插入图片描述

    2.2 基于索引的方法

    对于一个给定数据集,基于索引的方法利用多维索引结构(如R树\k-d树)来搜索每个数据对象A在半径D范围 内的相邻点。设 M是一个异常值在其 D-邻域内允许含有对象的最多个数,若发现某个数据对象A的 D-邻域内出现M+1 甚至更多个相邻点, 则判定对象A不是异常值。该算法时间复杂度在最坏情况下为O(kN^2) 其中 k是数据集维数,N是数据集包含对象的个数。该算法在数据集的维数增加时具有较好的扩展性,但是时间复杂度的估算仅考虑了搜索时间,而构造索引的任务本身就
    需要密集复杂的计算量。

    3、基于密度的度量

    基于密度的算法主要有局部离群因子(LocalOutlierFactor,LOF),以及LOCI、CLOF等基于LOF的改进算法。下面我们以LOF为例来进行详细的介绍和实践。

    基于距离的检测适用于各个集群的密度较为均匀的情况。在下图中,离群点B容易被检出,而若要检测出较为接近集群的离群点A,则可能会将一些集群边缘的点当作离群点丢弃。而LOF等基于密度的算法则可以较好地适应密度不同的集群情况。
    在这里插入图片描述
    那么,这个基于密度的度量值是怎么得来的呢?还是要从距离的计算开始。类似k近邻的思路,首先我们也需要来定义一个“k-距离”。

    3.1 k-距离(k-distance§):

    对于数据集D中的某一个对象o,与其距离最近的k个相邻点的最远距离表示为k-distance§,定义为给定点p和数据集D中对象o之间的距离d(p,o),满足:
    在这里插入图片描述
    直观一些理解,就是以对象o为中心,对数据集D中的所有点到o的距离进行排序,距离对象o第k近的点p与o之间的距离就是k-距离。

    在这里插入图片描述

    3.2 k-邻域(k-distance neighborhood):

    由k-距离,我们扩展到一个点的集合——到对象o的距离小于等于k-距离的所有点的集合,我们称之为k-邻域:
    在这里插入图片描述

    在二维平面上展示出来的话,对象o的k-邻域实际上就是以对象o为圆心、k-距离为半径围成的圆形区域。就是说,k-邻域已经从“距离”这个概念延伸到“空间”了。

    3.3 可达距离(reachability distance):

    在这里插入图片描述

    3.4 局部可达密度(local reachability density):

    我们可以将“密度”直观地理解为点的聚集程度,就是说,点与点之间距离越短,则密度越大。在这里,我们使用数据集D中给定点p与对象o的k-邻域内所有点的可达距离平均值的倒数(注意,不是导数)来定义局部可达密度。

    给定点p的局部可达密度计算公式为:
    在这里插入图片描述
    由公式可以看出,这里是对给定点p进行度量,计算其邻域内的所有对象o到给定点p的可达距离平均值。给定点p的局部可达密度越高,越可能与其邻域内的点 属于同一簇;密度越低,越可能是离群点。

    3.5 局部异常因子:

    在这里插入图片描述
    在这里插入图片描述

    最终得出的LOF数值,就是我们所需要的离群点分数。在sklearn中有LocalOutlierFactor库,可以直接调用。下面来直观感受一下LOF的图像呈现效果。

    LocalOutlierFactor库可以用于对单个数据集进行无监督的离群检测,也可以基于已有的正常数据集对新数据集进行新颖性检测。在这里我们进行单个数据集的无监督离群检测。

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    from sklearn.neighbors import LocalOutlierFactor
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus']=False
    pd.set_option('display.max_columns', None)
    pd.set_option('display.max_rows', None)
    

    首先构造一个含有集群和离群点的数据集。该数据集包含两个密度不同的正态分布集群和一些离群点。但是,这里我们手工对数据点的标注其实是不准确的,可能有一些随机点会散落在集群内部,而一些集群点由于正态分布的特性,会与其余点的距离相对远一些。在这里我们无法进行区分,所以按照生成方式统一将它们标记为“集群内部的点”或者“离群点”。

    np.random.seed(61)
    # 构造两个数据点集群
    X_inliers1 = 0.2 * np.random.randn(100, 2)
    X_inliers2 = 0.5 * np.random.randn(100, 2)
    X_inliers = np.r_[X_inliers1 + 2, X_inliers2 - 2]
    # 构造一些离群的点
    X_outliers = np.random.uniform(low=-4, high=4, size=(20, 2))
    # 拼成训练集
    X = np.r_[X_inliers, X_outliers]
    n_outliers = len(X_outliers)
    ground_truth = np.ones(len(X), dtype=int)
    # 打标签,群内点构造离群值为1,离群点构造离群值为-1
    ground_truth[-n_outliers:] = -1
    plt.title('构造数据集 (LOF)')
    plt.scatter(X[:-n_outliers, 0], X[:-n_outliers, 1], color='b', s=5, label='集群点')
    plt.scatter(X[-n_outliers:, 0], X[-n_outliers:, 1], color='orange', s=5,label='离群点')
    plt.axis('tight')
    plt.xlim((-5, 5))
    plt.ylim((-5, 5))
    legend = plt.legend(loc='upper left')
    legend.legendHandles[0]._sizes = [10]
    legend.legendHandles[1]._sizes = [20]
    plt.show()
    

    在这里插入图片描述
    然后使用LocalOutlierFactor库对构造数据集进行训练,得到训练的标签和训练分数(局部离群值)。为了便于图形化展示,这里对训练分数进行了一些转换。

    # 训练模型(找出每个数据的实际离群值)
    clf = LocalOutlierFactor(n_neighbors=20, contamination=0.1)
    # 对单个数据集进行无监督检测时,以1和-1分别表示非离群点与离群点
    y_pred = clf.fit_predict(X)
    # 找出构造离群值与实际离群值不同的点
    n_errors = y_pred != ground_truth
    X_pred = np.c_[X,n_errors]
    X_scores = clf.negative_outlier_factor_
    # 实际离群值有正有负,转化为正数并保留其差异性(不是直接取绝对值)
    X_scores_nor = (X_scores.max() - X_scores) / (X_scores.max() - X_scores.min())
    X_pred = np.c_[X_pred,X_scores_nor]
    X_pred = pd.DataFrame(X_pred,columns=['x','y','pred','scores'])
    X_pred_same = X_pred[X_pred['pred'] == False]
    X_pred_different = X_pred[X_pred['pred'] == True]
    # 直观地看一看数据
    X_pred
    

    在这里插入图片描述

    将训练分数(离群程度)用圆直观地表示出来,并对构造标签与训练标签不一致的数据用不同颜色的圆进行标注。

    plt.title('局部离群因子检测 (LOF)')
    plt.scatter(X[:-n_outliers, 0], X[:-n_outliers, 1], color='b', s=5, label='集群点')
    plt.scatter(X[-n_outliers:, 0], X[-n_outliers:, 1], color='orange', s=5,
    label='离群点')
    # 以标准化之后的局部离群值为半径画圆,以圆的大小直观表示出每个数据点的离群程度
    plt.scatter(X_pred_same.values[:,0], X_pred_same.values[:, 1],
    s=1000 * X_pred_same.values[:, 3], edgecolors='c',
    facecolors='none', label='标签一致')
    plt.scatter(X_pred_different.values[:, 0], X_pred_different.values[:, 1],
                s=1000 * X_pred_different.values[:, 3], edgecolors='violet',
                facecolors='none', label='标签不同')
    plt.axis('tight')
    plt.xlim((-5, 5))
    plt.ylim((-5, 5))
    legend = plt.legend(loc='upper left')
    legend.legendHandles[0]._sizes = [10]
    legend.legendHandles[1]._sizes = [20]
    plt.show()
    

    在这里插入图片描述
    可以看出,模型成功区分出了大部分的离群点,一些因为随机原因散落在集群内部的“离群点”也被识别为集群内部的点,但是一些与集群略为分散的“集群点”则被识别为离群点。

    同时可以看出,模型对于不同密度的集群有着较好的区分度,对于低密度集群与高密度集群使用了不同的密度阈值来区分是否离群点。

    因此,我们从直观上可以得到一个印象,即基于LOF模型的离群点识别在某些情况下,可能比基于某种统计学分布规则的识别更加符合实际情况。

    4、练习

    1.学习使用PyOD库生成toy example并调用LOF算法

    参考资料:

    1. LOF: Identifying Density-Based Local Outliers
      2.https://scikitlearn.org/stable/auto_examples/neighbors/plot_lof_outlier_detection.html?highlight=lof
    展开全文
  • 噪声可以视作特性较弱异常值,没有被分析的价值。噪声和异常之间、正常数据和噪声之间边界都是模糊。异常值通常具有更高离群程度分数值,同时也更具有可解释性。 在普通数据处理中,我们常常需要保留正常...

    1.概述

    异常通常是一个主观的判断,什么样的数据被认为是“异常”的,需要结合业务背景和环境来具体分析确定。 实际上,数据通常嵌入在大量的噪声中,而我们所说的“异常值”通常指具有特定业务意义的那一类特殊的异常值。噪声可以视作特性较弱的异常值,没有被分析的价值。噪声和异常之间、正常数据和噪声之间的边界都是模糊的。异常值通常具有更高的离群程度分数值,同时也更具有可解释性。
    在普通的数据处理中,我们常常需要保留正常数据,而对噪声和异常值的特性则基本忽略。但在异常检测中,我们弱化了“噪声”和“正常数据”之间的区别,专注于那些具有有价值特性的异常值。在基于相似度的方法中,主要思想是异常点的表示与正常点不同。

    2.基于距离的度量

    基于距离的方法是一种常见的适用于各种数据域的异常检测算法,它基于最近邻距离来定义异常值。 此类方法不仅适用于多维数值数据,在其他许多领域,例如分类数据,文本数据,时间序列数据和序列数据等方面也有广泛的应用。 基于距离的异常检测有这样一个前提假设,即异常点的 kk 近邻距离要远大于正常点。解决问题的最简单方法是使用嵌套循环。 第一层循环遍历每个数据,第二层循环进行异常判断,需要计算当前点与其他点的距离,一旦已识别出多于 kk 个数据点与当前点的距离在 DD 之内,则将该点自动标记为非异常值。 这样计算的时间复杂度为O(N2)O(N^{2}),当数据量比较大时,这样计算是及不划算的。 因此,需要修剪方法以加快距离计算。

    2.1 基于单元的方法

    在基于单元格的技术中,数据空间被划分为单元格,单元格的宽度是阈值D和数据维数的函数。具体地说,每个维度被划分成宽度最多为 D2d\frac{D}{{2 \cdot \sqrt d }} 单元格。在给定的单元以及相邻的单元中存在的数据点满足某些特性,这些特性可以让数据被更有效的处理。
    在这里插入图片描述
    以二维情况为例,此时网格间的距离为 D2d\frac{D}{{2 \cdot \sqrt d }} ,需要记住的一点是,网格单元的数量基于数据空间的分区,并且与数据点的数量无关。这是决定该方法在低维数据上的效率的重要因素,在这种情况下,网格单元的数量可能不多。 另一方面,此方法不适用于更高维度的数据。对于给定的单元格,其 L1L_{1} 邻居被定义为通过最多1个单元间的边界可从该单元到达的单元格的集合。请注意,在一个角上接触的两个单元格也是 L1L_{1} 邻居。 L2L_{2} 邻居是通过跨越2个或3个边界而获得的那些单元格。 上图中显示了标记为 XX的特定单元格及其 L1L_{1}L2L_{2} 邻居集。 显然,内部单元具有8个 L1L_{1} 邻居和40个 L2L_{2} 邻居。 然后,可以立即观察到以下性质:

    • 单元格中两点之间的距离最多为 D/2D/2
    • 一个点与 L1L_{1} 邻接点之间的距离最大为 DD
    • 一个点与它的 LrLr 邻居(其中rr > 2)中的一个点之间的距离至少为DD

    唯一无法直接得出结论的是 L2L_{2} 中的单元格。 这表示特定单元中数据点的不确定性区域。 对于这些情况,需要明确执行距离计算。 同时,可以定义许多规则,以便立即将部分数据点确定为异常值或非异常值。 规则如下:

    • 如果一个单元格中包含超过 kk 个数据点及其 L1L_{1} 邻居,那么这些数据点都不是异常值。
    • 如果单元 AA 及其相邻 L1L_{1}L2L_{2} 中包含少于 kk 个数据点,则单元A中的所有点都是异常值。

    此过程的第一步是将部分数据点直接标记为非异常值(如果由于第一个规则而导致它们的单元格包含 kk 个点以上)。 此外,此类单元格的所有相邻单元格仅包含非异常值。 为了充分利用第一条规则的修剪能力,确定每个单元格及其 L1L_{1} 邻居中点的总和。 如果总数大于 kk ,则所有这些点也都标记为非离群值。

    接下来,利用第二条规则的修剪能力。 对于包含至少一个数据点的每个单元格 AA,计算其中的点数及其 L1L_{1}L2L_{2} 邻居的总和。 如果该数字不超过 kk,则将单元格AA 中的所有点标记为离群值。 此时,许多单元可能被标记为异常值或非异常值。

    对于此时仍未标记为异常值或非异常值的单元格中的数据点需要明确计算其 kk 最近邻距离。即使对于这样的数据点,通过使用单元格结构也可以更快地计算出 kk 个最近邻的距离。考虑到目前为止尚未被标记为异常值或非异常值的单元格AA。这样的单元可能同时包含异常值和非异常值。单元格 AA 中数据点的不确定性主要存在于该单元格的 L2L_{2} 邻居中的点集。无法通过规则知道 AAL2L_{2} 邻居中的点是否在阈值距离 DD 内,为了确定单元 AA 中数据点与其L2L_{2} 邻居中的点集在阈值距离 DD 内的点数,需要进行显式距离计算。对于那些在 L1L_{1}L2L_{2} 中不超过 kk 个且距离小于 DD 的数据点,则声明为异常值。需要注意,仅需要对单元 AA 中的点到单元AAL2L_{2}邻居中的点执行显式距离计算。这是因为已知 L1L_{1} 邻居中的所有点到 AA 中任何点的距离都小于 DD,并且已知 LrLr(r>2)(r> 2) 的所有点与 AA上任何点的距离至少为 DD。因此,可以在距离计算中实现额外的节省。

    2.2 基于索引的方法

    对于一个给定数据集,基于索引的方法利用多维索引结构(如 R\mathrm{R} 树、kdk-d 树)来搜索每个数据对象 AA 在半径 DD 范围 内的相邻点。设 MM 是一个异常值在其 DD -邻域内允许含有对象的最多个数,若发现某个数据对象 AADD -邻域内出现 M+1M+1 甚至更多个相邻点, 则判定对象 AA 不是异常值。该算法时间复杂度在最坏情况下为 O(kN2),O\left(k N^{2}\right), 其中 kk 是数据集维数, NN 是数据集包含对象的个数。该算法在数据集的维数增加时具有较好的扩展性,但是时间复杂度的估算仅考虑了搜索时间,而构造索引的任务本身就需要密集复杂的计算量。

    3、基于密度的度量

    基于密度的算法主要有局部离群因子(LocalOutlierFactor,LOF),以及LOCI、CLOF等基于LOF的改进算法。下面我们以LOF为例来进行详细的介绍和实践。

    基于距离的检测适用于各个集群的密度较为均匀的情况。在下图中,离群点B容易被检出,而若要检测出较为接近集群的离群点A,则可能会将一些集群边缘的点当作离群点丢弃。而LOF等基于密度的算法则可以较好地适应密度不同的集群情况。
    在这里插入图片描述
    那么,这个基于密度的度量值是怎么得来的呢?还是要从距离的计算开始。类似k近邻的思路,首先我们也需要来定义一个“k-距离”。

    3.1 k-距离(k-distance§):

    对于数据集D中的某一个对象o,与其距离最近的k个相邻点的最远距离表示为kdistance(p)k-distance(p),定义为给定点p和数据集D中对象o之间的距离d(p,o),满足:

    • 在集合D中至少有k个点 o’,其中oDpo'∈D{p},满足d(p,o)d(p,o)d(p,o')≤d(p,o)
    • 在集合D中最多有k-1个点o’,其中oDpo'∈D{p},满足d(p,o;)<d(p,o)d(p,o;)<d(p,o)

    直观一些理解,就是以对象o为中心,对数据集D中的所有点到o的距离进行排序,距离对象o第k近的点p与o之间的距离就是k-距离
    在这里插入图片描述

    3.2 k-邻域(k-distance neighborhood):

    由k-距离,我们扩展到一个点的集合——到对象o的距离小于等于k-距离的所有点的集合,我们称之为k-邻域:
    Nkdistance(p)(P)=qD backslashpd(p,q)kdistance(p)N_{k − d i s t a n c e ( p )}( P ) = { q ∈ D \ backslash{ p } ∣ d ( p , q ) ≤ k − d i s t a n c e ( p )}

    在二维平面上展示出来的话,对象o的k-邻域实际上就是以对象o为圆心、k-距离为半径围成的圆形区域。就是说,k-邻域已经从“距离”这个概念延伸到“空间”了。

    3.3 可达距离(reachability distance):

    有了邻域的概念,我们可以按照到对象o的距离远近,将数据集D内的点按照到o的距离分为两类:

    • pip_i在对象o的k-邻域内,则可达距离就是给定点p关于对象o的k-距离;
    • pip_i在对象o的k-邻域外,则可达距离就是给定点p关于对象o的实际距离。

    给定点p关于对象o的可达距离用数学公式可以表示为:
    reachdistk(p,o)=maxkdistance(o),d(p,o)r e a c h−d i s t_ k ( p , o ) = m a x {k−distance( o ) , d ( p , o )}
    这样的分类处理可以简化后续的计算,同时让得到的数值区分度更高。

    3.4 局部可达密度(local reachability density):

    我们可以将“密度”直观地理解为点的聚集程度,就是说,点与点之间距离越短,则密度越大。在这里,我们使用数据集D中给定点p与对象o的k-邻域内所有点的可达距离平均值的倒数(注意,不是导数)来定义局部可达密度。
    给定点p的局部可达密度计算公式为:lrdMinPts(p)=1/(oNMinPts(p)reachdistMinPts(p,o)NMinPts(p))lrd_{MinPts}(p)=1/(\frac {\sum\limits_{o∈N_{MinPts}(p)} reach-dist_{MinPts}(p,o)} {\left\vert N_{MinPts}(p) \right\vert})

    由公式可以看出,这里是对给定点p进行度量,计算其邻域内的所有对象o到给定点p的可达距离平均值。给定点p的局部可达密度越高,越可能与其邻域内的点 属于同一簇;密度越低,越可能是离群点。

    3.5 局部异常因子:

    局部离群因子公式:
    在这里插入图片描述
    表示点p的邻域Nk(p)N_k(p)内其他点的局部可达密度与点p的局部可达密度之比的平均数

    • 如果这个比值越接近1,说明o的邻域点密度差不多,o可能和邻域同属一簇;
    • 如果这个比值小于1,说明o的密度高于其邻域点密度,o为密集点;
    • 如果这个比值大于1,说明o的密度小于其邻域点密度,o可能是异常点。

    最终得出的LOF数值,就是我们所需要的离群点分数。在sklearn中有LocalOutlierFactor库,可以直接调用。下面来直观感受一下LOF的图像呈现效果。

    LocalOutlierFactor库可以用于对单个数据集进行无监督的离群检测,也可以基于已有的正常数据集对新数据集进行新颖性检测。在这里我们进行单个数据集的无监督离群检测。

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    from sklearn.neighbors import LocalOutlierFactor
    
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus']=False
    pd.set_option('display.max_columns', None)
    pd.set_option('display.max_rows', None)
    

    首先构造一个含有集群和离群点的数据集。该数据集包含两个密度不同的正态分布集群和一些离群点。但是,这里我们手工对数据点的标注其实是不准确的,可能有一些随机点会散落在集群内部,而一些集群点由于正态分布的特性,会与其余点的距离相对远一些。在这里我们无法进行区分,所以按照生成方式统一将它们标记为“集群内部的点”或者“离群点”。

    np.random.seed(61)
    
    # 构造两个数据点集群
    X_inliers1 = 0.2 * np.random.randn(100, 2)
    X_inliers2 = 0.5 * np.random.randn(100, 2)
    X_inliers = np.r_[X_inliers1 + 2, X_inliers2 - 2]
    
    # 构造一些离群的点
    X_outliers = np.random.uniform(low=-4, high=4, size=(20, 2))
    
    # 拼成训练集
    X = np.r_[X_inliers, X_outliers]
    
    n_outliers = len(X_outliers)
    ground_truth = np.ones(len(X), dtype=int)
    # 打标签,群内点构造离群值为1,离群点构造离群值为-1
    ground_truth[-n_outliers:] = -1
    
    plt.title('构造数据集 (LOF)')
    plt.scatter(X[:-n_outliers, 0], X[:-n_outliers, 1], color='b', s=5, label='集群点')
    plt.scatter(X[-n_outliers:, 0], X[-n_outliers:, 1], color='orange', s=5, label='离群点')
    
    plt.axis('tight')
    plt.xlim((-5, 5))
    plt.ylim((-5, 5))
    legend = plt.legend(loc='upper left')
    legend.legendHandles[0]._sizes = [10]
    legend.legendHandles[1]._sizes = [20]
    plt.show()
    

    在这里插入图片描述
    然后使用LocalOutlierFactor库对构造数据集进行训练,得到训练的标签和训练分数(局部离群值)。为了便于图形化展示,这里对训练分数进行了一些转换。

    # 训练模型(找出每个数据的实际离群值)
    clf = LocalOutlierFactor(n_neighbors=20, contamination=0.1)
    
    # 对单个数据集进行无监督检测时,以1和-1分别表示非离群点与离群点
    y_pred = clf.fit_predict(X)
    
    # 找出构造离群值与实际离群值不同的点
    n_errors = y_pred != ground_truth
    X_pred = np.c_[X,n_errors]
    
    X_scores = clf.negative_outlier_factor_
    # 实际离群值有正有负,转化为正数并保留其差异性(不是直接取绝对值)
    X_scores_nor = (X_scores.max() - X_scores) / (X_scores.max() - X_scores.min())
    X_pred = np.c_[X_pred,X_scores_nor]
    X_pred = pd.DataFrame(X_pred,columns=['x','y','pred','scores'])
    
    X_pred_same = X_pred[X_pred['pred'] == False]
    X_pred_different = X_pred[X_pred['pred'] == True]
    
    # 直观地看一看数据
    X_pred
    

    在这里插入图片描述

    将训练分数(离群程度)用圆直观地表示出来,并对构造标签与训练标签不一致的数据用不同颜色的圆进行标注。

    plt.title('局部离群因子检测 (LOF)')
    plt.scatter(X[:-n_outliers, 0], X[:-n_outliers, 1], color='b', s=5, label='集群点')
    plt.scatter(X[-n_outliers:, 0], X[-n_outliers:, 1], color='orange', s=5, label='离群点')
    
    # 以标准化之后的局部离群值为半径画圆,以圆的大小直观表示出每个数据点的离群程度
    plt.scatter(X_pred_same.values[:,0], X_pred_same.values[:, 1], 
                s=1000 * X_pred_same.values[:, 3], edgecolors='c', 
                facecolors='none', label='标签一致')
    plt.scatter(X_pred_different.values[:, 0], X_pred_different.values[:, 1], 
                s=1000 * X_pred_different.values[:, 3], edgecolors='violet', 
                facecolors='none', label='标签不同')
    
    plt.axis('tight')
    plt.xlim((-5, 5))
    plt.ylim((-5, 5))
    
    legend = plt.legend(loc='upper left')
    legend.legendHandles[0]._sizes = [10]
    legend.legendHandles[1]._sizes = [20]
    plt.show()
    

    在这里插入图片描述
    可以看出,模型成功区分出了大部分的离群点,一些因为随机原因散落在集群内部的“离群点”也被识别为集群内部的点,但是一些与集群略为分散的“集群点”则被识别为离群点。
    同时可以看出,模型对于不同密度的集群有着较好的区分度,对于低密度集群与高密度集群使用了不同的密度阈值来区分是否离群点。
    因此,我们从直观上可以得到一个印象,即基于LOF模型的离群点识别在某些情况下,可能比基于某种统计学分布规则的识别更加符合实际情况。

    4. 练习

    from __future__ import division
    from __future__ import print_function
    
    import os
    import sys
    
    # temporary solution for relative imports in case pyod is not installed
    # if pyod is installed, no need to use the following line
    sys.path.append(
        os.path.abspath(os.path.join(os.path.dirname("__file__"), '..')))
    
    from pyod.models.lof import LOF
    from pyod.utils.data import generate_data
    from pyod.utils.data import evaluate_print
    from pyod.utils.example import visualize
    
    if __name__ == "__main__":
        contamination = 0.1  # percentage of outliers
        n_train = 200  # number of training points
        n_test = 100  # number of testing points
    
        # Generate sample data
        X_train, y_train, X_test, y_test = \
            generate_data(n_train=n_train,
                          n_test=n_test,
                          n_features=2,
                          contamination=contamination,
                          random_state=42)
    
        # train LOF detector
        clf_name = 'LOF'
        clf = LOF()
        clf.fit(X_train)
    
        # get the prediction labels and outlier scores of the training data
        y_train_pred = clf.labels_  # binary labels (0: inliers, 1: outliers)
        y_train_scores = clf.decision_scores_  # raw outlier scores
    
        # get the prediction on the test data
        y_test_pred = clf.predict(X_test)  # outlier labels (0 or 1)
        y_test_scores = clf.decision_function(X_test)  # outlier scores
    
        # evaluate and print the results
        print("\nOn Training Data:")
        evaluate_print(clf_name, y_train, y_train_scores)
        print("\nOn Test Data:")
        evaluate_print(clf_name, y_test, y_test_scores)
    
        # visualize the results
        visualize(clf_name, X_train, y_train, X_test, y_test, y_train_pred,
                  y_test_pred, show_figure=True, save_figure=False)
    

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 想成为嵌入式程序员应知道 0x10 个基本问题 C 语言嵌入式系统编程修炼之一:背景篇 C 语言嵌入式系统编程修炼之二:软件架构篇 C 语言嵌入式系统编程修炼之三:内存操作 C 语言嵌入式系统编程修炼之:屏幕操作 C ...
  • 思考框架

    2019-09-23 10:12:19
    三个问题 现状-目标-实现路径 我现在是个什么水平 基本的业务开发能力 基本的并发处理能力 我想达到一个什么水平 中间件开发能力 我将怎样达到那个目标 了解清除设计模式和算法...问题需要遵循的四个原则如下: 以...

    三个问题 现状-目标-实现路径

    • 我现在是个什么水平
      基本的业务开发能力
      基本的并发处理能力
    • 我想达到一个什么水平
      中间件开发能力
    • 我将怎样达到那个目标
      了解清除设计模式和算法

    四个思考原则

    产品交给一个功能的时候,问题示例:

    • 这个功能会给用户带来什么价值
    • 什么样的用户,什么场景下,会怎么使用这个特性
    • 是否还有其他手段达成目标
    • 如何衡量功能的有效性和收益

    问题需要遵循的四个原则如下:

    • 以终为始,确定好真实目标
    • 任务分解,找到实施路径
    • 沟通反馈,解决与人打交道出现的问题
    • 自动化 ,解决与机器打交道出现的问题
    展开全文
  • 与3G中所选用CDMA技术相比较,该技术在频谱利用率及抗多径干扰等特性上更胜一筹,因此受到了广大科学研究者青睐。 本次毕业设计首先对OFDM发展背景、基本原理等进行了阐述,对MATLAB进行了介绍;建立一...
  • 并从这六大判据依次上升到图像的四项特征(颜色、纹理、边缘、形状),为 准确地进行云图处理和识别奠定了基础。 3.在小波域内系统研究了基于组合滤波思想的卫星云图预处理算法 卫星云图由于其成像机制,遥感图像的...
  •  本书发行之时,恰逢Oracle 11gR2 发布,新版本设计已经开始面向数据中心,增加了更多技术特性,DBA们眼光,也不再局限于数据库本身,而应该放眼更大数据中心,建设这更大主题。因此,更希望本书对于...
  •  本书是高等院校计算机及相关专业本科生、研究生数据库系统课程的理想教材,也是对相关技术人员非常有价值的参考书。 作译者  Ramez elmasri是世界知名的数据库学者。现任得克萨斯大学职灵顿分校计算机科学与工程...
  • 而越来越多专业知识 Blog 出现,让我们看到了 Blog 更多所蕴涵巨大信息价值:不同 Blog 选择不同内容,收集和整理成为很多人关注专业 Blog ——目前越来越多人获取信息来源是一些固定 Blog 。...
  • 全书共分4部分,第一部分介绍最基本的概念、术语及建模原则,第二部分描述了关系数据模型和关系型DBMS,第三部分讨论数据库设计,第部分主要描述数据库系统中使用物理文件结构和存取方法。书中涉及内容非常...
  • 已经有8756初学者都下载了!三歪把【大厂面试知识点】、【简历模板】、【原创文章】全部整理成电子书,共有1263页!...主要是我可以通过这打赏情况来看我心血有没有价值,嘻嘻。 全网最低价...
  • 本文所介绍网络聊天系统是基于开放web服务器应用程序开发设计,其主要特性是能动态完成信息传递,且具有高效交互性,更有效处理客户请求, 且具有更简单, 更方便数据库访问方法, 易于维护和更新....
  • 网上会展实施依赖于现代计算机信息技术,由于计算机网络信息全球性、开放性、扩散性、共享性和动态性等特性,它在存储、处理、使用和传输上存在严重脆弱性,易于受计算机病毒感染,数据被干扰、遗漏、丢失,...
  •  与其他同类图书不同,本书不仅涵盖了市面上常见各种oracle版本,还指明了各个版本独有性能优化特性。全书以崭新视角开篇立论,围绕查明问题真相和搜寻有效方略,透彻讲解了查询优化器配置,表访问、连接和...
  • 汽车操纵动力学

    2018-11-14 21:22:20
    《汽车操纵动力学原理》系统地介绍了汽车操纵性与稳定性的基本概念、评价理论、试验方法、轮胎的特性理论与实用模型以及汽车操纵运动计算机仿真与性能预测,并讨论了各种设计参数对汽车操纵性能影响。...
  • 解决问题软件是否有一定商业价值和实用价值,即经济可行性;是否有可行方法来解决问题,即是否有技术可行性;以及是否使工作更便利,并能较为方便操作使用,即操作可行性。经过分析,认定可行后再去投资、...
  • 整个设计模式领域还很新,本书的四位作者也许已占据了这领域造诣最深的专家中的半数,因而他们定义模式的方法可以作为后来者的榜样。如果要知道怎样恰当定义和描述设计模式,我们应该可以从他们那儿获得启发”--steve...
  • 而越来越多专业知识 Blog 出现,让我们看到了 Blog 更多所蕴涵巨大信息价值:不同 Blog 选择不同内容,收集和整理成为很多人关注专业 Blog ——目前越来越多人获取信息来源是一些固定 Blog 。...
  •  从用户的角度出发,“交易”的参与用户都是为了盈利而来的,而盈利,基本上可以概括为“开源节流”四个字。开源,即提高收益,让用户能便捷的买到更具升值价值的代币,能方便的使用各种金融...
  •  从用户的角度出发,“交易”的参与用户都是为了盈利而来的,而盈利,基本上可以概括为“开源节流”四个字。开源,即提高收益,让用户能便捷的买到更具升值价值的代币,能方便的使用各种金融工具手段降...
  • 判定表(适用,四个组成部分,规则,使用) 因果图(适用,四个符号,使用) 正交表(定义,特性,L_n(m^k),因素,水平,使用,工具allpairs) 场景法(定义,价值基本流,备选流,使用) 流程图(定义,使用)...
  • 紫辰报表根据报表数据项的特性,将报表设计区域划分为主单区和明细区。主单区所有单元格数据在整个报表中只出现一次;明细区所有单元格数据在整个报表中可出现多次,且必须位于同一行内。 主单区Excel单元格与...
  • 二叉排序树与平衡二叉树实现

    热门讨论 2010-12-26 15:25:31
    35% 04 综合运用知识的能力 10 能运用所学知识和技能去发现与解决实际问题,能正确处理实验数据,能对课题进行理论分析,得出有价值的结论。 05 应用文献的能力 5 能独立查阅相关文献和从事其他调研;能提出并较...
  • 5.9.6 光源物理特性 379 5.10 透明 380 5.10.1 透明材料折射效果 381 5.10.2 简单透明模型 382 5.10.3 Z缓冲器算法中透明处理 383 5.10.4 伪透明 384 5.11 阴影 385 5.11.1 扫描转换阴影算法 388 ...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 138
精华内容 55
关键字:

价值的四个基本特性