判别式方法(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)
网友评论