zoukankan      html  css  js  c++  java
  • 4、2支持向量机SVM算法实践

    支持向量机SVM算法实践

      利用Python构建一个完整的SVM分类器,包含SVM分类器的训练和利用SVM分类器对未知数据的分类,

    一、训练SVM模型

      首先构建SVM模型相关的类

     1 class SVM:
     2     def __init__(self, dataSet, labels, C, toler, kernel_option):
     3         self.train_x = dataSet  # 训练特征
     4         self.train_y = labels  # 训练标签
     5         self.C = C  # 惩罚参数
     6         self.toler = toler  # 迭代的终止条件之一
     7         self.n_samples = np.shape(dataSet)[0]  # 训练样本的个数
     8         self.alphas = np.mat(np.zeros((self.n_samples, 1)))  # 拉格朗日乘子
     9         self.b = 0
    10         self.error_tmp = np.mat(np.zeros((self.n_samples, 2)))  # 保存E的缓存
    11         self.kernel_opt = kernel_option  # 选用的核函数及其参数
    12         self.kernel_mat = calc_kernel(self.train_x, self.kernel_opt)  # 核函数的输出
    View Code

    其中,calc_kernel函数用于根据指定的核函数kernel_opt计算样本的核函数矩阵,

    calc_kernel函数的具体实现如下:

     1 def calc_kernel(train_x, kernel_option):
     2     '''计算核函数矩阵
     3     input:  train_x(mat):训练样本的特征值
     4             kernel_option(tuple):核函数的类型以及参数
     5     output: kernel_matrix(mat):样本的核函数的值
     6     '''
     7     m = np.shape(train_x)[0]  # 样本的个数
     8     kernel_matrix = np.mat(np.zeros((m, m)))  # 初始化样本之间的核函数值
     9     for i in range(m):
    10         kernel_matrix[:, i] = cal_kernel_value(train_x, train_x[i, :],kernel_option)
    11     return kernel_matrix
    View Code

    在程序中,calc_kernel函数用于根据指定的核函数数类型以及参数kernel_option计算最终的样本和函数矩阵,样本核函数矩阵为:

                               

      其中,Ki,j表示的是第i个样本和第j个样本之间的核函数的值,在计算的过程中,利用cal_kernel_value函数计算每一个样本与其他样本的核函数的值。

    函数cal_kernel_value的具体实现代码:

     1 def cal_kernel_value(train_x, train_x_i, kernel_option):
     2     '''样本之间的核函数的值
     3     input:  train_x(mat):训练样本
     4             train_x_i(mat):第i个训练样本
     5             kernel_option(tuple):核函数的类型以及参数
     6     output: kernel_value(mat):样本之间的核函数的值
     7 
     8     '''
     9     kernel_type = kernel_option[0]  # 核函数的类型,分为rbf和其他
    10     m = np.shape(train_x)[0]  # 样本的个数
    11 
    12     kernel_value = np.mat(np.zeros((m, 1)))
    13 
    14     if kernel_type == 'rbf':  # rbf核函数
    15         sigma = kernel_option[1]
    16         if sigma == 0:
    17             sigma = 1.0
    18         for i in range(m):
    19             diff = train_x[i, :] - train_x_i
    20             kernel_value[i] = np.exp(diff * diff.T / (-2.0 * sigma ** 2))
    21     else:  # 不使用核函数
    22         kernel_value = train_x * train_x_i.T
    23     return kernel_value
    View Code

    cal_kernel_value函数用于根据指定的核函数类型以及参数kernel_option计算样本train_x_i与其他所有样本之间的核函数的值,在实现过程中只实现了高斯核函数。若没有指定和函数的类型,则默认不使用核函数。

      当定义好SVM模型后,我们需要完成SVN模型的最重要的功能,即利用SMO算法对SVM模型进行训练,训练SVM模型的具体过程如下:

     1 def SVM_training(train_x, train_y, C, toler, max_iter, kernel_option=('rbf', 0.431029)):
     2     '''SVM的训练
     3     input:  train_x(mat):训练数据的特征
     4             train_y(mat):训练数据的标签
     5             C(float):惩罚系数
     6             toler(float):迭代的终止条件之一
     7             max_iter(int):最大迭代次数
     8             kerner_option(tuple):核函数的类型及其参数
     9     output: svm模型
    10     '''
    11     # 1、初始化SVM分类器
    12     svm = SVM(train_x, train_y, C, toler, kernel_option)
    13 
    14     # 2、开始训练
    15     entireSet = True
    16     alpha_pairs_changed = 0
    17     iteration = 0
    18 
    19     while (iteration < max_iter) and ((alpha_pairs_changed > 0) or entireSet):
    20         print
    21         "	 iterration: ", iteration
    22         alpha_pairs_changed = 0
    23 
    24         if entireSet:
    25             # 对所有的样本
    26             for x in range(svm.n_samples):
    27                 alpha_pairs_changed += choose_and_update(svm, x)
    28             iteration += 1
    29         else:
    30             # 非边界样本
    31             bound_samples = []
    32             for i in range(svm.n_samples):
    33                 if svm.alphas[i, 0] > 0 and svm.alphas[i, 0] < svm.C:
    34                     bound_samples.append(i)
    35             for x in bound_samples:
    36                 alpha_pairs_changed += choose_and_update(svm, x)
    37             iteration += 1
    38 
    39         # 在所有样本和非边界样本之间交替
    40         if entireSet:
    41             entireSet = False
    42         elif alpha_pairs_changed == 0:
    43             entireSet = True
    44 
    45     return svm
    View Code

    函数SVM_training通过在非边界样本或所有样本中交替遍历,选择出第一个需要优化的αi,优先选择遍历非边界样本,因为非边界样本更有可能需要调整,而边界样本常常不能得到进一步调整而留在边界。循环遍历非边界样本并选出它们当中违反KKT条件的样本进行调整,直到非边界样本全部满足KKT条件为止。当某一次遍历发现没有非边界样本得到调整时,就遍历所有样本,已检验是否整个几何都满足KKT条件。如果Zaire整个集合的检验中又有样本被进一步优化,就有必要在遍历非边界样本。这样不停的遍历所有样本和遍历非边界样本之间切换,直到整个训练样本集都满足KKT条件为止。在选择出第一个变量αi后,需要判断其是否满足条件,同时,需要选择第二个变量αj,这个过程的实现代码为choose_and_update

     1 def choose_and_update(svm, alpha_i):
     2     '''判断和选择两个alpha进行更新
     3     input:  svm:SVM模型
     4             alpha_i(int):选择出的第一个变量
     5     '''
     6     error_i = cal_error(svm, alpha_i)  # 计算第一个样本的E_i
     7 
     8     # 判断选择出的第一个变量是否违反了KKT条件
     9     if (svm.train_y[alpha_i] * error_i < -svm.toler) and (svm.alphas[alpha_i] < svm.C) or 
    10             (svm.train_y[alpha_i] * error_i > svm.toler) and (svm.alphas[alpha_i] > 0):
    11 
    12         # 1、选择第二个变量
    13         alpha_j, error_j = select_second_sample_j(svm, alpha_i, error_i)
    14         alpha_i_old = svm.alphas[alpha_i].copy()
    15         alpha_j_old = svm.alphas[alpha_j].copy()
    16 
    17         # 2、计算上下界
    18         if svm.train_y[alpha_i] != svm.train_y[alpha_j]:
    19             L = max(0, svm.alphas[alpha_j] - svm.alphas[alpha_i])
    20             H = min(svm.C, svm.C + svm.alphas[alpha_j] - svm.alphas[alpha_i])
    21         else:
    22             L = max(0, svm.alphas[alpha_j] + svm.alphas[alpha_i] - svm.C)
    23             H = min(svm.C, svm.alphas[alpha_j] + svm.alphas[alpha_i])
    24         if L == H:
    25             return 0
    26 
    27         # 3、计算eta
    28         eta = 2.0 * svm.kernel_mat[alpha_i, alpha_j] - svm.kernel_mat[alpha_i, alpha_i] 
    29               - svm.kernel_mat[alpha_j, alpha_j]
    30         if eta >= 0:
    31             return 0
    32 
    33         # 4、更新alpha_j
    34         svm.alphas[alpha_j] -= svm.train_y[alpha_j] * (error_i - error_j) / eta
    35 
    36         # 5、确定最终的alpha_j
    37         if svm.alphas[alpha_j] > H:
    38             svm.alphas[alpha_j] = H
    39         if svm.alphas[alpha_j] < L:
    40             svm.alphas[alpha_j] = L
    41 
    42         # 6、判断是否结束
    43         if abs(alpha_j_old - svm.alphas[alpha_j]) < 0.00001:
    44             update_error_tmp(svm, alpha_j)
    45             return 0
    46 
    47         # 7、更新alpha_i
    48         svm.alphas[alpha_i] += svm.train_y[alpha_i] * svm.train_y[alpha_j] 
    49                                * (alpha_j_old - svm.alphas[alpha_j])
    50 
    51         # 8、更新b
    52         b1 = svm.b - error_i - svm.train_y[alpha_i] * (svm.alphas[alpha_i] - alpha_i_old) 
    53              * svm.kernel_mat[alpha_i, alpha_i] 
    54              - svm.train_y[alpha_j] * (svm.alphas[alpha_j] - alpha_j_old) 
    55              * svm.kernel_mat[alpha_i, alpha_j]
    56         b2 = svm.b - error_j - svm.train_y[alpha_i] * (svm.alphas[alpha_i] - alpha_i_old) 
    57              * svm.kernel_mat[alpha_i, alpha_j] 
    58              - svm.train_y[alpha_j] * (svm.alphas[alpha_j] - alpha_j_old) 
    59              * svm.kernel_mat[alpha_j, alpha_j]
    60         if (0 < svm.alphas[alpha_i]) and (svm.alphas[alpha_i] < svm.C):
    61             svm.b = b1
    62         elif (0 < svm.alphas[alpha_j]) and (svm.alphas[alpha_j] < svm.C):
    63             svm.b = b2
    64         else:
    65             svm.b = (b1 + b2) / 2.0
    66 
    67         # 9、更新error
    68         update_error_tmp(svm, alpha_j)
    69         update_error_tmp(svm, alpha_i)
    70 
    71         return 1
    72     else:
    73         return 0
    View Code

    函数choose_and_update实现了SMO中最核心的部分,在函数choose_and_update中,首先,判断选择出的第一个变量αi是否满足要求,在判断的过程中需要计算第一个变量的误差值Ei,使用函数cal_reeor计算变量的误差值,当检查第一个变量αi满足条件后,需要现在第二个变量αj,对于第二个变量,选择的标准是使得其改变最大,选择的具体过程使用select_second_sample_j函数来具体实现,当两个变量αi和αj都跟新完成后,此时需要重新计算b的值如svm.b = (b1+b2)/2.0。最终,需要重新计算两个变量αi和αj对应的误差值Ei和Ej。

    函数cal_error:

    1 def cal_error(svm, alpha_k):
    2     '''误差值的计算
    3     input:  svm:SVM模型
    4             alpha_k(int):选择出的变量
    5     output: error_k(float):误差值
    6     '''
    7     output_k = float(np.multiply(svm.alphas, svm.train_y).T * svm.kernel_mat[:, alpha_k] + svm.b)
    8     error_k = output_k - float(svm.train_y[alpha_k])
    9     return error_k
    View Code

    函数select_second_sample_j:

     1 def select_second_sample_j(svm, alpha_i, error_i):
     2     '''选择第二个样本
     3     input:  svm:SVM模型
     4             alpha_i(int):选择出的第一个变量
     5             error_i(float):E_i
     6     output: alpha_j(int):选择出的第二个变量
     7             error_j(float):E_j
     8     '''
     9     # 标记为已被优化
    10     svm.error_tmp[alpha_i] = [1, error_i]
    11     candidateAlphaList = np.nonzero(svm.error_tmp[:, 0].A)[0]
    12 
    13     maxStep = 0
    14     alpha_j = 0
    15     error_j = 0
    16 
    17     if len(candidateAlphaList) > 1:
    18         for alpha_k in candidateAlphaList:
    19             if alpha_k == alpha_i:
    20                 continue
    21             error_k = cal_error(svm, alpha_k)
    22             if abs(error_k - error_i) > maxStep:
    23                 maxStep = abs(error_k - error_i)
    24                 alpha_j = alpha_k
    25                 error_j = error_k
    26     else:  # 随机选择
    27         alpha_j = alpha_i
    28         while alpha_j == alpha_i:
    29             alpha_j = int(np.random.uniform(0, svm.n_samples))
    30         error_j = cal_error(svm, alpha_j)
    31 
    32     return alpha_j, error_j
    View Code

    函数update_error_tmp:

    1 def update_error_tmp(svm, alpha_k):
    2     '''重新计算误差值
    3     input:  svm:SVM模型
    4             alpha_k(int):选择出的变量
    5     output: 对应误差值
    6     '''
    7     error = cal_error(svm, alpha_k)
    8     svm.error_tmp[alpha_k] = [1, error]
    View Code

    上述代码为构建SVM模型,存在SVM.py文件中,现在要训练SVM,我们建立“svm_train.py”文件。首先,导入svm文件:

    # coding:UTF-8
    import numpy as np
    import svm

    SVM训练模型的主函数:

      

    if __name__ == "__main__":
        # 1、导入训练数据
        print("------------ 1、load data --------------")
        dataSet, labels = load_data_libsvm("resource/heart_scale")
        # 2、训练SVM模型
        print("------------ 2、training ---------------")
        C = 0.6
        toler = 0.001
        maxIter = 500
        svm_model = svm.SVM_training(dataSet, labels, C, toler, maxIter)
        # 3、计算训练的准确性
        print("------------ 3、cal accuracy --------------")
        accuracy = svm.cal_accuracy(svm_model, dataSet, labels)
        print(type(svm_model))
        print("The training accuracy is: %.3f%%" % (accuracy * 100))
        # 4、保存最终的SVM模型
        print("------------ 4、save model ----------------")
        svm.save_svm_model(svm_model, "model_file")

    主要分为四个部分:

    1.使用loda_data_libsvm函数导入训练数据

    2.调用svm.py文件中的SVM_training函数对SVM模型进行模型训练

    3.利用svm.py文件中的cal_accuracy函数对模型准确性进行评估

    4.利用svm.py文件中的save_model函数将最终的svm模型保存到指定额目录

    load_data_libsvm函数:

     1 def load_data_libsvm(data_file):
     2     '''导入训练数据
     3     input:  data_file(string):训练数据所在文件
     4     output: data(mat):训练样本的特征
     5             label(mat):训练样本的标签
     6     '''
     7     data = []
     8     label = []
     9     f = open(data_file)
    10     for line in f.readlines():
    11         lines = line.strip().split(' ')
    12 
    13         # 提取得出label
    14         label.append(float(lines[0]))
    15         # 提取出特征,并将其放入到矩阵中
    16         index = 0
    17         tmp = []
    18         for i in range(1, len(lines)):
    19             li = lines[i].strip().split(":")
    20             if int(li[0]) - 1 == index:
    21                 tmp.append(float(li[1]))
    22             else:
    23                 while (int(li[0]) - 1 > index):
    24                     tmp.append(0)
    25                     index += 1
    26                 tmp.append(float(li[1]))
    27             index += 1
    28         while len(tmp) < 13:
    29             tmp.append(0)
    30         data.append(tmp)
    31     f.close()
    32     return np.mat(data), np.mat(label).T
    View Code

    cal_accuracy函数计算SVM模型的准确率:

     1 def cal_accuracy(svm, test_x, test_y):
     2     '''计算预测的准确性
     3     input:  svm:SVM模型
     4             test_x(mat):测试的特征
     5             test_y(mat):测试的标签
     6     output: accuracy(float):预测的准确性
     7     '''
     8     n_samples = np.shape(test_x)[0]  # 样本的个数
     9     correct = 0.0
    10     for i in range(n_samples):
    11         # 对每一个样本得到预测值
    12         predict = svm_predict(svm, test_x[i, :])
    13         # 判断每一个样本的预测值与真实值是否一致
    14         if np.sign(predict) == np.sign(test_y[i]):
    15             correct += 1
    16     accuracy = correct / n_samples
    17     return accuracy
    View Code

    svm_predict:函数对每一个样本预测:

     1 def svm_predict(svm, test_sample_x):
     2     '''利用SVM模型对每一个样本进行预测
     3     input:  svm:SVM模型
     4             test_sample_x(mat):样本
     5     output: predict(float):对样本的预测
     6     '''
     7     # 1、计算核函数矩阵
     8     kernel_value = cal_kernel_value(svm.train_x, test_sample_x, svm.kernel_opt)
     9     # 2、计算预测值
    10     predict = kernel_value.T * np.multiply(svm.train_y, svm.alphas) + svm.b
    11     return predict
    View Code

    save_svm_model:保存SVM模型:

    1 def save_svm_model(svm_model, model_file):
    2     '''保存SVM模型
    3     input:  svm_model:SVM模型
    4             model_file(string):SVM模型需要保存到的文件
    5     '''
    6     with open(model_file, 'wb') as f:
    7         pickle.dump(svm_model, f)
    View Code

    训练过程:

    二、利用训练好的SVM模型对新的数据精心预测:

      对于分类算法而言,训练好的模型需要能够对新的数据集进行划分。利用上述的训练好的SVM模型,并将其保存到了“model_file”文件中,此时,我们需要利用训练好的模型精心预测。

    导入模块:

    1 # coding:UTF-8
    2 import numpy as np
    3 import pickle
    4 from svm import svm_predict

    对新数据预测的主函数:

     1 if __name__ == "__main__":
     2     # 1、导入测试数据
     3     print("--------- 1.load data ---------")
     4     test_data = load_test_data("resource/svm_test_data")
     5     # 2、导入SVM模型
     6     print("--------- 2.load model ----------")
     7     svm_model = load_svm_model("model_file")
     8     # 3、得到预测值
     9     print("--------- 3.get prediction ---------")
    10     prediction = get_prediction(test_data, svm_model)
    11     # 4、保存最终的预测值
    12     print("--------- 4.save result ----------")
    13     save_prediction("result", prediction)

    1.导入测试数据

    2.导入SVM模型

    3.计算得到预测值

    4.保存预测值

    导入测试数据集:

     1 def load_test_data(test_file):
     2     '''导入测试数据
     3     input:  test_file(string):测试数据
     4     output: data(mat):测试样本的特征
     5     '''
     6     data = []
     7     f = open(test_file)
     8     for line in f.readlines():
     9         lines = line.strip().split(' ')
    10 
    11         # 处理测试样本中的特征
    12         index = 0
    13         tmp = []
    14         for i in range(0, len(lines)):
    15             li = lines[i].strip().split(":")
    16             if int(li[0]) - 1 == index:
    17                 tmp.append(float(li[1]))
    18             else:
    19                 while (int(li[0]) - 1 > index):
    20                     tmp.append(0)
    21                     index += 1
    22                 tmp.append(float(li[1]))
    23             index += 1
    24         while len(tmp) < 13:
    25             tmp.append(0)
    26         data.append(tmp)
    27     f.close()
    28     return np.mat(data)
    View Code

    导入SVM模型:

    1 def load_svm_model(svm_model_file):
    2     '''导入SVM模型
    3     input:  svm_model_file(string):SVM模型保存的文件
    4     output: svm_model:SVM模型
    5     '''
    6     with open(svm_model_file, 'rb') as f:
    7         svm_model = pickle.load(f)
    8     return svm_model
    View Code

    对新数据精心预测:

     1 def get_prediction(test_data, svm):
     2     '''对样本进行预测
     3     input:  test_data(mat):测试数据
     4             svm:SVM模型
     5     output: prediction(list):预测所属的类别
     6     '''
     7     m = np.shape(test_data)[0]
     8     prediction = []
     9     for i in range(m):
    10         # 对每一个样本得到预测值
    11         predict = svm_predict(svm, test_data[i, :])
    12         # 得到最终的预测类别
    13         prediction.append(str(np.sign(predict)[0, 0]))
    14     return prediction
    View Code

    保存预测结果:

    1 def save_prediction(result_file, prediction):
    2     '''保存预测的结果
    3     input:  result_file(string):结果保存的文件
    4             prediction(list):预测的结果
    5     '''
    6     f = open(result_file, 'w')
    7     f.write(" ".join(prediction))
    8     f.close()
    View Code

    输出的预测结果文件

     

  • 相关阅读:
    MySQL数据库索引相关
    springMVC架构说明
    @responseBody注解的使用
    springMVC配置
    17_10_31 ./ ../ / ~/
    Nginx的简单操作
    MySQL中sql语句的优化
    Mac下Redis的简单操作
    github下载与安装(windows版)
    结合工程实践选题调研分析3个同类软件产品
  • 原文地址:https://www.cnblogs.com/wanshuai/p/9353035.html
Copyright © 2011-2022 走看看