02_svm_多分类

描述

支持向量机(SVM)最初是设计用于二分类问题的,但在实际应用中经常要处理多分类问题。为了使用SVM解决多分类问题,研究者们提出了多种策略,主要分为两类:一对多(one-vs-all )和一对一(one-vs-one)。

sklearn 中的svm多分类算法实现采用的是one-vs-one;dlib虽然提供了多分类算法实现,但使用起来相对繁琐(也可以说是太灵活了)。

one-vs-one

one-vs-one 策略是一种将多类分类问题转化为多个二元分类问题的经典方法。对于具有 N 个类别的分类任务,它会训练 N*(N-1)/2 个二元分类器,每个分类器专门用于区分两个不同的类别。在预测阶段,所有二元分类器都会对测试样本进行投票,最终得票最多的类别即为预测结果。

定义trainer

这里以鸢尾花分类为例(鸢尾花分类的例子相当于编程入门的"hello world",很多机器学习的入门案例都是这三朵花)

c++ 复制代码
typedef dlib::matrix<double, 4, 1> iris_type;
typedef dlib::one_vs_one_trainer<dlib::any_trainer<iris_type>> ovo_trainer; // one-vs-one 训练器
typedef dlib::polynomial_kernel<iris_type> poly_kernel;  // 多项式核
typedef dlib::radial_basis_kernel<iris_type> rbf_kernel; // 径向基函数核

训练过程:ovo_trainer可以设置多个二分类trainer(首先要设置一个默认的二分类训练器);可以根据数据特征,指定哪些类别用特定的trainer,具体可见下例。

c++ 复制代码
void SvmOvoClassifier(const string &pfile)
{
    vector<iris_type> train_datas;
    vector<double> train_labels;
    map<int, string> label_map = LoadIrisVec(pfile, train_datas, train_labels);

    ovo_trainer trainer;
    dlib::svm_c_trainer<rbf_kernel> rbf_trainer;
    rbf_trainer.set_kernel(rbf_kernel(0.001)); // gamma 参数
    rbf_trainer.set_c(10);                     // 大C参数
    trainer.set_trainer(rbf_trainer);          // 设置默认trainer,这里是3分类问题,需要创建三个trainer,如果后面不指定,三个分类器都一样

    dlib::svm_nu_trainer<poly_kernel> poly_trianer;
    poly_trianer.set_kernel(poly_kernel(0.1, 2, 3)); // 多项式核参数
    trainer.set_trainer(poly_trianer, 2, 3);         // 指定poly_trianer处理(2,3)标签的分类;另外的(1,2)、(1,3)的分类使用rbf_trainer处理

    dlib::one_vs_one_decision_function<ovo_trainer,
                                       dlib::decision_function<rbf_kernel>,
                                       dlib::decision_function<poly_kernel>>
        learned_func = trainer.train(train_datas, train_labels);
    int ok_count = 0;
    for (int i = 0; i < train_datas.size(); i++)
    {
        iris_type ii_type = train_datas.at(i);
        double ret = learned_func(ii_type);
        if (ret == train_labels.at(i))
            ok_count += 1;
        cout << "predicted label:" << ret << "(" << label_map[ret] << ");"
             << "real label:" << label_map[train_labels.at(i)] << endl;
    }
    cout << "accurary:" << (ok_count * 1.0) / train_datas.size() << endl;
}

one-vs-all

与 one-vs-one 策略相比,one-vs-all 的优势是需要训练的分类器数量更少(N 个 vs N*(N-1)/2 个),对于类别较多的任务计算成本更低。但它可能受类别不平衡影响更大,因为每个二元分类器都要处理 "该类别" 与 "所有其他类别" 的不平衡数据。

该实现的灵活性体现在可以轻松替换基础二元分类器,例如将svm_nu_trainer替换为kernel_ridge_regression_trainer,而无需修改 one-vs-all 框架本身。

定义trainer

还以鸢尾花为例

c++ 复制代码
typedef dlib::one_vs_all_trainer<dlib::any_trainer<iris_type>> ova_trainer;

