机器学习算法实战项目—支持向量机(3)—在复杂数据上应用核函数

下面使用的数据集分享如下:
链接:https://pan.baidu.com/s/1oySkfAhFWvSUL4vWo3bLqQ
提取码:6666
3.在复杂数据上应用核函数

我们上面的SMO算法核函数其实就是线性可分的,那么对于非线性可分的呢?

接下来,我们就要使用一种称为核函数的工具将数据转换成易分类器理解的形式。

径向基核函数

径向基函数是SVM中常用的一个核函数。

径向基函数是一个采用向量作为自变量的函数,能够基于向量距离运算输出一个标量。这个距离可以是从<0,0>向量或者其他向量开始计算的距离。

接下来,我们将会使用到径向基函数的高斯版本,其具体公式为:
K ( x , y ) = e x p ( − ∣ ∣ x − y ∣ ∣ 2 2 σ 2 ) K(x,y)=exp(\frac{-||x-y||^2}{2\sigma^2}) K(x,y)=exp(2σ2∣∣xy2)
其中, s i g m a sigma sigma是用户定义的用于确定到达率或者说函数值跌落到0的速度参数。

上述高斯核函数将数据从其特征空间映射到更高维的空间,具体来说这里是映射到一个无穷维的空间。

高斯核函数只是一个常用的核函数,使用者并不需要确切地理解数据到底是如何表现的,而且使用高斯核函数还会得到一个理想的结果。

我们定义核函数如下:

"""
函数说明:通过核函数将数据转换更高维空间
Parameters:
    X - 数据矩阵
    A - 单个数据的向量
    kTup - 包含核函数信息的元组 
Returns:
    K - 计算的核K
"""
def kernel_trans(xi, xj, kTup):
    """
    :param kTup: 两维,第一列是字符串,为lin,rbf,如果是rbf,第二列多一个sigmma
    :return:
    """
    # 读取X的行列数
    m, n = np.shape(xi)
    # K初始化为m行1列的零向量
    K = np.mat(np.zeros((m, 1)))
    # 线性核函数只进行内积
    if kTup[0] == 'lin':
        K = xi * xj.T
    # 高斯核函数,根据高斯核函数公式计算
    elif kTup[0] == 'rbf':
        for j in range(m):
            delta_row = xi[j, :] - xj
            K[j] = delta_row * delta_row.T
        K = np.exp(K / (-1 * kTup[1] ** 2))
    else:
        raise NameError('核函数无法识别')
    return K

此时我们定义的数据结构为:

"""
类说明:维护所有需要操作的值
Parameters:
    data_mat_in - 数据矩阵
    class_cabels - 数据标签
    C - 松弛变量
    toler - 容错率   
Returns:
    None
"""
"""
定义一个新的数据结构
"""
class opt_struct:
    def __init__(self, data_mat_in, class_labels, C, toler,kTup):
        # 数据矩阵
        self.X = data_mat_in #传进来的数据
        # 数据标签
        self.label_mat = class_labels
        # 松弛变量
        self.C = C
        # 容错率
        self.tol = toler
        # 矩阵的行数
        self.m = np.shape(data_mat_in)[0]
        # 根据矩阵行数初始化alphas矩阵,一个m行1列的全零列向量
        self.alphas = np.mat(np.zeros((self.m, 1)))
        # 初始化b参数为0
        self.b = 0
        # 根据矩阵行数初始化误差缓存矩阵,第一列为是否有效标志位,其中0无效,1有效;第二列为实际的误差Ei的值
        """
        我们之前的定义为:Ei=gxi-yi
        yi是标签的实际值。
        gx=alpha_i*y_i*x_i.x,就相当于是w.x+b
        因为误差值经常用到,所以希望每次计算后放到一个缓存当中,将ecache一分为二,第一列是标志位,取值为0或者1,为1时表示已经算出来
        """
        self.ecache = np.mat(np.zeros((self.m, 2)))
        self.K=np.mat(np.zeros((self.m,self.m)))
        for i in range(self.m):
            self.K[:,i]=kernel_trans(self.X,self.X[i,:],kTup)

