dlib庫中svm 例程解析

int main()
{
    // svm函數使用列向量,這裏我們先定義一個方面的typedef
    // 這個 typedef 聲明瞭一個2行1列的矩陣。如果你像包含更多維,把2修改成更多即可。
    //如果你不知道特徵有多少維,你可以先把這個值設置爲零,然後用的時候,用matrix.set_size()函數即可。 
    typedef matrix<double, 2, 1> sample_type;


    // 核函數的定義
    //我選擇了一個radial basis kernel,這個核適合我們的2維的樣本。
    //你也可以使用自己定義的任何kernels,可以參考 custom_trainer_ex.cpp
    typedef radial_basis_kernel<sample_type> kernel_type;


    // 現在我們聲明 samples和labels表示樣本和其對應的標記
    std::vector<sample_type> samples;
    std::vector<double> labels;




    // 給samples和labels賦值。用一個循環生成一系列的點,然後一句他們到圓心的距離來標記
    for (int r = -20; r <= 20; ++r)
    {
        for (int c = -20; c <= 20; ++c)
        {
            sample_type samp;
            samp(0) = r;
            samp(1) = c;
            samples.push_back(samp);




            //如果到圓心的距離小於10
            if (sqrt((double)r*r + c*c) <= 10)
                labels.push_back(+1);
            else
                labels.push_back(-1);




        }
    }








    // 歸一化樣本,減去均值,除以標準差。這是去除數值穩定性問題的好辦法,防止某些值過大。
    // 做這一步,對本例的問題來說,並不是那麼重要。下面的代碼會教你怎麼做歸一化。  
    vector_normalizer<sample_type> normalizer;
    // 計算均值和方差,放入normalizer的成員變量中
    normalizer.train(samples);
    // 然後分別歸一化每一個
    for (unsigned long i = 0; i < samples.size(); ++i)
        samples[i] = normalizer(samples[i]); 








    // 數據有了,開始訓練。這裏有兩個超參數:C and gamma.這兩個超參數
    // 將影響分類函數的性能。爲了測試並選擇出好的超參數,我們可以用cross_validate_trainer()
    // 來進行n-ford 交叉驗證。然而,我們數據的分佈還有些問題。數據有順序,一部分全是正樣本,
    // 一部分全是負樣本。 前辦部分和後辦部分分別來自不同的分佈。這將使交叉驗證無效。所以我們進行
    // 隨機化操作,打亂樣本的嚴格順序。用randomize_samples函數。
    randomize_samples(samples, labels);








    // 用我們聲明的核函數來實例化svm_c_trainer對象
    svm_c_trainer<kernel_type> trainer;




    // 現在我們循環調用一些不同的 C 和 gamma 的值來測試決策函數的性能。這是非常簡單的方式。
    // 你可以看看model_selection_ex.cpp 來看看更多用複雜策略來選擇較好超參數的方法
    cout << "doing cross validation" << endl;
    for (double gamma = 0.00001; gamma <= 1; gamma *= 5)
    {
        for (double C = 1; C < 100000; C *= 5)
        {
            //設置我們用的超參數
            trainer.set_kernel(kernel_type(gamma));
            trainer.set_c(C);




            cout << "gamma: " << gamma << "    C: " << C;
            // 輸出交叉驗證的準確度。
            // cross_validate_trainer()返回一個行向量,第一個元素爲正例的準確度,第二個元素
            // 負例的準確度。
            cout << "     cross validation accuracy: " 
                 << cross_validate_trainer(trainer, samples, labels, 3);
        }
    }








    //查看上面的輸出,發現C=5,gamma=0.15625 is good.so we will use them




    // 現在我們用所有的樣本訓練,然後得到決策函數。
    // 正樣本時,決策函數將返回大於零的值。負樣本是決策函數返回小於零的值。


    trainer.set_kernel(kernel_type(0.15625));
    trainer.set_c(5);
    typedef decision_function<kernel_type> dec_funct_type;
    typedef normalized_function<dec_funct_type> funct_type;




    // 這裏我們實例化一個對象,這個對象可以提供一個方便的方式來存儲歸一化後的信息和訓練得到的決策函數。
    funct_type learned_function;
    learned_function.normalizer = normalizer;  // 保存歸一化信息。
    learned_function.function = trainer.train(samples, labels); // 訓練並保存結果到這個對象的function中


    // 輸出支持向量的數據
    cout << "\nnumber of support vectors in our learned_function is " 
         << learned_function.function.basis_vectors.size() << endl;




    // 現在來測試我們得到的決策函數在新樣本上的性能
    sample_type sample;




    sample(0) = 3.123;
    sample(1) = 2;
    cout << "這是正例,分類器的輸出是 " << learned_function(sample) << endl;




    sample(0) = 3.123;
    sample(1) = 9.3545;
    cout << "這是正例,分類器的輸出是  " << learned_function(sample) << endl;




    sample(0) = 13.123;
    sample(1) = 9.3545;
    cout << "這是負例,分類器的輸出是  " << learned_function(sample) << endl;




    sample(0) = 13.123;
    sample(1) = 0;
    cout << "這是負例,分類器的輸出是  " << learned_function(sample) << endl;








    // 我們也可以訓練一個可以返回概率的決策函數,不單單隻看大於小於零的情況,下面是例子:
    typedef probabilistic_decision_function<kernel_type> probabilistic_funct_type;  
    typedef normalized_function<probabilistic_funct_type> pfunct_type;




    pfunct_type learned_pfunct; 
    learned_pfunct.normalizer = normalizer;
    learned_pfunct.function = train_probabilistic_decision_function(trainer, samples, labels, 3);
    // 現在我們擁有的可以返回概率的決策函數.


    // 輸出決策函數含有的支持向量的數目,應該與上面相同
    cout << "\nnumber of support vectors in our learned_pfunct is " 
         << learned_pfunct.function.decision_funct.basis_vectors.size() << endl;




    sample(0) = 3.123;
    sample(1) = 2;
    cout << "正例應該輸出高概率值,輸出概率爲: " 
         << learned_pfunct(sample) << endl;




    sample(0) = 3.123;
    sample(1) = 9.3545;
    cout << "正例應該輸出高概率值,輸出概率爲: " 
         << learned_pfunct(sample) << endl;




    sample(0) = 13.123;
    sample(1) = 9.3545;
    cout << "負例應該輸出低概率值,輸出概率爲: " 
         << learned_pfunct(sample) << endl;




    sample(0) = 13.123;
    sample(1) = 0;
    cout << "負例應該輸出低概率值,輸出概率爲: " 
         << learned_pfunct(sample) << endl;












    // 值得注意的是,dlib中的所有對象都可以序列化到文件中。所以你可以把學到的分類函數保存到磁盤中,然後稍後再從磁盤讀出,使用。
    serialize("saved_function.dat") << learned_pfunct;




    //從文件中讀入學到的分類函數
    deserialize("saved_function.dat") >> learned_pfunct;




    // 這裏還有個例子:file_to_code_ex.cpp,輸入爲文件,輸出爲 c++代碼。所以你可以用這個和std::istringstream 來保存你學習到的決策函數














    // 最後,注意到我們訓練得到的決策函數包含200個支持向量。支持向量越多,計算量越大,所以dlib提供近似的性能,但更少的支持向量
    cout << "\ncross validation accuracy with only 10 support vectors: " 
         << cross_validate_trainer(reduced2(trainer,10), samples, labels, 3);




    // 打印出原來的交叉驗證得分用於對比
    cout << "cross validation accuracy with all the original support vectors: " 
         << cross_validate_trainer(trainer, samples, labels, 3);




    // 你可以在保證交叉驗證的性能的同時,減少支持向量的數目,減少到10個。








    // 通過如下函數操作獲取減小後的決策函數:
    learned_function.function = reduced2(trainer,10).train(samples, labels);
    // 對於概率決策函數,也用類似的方法得到:
    learned_pfunct.function = train_probabilistic_decision_function(reduced2(trainer,10), samples, labels, 3);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章