메뉴 건너뛰기

enjoyTools.net

svm_c_ex.cpp

2020.02.16 07:19

꿈돌이 조회 수:352

번역기 돌린 거

 

// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt

/*

 

    This is an example illustrating the use of the support vector machine

    utilities from the dlib C++ Library.  In particular, we show how to use the

    C parametrization of the SVM in this example.

 

    This example creates a simple set of data to train on and then shows

    you how to use the cross validation and svm training functions

    to find a good decision function that can classify examples in our

    data set.

 

 

    The data used in this example will be 2 dimensional data and will

    come from a distribution where points with a distance less than 10

    from the origin are labeled +1 and all other points are labeled

    as -1.

    

    Dlib C++ 라이브러리의 Support Vector Machine 유틸리티의 사용을 보여 주는 예입니다.

    특히 이 예에서는 SVM의 C 매개 변수를 사용하는 방법을 보여 줍니다.

 

    이 예제에서는 교육할 간단한 데이터 세트를 생성한 다음 교차 검증 및 svm 교육 기능을 사용하여

    데이터 세트의 예를 분류할 수 있는 올바른 의사 결정 기능을 찾는 방법을 보여 줍니다.

 

    이 예에서 사용되는 데이터는 2차원 데이터이며, 원본에서 거리가 10 미만인 점이 +1로

    표시되고 다른 모든 점이 -1로 레이블이 지정된 분포에서 가져옵니다.

*/

 

 

#include <iostream>

#include <dlib/svm.h>

 

using namespace std;

using namespace dlib;

 

 

int main()