引入了一个新变量kTup,kTup是一个包含核函数信息的元组,元组的第一个参数是描述所用核函数类型的一个字符串,其他2个参数则都是核函数可能需要的可选参数。该函数首先构建出了一个列向量,然后检查元组以确定核函数的类型。这里只给出了2种选择,但是依然可以很容易地通过添加elif语句来扩展到更多选项。

在线性核函数的情况下,内积计算在“所有数据集”和“数据集中的一行”这两个输入之间展开。在径向基核函数的情况下,在for循环中对于矩阵的每个元素计算高斯函数的值。而在for循环结束之后,我们将计算过程应用到整个向量上去。值得一提的是,在NumPy矩阵中,除法符号意味着对矩阵元素展开计算。

为了使用核函数,先期的两个函数innerL()和cal-Ek()的代码需要做些修改。

"""
函数说明:优化的SMO算法
Parameters:
    i - 标号为i的数据的索引值
    oS - 数据结构 
Returns:
    1 - 有任意一对alpha值发生变化
    0 - 没有任意一对alpha值发生变化或变化太小
"""
def innerL(i, os):
    # 步骤1:计算误差Ei
    Ei = cal_Ek(os, i)
    # 优化alpha,设定一定的容错率
    if((os.label_mat[i] * Ei < -os.tol) and (os.alphas[i] < os.C)) or ((os.label_mat[i] * Ei > os.tol) and (os.alphas[i] > 0)):
        # 使用内循环启发方式2选择alpha_j,并计算Ej
        j, Ej = select_j(i, os, Ei)
        # 保存更新前的alpha值,使用深层拷贝
        alpha_i_old = os.alphas[i].copy()
        alpha_j_old = os.alphas[j].copy()
        # 步骤2:计算上界H和下界L
        if(os.label_mat[i] != os.label_mat[j]):
            L = max(0, os.alphas[j] - os.alphas[i])
            H = min(os.C, os.C + os.alphas[j] - os.alphas[i])
        else:
            L = max(0, os.alphas[j] + os.alphas[i] - os.C)
            H = min(os.C, os.alphas[j] + os.alphas[i])
        if L == H:
            print("L == H")
            return 0
        # 步骤3:计算eta
        eta = 2.0 * os.K[i, j] - os.K[i, i] - os.K[j, j] #这里的计算就要采用核函数了
        if eta >= 0:
            print("eta >= 0")
            return 0
        # 步骤4:更新alpha_j
        os.alphas[j] -= os.label_mat[j] * (Ei - Ej) / eta
        # 步骤5:修剪alpha_j
        os.alphas[j] = clip_alpha(os.alphas[j], H, L)
        # 更新Ej至误差缓存
        update_Ek(os, j)
        if(abs(os.alphas[j] - alpha_j_old) < 0.00001):
            print("alpha_j变化太小")
            return 0
        # 步骤6:更新alpha_i
        os.alphas[i] += os.label_mat[i] * os.label_mat[j] * (alpha_j_old - os.alphas[j])
        # 更新Ei至误差缓存
        update_Ek(os, i)
        # 步骤7:更新b_1和b_2:
        b1 = os.b - Ei - os.label_mat[i] * (os.alphas[i] - alpha_i_old) * os.K[i, i] - os.label_mat[j] * (os.alphas[j] - alpha_j_old) * os.K[j, i]
        b2 = os.b - Ej - os.label_mat[i] * (os.alphas[i] - alpha_i_old) * os.K[i, j] - os.label_mat[j] * (os.alphas[j] - alpha_j_old) * os.K[j, j]
        # 步骤8:根据b_1和b_2更新b
        if(0 < os.alphas[i] < os.C):
            os.b = b1
        elif(0 < os.alphas[j] < os.C):
            os.b = b2
        else:
            os.b = (b1 + b2) / 2.0
        return 1
    else:
        return 0
