Что не так с моим классификатором SURF/KMeans

Я пытаюсь создать классификатор / предиктор, используя SURF и наивный байесовский. Я в значительной степени следую технике из "Визуальной категоризации с сумками ключевых точек" от Dance, Csurka... Я использую SURF вместо SIFT.

Мои результаты довольно ужасны, и я не уверен, где моя ошибка. Я использую 20 образцов автомобилей (ветчина) и 20 образцов мотоциклов (спам) из набора CalTec. Я подозреваю, что именно так я создаю свой словарный запас. Я могу видеть, что классификатор EMGU/OpenCV kmeans2 возвращает разные результаты при одном и том же входе дескриптора SURF. Это делает меня подозрительным. Вот мой код до сих пор.

public Matrix<float> Extract<TColor, TDepth>(Image<TColor, TDepth> image)
        where TColor : struct, Emgu.CV.IColor
        where TDepth : new()
    {            
        ImageFeature[] modelDescriptors;

        using (var imgGray = image.Convert<Gray, byte>())
        {
            var modelKeyPoints = surfCPU.DetectKeyPoints(imgGray, null);
            //the surf descriptor is a size 64 vector describing the intensity pattern surrounding
            //the corresponding modelKeyPoint
            modelDescriptors = surfCPU.ComputeDescriptors(imgGray, null, modelKeyPoints);
        }

        var samples = new Matrix<float>(modelDescriptors.Length, DESCRIPTOR_COUNT);//SURF Descriptors have 64 samples
        for (int k = 0; k < modelDescriptors.Length; k++)
        {
            for (int i = 0; i < modelDescriptors[k].Descriptor.Length; i++)
            {
                samples.Data[k, i] = modelDescriptors[k].Descriptor[i];
            }

        }

        //group descriptors into clusters using K-means to form the feature vectors
        //create "vocabulary" based on square-error partitioning K-means
        var centers = new Matrix<float>(CLUSTER_COUNT, samples.Cols, 1);
        var term = new MCvTermCriteria();
        var labelVector = new Matrix<int>(modelDescriptors.Length, 1);
        var cluster = CvInvoke.cvKMeans2(samples, CLUSTER_COUNT, labelVector, term, 3, IntPtr.Zero, 0, centers, IntPtr.Zero);

        //this is the quantized feature vector as described in Dance, Csurska Bag of Keypoints (2004)
        var keyPoints = new Matrix<float>(1, CLUSTER_COUNT);

        //quantize the vector into a feature vector
        //making a histogram of the result counts
        for (int i = 0; i < labelVector.Rows; i++)
        {
            var value = labelVector.Data[i, 0];
            keyPoints.Data[0, value]++;
        }
        //normalize the histogram since it will have different amounts of points
        keyPoints = keyPoints / keyPoints.Norm;
        return keyPoints;
    }

Выходные данные передаются в NormalBayesClassifier. Вот как я это тренирую.

Parallel.For(0, hamCount, i =>
            {
                using (var img = new Image<Gray, byte>(_hams[i].FullName))
                {
                    var features = _extractor.Extract(img);
                    features.CopyTo(trainingData.GetRow(i));
                    trainingClass.Data[i, 0] = 1;
                }
            });

        Parallel.For(0, spamCount, j =>
        {
            using (var img = new Image<Gray, byte>(_spams[j].FullName))
            {
                var features = img.ClassifyFeatures(_extractor);
                features.CopyTo(trainingData.GetRow(j));
                trainingClass.Data[j + hamCount, 0] = 0;
            }
        });

        using (var classifier = new NormalBayesClassifier())
        {
            if (classifier.Train(trainingData, trainingClass, null, null, false))
            {

                classifier.Save(_statModelFilePath);
            }
        }

Когда я вызываю Predict с помощью NormalBayesClassifier, он возвращает 1(совпадение) для всех обучающих примеров... ветчина и спам.

Любая помощь будет принята с благодарностью.

Редактировать. Еще одно замечание: я выбрал CLUSTER_COUNT от 5 до 500 с одинаковым результатом.

1 ответ

Решение

Проблема была скорее концептуальной, чем технической. Я не понимал, что кластер K Means создавал словарь для "всего" набора данных. Способ сделать это правильно - дать CvInvoke.cvKMeans2 вызов обучающей матрицы, содержащей все функции для каждого изображения. Я строил словарный запас каждый раз на основе одного изображения.

Мое окончательное решение заключалось в том, чтобы использовать код SURF в своем собственном методе и запускать его на каждом изображении ветчины и спама. Затем я использовал массивный набор результатов для построения обучающей матрицы и передал это методу CvInvoke.cvKMeans2. Прошло довольно много времени, чтобы закончить тренировку. У меня всего около 3000 изображений.

Мои результаты были лучше. Уровень прогноза был на 100% точным с данными обучения. Моя проблема сейчас заключается в том, что я, вероятно, страдаю от чрезмерной подгонки, потому что его прогноз по-прежнему плох для данных без обучения. Я поэкспериментирую с порогом гессиана в алгоритме SURF, а также с количеством кластеров, чтобы посмотреть, смогу ли я свести к минимуму перебор.

Другие вопросы по тегам