支持向量机

间隔、对偶、核技巧

Posted by 新宇 on March 3, 2020

一、原理介绍

1. 算法简介

SVM全称是supported vector machine(支持向量机),即寻找到一个超平面使样本分成两类,并且间隔最大。

2. 超平面最大间隔介绍

3. 硬间隔和软间隔

3.1 硬间隔

  • 严格地让所有实例都不在最大间隔之间,并且位于正确的一边,这就是硬间隔分类。
  • 硬间隔分类有两个问题:
    • 首先,它只在数据是线性可分离的时候才有效;
    • 其次,它对异常值非常敏感。

3.2 软间隔

  • 尽可能在保持最大间隔宽阔和限制间隔违例(即位于最大间隔之上,甚至在错误的一边的 实例)之间找到良好的平衡,这就是软间隔分类。
  • 要避免这些问题,最好使用更灵活的模型。
  • 目标是尽可能在保持间隔宽阔和限制间隔违例之间找到良好的平衡,这就是软间隔分类。
  • 超参数C来控制这个平衡:
    • C值越小,则间隔越宽,但是间隔违例也会越多。

二、算法原理

1. 推导目标函数

2. 拉格朗日乘子法

2.1 约束优化问题

2.2 Slater

2.3 KKT

三、核函数

1. 核函数

通过核函数能够解决在线性不可分场景下的分类,将原样本通过核函数映射到高维空间,即可更加容易划分;

2. 常见的核函数

3. 核函数的使用原则

  • 一般有如下指导规则:
    • 1) 如果Feature的数量很大,甚至和样本数量差不多时,往往线性可分,这时选用Sigmoid或者Linear线性核;
    • 2) 如果Feature的数量很小,样本数量正常,不算多也不算少,这时选用RBF核;
    • 3) 如果Feature的数量很小,而样本的数量很大,这时手动添加一些Feature,使得线性可分,然后选用Sigmoid或者Linear线性核;
    • 4) 多项式核一般很少使用,效率不高,结果也不优于RBF;
    • 5) Linear核参数少,速度快;RBF核参数多,分类结果非常依赖于参数,需要交叉验证或网格搜索最佳参数,比较耗时; 6)应用最广的应该就是RBF核,无论是小样本还是大样本,高维还是低维等情况,RBF核函数均适用。

四、SVM回归

五、SVM案例

1. API介绍

  • sklearn中支持向量分类主要有三种方法:SVC、NuSVC、LinearSVC,扩展为三个支持向量回归方法:SVR、NuSVR、LinearSVR。
    • SVC和NuSVC方法基本一致,唯一区别就是损失函数的度量方式不同
    • NuSVC中的nu参数和SVC中的C参数;
    • LinearSVC是实现线性核函数的支持向量分类,没有kernel参数。

1.1 SVC

  • class sklearn.svm.SVC(C=1.0, kernel=’rbf’, degree=3,coef0=0.0,random_state=None)
    • C: 惩罚系数,用来控制损失函数的惩罚系数,类似于线性回归中的正则化系数。
      • C越大,相当于惩罚松弛变量,希望松弛变量接近0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样会出现训练集测试时准确率很高,但泛化能力弱,容易导致过拟合。
      • C值小,对误分类的惩罚减小,容错能力增强,泛化能力较强,但也可能欠拟合。
  • kernel:算法中采用的核函数类型,核函数是用来将非线性问题转化为线性问题的一种方法。
    • 参数选择有RBF, Linear, Poly, Sigmoid或者自定义一个核函数。
    • 默认的是”RBF”,即径向基核,也就是高斯核函数;
    • 而Linear指的是线性核函数,
    • Poly指的是多项式核,
    • Sigmoid指的是双曲正切函数tanh核;。
  • degree:
    • 当指定kernel为’poly’时,表示选择的多项式的最高次数,默认为三次多项式;
    • 若指定kernel不是’poly’,则忽略,即该参数只对’poly’有用。
      • 多项式核函数是将低维的输入空间映射到高维的特征空间。
  • coef0: 核函数常数值(y=kx+b中的b值),
    • 只有‘poly’和‘sigmoid’核函数有,默认值是0。

1.2 NuSVC

  • class sklearn.svm.NuSVC(nu=0.5)
    • nu: 训练误差部分的上限和支持向量部分的下限,取值在(0,1)之间,默认是0.5

