半监督学习算法(3)-- 半监督支持向量机

判别式方法(discriminative model)试图寻找不同类别之间的最优分类面,反映异类数据之间的差异。

支持向量机

假设我们要用分界线把红蓝两色小球分开,那么分界线的最佳位置就是让两边有尽可能大的间隙。这个间隙就是样本到分界线的距离。

但是遇到下面这样的数据怎么办呢?

我们可以像所有武侠片中一样大侠桌子一拍,让球飞到空中。然后抓起一张纸,插到两种球的中间。

这样,从空中的角度看这些球,这些球看起来像是被一条曲线分开了。

这就是支持向量机(Support Vector Machines,SVM)的原理,它是一种用于分类的算法。它要干的事就是找到一个分界面, 放到不同类别之间,就把它们分开了,如二维上,是一条直线,三维上就一个平面,四维上就是一个三维的东西了。如果在低维不可分时,就先通过核函数把低维数据映射到高维,高维上变成线性可分的以后,然后呢,在高维上再用支持向量机进行分就可以了。
我们把这些球叫做data,找到最大间隙的trick叫做optimization(优化器),拍桌子叫做kernelling(核函数), 那张纸叫做hyperplane(超平面)。

半监督支持向量机

半监督支持向量机(Semi-Supervised Support Vector Machine,简称 S3VM)是支持向量机在半监督学习上的推广。在不考虑未标记样本时,支持向量机试图找到最大间隔划分超平面,而在考虑未标记样本后,S3VM试图找到能将两类有标记样本分开,且穿过数据低密度区域的划分超平面。如下图所示,这里的基本假设是"低密度分隔"(low-density separation)

半监督支持向量机中最著名的是TSVM(Transductive Support Vector Machine)。TSVM试图考虑对未标记样本进行各种可能的标记指派,然后从中找出在所有标记和未标记样本上间隔最大化的划分超平面。
TSVM采用局部搜索的策略来进行迭代求解,即首先使用有标记样本集训练出一个初始SVM,接着使用该学习器对未标记样本进行打标,这样所有样本都有了标记,并基于这些有标记的样本重新训练SVM,之后再寻找易出错样本不断调整。

# coding:utf-8
import random
import numpy as np
import sklearn.svm as svm
from sklearn.datasets.samples_generator import make_classification
from sklearn.externals import joblib
import warnings; warnings.filterwarnings(action='ignore')

class TSVM(object):
    def __init__(self, kernel='linear'):
        self.Cl, self.Cu = 1.5, 0.001
        self.kernel = kernel
        self.clf = svm.SVC(C=1.5, kernel=self.kernel)

    def train(self, X1, Y1, X2):
        N = len(X1) + len(X2)
        # 样本权值初始化
        sample_weight = np.ones(N)
        sample_weight[len(X1):] = self.Cu

        # 用已标注部分训练出一个初始SVM
        self.clf.fit(X1, Y1)
        
        # 对未标记样本进行标记
        Y2 = self.clf.predict(X2)
        Y2 = Y2.reshape(-1,1)
        
        X = np.vstack([X1, X2])
        Y = np.vstack([Y1, Y2])
        
        # 未标记样本的序号
        Y2_id = np.arange(len(X2))
        
        while self.Cu < self.Cl:
            # 重新训练SVM, 之后再寻找易出错样本不断调整
            self.clf.fit(X, Y, sample_weight=sample_weight)
            while True:
                Y2_decision = self.clf.decision_function(X2)   # 参数实例到决策超平面的距离
                Y2 = Y2.reshape(-1)
                epsilon = 1 - Y2 * Y2_decision
                negative_max_id = Y2_id[epsilon==min(epsilon)]

                print(epsilon[negative_max_id][0])
                if epsilon[negative_max_id][0] > 0:
                    # 寻找很可能错误的未标记样本,改变它的标记成其他标记
                    pool = list(set(np.unique(Y1))-set(Y2[negative_max_id]))
                    Y2[negative_max_id] = random.choice(pool)
                    Y2 = Y2.reshape(-1, 1)
                    Y = np.vstack([Y1, Y2])
                    
                    self.clf.fit(X, Y, sample_weight=sample_weight)
                else:
                    break
            self.Cu = min(2*self.Cu, self.Cl)
            sample_weight[len(X1):] = self.Cu

    def score(self, X, Y):
        return self.clf.score(X, Y)

    def predict(self, X):
        return self.clf.predict(X)

    def save(self, path='./TSVM.model'):
        joblib.dump(self.clf, path)

    def load(self, model_path='./TSVM.model'):
        self.clf = joblib.load(model_path)

if __name__ == '__main__':
    features, labels = make_classification(n_samples=200, n_features=3, n_redundant=1, n_repeated=0, n_informative=2, n_clusters_per_class=2)
    n_given = 70
    # 取前n_given个数字作为标注集
    X1 = np.copy(features)[:n_given]
    X2 = np.copy(features)[n_given:]
    Y1 = np.array(np.copy(labels)[:n_given]).reshape(-1,1)
    Y2_labeled = np.array(np.copy(labels)[n_given:]).reshape(-1,1)

    model = TSVM()
    model.train(X1, Y1, X2)

    # Y2_hat = model.predict(X2)
    accuracy = model.score(X2, Y2_labeled)
    print(accuracy)
    

你可能感兴趣的:(半监督学习算法(3)-- 半监督支持向量机)