"""
函数说明:计算误差
Parameters:
    os - 数据结构
    k - 标号为k的数据
Returns:
    Ek - 标号为k的数据误差
"""
def cal_Ek(os, k):
    # multiply(a,b)就是个乘法,如果a,b是两个数组,那么对应元素相乘
    # .T为转置
    fXk = float(np.multiply(os.alphas, os.label_mat).T * os.K[:, k]+os.b)
    # 计算误差项
    Ek = fXk - float(os.label_mat[k])
    # 返回误差项
    return Ek

3.在测试中使用核函数

接下来我们将构建一个对上节中的数据点进行有效分类的分类器,该分类器使用了径向基核函数。前面提到的径向基函数有一个用户定义的输入。首先,我们需要确定它的大小,然后利用该核函数构建出一个分类器。

利用核函数进行分类的径向基测试函数如下所示:

"""
函数说明:测试函数
Parameters:
    k1 - 使用高斯核函数的时候表示到达率  
Returns:
    None
"""
def test_rbf(k1 = 1.3):
    # 加载训练集
    data_array, label_array = load_dataset('testSetRBF.txt')
    # 根据训练集计算b, alphas
    b, alphas = smo_p(data_array, label_array, 200, 0.0001, 100, ('rbf', k1))
    data_mat = np.mat(data_array)
    label_mat = np.mat(label_array).transpose()
    # 获得支持向量
    svInd = np.nonzero(alphas.A > 0)[0] # 从行维度来描述索引值
    sVs = data_mat[svInd]
    labelSV = label_mat[svInd]
    print("支持向量个数:%d" % np.shape(sVs)[0])
    m, n = np.shape(data_mat)
    error_count = 0
    for i in range(m):
        # 计算各个点的核
        kernel_eval = kernel_trans(sVs, data_mat[i, :], ('rbf', k1))
        # 根据支持向量的点计算超平面,返回预测结果
        predict = kernel_eval.T * np.multiply(labelSV, alphas[svInd]) + b
        # 返回数组中各元素的正负号,用1和-1表示,并统计错误个数
        if np.sign(predict) != np.sign(label_array[i]):
            error_count += 1
    # 打印错误率
    print('训练集错误率:%.2f%%' % ((float(error_count) / m) * 100))
    # 加载测试集
    data_array, label_array = load_dataset('testSetRBF2.txt')
    error_count = 0
    data_mat = np.mat(data_array)
    label_mat = np.mat(label_array).transpose()
    m, n = np.shape(data_mat)
    for i in range(m):
        # 计算各个点的核
        kernel_eval = kernel_trans(sVs, data_mat[i, :], ('rbf', k1))
        # 根据支持向量的点计算超平面,返回预测结果
        predict = kernel_eval.T * np.multiply(labelSV, alphas[svInd]) + b
        # 返回数组中各元素的正负号,用1和-1表示,并统计错误个数
        if np.sign(predict) != np.sign(label_array[i]):
            error_count += 1
    # 打印错误率
    print('测试集错误率:%.2f%%' % ((float(error_count) / m) * 100))

上述代码只有一个可选的输入参数,该输入参数是高斯径向基函数中的一个用户定义变量。

整个代码中最重要的是for循环开始的那两行,它们给出了如何利用核函数进行分类。首先利用结构初始化方法中使用过的kernel-trans()函数,得到转换后的数据。然后,再用其与前面的alpha及类别标签值求积。其中需要特别注意的另一件事是,在这几行代码中,是如何做到只
需要支持向量数据就可以进行分类的。除此之外,其他数据都可以直接舍弃。

与第一个for循环相比,第二个for循环仅仅只有数据集不同,后者采用的是测试数据集。

我们分类后的支持向量可视化为:

其中,第一张图是关于训练集的,第二张图是关于测试集的。

