Проблемы с построением границы решения подобранной модели SVM с использованием функции stat_contour () ggplot2

Я пытаюсь понять, как построить границу решения для подобранной модели SVM в ggplot2. Прямо сейчас я пытаюсь сделать это с помощью stat_contour. Вот мой код с примером вызова моей функции в конце. Вы можете найти файлы данных, которые я использую, на моей странице github:

train.txt

test.txt

train <- read.table('train.txt', col.names = c('digit', 'intensity', 'symmetry'))
test <- read.table('test.txt', col.names = c('digit', 'intensity', 'symmetry'))    

        digits.SVM <- function(train, test, digits = c(1, 5), C = 0.01, kernel = 'radial', degree = 3, gamma = 1, coef0 = 0, scale = FALSE, type = 'C-classification', plotApproximation = FALSE) {
              library(e1071)
              library(ggplot2)
              library(reshape2)
              if(length(digits) != 1 && length(digits) != 2)
                stop('Invalid length of digits vector.  Must specify one or two digits to classify')
              if(length(digits) == 2) {
                train <- train[(train$digit == digits[1]) | (train$digit == digits[2]), ]
                test <- test[(test$digit == digits[1]) | (test$digit == digits[2]), ]
              }

              train$class <- -1
              test$class <- -1
              train[train$digit == digits[1], ]$class <- 1
              test[test$digit == digits[1], ]$class <- 1
              fit <- svm(class~intensity + symmetry, data = train, cost = C, kernel = kernel, degree = degree, gamma = gamma, coef0 = coef0, scale = scale, type = type)
              class_fitted <- predict(fit, train[c('intensity', 'symmetry')])

              gridRange <- apply(train[c('intensity', 'symmetry')], 2, range)
              x1 <- seq(from = gridRange[1, 1] - 0.025, to = gridRange[2, 1] + 0.025, length = 75)
              x2 <- seq(from = gridRange[1, 2] - 0.05, to = gridRange[2, 2] + 0.05, length = 75)
              grid <- expand.grid(intensity = x1, symmetry = x2)
              grid$class <- predict(fit, grid)
              decisionValues <- predict(fit, grid, decision.values = TRUE)
              grid$z <- as.vector(attributes(decisionValues)$decision)
              print(colnames(grid))
              print(head(grid))
              p <- ggplot(data = grid, aes(intensity, symmetry, colour = as.factor(class))) + 
                geom_point(size = 1.5) +
                scale_fill_manual(values = c('red', 'black')) + 
                stat_contour(data = grid, aes(x = intensity, y = symmetry, z = z), breaks = c(0)) + 
                geom_point(data = train, aes(intensity, symmetry, colour = as.factor(class)), alpha = 0.7) +
                scale_colour_manual(values = c('red', 'black')) + labs(colour = 'Class') +
                scale_x_continuous(expand = c(0,0)) +
                scale_y_continuous(expand = c(0,0))
              print(p)
              mean(train$class != class_fitted)
            }

    digits.SVM(train, test, digits = c(0), kernel = 'polynomial', degree = 2, coef0 = 1)

Моя проблема возникает при установке параметра «перерывы» в stat_contour (). Большинство значений, которые я установил, разбиваются, чтобы не вызывать никаких проблем; вот график, который получается, когда breaks = -1.

breaks = -1

Однако правильная граница соответствует контуру, который возник бы в результате установки breaks = 0, и когда я установил breaks ближе к 0, ggplot начинает испытывать проблемы с построением контура. Он начинает обрезаться и при значении ровно 0 просто не рисует контур.

Вот пример графика с breaks = -0.05: breaks = 0.05

Как видите, граница начинает срезаться. Вот график с использованием breaks = 0: breaks = 0

Вырезан весь контур.

Я также получаю это сообщение об ошибке:

Предупреждающие сообщения: 1: Невозможно создать данные контура

Я относительно новичок в ggplot2 и не уверен, что stat_contour () делает в фоновом режиме. Я пытался найти его реализацию, но безуспешно. Мы будем благодарны за любую помощь и разъяснения!

Я также приветствовал бы любые предложения о лучших способах достижения этой цели.


person Community    schedule 22.11.2014    source источник


Ответы (1)


Мне удалось успешно построить контуры, однако я сделал это, используя базовую графику R вместо ggplot2. Мне все еще интересно узнать, как создавать похожие графики с помощью ggplot2.

Вот мой обновленный код и несколько примеров графиков:

train <- read.table('train.txt', col.names = c('digit', 'intensity', 'symmetry'))
test <- read.table('test.txt', col.names = c('digit', 'intensity', 'symmetry'))

    digits.SVM <- function(train, test, digits = c(1, 5), C = 0.01, kernel = 'radial', degree = 3, gamma = 1, coef0 = 0, scale = FALSE, type = 'C-classification', classification.plot = FALSE) {
      library(e1071)
      if(length(digits) != 1 && length(digits) != 2)
        stop('Invalid length of digits vector.  Must specify one or two digits to classify')
      if(length(digits) == 2) {
        train <- train[(train$digit == digits[1]) | (train$digit == digits[2]), ]
        test <- test[(test$digit == digits[1]) | (test$digit == digits[2]), ]
      }

      train$class <- -1
  test$class <- -1
      train[train$digit == digits[1], ]$class <- 1
      test[test$digit == digits[1], ]$class <- 1
      fit <- svm(class~intensity + symmetry, data = train, cost = C, kernel = kernel, degree = degree, gamma = gamma, coef0 = coef0, scale = scale, type = type)
      train$fitted <- predict(fit, train[c('intensity', 'symmetry')])
      test$fitted <- predict(fit, test[c('intensity', 'symmetry')])

      if(classification.plot) {
        gridRange <- apply(train[c('intensity', 'symmetry')], 2, range)
        x1 <- seq(from = gridRange[1, 1] - 0.025, to = gridRange[2, 1] + 0.025, length = 75)
        x2 <- seq(from = gridRange[1, 2] - 0.05, to = gridRange[2, 2] + 0.05, length = 75)
        grid <- expand.grid(intensity = x1, symmetry = x2)
        grid$class <- predict(fit, grid)
        decisionValues <- predict(fit, grid, decision.values = TRUE)
        grid$z <- as.vector(attributes(decisionValues)$decision)

        ##  TESTING PURPOSES  
        #   print(range(grid$z))
    #   print(sum(train$fitted == -1))
        #   print(length(train$fitted))

        ##  GGPLOT VERSION OF PLOT; CONTOUR NEEDS DEBUGGING  
        #   library(ggplot2)
        #   p <- ggplot(data = grid, aes(intensity, symmetry, colour = as.factor(class))) + 
        #     geom_point(size = 1.5) +
        #     scale_fill_manual(values = c('red', 'black')) + 
        #     stat_contour(data = grid, aes(x = intensity, y = symmetry, z = z), breaks = c(0)) + 
        #     geom_point(data = train, aes(intensity, symmetry, colour = as.factor(class)), alpha = 0.7) +
        #     scale_colour_manual(values = c('red', 'black')) + labs(colour = 'Class') +
        #     scale_x_continuous(expand = c(0,0)) +
        #     scale_y_continuous(expand = c(0,0))
        #   print(p)

        par(mfrow = c(1,2))
        ## Note: RGB Specification seems to increase running and plotting time complexity
        plot(grid[c('intensity', 'symmetry')], col = ifelse(grid$class == 1, '#0571B070', '#CA002070'), main = 'Training', pch='20', cex=.2)
        points(train[c('intensity', 'symmetry')], col = ifelse(train$class == 1, '#0571B070', '#CA002070'))
        contour(x1, x2, matrix(grid$z, length(x1), length(x2)), level=0, lwd = 1.5, drawlabels = FALSE, add=TRUE)
        plot(grid[c('intensity', 'symmetry')], col = ifelse(grid$class == 1, '#0571B070', '#CA002070'), main = 'Test', pch='20', cex=.2)
        points(test[c('intensity', 'symmetry')], col = ifelse(test$class == 1, '#0571B070', '#CA002070'))
        contour(x1, x2, matrix(grid$z, length(x1), length(x2)), level=0, lwd = 1.5, drawlabels = FALSE, add=TRUE)
        mtext(paste('Digit Classification Plots:', digits[1], 'vs', ifelse(length(digits) == 2, digits[2], 'All'), '\nKernel:', kernel, '\nC:', C), line = -3, outer = TRUE)
      }

      list(E_in = mean(train$fitted != train$class), E_out = mean(test$fitted != test$class), num_support_vectors = nrow(fit$SV))
    }

digits.SVM(train, test, digits = c(0), kernel = 'polynomial', degree = 2, coef0 = 1, classification.plot = TRUE)

digits.SVM(train, test, digits = c(1, 5), kernel = 'radial', gamma = 1, C = 10^6, classification.plot = TRUE)

введите описание изображения здесь

введите описание изображения здесь

person Gaussian0617    schedule 24.11.2014
comment
Спасибо за это. Пожалуйста, зарегистрируйтесь и объедините свои аккаунты. - person gung - Reinstate Monica; 24.11.2014
comment
Перейдите в наш Справочный центр, чтобы объединить две свои учетные записи. - person chl; 09.12.2014
comment
Взгляните на этот прекрасный учебник, который содержит множество примеров графиков на основе ggplot2 с топографическими контурами и шкалой градиента. - person Aleksandr Blekh; 09.01.2015