{

    // The svm functions use column vectors to contain a lot of the data on

    // which they operate. So the first thing we do here is declare a convenient

    // typedef.  

 

    // This typedef declares a matrix with 2 rows and 1 column.  It will be the

    // object that contains each of our 2 dimensional samples.   (Note that if

    // you wanted more than 2 features in this vector you can simply change the

    // 2 to something else.  Or if you don't know how many features you want

    // until runtime then you can put a 0 here and use the matrix.set_size()

    // member function)

    /*

    svm 함수는 열 벡터를 사용하여 작동하는 많은 데이터를 포함합니다.

그래서 우리가 여기서 가장 먼저 할 일은 편리한 typedef를 선언하는 것입니다.

 

이 입력은 2개의 행과 1개의 열이 있는 매트릭스를 선언합니다.

이 물체는 우리의 2차원 샘플이 각각 들어 있는 물체가 될 것입니다.

(이 벡터에 두 개 이상의 피쳐를 원하는 경우 두 기능을 다른 기능으로 변경할 수 있습니다.

아니면 런타임까지 원하는 기능의 수를 모르는 경우 여기에 0을 입력하고 matrix.set_size()

멤버함수를 사용할 수 있습니다.)

    */

    typedef matrix<double, 2, 1> sample_type;

 

    // This is a typedef for the type of kernel we are going to use in this

    // example.  In this case I have selected the radial basis kernel that can

    // operate on our 2D sample_type objects.  You can use your own custom

    // kernels with these tools as well, see custom_trainer_ex.cpp for an

    // example.

    /*

    이것은 이 예에서 사용할 커널 유형에 대한 typedef입니다.

이번 경우에는 우리의 2D sample_type 개체에서 운용할 수 있는 RBF(방사형 기저 커널)을 선택했습니다.

더불어, 이러한 도구를 사용자 지정 커널에서도 사용할 수 있습니다.

custom_trainer_ex.cpp 예제를 참조하십시오.

    */

    typedef radial_basis_kernel<sample_type> kernel_type;

 

 

    // Now we make objects to contain our samples and their respective labels.

    /* 이제 우리는 우리의 샘플과 각각의 라벨을 포함하는 객체를 만듭니다. */

    std::vector<sample_type> samples;

    std::vector<double> labels;

 

    // Now let's put some data into our samples and labels objects.  We do this

    // by looping over a bunch of points and labeling them according to their

    // distance from the origin.

    /*

    이제 우리의 샘플과 라벨 객체에 데이터를 주입해봅시다.

우리는 많은 포인트를 반복하고 원점에서 거리에 따른 라벨링하는 작업입니다.

    */

    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);

 

            // if this point is less than 10 from the origin

            /* 현재 포인트가 원점에서 10보다 작은 경우 */

            if (sqrt((double)r*r + c*c) <= 10)

                labels.push_back(+1);

            else

                labels.push_back(-1);

 

        }

    }

 

 

    // Here we normalize all the samples by subtracting their mean and dividing

    // by their standard deviation.  This is generally a good idea since it

    // often heads off numerical stability problems and also prevents one large

    // feature from smothering others.  Doing this doesn't matter much in this

    // example so I'm just doing this here so you can see an easy way to

    // accomplish it.

    /*

    여기서는 평균을 빼고 표준 편차로 나누어 모든 샘플을 정규화합니다.

이것은 수치 안정성 문제를 해결하고 하나의 큰 feature가 다른 feature를 덮어버리는 것을

방지하기 때문에 일반적으로 좋은 아이디어입니다.

이 예제에서는 정규화 작업이 별로 중요한 건 아니므로, 요건을 만족하는 쉬운 방법만

보여줄 겁니다.

    */

    vector_normalizer<sample_type> normalizer;

    // Let the normalizer learn the mean and standard deviation of the samples.

    /* 정규화기에서 표본의 평균 및 표준 편차를 학습합니다. */

    normalizer.train(samples);

    // now normalize each sample

    /* 각 샘플을 정규화시킵니다 */

    for (unsigned long i = 0; i < samples.size(); ++i)

        samples[i] = normalizer(samples[i]); 

 

 

    // Now that we have some data we want to train on it.  However, there are

    // two parameters to the training.  These are the C and gamma parameters.

    // Our choice for these parameters will influence how good the resulting

    // decision function is.  To test how good a particular choice of these

    // parameters are we can use the cross_validate_trainer() function to perform

    // n-fold cross validation on our training data.  However, there is a

    // problem with the way we have sampled our distribution above.  The problem

    // is that there is a definite ordering to the samples.  That is, the first

    // half of the samples look like they are from a different distribution than

    // the second half.  This would screw up the cross validation process but we

    // can fix it by randomizing the order of the samples with the following

    // function call.

    /*

    이제 훈련용 데이터를 확보했습니다.

하지만, 훈련에 사용할 두 개의 매개 변수가 있는데, C와 gamma 변수입니다.

이 변수에 대한 우리의 선택은 decision function 함수 결과에 영향을 줍니다.

변수들에 대한 특정 선택의 영향을 테스트하기 위해, 우리는 훈련 데이터에 n-fold

교차 검증을 실시하기 위한 cross_validate_trainer() 함수를 사용할 수 있습니다.

다만, 위의 분포를 샘플링하는 방식에 문제가 있습니다.

문제는 샘플에 명시된 순서가 존재한다는 것입니다.

즉, 샘플의 절반 앞쪽이 절반 뒷쪽과는 다른 분포를 보인다는 것입니다.

이렇게 하면 교차 검증 프로세스가 엉망이 되지만 다음과 같은 함수 호출로

표본의 순서를 랜덤화하여 교정할 수 있습니다.

    */

    randomize_samples(samples, labels);

 

 

    // here we make an instance of the svm_c_trainer object that uses our kernel

    // type.

    /* 여기서 우리의 커널 유형을 사용하는 svm_c_trainer 개체의 인스턴스를 만듭니다. */

    svm_c_trainer<kernel_type> trainer;

 

// Now we loop over some different C and gamma values to see how good they

// are.  Note that this is a very simple way to try out a few possible

// parameter choices.  You should look at the model_selection_ex.cpp program

// for examples of more sophisticated strategies for determining good

// parameter choices.