我们可以尝试更换不同的k1参数以观察测试错误率、训练错误率、支持向量个数随k1的变化情况。

这次不再赘述。

我们看一下在训练集和测试集上的误差率:

支持向量个数:30
训练集错误率:13.00%
测试集错误率:15.00%

完整版代码如下:

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
import random
import matplotlib as mpl
from matplotlib.patches import Circle
"""
函数说明:读取数据
Parameters:
    file_name - 文件名 
Returns:
    data_mat - 数据矩阵
    label_mat - 数据标签
"""
def load_dataset(file_name):
    # 数据矩阵
    data_mat = []
    # 标签向量
    label_mat = []
    # 打开文件
    fr = open(file_name)
    # 逐行读取
    for line in fr.readlines():
        # 去掉每一行首尾的空白符,例如'\n','\r','\t',' '
        # 将每一行内容根据'\t'符进行切片
        line_array = line.strip().split('\t')
        # 添加数据(100个元素排成一行)
        data_mat.append([float(line_array[0]), float(line_array[1])])
        # 添加标签(100个元素排成一行)
        label_mat.append(float(line_array[2]))
    return data_mat, label_mat

"""
函数说明:通过核函数将数据转换更高维空间
Parameters:
    X - 数据矩阵
    A - 单个数据的向量
    kTup - 包含核函数信息的元组 
Returns:
    K - 计算的核K
"""
def kernel_trans(xi, xj, kTup):
    """
    :param kTup: 两维,第一列是字符串,为lin,rbf,如果是rbf,第二列多一个sigmma
    :return:
    """
    # 读取X的行列数
    m, n = np.shape(xi)
    # K初始化为m行1列的零向量
    K = np.mat(np.zeros((m, 1)))
    # 线性核函数只进行内积
    if kTup[0] == 'lin':
        K = xi * xj.T
    # 高斯核函数,根据高斯核函数公式计算
    elif kTup[0] == 'rbf':
        for j in range(m):
            delta_row = xi[j, :] - xj
            K[j] = delta_row * delta_row.T
        K = np.exp(K / (-1 * kTup[1] ** 2))
    else:
        raise NameError('核函数无法识别')
    return K

"""
类说明:维护所有需要操作的值
Parameters:
    data_mat_in - 数据矩阵
    class_cabels - 数据标签
    C - 松弛变量
    toler - 容错率   
Returns:
    None
"""
"""
定义一个新的数据结构
"""
class opt_struct:
    def __init__(self, data_mat_in, class_labels, C, toler,kTup):
        # 数据矩阵
        self.X = data_mat_in #传进来的数据
        # 数据标签
        self.label_mat = class_labels
        # 松弛变量
        self.C = C
        # 容错率
        self.tol = toler
        # 矩阵的行数
        self.m = np.shape(data_mat_in)[0]
        # 根据矩阵行数初始化alphas矩阵,一个m行1列的全零列向量
        self.alphas = np.mat(np.zeros((self.m, 1)))
        # 初始化b参数为0
        self.b = 0
        # 根据矩阵行数初始化误差缓存矩阵,第一列为是否有效标志位,其中0无效,1有效;第二列为实际的误差Ei的值
        """
        我们之前的定义为:Ei=gxi-yi
        yi是标签的实际值。
        gx=alpha_i*y_i*x_i.x,就相当于是w.x+b
        因为误差值经常用到,所以希望每次计算后放到一个缓存当中,将ecache一分为二,第一列是标志位,取值为0或者1,为1时表示已经算出来
        """
        self.ecache = np.mat(np.zeros((self.m, 2)))
        self.K=np.mat(np.zeros((self.m,self.m)))
        for i in range(self.m):
            self.K[:,i]=kernel_trans(self.X,self.X[i,:],kTup)