1.3 LinearSVC

  • class sklearn.svm.LinearSVC(penalty=’l2’, loss=’squared_hinge’, dual=True, C=1.0)
    • penalty:正则化参数, L1和L2两种参数可选,仅LinearSVC有。
    • loss:损失函数,
      • 有hinge和squared_hinge两种可选,前者又称L1损失,后者称为L2损失,默认是squared_hinge,
      • 其中hinge是SVM的标准损失,squared_hinge是hinge的平方
    • dual:是否转化为对偶问题求解,默认是True。
    • C:惩罚系数, 用来控制损失函数的惩罚系数,类似于线性回归中的正则化系数

六、数字识别案例

1. 案例背景介绍

2. 数据介绍

数据文件train.csv和test.csv包含从0到9的手绘数字的灰度图像。 每个图像的高度为28个像素,宽度为28个像素,总共为784个像素。 每个像素具有与其相关联的单个像素值,指示该像素的亮度或暗度,较高的数字意味着较暗。该像素值是0到255之间的整数,包括0和255。 训练数据集(train.csv)有785列。第一列称为“标签”,是用户绘制的数字。其余列包含关联图像的像素值。 训练集中的每个像素列都具有像pixelx这样的名称,其中x是0到783之间的整数,包括0和783。为了在图像上定位该像素,假设我们已经将x分 解为x = i * 28 + j,其中i和j是0到27之间的整数,包括0和27。然后,pixelx位于28 x 28矩阵的第i行和第j列上(索引为零)。

3. 代码实现

#!/usr/bin/env python
# coding: utf-8

# In[1]:


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.model_selection import train_test_split


# # 获取数据

# In[2]:


train = pd.read_csv("./data/train.csv")


# In[4]:


train.head()


# In[6]:


train.shape


# ## 确定特征值\目标值

# In[7]:


train_image = train.ix[:, 1:]


# In[8]:


train_image.head()


# In[9]:


train_label = train.ix[:, 0]


# In[10]:


train_label.head()


# ## 查看具体图像

# In[14]:


num = train_image.ix[0,].values.reshape(28, 28)


# In[15]:


plt.imshow(num)
plt.axis("off")
plt.show()


# In[16]:


def to_plot(n):
    num = train_image.ix[n,].values.reshape(28, 28)
    
    plt.imshow(num)
    plt.axis("off")
    plt.show()


# In[19]:


to_plot(n=40)


# # 数据基本处理

# In[22]:


train_image.head()


# ## 数据归一化处理

# In[23]:


# 对数据特征值归一化处理
train_image = train_image.values / 255


# In[26]:


train_label = train_label.values


# ## 数据集分割

# In[28]:


x_train, x_val, y_train, y_val = train_test_split(train_image, train_label, train_size = 0.8, random_state=0)


# In[30]:


print(x_train.shape, x_val.shape)


# # 特征降维和模型训练

# In[32]:


import time
from sklearn.decomposition import PCA

# 多次使用pca,确定最后的最优模型

def n_components_analysis(n, x_train, y_train, x_val, y_val):
    # 记录开始时间
    start = time.time()
    
    # pca降维实现
    pca = PCA(n_components=n)
    print("特征降维,传递的参数为:{}".format(n))
    pca.fit(x_train)
    
    # 在训练集和测试集进行降维
    x_train_pca = pca.transform(x_train)
    x_val_pca = pca.transform(x_val)
    
    # 利用svc进行训练
    print("开始使用svc进行训练")
    ss = svm.SVC()
    ss.fit(x_train_pca, y_train)
    
    # 获取accuracy结果
    accuracy = ss.score(x_val_pca, y_val)
    
    # 记录结束时间
    end = time.time()
    print("准确率是:{}, 消耗时间是:{}s".format(accuracy, int(end-start)))
    
    return accuracy 


# In[34]:


# 传递多个n_components,寻找合理的n_components:

n_s = np.linspace(0.70, 0.85, num=5)
accuracy = []

for n in n_s:
    tmp = n_components_analysis(n, x_train, y_train, x_val, y_val)
    accuracy.append(tmp)


# In[35]:


# 准确率可视化展示
plt.plot(n_s, np.array(accuracy), "r")
plt.show()


# 经过图形展示,选择合理的n_components, 最后综合考虑确定结果为:0.80

# # 确定最优模型

# In[36]:


pca = PCA(n_components=0.80)

pca.fit(x_train)
pca.n_components_


# In[37]:


x_train_pca = pca.transform(x_train)
x_val_pca = pca.transform(x_val)


# In[39]:


print(x_train_pca.shape, x_val_pca.shape)


# In[40]:


# 训练比较优的模型,计算accuracy

ss1 = svm.SVC()

ss1.fit(x_train_pca, y_train)

ss1.score(x_val_pca, y_val)


# In[ ]: