首先,声明一下,这篇文章的目的是分享对svm工具箱的使用心得,希望能对小白、新手快速上手svm分类器,并对它有个初步的了解,有助于以后深入的了解。
所以,本文使用的svm分类器并不是我自己编写的,使用的也是网上找来的工具。
svm下载地址:https://pan.baidu.com/s/189Bc2Kz3-nhJbG1hjFkp5w
提取码:6xtp
里面包含了几个文件
heart_scale是一个心脏有关的数据集,model_file存放训练模型,result是学习结果也就是预测的类别的集合,svm_test_data是测试数据集。
svm是分类算法,svm_train是训练器,svm_test是测试器。
svm的相关概念,这里就不多说了,有不懂的可以先看看这篇博客:
https://blog.csdn.net/varyshare/article/details/90521649
更详细的的公式推导过程:(强烈推荐)
https://www.bilibili.com/video/av28186618?p=1
基础的名词解释可以去看周志华的机器学习。
对于想快速上手的,只需要修改输入的数据集,及对输入数据集做一些处理即可。这里放上对iris.data数据集的分类代码:
# coding:UTF-8
import numpy as np
import svm
import operator
from random import shuffle
def load_data_libsvm(data_file):
'''导入训练数据
input: data_file(string):训练数据所在文件
output: data(mat):训练样本的特征
label(mat):训练样本的标签
'''
f = open(data_file)
arrayLines = f.readlines()
del arrayLines[-1]
shuffle(arrayLines)
numberLines = len(arrayLines)
returnMat = np.zeros((numberLines,4))
classLabelVector = []
index = 0
for line in arrayLines:
line = line.strip()
listFromLine = line.split(',')
returnMat[index,:] = listFromLine[0:4]
if listFromLine[-1] == 'Iris-setosa':
classLabelVector.append(1)
elif listFromLine[-1] == 'Iris-versicolor':
classLabelVector.append(2)
elif listFromLine[-1] == 'Iris-virginica':
classLabelVector.append(3)
index += 1
return np.mat(returnMat),np.mat(classLabelVector).T
if __name__ == "__main__":
# 1、导入训练数据
print("------------ 加载数据 --------------")
dataSet, labels = load_data_libsvm("iris.data")
# 2、训练SVM模型
print("------------ 训练模型 ---------------")
C = 1
toler = 0.001
maxIter = 5
svm_model = svm.SVM_training(dataSet, labels, C, toler, maxIter)
# 3、计算训练的准确性
print("------------ 计算训练的正确率 --------------")
accuracy = svm.cal_accuracy(svm_model, dataSet, labels)
print("训练的正确率是: %.3f%%" % (accuracy * 100))
# 4、保存最终的SVM模型
print("------------ 保存模型 ----------------")
svm.save_svm_model(svm_model, "model_file")
这里的C是惩罚参数,表示分类过程中对间隔大小和分类准确度的偏好的权重,C越大,表示能容忍的误差越小,容易过拟合;C越小,表示允许的误差越大,容易欠拟合。
maxIter表示允许的最大迭代次数。
数据集iris.data在UCI上可以自己下载。
放上测试器的代码:
import numpy as np
import pickle as pickle
from svm import svm_predict
def load_test_data(test_file):
'''导入测试数据
input: test_file(string):测试数据
output: data(mat):测试样本的特征
'''
data = []
f = open(test_file)
for line in f.readlines():
lines = line.strip().split(' ')
# 处理测试样本中的特征
index = 0
tmp = []
for i in range(0, len(lines)):
li = lines[i].strip().split(":")
if int(li[0]) - 1 == index:
tmp.append(float(li[1]))
else:
while(int(li[0]) - 1 > index):
tmp.append(0)
index += 1
tmp.append(float(li[1]))
index += 1
while len(tmp) < 13:
tmp.append(0)
data.append(tmp)
f.close()
return np.mat(data)
def load_svm_model(svm_model_file):
'''导入SVM模型
input: svm_model_file(string):SVM模型保存的文件
output: svm_model:SVM模型
'''
with open(svm_model_file, 'rb') as f:
svm_model = pickle.load(f)
return svm_model
def get_prediction(test_data, svm):
'''对样本进行预测
input: test_data(mat):测试数据
svm:SVM模型
output: prediction(list):预测所属的类别
'''
m = np.shape(test_data)[0]
prediction = []
for i in range(m):
# 对每一个样本得到预测值
predict = svm_predict(svm, test_data[i, :])
# 得到最终的预测类别
prediction.append(str(np.sign(predict)[0, 0]))
return prediction
def save_prediction(result_file, prediction):
'''保存预测的结果
input: result_file(string):结果保存的文件
prediction(list):预测的结果
'''
f = open(result_file, 'w')
f.write(" ".join(prediction))
f.close()
if __name__ == "__main__":
# 1、导入测试数据
print("--------- 加载数据 ---------")
test_data = load_test_data("svm_test_data")
# 2、导入SVM模型
print("--------- 导入模型 ----------")
svm_model = load_svm_model("model_file")
# 3、得到预测值
print("--------- 获得预测结果 ---------")
prediction = get_prediction(test_data, svm_model)
# 4、保存最终的预测值
print("--------- 保存结果 ----------")
save_prediction("result", prediction)
SVM分类器的代码:
#coding:UTF-8
import numpy as np
import pickle as pickle
class SVM:
def __init__(self, dataSet, labels, C, toler, kernel_option):
self.train_x = dataSet # 训练特征
self.train_y = labels # 训练标签
self.C = C # 惩罚参数
self.toler = toler # 迭代的终止条件之一
self.n_samples = np.shape(dataSet)[0] # 训练样本的个数
self.alphas = np.mat(np.zeros((self.n_samples, 1))) # 拉格朗日乘子
self.b = 0
self.error_tmp = np.mat(np.zeros((self.n_samples, 2))) # 保存E的缓存
self.kernel_opt = kernel_option # 选用的核函数及其参数
self.kernel_mat = calc_kernel(self.train_x, self.kernel_opt) # 核函数的输出
def cal_kernel_value(train_x, train_x_i, kernel_option):
'''样本之间的核函数的值
input: train_x(mat):训练样本
train_x_i(mat):第i个训练样本
kernel_option(tuple):核函数的类型以及参数
output: kernel_value(mat):样本之间的核函数的值
'''
kernel_type = kernel_option[0] # 核函数的类型,分为rbf和其他
m = np.shape(train_x)[0] # 样本的个数
kernel_value = np.mat(np.zeros((m, 1)))
if kernel_type == 'rbf': # rbf核函数
sigma = kernel_option[1]
if sigma == 0:
sigma = 1.0
for i in range(m): #从0到样本数目循环
diff = train_x[i, :] - train_x_i
kernel_value[i] = np.exp(diff * diff.T / (-2.0 * sigma**2))
#输入数组是(4),而输出数组是(1,1),不匹配。
else: # 不使用核函数
kernel_value = train_x * train_x_i.T
return kernel_value
def calc_kernel(train_x, kernel_option):
'''计算核函数矩阵
input: train_x(mat):训练样本的特征值
kernel_option(tuple):核函数的类型以及参数
output: kernel_matrix(mat):样本的核函数的值
'''
m = np.shape(train_x)[0] # 样本的个数
kernel_matrix = np.mat(np.zeros((m, m))) # 初始化样本之间的核函数值
for i in range(m):
kernel_matrix[:, i] = cal_kernel_value(train_x, train_x[i, :], kernel_option)
return kernel_matrix
def cal_error(svm, alpha_k):
'''误差值的计算
input: svm:SVM模型
alpha_k(int):选择出的变量
output: error_k(float):误差值
'''
output_k = float(np.multiply(svm.alphas, svm.train_y).T * svm.kernel_mat[:, alpha_k] + svm.b)
error_k = output_k - float(svm.train_y[alpha_k])
return error_k
def update_error_tmp(svm, alpha_k):
'''重新计算误差值
input: svm:SVM模型
alpha_k(int):选择出的变量
output: 对应误差值
'''
error = cal_error(svm, alpha_k)
svm.error_tmp[alpha_k] = [1, error]
def select_second_sample_j(svm, alpha_i, error_i):
'''选择第二个样本
input: svm:SVM模型
alpha_i(int):选择出的第一个变量
error_i(float):E_i
output: alpha_j(int):选择出的第二个变量
error_j(float):E_j
'''
# 标记为已被优化
svm.error_tmp[alpha_i] = [1, error_i]
candidateAlphaList = np.nonzero(svm.error_tmp[:, 0].A)[0]
maxStep = 0
alpha_j = 0
error_j = 0
if len(candidateAlphaList) > 1:
for alpha_k in candidateAlphaList:
if alpha_k == alpha_i:
continue
error_k = cal_error(svm, alpha_k)
if abs(error_k - error_i) > maxStep:
maxStep = abs(error_k - error_i)
alpha_j = alpha_k
error_j = error_k
else: # 随机选择
alpha_j = alpha_i
while alpha_j == alpha_i:
alpha_j = int(np.random.uniform(0, svm.n_samples))
error_j = cal_error(svm, alpha_j)
return alpha_j, error_j
def choose_and_update(svm, alpha_i):
'''判断和选择两个alpha进行更新
input: svm:SVM模型
alpha_i(int):选择出的第一个变量
'''
error_i = cal_error(svm, alpha_i) # 计算第一个样本的E_i
# 判断选择出的第一个变量是否违反了KKT条件
if (svm.train_y[alpha_i] * error_i < -svm.toler) and (svm.alphas[alpha_i] < svm.C) or\
(svm.train_y[alpha_i] * error_i > svm.toler) and (svm.alphas[alpha_i] > 0):
# 1、选择第二个变量
alpha_j, error_j = select_second_sample_j(svm, alpha_i, error_i)
alpha_i_old = svm.alphas[alpha_i].copy()
alpha_j_old = svm.alphas[alpha_j].copy()
# 2、计算上下界
if svm.train_y[alpha_i] != svm.train_y[alpha_j]:
L = max(0, svm.alphas[alpha_j] - svm.alphas[alpha_i])
H = min(svm.C, svm.C + svm.alphas[alpha_j] - svm.alphas[alpha_i])
else:
L = max(0, svm.alphas[alpha_j] + svm.alphas[alpha_i] - svm.C)
H = min(svm.C, svm.alphas[alpha_j] + svm.alphas[alpha_i])
if L == H:
return 0
# 3、计算eta
eta = 2.0 * svm.kernel_mat[alpha_i, alpha_j] - svm.kernel_mat[alpha_i, alpha_i] \
- svm.kernel_mat[alpha_j, alpha_j]
if eta >= 0:
return 0
# 4、更新alpha_j
svm.alphas[alpha_j] -= svm.train_y[alpha_j] * (error_i - error_j) / eta
# 5、确定最终的alpha_j
if svm.alphas[alpha_j] > H:
svm.alphas[alpha_j] = H
if svm.alphas[alpha_j] < L:
svm.alphas[alpha_j] = L
# 6、判断是否结束
if abs(alpha_j_old - svm.alphas[alpha_j]) < 0.00001:
update_error_tmp(svm, alpha_j)
return 0
# 7、更新alpha_i
svm.alphas[alpha_i] += svm.train_y[alpha_i] * svm.train_y[alpha_j] \
* (alpha_j_old - svm.alphas[alpha_j])
# 8、更新b
b1 = svm.b - error_i - svm.train_y[alpha_i] * (svm.alphas[alpha_i] - alpha_i_old) \
* svm.kernel_mat[alpha_i, alpha_i] \
- svm.train_y[alpha_j] * (svm.alphas[alpha_j] - alpha_j_old) \
* svm.kernel_mat[alpha_i, alpha_j]
b2 = svm.b - error_j - svm.train_y[alpha_i] * (svm.alphas[alpha_i] - alpha_i_old) \
* svm.kernel_mat[alpha_i, alpha_j] \
- svm.train_y[alpha_j] * (svm.alphas[alpha_j] - alpha_j_old) \
* svm.kernel_mat[alpha_j, alpha_j]
if (0 < svm.alphas[alpha_i]) and (svm.alphas[alpha_i] < svm.C):
svm.b = b1
elif (0 < svm.alphas[alpha_j]) and (svm.alphas[alpha_j] < svm.C):
svm.b = b2
else:
svm.b = (b1 + b2) / 2.0
# 9、更新error
update_error_tmp(svm, alpha_j)
update_error_tmp(svm, alpha_i)
return 1
else:
return 0
def SVM_training(train_x, train_y, C, toler, max_iter, kernel_option = ('rbf', 0.431029)):
'''SVM的训练
input: train_x(mat):训练数据的特征
train_y(mat):训练数据的标签
C(float):惩罚系数
toler(float):迭代的终止条件之一
max_iter(int):最大迭代次数
kerner_option(tuple):核函数的类型及其参数
output: svm模型
'''
# 1、初始化SVM分类器
svm = SVM(train_x, train_y, C, toler, kernel_option)
# 2、开始训练
entireSet = True
alpha_pairs_changed = 0
iteration = 0
while (iteration < max_iter) and ((alpha_pairs_changed > 0) or entireSet):
print("\t 迭代: ", iteration)
alpha_pairs_changed = 0
if entireSet:
# 对所有的样本
for x in range(svm.n_samples):
alpha_pairs_changed += choose_and_update(svm, x)
iteration += 1
else:
# 非边界样本
bound_samples = []
for i in range(svm.n_samples):
if svm.alphas[i,0] > 0 and svm.alphas[i,0] < svm.C:
bound_samples.append(i)
for x in bound_samples:
alpha_pairs_changed += choose_and_update(svm, x)
iteration += 1
# 在所有样本和非边界样本之间交替
if entireSet:
entireSet = False
elif alpha_pairs_changed == 0:
entireSet = True
return svm
def svm_predict(svm, test_sample_x):
'''利用SVM模型对每一个样本进行预测
input: svm:SVM模型
test_sample_x(mat):样本
output: predict(float):对样本的预测
'''
# 1、计算核函数矩阵
kernel_value = cal_kernel_value(svm.train_x, test_sample_x, svm.kernel_opt)
# 2、计算预测值
predict = kernel_value.T * np.multiply(svm.train_y, svm.alphas) + svm.b
return predict
def cal_accuracy(svm, test_x, test_y):
'''计算预测的准确性
input: svm:SVM模型
test_x(mat):测试的特征
test_y(mat):测试的标签
output: accuracy(float):预测的准确性
'''
n_samples = np.shape(test_x)[0] # 样本的个数
correct = 0.0
for i in range(n_samples):
# 对每一个样本得到预测值
predict=svm_predict(svm, test_x[i, :])
# 判断每一个样本的预测值与真实值是否一致
if np.sign(predict) == np.sign(test_y[i]):
correct += 1
accuracy = correct / n_samples
return accuracy
def save_svm_model(svm_model, model_file):
'''保存SVM模型
input: svm_model:SVM模型
model_file(string):SVM模型需要保存到的文件
'''
with open(model_file, 'wb') as f:
pickle.dump(svm_model, f)