训练过程:ova_trainer也可以指定多个分类器(不推荐,如果需要多个不同的分类器,建议使用one-vs-one)

c++ 复制代码
void SvmOvaClassifier(const string &pfile)
{
    vector<iris_type> train_datas;
    vector<double> train_labels;
    map<int, string> label_map = LoadIrisVec(pfile, train_datas, train_labels);

    ova_trainer trainer;

    dlib::svm_nu_trainer<poly_kernel> poly_trianer;
    poly_trianer.set_kernel(poly_kernel(0.1, 2, 3)); // 多项式核参数
    trainer.set_trainer(poly_trianer);               // 也可以指定多个trianer,但不推荐

    // dlib::svm_c_trainer<rbf_kernel> rbf_trainer;
    // rbf_trainer.set_kernel(rbf_kernel(0.001)); // gamma 参数
    // rbf_trainer.set_c(10);                     // 大C参数
    // trainer.set_trainer(rbf_trainer, 1);       // 指定处理分类1

    dlib::one_vs_all_decision_function<ova_trainer, dlib::decision_function<poly_kernel>>
        learned_func = trainer.train(train_datas, train_labels);
    int ok_count = 0;
    for (int i = 0; i < train_datas.size(); i++)
    {
        iris_type ii_type = train_datas.at(i);
        double ret = learned_func(ii_type);
        if (ret == train_labels.at(i))
            ok_count += 1;
        cout << "predicted label:" << ret << "(" << label_map[ret] << ");"
             << "real label:" << label_map[train_labels.at(i)] << endl;
    }
    cout << "accurary:" << (ok_count * 1.0) / train_datas.size() << endl;
}

数据加载

加载iris.csv数据

c++ 复制代码
typedef struct
{
    float ft[4];
    std::string specie;
} Iris;

vector<Iris> LoadIris(const string &fpath)
{
    vector<Iris> vec;
    fstream input_file(fpath);
    string line;
    if (input_file.is_open())
    {
        getline(input_file, line); // 跳过头
        while (getline(input_file, line))
        {
            vector<string> sp_vec = SplitString(line, ',');
            if (sp_vec.size() == 0)
                continue;
            Iris iris;
            for (int i = 0; i < 4; i++)
            {
                iris.ft[i] = atof(sp_vec.at(i).c_str());
            }
            iris.specie = sp_vec.at(sp_vec.size() - 1);
            vec.push_back(iris);
        }
    }
    return vec;
}
相关推荐
Coding茶水间11 分钟前
基于深度学习的非机动车头盔检测系统演示与介绍(YOLOv12/v11/v8/v5模型+Pyqt5界面+训练代码+数据集)
图像处理·人工智能·深度学习·yolo·目标检测·机器学习·计算机视觉
brave and determined1 小时前
CANN训练营 学习(day9)昇腾AscendC算子开发实战:从零到性能冠军
人工智能·算法·机器学习·ai·开发环境·算子开发·昇腾ai
brave and determined2 小时前
CANN训练营 学习(day8)昇腾大模型推理调优实战指南
人工智能·算法·机器学习·ai实战·昇腾ai·ai推理·实战记录
源于花海3 小时前
迁移学习的第一类方法:数据分布自适应(1)——边缘分布自适应
人工智能·机器学习·迁移学习·数据分布自适应
科士威传动3 小时前
丝杆支撑座同轴度如何安装?
人工智能·科技·机器学习·自动化
_Li.4 小时前
机器学习-集成学习
人工智能·机器学习·集成学习
极度畅想4 小时前
脑电模型实战系列(三):基于 KNN 的 DEAP 脑电情绪识别 KNN 算法与 Canberra 距离深度剖析(三)
机器学习·knn·脑机接口·情绪识别·bci·canberra距离
一个没有感情的程序猿5 小时前
前端实现人体骨架检测与姿态对比:基于 MediaPipe 的完整方案
机器学习·计算机视觉·前端框架·开源
Dev7z5 小时前
基于Stanley算法的自动驾驶车辆路径跟踪控制研究
人工智能·机器学习·自动驾驶
_Li.5 小时前
机器学习-线性判别函数
人工智能·算法·机器学习