支持向量机(SVM)简介+SVM15种场景分类实例

  • Post author:
  • Post category:其他




支持向量机简介

支持向量机(SVM)是Vapnik和Corinna Cortes提出的,是基于优美的数学理论的一种机器学习算法。SVM是针对线性可分情景的广义线性回归模型,对于线性不可分的情况,则通过“核技巧”将低维特征空间的线性不可分样本映射到高维空间中,实现对非线性样本进行线性分析。SVM在小样本的情况下表现也不错,但是其计算代价较高,计算速度慢,且性能逐渐被新晋的树类集成算法和深度算法所领先。SVM目前在小样本、非线性、高维的应用场景中仍有一席之地,且其数学理论完备,非常值得学习。

SVM是一个二分类的分类模型,即寻找一个超平面将样本分为两类,若是多分类问题,则通过one vs rest构建多个二分类SVM形成多分类SVM。支持向量机用作回归(SVR)时,即学习得一个超平面函数f(x),使得f(x)尽量与标签y接近,而与传统回归模型不同的是,当f(x)与y的差值大于设定阈值时才开始计算损失。

SVM的重点在于超平面分割、核技巧映射、凸二次规划问题求解等等,推荐

这位大佬的理论推导

,非常详细。本人数学知识也是比较有限,就不在理论上班门弄斧了。



SVM15种场景分类实例

参考李飞飞在2005年发表的论文《A Bayesian Hierarchical Model for Learning Natural Scene Categories》中的bag of words思想,先通过对每张图进行SIFT+Kmeans聚类+直方图统计构建场景特征的特征库(词库),即每种场景都可由特征库中的特征表示,然后利用SVM对场景进行分类。

这是要用到的15类场景数据库:

在这里插入图片描述

主要流程如下:

数据预处理:

1、 读取15个类别共8970张图片建立总数据集

2、分层随机抽样划分训练集与测试集

Bag Of Word词库建立:

1、 对训练集与测试集中每张图进行SIFT特征提取,出于减少计算量的考虑,只计算通过固定步长进行网格采样获得的点的SIFT特征。

2、指定聚类中心即总单词个数后对训练集中的所有SIFT特征向量进行K-Means聚类

3、对训练集与测试集中每张图对应的SIFT特征向量分别进行直方图统计,对应生成直方图统计数据的训练集与测试集

SVM模型构建:

1、调用sklearn库中的SVM模型,在默认参数下换用不同的核函数进行训练,并计算其在测试集上的表现。

2、对上一步的模型在训练集上进行5折交叉验证的网格调参以提高模型的性能,并计算调参后的模型在测试集上的表现

sklearn库SVM中常用的核函数有三种:


kernel='linear'

用于线性可分情形,参数少,速度快


kernel='rbf'

主要用于线性不可分情形,参数多,需要调参


kernel='poly'

多项式核函数,可用于非线性分类

各SVM的主要调参对象是惩罚系数c,c越小,则允许更多错分样本。

接下来就上代码啦:

import cv2
import numpy as np
import matplotlib.pyplot as plt
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from sklearn import preprocessing
from sklearn.svm import LinearSVC
import copy
from sklearn.metrics import roc_auc_score,roc_curve,auc,accuracy_score,classification_report,confusion_matrix,f1_score
import itertools
from sklearn.metrics import confusion_matrix
import pandas as pd
from sklearn.svm import SVC
import glob
from sklearn.model_selection import cross_val_score
from sklearn import metrics
from sklearn.model_selection import GridSearchCV
class_names = [name[11:] for name in glob.glob('D:/Computer_Vision/scene15/*')] #读取目录下所有文件夹名
for i in range(len(class_names)):       #提取15个类别名
    class_names[i]=class_names[i].split('\\')[1]    
class_names = dict(zip(range(0,len(class_names)), class_names))   #对15种类别进行labelencoding
print (class_names)
def load_dataset(path, num_per_class=-1): #通过遍历读取每个文件夹中的图片,num_per_class设置每个类别中读取的图片数,默认全部读取
    data = []
    labels = []
    for id, class_name in class_names.items():  
        img_path_class = glob.glob(path + class_name + '/*.jpg')
        if num_per_class > 0:
            img_path_class = img_path_class[:num_per_class]
        labels.extend([id]*len(img_path_class))
        for filename in img_path_class:
            data.append(cv2.pyrDown(cv2.imread(filename), 0))
    return data, labels
X, y = load_dataset('D:/Computer_Vision/scene15/') #调用load_dataset()函数构建数据集及标签集
X_num= len(X)

train_data,test_data,train_label,test_label= train_test_split(X,y, test_size=0.2,#随机抽取划分训练集与测试集,stratify的设置保留了原数据集中的样本分布
                                                              shuffle=True, 
                                                              random_state=111, 
                                                              stratify=y)
def computeSIFT(data):
    x = []
    for i in range(0, len(data)):
        sift = cv2.xfeatures2d.SIFT_create()  #构建SIFT特征提取器
        img = data[i]
        step_size = 15  #设置固定步长进行网格采样
        kp = [cv2.KeyPoint(x, y, step_size) for x in range(0, img.shape[0], step_size) for y in range(0, img.shape[1], step_size)]
        dense_feat = sift.compute(img, kp)  #计算SIFT特征
        x.append(dense_feat[1])
        
    return x