/*

이제 우리는 몇 가지 다른 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)

        {

            // tell the trainer the parameters we want to use

            /* 훈련기에게 사용하고자하는 파라미터를 알려줍니다 */

            trainer.set_kernel(kernel_type(gamma));

            trainer.set_c(C);

 

            cout << "gamma: " << gamma << "    C: " << C;

            // Print out the cross validation accuracy for 3-fold cross validation using

            // the current gamma and C.  cross_validate_trainer() returns a row vector.

            // The first element of the vector is the fraction of +1 training examples

            // correctly classified and the second number is the fraction of -1 training

            // examples correctly classified.

            /*

            현재의 gamma 및 C를 사용하여 3중 교차 검증에 대한 정확도를 출력합니다.

cross_validate_trainer()는 행 벡터를 반환합니다.

벡터의 첫 번째 요소는 +1 훈련 예제의 일부이며, 두 번째 숫자는 -1 훈련 예제를

올바르게 분류한 비율입니다.

            */

            cout << "     cross validation accuracy: " 

                 << cross_validate_trainer(trainer, samples, labels, 3);

        }

    }

 

 

    // From looking at the output of the above loop it turns out that good

    // values for C and gamma for this problem are 5 and 0.15625 respectively.

    // So that is what we will use.

 

    // Now we train on the full set of data and obtain the resulting decision

    // function.  The decision function will return values >= 0 for samples it

    // predicts are in the +1 class and numbers < 0 for samples it predicts to

    // be in the -1 class.

    /*

    위의 루프의 출력을 보면 이 문제에 대한 C 및 감마의 양호한 값이 각각 5와 0.15625임을

    알 수 있습니다.

그러니 우리는 이것을 사용할 것입니다.

 

이제 우리는 전체 데이터 집합에 대해 훈련하고 결과 의사결정 함수를 얻습니다.

의사결정 함수는 예측한 표본의 경우 +1 클래스에 있을 경우 >= 0 값을 반환하고 -1 클래스에

있을 것으로 예측하는 표본의 경우 숫자 < 0을 반환합니다.

    */

    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;

 

    // Here we are making an instance of the normalized_function object.  This

    // object provides a convenient way to store the vector normalization

    // information along with the decision function we are going to learn.

    /*

    여기서는 정규화된_기능 개체의 인스턴스를 만듭니다.

이 오브젝트는 앞으로 학습할 의사결정 기능과 함께 벡터 정규화 정보를 편리하게

저장하는 방법을 제공합니다.

    */

    funct_type learned_function;

    /* 정규화 정보 저장 */

    learned_function.normalizer = normalizer;  // save normalization information

    /* SVM 훈련과 결과 저장을 수행 */

    learned_function.function = trainer.train(samples, labels); // perform the actual SVM training and save the results

 

    // print out the number of support vectors in the resulting decision function

    /* 결과 의사결정 함수내 support vector 수를 출력 */

    cout << "\nnumber of support vectors in our learned_function is " 

         << learned_function.function.basis_vectors.size() << endl;

 

    // Now let's try this decision_function on some samples we haven't seen before.

    /*

    이제 이전에 보지 못한 샘플에 대해 이 결정 함수를 사용해 보겠습니다.

    (=테스트하려는 데이터 주입)

    */

    sample_type sample;

 

    sample(0) = 3.123;

    sample(1) = 2;

    cout << "This is a +1 class example, the classifier output is " << learned_function(sample) << endl;

 

    sample(0) = 3.123;

    sample(1) = 9.3545;

    cout << "This is a +1 class example, the classifier output is " << learned_function(sample) << endl;

 

    sample(0) = 13.123;

    sample(1) = 9.3545;

    cout << "This is a -1 class example, the classifier output is " << learned_function(sample) << endl;

 

    sample(0) = 13.123;

    sample(1) = 0;

    cout << "This is a -1 class example, the classifier output is " << learned_function(sample) << endl;

 

 

    // We can also train a decision function that reports a well conditioned

    // probability instead of just a number > 0 for the +1 class and < 0 for the

    // -1 class.  An example of doing that follows:

/*

또한 +1 클래스를 위한 number > 0 및 -1 클래스를 위한 number < 0 같은 조건 대신, 양호한 조건일 확률을

보고하는 의사결정 함수를 훈련할 수 있습니다.

그 예는 다음과 같습니다.:

*/

    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);

    // Now we have a function that returns the probability that a given sample is of the +1 class.

    /* 이제 주어진 샘플이 +1 클래스일 확률을 반환하는 함수를 가졌습니다. */

 

    // print out the number of support vectors in the resulting decision function.  

    // (it should be the same as in the one above)

    /*

    결과 의사결정 함수에서 지원 벡터의 수를 출력합니다.

(위의 것과 같아야 합니다)

    */

    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 << "This +1 class example should have high probability.  Its probability is: " 

         << learned_pfunct(sample) << endl;

 

    sample(0) = 3.123;

    sample(1) = 9.3545;

    cout << "This +1 class example should have high probability.  Its probability is: " 

         << learned_pfunct(sample) << endl;

 

    sample(0) = 13.123;

    sample(1) = 9.3545;

    cout << "This -1 class example should have low probability.  Its probability is: " 

         << learned_pfunct(sample) << endl;

 

    sample(0) = 13.123;

    sample(1) = 0;

    cout << "This -1 class example should have low probability.  Its probability is: " 

         << learned_pfunct(sample) << endl;

 

 

 

    // Another thing that is worth knowing is that just about everything in dlib

    // is serializable.  So for example, you can save the learned_pfunct object

    // to disk and recall it later like so:

/*

또 다른 중요한 것은 위의 모든 것을 dlib에서 serialize 할 수 있다는 것입니다.

예를 들면 learned_pfunt 개체를 디스크에 저장하고 나중에 아래와 같이 호출할 수 있습니다.:

*/

    serialize("saved_function.dat") << learned_pfunct;

 

    // Now let's open that file back up and load the function object it contains.

    /* 이제 저장된 이 파일을 열어 포함된 함수 개체를 불러들이겠습니다. */

    deserialize("saved_function.dat") >> learned_pfunct;

 

    // Note that there is also an example program that comes with dlib called

    // the file_to_code_ex.cpp example.  It is a simple program that takes a

    // file and outputs a piece of C++ code that is able to fully reproduce the

    // file's contents in the form of a std::string object.  So you can use that

    // along with the std::istringstream to save learned decision functions

    // inside your actual C++ code files if you want.

    /*

    dlib과 함께 제공되는 file_to_code_ex.cpp 예제 프로그램도 있습니다.

파일을 가져와서 파일의 내용을 std::string 객체의 형태로 완전히 재현할 수 있는

C++ 코드를 출력하는 간단한 프로그램입니다.

따라서 이 기능을 std::istringstream과 함께 사용하여 학습된 의사결정 함수를

실제 C++ 코드 파일 내에 저장할 수 있습니다.

    */

 

 

 

 

    // Lastly, note that the decision functions we trained above involved well

    // over 200 basis vectors.  Support vector machines in general tend to find

    // decision functions that involve a lot of basis vectors.  This is

    // significant because the more basis vectors in a decision function, the

    // longer it takes to classify new examples.  So dlib provides the ability

    // to find an approximation to the normal output of a trainer using fewer

    // basis vectors.

    /*

    마지막으로, 위에서 교육한 의사결정 함수에는 200개 이상의 기본 벡터가 포함되었습니다.

일반적으로 Support Vector Machine은 많은 기본 벡터가 포함된 의사결정 함수를 찾는 경향이 있습니다.

이는 의사결정 함수에 기본 벡터가 많을수록 새로운 사례를 분류하는 데 시간이 오래 걸리기 때문에 중요합니다.

따라서 dlib는 더 적은 수의 기본 벡터를 사용하여 훈련기의 정상 출력에 대한 근사치를 찾을 수 있는 기능을 제공합니다.

    */

 

    // Here we determine the cross validation accuracy when we approximate the

    // output using only 10 basis vectors.  To do this we use the reduced2()

    // function.  It takes a trainer object and the number of basis vectors to

    // use and returns a new trainer object that applies the necessary post

    // processing during the creation of decision function objects.

    /*

    여기서는 10개의 기본 벡터만 사용하여 출력에 근접한 경우 교차 유효성 검사 정확도를 결정합니다.

이를 위해 reduce2() 함수를 사용합니다.

이 함수는 훈련기 개체와 사용할 기본 벡터의 수를 받고, 의사결정 함수 개체를 생성하는 동안 필요한

사후 처리를 적용하는 새 트레이너 개체를 반환합니다.

    */

    cout << "\ncross validation accuracy with only 10 support vectors: " 

         << cross_validate_trainer(reduced2(trainer,10), samples, labels, 3);

 

    // Let's print out the original cross validation score too for comparison.

    /* 비교를 위해 원래의 교차 유효성 검사 점수도 출력해 보겠습니다. */

    cout << "cross validation accuracy with all the original support vectors: " 

         << cross_validate_trainer(trainer, samples, labels, 3);

 

    // When you run this program you should see that, for this problem, you can

    // reduce the number of basis vectors down to 10 without hurting the cross

    // validation accuracy. 

    /*

    이 프로그램을 실행하면 교차 검증 정확도를 손상시키지 않으면서 기본 벡터 수를 10개로 줄일 수 있습니다.

    */

 

 

    // To get the reduced decision function out we would just do this:

/* 축소된 의사 결정 기능을 얻기 위해 다음과 같은 작업을 수행합니다.: */

    learned_function.function = reduced2(trainer,10).train(samples, labels);

    // And similarly for the probabilistic_decision_function:

    /* 그리고 probabilistic_decision_function 비슷하게 아래 작업을 수행합니다.: */

    learned_pfunct.function = train_probabilistic_decision_function(reduced2(trainer,10), samples, labels, 3);

}