"""
函数说明:计算误差
Parameters:
    os - 数据结构
    k - 标号为k的数据
Returns:
    Ek - 标号为k的数据误差
"""
def cal_Ek(os, k):
    # multiply(a,b)就是个乘法,如果a,b是两个数组,那么对应元素相乘
    # .T为转置
    fXk = float(np.multiply(os.alphas, os.label_mat).T * os.K[:, k]+os.b)
    # 计算误差项
    Ek = fXk - float(os.label_mat[k])
    # 返回误差项
    return Ek

"""
函数说明:随机选择alpha_j
Parameters:
    i - alpha_i的索引值
    m - alpha参数个数
Returns:
    j - alpha_j的索引值
"""
def select_j_random(i, m):
    j = i
    while(j == i):
        # uniform()方法将随机生成一个实数,它在[x, y)范围内
        j = int(random.uniform(0, m))
    return j

"""
函数说明:内循环启发方式2
Parameters:
    i - 标号为i的数据的索引值
    os - 数据结构
    Ei - 标号为i的数据误差
Returns:
    j - 标号为j的数据的索引值
    max_K - 标号为maxK的数据的索引值
    Ej - 标号为j的数据误差
"""
def select_j(i, os, Ei):
    # 初始化
    max_K = -1
    max_delta_E = 0
    Ej = 0
    # 根据Ei更新误差缓存
    os.ecache[i] = [1, Ei]
    # 对一个矩阵.A转换为Array类型
    # 返回误差不为0的数据的索引值
    valid_ecache_list = np.nonzero(os.ecache[:, 0].A)[0]
    # 有不为0的误差
    if(len(valid_ecache_list) > 1):
        # 遍历,找到最大的Ek
        for k in valid_ecache_list:
            # 不计算k==i节省时间
            if k == i:
                continue
            # 计算Ek
            Ek = cal_Ek(os, k)
            # 计算|Ei - Ek|
            delta_E = abs(Ei - Ek)
            # 找到maxDeltaE
            if(delta_E > max_delta_E):
                max_K = k
                max_delta_E = delta_E
                Ej = Ek
        # 返回maxK,Ej
        return max_K, Ej
    # 没有不为0的误差
    else:
        # 随机选择alpha_j的索引值
        j = select_j_random(i, os.m)
        # 计算Ej
        Ej = cal_Ek(os, j)
    # 返回j,Ej
    return j, Ej

"""
函数说明:计算Ek,并更新误差缓存
Parameters:
    oS - 数据结构
    k - 标号为k的数据的索引值 
Returns:
    None
"""
def update_Ek(os, k):
    # 计算Ek
    Ek = cal_Ek(os, k)
    # 更新误差缓存
    os.ecache[k] = [1, Ek]

"""
函数说明:修剪alpha_j
Parameters:
    aj - alpha_j值
    H - alpha上限
    L - alpha下限 
Returns:
    aj - alpha_j值
"""
def clip_alpha(aj, H, L):
    if aj > H:
        aj = H
    if L > aj:
        aj = L
    return aj

