精华内容
下载资源
问答
  • 含义类的问题怎么回答
    千次阅读
    2020-05-28 22:20:30

    一、背景

    2000 年,美国克莱数学研究所公布了世界七大数学难题,又称千禧年大奖难题,规定对每一难题的破解者颁发一百万美元的奖金,P与 NP 问题被列为七大世界数学难题之首
    NP 问题是随着计算复杂性理论的产生而出现的. 根据计算复杂性理论,所有科学问题按其解决时间可分为三大类:多项式类、指数类和不可解类。

    二、什么是P和NP问题

    P类问题: 所有可以在多项式时间内求解的判定问题构成P类问题,例如乘法、人名排序。

    判定问题:判断是否有一种能够解决某一类问题的能行算法的研究课题。

    NP类问题: 所有的非确定性多项式时间可解的判定问题构成NP类问题。例如车辆路径规划、TSP 等,NP问题最显著的两个特点:一是分布及每一步的并行性,二是任何NP问题都可以转化为是和否的问题,最根本的是验证多项式。

    非确定性算法将问题分解成猜测和验证两个阶段。算法的猜测阶段是非确定性的,算法的验证阶段是确定性的,它验证猜测阶段给出解的正确性。

    NP问题包括P问题,如果能快速地检验问题,是否代表有快速方法破解问题?
    NP问题

    1. 问题变大时,困难度是怎么增加的;
    2. P代表多项式时间(Polynomial),P类问题中解决问题的步数和时间可以依问题大小用多项式函数表达;NP代表非确定性多项式时间(Non deterministic Polynomial time),重点是多项式时间内做检查,即如果有无限台电脑,可以在同一时间检查所有可能的答案,就可以在多项式时间内找到正确的答案;
    3. NP完全问题:NP中困难的部分,即任何一个NP问题都能通过一个多项式时间算法转换为某个NP问题,如果可以找到解决NP-complete问题的快速程序,就可以解决所有NP问题;
    4. NP-hard:指所有NP问题都能在多项式时间复杂度内归约到的问题。
    5. EXP(指数)类问题,例如下棋的最佳棋路、运算和检查都需要指数时间;Co-NP不容易检查正确答案,但很容易排除错误答案;

    三、理解P和NP问题

    首先,了解图灵机模型(TURing Machine-TM),图灵机可以看做是计算机读或写 0 1 时直接进行工作的不见,实际上它是一个抽象的理想计算模型。在计算的每一时刻,它都处于一个可以用表达式描述的状态,这个表达式里包含了图灵机当前的状态、读写头目前所读的数,读写头即将在目前位置写入的数,以及未来读写头的去向(转移方案)等信息。
    P和NP也可以表述为 1:在确定性图灵机下能用多项式求解的问题就是P类问题,而在非确定性图灵机下能用多项式求解的问题就是NP问题;

    • 确定性图灵机与现实中单CPU的计算机在功能上完全等价,只能进行串行思维,而不能同时并行工作,只存在一种状态转移方案;
    • 非确定性图灵机只是一种理论模型,现实中是不存在的。它具有并行工作的能力,完成一件事,要分若干部进行,每一步有多个可能的选择。
    • 确定性图灵机在每一步只能选择一个目标,若进行下去后不能达到目的,可返回到这一步,再试另外的目标;而对非确定性图灵机而言,它每一步可以同时选择这一步的所有目标,并行的同时计算下去。

    1971 年,库克发现并证明了第一个 NP 完全问题( NPC) ,也就是可满足性问题,即任何 NP 问题都可以在多项式时间内按多项式的规模转化为对可满足性问题的求解。只要可满足性问题具有多项式时间算法,则所有 NP问题都具有多项式时间算法,具有这样特性的 NP 问题,称为 NP 完全问题。从此你只要证明任何一个已知的 NP 完全问题,能在
    多项式内转化到你所论及的问题就足够了,因为多项式转化具有传递性。
    对于某个问题,若任何一个 NP 问题都可在多项式时间内按多项式的规模转化为对该问题的求解,则称该问题为 NP-hard

    显然,若任何一个 NP-hard 问题有多项式时间算法,则所有NP 问题具有多项式时间算法。而 NP 完全问题则是:既是 NP-hard 同时又属于 NP 的问题。

    随着第一个 NP 完全问题的发现和证明,兴起了对这类问题之间的多项式转化研究. 在教科书中通常将这种转化称为归约。注意理解多项式归约必须把握两个关键点2:一是转化本身所需的时间是多项式, 二是转化后对原问题计算规模的扩大,不超出多项式的范围。A 问题归约到 B 问题是指 B 问题更具有普适性,即 B 问题的解决方法相对于 A 问题更具有普适性。
    P 和 NP 属计算机算法,算法问题的核心是复杂的思路,而不是数学理论和公式。:即使证明了 NP = P,也不意味着能得到任意 NP 问题的有效的多项式时间算法。从根本上解决 NP 问题的途径是,或者证明NP 等于 P,从而从理论上找到 NP 问题的多项式时间算法;或者证明 NP 不等于 P,从而,某些 NP 问题永远也不可能有多项式时间算法,以免白费时间精力。


    1. 杜立智,符海东,张鸿,黄远林.P与NP问题研究[J].计算机技术与发展,2013,23(01):37-42. ↩︎

    2. 杜立智,陈和平,符海东.NP完全问题研究及前景剖析[J].武汉工程大学学报,2015,37(10):73-78. ↩︎

    更多相关内容
  • 机器学习:线性分类问题(基础知识)

    千次阅读 多人点赞 2019-09-28 17:35:43
    {1,2,3,...,m}xi​∈C=Rn,yi​∈Y=1,2,3,...,m,要求寻找C上的决策函数 g(x):C→Yg(\mathbf x):C\to Yg(x):C→Y 含义解析 数据集中一个组合是一条数据, xi\mathbf x_ixi​表数据特征, yiy_iyi​表数据的分类结果,...


    本文属于我的机器学习/深度学习系列文章,点此查看系列文章目录

    一、超平面

    超平面不一定是一个面,它是所处向量空间的一个子空间,如立体空间中一个面,二维平面上一条线。它的作用在于将空间中的数据一分为二,达到分类的目的。

    1.1 超平面表达式

    g ( x ) = w T x + w 0 = 0 g(x) = \mathbf w^T\mathbf x+w_0 = 0\\ g(x)=wTx+w0=0

    • w = ( w 1 , w 2 , . . . , w l ) T \mathbf w = (w_1,w_2,...,w_l)^T w=(w1,w2,...,wl)T,权向量(超平面法向量)
    • x = ( x 1 , x 2 , . . . , x l ) T \mathbf x = (x_1,x_2,...,x_l)^T x=(x1,x2,...,xl)T,实例(样本向量)
    • w 0 w_0 w0,偏移量
      在实际中,w确定,因此这个方程代表所有满足的向量x(点)的集合, w T x w^Tx wTx可视为x向w的投影乘以w的模长

    这个方程的解读也可以是x向w的投影长度为 − w 0 ∣ ∣ w ∣ ∣ \frac{-w_0}{||w||} ww0的点集合。

    二、线性函数:距离刻画

    上面的超平面定义了所有在超平面上的点,那如果是不在超平面上的点与该超平面又有什么关系?

    x p x_p xp是x在超平面 w T x + w 0 = 0 \mathbf w^T\mathbf x+w_0=0 wTx+w0=0的投影点,得公式如下:
    x = x p + ( x − x p ) w T x ∣ ∣ w ∣ ∣ = w T x p ∣ ∣ w ∣ ∣ + w T ( x − x p ) ∣ ∣ w ∣ ∣ , 两 边 同 时 乘 上 w T ∣ ∣ w ∣ ∣ \mathbf x = \mathbf x_p + (\mathbf x-\mathbf x_p) \\ \frac{\mathbf w^Tx}{||\mathbf w||} = \frac{\mathbf w^T\mathbf x_p}{||\mathbf w||} + \frac{\mathbf w^T(\mathbf x - \mathbf x_p)}{||\mathbf w||}, 两边同时乘上 \frac{\mathbf w^T}{||\mathbf w||} \\ x=xp+(xxp)wwTx=wwTxp+wwT(xxp),wwT
    可绘出图例如下:
    在这里插入图片描述

    令 d = w T x p ∣ ∣ w ∣ ∣ , z = w T ( x − x p ) ∣ ∣ w ∣ ∣ 结 合   w T x p + w 0 = 0 , 有 d = − w 0 ∣ ∣ w ∣ ∣ , z = w T x + w 0 ∣ ∣ w ∣ ∣ = g ( x ) ∣ ∣ w ∣ ∣ 令d = \frac{\mathbf w^T\mathbf x_p}{||\mathbf w||},z = \frac{\mathbf w^T(\mathbf x - \mathbf x_p)}{||\mathbf w||}\\ 结合\ \mathbf w^T\mathbf x_p + w_0 = 0,\\ 有d = \frac{-w_0}{||\mathbf w||},z = \frac{\mathbf w^T\mathbf x+w_0}{||\mathbf w||} = \frac{g(\mathbf x)}{||\mathbf w||} d=wwTxp,z=wwT(xxp) wTxp+w0=0,d=ww0,z=wwTx+w0=wg(x)
    由图可知,z是任意一个向量 x \mathbf x x到超平面的距离。这个距离的作用在于让我们可以调整超平面的位置使得分类更加准确。当 x \mathbf x x方向和 w \mathbf w w一致时,该距离为正值,否则为负值,因此也可以得出结论,超平面一侧的点全是正值,另一侧全是负值,由此可以得到二分类。

    这两个概念的确立使得对于数据的分类有了依据,我们说将数据分成不同的两类,自然要有一个分界线(即超平面),而给定一个数据,如何去刻画该数据在超平面的一侧还是另一侧,依靠线性函数的符号(实现了类间分类),数据与分界线的远近由线性函数的绝对值刻画(体现了类内距离)。

    三、相似度度量

    相似度从几何的角度看就是两个样本点间距离的大小,距离越近,相似度越大。对于这个距离有好几种刻画形式,主要是不同的范式。

    • MinkovskiMetric 闵氏距离(p-范数)
      D ( x , y ) = [ ∑ i ∣ x i − y i ∣ p ] 1 p D(\mathbf x,\mathbf y) = [\sum_i|x_i-y_i|^p]^{\frac{1}{p}} D(x,y)=[ixiyip]p1

    • 欧式距离(p=2的特殊情况,2范数)
      D ( x , y ) = [ ( x − y ) T ( x − y ) ] = [ ∑ i ∣ x i − y i ∣ 2 ] D(\mathbf x,\mathbf y) = \sqrt{[(\mathbf x- \mathbf y)^T(\mathbf x - \mathbf y)]} = \sqrt{[\sum_i|x_i-y_i|^2]} D(x,y)=[(xy)T(xy)] =[ixiyi2]

    • 曼哈顿距离(p=1的特殊情况,1范数)
      D ( x , y ) = ∑ i ∣ x i − y i ∣ D(\mathbf x, \mathbf y) = \sum_i|x_i-y_i| D(x,y)=ixiyi

    • Chobychev距离(p= ∞ \infty
      D ( x , y ) = max ⁡ i ∣ x i − y i ∣ D(\mathbf x, \mathbf y) = \max_i|x_i-y_i| D(x,y)=imaxxiyi

    为什么要有这么多种距离?

    大部分情况下,一般欧式距离使用的情况较多,我们对于二维平面中的两个点,想要刻画他们的远近往往用欧式距离即可。但设想这样一种情况,假设两个点不能直达(最常见的就是生活中的街道,必须沿街道行走),这个时候用欧式距离刻画两点距离就显得不合理。

    下图为四种不同距离描述下所有到原点相同距离的点构成的图形。
    在这里插入图片描述

    四、分类问题

    4.1 定义

    • 书本定义:

      根据给定数据集 T = { ( x 1 , y 1 ) , . . . , ( x l , y l ) ) } T=\{(\mathbf x_1,y_1),...,(\mathbf x_l,y_l))\} T={(x1,y1),...,(xl,yl))},其中 x i ∈ C = R n , y i ∈ Y = 1 , 2 , 3 , . . . , m x_i\in C=R^n,y_i \in Y= {1,2,3,...,m} xiC=Rn,yiY=1,2,3,...,m,要求寻找C上的决策函数 g ( x ) : C → Y g(\mathbf x):C\to Y g(x):CY

    • 含义解析

      数据集中一个组合是一条数据, x i \mathbf x_i xi表数据特征, y i y_i yi表数据的分类结果,基于已有的 l l l条数据,训练一个从n维空间数据C到分类结果集合Y的映射函数 g ( x ) g(\mathbf x) g(x),使得在随意给定一个新的数据 ( x k , y k ) (\mathbf x_k,y_k) (xk,yk) g ( x k ) = y k ′ ( 预 期 结 果 ) g(\mathbf x_k)=y_k'(预期结果) g(xk)=yk就是其分类的结果,分类的目的就是使 y k ′ y_k' yk(预期结果)与 y k y_k yk(实际结果)尽可能相同。

    4.2 评估方法

    其实就是分训练集和测试集,训练集用于训练得到决策函数,测试集用于测试决策函数的好坏。主要包含两种方法

    1. 交叉验证法
      交叉验证法就是将数据分成两类(训练、测试),进行交叉验证,其中可以简单按比例划分(8:1,10:1等),也可以分成k类,其中1类做测试,剩余k-1类做训练,每一类训练集进行一次测试,最后取平均
    2. 自助法
      取m个随机样本,构成集合D,剩余除D以外的用于测试。

    核心是训练+测试

    4.3 性能评价

    有了测试作为评估,自然要考虑测试结果的好坏,直观来说,一般是预期与实际越相符,决策函数越好。这是采用错误率和精度作为评价标准:

    • 错误率与准确率
      所谓错误率即分类错误的样本站总样本数的比例,定义如下:
      E ( f ; D ) = 1 m ∑ i = 1 m h ( ( f ( x i ) ≠ y i ) ) h ( x ) = { 1 if  x   i s   t r u e 0 else  E(f;D) = \frac{1}{m}\sum_{i=1}^mh((f(\mathbf x_i)\ne y_i))\\ h(x) = \begin{cases} 1 &\text{if } x \ is\ true \\ 0 &\text{else } \end{cases} E(f;D)=m1i=1mh((f(xi)=yi))h(x)={10if x is trueelse 
      准确率相对于错误率,等于1-错误率,定义如下:
      a c c ( f ; D ) = 1 m ∑ i = 1 m h ( f ( x i ) = y i ) = 1 − E ( f ; D ) acc(f;D) = \frac{1}{m}\sum_{i=1}^mh(f(\mathbf x_i)=y_i) \\ =1-E(f;D) acc(f;D)=m1i=1mh(f(xi)=yi)=1E(f;D)
      以上为针对离散型分布数据,连续型分布数据(设密度函数为 p ( x ) p(\mathbf x) p(x))的E与acc如下:
      E ( f ; D ) = ∫ x ∈ D h ( f ( x ) ≠ y ) p ( x ) d x a c c ( f ; D ) = ∫ x ∈ D h ( f ( x ) = y ) p ( x ) d x = 1 − E ( f ; D ) E(f;D) = \int_{x\in D}h(f(\mathbf x) \ne y)p(\mathbf x)d\mathbf x\\ acc(f;D) = \int_{x\in D}h(f(\mathbf x) = y)p(\mathbf x)d\mathbf x\\ =1-E(f;D) E(f;D)=xDh(f(x)=y)p(x)dxacc(f;D)=xDh(f(x)=y)p(x)dx=1E(f;D)
      错误率和准确率本质上可看作一个东西,但是错误率只让我们知道了有多少样本被判别错误(例如正判成负,负判成正,都属于判别错误),但有的时候往往我们更关心判别为正的数据,因为正项数据对我们有利。这时候就需要用到查准率(precision)和查全率(recall)。

      这里举一个例子,假设判定地震是否发生,我们测得决策函数的精度在99%,即对100次测验,有99次预测成功,但是这99次我们不知道发生有多少次,不发生有多少次。假设99次均为不发生。说明你这个决策函数测地震不发生很准,但是发生的情况你测不准(容易出错),这就没有意义,因为我们更关心地震发生的情况。

    • 查准率(精度,precison)、查全率(召回率,recall)和 F 1 , F β F1,F_{\beta} F1,Fβ
      查准率和查全率更适宜被应用于类似这样的场景,如“查出的信息中有多少是用户感兴趣的”或“用户感兴趣的信息查出了多少”。在此基础上,我们引入“真正例(TP,预测结果为正,真实为正)”,“假正例(FP,预测结果正,真实为反)”,“真反例(TN,预测结果为反,真实为反)”,“假反例(FN,预测结果为反,真实为正)”的概念,由四者构成混淆矩阵如下:
      在这里插入图片描述
      由此我们得到查准率P和查全率R的定义如下:
      P = T P T P + F P R = T P T P + F N P = \frac{TP}{TP+FP}\\ R = \frac{TP}{TP+FN} P=TP+FPTPR=TP+FNTP
      从公式中可以看出查准率在于找出所有正例中真实为正的比例(将正例比做用户感兴趣内容,即所有决策函数认为用户感兴趣的内容中用户真正感兴趣的比例),查全率在于刻画有多少真实为正的内容被我们所找出(即决策函数挖掘出的用户真正感兴趣的内容占所有用户会感兴趣内容的比例)。

      事实上这两者存在矛盾性,如果我们想让尽可能多的用户感兴趣的内容被选上(提高查全率),那么必然会选上许多把握不大的内容(查准率下降);如果我们想让我们选出的东西用户必然会感兴趣(提高查准率),那么必然要放弃那些把握不大的内容,就会导致漏掉许多可能感兴趣的内容(查全率下降)。

      • P-R曲线
        我们根据学习器的预测结果对样本进行排序,将“最有可能为正例”的样本放在前面,“最不可能是正例的”样本放在最后,遍历这些样本,过程中不断计算当前的查准率和查全率(很显然一开始查准率很高,查全率会很低),得到P-R曲线如下:
        在这里插入图片描述

      A,B,C为三个不同的学习器,很显然A,B的性能要优于C,因为在任何情况下他们的查全率和查准率都要比C高。

      A,B的性能不好比较,因此需要引入另一个度量平衡点(查准率=查全率时的取值),粗略地认为,既然无法保证两者都好,就取一个两者都尽量好的位置作为学习器的代表位置,进而比较A与B的性能差异。但更常用的是 F 1 F_1 F1度量:
      F 1 = 2 1 P + 1 R F_1 = \frac{2}{\frac{1}{P}+\frac{1}{R}} F1=P1+R12
      F 1 F_1 F1度量本身求两者的调和平均,这个比简单地取两者相等位置等有说服力。但在实际应用中,我们不一定要取两者都好的情况,例如b站给用户推荐视频,肯定是希望选出的视频都是用户感兴趣的,这个时候查准率比查全率更重要。而如警方的逃犯信息检索系统,肯定希望不要漏抓一个逃犯,此时查全率更重要。所以这个时候就要根据实际需求进行选择,所以引入 F β F_{\beta} Fβ度量:
      F β = 1 + β 2 β 2 R + 1 P F_{\beta} = \frac{1+\beta^2}{\frac{\beta^2}{R}+\frac{1}{P}} Fβ=Rβ2+P11+β2
      其中 β \beta β度量了查全率对于查准率的重要性,当 β = 1 \beta=1 β=1即退化为 F 1 F_1 F1度量,当 β > 1 \beta >1 β>1,说明查全率权重更大(更重要),当 0 < β < 1 0<\beta<1 0<β<1,说明查准率权重更大(更重要)。

      查全率和查准率是十分重要的概念,相对于错误率描述你学习器的直观判别能力,查全率和查准率能够描述你学习器的是否有意义。如果判断不重要的事一判一个准,判断重要的事老是判错,准确率高与否也就失去了意义。

    • 真正例率(TPR),假正例率(FPR)
      前面P,R能够较好地体现正例样本被划分正确的效果,但我们较为关心分类对正例效果的影响时,就倾向于使用这两者。但当正例和负例样本分布均匀,两者均是我们期望能较好划分的目标时(例如男女划分),那么P,R就显得力不从心了,因此需要TPR(所有实际为正例的样本中被正确判断成正例的比例),FPR(在所有实际为负例的样本中被错误判断成正例的比例) 以及 ROC曲线(以FPR为横轴,TPR为纵轴的曲线)和AUC(ROC曲线与横轴围成的面积) 来体现了。
      T P R = T P T P + F N F P R = F P T N + F P TPR = \frac{TP}{TP+FN}\\ FPR = \frac{FP}{TN+FP} TPR=TP+FNTPFPR=TN+FPFP
      很显然,TPR就是R(召回率),而FPR则从侧面体现了负例被划分成正例的程度,可以理解为模型成本,将模型比作一台过滤器,FPR就是模型过滤错误掺入杂质的比例。很显然我们希望TPR越大越好,FPR越小越好。为了同时体现这两点,引入了ROC曲线,如下图。
      在这里插入图片描述
      要体现上面两点,就是要让曲线尽可能上凸。为此我们需要一个定量的标准去计算这个凸度,因此又引入了AUC,它时图中绿色阴影区域的面积,显然面积越大,效果越好。

      一个良好的分类器,其ROC曲线必然时在y=x之上的,否则相当于大部分负例被当成了正例,效果极差。


    关于分类评价指标更多的还可以看知乎上一篇比较好的回答

    五、线性分类问题

    上面我们讨论了一般分类问题需要考虑的部分基础知识(还有些不常用的后续添加),本节考虑分类问题中的线性分类。

    所谓线性分类,就是透过特征的线性组合来作出分类决策。对象的特征通常描述为特征值,在向量空间中则是特征向量。如果两类数据可以通过一个线性平面划分,则其分类属于线性分类问题。

    在这里插入图片描述

    5.1 线性判别函数

    线性判别函数即决策函数,在上文中已提过,其表达形式如下:
    g ( x ) = ∑ i w i x i + w 0 = w T x + w 0 g(\mathbf x) = \sum_iw_ix_i + w_0 = \mathbf w^T \mathbf x + w_0 g(x)=iwixi+w0=wTx+w0
    这个式子很容易联想到超平面(分类界):
    g ( x ) = w T x + w 0 = 0 g(\mathbf x) =\mathbf w^T \mathbf x + w_0 = 0 g(x)=wTx+w0=0
    由此对于每一个给定的特征向量 x = ( x 1 , x 2 , . . . , x n ) \mathbf x=(x_1,x_2,...,x_n) x=(x1,x2,...,xn),我们只需代入线性判别函数,观察其结果正负号即可,如下:
    x ∈ { ω 1 if  w T x + w 0 > 0 ω 2 if  w T x + w 0 < 0 \mathbf x\in \begin{cases} \omega_1 &\text{if } \mathbf w^Tx+w_0>0\\ \omega_2 &\text{if } \mathbf w^Tx+w_0 <0 \end{cases} x{ω1ω2if wTx+w0>0if wTx+w0<0

    5.2 线性分类器

    由给定训练样本 { ( x 1 , y 1 ) , . . . , ( x n , y n ) } \{(\mathbf x_1,y_1),...,(\mathbf x_n,y_n)\} {(x1,y1),...,(xn,yn)},求得线性判别函数(决策函数) g ( x ) g(\mathbf x) g(x),实际上是求 w , w 0 \mathbf w,w_0 w,w0的过程,我们要找满足以下条件的 w , w 0 \mathbf w,w_0 w,w0
    w T x i + w 0 ≥ 0 , f o r   e a c h   y i = + 1 w T x i + w 0 ≤ 0 , f o r   e a c h   y i = − 1 \mathbf w^T\mathbf x_i + w_0 \ge0,for \ each \ y_i=+1\\ \mathbf w^T \mathbf x_i+w_0 \le0,for \ each\ y_i = -1 wTxi+w00,for each yi=+1wTxi+w00,for each yi=1
    关于这样的 w , w 0 \mathbf w,w_0 w,w0参数的寻找,就有很多的线性分类器可用,如感知机,线性鉴别分析,Logistic模型等。

    六、参考资料

    • 机器学习.周志华.2016
    展开全文
  • 分类(Classification)属于有监督学习(Supervised Learning)中的一,它是数据挖掘、机器学习和数据科学中一个重要的研究领域。分类模型类似于人类学习的方式,通过对历史数据或训练集的学习得到一个目标函数,...

    欢迎大家来到“Python从零到壹”,在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界。所有文章都将结合案例、代码和作者的经验讲解,真心想把自己近十年的编程经验分享给大家,希望对您有所帮助,文章中不足之处也请海涵。Python系列整体框架包括基础语法10篇、网络爬虫30篇、可视化分析10篇、机器学习20篇、大数据分析20篇、图像识别30篇、人工智能40篇、Python安全20篇、其他技巧10篇。您的关注、点赞和转发就是对秀璋最大的支持,知识无价人有情,希望我们都能在人生路上开心快乐、共同成长。

    前一篇文章讲述了聚类算法的原理知识级案例,包括K-Means聚类、BIRCH算法、PCA降维聚类、均值漂移聚类、文本聚类等。。本文将详细讲解分类算法的原理知识级案例,包括决策树、KNN、SVM,并通过详细的分类对比实验和可视化边界分析与大家总结。四万字基础文章,希望对您有所帮助。

    下载地址:

    前文赏析:

    第一部分 基础语法

    第二部分 网络爬虫

    第三部分 数据分析和机器学习

    作者新开的“娜璋AI安全之家”将专注于Python和安全技术,主要分享Web渗透、系统安全、人工智能、大数据分析、图像识别、恶意代码检测、CVE复现、威胁情报分析等文章。虽然作者是一名技术小白,但会保证每一篇文章都会很用心地撰写,希望这些基础性文章对你有所帮助,在Python和安全路上与大家一起进步。


    分类(Classification)属于有监督学习(Supervised Learning)中的一类,它是数据挖掘、机器学习和数据科学中一个重要的研究领域。分类模型类似于人类学习的方式,通过对历史数据或训练集的学习得到一个目标函数,再用该目标函数预测新数据集的未知属性。本章主要讲述分类算法基础概念,并结合决策树、KNN、SVM分类算法案例分析各类数据集,从而让读者学会使用Python分类算法分析自己的数据集,研究自己领域的知识,从而创造价值。

    一.分类

    1.分类模型

    与前面讲述的聚类模型类似,分类算法的模型如图1所示。它主要包括两个步骤:

    • 训练。给定一个数据集,每个样本包含一组特征和一个类别信息,然后调用分类算法训练分类模型。
    • 预测。利用生成的模型或函数对新的数据集(测试集)进行分类预测,并判断其分类后的结果,并进行可视化绘图显示。

    在这里插入图片描述

    通常为了检验学习模型的性能,会使用校验集。数据集会被分成不相交的训练集和测试集,训练集用来构造分类模型,测试集用来检验多少类标签被正确分类。

    下面举一个分类实例进行讲解。假设存在一个垃圾分类系统,将邮件划分为“垃圾邮件”和“非垃圾邮件”,现在有一个带有是否是垃圾邮件类标的训练集,然后训练一个分类模型,对测试集进行预测,步骤如下:

    • (1) 分类模型对训练集进行训练,判断每行数据是正向数据还是负向数据,并不断与真实的结果进行比较,反复训练模型,直到模型达到某个状态或超出某个阈值,模型训练结束。
    • (2) 利用该模型对测试集进行预测,判断其类标是“垃圾邮件”还是“非垃圾邮件”,并计算出该分类模型的准确率、召回率和F特征值。

    经过上述步骤,当收到一封新邮件时,我们可以根据它邮件的内容或特征,判断其是否是垃圾邮件,这为我们提供了很大的便利,能够防止垃圾邮件信息的骚扰。


    2.常见分类算法

    监督式学习包括分类和回归。其中常见的分类算法包括朴素贝叶斯分类器、决策树、K最近邻分类算法、支持向量机、神经网络和基于规则的分类算法等,同时还有用于组合单一类方法的集成学习算法,如Bagging和Boosting等。

    (1) 朴素贝叶斯分类器
    朴素贝叶斯分类器(Naive Bayes Classifier,简称NBC)发源于古典数学理论,有着坚实的数学基础和稳定的分类效率。该算法是利用Bayes定理来预测一个未知类别的样本属于各个类别的可能性,选择其中可能性最大的一个类别作为该样本的最终类别。其中,朴素贝叶斯(Naive Bayes)法是基于贝叶斯定理与特征条件独立假设的方法 ,是一类利用概率统计知识进行分类的算法,该算法被广泛应用的模型称为朴素贝叶斯模型(Naive Bayesian Model,简称NBM)。

    根据贝叶斯定理,对于一个分类问题,给定样本特征x,样本属于类别y的概率如下:

    在这里插入图片描述

    其中p(x)表示x事件发生的概率,p(y)表示y事件发生的概率,p(x|y)表示事件y发生后事件x发生的概率。由于贝叶斯定理的成立本身需要一个很强的条件独立性假设前提,而此假设在实际情况中经常是不成立的,因而其分类准确性就会下降,同时它对缺失的数据不太敏感。本书没有详细介绍朴素贝叶斯分类实例,希望读者下来自行研究学习。

    (2) 决策树算法
    决策树(Decision Tree)是以实例为基础的归纳学习(Inductive Learning)算法,它是对一组无次序、无规则的实例建立一棵决策判断树,并推理出树形结果的分类规则。决策树作为分类和预测的主要技术之一,其构造目的是找出属性和类别间的关系,用它来预测未知数据的类别。该算法采用自顶向下的递归方式,在决策树的内部节点进行属性比较,并根据不同属性值判断从该节点向下的分支,在决策树的叶子节点得到反馈的结果。

    决策树算法根据数据的属性采用树状结构建立决策模型,常用来解决分类和回归问题。常见的算法包括:分类及回归树、ID3 、C4.5、随机森林等。

    (3) K最近邻分类算法
    K最近邻(K-Nearest Neighbor,简称KNN)分类算法是一种基于实例的分类方法,是数据挖掘分类技术中最简单常用的方法之一。所谓K最近邻,就是寻找K个最近的邻居,每个样本都可以用它最接近的K个邻居来代表。该方法需要找出与未知样本X距离最近的K个训练样本,看这K个样本中属于哪一类的数量多,就把未知样本X归为那一类。

    K-近邻方法是一种懒惰学习方法,它存放样本,直到需要分类时才进行分类,如果样本集比较复杂,可能会导致很大的计算开销,因此无法应用到实时性很强的场合。

    (4) 支持向量机
    支持向量机(Support Vector Machine,简称SVM)是数学家Vapnik等人根据统计学习理论提出的一种新的学习方法,其基本模型定义为特征空间上间隔最大的线性分类器,其学习策略是间隔最大化,最终转换为一个凸二次规划问题的求解。

    SVM算法的最大特点是根据结构风险最小化准则,以最大化分类间隔构造最优分类超平面来提高学习机的泛化能力,较好地解决了非线性、高维数、局部极小点等问题,同时维数大于样本数时仍然有效,支持不同的内核函数(线性、多项式、s型等)。

    (5) 神经网络
    神经网络(Neural Network,也称之为人工神经网络)算法是80年代机器学习界非常流行的算法,不过在90年代中途衰落。现在又随着“深度学习”之势重新火热,成为最强大的机器学习算法之一。图2是一个神经网络的例子,包括输入层、隐藏层和输出层。

    在这里插入图片描述

    人工神经网络(Artificial Neural Network,简称ANN)是一种模仿生物神经网络的结构和功能的数学模型或计算模型。在这种模型中,大量的节点或称“神经元”之间相互联接构成网络,即“神经网络”,以达到处理信息的目的。神经网络通常需要进行训练,训练的过程就是网络进行学习的过程,训练改变了网络节点的连接权的值使其具有分类的功能,经过训练的网络就可用于对象的识别。

    常见的人工神经网络有BP(Back Propagation)神经网络、径向基RBF神经网络、Hopfield神经网络、随机神经网络(Boltzmann机)、深度神经网络DNN、卷积神经网络CNN等。

    (6) 集成学习
    集成学习(Ensemble Learning)是一种机器学习方法,它使用一系列学习器进行学习,并使用某种规则把各个学习结果进行整合从而获得比单个学习器更好的学习效果。由于实际应用的复杂性和数据的多样性往往使得单一的分类方法不够有效,因此,学者们对多种分类方法的融合即集成学习进行了广泛的研究,它已俨然成为了国际机器学习界的研究热点。

    集成学习试图通过连续调用单个的学习算法,获得不同的基学习器,然后根据规则组合这些学习器来解决同一个问题,可以显著的提高学习系统的泛化能力。组合多个基学习器主要采用投票(加权)的方法,常见的算法有装袋(Bagging)、推进(Boosting)等。


    3.回归、聚类和分类的区别

    在第12篇文章中我们详细讲解了回归分析,13篇详细讲解了聚类分析,本章着重讲解分类分析,而它们之间究竟存在什么区别和关系呢?

    • 分类(Classification)和回归(Regression)都属于监督学习,它们的区别在于:回归是用来预测连续的实数值,比如给定了房屋面积,来预测房屋价格,返回的结果是房屋价格;而分类是用来预测有限的离散值,比如判断一个人是否患糖尿病,返回值是“是”或“否”。即明确对象属于哪个预定义的目标类,预定义的目标类是离散时为分类,连续时为回归。
    • 分类属于监督学习,而聚类属于无监督学习,其主要区别是:训练过程中是否知道结果或是否存在类标。比如让小孩给水果分类,给他苹果时告诉他这是苹果,给他桃子时告诉他这是桃子,经过反复训练学习,现在给他一个新的水果,问他“这是什么?”,小孩对其进行回答判断,整个过程就是一个分类学习的过程,在训练小孩的过程中反复告诉他对应水果真实的类别。而如果采用聚类算法对其进行分析,则是给小孩一堆水果,包括苹果、橘子、桃子,小孩开始不知道需要分类的水果是什么,让小孩自己对水果进行分类,按照水果自身的特征进行归纳和判断,小孩分成三堆后,再给小孩新的水果,比如是苹果,小孩把它放到苹果堆的整个过程称之为聚类学习过程。

    总之,分类学习在训练过程中是知道对应的类标结果的,即训练集是存在对应的类标的;而聚类学习在训练过程中不知道数据对应的结果,根据数据集的特征特点,按照“物以类聚”的方法,将具有相似属性的数据聚集在一起。


    4.性能评估

    分类算法有很多,不同的分类算法又有很多不同的变种,不同的分类算法有不同的特点,在不同的数据集上表现的效果也不同,我们需要根据特定的任务来选择对应的算法。选择好了分类算法之后,我们如何评价一个分类算法的好坏呢?

    本书主要采用精确率(Precision)、召回率(Recall)和F值(F-measure或F-score)来评价分类算法。

    (1) 精确率(Precision)和召回率(Recall)
    精确率定义为检索出相关文档数与检索出的文档总数的比率,衡量的是检索系统的查准率;召回率定义为检索出的相关文档数和文档库中所有相关文档数的比率,衡量的是检索系统的查全率。公式如下:

    在这里插入图片描述

    在这里插入图片描述

    其中,参数N表示实验结果中正确识别出的聚类类簇数,S表示实验结果中实际识别出的聚类类簇数,T表示数据集中所有真实存在的聚类相关类簇数。

    (2) F值(F-measure或F-score)
    精确率和召回率两个评估指标在特定的情况下是相互制约的,因而很难使用单一的评价指标来衡量实验的效果。F-值是准确率和召回率的调和平均值,它可作为衡量实验结果的最终评价指标,F值更接近两个数中较小的那个。F值指的计算公式如下公式所示:

    在这里插入图片描述

    (3) 其他指标
    其他常用的分类算法的评价指标包括:

    • 准确率(Accuracy)
    • 错误率(Error Rate)
    • 灵敏度(Sensitive)
    • 特效度(Specificity)
    • ROC曲线

    二.决策树

    1.算法实例描述

    下面通过一个招聘的案例讲述决策树的基本原理及过程。假设一位程序员与面试官的初次面试的简单对话,我们利用决策树分类的思想来构建一棵树形结构。对话如下:

    面试官:多大年纪了?
    程序员:25岁。
    面试官:本科是不是已经毕业呢?
    程序员:是的。
    面试官:编程技术厉不厉害?
    程序员:不算太厉害,中等水平。
    面试官:熟悉Python语言吗?
    程序员:熟悉的,做过数据挖掘相关应用。
    面试官:可以的,你通过了。
    

    这个面试的决策过程就是典型的分类树决策。相当于通过年龄、学历、编程技术和是否熟悉Python语言将程序员初试分为两个类别:通过和不通过。假设这个面试官对程序员的要求是30岁以下、学历本科以上并且是编程厉害或熟悉Pyhon语言中等以上编程技术的程序员,这个面试官的决策逻辑过程用图3表示。

    在这里插入图片描述

    第二个实例是典型的决策树判断苹果的例子,假设存在4个样本,2个属性判断是否是好苹果,其中第二列1表示苹果很红,0表示苹果不红;第三列1表示苹果很大,0表示苹果很小;第4列结果1表示苹果好吃,0表示苹果不好吃。

    在这里插入图片描述

    样本中有2个属性,即苹果红色属性和苹果大小属性。这里红苹果用A0表示,大苹果用A1表示,构建的决策树如图19.4所示。图中最顶端有四个苹果(1、2、3、4),然后它将颜色红的苹果放在一边(A0=红),颜色不红的苹果放在另一边,其结果为1、2是红苹果,3、4是不红的苹果;再根据苹果的大小进行划分,将大的苹果判断为好吃的(A1=大),最终输出结果在图中第三层显示,其中1和3是好吃的苹果,2和4是不好吃的苹果,该实例表明苹果越大越好吃。

    在这里插入图片描述

    决策树算法根据数据的属性并采用树状结构构建决策模型,常用来解决分类和回归问题。常见的决策树算法包括:

    • 分类及回归树(Classification And Regression Tree,简称CART)
    • ID3算法(Iterative Dichotomiser 3)
    • C4.5算法
    • 随机森林算法(Random Forest)
    • 梯度推进机算法(Gradient Boosting Machine,简称GBM)

    决策树构建的基本步骤包括4步,具体步骤如下:

    • 第一步:开始时将所有记录看作一个节点。
    • 第二步:遍历每个变量的每一种分割方式,找到最好的分割点。
    • 第三步:分割成两个节点N1和N2。
    • 第四步:对N1和N2分别继续执行第二步和第三步,直到每个节点足够“纯”为止。

    决策数具有两个优点:

    • 模型可以读性好,描述性强,有助于人工分析。
    • 效率高,决策树只需要一次构建,可以被反复使用,每一次预测的最大计算次数不超过决策树的深度。

    2.DTC算法

    Sklearn机器学习包中,实现决策树(DecisionTreeClassifier,简称DTC)的类是:

    • sklearn.tree.DecisionTreeClassifier

    它能够解决数据集的多类分类问题,输入参数为两个数组X[n_samples,n_features]和y[n_samples],X为训练数据,y为训练数据标记值。DecisionTreeClassifier构造方法为:

    sklearn.tree.DecisionTreeClassifier(criterion='gini'  
                          , splitter='best'  
                          , max_depth=None  
                          , min_samples_split=2  
                          , min_samples_leaf=1  
                          , max_features=None  
                          , random_state=None  
                          , min_density=None  
                          , compute_importances=None  
                          , max_leaf_nodes=None) 
    

    DecisionTreeClassifier类主要包括两个方法:

    • clf.fit(train_data, train_target)
      用来装载(train_data,train_target)训练数据,并训练分类模型。
    • pre = clf.predict(test_data)
      用训练得到的决策树模型对test_data测试集进行预测分析。

    3.决策树分析鸢尾花

    前面第12篇文章介绍过逻辑回归分析鸢尾花的实例,这里再次讲解决策树分析鸢尾花实例,从而加深读者印象。

    (1) 数据集回顾
    在Sklearn机器学习包中,集成了各种各样的数据集,包括糖尿病数据集、鸢尾花数据集、新闻数据集等。这里使用的是鸢尾花卉Iris数据集,它是一个很常用的数据集,共150行数据,包括四个特征变量:

    • 萼片长度
    • 萼片宽度
    • 花瓣长度
    • 花瓣宽度。

    同时包括一个类别变量,将鸢尾花划分为三个类别,即:

    • 山鸢尾(Iris-setosa)
    • 变色鸢尾(Iris-versicolor)
    • 维吉尼亚鸢尾(Iris-virginica)

    表2为鸢尾花数据集,详细信息如下表所示。

    在这里插入图片描述

    iris是鸢尾植物,这里存储了其萼片和花瓣的长宽,共4个属性,鸢尾植物分三类。 iris数据集中包括两个属性iris.data和iris.target。其中,data数据是一个矩阵,每一列代表了萼片或花瓣的长宽,一共4列,每一行数据代表某个被测量的鸢尾植物,一共采样了150条记录。载入鸢尾花数据集代码如下所示:

    from sklearn.datasets import load_iris 
    iris = load_iris()
    print(iris.data)
    print(iris.target)
    

    在这里插入图片描述


    (2) 决策树简单分析鸢尾花
    下述代码实现了调用Sklearn机器学习包中DecisionTreeClassifier决策树算法进行分类分析,并绘制预测的散点图。

    # -*- coding: utf-8 -*-
    # By:Eastmount CSDN 2021-07-06
    
    #导入数据集iris
    from sklearn.datasets import load_iris 
    iris = load_iris()
    print(iris.data)           #输出数据集
    print(iris.target)         #输出真实标签
    print(len(iris.target))
    print(iris.data.shape)     #150个样本 每个样本4个特征
    
    #导入决策树DTC包
    from sklearn.tree import DecisionTreeClassifier
    clf = DecisionTreeClassifier()
    clf.fit(iris.data, iris.target)        #训练
    print(clf)
    predicted = clf.predict(iris.data)     #预测
    
    #获取花卉两列数据集
    X = iris.data
    L1 = [x[0] for x in X]
    L2 = [x[1] for x in X]
    
    #绘图
    import numpy as np
    import matplotlib.pyplot as plt
    plt.scatter(L1, L2, c=predicted, marker='x')  #cmap=plt.cm.Paired
    plt.title("DTC")
    plt.show()
    

    输出结果如图5所示,可以看到决策树算法将数据集预测为三类,分别代表着数据集对应的三种鸢尾花,但数据集中存在小部分交叉结果。预测的结果如下:

    [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
     1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
     2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
     2 2]
    150
    (150, 4)
    
    DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,
                           max_features=None, max_leaf_nodes=None,
                           min_impurity_decrease=0.0, min_impurity_split=None,
                           min_samples_leaf=1, min_samples_split=2,
                           min_weight_fraction_leaf=0.0, presort=False,
                           random_state=None, splitter='best')
    

    在这里插入图片描述

    下面对上述核心代码进行简单描述。

    • from sklearn.datasets import load_iris
    • iris = load_iris()

    该部分代码是导入sklearn机器学习包自带的鸢尾花数据集,调用load_iris()函数导入数据,数据共分为数据(data)和类标(target)两部分。

    • from sklearn.tree import DecisionTreeClassifier
    • clf = DecisionTreeClassifier()
    • clf.fit(iris.data, iris.target)
    • predicted = clf.predict(iris.data)

    该部分代码导入决策树模型,并调用fit()函数进行训练,predict()函数进行预测。

    • import matplotlib.pyplot as plt
    • plt.scatter(L1, L2, c=predicted, marker=‘x’)

    该部分代码是导入matplotlib绘图扩展包,调用scatter()函数绘制散点图。

    但上面的代码中存在两个问题:

    • 代码中通过“L1 = [x[0] for x in X]”获取了第一列和第二列数据集进行了分类分析和绘图,而真实的iris数据集中包括四个特征,那怎么绘制四个特征的图形呢? 这就需要利用PCA降维技术处理,参考前一篇文章。
    • 第二个问题是在聚类、回归、分类模型中,都需要先进行训练,再对新的数据集进行预测,这里却是对整个数据集进行分类分析,而真实情况是需要把数据集划分为训练集和测试集的,例如数据集的70%用于训练、30%用于预测,或80%用于训练、20%用于预测。

    4.数据集划分及分类评估

    这部分内容主要是进行代码优化,将数据集划分为80%训练集-20%预测集,并对决策树分类算法进行评估。由于提供的数据集类标是存在一定规律的,前50个类标为0(山鸢尾)、中间50个类标为1(变色鸢尾)、最后50个类标为2(维吉尼亚鸢)。即:

    [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
     0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
     1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
     2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
     2 2]
    

    这里调用NumPy库中的 concatenate() 函数对数据集进行挑选集成,选择第0-40行、第50-90行、第100-140行数据作为训练集,对应的类标作为训练样本类标;再选择第40-50行、第90-100行、第140-150行数据作为测试集合,对应的样本类标作为预测类标。

    代码如下,“axis=0”表示选取数值的等差间隔为0,即紧挨着获取数值。

    #训练集
    train_data = np.concatenate((iris.data[0:40, :], iris.data[50:90, :], iris.data[100:140, :]), axis = 0)
    #训练集样本类别
    train_target = np.concatenate((iris.target[0:40], iris.target[50:90], iris.target[100:140]), axis = 0)
    #测试集
    test_data = np.concatenate((iris.data[40:50, :], iris.data[90:100, :], iris.data[140:150, :]), axis = 0)
    #测试集样本类别
    test_target = np.concatenate((iris.target[40:50], iris.target[90:100], iris.target[140:150]), axis = 0)
    

    同时,调用sklearn机器学习包中metrics类对决策树分类算法进行评估,它将输出准确率(Precison)、召回率(Recall)、F特征值(F-score)、支持度(Support)等。

    #输出准确率 召回率 F值  
    from sklearn import metrics  
    print(metrics.classification_report(test_target, predict_target))  
    print(metrics.confusion_matrix(test_target, predict_target)) 
    

    分类报告的核心函数为:

    sklearn.metrics.classification_report(y_true, 
                                  y_pred, 
                                  labels=None,
                                  target_names=None,
                                  sample_weight=None, 
                                  digits=2)
    

    其中y_true参数表示正确的分类类标,y_pred表示分类预测的类标,labels表示分类报告中显示的类标签的索引列表,target_names参数显示与labels对应的名称,digits是指定输出格式的精确度。评价公式如下:

    在这里插入图片描述

    调用 metrics.classification_report() 方法对决策树算法进行评估后,会在最后一行将对所有指标进行加权平均值,详见下面完整代码。

    # -*- coding: utf-8 -*-
    # By:Eastmount CSDN 2021-07-06
    from sklearn.datasets import load_iris
    from sklearn.tree import DecisionTreeClassifier
    from sklearn import metrics
    import numpy as np
    import matplotlib.pyplot as plt
    
    #导入数据集iris
    '''
    重点:分割数据集 构造训练集/测试集,80/20
         70%训练  0-40  50-90  100-140
         30%预测  40-50 90-100 140-150
    '''
    iris = load_iris()
    train_data = np.concatenate((iris.data[0:40, :], iris.data[50:90, :], iris.data[100:140, :]), axis = 0)  #训练集
    train_target = np.concatenate((iris.target[0:40], iris.target[50:90], iris.target[100:140]), axis = 0)  #训练集样本类别
    test_data = np.concatenate((iris.data[40:50, :], iris.data[90:100, :], iris.data[140:150, :]), axis = 0)  #测试集
    test_target = np.concatenate((iris.target[40:50], iris.target[90:100], iris.target[140:150]), axis = 0)  #测试集样本类别
    
    #导入决策树DTC包
    clf = DecisionTreeClassifier()
    clf.fit(train_data, train_target)        #注意均使用训练数据集和样本类标
    print(clf)
    predict_target = clf.predict(test_data)  #测试集
    print(predict_target)
    
    #预测结果与真实结果比对
    print(sum(predict_target == test_target))
    
    #输出准确率 召回率 F值
    print(metrics.classification_report(test_target, predict_target))
    print(metrics.confusion_matrix(test_target, predict_target))
    
    #获取花卉测试数据集两列数据
    X = test_data
    L1 = [n[0] for n in X]
    L2 = [n[1] for n in X]
    
    #绘图
    plt.scatter(L1, L2, c=predict_target, marker='x')  #cmap=plt.cm.Paired
    plt.title("DecisionTreeClassifier")
    plt.show()
    

    输出结果如下,包括对数据集40-50、90-100、140-150的预测结果,接下来输出的“30”表示整个30组类标预测结果和真实结果是一致的,最后输出评估结果。

    [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2]
    30
                 precision    recall  f1-score   support
              0       1.00      1.00      1.00        10
              1       1.00      1.00      1.00        10
              2       1.00      1.00      1.00        10
    
    avg / total       1.00      1.00      1.00        30
    
    [[10  0  0]
     [ 0 10  0]
     [ 0  0 10]]
    

    同时输出图形如图6所示。

    在这里插入图片描述

    读者可以自行深入研究,调用sklearn.tree.export_graphviz类实现导出决策树绘制树形结构的过程,比如鸢尾花数据集输出如图7所示的树形结构。

    在这里插入图片描述


    5.区域划分对比

    下面讲述区域划分对比实验(前面已经出现过),它是指按照数据集真实的类标,将其划分为不同颜色区域,这里的鸢尾花数据集共分为三个区域,最后进行散点图绘制对比。每个区域对应一类散点,表示预测结果和真实结果一致,如果某个区域混入其他类型的散点,则表示该点的预测结果与真实结果不一致。

    完整代码如下所示,代码首先调用“iris.data[:, :2]”代码获取其中两列数据(两个特征),再进行决策树分类分析。

    # -*- coding: utf-8 -*-
    # By:Eastmount CSDN 2021-07-06
    import matplotlib.pyplot as plt
    import numpy as np
    from sklearn.datasets import load_iris   
    from sklearn.tree import DecisionTreeClassifier 
    
    #载入鸢尾花数据集
    iris = load_iris()         
    X = X = iris.data[:, :2]   #获取花卉前两列数据
    Y = iris.target           
    lr = DecisionTreeClassifier()  
    lr.fit(X,Y)
    
    #meshgrid函数生成两个网格矩阵
    h = .02
    x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    
    #pcolormesh函数将xx,yy两个网格矩阵和对应的预测结果Z绘制在图片上
    Z = lr.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    plt.figure(1, figsize=(8,6))
    plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired)
    
    #绘制散点图
    plt.scatter(X[:50,0], X[:50,1], color='red',marker='o', label='setosa')
    plt.scatter(X[50:100,0], X[50:100,1], color='blue', marker='x', label='versicolor')
    plt.scatter(X[100:,0], X[100:,1], color='green', marker='s', label='Virginica') 
    plt.xlabel('Sepal length')
    plt.ylabel('Sepal width')
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())
    plt.xticks(())
    plt.yticks(())
    plt.legend(loc=2) 
    plt.show()
    

    下面作者对区域划分对比代码进行详细讲解。

    • x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    • y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    • xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

    获取的鸢尾花两列数据,对应为萼片长度和萼片宽度,每个点的坐标就是(x,y)。先取X二维数组的第一列(长度)的最小值、最大值和步长h(设置为0.02)生成数组,再取X二维数组的第二列(宽度)的最小值、最大值和步长h生成数组,最后用meshgrid()函数生成两个网格矩阵xx和yy,如下所示:

    [[ 3.8   3.82  3.84 ...,  8.36  8.38  8.4 ]
     [ 3.8   3.82  3.84 ...,  8.36  8.38  8.4 ]
     ..., 
     [ 3.8   3.82  3.84 ...,  8.36  8.38  8.4 ]
     [ 3.8   3.82  3.84 ...,  8.36  8.38  8.4 ]]
    [[ 1.5   1.5   1.5  ...,  1.5   1.5   1.5 ]
     [ 1.52  1.52  1.52 ...,  1.52  1.52  1.52]
     ..., 
     [ 4.88  4.88  4.88 ...,  4.88  4.88  4.88]
     [ 4.9   4.9   4.9  ...,  4.9   4.9   4.9 ]]
    
    • Z = lr.predict(np.c_[xx.ravel(), yy.ravel()])

    调用ravel()函数将xx和yy的两个矩阵转变成一维数组,再进行预测分析。由于两个矩阵大小相等,因此两个一维数组大小也相等。np.c_[xx.ravel(), yy.ravel()]是生成矩阵,即:

    xx.ravel() 
    [ 3.8   3.82  3.84 ...,  8.36  8.38  8.4 ]
    yy.ravel() 
    [ 1.5  1.5  1.5 ...,  4.9  4.9  4.9]
    np.c_[xx.ravel(), yy.ravel()]
    [[ 3.8   1.5 ]
     [ 3.82  1.5 ]
     [ 3.84  1.5 ]
     ..., 
     [ 8.36  4.9 ]
     [ 8.38  4.9 ]
     [ 8.4   4.9 ]]
    

    总之,上述操作是把第一列萼片长度数据按h取等分作为行,并复制多行得到xx网格矩阵;再把第二列萼片宽度数据按h取等分,作为列,并复制多列得到yy网格矩阵;最后将xx和yy矩阵都变成两个一维数组,调用np.c_[]函数组合成一个二维数组进行预测。

    调用predict()函数进行预测,预测结果赋值给Z。即:

    Z = logreg.predict(np.c_[xx.ravel(), yy.ravel()])
    [1 1 1 ..., 2 2 2]
    size: 39501
    
    • Z = Z.reshape(xx.shape)
      调用reshape()函数修改形状,将其Z转换为两个特征(长度和宽度),则39501个数据转换为171*231的矩阵。Z = Z.reshape(xx.shape)输出如下:
    [[1 1 1 ..., 2 2 2]
     [1 1 1 ..., 2 2 2]
     [0 1 1 ..., 2 2 2]
     ..., 
     [0 0 0 ..., 2 2 2]
     [0 0 0 ..., 2 2 2]
     [0 0 0 ..., 2 2 2]]
    
    • plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired)

    调用pcolormesh()函数将xx、yy两个网格矩阵和对应的预测结果Z绘制在图片上,可以发现输出为三个颜色区块,分别表示三类区域。cmap=plt.cm.Paired表示绘图样式选择Paired主题。输出的区域如下图所示:

    在这里插入图片描述

    • plt.scatter(X[:50,0], X[:50,1], color=‘red’,marker=‘o’, label=‘setosa’)

    调用scatter()绘制散点图,第一个参数为第一列数据(长度),第二个参数为第二列数据(宽度),第三、四个参数为设置点的颜色为红色,款式为圆圈,最后标记为setosa。

    在这里插入图片描述

    最终输出如图9所示,经过决策树分析后划分为三个区域,左上角部分为红色的圆点,对应setosa鸢尾花;右边部分为绿色方块,对应virginica鸢尾花;中间靠下部分为蓝色星形,对应versicolor鸢尾花。散点图为各数据点真实的花类型,划分的三个区域为数据点预测的花类型,预测的分类结果与训练数据的真实结果结果基本一致,部分鸢尾花出现交叉。


    三.KNN分类算法

    1.算法实例描述

    KNN分类算法是最近邻算法,字面意思就是寻找最近邻居,由Cover和Hart在1968年提出,它简单直观易于实现。下面通过一个经典例子来讲解如何寻找邻居,选取多少个邻居。图10需要判断右边这个动物是鸭子、鸡还是鹅?这就涉及到了KNN算法的核心思想,判断与这个样本点相似的类别,再预测其所属类别。由于它走路和叫声像一只鸭子,所以右边的动物很可能是一只鸭子。

    在这里插入图片描述

    KNN分类算法的核心思想是从训练样本中寻找所有训练样本X中与测试样本距离(常用欧氏距离)最近的前K个样本(作为相似度),再选择与待分类样本距离最小的K个样本作为X的K个最邻近,并检测这K个样本大部分属于哪一类样本,则认为这个测试样本类别属于这一类样本。

    KNN分类的算法步骤如下:

    • 计算测试样本点到所有样本点的欧式距离dist,采用勾股定理计算
    • 用户自定义设置参数K,并选择离带测点最近的K个点
    • 从这K个点中,统计各个类型或类标的个数
    • 选择出现频率最大的类标号作为未知样本的类标号,反馈最终预测结果

    假设现在需要判断图11中的圆形图案属于三角形还是正方形类别,采用KNN算法分析步骤如下:

    • 当K=3时,图中第一个圈包含了三个图形,其中三角形2个,正方形一个,该圆的则分类结果为三角形。
    • 当K=5时,第二个圈中包含了5个图形,三角形2个,正方形3个,则以3:2的投票结果预测圆为正方形类标。
    • 同理,当K=11原理也是一样,设置不同的K值,可能预测得到结果也不同。所以,KNN是一个非常简单、易于理解实现的分类算法。

    在这里插入图片描述

    最后简单讲述KNN算法的优缺点。KNN分类算法存在的优点包括:

    • 算法思路较为简单,易于实现。
    • 当有新样本要加入训练集中时,无需重新训练,即重新训练的代价低。
    • 计算时间和空间线性于训练集的规模。

    其缺点主要表现为分类速度慢,由于每次新的待分样本都必须与所有训练集一同计算比较相似度,以便取出靠前的K个已分类样本,所以时间复杂度较高。整个算法的时间复杂度可以用O(m*n)表示,其中m是选出的特征项的个数,而n是训练集样本的个数。同时,如果K值确定不好,也会影响整个实验的结果,这也是KNN算法的另一个缺点。


    2.KNeighborsClassifier

    Sklearn机器学习包中,实现KNN分类算法的类是neighbors.KNeighborsClassifier。构造方法如下:

    KNeighborsClassifier(algorithm='ball_tree', 
    	leaf_size=30, 
    	metric='minkowski',
    	metric_params=None, 
    	n_jobs=1, 
    	n_neighbors=3, 
    	p=2, 
    	weights='uniform')
    

    其中最重要的参数是n_neighbors=3,设置最近邻K值。同时,KNeighborsClassifier可以设置3种算法:brute、kd_tree、ball_tree。具体调用方法如下:

    from sklearn.neighbors import KNeighborsClassifier  
    knn = KNeighborsClassifier(n_neighbors=3, algorithm="ball_tree")
    

    KNN算法分析时也包括训练和预测两个方法。

    • 训练knn.fit(data, target)
    • 预测pre = knn.predict(data)

    下面这段代码是简单调用KNN分类算法进行预测的例子,代码如下。

    # -*- coding: utf-8 -*-
    # By:Eastmount CSDN 2021-07-06
    import numpy as np  
    from sklearn.neighbors import KNeighborsClassifier  
    
    X = np.array([[-1,-1],[-2,-2],[1,2], [1,1],[-3,-4],[3,2]])
    Y = [0,0,1,1,0,1]
    x = [[4,5],[-4,-3],[2,6]]
    knn = KNeighborsClassifier(n_neighbors=3, algorithm="ball_tree")
    knn.fit(X,Y)
    pre = knn.predict(x)
    print(pre)
    

    定义了一个二维数组用于存储6个点,其中x和y坐标为负数的类标定义为0,x和y坐标为正数的类标定义为1。调用knn.fit(X,Y)函数训练模型后,再调用predict()函数预测[4,5]、[-4,-3]、[2,6]三个点的坐标,输出结果分别为:[1, 0, 1],其中x和y坐标为正数的划分为一类,负数的一类。

    同时也可以计算K个最近点的下标和距离,代码和结果如下,其中,indices表示点的下标,distances表示距离。

    distances, indices = knn.kneighbors(X)  
    print(indices)
    print(distances)
    
    >>> 
    [1 0 1]
    [[0 1 3]
     [1 0 4]
     [2 3 5]
     [3 2 5]
     [4 1 0]
     [5 2 3]]
    [[ 0.          1.41421356  2.82842712]
     [ 0.          1.41421356  2.23606798]
     [ 0.          1.          2.        ]
     [ 0.          1.          2.23606798]
     [ 0.          2.23606798  3.60555128]
     [ 0.          2.          2.23606798]]
    >>> 
    

    下面通过一个完整的实例结合可视化技术进行讲解,加深读者的印象。


    3.KNN分析红酒类型

    (1) 数据集
    该实验数据集是UCI Machine Learning Repository开源网站提供的MostPopular Data Sets(hits since 2007)红酒数据集,它是对意大利同一地区生产的三种不同品种的酒,做大量分析所得出的数据。这些数据包括了三种类别的酒,酒中共13种不同成分的特征,共178行数据,如图13所示。

    在这里插入图片描述

    该数据集包括了三种类型酒中13种不同成分的数量,13种成分分别是:Alcohol、Malicacid、Ash、Alcalinity of ash、Magnesium、Total phenols、Flavanoids、Nonflavanoid phenols、Proanthocyanins、Color intensity、Hue、OD280/OD315 of diluted wines和Proline,每一种成分可以看成一个特征,对应一个数据。三种类型的酒分别标记为“1”、“2”、“3”。数据集特征描述如表3所示。

    在这里插入图片描述

    数据存储在wine.txt文件中,如图14所示。每行数据代表一个样本,共178行数据,每行数据包含14列,即第一列为类标属性,后面依次是13列特征。其中第1类有59个样本,第2类有71个样本,第3类有48个样本。

    在这里插入图片描述

    注意:前面讲述了如何读取CSV文件数据集或Sklearn扩展包所提供的数据集,但现实分析中,很多数据集会存储于TXT或DATA文件中,它们采用一定的符号进行分隔,比如图中采用逗号分隔,如何获取这类文件中的数据,也是非常重要的知识。所以接下来先教大家读取这类文件的数据。


    (2) 读取数据集
    从图14可以看到整个数据集采用逗号分隔,常用读取该类型数据集的方法是调用open()函数读取文件,依次读取TXT文件中所有内容,再按照逗号分割符获取每行的14列数据存储至数组或矩阵中,从而进行数据分析。这里讲述另一种方法,调用loadtxt()函数读取逗号分隔的数据,代码如下:

    # -*- coding: utf-8 -*-  
    import os 
    import numpy as np
    path = "wine/wine.txt"
    data = np.loadtxt(path,dtype=float,delimiter=",")
    print(data)
    

    输出如下所示:

    在这里插入图片描述

    loadtxt()读入文件函数原型如下:

    • loadtxt(fname, dtype, delimiter, converters, usecols)

    其中参数fname表示文件路径,dtype表示数据类型,delimiter表示分隔符,converters将数据列与转换函数进行映射的字段,如{1:fun},usecols表示选取数据的列。


    (3) 数据集拆分训练集和预测集
    由于Wine数据集前59个样本全是第1类,中间71个样本为第2类,最后48个样本是第3类,所以需要将数据集拆分成训练集和预测集。步骤如下:

    • 调用split()函数将数据集的第一列类标(Y数据)和13列特征(X数组)分隔开来。该函数参数包括data数据,分割位置,其中1表示从第一列分割,axis为1表示水平分割、0表示垂直分割。
    • 由于数据集第一列存储的类标为1.0、2.0或3.0浮点型数据,需要将其转换为整型,这里在for循环中调用int()函数转换,存储至y数组中,也可采用np.astype()实现。
    • 最后调用np.concatenate()函数将0-40、60-100、140-160行数据分割为训练集,包括13列特征和类标,其余78行数据为测试集。

    代码如下:

    # -*- coding: utf-8 -*-  
    import os 
    import numpy as np
    path = "wine/wine.txt"
    data = np.loadtxt(path,dtype=float,delimiter=",")
    print(data)
    
    yy, x = np.split(data, (1,), axis=1)
    print(yy.shape, x.shape)
    y = []
    for n in yy:
        y.append(int(n))
    
    train_data = np.concatenate((x[0:40,:], x[60:100,:], x[140:160,:]), axis = 0)  #训练集
    train_target = np.concatenate((y[0:40], y[60:100], y[140:160]), axis = 0)      #样本类别
    test_data = np.concatenate((x[40:60, :], x[100:140, :], x[160:,:]), axis = 0)  #测试集
    test_target = np.concatenate((y[40:60], y[100:140], y[160:]), axis = 0)        #样本类别
    
    print(train_data.shape, train_target.shape)
    print(test_data.shape, test_target.shape)
    

    输出结果如下:

    (178L, 1L)
    (178L, 13L)
    (100L, 1L) (100L, 13L)
    (78L, 1L) (78L, 13L)
    

    下面补充一种随机拆分的方式,调用 sklearn.model_selection.train_test_split 类随机划分训练集与测试集。代码如下:

    from sklearn.model_selection import train_test_split
    x, y = np.split(data, (1,), axis=1)
    x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1, train_size=0.7)
    
    #Python2调用方法
    #from sklearn.cross_validation import train_test_split
    

    参数x表示所要划分的样本特征集;y是所要划分的样本结果;train_size表示训练样本占比,0.7表示将数据集划分为70%的训练集、30%的测试集;random_state是随机数的种子。该函数在部分版本的sklearn库中是导入model_selection类,建议读者下来尝试。


    (4) KNN分类算法分析
    上面已经将178个样本分成100个训练样本和78个测试样本,采用KNN分类算法训练模型,再对测试集进行预测,判别出测试样本所属于酒的类型,同时输出测试样本计算的正确率和错误率。KNN核心代码如下:

    from sklearn.neighbors import KNeighborsClassifier  
    clf = KNeighborsClassifier(n_neighbors=3,algorithm='kd_tree')
    clf.fit(train_data,train_target)
    result = clf.predict(test_data)
    print(result)
    

    预测输出结果如下所示:

    [1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 3 2 2 2 2 2 2 2 2 3 2 3 2 2 2 2
     2 2 3 3 2 2 2 2 2 2 2 2 3 2 3 3 3 3 2 1 2 3 3 2 2 3 2 3 2 2 2 1 2 2 2 3 1
     1 1 1 3]
    

    (5) 完整代码
    下面代码实现了调用Sklearn机器学习包中KNeighborsClassifier算法进行分类分析,并绘制预测的散点图和背景图,完整代码如下。

    # -*- coding: utf-8 -*-  
    # By:Eastmount CSDN 2021-07-06
    import os 
    import numpy as np
    from sklearn.neighbors import KNeighborsClassifier  
    from sklearn import metrics
    from sklearn.decomposition import PCA 
    import matplotlib.pyplot as plt
    from matplotlib.colors import ListedColormap
    
    #----------------------------------------------------------------------------
    #第一步 加载数据集
    path = "wine/wine.txt"
    data = np.loadtxt(path,dtype=float,delimiter=",")
    print(data)
    
    #----------------------------------------------------------------------------
    #第二步 划分数据集
    yy, x = np.split(data, (1,), axis=1) #第一列为类标yy,后面13列特征为x
    print(yy.shape, x.shape)
    y = []
    for n in yy:  #将类标浮点型转化为整数
        y.append(int(n))
    x = x[:, :2]  #获取x前两列数据,方便绘图 对应x、y轴
    train_data = np.concatenate((x[0:40,:], x[60:100,:], x[140:160,:]), axis = 0)  #训练集
    train_target = np.concatenate((y[0:40], y[60:100], y[140:160]), axis = 0)      #样本类别
    test_data = np.concatenate((x[40:60, :], x[100:140, :], x[160:,:]), axis = 0)  #测试集
    test_target = np.concatenate((y[40:60], y[100:140], y[160:]), axis = 0)        #样本类别
    print(train_data.shape, train_target.shape)
    print(test_data.shape, test_target.shape)
    
    #----------------------------------------------------------------------------
    #第三步 KNN训练
    clf = KNeighborsClassifier(n_neighbors=3,algorithm='kd_tree') #K=3
    clf.fit(train_data,train_target)
    result = clf.predict(test_data)
    print(result)
    
    #----------------------------------------------------------------------------
    #第四步 评价算法 
    print(sum(result==test_target))                             #预测结果与真实结果比对
    print(metrics.classification_report(test_target, result))   #准确率 召回率 F值
    
    #----------------------------------------------------------------------------
    #第五步 创建网格
    x1_min, x1_max = test_data[:,0].min()-0.1, test_data[:,0].max()+0.1    #第一列
    x2_min, x2_max = test_data[:,1].min()-0.1, test_data[:,1].max()+0.1    #第二列
    xx, yy = np.meshgrid(np.arange(x1_min, x1_max, 0.1),  
                         np.arange(x2_min, x2_max, 0.1))                   #生成网格型数据
    print(xx.shape, yy.shape)                                               #(53L, 36L) (53L, 36L)
    
    z = clf.predict(np.c_[xx.ravel(), yy.ravel()])                         #ravel()拉直函数
    print(xx.ravel().shape, yy.ravel().shape)                              #(1908L,) (1908L,)
    print(np.c_[xx.ravel(), yy.ravel()].shape)                             #合并 (1908L,2)
    
    #----------------------------------------------------------------------------
    #第六步 绘图可视化
    cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])         #颜色Map
    cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
    plt.figure()
    z = z.reshape(xx.shape)
    print(xx.shape, yy.shape, z.shape, test_target.shape)                 
    #(53L, 36L) (53L, 36L) (53L, 36L)  (78L,)
    plt.pcolormesh(xx, yy, z, cmap=cmap_light)
    plt.scatter(test_data[:,0], test_data[:,1], c=test_target,
                cmap=cmap_bold, s=50)
    plt.show()
    

    输出结果如下所示,包括预测的78行类标,共预测正确58行数据,准确率为0.76,召回率为0.74,f特征为0.74。其结果不太理想,需要进一步优化算法。

    [1 3 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 3 2 2 3 2 2 2 2 2 3 2 2 2 2 2
     2 1 2 2 2 3 3 3 2 2 2 2 3 2 3 1 1 2 3 3 3 3 3 1 3 3 3 3 3 3 3 1 3 2 1 1 3
     3 3 1 3]
    58
                 precision    recall  f1-score   support
    
              1       0.68      0.89      0.77        19
              2       0.88      0.74      0.81        31
              3       0.67      0.64      0.65        28
    
    avg / total       0.76      0.74      0.74        78
    

    在这里插入图片描述

    输出图形如图15所示,可以看到整个区域划分为三种颜色,左下角为绿色区域,右下角为红色区域,右上部分为蓝色区域。同时包括78个点分布,对应78行数据的类标,包括绿色、蓝色和红色的点。可以发现,相同颜色的点主要集中于该颜色区域,部分蓝色点划分至红色区域或绿色点划分至蓝色区域,则表示预测结果与实际结果不一致。

    在这里插入图片描述

    最后简单总结,整个分析过程包括六个步骤,大致内容如下:

    • 1) 加载数据集
      采用loadtxt()函数加载酒类数据集,采用逗号(,)分割。
    • 2) 划分数据集
      由于Wine数据集第一列为类标,后面13列为13个酒类特征,获取其中两列特征,并将其划分成特征数组和类标数组,调用concatenate()函数实现。
    • 3) KNN训练
      调用Sklearn机器学习包中KNeighborsClassifier()函数训练,设置K值为3类,并调用clf.fit(train_data,train_target)训练模型,clf.predict(test_data)预测分类结果。
    • 4) 评价算法
      通过classification_report()函数计算该分类预测结果的准确率、召回率和F值。
    • 5) 创建网格
      由于绘图中,拟将预测的类标划分为三个颜色区域,真实的分类结果以散点图形式呈现,故需要获取数据集中两列特征的最大值和最小值,并创建对应的矩阵网格,调用numpy扩展包的meshgrid()函数实现,在对其颜色进行预测。
    • 6) 绘图可视化
      设置不同类标的颜色,调用pcolormesh()函数绘制背景区域颜色,调用scatter()函数绘制实际结果的散点图,形成如图15的效果图。

    四.SVM分类算法

    支持向量机(Support Vector Machine,简称SVM)是常见的一种判别方法。在机器学习领域,是一个有监督的学习模型,通常用来进行模式识别、分类以及回归分析。该算法的最大特点是根据结构风险最小化准则,以最大化分类间隔构造最优分类超平面来提高学习机的泛化能力,较好地解决了非线性、高维数、局部极小点等问题。

    1.SVM基础知识

    (1) 基础概念
    由于作者数学推算能力不太好,同时SVM原理也比较复杂,所以SVM算法基础知识推荐大家阅读CSDN博客著名算法大神“JULY”的文章《支持向量机通俗导论(理解SVM的三层境界)》,这篇文章由浅入深的讲解了SVM算法,而本小节作者主要讲解SVM的用法。

    SVM分类算法的核心思想是通过建立某种核函数,将数据在高维寻找一个满足分类要求的超平面,使训练集中的点距离分类面尽可能的远,即寻找一个分类面使得其两侧的空白区域最大。如图19.16所示,两类样本中离分类面最近的点且平行于最优分类面的超平面上的训练样本就叫做支持向量。

    在这里插入图片描述

    (2) SVM导入方法
    SVM分类算法在Sklearn机器学习包中,实现的类是 svm.SVC,即C-Support Vector Classification,它是基于libsvm实现的。构造方法如下:

    SVC(C=1.0, 
    	cache_size=200, 
    	class_weight=None, 
    	coef0=0.0,
    	decision_function_shape=None, 
    	degree=3, 
    	gamma='auto', 
    	kernel='rbf',
    	max_iter=-1, 
    	probability=False, 
    	random_state=None, 
    	shrinking=True,
    	tol=0.001, 
    	verbose=False)
    

    其中参数含义如下:

    • C表示目标函数的惩罚系数,用来平衡分类间隔margin和错分样本的,默认值为1.0;
    • cache_size是制定训练所需要的内存(以MB为单位);
    • gamma是核函数的系数,默认是gamma=1/n_features;
    • kernel可以选择RBF、Linear、Poly、Sigmoid,默认的是RBF;
    • degree决定了多项式的最高次幂;
    • max_iter表示最大迭代次数,默认值为1;
    • coef0是核函数中的独立项;
    • class_weight表示每个类所占据的权重,不同的类设置不同的惩罚参数C,缺省为自适应;
    • decision_function_shape包括ovo(一对一)、ovr(多对多)或None(默认值)。

    SVC算法主要包括两个步骤:

    • 训练nbrs.fit(data, target)
    • 预测pre = clf.predict(data)

    下面这段代码是简单调用SVC分类算法进行预测的例子,数据集中x和y坐标为负数的类标为1,x和y坐标为正数的类标为2,同时预测点[-0.8,-1]的类标为1,点[2,1]的类标为2。

    import numpy as np
    from sklearn.svm import SVC
    
    X = np.array([[-1, -1], [-2, -2], [1, 3], [4, 6]])  
    y = np.array([1, 1, 2, 2])
    clf = SVC()  
    clf.fit(X, y)   
    print(clf)
    print(clf.predict([[-0.8,-1], [2,1]]))
    
    #输出结果:[1, 2]
    

    支持向量机分类器还有其他的方法,比如NuSVC核支持向量分类,LinearSVC线性向量支持分类等,这里不再介绍。同时,支持向量机也已经推广到解决回归问题,称为支持向量回归,比如SVR做线性回归。


    2.SVM分析红酒数据

    接着采用SVM分类算法对酒类数据集Wine进行分析,并对比前面19.3小节的实例代码,校验SVM分类算法和KNN分类算法的分析结果和可视化分析的优劣。其分析步骤基本一致,主要包括如下六个步骤:

    • 第一步,加载数据集。采用loadtxt()函数加载酒类数据集,采用逗号(,)分割。
    • 第二步,划分数据集。将Wine数据集划分为训练集和预测集,仅提取酒类13个特种中的两列特征进行数据分析。
    • 第三步,SVM训练。导入Sklearn机器学习包中svm.SVC()函数分析,调用fit()函数训练模型,predict(test_data)函数预测分类结果。
    • 第四步,评价算法。通过classification_report()函数计算该分类预测结果的准确率、召回率和F值。
    • 第五步,创建网格。获取数据集中两列特征的最大值和最小值,并创建对应的矩阵网格,用于绘制背景图,调用numpy扩展包的meshgrid()函数实现。
    • 第六步,绘图可视化。设置不同类标的颜色,调用pcolormesh()函数绘制背景区域颜色,调用scatter()函数绘制实际结果的散点图。

    完整代码如下所示:

    # -*- coding: utf-8 -*-
    # By:Eastmount CSDN 2021-07-06
    import os 
    import numpy as np
    from sklearn.svm import SVC  
    from sklearn import metrics
    import matplotlib.pyplot as plt
    from matplotlib.colors import ListedColormap
    
    #----------------------------------------------------------------------------
    #第一步 加载数据集
    path = "wine/wine.txt"
    data = np.loadtxt(path,dtype=float,delimiter=",")
    print(data)
    
    #----------------------------------------------------------------------------
    #第二步 划分数据集
    yy, x = np.split(data, (1,), axis=1) #第一列为类标yy,后面13列特征为x
    print(yy.shape, x.shape)
    y = []
    for n in yy:  #将类标浮点型转化为整数
        y.append(int(n))
    x = x[:, :2]  #获取x前两列数据,方便绘图 对应x、y轴
    train_data = np.concatenate((x[0:40,:], x[60:100,:], x[140:160,:]), axis = 0) #训练集
    train_target = np.concatenate((y[0:40], y[60:100], y[140:160]), axis = 0)     #样本类别
    test_data = np.concatenate((x[40:60, :], x[100:140, :], x[160:,:]), axis = 0) #测试集
    test_target = np.concatenate((y[40:60], y[100:140], y[160:]), axis = 0)       #样本类别
    print(train_data.shape, train_target.shape)
    print(test_data.shape, test_target.shape)
    
    #----------------------------------------------------------------------------
    #第三步 SVC训练
    clf = SVC()
    clf.fit(train_data,train_target)
    result = clf.predict(test_data)
    print(result)
    
    #----------------------------------------------------------------------------
    #第四步 评价算法 
    print(sum(result==test_target))                            #预测结果与真实结果比对
    print(metrics.classification_report(test_target, result))  #准确率 召回率 F值
    
    #----------------------------------------------------------------------------
    #第五步 创建网格 
    x1_min, x1_max = test_data[:,0].min()-0.1, test_data[:,0].max()+0.1    #第一列
    x2_min, x2_max = test_data[:,1].min()-0.1, test_data[:,1].max()+0.1    #第二列
    xx, yy = np.meshgrid(np.arange(x1_min, x1_max, 0.1),  
                         np.arange(x2_min, x2_max, 0.1))                   #生成网格型数据
    z = clf.predict(np.c_[xx.ravel(), yy.ravel()])                        
    
    #----------------------------------------------------------------------------
    #第六步 绘图可视化
    cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])         #颜色Map
    cmap_bold = ListedColormap(['#000000', '#00FF00', '#FFFFFF'])
    plt.figure()
    z = z.reshape(xx.shape)
    print(xx.shape, yy.shape, z.shape, test_target.shape)
    plt.pcolormesh(xx, yy, z, cmap=cmap_light)
    plt.scatter(test_data[:,0], test_data[:,1], c=test_target,
                cmap=cmap_bold, s=50)
    plt.show()
    

    代码提取了178行数据的第一列作为类标,剩余13列数据作为13个特征的数据集,并划分为训练集(100行)和测试集(78行)。输出结果如下,包括78行SVM分类预测的类标结果,其中61行数据类标与真实的结果一致,其准确率为0.78,召回率为0.78,F1特征为0.78。

    在这里插入图片描述

    最后可视化绘图输出如下图所示的结果。

    在这里插入图片描述


    3.优化SVM分析红酒数据集

    前面SVM分析红酒数据集的代码存在两个缺点,一是采用固定的组合方式划分的数据集,即调用np.concatenate()函数将0-40、60-100、140-160行数据分割为训练集,其余为预测集;二是只提取了数据集中的两列特征进行SVM分析和可视化绘图,即调用“x = x[:, :2]”获取前两列特征,而红酒数据集共有13列特征。

    真实的数据分析中通常会随机划分数据集,分析过程也是对所有的特征进行训练及预测操作,再经过降维处理之后进行可视化绘图展示。下面对SVM分析红酒数据集实例进行简单的代码优化,主要包括:

    • 随机划分红酒数据集
    • 对数据集的所有特征进行训练和预测分析
    • 采用PCA算法降维后再进行可视化绘图操作

    完整代码如下,希望读者也认真学习该部分知识,更好地优化自己的研究或课题。

    # -*- coding: utf-8 -*-
    # By:Eastmount CSDN 2021-07-06
    import os 
    import numpy as np
    from sklearn.svm import SVC  
    from sklearn import metrics
    import matplotlib.pyplot as plt
    from matplotlib.colors import ListedColormap
    from sklearn.model_selection import train_test_split
    from sklearn.decomposition import PCA
    
    #第一步 加载数据集
    path = "wine/wine.txt"
    data = np.loadtxt(path,dtype=float,delimiter=",")
    print(data)
    
    #第二步 划分数据集
    yy, x = np.split(data, (1,), axis=1) #第一列类标yy,后面13列特征为x
    print(yy.shape, x.shape)
    y = []
    for n in yy: 
        y.append(int(n))
    y =  np.array(y, dtype = int) #list转换数组
    #划分数据集 测试集40%
    train_data, test_data, train_target, test_target = train_test_split(x, y, test_size=0.4, random_state=42)
    print(train_data.shape, train_target.shape)
    print(test_data.shape, test_target.shape)
    
    #第三步 SVC训练
    clf = SVC()
    clf.fit(train_data, train_target)
    result = clf.predict(test_data)
    print(result)
    print(test_target)
    
    #第四步 评价算法 
    print(sum(result==test_target))                            #预测结果与真实结果比对
    print(metrics.classification_report(test_target, result))  #准确率 召回率 F值
    
    #第五步 降维操作
    pca = PCA(n_components=2)      
    newData = pca.fit_transform(test_data)
                      
    #第六步 绘图可视化
    plt.figure()
    cmap_bold = ListedColormap(['#000000', '#00FF00', '#FFFFFF'])
    plt.scatter(newData[:,0], newData[:,1], c=test_target, cmap=cmap_bold, s=50)
    plt.show()
    

    输出结果如下所示,其准确率、召回率和F值很低,仅为50%、39%和23%。

    (106L, 13L) (106L,)
    (72L, 13L) (72L,)
    [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
     2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
    [1 1 3 1 2 1 2 3 2 3 1 3 1 2 1 2 2 2 1 2 1 2 2 3 3 3 2 2 2 1 1 2 3 1 1 1 3
     3 2 3 1 2 2 2 3 1 2 2 3 1 2 1 1 3 3 2 2 1 2 1 3 2 2 3 1 1 1 3 1 1 2 3]
    28
                 precision    recall  f1-score   support
    
              1       1.00      0.04      0.07        26
              2       0.38      1.00      0.55        27
              3       0.00      0.00      0.00        19
    
    avg / total       0.50      0.39      0.23        72
    

    上述代码如下采用决策树进行分析,则其准确率、召回率和F值就很高,结果如下所示。所以并不是每种分析算法都适应所有的数据集,不同数据集其特征不同,最佳分析的算也会不同,我们在进行数据分析时,通常会对比多种分析算法,再优化自己的实验和模型。

    from sklearn.tree import DecisionTreeClassifier 
    clf = DecisionTreeClassifier()
    print(metrics.classification_report(test_target, result))
    
    #             precision    recall  f1-score   support
    #
    #          1       0.96      0.88      0.92        26
    #          2       0.90      1.00      0.95        27
    #          3       1.00      0.95      0.97        19
    #
    #avg / total       0.95      0.94      0.94        72
    

    SVM算法分析后输出的图形如下所示。

    在这里插入图片描述


    五.各模型分类对比实验

    算法评价和对比实验是深度学习重要的知识点,这里作者对各种机器学习分类算法进行对比,以鸢尾花数据集为例,我们从绘制的分类边界效果以及实验评估指标(Precision、Recall、F1-socre)分别进行对比。参考文章如下,推荐大家学习:

    1.决策树

    原始代码如下:

    # -*- coding: utf-8 -*-
    # By:Eastmount CSDN 2021-07-06
    # 该部分参考知乎萌弟老师:https://zhuanlan.zhihu.com/p/173945775
    import numpy as np
    from sklearn import metrics
    from sklearn import datasets
    import matplotlib.pyplot as plt
    from matplotlib.colors import ListedColormap
    from sklearn.model_selection import train_test_split
    from sklearn.decomposition import PCA
    from sklearn.preprocessing import StandardScaler
    
    #------------------------------------------------------------------------
    #第一步 导入数据
    iris = datasets.load_iris()
    X = iris.data[:,[2,3]]
    y = iris.target
    print("Class labels:",np.unique(y))  #打印分类类别的种类 [0 1 2]
     
    #30%测试数据 70%训练数据 stratify=y表示训练数据和测试数据具有相同的类别比例
    X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=1,stratify=y)
    
    #------------------------------------------------------------------------
    #第二步 数据标准化
    sc = StandardScaler()      #估算训练数据中的mu和sigma
    sc.fit(X_train)            #使用训练数据中的mu和sigma对数据进行标准化
    X_train_std = sc.transform(X_train)
    X_test_std = sc.transform(X_test)
    print(X_train_std)
    print(X_test_std)
    
    #------------------------------------------------------------------------
    #第三步 可视化函数 画出决策边界
    def plot_decision_region(X,y,classifier,resolution=0.02):
        markers = ('s','x','o','^','v')
        colors = ('red','blue','lightgreen','gray','cyan')
        cmap = ListedColormap(colors[:len(np.unique(y))])
        
        # plot the decision surface
        x1_min,x1_max = X[:,0].min()-1,X[:,0].max()+1
        x2_min,x2_max = X[:,1].min()-1,X[:,1].max()+1
        xx1,xx2 = np.meshgrid(np.arange(x1_min,x1_max,resolution),
                             np.arange(x2_min,x2_max,resolution))
        Z = classifier.predict(np.array([xx1.ravel(),xx2.ravel()]).T)
        Z = Z.reshape(xx1.shape)
        plt.contourf(xx1,xx2,Z,alpha=0.3,cmap=cmap)
        plt.xlim(xx1.min(),xx1.max())
        plt.ylim(xx2.min(),xx2.max())
        
        # plot class samples
        for idx,cl in enumerate(np.unique(y)):
            plt.scatter(x=X[y==cl,0],
                       y = X[y==cl,1],
                       alpha=0.8,
                       c=colors[idx],
                       marker = markers[idx],
                       label=cl,
                       edgecolors='black')
    
    #------------------------------------------------------------------------
    #第四步 决策树分类
    from sklearn.tree import DecisionTreeClassifier
    tree = DecisionTreeClassifier(criterion='gini',max_depth=4,random_state=1)
    tree.fit(X_train_std,y_train)
    print(X_train_std.shape, X_test_std.shape, len(y_train), len(y_test)) #(105, 2) (45, 2) 105 45
    res1 = tree.predict(X_test_std)
    print(res1)
    print(metrics.classification_report(y_test, res1, digits=4)) #四位小数
    
    plot_decision_region(X_train_std,y_train,classifier=tree,resolution=0.02)
    plt.xlabel('petal length [standardized]')
    plt.ylabel('petal width [standardized]')
    plt.title('DecisionTreeClassifier')
    plt.legend(loc='upper left')
    plt.show()
    

    实验的精确率、召回率和F1值输出如下:

    • macro avg: 0.9792 0.9778 0.9778
                  precision    recall  f1-score   support
    
               0     1.0000    1.0000    1.0000        15
               1     0.9375    1.0000    0.9677        15
               2     1.0000    0.9333    0.9655        15
    
        accuracy                         0.9778        45
       macro avg     0.9792    0.9778    0.9778        45
    weighted avg     0.9792    0.9778    0.9778        45
    

    绘制的训练数据分类效果如下图所示,可以看到分类效果明显。

    在这里插入图片描述


    2.KNN

    核心代码如下:

    • macro avg: 0.98 0.98 0.98
    #第五步 KNN分类
    from sklearn.neighbors import KNeighborsClassifier
    knn = KNeighborsClassifier(n_neighbors=2,p=2,metric="minkowski")
    knn.fit(X_train_std,y_train)
    res2 = knn.predict(X_test_std)
    print(res2)
    print(metrics.classification_report(y_test, res2, digits=4)) #四位小数
    
    plot_decision_region(X_train_std,y_train,classifier=knn,resolution=0.02)
    plt.xlabel('petal length [standardized]')
    plt.ylabel('petal width [standardized]')
    plt.title('KNeighborsClassifier')
    plt.legend(loc='upper left')
    plt.show()
    

    输出结果如下:

    • macro avg: 0.9792 0.9778 0.9778
                  precision    recall  f1-score   support
    
               0     1.0000    1.0000    1.0000        15
               1     0.9375    1.0000    0.9677        15
               2     1.0000    0.9333    0.9655        15
    
        accuracy                         0.9778        45
       macro avg     0.9792    0.9778    0.9778        45
    weighted avg     0.9792    0.9778    0.9778        45
    

    绘制的训练数据分类效果如下图所示:

    在这里插入图片描述


    3.SVM

    核心代码如下:

    #第六步 SVM分类 核函数对非线性分类问题建模(gamma=0.20)
    from sklearn.svm import SVC
    svm = SVC(kernel='rbf',random_state=1,gamma=0.20,C=1.0) #较小的gamma有较松的决策边界
    svm.fit(X_train_std,y_train)
    res3 = svm.predict(X_test_std)
    print(res3)
    print(metrics.classification_report(y_test, res3, digits=4))
    
    plot_decision_region(X_train_std,y_train,classifier=svm,resolution=0.02)
    plt.xlabel('petal length [standardized]')
    plt.ylabel('petal width [standardized]')
    plt.title('SVM')
    plt.legend(loc='upper left')
    plt.show()
    

    输出结果如下:

    • macro avg: 0.9361 0.9333 0.9340
                  precision    recall  f1-score   support
    
               0     1.0000    0.9333    0.9655        15
               1     0.9333    0.9333    0.9333        15
               2     0.8750    0.9333    0.9032        15
    
        accuracy                         0.9333        45
       macro avg     0.9361    0.9333    0.9340        45
    weighted avg     0.9361    0.9333    0.9340        45
    

    绘制的训练数据分类效果如下图所示:

    在这里插入图片描述

    如果使用的核函数gamma为100,然后实现非线性分类,则绘制结果如下图所示:

    • svm = SVC(kernel=‘rbf’,random_state=1,gamma=100.0,C=1.0,verbose=1)

    在这里插入图片描述

    引用萌弟老师的结论,非常土建大家去关注他。
    从不同的gamma取值的图像来看:对于高斯核函数,增大gamma值,将增大训练样本的影响范围,导致决策边界紧缩和波动;较小的gamma值得到的决策边界相对宽松。虽然较大的gamma值在训练样本中有很小的训练误差,但是很可能泛化能力较差,容易出现过拟合。


    4.逻辑回归

    核心代码如下:

    #第七步 逻辑回归分类
    from sklearn.linear_model import LogisticRegression
    lr = LogisticRegression(C=100.0,random_state=1)
    lr.fit(X_train_std,y_train)
    res4 = lr.predict(X_test_std)
    print(res4)
    print(metrics.classification_report(y_test, res4, digits=4))
    
    plot_decision_region(X_train_std,y_train,classifier=lr,resolution=0.02)
    plt.xlabel('petal length [standardized]')
    plt.ylabel('petal width [standardized]')
    plt.title('LogisticRegression')
    plt.legend(loc='upper left')
    plt.show()
    

    输出结果如下:

    • macro avg: 0.9792 0.9778 0.9778
                  precision    recall  f1-score   support
    
               0     1.0000    1.0000    1.0000        15
               1     0.9375    1.0000    0.9677        15
               2     1.0000    0.9333    0.9655        15
    
        accuracy                         0.9778        45
       macro avg     0.9792    0.9778    0.9778        45
    weighted avg     0.9792    0.9778    0.9778        45
    

    绘制的训练数据分类效果如下图所示:

    在这里插入图片描述


    5.朴素贝叶斯

    核心代码如下:

    #第八步 朴素贝叶斯分类
    from sklearn.naive_bayes import GaussianNB
    gnb = GaussianNB()
    gnb.fit(X_train_std,y_train)
    res5 = gnb.predict(X_test_std)
    print(res5)
    print(metrics.classification_report(y_test, res5, digits=4))
    
    plot_decision_region(X_train_std,y_train,classifier=gnb,resolution=0.02)
    plt.xlabel('petal length [standardized]')
    plt.ylabel('petal width [standardized]')
    plt.title('GaussianNB')
    plt.legend(loc='upper left')
    plt.show()
    

    输出结果如下:

    • macro avg: 0.9792 0.9778 0.9778
                  precision    recall  f1-score   support
    
               0     1.0000    1.0000    1.0000        15
               1     0.9375    1.0000    0.9677        15
               2     1.0000    0.9333    0.9655        15
    
        accuracy                         0.9778        45
       macro avg     0.9792    0.9778    0.9778        45
    weighted avg     0.9792    0.9778    0.9778        45
    

    绘制的训练数据分类效果如下图所示,还挺好看的,边界呈曲线分布。

    在这里插入图片描述


    6.随机森林

    核心代码如下:

    #第九步 随机森林分类
    from sklearn.ensemble import RandomForestClassifier
    forest = RandomForestClassifier(criterion='gini',
                                    n_estimators=25,
                                    random_state=1,
                                    n_jobs=2,
                                    verbose=1)
    forest.fit(X_train_std,y_train)
    res6 = gnb.predict(X_test_std)
    print(res6)
    print(metrics.classification_report(y_test, res6, digits=4))
    
    plot_decision_region(X_train_std,y_train,classifier=forest,resolution=0.02)
    plt.xlabel('petal length [standardized]')
    plt.ylabel('petal width [standardized]')
    plt.title('GaussianNB')
    plt.legend(loc='upper left')
    plt.show()
    

    输出结果如下:

    • macro avg: 0.9792 0.9778 0.9778
                  precision    recall  f1-score   support
    
               0     1.0000    1.0000    1.0000        15
               1     0.9375    1.0000    0.9677        15
               2     1.0000    0.9333    0.9655        15
    
        accuracy                         0.9778        45
       macro avg     0.9792    0.9778    0.9778        45
    weighted avg     0.9792    0.9778    0.9778        45
    

    绘制的训练数据分类效果如下图所示:

    在这里插入图片描述


    7.AdaBoost

    核心代码如下:

    #第十步 集成学习分类
    from sklearn.ensemble import AdaBoostClassifier
    ada = AdaBoostClassifier()
    ada.fit(X_train_std,y_train)
    res7 = ada.predict(X_test_std)
    print(res7)
    print(metrics.classification_report(y_test, res7, digits=4))
    
    plot_decision_region(X_train_std,y_train,classifier=forest,resolution=0.02)
    plt.xlabel('petal length [standardized]')
    plt.ylabel('petal width [standardized]')
    plt.title('AdaBoostClassifier')
    plt.legend(loc='upper left')
    plt.show()
    

    输出结果如下:

    • macro avg: 0.9792 0.9778 0.9778
                  precision    recall  f1-score   support
    
               0     1.0000    1.0000    1.0000        15
               1     0.9375    1.0000    0.9677        15
               2     1.0000    0.9333    0.9655        15
    
        accuracy                         0.9778        45
       macro avg     0.9792    0.9778    0.9778        45
    weighted avg     0.9792    0.9778    0.9778        45
    

    绘制的训练数据分类效果如下图所示:

    在这里插入图片描述


    8.GradientBoosting

    核心代码如下:

    #第11步 GradientBoosting分类
    from sklearn.ensemble import GradientBoostingClassifier
    gb = GradientBoostingClassifier()
    ada.fit(X_train_std,y_train)
    res8 = ada.predict(X_test_std)
    print(res8)
    print(metrics.classification_report(y_test, res8, digits=4))
    
    plot_decision_region(X_train_std,y_train,classifier=forest,resolution=0.02)
    plt.xlabel('petal length [standardized]')
    plt.ylabel('petal width [standardized]')
    plt.title('GradientBoostingClassifier')
    plt.legend(loc='upper left')
    plt.show()
    

    输出结果如下:

    • macro avg: 0.9792 0.9778 0.9778
                  precision    recall  f1-score   support
    
               0     1.0000    1.0000    1.0000        15
               1     0.9375    1.0000    0.9677        15
               2     1.0000    0.9333    0.9655        15
    
        accuracy                         0.9778        45
       macro avg     0.9792    0.9778    0.9778        45
    weighted avg     0.9792    0.9778    0.9778        45
    

    绘制的训练数据分类效果如下图所示:

    在这里插入图片描述


    9.实验结果对比

    最后通常需要对实验结果进行对比,由于数据集比较少,所有效果都比较好,这里不太好进行对比实验。简单给出两张对比结果图,但方法是类似的。随着作者深入会分享更多相关文章。

    在这里插入图片描述

    在这里插入图片描述


    六.本章小结

    写到这里,这篇文章就结束了,您是否对分类更好的认识呢?
    聚类是通过定义一种距离度量方法,表示两个东西的相似程度,然后将类内相似度高且类间相似度低的数据放在一个类中,它是不需要标注结果的无监督学习算法。与之不同,分类是需要标注类标的,属于有监督学习,它表示收集某一类数据的共有特征,找出区分度大的特征,用这些特征对要分类的数据进行分类,并且由于是标注结果的,可以通过反复地训练来提升分类算法。

    常见的分类算法包括朴素贝叶斯、逻辑回归、决策树、支持向量机等。常见应用比如通过分析市民历史公交卡交易数据来分类预测乘客的出行习惯和偏好;京东从海量商品图片中提取图像特征,通过分类给用户推荐商品和广告,比如“找同款”应用;基于短信文本内容的分类智能化识别垃圾短信及其变种,防止骚扰手机用户;搜索引擎通过训练用户的历史查询词和用户属性标签(如性别、年龄、爱好),构建分类算法来预测新增用户的属性及偏好等。不同的分类算法有不同的优劣,希望读者下来编写代码体会不同的分类算法的特点。

    在这里插入图片描述

    最后希望读者能复现每一行代码,只有实践才能进步。同时更多聚类算法和原理知识,希望读者下来自行深入学习研究,也推荐大家结合Sklearn官网和开源网站学习更多的机器学习知识。

    该系列所有代码下载地址:

    感谢在求学路上的同行者,不负遇见,勿忘初心。这周的留言感慨~

    在这里插入图片描述

    (By:娜璋之家 Eastmount 2021-07-10 夜于武汉 https://blog.csdn.net/Eastmount )


    参考文献:

    • [1] 杨秀璋. 专栏:知识图谱、web数据挖掘及NLP - CSDN博客[EB/OL]. (2016-09-19)[2017-11-07]. http://blog.csdn.net/column/details/eastmount-kgdmnlp.html.
    • [2] 张良均,王路,谭立云,苏剑林. Python数据分析与挖掘实战[M]. 北京:机械工业出版社,2016.
    • [3] (美)Wes McKinney著. 唐学韬等译. 利用Python进行数据分析[M]. 北京:机械工业出版社,2013.
    • [4] Jiawei Han,Micheline Kamber著. 范明,孟小峰译. 数据挖掘概念与技术. 北京:机械工业出版社,2007.
    • [5] 杨秀璋. [Python数据挖掘课程] 四.决策树DTC数据分析及鸢尾数据集分析[EB/OL].(2016-10-15)[2017-11-26]. http://blog.csdn.net/eastmount/article/details/52820400.
    • [6] 杨秀璋. [Python数据挖掘课程] 五.线性回归知识及预测糖尿病实例[EB\OL]. (2016-10-28)[2017-11-26]. http://blog.csdn.net/eastmount/article/details/52929765.
    • [7] jackywu1010. 分类算法概述与比较[EB/OL]. (2011-12-09)[2017-11-26]. http://blog.csdn.net/jackywu1010/article/details/7055561.
    • [8] 百度百科. 邻近算法[EB/OL]. (2015-09-16)[2017-11-26]. https://baike.baidu.com/item/邻近算法/1151153?fr=aladdin.
    • [9] 杨秀璋. [python数据挖掘课程] 十九.鸢尾花数据集可视化、线性回归、决策树花样分析[EB/OL]. (2017-12-02)[2017-12-02]. http://blog.csdn.net/eastmount/article/
      details/78692227.
    • [10]杨秀璋. [python数据挖掘课程] 二十.KNN最近邻分类算法分析详解及平衡秤TXT数据集读取[EB/OL]. (2017-12-08)[2017-12-08]. http://blog.csdn.net/eastmount/article/
      details/78747128.
    • [11] lsldd. 用Python开始机器学习(4:KNN分类算法)[EB/OL]. (2014-11-23)[2017-11-26]. http://blog.csdn.net/lsldd/article/details/41357931.
    • [12] UCI官网. UCI Machine Learning Repository: Wine Data Set[EB/OL]. (2017)[2017-12-08]. http://archive.ics.uci.edu/ml/datasets/Wine.
    • [13]杨秀璋. [python数据挖掘课程] 二十一.朴素贝叶斯分类器详解及中文文本舆情分析[EB/OL]. (2018-01-24)[2018-01-24]. http://blog.csdn.net/eastmount/article/
      details/79128235.
    • [14] scikit-learn官网. Nearest Neighbors Classification scikit-learn[EB\OL]. (2017)[2017-12-08]. http://scikit-learn.org/stable/auto_examples/neighbors/
      plot_classification.html#sphx-glr-auto-examples-neighbors-plot-classification-py.
    • [15]July大神. 支持向量机通俗导论(理解SVM的三层境界)[EB/OL]. http://blog.csdn.net/v_JULY_v/article/details/7624837.
    展开全文
  • 10 个最难回答的 Java 问题

    万次阅读 多人点赞 2019-10-08 19:31:08
    一个棘手的 Java 问题,如果 Java编程语言不是你设计的,你怎么能回答这个问题呢。Java编程的常识和深入了解有助于回答这种棘手的 Java 核心方面的面试问题。 为什么 wait,notify 和 notifyAll 是在 Object 中...

     1.为什么等待和通知是在 Object 类而不是 Thread 中声明的?

    一个棘手的 Java 问题,如果 Java编程语言不是你设计的,你怎么能回答这个问题呢。Java编程的常识和深入了解有助于回答这种棘手的 Java 核心方面的面试问题。

    为什么 wait,notify 和 notifyAll 是在 Object 类中定义的而不是在 Thread 类中定义

    这是有名的 Java 面试问题,招2~4年经验的到高级 Java 开发人员面试都可能碰到。

    这个问题的好在它能反映了面试者对等待通知机制的了解, 以及他对此主题的理解是否明确。就像为什么 Java 中不支持多继承或者为什么 String 在 Java 中是 final 的问题一样,这个问题也可能有多个答案。

    为什么在 Object 类中定义 wait 和 notify 方法,每个人都能说出一些理由。从我的面试经验来看, wait 和 nofity 仍然是大多数Java 程序员最困惑的,特别是2到3年的开发人员,如果他们要求使用 wait 和 notify, 他们会很困惑。因此,如果你去参加 Java 面试,请确保对 wait 和 notify 机制有充分的了解,并且可以轻松地使用 wait 来编写代码,并通过生产者-消费者问题或实现阻塞队列等了解通知的机制。

    为什么等待和通知需要从同步块或方法中调用, 以及 Java 中的 wait,sleep 和 yield 方法之间的差异,如果你还没有读过,你会觉得有趣。为何 wait,notify 和 notifyAll 属于 Object 类? 为什么它们不应该在 Thread 类中? 以下是我认为有意义的一些想法:

    1) wait 和 notify 不仅仅是普通方法或同步工具,更重要的是它们是 Java 中两个线程之间的通信机制。对语言设计者而言, 如果不能通过 Java 关键字(例如 synchronized)实现通信此机制,同时又要确保这个机制对每个对象可用, 那么 Object 类则是的正确声明位置。记住同步和等待通知是两个不同的领域,不要把它们看成是相同的或相关的。同步是提供互斥并确保 Java 类的线程安全,而 wait 和 notify 是两个线程之间的通信机制。

    2) 每个对象都可上锁,这是在 Object 类而不是 Thread 类中声明 wait 和 notify 的另一个原因。

    3) 在 Java 中为了进入代码的临界区,线程需要锁定并等待锁定,他们不知道哪些线程持有锁,而只是知道锁被某个线程持有, 并且他们应该等待取得锁, 而不是去了解哪个线程在同步块内,并请求它们释放锁定。

    4) Java 是基于 Hoare 的监视器的思想。在Java中,所有对象都有一个监视器。

    线程在监视器上等待,为执行等待,我们需要2个参数:

    • 一个线程

    • 一个监视器(任何对象)

    在 Java 设计中,线程不能被指定,它总是运行当前代码的线程。但是,我们可以指定监视器(这是我们称之为等待的对象)。这是一个很好的设计,因为如果我们可以让任何其他线程在所需的监视器上等待,这将导致“入侵”,导致在设计并发程序时会遇到困难。请记住,在 Java 中,所有在另一个线程的执行中侵入的操作都被弃用了(例如 stop 方法)。

    2.为什么Java中不支持多重继承?

    我发现这个 Java 核心问题很难回答,因为你的答案可能不会让面试官满意,在大多数情况下,面试官正在寻找答案中的关键点,如果你提到这些关键点,面试官会很高兴。在 Java 中回答这种棘手问题的关键是准备好相关主题, 以应对后续的各种可能的问题。

    这是非常经典的问题,与为什么 String 在 Java 中是不可变的很类似; 这两个问题之间的相似之处在于它们主要是由 Java 创作者的设计决策使然。

    为什么Java不支持多重继承, 可以考虑以下两点:

    1)第一个原因是围绕钻石形继承问题产生的歧义,考虑一个类 A 有 foo() 方法, 然后 B 和 C 派生自 A, 并且有自己的 foo() 实现,现在 D 类使用多个继承派生自 B 和C,如果我们只引用 foo(), 编译器将无法决定它应该调用哪个 foo()。这也称为 Diamond 问题,因为这个继承方案的结构类似于菱形,见下图:

    即使我们删除钻石的顶部 A 类并允许多重继承,我们也将看到这个问题含糊性的一面。如果你把这个理由告诉面试官,他会问为什么 C++ 可以支持多重继承而 Java不行。嗯,在这种情况下,我会试着向他解释我下面给出的第二个原因,它不是因为技术难度, 而是更多的可维护和更清晰的设计是驱动因素, 虽然这只能由 Java 言语设计师确认,我们只是推测。维基百科链接有一些很好的解释,说明在使用多重继承时,由于钻石问题,不同的语言地址问题是如何产生的。

    2)对我来说第二个也是更有说服力的理由是,多重继承确实使设计复杂化并在转换、构造函数链接等过程中产生问题。假设你需要多重继承的情况并不多,简单起见,明智的决定是省略它。此外,Java 可以通过使用接口支持单继承来避免这种歧义。由于接口只有方法声明而且没有提供任何实现,因此只有一个特定方法的实现,因此不会有任何歧义。(实用详尽的Java面试题大全,可以在Java知音公众号回复“面试题聚合”)

    3.为什么Java不支持运算符重载?

    另一个类似棘手的Java问题。为什么 C++ 支持运算符重载而 Java 不支持? 有人可能会说+运算符在 Java 中已被重载用于字符串连接,不要被这些论据所欺骗。

    与 C++ 不同,Java 不支持运算符重载。Java 不能为程序员提供自由的标准算术运算符重载,例如+, - ,*和/等。如果你以前用过 C++,那么 Java 与 C++ 相比少了很多功能,例如 Java 不支持多重继承,Java中没有指针,Java中没有引用传递。另一个类似的问题是关于 Java 通过引用传递,这主要表现为 Java 是通过值还是引用传参。虽然我不知道背后的真正原因,但我认为以下说法有些道理,为什么 Java 不支持运算符重载。

    1)简单性和清晰性。清晰性是Java设计者的目标之一。设计者不是只想复制语言,而是希望拥有一种清晰,真正面向对象的语言。添加运算符重载比没有它肯定会使设计更复杂,并且它可能导致更复杂的编译器, 或减慢 JVM,因为它需要做额外的工作来识别运算符的实际含义,并减少优化的机会, 以保证 Java 中运算符的行为。

    2)避免编程错误。Java 不允许用户定义的运算符重载,因为如果允许程序员进行运算符重载,将为同一运算符赋予多种含义,这将使任何开发人员的学习曲线变得陡峭,事情变得更加混乱。据观察,当语言支持运算符重载时,编程错误会增加,从而增加了开发和交付时间。由于 Java 和 JVM 已经承担了大多数开发人员的责任,如在通过提供垃圾收集器进行内存管理时,因为这个功能增加污染代码的机会, 成为编程错误之源, 因此没有多大意义。

    3)JVM复杂性。从JVM的角度来看,支持运算符重载使问题变得更加困难。通过更直观,更干净的方式使用方法重载也能实现同样的事情,因此不支持 Java 中的运算符重载是有意义的。与相对简单的 JVM 相比,复杂的 JVM 可能导致 JVM 更慢,并为保证在 Java 中运算符行为的确定性从而减少了优化代码的机会。

    4)让开发工具处理更容易。这是在 Java 中不支持运算符重载的另一个好处。省略运算符重载使语言更容易处理,这反过来又更容易开发处理语言的工具,例如 IDE 或重构工具。Java 中的重构工具远胜于 C++。

    4.为什么 String 在 Java 中是不可变的?

    我最喜欢的 Java 面试问题,很棘手,但同时也非常有用。一些面试者也常问这个问题,为什么 String 在 Java 中是 final 的。

    字符串在 Java 中是不可变的,因为 String 对象缓存在 String 池中。由于缓存的字符串在多个客户之间共享,因此始终存在风险,其中一个客户的操作会影响所有其他客户。例如,如果一段代码将 String “Test” 的值更改为 “TEST”,则所有其他客户也将看到该值。由于 String 对象的缓存性能是很重要的一方面,因此通过使 String 类不可变来避免这种风险。

    同时,String 是 final 的,因此没有人可以通过扩展和覆盖行为来破坏 String 类的不变性、缓存、散列值的计算等。String 类不可变的另一个原因可能是由于 HashMap。

    由于把字符串作为 HashMap 键很受欢迎。对于键值来说,重要的是它们是不可变的,以便用它们检索存储在 HashMap 中的值对象。由于 HashMap 的工作原理是散列,因此需要具有相同的值才能正常运行。如果在插入后修改了 String 的内容,可变的 String将在插入和检索时生成两个不同的哈希码,可能会丢失 Map 中的值对象。

    如果你是印度板球迷,你可能能够与我的下一句话联系起来。字符串是Java的 VVS Laxman,即非常特殊的类。我还没有看到一个没有使用 String 编写的 Java 程序。这就是为什么对 String 的充分理解对于 Java 开发人员来说非常重要。

    String 作为数据类型,传输对象和中间人角色的重要性和流行性也使这个问题在 Java 面试中很常见。

    为什么 String 在 Java 中是不可变的是 Java 中最常被问到的字符串访问问题之一,它首先讨论了什么是 String,Java 中的 String 如何与 C 和 C++ 中的 String 不同,然后转向在Java中什么是不可变对象,不可变对象有什么好处,为什么要使用它们以及应该使用哪些场景。

    这个问题有时也会问:“为什么 String 在 Java 中是 final 的”。在类似的说明中,如果你正在准备Java 面试,我建议你看看《Java程序员面试宝典(第4版) 》,这是高级和中级Java程序员的优秀资源。它包含来自所有重要 Java 主题的问题,包括多线程,集合,GC,JVM内部以及 Spring和 Hibernate 框架等。

    正如我所说,这个问题可能有很多可能的答案,而 String 类的唯一设计者可以放心地回答它。我在 Joshua Bloch 的 Effective Java 书中期待一些线索,但他也没有提到它。我认为以下几点解释了为什么 String 类在 Java 中是不可变的或 final 的:

    1)想象字符串池没有使字符串不可变,它根本不可能,因为在字符串池的情况下,一个字符串对象/文字,例如 “Test” 已被许多参考变量引用,因此如果其中任何一个更改了值,其他参数将自动受到影响,即假设

    现在字符串 B 调用 "Test".toUpperCase(), 将同一个对象改为“TEST”,所以 A 也是 “TEST”,这不是期望的结果。

    下图显示了如何在堆内存和字符串池中创建字符串。

    2)字符串已被广泛用作许多 Java 类的参数,例如,为了打开网络连接,你可以将主机名和端口号作为字符串传递,你可以将数据库 URL 作为字符串传递, 以打开数据库连接,你可以通过将文件名作为参数传递给 File I/O 类来打开 Java 中的任何文件。如果 String 不是不可变的,这将导致严重的安全威胁,我的意思是有人可以访问他有权授权的任何文件,然后可以故意或意外地更改文件名并获得对该文件的访问权限。由于不变性,你无需担心这种威胁。这个原因也说明了,为什么 String 在 Java 中是最终的,通过使 java.lang.String final,Java设计者确保没有人覆盖 String 类的任何行为。

    3)由于 String 是不可变的,它可以安全地共享许多线程,这对于多线程编程非常重要. 并且避免了 Java 中的同步问题,不变性也使得String 实例在 Java 中是线程安全的,这意味着你不需要从外部同步 String 操作。关于 String 的另一个要点是由截取字符串 SubString 引起的内存泄漏,这不是与线程相关的问题,但也是需要注意的。

    4)为什么 String 在 Java 中是不可变的另一个原因是允许 String 缓存其哈希码,Java 中的不可变 String 缓存其哈希码,并且不会在每次调用 String 的 hashcode 方法时重新计算,这使得它在 Java 中的 HashMap 中使用的 HashMap 键非常快。简而言之,因为 String 是不可变的,所以没有人可以在创建后更改其内容,这保证了 String 的 hashCode 在多次调用时是相同的。

    5)String 不可变的绝对最重要的原因是它被类加载机制使用,因此具有深刻和基本的安全考虑。如果 String 是可变的,加载“java.io.Writer” 的请求可能已被更改为加载 “mil.vogoon.DiskErasingWriter”. 安全性和字符串池是使字符串不可变的主要原因。顺便说一句,上面的理由很好回答另一个Java面试问题: “为什么String在Java中是最终的”。要想是不可变的,你必须是最终的,这样你的子类不会破坏不变性。你怎么看?

    5.为什么 char 数组比 Java 中的 String 更适合存储密码?

    另一个基于 String 的棘手 Java 问题,相信我只有很少的 Java 程序员可以正确回答这个问题。这是一个真正艰难的核心Java面试问题,并且需要对 String 的扎实知识才能回答这个问题。

    这是最近在 Java 面试中向我的一位朋友询问的问题。他正在接受技术主管职位的面试,并且有超过6年的经验。如果你还没有遇到过这种情况,那么字符数组和字符串可以用来存储文本数据,但是选择一个而不是另一个很难。但正如我的朋友所说,任何与 String 相关的问题都必须对字符串的特殊属性有一些线索,比如不变性,他用它来说服访提问的人。在这里,我们将探讨为什么你应该使用char[]存储密码而不是String的一些原因。

    字符串:

    1)由于字符串在 Java 中是不可变的,如果你将密码存储为纯文本,它将在内存中可用,直到垃圾收集器清除它. 并且为了可重用性,会存在 String 在字符串池中, 它很可能会保留在内存中持续很长时间,从而构成安全威胁。

    由于任何有权访问内存转储的人都可以以明文形式找到密码,这是另一个原因,你应该始终使用加密密码而不是纯文本。由于字符串是不可变的,所以不能更改字符串的内容,因为任何更改都会产生新的字符串,而如果你使用char[],你就可以将所有元素设置为空白或零。因此,在字符数组中存储密码可以明显降低窃取密码的安全风险。

    2)Java 本身建议使用 JPasswordField 的 getPassword() 方法,该方法返回一个 char[] 和不推荐使用的getTex() 方法,该方法以明文形式返回密码,由于安全原因。应遵循 Java 团队的建议, 坚持标准而不是反对它。

    3)使用 String 时,总是存在在日志文件或控制台中打印纯文本的风险,但如果使用 Array,则不会打印数组的内容而是打印其内存位置。虽然不是一个真正的原因,但仍然有道理。

    我还建议使用散列或加密的密码而不是纯文本,并在验证完成后立即从内存中清除它。因此,在Java中,用字符数组用存储密码比字符串是更好的选择。虽然仅使用char[]还不够,还你需要擦除内容才能更安全。(实用详尽的Java面试题大全,可以在Java知音公众号回复“面试题聚合”)

    6.如何使用双重检查锁定在 Java 中创建线程安全的单例?

    这个 Java 问题也常被问: 什么是线程安全的单例,你怎么创建它。好吧,在Java 5之前的版本, 使用双重检查锁定创建单例 Singleton 时,如果多个线程试图同时创建 Singleton 实例,则可能有多个 Singleton 实例被创建。从 Java 5 开始,使用 Enum 创建线程安全的Singleton很容易。但如果面试官坚持双重检查锁定,那么你必须为他们编写代码。记得使用volatile变量。

    为什么枚举单例在 Java 中更好

    枚举单例是使用一个实例在 Java 中实现单例模式的新方法。虽然Java中的单例模式存在很长时间,但枚举单例是相对较新的概念,在引入Enum作为关键字和功能之后,从Java5开始在实践中。本文与之前关于 Singleton 的内容有些相关, 其中讨论了有关 Singleton 模式的面试中的常见问题, 以及 10 个 Java 枚举示例, 其中我们看到了如何通用枚举可以。这篇文章是关于为什么我们应该使用Eeame作为Java中的单例,它比传统的单例方法相比有什么好处等等。

    Java 枚举和单例模式

    Java 中的枚举单例模式是使用枚举在 Java 中实现单例模式。单例模式在 Java 中早有应用, 但使用枚举类型创建单例模式时间却不长. 如果感兴趣, 你可以了解下构建者设计模式和装饰器设计模式。

    1) 枚举单例易于书写

    这是迄今为止最大的优势,如果你在Java 5之前一直在编写单例, 你知道, 即使双检查锁定, 你仍可以有多个实例。虽然这个问题通过 Java 内存模型的改进已经解决了, 从 Java 5 开始的 volatile 类型变量提供了保证, 但是对于许多初学者来说, 编写起来仍然很棘手。与同步双检查锁定相比,枚举单例实在是太简单了。如果你不相信, 那就比较一下下面的传统双检查锁定单例和枚举单例的代码:

    在 Java 中使用枚举的单例

    这是我们通常声明枚举的单例的方式,它可能包含实例变量和实例方法,但为了简单起见,我没有使用任何实例方法,只是要注意,如果你使用的实例方法且该方法能改变对象的状态的话, 则需要确保该方法的线程安全。默认情况下,创建枚举实例是线程安全的,但 Enum 上的任何其他方法是否线程安全都是程序员的责任。

    你可以通过EasySingleton.INSTANCE来处理它,这比在单例上调用getInstance()方法容易得多。

    具有双检查锁定的单例示例

    下面的代码是单例模式中双重检查锁定的示例,此处的 getInstance() 方法检查两次,以查看 INSTANCE 是否为空,这就是为什么它被称为双检查锁定模式,请记住,双检查锁定是代理之前Java 5,但Java5内存模型中易失变量的干扰,它应该工作完美。

    你可以调用DoubleCheckedLockingSingleton.getInstance() 来获取此单例类的访问权限。

    现在,只需查看创建延迟加载的线程安全的 Singleton 所需的代码量。使用枚举单例模式, 你可以在一行中具有该模式, 因为创建枚举实例是线程安全的, 并且由 JVM 进行。

    人们可能会争辩说,有更好的方法来编写 Singleton 而不是双检查锁定方法, 但每种方法都有自己的优点和缺点, 就像我最喜欢在类加载时创建的静态字段 Singleton, 如下面所示, 但请记住, 这不是一个延迟加载单例:

    单例模式用静态工厂方法

    这是我最喜欢的在 Java 中影响 Singleton 模式的方法之一,因为 Singleton 实例是静态的,并且最后一个变量在类首次加载到内存时初始化,因此实例的创建本质上是线程安全的。

    你可以调用 Singleton.getSingleton() 来获取此类的访问权限。

    2) 枚举单例自行处理序列化

    传统单例的另一个问题是,一旦实现可序列化接口,它们就不再是 Singleton, 因为 readObject() 方法总是返回一个新实例, 就像 Java 中的构造函数一样。通过使用 readResolve() 方法, 通过在以下示例中替换 Singeton 来避免这种情况:

    如果 Singleton 类保持内部状态, 这将变得更加复杂, 因为你需要标记为 transient(不被序列化),但使用枚举单例, 序列化由 JVM 进行。

    3) 创建枚举实例是线程安全的

    如第 1 点所述,因为 Enum 实例的创建在默认情况下是线程安全的, 你无需担心是否要做双重检查锁定。

    总之, 在保证序列化和线程安全的情况下,使用两行代码枚举单例模式是在 Java 5 以后的世界中创建 Singleton 的最佳方式。你仍然可以使用其他流行的方法, 如你觉得更好, 欢迎讨论。

    7. 编写 Java 程序时, 如何在 Java 中创建死锁并修复它?

    经典但核心Java面试问题之一。

    如果你没有参与过多线程并发 Java 应用程序的编码,你可能会失败。

    如何避免 Java 线程死锁?

    如何避免 Java 中的死锁?是 Java 面试的热门问题之一, 也是多线程的编程中的重口味之一, 主要在招高级程序员时容易被问到, 且有很多后续问题。尽管问题看起来非常基本, 但大多数 Java 开发人员一旦你开始深入, 就会陷入困境。

    面试问题总是以“什么是死锁?”开始

    当两个或多个线程在等待彼此释放所需的资源(锁定)并陷入无限等待即是死锁。它仅在多任务或多线程的情况下发生。

    如何检测 Java 中的死锁?

    虽然这可以有很多答案, 但我的版本是首先我会看看代码, 如果我看到一个嵌套的同步块,或从一个同步的方法调用其他同步方法, 或试图在不同的对象上获取锁, 如果开发人员不是非常小心,就很容易造成死锁。

    另一种方法是在运行应用程序时实际锁定时找到它, 尝试采取线程转储,在 Linux 中,你可以通过kill -3命令执行此操作, 这将打印应用程序日志文件中所有线程的状态, 并且你可以看到哪个线程被锁定在哪个线程对象上。

    你可以使用 fastthread.io 网站等工具分析该线程转储, 这些工具允许你上载线程转储并对其进行分析。

    另一种方法是使用 jConsole 或 VisualVM, 它将显示哪些线程被锁定以及哪些对象被锁定。

    如果你有兴趣了解故障排除工具和分析线程转储的过程, 我建议你看看 Uriah Levy 在多元视觉(PluraIsight)上《分析 Java 线程转储》课程。旨在详细了解 Java 线程转储, 并熟悉其他流行的高级故障排除工具。

    编写一个将导致死锁的Java程序?

    一旦你回答了前面的问题,他们可能会要求你编写代码,这将导致Java死锁。

    这是我的版本之一

    如果 method1() 和 method2() 都由两个或多个线程调用,则存在死锁的可能性, 因为如果线程 1 在执行 method1() 时在 Sting 对象上获取锁, 线程 2 在执行 method2() 时在 Integer 对象上获取锁, 等待彼此释放 Integer 和 String 上的锁以继续进行一步, 但这永远不会发生。

    此图精确演示了我们的程序, 其中一个线程在一个对象上持有锁, 并等待其他线程持有的其他对象锁。

    你可以看到, Thread1 需要 Thread2 持有的 Object2 上的锁,而 Thread2 希望获得 Thread1 持有的 Object1 上的锁。由于没有线程愿意放弃, 因此存在死锁, Java 程序被卡住。

    其理念是, 你应该知道使用常见并发模式的正确方法, 如果你不熟悉这些模式,那么 Jose Paumard 《应用于并发和多线程的常见 Java 模式》是学习的好起点。

    如何避免Java中的死锁?

    现在面试官来到最后一部分, 在我看来, 最重要的部分之一; 如何修复代码中的死锁?或如何避免Java中的死锁?

    如果你仔细查看了上面的代码,那么你可能已经发现死锁的真正原因不是多个线程, 而是它们请求锁的方式, 如果你提供有序访问, 则问题将得到解决。

    下面是我的修复版本,它通过避免循环等待,而避免死锁, 而不需要抢占这是需要死锁的四个条件之一。

    现在没有任何死锁,因为两种方法都按相同的顺序访问 Integer 和 String 类文本上的锁。因此,如果线程 A 在 Integer 对象上获取锁, 则线程 B 不会继续, 直到线程 A 释放 Integer 锁, 即使线程 B 持有 String 锁, 线程 A 也不会被阻止, 因为现在线程 B 不会期望线程 A 释放 Integer 锁以继续。(实用详尽的Java面试题大全,可以在Java知音公众号回复“面试题聚合”)

    8. 如果你的Serializable类包含一个不可序列化的成员,会发生什么?你是如何解决的?

    任何序列化该类的尝试都会因NotSerializableException而失败,但这可以通过在 Java中 为 static 设置瞬态(trancient)变量来轻松解决。

    Java 序列化相关的常见问题

    Java 序列化是一个重要概念, 但它很少用作持久性解决方案, 开发人员大多忽略了 Java 序列化 API。根据我的经验, Java 序列化在任何 Java核心内容面试中都是一个相当重要的话题, 在几乎所有的网面试中, 我都遇到过一两个 Java 序列化问题, 我看过一次面试, 在问几个关于序列化的问题之后候选人开始感到不自在, 因为缺乏这方面的经验。

    他们不知道如何在 Java 中序列化对象, 或者他们不熟悉任何 Java 示例来解释序列化, 忘记了诸如序列化在 Java 中如何工作, 什么是标记接口, 标记接口的目的是什么, 瞬态变量和可变变量之间的差异, 可序列化接口具有多少种方法, 在 Java 中,Serializable 和 Externalizable 有什么区别, 或者在引入注解之后, 为什么不用 @Serializable 注解或Serializalbe 接口。

     

    转载于:https://www.cnblogs.com/weirdo-lenovo/p/11418871.html

    展开全文
  • ICMP协议及报文类型含义

    千次阅读 2021-11-11 20:31:16
    ICMP是因特网控制报文协议的简称,它与IP协议同属于OSI结构的第三层网络层,用于传送有关通信问题的消息。例如,数据报不能到达目标站,路由器没有足够的缓存空间,或路由器向发送主机提供最短路径信息等。ICMP报文...
  • 挑战10个最难回答的Java面试题(附答案)

    万次阅读 多人点赞 2019-08-12 10:28:36
    译者:Yujiaao segmentfault.... ... 1.SpringBoot内容聚合 2.面试题内容聚合 ...3.设计模式内容聚合 ...这是我收集的10个最棘手的Java面试问题列表。这些问题主要来自 Java 核心部分 ,不涉及 Java EE 相关问题。...
  • 宿舍管理系统答辩问题总结

    千次阅读 2021-03-17 17:52:10
    3.Public与private的区别及功能答:private是完全私有的,只有在自己里面可以调用,在的外部和子类都不能调用,子类也不能继承父类的private的属性和方法。public对任何和成员都完全公开,无限制访问。4.@符号...
  • 什么是路径?

    千次阅读 2021-02-13 00:54:24
    问题我刚读这行:format()方法的第一件事是从名为output.vm的路径加载Velocity模板在这种情况下,我无法弄清楚classpath的含义。#1 热门回答(351 赞)在使用Java编程时,通过在源文件的顶部放置类似的内容,可以使...
  • 问卷调查设计以及敏感性问题调查

    千次阅读 2020-11-10 14:36:21
    4 敏感性问题调查的方式4.1 方式1:迂回式提问4.2 方式2:问题设置4.3 方式3:随机化回答技术4.3.1 沃纳模型4.3.2 西蒙斯模型5 参考 1 问卷调查的目的? 首先来看看问卷调查的定义,引自维基百科: 问卷调查是对目标...
  • 谈谈RNN的梯度消失/爆炸问题

    千次阅读 2020-11-30 14:06:37
    尽管 Transformer 的模型已经攻占了 NLP 的多数领域,但诸如 LSTM、GRU之的 RNN模型...关于此类问题,已有不少网友做出过回答,然而笔者查找了一些文章(包括知乎上的部分回答、专栏以及经典的英文博客),发现没..
  • 机器学习大致可分为三:监督学习、非监督学习、半监督学习,下面我们就来分别介绍。 监督学习 用数据挖掘领域著名学者韩家炜教授的话来说,所有的监督学习(Supervised Learning),基本上都是分类...
  • 两个对象是不是的相同实例,即用“===”是什么意思java中如何判定两个对象属于同一 两个对象是不是的相同实例,即用“===”是什么意思相关问题:匿名网友:首先,针对你的提问回答你的问题:可用instanceof判断...
  • 【转】去中心化的含义

    万次阅读 2019-05-13 18:55:54
    我认为,一个具有实践意义的“去中心化”概念回答的不是一个系统在功能提供方面的问题,而是一个免信任的系统与外在于这个系统的其它资源供应系统之间的关系问题。符合该定义的系统可预期拥有更多全节点,最终实现...
  • 选择器和ID选择器的区别

    万次阅读 多人点赞 2020-12-24 18:01:18
    的小女孩,上课从来不敢回答老师提出的问题,生怕回答错了老师会批评我。就一直没有这个 <span class="stress">勇气</span> 来回答老师提出的问题。 </p> 而下面代码是错误的: <p> 三年级...
  • 问题说我有一个像这样的代码:import java.util.Date;import my.own.Date;class Test{public static void main(String [] args){// I want to choose my.own.Date here. How?..// I want to choose util.Date here. ...
  • 1.UML简介Unified Modeling Language (UML)又称统一建模语言或...类图:显示和它们的相互关系。3.对象图:只显示对象及它们的相互关系。4.活动图:显示人或对象的活动,其方式类似于流程图。5.状态机图:显示生命周
  • 借汉诺塔理解栈与递归 单调栈 双端单调队列 单调队列优化的背包问题 01背包问题 完全背包问题 多重背包问题 串的定长表示 串的堆分配实现 KMP 一、引子 二、分析总结 三、基本操作 四、原理 五、复杂度分析 ...
  • 重新认识java(八) ---- 抽象与接口

    千次阅读 多人点赞 2017-02-10 18:40:14
    你很清楚的知道什么时候用抽象,什么时候用接口么? p.s. 多文字预警!
  • 最近因为要证明np问题,所以找了一系列概念去理解这4个问题。理解的时候看到好多人给出了不同的答案,我下面会借鉴别人的答案来总结出一份对于我自己来说,最容易理解这4个问题的说法。 预备知识了解: 这部分内容...
  • 使用决策树实现分类

    万次阅读 多人点赞 2016-12-29 21:56:12
    决策树是一种树形结构,为人们提供决策依据,决策树可以用来回答yes和no问题,它通过树形结构将各种情况组合都表示出来,每个分支表示一次选择(选择yes还是no),直到所有选择都进行完毕,最终给出正确答案。
  • Pytorch使用预训练模型进行图像分类

    千次阅读 多人点赞 2021-11-27 19:14:03
    模型比较 到目前为止,我们已经讨论了如何使用预先训练的模型来执行图像分类,但我们还没有回答的一个问题是,我们如何决定为特定的任务选择哪个模型。在本节中,我们将根据以下标准比较预训练模型: Top-1误差:如果...
  • 本科论文常见答辩问题整理

    千次阅读 多人点赞 2022-05-10 22:49:51
    比如填补什么空白、国内领先、很好、没有什么问题、最优之的字眼。回答问题时要谦虚、诚恳,态度端正。 下面总结了一下提问的问题给大家: 业务,功能模块,老师对某个模块需要详细了解,问你是怎么做的,其实就是...
  • 面试最后一问:你有什么问题想问我吗?

    万次阅读 多人点赞 2019-10-22 09:37:08
    那么如何回答好这类问题呢?今天分享一个万能的Github上的开源项目:reverse-interview,即:反向面试。 Github地址:https://github.com/viraptor/reverse-interview 这里记录了网友们整理的如何应对反向面试的N多...
  • Java加载机制与Tomcat加载器架构

    万次阅读 热门讨论 2017-02-26 10:58:11
    Java加载机制 加载器 虚拟机设计团队把加载阶段中的“通过一个的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的。实现这个动作的...
  • 文本分类入门(一)文本分类问题的定义 文本分类系列文章,从文本分类问题的定义开始,主要讲解文本分类系统的构成,主流的统计... 一个文本(以下基本不区分“文本”和“文档”两个词的含义)分类问题就是将一篇文档
  • 在本文中,我们分析了句子对建模的几种神经网络设计(及其变体), 并在八个数据集中比较它们的性能,包括释义识别、语义文本相似性、自然语言推理和问题回答任务。 尽管这些模型中的大多数都声称具有最先进的性能,但...
  • 全网最详细完备的class文件结构解析

    万次阅读 多人点赞 2021-05-21 23:54:03
    写在前面 本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和文献引用请见...4. 索引、父类索引与接口索引集合 5. 字段表
  • QQ IM 通讯软件开发实战

    万次阅读 2018-07-03 02:45:09
    但这种方式要求为每一个客户端连接都开辟一个单独的线程,在少量客户端连入的时候没有什么问题,但如果有成千上万的并发请求传入时,系统就要分配成千上万的线程来应对每一个连接,很显然,这种方案不能应对高并发。...
  • 目录 开放性的题目 1.自我介绍 2.平时是如何学习前端开发的 3.未来三到五年的规划是怎样的 4.说一下web产品开发的流程 ...问题:说一说你对Doctype的理解 ...问题:HTML新特性 ...问题:说一下webworker...问题:介绍一下
  • 教育心理学向我们揭示:幼儿的思维过程往往从问题开始。古语亦云:学起于思,思源于疑。有经验的教师在教学过程中,总是精心设计提问,竭力点燃幼儿思维的火花,激发他们的求知欲望,并有意识地为幼儿发现...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 56,405
精华内容 22,562
热门标签
关键字:

含义类的问题怎么回答