机器学习:Python测试线性可分性的方法

线性和非线性分类 两个子集是线性可分的,如果存在一个超平面将每组的元素的所有元素的一组驻留在另一侧的超平面其他设置。我们可以描述它在2D绘图中通过分离线,并且在3D绘图通过一个超平面。

根据定义,线性可分性定义为:如果存在

,

,那么两组集合

是线性可分的。

简而言之,如果存在一个超平面完全分离H元素和M元素,那么上面的表达式表示H和M是线

性可分的。

图片来源:Sebastian Raschka 2

在上图中,A显示了一个线性分类问题,B显示了一个非线性的分类问题。在A中,我们的决策边界是一个线性的,它将蓝色的点和绿色的点完全分开。在这个场景中,可以实现几个线性分类器。

在B中,我们的决策边界是非线性的,我们将使用非线性的核函数和其他非线性的分类算法和技术。

一般来说,在机器学习中,在运行任何类型的分类器之前,理解我们要处理的数据是很重要的,以确定应该从哪一种算法开始,以及我们需要调整哪些参数来适应任务。如果我们的问题是线性的或者非线性的,这就会给我们带来线性可分性和理解的问题。

如上所述,有几种分类算法是通过构造一个线性决策边界(超平面)分类来分离这些数据的,而这样做的假设是:数据是线性可分的。然而,在实际操作中,事情并非那么简单,许多情况下的数据可能不是线性可分的,因此应用了非线性技术。线性和非线性技术的决策是基于数据科学家所知道的最终目标,他们愿意接受的错误,平衡模型复杂性和泛化,偏见方差权衡等等。

这篇文章的灵感来自于线性可分性问题的研究论文,论文地址如下:

1.《线性可分性问题:一些测试方法》: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.121.6481&rep=rep1&type=pdf

2.《线性可分性测试的一个简单算法》:http://mllab.csa.iisc.ernet.in/downloads/Labtalk/talk30_01_08/lin_sep_test.pdf

本文的目标是在Python中应用和测试少数技术,并演示如何实现它们。测试线性可分性的一些技术是:

  • 领域和专业知识
  • 数据可视化
  • 计算几何学(凸包)
  • 机器学习:
    • 感知器
    • 支持向量机

领域和专业知识

这应该是显而易见的,第一步应该是寻求分析师和其他已经熟悉数据的数据科学家的见解。在开始任何数据发现之旅前学会先问问题,以便更好地理解任务的目的(你的目标),并在早期获得来自领域专家的见解。

获得数据 对于上面列出的其他三种方法,我们将使用传统的Iris数据集(鸢尾花数据集)来探索这些概念,并使用Python实现线性可分测试的一些理论。

  • Iris数据集:https://archive.ics.uci.edu/ml/datasets/iris

因为这是一个众所周知的数据集,我们预先知道哪些类是线性可分的。对于我们的分析,我们将使用这些已知的知识来证实我们的发现。

Iris以鸢尾花的特征作为数据来源,数据集包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性,是在数据挖掘、数据分类中非常常用的测试集、训练集。

首先,导入必要的库并加载数据。

import numpy as np 
import pandas as pd 
import matplotlib.pyplot as plt 

from sklearn import datasets 
data = datasets.load_iris() 

#create a DataFrame 
df = pd.DataFrame(data.data, columns=data.feature_names) 
df['Target'] = pd.DataFrame(data.target) 
df.head()

数据可视化

最简单和最快速的方法是可视化数据。如果特性(feature)的数量很大,那么这种方法可能是不可行的,或者说太过直接了,因此很难在2D中绘图。在这种情况下,我们可以使用Pair Plot方法,并且Pandas库为我们使用scatter_matrix提供了一个很好的选项:

from pandas.tools.plotting import scatter_matrix
scatter_matrix(df.iloc[:,0:4], figsize=(15,11))

上面的散布矩阵是数据集中所有特性的一个成对的散点图(我们有4个特征,所以我们得到一个4×4矩阵)。散布矩阵提供了关于这些变量是如何相互关联的见解。让我们通过创建一个散布矩阵中花瓣的长度(Petal Length)和花瓣宽度(Petal Width)的散点图来展开这一扩展。

plt.clf()
plt.figure(figsize=(10,6))
plt.scatter(df.iloc[:,2], df.iloc[:,3])
plt.title('Petal Width vs Petal Length')
plt.xlabel(data.feature_names[2])
plt.ylabel(data.feature_names[3])
plt.show()

它仍然没有那么有用。让我们给每个类着色,并添加一个图例,这样我们就能理解图中每个类的数据分布,并确定这些类是否可以被线性分离。

让我们更新我们的代码:

plt.clf()
plt.figure(figsize = (10, 6))
names = data.target_names
colors = ['b','r','g']
label = (data.target).astype(np.int)
plt.title('Petal Width vs Petal Length')
plt.xlabel(data.feature_names[2])
plt.ylabel(data.feature_names[3])
for i in range(len(names)):
 bucket = df[df['Target'] == i]
 bucket = bucket.iloc[:,[2,3]].values
 plt.scatter(bucket[:, 0], bucket[:, 1], label=names[i]) 
plt.legend()
plt.show()

看上去好多了。我们只是绘制了整个数据集,所有150个点。每个类有50个数据点。是的,乍一看,我们可以看到蓝色的点(Setosa类)可以很容易地通过画一条线来分隔,并将其与其他类隔离开来。但是其他两个类呢?

让我们检查另一种更确定的方法。

计算几何学

在这种方法中,我们将使用凸包(Convex Hull)来检查一个特定的类是否是线性可分的。简而言之,凸包代表了一组数据点(类)的外边界,这就是为什么有时它被称为凸包。

当测试线性可分性时使用凸包的逻辑是相当直接的,可以这样说:

如果X和Y的凸包的交点是空的,那么两个类X和Y是线性可分的。

一种快速的方法来查看它是如何工作的,就是将每个类的凸包的数据点可视化。我们将绘制凸包边界,以直观地检查交点。我们将使用Scipy库来帮助我们计算凸包。更多信息请参阅下方Scipy文档地址。

  • 地址:https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.ConvexHull.html

让我们更新前面的代码,以包含凸包。

from scipy.spatial import ConvexHull
 
plt.clf()
plt.figure(figsize = (10, 6))
names = data.target_names
label = (data.target).astype(np.int)
colors = ['b','r','g']
plt.title('Petal Width vs Petal Length')
plt.xlabel(data.feature_names[2])
plt.ylabel(data.feature_names[3])
for i in range(len(names)):
 bucket = df[df['Target'] == i]
 bucket = bucket.iloc[:,[2,3]].values
 hull = ConvexHull(bucket)
 plt.scatter(bucket[:, 0], bucket[:, 1], label=names[i]) 
 for j in hull.simplices:
 plt.plot(bucket[j,0], bucket[j,1], colors[i])
plt.legend()
plt.show()

我们的输出看起来像这样:

至少从直观上看,Setosa是一个线性可分的类。换句话说,我们可以很容易地画出一条直线,将Setosa类与非Setosa类分开。Versicolor类和Versicolor类都不是线性可分的,因为我们可以看到它们两个之间确实有一个交集。

机器学习

在本节中,我们将研究两个分类器,用于测试线性可分性:感知器(最简单的神经网络)和支持向量机(称为核方法的一部分)。

单层感知器 感知器(perceptron)是一种用于二进制分类的算法,属于一类线性分类器。在二进制分类中,我们尝试将数据分成两个Bucket:要么是在Buket A或 Bucket B中,或是更简单的:要么是在Bucket A中,要么不在Bucket A中(假设我们只有两个类),因此命名为二进制分类。

只有当输入向量是线性可分的时,一个单层感知器才会收敛。在这个状态下,所有输入向量都将被正确地分类,表示线性可分性。如果它们不是线性可分的,它就不会收敛。换句话说,如果数据集不是线性可分的,它就不能正确地分类。对于我们的测试目的,这正是我们所需要的。

我们将把它应用在整个数据上,而不是将它分割成测试/训练,因为我们的目的是测试类之间的线性可分性,而不是为将来的预测建立模型。

我们将使用Scikit-Learn并选择感知器作为我们的线性模型选择。在此之前,让我们做一些基本的数据预处理任务:

# Data Preprocessing
x = df.iloc[:, [2,3]].values
# we are picking Setosa to be 1 and all other classes will be 0
y = (data.target == 0).astype(np.int) 
 
#Perform feature scaling
from sklearn.preprocessing import StandardScaler
sc= StandardScaler()
x = sc.fit_transform(x)

现在,让我们来构建我们的分类器:

from sklearn.linear_model import Perceptron
perceptron = Perceptron(random_state = 0)
perceptron.fit(x, y)
predicted = perceptron.predict(x)

为了更好地理解结果,我们将绘制混淆矩阵和决策边界。

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y, predicted)
 
plt.clf() 
plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Wistia)
classNames = ['Negative','Positive']
plt.title('Perceptron Confusion Matrix - Entire Data')
plt.ylabel('True label')
plt.xlabel('Predicted label')
tick_marks = np.arange(len(classNames))
plt.xticks(tick_marks, classNames, rotation=45)
plt.yticks(tick_marks, classNames)
s = [['TN','FP'], ['FN', 'TP']]
 
for i in range(2):
 for j in range(2):
 plt.text(j,i, str(s[i][j])+" = "+str(cm[i][j]))
plt.show()

现在,让我们绘制出决策边界:

from matplotlib.colors import ListedColormap
plt.clf()
X_set, y_set = x, y
X1, X2 = np.meshgrid(np.arange(start = X_set[:, 0].min() - 1, stop = X_set[:, 
0].max() + 1, step = 0.01),
 np.arange(start = X_set[:, 1].min() - 1, stop = X_set[:, 1].max() + 1, step =
 0.01))
plt.contourf(X1, X2, perceptron.predict(np.array([X1.ravel(), X2.ravel()]).T).
 reshape(X1.shape),
 alpha = 0.75, cmap = ListedColormap(('navajowhite', 'darkkhaki')))
plt.xlim(X1.min(), X1.max())
plt.ylim(X2.min(), X2.max())
for i, j in enumerate(np.unique(y_set)):
 plt.scatter(X_set[y_set == j, 0], X_set[y_set == j, 1],
 c = ListedColormap(('red', 'green'))(i), label = j)
plt.title('Perceptron Classifier (Decision boundary for Setosa vs the rest)')
plt.xlabel('Petal Length')
plt.ylabel('Petal Width')
plt.legend()
plt.show()

我们可以看到,我们的感知器确实收敛了,并能够将Setosa从非Setosa中区分出来,因为数据是线性可分的。如果数据不是线性可分的,情况就不一样了。让我们在另一个类上试一下。

以下是Versicolor类(试图将Versicolor与剩下的类分开):

支持向量机 现在,让我们用一个核函数的支持向量机来检查另一种方法。为了测试线性可分性,我们将选择一个核函数的硬间隔(hard-margin)(最大距离,反义词为soft-margin)支持向量机。现在,如果我们的目的是训练一个模型,我们的选择将会完全不同。但是,由于我们正在测试线性可分性,所以我们想要一个能够失败的严格的测试(或者如果不收敛的话就会产生错误的结果)来帮助我们更好地评估手头的数据。

图片来源:维基百科

现在,让我们编码:

from sklearn.svm import SVC
svm = SVC(C=1.0, kernel='linear', random_state=0)
svm.fit(x, y)
 
predicted = svm.predict(x)
 
cm = confusion_matrix(y, predicted)
 
plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Wistia)
classNames = ['Negative','Positive']
plt.title('SVM Linear Kernel Confusion Matrix - Setosa')
plt.ylabel('True label')
plt.xlabel('Predicted label')
tick_marks = np.arange(len(classNames))
plt.xticks(tick_marks, classNames, rotation=45)
plt.yticks(tick_marks, classNames)
s = [['TN','FP'], ['FN', 'TP']]
 
for i in range(2):
 for j in range(2):
 plt.text(j,i, str(s[i][j])+" = "+str(cm[i][j]))

下面是混淆矩阵和决策边界的图:

完美的分离/分类指示一个线性可分性。