"""
函数说明:优化的SMO算法
Parameters:
    i - 标号为i的数据的索引值
    oS - 数据结构 
Returns:
    1 - 有任意一对alpha值发生变化
    0 - 没有任意一对alpha值发生变化或变化太小
"""
def innerL(i, os):
    # 步骤1:计算误差Ei
    Ei = cal_Ek(os, i)
    # 优化alpha,设定一定的容错率
    if((os.label_mat[i] * Ei < -os.tol) and (os.alphas[i] < os.C)) or ((os.label_mat[i] * Ei > os.tol) and (os.alphas[i] > 0)):
        # 使用内循环启发方式2选择alpha_j,并计算Ej
        j, Ej = select_j(i, os, Ei)
        # 保存更新前的alpha值,使用深层拷贝
        alpha_i_old = os.alphas[i].copy()
        alpha_j_old = os.alphas[j].copy()
        # 步骤2:计算上界H和下界L
        if(os.label_mat[i] != os.label_mat[j]):
            L = max(0, os.alphas[j] - os.alphas[i])
            H = min(os.C, os.C + os.alphas[j] - os.alphas[i])
        else:
            L = max(0, os.alphas[j] + os.alphas[i] - os.C)
            H = min(os.C, os.alphas[j] + os.alphas[i])
        if L == H:
            print("L == H")
            return 0
        # 步骤3:计算eta
        eta = 2.0 * os.K[i, j] - os.K[i, i] - os.K[j, j] #这里的计算就要采用核函数了
        if eta >= 0:
            print("eta >= 0")
            return 0
        # 步骤4:更新alpha_j
        os.alphas[j] -= os.label_mat[j] * (Ei - Ej) / eta
        # 步骤5:修剪alpha_j
        os.alphas[j] = clip_alpha(os.alphas[j], H, L)
        # 更新Ej至误差缓存
        update_Ek(os, j)
        if(abs(os.alphas[j] - alpha_j_old) < 0.00001):
            print("alpha_j变化太小")
            return 0
        # 步骤6:更新alpha_i
        os.alphas[i] += os.label_mat[i] * os.label_mat[j] * (alpha_j_old - os.alphas[j])
        # 更新Ei至误差缓存
        update_Ek(os, i)
        # 步骤7:更新b_1和b_2:
        b1 = os.b - Ei - os.label_mat[i] * (os.alphas[i] - alpha_i_old) * os.K[i, i] - os.label_mat[j] * (os.alphas[j] - alpha_j_old) * os.K[j, i]
        b2 = os.b - Ej - os.label_mat[i] * (os.alphas[i] - alpha_i_old) * os.K[i, j] - os.label_mat[j] * (os.alphas[j] - alpha_j_old) * os.K[j, j]
        # 步骤8:根据b_1和b_2更新b
        if(0 < os.alphas[i] < os.C):
            os.b = b1
        elif(0 < os.alphas[j] < os.C):
            os.b = b2
        else:
            os.b = (b1 + b2) / 2.0
        return 1
    else:
        return 0