x_train = computeSIFT(train_data) #对训练集和测试集分别计算SIFT特征
x_test = computeSIFT(test_data)
all_train_desc = []           #通过遍历展开训练集的list
for i in range(len(x_train)):
    for j in range(x_train[i].shape[0]):
        all_train_desc.append(x_train[i][j,:])
all_train_desc = np.array(all_train_desc)
def clusterFeatures(all_train_desc, k):#k表示聚类中心数即单词数
    kmeans = KMeans(n_clusters=k, random_state=0,n_jobs=2).fit(all_train_desc) #创建K-means模型,n_jobs指定并行内核数
    return kmeans
def formTrainingSetHistogram(x_train, kmeans, k): 
    train_hist = []
    for i in range(len(x_train)):
        data = copy.deepcopy(x_train[i])
        predict = kmeans.predict(data)
        train_hist.append(np.bincount(predict, minlength=k).reshape(1,-1).ravel()) #对每幅图的SIFT特征进行直方图统计
        
    return np.array(train_hist)
k = 50
kmeans = clusterFeatures(all_train_desc, k) #进行kmeans聚类
train_hist = formTrainingSetHistogram(x_train, kmeans, k)  #生成训练集和测试集的直方图集
test_hist = formTrainingSetHistogram(x_test, kmeans, k)
scaler = preprocessing.StandardScaler().fit(train_hist) #进行归一化
train_hist = scaler.transform(train_hist)
test_hist = scaler.transform(test_hist)
svm = sklearn.svm.SVC(kernel='linear',class_weight='balanced',probability=True) #使用线性支持向量机进行训练及预测
svm.fit(train_hist, train_label)
predict=svm.predict(test_hist)
print('准确率是:%s'%(accuracy_score(test_label,predict)))
print(classification_report(test_label,predict)) #输出其在测试集上的表现
print(confusion_matrix(test_label,predict))
###########  对linearsvm分类器进行网格调参  ############
param_test1 = {'C': np.arange(0.01, 1.0001, 0.01)   #设定网格搜寻范围        
        }
gsearch1 = GridSearchCV(estimator = SVC(kernel='linear',class_weight='balanced',probability=True),
                        param_grid = param_test1, scoring ="accuracy",cv=5,n_jobs=5,verbose=2) #cv指定交叉验证折数
gsearch1.fit(train_hist, train_label) 
print(gsearch1.best_params_)
print(gsearch1.best_score_)
print(gsearch1.best_estimator_)
svm_t = sklearn.svm.SVC(C=0.9400000000000001, break_ties=False, cache_size=200, #调参后线性支持向量机性能检验
    class_weight='balanced', coef0=0.0, decision_function_shape='ovr', degree=3,
    gamma='scale', kernel='linear', max_iter=-1, probability=True,
    random_state=None, shrinking=True, tol=0.001, verbose=False)
svm_t.fit(train_hist, train_label)
predict_t=svm_t.predict(test_hist)
print('准确率是:%s'%(accuracy_score(test_label,predict_t)))
print(classification_report(test_label,predict_t))
print(confusion_matrix(test_label,predict_t))
svm_rbf= sklearn.svm.SVC(kernel='rbf',class_weight='balanced',probability=True) #使用径向基支持向量机进行训练及预测
svm_rbf.fit(train_hist, train_label)
predict_rbf=svm_rbf.predict(pd.DataFrame(test_hist))
print('准确率是:%s'%(accuracy_score(test_label,predict_rbf)))
print(classification_report(test_label,predict_rbf)) #输出其在测试集上的表现
print(confusion_matrix(test_label,predict_rbf))
svm_poly= sklearn.svm.SVC(kernel='poly',class_weight='balanced',probability=True) #使用多项式支持向量机进行训练及预测
svm_poly.fit(train_hist, train_label)
predict_poly=svm_poly.predict(pd.DataFrame(test_hist))
print('准确率是:%s'%(accuracy_score(test_label,predict_poly)))
print(classification_report(test_label,predict_poly)) #输出其在测试集上的表现
print(confusion_matrix(test_label,predict_poly))
def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)
    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")
    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
cnf_matrix = confusion_matrix(np.array([test_label]).T, predict_poly)  #指定不同predict_poly更换不同混淆矩阵画图数据
np.set_printoptions(precision=2)
plt.figure(figsize=(18, 6))
plot_confusion_matrix(cnf_matrix, classes=class_names,
                      title='15 scene Confusion matrix')
plt.figure(figsize=(18, 6))
plot_confusion_matrix(cnf_matrix, classes=class_names, normalize=True,
                      title='15 scene Confusion matrix')
plt.show()

线性支持向量机默认参数在独立测试集上的各项指标如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

调参后的线性支持向量机性能有少量上升,在实验中可以观察到不同的核函数带来的差异也是不小的,同时,随着单词数的增加性能不断上升,在该应用场景中多项式支持向量机的准确度最高。

要想进一步提高SVM的准确度,可以从特征工程中进行改进,比如参考这篇论文中的空间金字塔特征构建方法,特征大幅度增加后SVM模型的性能也大幅度提高。

在这里插入图片描述

如果换用下其它机器学习模型(比如LightGBM),就会发现SVM的性能真的有点低了。



Reference


Spatial Pyramid Matching Scene Recognition

《深度学习原理与TensorFlow实践》 黄理灿



版权声明:本文为weixin_45575229原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。