支持向量机

2016/11/15 Python scikitlearn

支持向量机

支持向量机简称SVM,是Support Vector Machine的缩写。SVM是一种分类算法,在工业界和学术界都有广泛的应用。特别是针对数据集较小的情况下,往往其分类效果比神经网络好。

算法原理

SVM的最大特点是能构造出最大间距的决策边界,从而提高分类算法的鲁棒性。

大间距分类算法

假设要对一个数据集进行分类,可以构造一个分隔线把圆形的点和方形的点分开。这个分隔线称为分隔超平面(Separating hyperplane)。

实线的分隔线比虚线的分隔线更好,因为使用实线的分隔线进行分类时,离分隔线最近的点到分隔线上的距离更大,即margin2>margin1。这段距离的两倍,称为间距(margin)。那些离分隔超平面最近的点,称为支持向量(support vector)。为了达到最好的分类效果,SVM的算法原理就是要找到一个分隔超平面,它能把数据集正确地分类,并且间距最大。

乳腺癌检测

本章使用支持向量机来解决这个问题。首先,我们载入数据:

# 载入数据
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
 
cancer = load_breast_cancer()
X = cancer.data
y = cancer.target
print('data shape: {0}; no. positive: {1}; no. negative: {2}'.format(
    X.shape, y[y==1].shape[0], y[y==0].shape[0]))
 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

输出如下:

data shape: (569, 30); no. positive: 357; no. negative: 212

可以看出,我们的数据集很小。高斯核函数太复杂,容易造成过拟合,模型效果应该不会很好。我们先用高斯核函数试一下看与我们猜测的是否一致:

from sklearn.svm import SVC
 
clf = SVC(C=1.0, kernel='rbf', gamma=0.1)
clf.fit(X_train, y_train)
train_score = clf.score(X_train, y_train)
test_score = clf.score(X_test, y_test)
print('train score: {0}; test score: {1}'.format(train_score, test_score))

k-均值算法

k-均值算法是一种典型的无监督机器学习算法,用来解决聚类问题(Clustering)。这是一个无监督的学习算法。但这并不意味着无监督机器学习不重要。相反,由于数据标记需要耗费巨大的资源,无监督或者半监督的学习算法近来逐渐受到学者青睐,原因是不需要对数据进行标记,可以大大减少工作量。

算法原理

读者需要注意聚类问题和分类问题的区别。针对监督式学习算法,如k-近邻算法,其输入数据是已经标记了的(x(1),y(1)),(x(2),y(2))…,(x(m),y(m)),目标是找出分类边界,然后对新的数据进行分类。而无监督式学习算法,如k-均值算法,只给出一组无标记的数据集x(1),x(2),…,x(m),目标是找出这组数据的模式特征,如哪些数据是同一种类型的,哪些数据是另外一种类型。

典型的无监督式学习包括市场细分,即通过分析用户数据,把一个产品的市场进行细分,找出细分人群。另外一个是社交网络分析,分析社交网络中参与人员的不同特点,根据特点区分出不同群体。这些都是无监督式学习里的聚类(Clustering)问题。

k-均值算法算法包含以下两个步骤。

  • 给聚类中心分配点。计算所有的训练样例,把每个训练样例分配到距离其最近的聚类中心所在的类别里。
  • 移动聚类中心。新的聚类中心移动到这个聚类所有的点的平均值处。

一直重复做上面的动作,直到聚类中心不再移动为止,这时就探索出了数据集的结构了。

我们也可以用数学方法来描述k-均值算法。算法有两个输入信息,一是k,表示选取的聚类个数;另一个是训练数据集x(1),x(2),…,x(m)。

  • 随机选择k个聚类中心u1,u2,…,uk。
  • 从1~m中遍历所有的数据集,计算x(i)分别到u1,u2,…,uk的距离,记录距离最短的聚类中心点uj(1≤j≤k),然后把x(i)这个点分配给这个聚类。即令c(i)=j。计算距离时,一般使用   x(i)-uj   来计算。
  • 从1~k中遍历所有的聚类中心,移动聚类中心的新位置到这个聚类的均值处。即其中c表示分配给这个聚类的训练样例点的个数,x(d)表示属于uj这个类别的点。
  • 重复步骤(2),直到聚类中心不再移动为止。

scikit-learn里的k-均值算法

scikit-learn里的k-均值算法由sklearn.cluster.KMeans类实现。下面通过一个简单的例子,来学习怎样在scikit-learn里使用k-均值算法。

我们先生成一组包含两个特征的200个样本:

from sklearn.datasets import make_blobs
 
X, y = make_blobs(n_samples=200,
                  n_features=2,
                  centers=4,
                  cluster_std=1,
                  center_box=(-10.0, 10.0),
                  shuffle=True,
                  random_state=1);

然后把样本画在二维坐标上,以便直观地观察:

plt.figure(figsize=(6,4), dpi=144)
plt.xticks(())
plt.yticks(())
plt.scatter(X[:, 0], X[:, 1], s=20, marker='o');

接着使用KMeans模型来拟合。我们设置类别数量为3,并计算出其拟合后的成本:

from sklearn.cluster import KMeans
 
n_clusters = 3
kmean = KMeans(n_clusters=n_clusters)
kmean.fit(X);
print("kmean: k={}, cost={}".format(n_clusters, int(kmean.score(X))))

KMeans.score()函数计算k-均值算法拟合后的成本,用负数表示,其绝对值越大,说明成本越高。前面介绍过,k-均值算法成本的物理意义为训练样例到其所属的聚类中心点的距离的平均值,在scikit-learn里,其计算成本的方法略有不同,它是计算训练样例到其所属的聚类中心点的距离的总和。

当然,我们还可以把分类后的样本及其所属的聚类中心都画出来,这样可以更直观地观察算法的拟合结果:

labels = kmean.labels_
centers = kmean.cluster_centers_
markers = ['o', '^', '*']
colors = ['r', 'b', 'y']
 
plt.figure(figsize=(6,4), dpi=144)
plt.xticks(())
plt.yticks(())
 
# 画样本
for c in range(n_clusters):
    cluster = X[labels == c]
    plt.scatter(cluster[:, 0], cluster[:, 1],
                marker=markers[c], s=20, c=colors[c])
# 画出中心点
plt.scatter(centers[:, 0], centers[:, 1],
            marker='o', c="white", alpha=0.9, s=300)
for i, c in enumerate(centers):
    plt.scatter(c[0], c[1], marker='$%d$' % i, s=50, c=colors[i])

前面说过,k-均值算法的一个关键参数是k,即聚类个数。从技术角度来讲,k值越大,算法成本越低,这个很容易理解。但从业务角度来看,不是k值越大越好。针对本节的例子,分别选择k=2,3,4这3种不同的聚类个数,来观察一下k-均值算法最终拟合的结果及其成本值。

我们可以把画出k-均值聚类结果的代码稍微改造一下,变成一个函数。这个函数会使用k-均值算法来进行聚类拟合,同时会画出按照这个聚类个数拟合后的分类情况:

def fit_plot_kmean_model(n_clusters, X):
    plt.xticks(())
    plt.yticks(())
 
    # 使用 k-均值算法进行拟合
    kmean = KMeans(n_clusters=n_clusters)
    kmean.fit_predict(X)
 
    labels = kmean.labels_
    centers = kmean.cluster_centers_
    markers = ['o', '^', '*', 's']
    colors = ['r', 'b', 'y', 'k']
 
    # 计算成本
    score = kmean.score(X)
    plt.title("k={}, score={}".format(n_clusters, (int)(score)))
 
    # 画样本
    for c in range(n_clusters):
        cluster = X[labels == c]
        plt.scatter(cluster[:, 0], cluster[:, 1],
                    marker=markers[c], s=20, c=colors[c])
    # 画出中心点
    plt.scatter(centers[:, 0], centers[:, 1],
                marker='o', c="white", alpha=0.9, s=300)
    for i, c in enumerate(centers):
        plt.scatter(c[0], c[1], marker='$%d$' % i, s=50, c=colors[i])

函数代码略微有点长,但通过注释应该不难理解函数的意图。函数接受两个参数,一个是聚类个数,即k的值,另一个是数据样本。有了这个函数,接下来的代码就简单了,可以很容易地分别对[2,3,4]3种不同的k值情况进行聚类分析,并把聚类结果可视化。

from sklearn.cluster import KMeans
 
n_clusters = [2, 3, 4]
 
plt.figure(figsize=(10, 3), dpi=144)
for i, c in enumerate(n_clusters):
    plt.subplot(1, 3, i + 1)
    fit_plot_kmean_model(c, X)

Search

    微信好友

    博士的沙漏

    Table of Contents