现在,让我们来测试一下,对Versicolor类进行测试,我们得到了下面的绘图。有趣的是,我们没有看到一个决策边界,而混淆矩阵表示分类器的工作完成得并不好。

现在,为了好玩,并且可以演示出支持向量机的强大功能,让我们应用一个非线性的内核。在这种情况下,我们将应用一个径向基函数(RBF Kernel)对上面的代码稍微改动一下,得到了完全不同的结果:

x = df.iloc[:, [2,3]].values
y = (data.target == 1).astype(np.int) # we are picking Versicolor to be 1 
and all other classes will be 0
 
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
x = sc.fit_transform(x)
 
from sklearn.svm import SVC
svm = SVC(kernel='rbf', random_state=0)
svm.fit(x, y)
 
predicted = svm.predict(x)
 
cm = confusion_matrix(y, predicted)
 
plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Wistia)
classNames = ['Negative','Positive']
plt.title('SVM RBF Confusion Matrix - Versicolor')
plt.ylabel('True label')
plt.xlabel('Predicted label')
tick_marks = np.arange(len(classNames))
plt.xticks(tick_marks, classNames, rotation=45)
plt.yticks(tick_marks, classNames)
s = [['TN','FP'], ['FN', 'TP']]
 
for i in range(2):
 for j in range(2):
 plt.text(j,i, str(s[i][j])+" = "+str(cm[i][j]))
  • Python源代码:https://github.com/tatwan/Linear-Separability/blob/master/linear_separability.py

原文发布于微信公众号 - ATYUN订阅号(atyun_com)

原文发表时间:2018-01-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ATYUN订阅号

使用Keras建立Wide & Deep神经网络,通过描述预测葡萄酒价格

你能通过“优雅的单宁香”、“成熟的黑醋栗香气”或“浓郁的酒香”这样的描述,预测葡萄酒的价格吗?事实证明,机器学习模型可以。

1774
来自专栏杨熹的专栏

深度学习与自然语言处理 主要概念一览CS224d-Day 1:

---- CS224d-Day 1: 要开始系统地学习 NLP 课程 cs224d,今天先来一个课程概览。 课程一共有16节,先对每一节中提到的模型,算法,工...

35211
来自专栏人工智能LeadAI

BAT机器学习面试1000题系列(第1~75题)

今17年,近期和团队整理BAT机器学习面试1000题系列,侧重机器学习、深度学习。我们将通过这个系列索引绝大部分机器学习和深度学习的笔试面试题、知识点,它将更是...

5785
来自专栏图形学与OpenGL

模拟试题B

1.灰度等级为256级,分辨率为2048*1024的显示器,至少需要的帧缓存容量为( )

1631
来自专栏AI科技评论

想了解递归神经网络?这里有一份入门教程

本文来自deeplearning4j,AI科技评论编辑。 递归神经网络是一类人工神经网络,用于识别诸如文本、基因组、手写字迹、语音等序列数据的模式,或用于识别传...

2983
来自专栏杨熹的专栏

凸优化有什么用

本文结构: 凸优化有什么用? 什么是凸优化? ---- 凸优化有什么用? 鉴于本文中公式比较多,先把凸优化的意义写出来吧,就会对它更有兴趣。 我们知道在机器学习...

3798
来自专栏机器之心

爆款论文提出简单循环单元SRU:像CNN一样快速训练RNN(附开源代码)

选自arXiv 机器之心编译 机器之心编辑部 近日,一篇题为《Training RNNs as Fast as CNNs》的 arXiv 论文通过有意简化状...

33611
来自专栏PPV课数据科学社区

机器学习系列:(五)决策树——非线性回归与分类

决策树——非线性回归与分类 前面几章,我们介绍的模型都是广义线性模型,基本方法都是通过联接方程构建解释变量与若干响应变量的关联关系。我们用多元线性回归解决回归问...

3316
来自专栏深度学习自然语言处理

word2vec理论与实践

导读 本文简单的介绍了Google 于 2013 年开源推出的一个用于获取 word vector 的工具包(word2vec),并且简单的介绍了其中的两个训练...

2836
来自专栏机器之心

终于,Geoffrey Hinton那篇备受关注的Capsule论文公开了

27310

扫码关注云+社区