"""
函数说明:完整的线性SMO算法
Parameters:
    data_mat_in - 数据矩阵
    class_labels - 数据标签
    C - 松弛变量
    toler - 容错率
    max_iter - 最大迭代次数
    kTup - 包含核函数信息的元组 
Returns:
    os.b - SMO算法计算的b
    os.alphas - SMO算法计算的alphas
"""
def smo_p(data_mat_in, class_labels, C, toler, max_iter, kTup = ('lin', 0)):
    # 初始化数据结构
    os = opt_struct(np.mat(data_mat_in), np.mat(class_labels).transpose(), C, toler, kTup)
    # 初始化当前迭代次数
    iter = 0
    entrie_set = True
    alpha_pairs_changed = 0
    # 遍历整个数据集alpha都没有更新或者超过最大迭代次数,则退出循环
    while(iter < max_iter) and ((alpha_pairs_changed > 0) or (entrie_set)):
        alpha_pairs_changed = 0
        # 遍历整个数据集
        if entrie_set:
            for i in range(os.m):
                # 使用优化的SMO算法
                alpha_pairs_changed += innerL(i, os)
                print("全样本遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter, i, alpha_pairs_changed))
            iter += 1
        # 遍历非边界值
        else:
            # 遍历不在边界0和C的alpha
            non_nound_i_s = np.nonzero((os.alphas.A > 0) * (os.alphas.A < C))[0]
            for i in non_nound_i_s:
                alpha_pairs_changed += innerL(i, os)
                print("非边界遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter, i, alpha_pairs_changed))
            iter += 1
        # 遍历一次后改为非边界遍历
        if entrie_set:
            entrie_set = False
        # 如果alpha没有更新,计算全样本遍历
        elif(alpha_pairs_changed == 0):
            entrie_set = True
        print("迭代次数:%d" % iter)
    # 返回SMO算法计算的b和alphas
    return os.b, os.alphas

"""
函数说明:测试函数
Parameters:
    k1 - 使用高斯核函数的时候表示到达率  
Returns:
    None
"""
def test_rbf(k1 = 1.3):
    # 加载训练集
    data_array, label_array = load_dataset('testSetRBF.txt')
    # 根据训练集计算b, alphas
    b, alphas = smo_p(data_array, label_array, 200, 0.0001, 100, ('rbf', k1))
    data_mat = np.mat(data_array)
    label_mat = np.mat(label_array).transpose()
    # 获得支持向量
    svInd = np.nonzero(alphas.A > 0)[0] # 从行维度来描述索引值
    sVs = data_mat[svInd]
    labelSV = label_mat[svInd]
    print("支持向量个数:%d" % np.shape(sVs)[0])
    m, n = np.shape(data_mat)
    error_count = 0
    for i in range(m):
        # 计算各个点的核
        kernel_eval = kernel_trans(sVs, data_mat[i, :], ('rbf', k1))
        # 根据支持向量的点计算超平面,返回预测结果
        predict = kernel_eval.T * np.multiply(labelSV, alphas[svInd]) + b
        # 返回数组中各元素的正负号,用1和-1表示,并统计错误个数
        if np.sign(predict) != np.sign(label_array[i]):
            error_count += 1
    # 打印错误率
    print('训练集错误率:%.2f%%' % ((float(error_count) / m) * 100))
    # 加载测试集
    data_array, label_array = load_dataset('testSetRBF2.txt')
    error_count = 0
    data_mat = np.mat(data_array)
    label_mat = np.mat(label_array).transpose()
    m, n = np.shape(data_mat)
    for i in range(m):
        # 计算各个点的核
        kernel_eval = kernel_trans(sVs, data_mat[i, :], ('rbf', k1))
        # 根据支持向量的点计算超平面,返回预测结果
        predict = kernel_eval.T * np.multiply(labelSV, alphas[svInd]) + b
        # 返回数组中各元素的正负号,用1和-1表示,并统计错误个数
        if np.sign(predict) != np.sign(label_array[i]):
            error_count += 1
    # 打印错误率
    print('测试集错误率:%.2f%%' % ((float(error_count) / m) * 100))

def cal_w_s(alphas, data_mat, class_labels):
    X = np.mat(data_mat)
    label_mat = np.mat(class_labels).transpose()
    m, n = np.shape(X)
    w = np.zeros((n, 1))
    for i in range(m):
        w += np.multiply(alphas[i] * label_mat[i], X[i, :].T)
    return w

"""
函数说明:数据可视化
Parameters:
    dataMat - 数据矩阵
    labelMat - 数据标签  
Returns:
    None
"""
def show_dataset(data_mat, label_mat):
    for path in ['testSetRBF.txt','testSetRBF2.txt']:
        data_mat, label_mat = load_dataset(path)
    # 画图
    fig = plt.figure()
    ax = fig.add_subplot(111)
    cm_dark = mpl.colors.ListedColormap(['b', 'g'])
    ax.scatter(np.array(data_mat)[:, 0], np.array(data_mat)[:, 1], c=np.array(label_mat).squeeze(), cmap=cm_dark, s=30)
    b, alphas = smo_p(data_mat, label_mat, 200, 0.0001, 10000, ('rbf', 2))
    w = cal_w_s(alphas, data_mat, label_mat)
    # 画支持向量
    alphas_non_zeros_index = np.where(alphas > 0)
    for i in alphas_non_zeros_index[0]:
        circle = Circle((data_mat[i][0], data_mat[i][1]), 0.035, facecolor='none', edgecolor='red', linewidth=1.5,alpha=1)
        ax.add_patch(circle)
    plt.show()

if __name__ == '__main__':
    for path in ['testSetRBF.txt','testSetRBF2.txt']:
        data_mat, label_mat = load_dataset(path)
        print('=='*60)
        show_dataset(data_mat,label_mat)
        print('=='*60)
    test_rbf()
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旅途中的宽~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值