Support Vector Machine en R : SVM linéaire, polynomial et radial

Trouvez la frontière à marge maximale, ajustez le coût et le noyau, et évaluez honnêtement un SVM tidymodels

Un guide pratique du support vector machine (SVM) pour la classification en R. Construisez des classifieurs SVM linéaires, polynomiaux et radiaux (RBF), comprenez comment fonctionnent la frontière à marge maximale et ses vecteurs de support, ajustez le coût C (souplesse de la marge) et le sigma RBF par validation croisée, et découvrez pourquoi vous devez d’abord standardiser les prédicteurs. Travaillé à la manière moderne tidymodels avec parsnip + kernlab dans un workflow, avec une note sur kernlab::ksvm et e1071::svm. Évalué sur l’exactitude et l’AUC ROC, sur les données PimaIndiansDiabetes2.

Date de publication

25 juin 2026

Modifié

7 juillet 2026

AstuceL’essentiel à retenir
  • Un support vector machine (SVM) classe en trouvant la frontière à la marge la plus large — la frontière séparatrice qui se tient le plus loin possible des points les plus proches de chaque classe. Seuls ces points les plus proches, les vecteurs de support, définissent la frontière ; tout le reste est sans importance.
  • Les données réelles sont rarement linéairement séparables, donc le SVM utilise l’astuce du noyau pour courber la frontière : un noyau linéaire trace une frontière droite, tandis que les noyaux polynomial et radial (RBF) tracent des frontières courbes et non linéaires.
  • Le coût C contrôle la souplesse de la marge : un petit C autorise davantage de violations de marge (une frontière plus large et plus indulgente qui généralise), un grand C punit chaque erreur (une frontière plus serrée qui peut surapprendre). Le noyau RBF ajoute un second réglage, rbf_sigma, qui détermine à quel point la frontière est sinueuse.
  • Standardisez d’abord les prédicteurs — le SVM mesure des distances, donc un prédicteur exprimé dans de grandes unités dominerait la marge. Utilisez toujours step_normalize() (ou la mise à l’échelle intégrée de kernlab).
  • Vous ajustez C (et sigma) par validation croisée, jamais à l’aveugle. La manière moderne : parsnip::svm_linear()/svm_poly()/svm_rbf() sur le moteur "kernlab" à l’intérieur d’un workflow, ajustés avec tune_grid() ; la manière classique : kernlab::ksvm() ou e1071::svm() directement.
  • Sur les données de diabète Pima, les noyaux linéaire et polynomial atteignent environ 76 % d’exactitude en test / 0.84 d’AUC ROC, tandis que le noyau radial est ici un peu moins bon (~72 % / 0.80) — donc le simple SVM linéaire égale ou dépasse les plus sophistiqués. Ajustez et comparez toujours plutôt que de supposer qu’un noyau courbe l’emporte.
Obtenez le livre — Machine Learning Essentials (PDF)

Introduction

Vous disposez d’un jeu de données clinique et d’une question oui/non : un patient sera-t-il positif au diabète, à partir d’une poignée de mesures — glucose, IMC, âge, pression artérielle, et ainsi de suite ? Vous voulez un classifieur qui trace une ligne (ou une courbe) nette entre les patients diabète-positif et diabète-négatif, et vous voulez que cette frontière généralise à des patients qu’il n’a jamais vus.

Le support vector machine (SVM) est conçu exactement pour cela. Son idée est géométrique et intuitive : parmi toutes les frontières qui séparent les deux classes, choisir celle à la marge la plus large — la frontière qui conserve le plus grand écart possible de chaque côté. Une marge large est une frontière confiante et robuste, et elle ne dépend que de la poignée de points situés juste au bord de cet écart. Ces points frontaliers sont les vecteurs de support, et ils donnent son nom à la méthode.

La plupart des frontières réelles ne sont pas droites, donc le SVM dispose d’une seconde astuce. À travers le noyau, il peut courber la frontière en une courbe — une frontière polynomiale ou, le plus souvent, radiale (RBF) — sans jamais quitter le confort de la même optimisation. Cette leçon construit des classifieurs SVM des trois manières, ajuste honnêtement leurs paramètres de coût et de noyau par validation croisée, et les évalue sur un jeu de test mis de côté avec l’exactitude et l’AUC ROC.

Nous travaillons à la manière moderne tidymodels — les spécifications svm_*() de parsnip sur le moteur kernlab à l’intérieur d’un workflow — et notons aussi les routes classiques kernlab::ksvm() / e1071::svm(). Le SVM est un classifieur d’une famille : pour les alternatives de type régression ou interprétables voyez la régression logistique pénalisée, et pour lire la matrice de confusion et la courbe ROC en profondeur voyez l’évaluation des modèles de classification.

NoteQuand y recourir

Utilisez un SVM lorsque vous voulez un solide classifieur binaire (ou multi-classe) et que vous soupçonnez que la frontière entre les classes peut être non linéaire. Le SVM brille sur les jeux de données de taille moyenne avec de nombreux prédicteurs, gère bien une frontière non linéaire grâce au noyau, et est robuste parce qu’il s’appuie uniquement sur les vecteurs de support. Tournez-vous ailleurs lorsque vous avez besoin de probabilités calibrées ou de coefficients interprétables d’emblée — le SVM donne une frontière de décision, pas des odds ratio.

Les données

Nous utilisons le jeu de données PimaIndiansDiabetes2 du package mlbench : 768 femmes d’origine amérindienne Pima, avec pour objectif de prédire le diabetes (un résultat binaire pos/neg) à partir de huit prédicteurs cliniques — le nombre de grossesses (pregnant), le glucose plasmatique, la pressure sanguine, l’épaisseur du pli cutané du triceps, l’insulin sérique, l’indice de masse corporelle (mass, IMC), la fonction pedigree du diabète, et l’age.

Cette version enregistre les valeurs manquantes en NA lorsqu’une mesure est invraisemblable (p. ex. un glucose à zéro). Le SVM a besoin de cas complets, donc — en suivant l’analyse source — nous supprimons les lignes avec des NA, laissant 392 enregistrements complets. Nous découpons ensuite en un jeu d’entraînement (80 %, pour l’ajustement et le réglage) et un jeu de test (20 %, mis de côté pour juger honnêtement le classifieur final), stratifié sur diabetes afin que les deux jeux conservent le même équilibre positif/négatif, avec une graine pour la reproductibilité :

library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")

# Drop rows with missing measurements (SVM needs complete cases)
pima <- na.omit(PimaIndiansDiabetes2)

set.seed(123)
split      <- initial_split(pima, prop = 0.80, strata = diabetes)
train_data <- training(split)
test_data  <- testing(split)

c(complete = nrow(pima), train = nrow(train_data), test = nrow(test_data))
complete    train     test 
     392      313       79 
# The stratified split preserves the class balance
prop.table(table(train_data$diabetes))

      neg       pos 
0.6677316 0.3322684 
head(pima, 4)
  pregnant glucose pressure triceps insulin mass pedigree age diabetes
4        1      89       66      23      94 28.1    0.167  21      neg
5        0     137       40      35     168 43.1    2.288  33      pos
7        3      78       50      32      88 31.0    0.248  26      pos
9        2     197       70      45     543 30.5    0.158  53      pos

Environ 313 patients pour apprendre, 79 mis de côté pour faire office de juge, et 8 prédicteurs. Environ un tiers des patients sont diabète-positif — un problème de classification modérément déséquilibré mais exploitable.

Ce que fait réellement un SVM : la marge et les vecteurs de support

Avant tout réglage, il est utile de voir ce que dessine un SVM. Ci-dessous, nous ajustons un SVM à noyau radial sur seulement deux prédicteurs — glucose et mass (IMC) — afin de pouvoir tracer sa frontière de décision sur le plan. Nous standardisons d’abord les deux (le SVM mesure des distances ; les prédicteurs doivent partager une échelle), puis colorons toute la région selon la prédiction du modèle et superposons les patients colorés selon leur classe réelle :

library(kernlab)
library(ggplot2)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)

# Two predictors so the boundary is drawable; standardize them
d <- pima[, c("glucose", "mass", "diabetes")]
d$glucose <- as.numeric(scale(d$glucose))
d$mass    <- as.numeric(scale(d$mass))

# A radial-kernel SVM on the two predictors
set.seed(123)
fit2d <- ksvm(diabetes ~ glucose + mass, data = d,
              kernel = "rbfdot", C = 1, kpar = "automatic")

# Predict over a fine grid to paint the decision regions
gx   <- seq(min(d$glucose), max(d$glucose), length = 200)
gy   <- seq(min(d$mass),    max(d$mass),    length = 200)
grid <- expand.grid(glucose = gx, mass = gy)
grid$pred <- predict(fit2d, grid)

ggplot() +
  geom_tile(data = grid, aes(glucose, mass, fill = pred), alpha = 0.25) +
  geom_point(data = d, aes(glucose, mass, color = diabetes), size = 1.3, alpha = 0.8) +
  scale_fill_manual(values = c(neg = "#3a86d4", pos = "#e15759"),
                    name = "Predicted") +
  scale_color_manual(values = c(neg = "#3a86d4", pos = "#e15759"),
                     name = "Actual") +
  labs(x = "Glucose (standardized)", y = "BMI (standardized)",
       title = "A radial SVM decision boundary (Pima diabetes)",
       subtitle = "The frontier is curved — that is the kernel at work") +
  theme_minimal()

Radial-kernel SVM decision boundary on the Pima diabetes data drawn over the standardized glucose and BMI plane: a curved, non-linear frontier splits the plane into a blue diabetes-negative region (low glucose) and a red diabetes-positive region (high glucose), with patients plotted as points coloured by their true class — most positives fall in the red region and most negatives in the blue, with mixing near the boundary.

Lisez l’image. Le plan est divisé en une région bleue diabète-négatif (surtout faible glucose) et une région rouge diabète-positif (surtout fort glucose), et la frontière entre elles est courbe, non droite — cette courbure est le noyau radial à l’œuvre. Les patients (les points) atterrissent majoritairement dans la bonne région, avec le mélange attendu près de la frontière où la décision est véritablement difficile. Quelques « îlots » apparaissent là où des regroupements locaux d’une classe poussent la frontière autour d’eux.

La frontière n’a besoin que des points proches d’elle — les vecteurs de support. Les points profondément à l’intérieur de leur propre région pourraient être supprimés sans déplacer la ligne du tout ; seuls les cas frontaliers comptent. C’est toute l’économie d’un SVM : une frontière définie par un petit ensemble décisif de points, avec la marge la plus large possible autour d’elle.

NoteLinéaire ou non linéaire, en une phrase

Remplacez kernel = "rbfdot" par kernel = "vanilladot" ci-dessus et la frontière devient une ligne droite — un SVM linéaire. Les noyaux polynomial ("polydot") et radial ("rbfdot") permettent au même algorithme de tracer des frontières courbes en projetant implicitement les données dans un espace de plus grande dimension (l’astuce du noyau), où une coupe droite redevient une courbe dans les prédicteurs d’origine.

Le SVM linéaire : ajuster le coût par validation croisée

Passons maintenant au vrai modèle sur les huit prédicteurs. En tidymodels, un SVM n’est qu’une spécification parsnip différente sur le moteur kernlab ; tout le reste est l’enchaînement découpage → recipe → réglage → finalisation du workflow tidymodels. La recipe normalise les prédicteurs (le SVM mesure des distances, donc une échelle commune est obligatoire), et nous laissons le coût C à ajuster.

C est le réglage clé du SVM. Il contrôle avec quelle sévérité le modèle punit une violation de marge — un point du mauvais côté de la marge, ou à l’intérieur de celle-ci :

  • Petit C → une marge souple et large. Le modèle tolère quelques points d’entraînement mal classés en échange d’une frontière plus simple qui généralise habituellement mieux.
  • Grand C → une marge dure et étroite. Le modèle s’efforce de classer correctement chaque point d’entraînement, ce qui peut surapprendre le bruit de l’entraînement.

Nous évaluons chaque C candidat par validation croisée à 10 plis sur le jeu d’entraînement, sur l’exactitude et l’AUC ROC, puis conservons le meilleur :

library(rsample)
library(recipes)
library(parsnip)
library(workflows)
library(tune)
library(dials)
library(yardstick)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)

set.seed(123)
split      <- initial_split(pima, prop = 0.80, strata = diabetes)
train_data <- training(split)

# Standardize the predictors — SVM measures distances, so scale matters
rec <- recipe(diabetes ~ ., data = train_data) |>
  step_normalize(all_numeric_predictors())

# Linear SVM, cost left to be tuned
svm_lin_spec <- svm_linear(cost = tune()) |>
  set_engine("kernlab") |>
  set_mode("classification")

svm_lin_wf <- workflow() |>
  add_recipe(rec) |>
  add_model(svm_lin_spec)

# 10-fold CV over a grid of cost values
set.seed(123)
folds <- vfold_cv(train_data, v = 10, strata = diabetes)
cost_grid <- grid_regular(cost(range = c(-3, 3)), levels = 15)   # C from 2^-3 to 2^3

set.seed(123)
svm_lin_tuned <- tune_grid(
  svm_lin_wf, resamples = folds, grid = cost_grid,
  metrics = metric_set(accuracy, roc_auc)
)

# The cost with the best cross-validated accuracy
best_cost <- select_best(svm_lin_tuned, metric = "accuracy")
best_cost
# A tibble: 1 × 2
   cost .config         
  <dbl> <chr>           
1 0.125 pre0_mod01_post0

tune_grid() a validé par validation croisée chaque coût candidat ; select_best() renvoie le gagnant. Ici le C choisi est petit (environ 0.125) — la marge souple et indulgente généralise le mieux sur ces données. C’est un résultat courant : sur des données réelles bruitées, une marge plus large l’emporte sur une marge plus serrée.

La courbe de réglage : exactitude vs coût

Le diagnostic le plus utile lors du réglage d’un SVM est l’exactitude validée par validation croisée en fonction du coût. Il montre tout le compromis biais–variance en une seule image :

library(rsample)
library(recipes)
library(parsnip)
library(workflows)
library(tune)
library(dials)
library(yardstick)
library(ggplot2)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split      <- initial_split(pima, prop = 0.80, strata = diabetes)
train_data <- training(split)
rec <- recipe(diabetes ~ ., data = train_data) |> step_normalize(all_numeric_predictors())
svm_lin_spec <- svm_linear(cost = tune()) |> set_engine("kernlab") |> set_mode("classification")
svm_lin_wf   <- workflow() |> add_recipe(rec) |> add_model(svm_lin_spec)
set.seed(123); folds <- vfold_cv(train_data, v = 10, strata = diabetes)
cost_grid <- grid_regular(cost(range = c(-3, 3)), levels = 15)
set.seed(123)
svm_lin_tuned <- tune_grid(svm_lin_wf, resamples = folds, grid = cost_grid,
                           metrics = metric_set(accuracy, roc_auc))
best_cost <- select_best(svm_lin_tuned, metric = "accuracy")

# Collect the CV accuracy for every candidate cost
acc_curve <- subset(collect_metrics(svm_lin_tuned), .metric == "accuracy")

ggplot(acc_curve, aes(x = cost, y = mean)) +
  geom_line(color = "#3a86d4", linewidth = 1) +
  geom_point(color = "#3a86d4", size = 1.8) +
  geom_vline(xintercept = best_cost$cost, linetype = "dashed", color = "gray40") +
  scale_x_log10() +
  labs(
    x = "Cost C (log scale)", y = "Cross-validated accuracy",
    title = "Choosing the SVM cost by cross-validation",
    subtitle = "Accuracy peaks at a small, soft-margin cost (dashed = best C)"
  ) +
  theme_minimal()

Cross-validated accuracy plotted against the SVM cost C on a log scale for the linear SVM on the Pima diabetes data: accuracy peaks at a small cost then drifts down and flattens as the cost grows and the margin tightens, with a dashed line marking the best cost.

Lisez-la de gauche à droite. Pour un petit coût (à gauche), la marge est souple et large, et l’exactitude validée par validation croisée est à son meilleur. À mesure que le coût augmente (à droite), la marge se resserre, le modèle se met à courir après des points d’entraînement individuels, et l’exactitude dérive vers le bas et s’aplatit. La ligne pointillée marque le gagnant de la validation croisée — le point d’équilibre entre une marge trop souple (sous-apprentissage) et une marge trop dure (surapprentissage).

Finaliser et évaluer sur le jeu de test

Nous finalisons le workflow avec le coût choisi et last_fit() — ajustement sur toutes les données d’entraînement et évaluation une seule fois sur le jeu de test intact :

library(rsample)
library(recipes)
library(parsnip)
library(workflows)
library(tune)
library(dials)
library(yardstick)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split      <- initial_split(pima, prop = 0.80, strata = diabetes)
train_data <- training(split)
rec <- recipe(diabetes ~ ., data = train_data) |> step_normalize(all_numeric_predictors())
svm_lin_spec <- svm_linear(cost = tune()) |> set_engine("kernlab") |> set_mode("classification")
svm_lin_wf   <- workflow() |> add_recipe(rec) |> add_model(svm_lin_spec)
set.seed(123); folds <- vfold_cv(train_data, v = 10, strata = diabetes)
cost_grid <- grid_regular(cost(range = c(-3, 3)), levels = 15)
set.seed(123)
svm_lin_tuned <- tune_grid(svm_lin_wf, resamples = folds, grid = cost_grid,
                           metrics = metric_set(accuracy, roc_auc))
best_cost <- select_best(svm_lin_tuned, metric = "accuracy")

# Finalize with the chosen cost, fit on train, score ONCE on test
final_lin <- finalize_workflow(svm_lin_wf, best_cost) |> last_fit(split)
collect_metrics(final_lin)
# A tibble: 3 × 4
  .metric     .estimator .estimate .config        
  <chr>       <chr>          <dbl> <chr>          
1 accuracy    binary         0.759 pre0_mod0_post0
2 roc_auc     binary         0.845 pre0_mod0_post0
3 brier_class binary         0.158 pre0_mod0_post0

Lisez les chiffres — la performance honnête, hors échantillon, de votre SVM linéaire :

  • Exactitude ≈ 0.76 — le modèle juge correctement le diabète environ 76 % du temps sur des patients qu’il n’a jamais vus pendant l’ajustement. Utile, mais à lire à côté de l’AUC, car l’exactitude peut flatter un modèle sur un résultat déséquilibré.
  • AUC ROC ≈ 0.85 — la probabilité que le modèle attribue à un positif aléatoire un score plus élevé qu’à un négatif aléatoire. 0.5 est un tirage à pile ou face, 1.0 est parfait ; 0.85 est un classifieur réellement bon. Parce que l’AUC est indépendante du seuil, elle juge le classement du modèle, et non un seuil arbitraire de 0.5. (Un score brier_class apparaît aussi — une mesure « plus c’est bas, mieux c’est » de la qualité des probabilités ; voyez l’évaluation des modèles de classification.)

SVM non linéaire : les noyaux radial et polynomial

Le SVM linéaire trace une frontière droite. Quand la vraie frontière est courbe, un noyau non linéaire fait mieux. Les deux chevaux de trait :

  • Radial / RBF (svm_rbf()) — le choix par défaut ; une frontière courbe lisse et flexible. Il ajoute un second réglage, rbf_sigma, qui détermine à quel point la frontière est sinueuse : un grand sigma fait une frontière serrée et locale (risque de surapprentissage), un petit sigma une frontière lisse et globale.
  • Polynomial (svm_poly()) — une frontière en forme de courbe polynomiale, avec le degree contrôlant sa flexibilité (le degré 1 est linéaire, le 2 quadratique, et ainsi de suite).

Le workflow est identique — seule la spécification change. Nous ajustons les paramètres du noyau par validation croisée avec une grille remplissant l’espace, puis évaluons chaque modèle finalisé sur le jeu de test :

library(rsample)
library(recipes)
library(parsnip)
library(workflows)
library(tune)
library(dials)
library(yardstick)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split      <- initial_split(pima, prop = 0.80, strata = diabetes)
train_data <- training(split)
rec <- recipe(diabetes ~ ., data = train_data) |> step_normalize(all_numeric_predictors())
set.seed(123); folds <- vfold_cv(train_data, v = 10, strata = diabetes)

# Helper: tune a spec by CV, finalize on best accuracy, return test accuracy + AUC
score_svm <- function(spec, grid) {
  wf    <- workflow() |> add_recipe(rec) |> add_model(spec)
  set.seed(123)
  tuned <- tune_grid(wf, resamples = folds, grid = grid,
                     metrics = metric_set(accuracy, roc_auc))
  best  <- select_best(tuned, metric = "accuracy")
  m     <- collect_metrics(finalize_workflow(wf, best) |> last_fit(split))
  data.frame(accuracy = m$.estimate[m$.metric == "accuracy"],
             roc_auc  = m$.estimate[m$.metric == "roc_auc"])
}

# Radial (RBF): tune cost + rbf_sigma
rbf_spec  <- svm_rbf(cost = tune(), rbf_sigma = tune()) |>
  set_engine("kernlab") |> set_mode("classification")
# Polynomial: tune cost + degree
poly_spec <- svm_poly(cost = tune(), degree = tune()) |>
  set_engine("kernlab") |> set_mode("classification")

rbind(
  Radial     = score_svm(rbf_spec, grid = 15),
  Polynomial = score_svm(poly_spec, grid = 15)
)
maximum number of iterations reached 0.007125988 0.007066688maximum number of iterations reached 0.000277231 0.0002772305maximum number of iterations reached 1.818525e-05 1.818525e-05maximum number of iterations reached 0.005606554 0.005509965maximum number of iterations reached 0.001894696 0.001894422maximum number of iterations reached 0.0001352326 0.0001352321maximum number of iterations reached 0.007771957 0.007697452maximum number of iterations reached 0.0002981254 0.0002981237maximum number of iterations reached 1.950506e-05 1.950506e-05maximum number of iterations reached 0.004951154 0.004863774maximum number of iterations reached 0.00204731 0.002046965maximum number of iterations reached 0.0001569284 0.0001569279maximum number of iterations reached 0.007417196 0.007357442maximum number of iterations reached 0.0002906193 0.0002906176maximum number of iterations reached 1.842763e-05 1.842762e-05maximum number of iterations reached 0.005711461 0.005584952maximum number of iterations reached 0.001945907 0.001945621maximum number of iterations reached 0.000143465 0.0001434646maximum number of iterations reached 0.008260928 0.008163126maximum number of iterations reached 0.0003420105 0.0003420079maximum number of iterations reached 2.401839e-05 2.401838e-05maximum number of iterations reached 0.005412609 0.005299179maximum number of iterations reached 0.002321359 0.002320826maximum number of iterations reached 0.000178926 0.0001789252maximum number of iterations reached 0.007543963 0.00747871maximum number of iterations reached 0.0003148769 0.0003148757maximum number of iterations reached 2.141298e-05 2.141298e-05maximum number of iterations reached 0.004977226 0.004881916maximum number of iterations reached 0.002026426 0.002026071maximum number of iterations reached 0.0001486634 0.0001486628maximum number of iterations reached 0.007999958 0.007923292maximum number of iterations reached 0.0003135545 0.0003135524maximum number of iterations reached 1.928426e-05 1.928425e-05maximum number of iterations reached 0.004539986 0.004485015maximum number of iterations reached 0.002026131 0.002025765maximum number of iterations reached 0.000154286 0.0001542855maximum number of iterations reached 0.007360605 0.007298256maximum number of iterations reached 0.0002908 0.0002907982maximum number of iterations reached 1.941965e-05 1.941965e-05maximum number of iterations reached 0.005145438 0.005042113maximum number of iterations reached 0.001950196 0.001949894maximum number of iterations reached 0.0001437948 0.0001437943maximum number of iterations reached 0.007565563 0.007499821maximum number of iterations reached 0.0003203407 0.0003203397maximum number of iterations reached 2.048476e-05 2.048476e-05maximum number of iterations reached 0.004989485 0.004906222maximum number of iterations reached 0.002111761 0.002111319maximum number of iterations reached 0.0001560655 0.0001560648maximum number of iterations reached 0.008008199 0.007927276maximum number of iterations reached 0.000340234 0.0003402325maximum number of iterations reached 2.152958e-05 2.152957e-05maximum number of iterations reached 0.005209444 0.005115809maximum number of iterations reached 0.002184366 0.002183934maximum number of iterations reached 0.0001696517 0.0001696511maximum number of iterations reached 0.007046988 0.006989501maximum number of iterations reached 0.0002873389 0.0002873379maximum number of iterations reached 1.864396e-05 1.864396e-05maximum number of iterations reached 0.005475701 0.005346058maximum number of iterations reached 0.001891311 0.001891039maximum number of iterations reached 0.0001391842 0.0001391836
            accuracy   roc_auc
Radial     0.7215190 0.8011611
Polynomial 0.7594937 0.8388970

Sur les données Pima, le noyau polynomial atteint essentiellement la même exactitude et la même AUC que le SVM linéaire (~0.76 / ~0.84), tandis que le noyau radial est ici un peu moins bon (~0.72 / ~0.80). C’est en soi un résultat utile : la frontière du diabète est ici assez proche du linéaire pour que la flexibilité supplémentaire d’un noyau courbe n’apporte rien — et la frontière plus sinueuse du noyau radial coûte en fait un peu. Sur ces données, le noyau linéaire (et polynomial) égale ou dépasse le radial, donc le simple SVM linéaire est le choix par défaut sensé — il est plus rapide à ajuster, a un paramètre de moins à régler, et est moins sujet au surapprentissage. Ajustez et comparez toujours sur le même jeu de test plutôt que de supposer qu’un noyau plus sophistiqué l’emporte ; les noyaux courbes gagnent leur place sur des données à frontière véritablement non linéaire, ce qui est courant mais pas universel.

NoteQuel noyau dois-je utiliser ?
  • Linéaire (svm_linear) — le premier essai par défaut, surtout avec de nombreux prédicteurs. Rapide, robuste, un paramètre (cost). S’il s’en sort bien, arrêtez-vous ici.
  • Radial / RBF (svm_rbf) — le noyau non linéaire de référence. Flexible et lisse ; ajustez cost et rbf_sigma. Le choix standard quand le SVM linéaire sous-apprend.
  • Polynomial (svm_poly) — une frontière courbe d’un degree choisi ; utile lorsque vous attendez une structure polynomiale spécifique, sinon le RBF est habituellement plus facile à régler. Ajustez toujours par validation croisée et comparez sur le même jeu de test mis de côté, comme ci-dessus.

La route classique : kernlab::ksvm et e1071::svm

La plupart des utilisateurs de R rencontrent d’abord le SVM via kernlab::ksvm() ou e1071::svm() directement — utile à connaître, car beaucoup de code existant (et la route originelle method = "svmLinear"/"svmRadial" de caret) repose sur eux. Standardisez vous-même les prédicteurs (ou utilisez la mise à l’échelle intégrée du moteur) et appelez la fonction :

library(kernlab)
library(rsample)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)
set.seed(123)
split      <- initial_split(pima, prop = 0.80, strata = diabetes)
train_data <- training(split)
test_data  <- testing(split)

# ksvm scales the predictors by default (scaled = TRUE); radial kernel, cost C = 1
set.seed(123)
fit_ksvm <- ksvm(diabetes ~ ., data = train_data,
                 kernel = "rbfdot", C = 1, kpar = "automatic")
fit_ksvm
Support Vector Machine object of class "ksvm" 

SV type: C-svc  (classification) 
 parameter : cost C = 1 

Gaussian Radial Basis kernel function. 
 Hyperparameter : sigma =  0.128432254141048 

Number of Support Vectors : 178 

Objective Function Value : -128.2245 
Training error : 0.153355 
# Accuracy on the held-out test set
pred <- predict(fit_ksvm, test_data)
mean(pred == test_data$diabetes)
[1] 0.7088608

ksvm() affiche le noyau, le coût, le rbf_sigma ajusté qu’il a choisi automatiquement, et le nombre de vecteurs de support — les points qui définissent réellement la frontière. L’exactitude en test est dans le même ordre de grandeur que les modèles tidymodels ajustés. Le package e1071 propose la même chose avec un vocabulaire légèrement différent : svm(diabetes ~ ., data = train_data, kernel = "radial", cost = 1, scale = TRUE), avec tune.svm() pour une recherche sur grille sur cost et gamma. Les deux conviennent ; tidymodels vous donne simplement un harnais de réglage + d’évaluation cohérent pour chaque modèle de ce pilier.

Un SVM linéaire cherche un hyperplan séparateur \(w^\top x + b = 0\) et choisit celui à la marge la plus large — le plus grand écart \(2 / \lVert w \rVert\) entre les deux classes. Avec une marge souple, il résout

\[ \min_{w,\,b,\,\xi} \;\; \tfrac{1}{2}\lVert w \rVert^2 \; + \; C \sum_i \xi_i , \]

sous la contrainte que chaque point soit du bon côté à un relâchement \(\xi_i \ge 0\) près. Le premier terme élargit la marge ; le second pénalise les violations, le coût \(C\) réglant le compromis — un grand \(C\) signifie peu de violations (une marge dure et étroite), un petit \(C\) une marge plus souple et plus large. La solution ne dépend que des points avec \(\xi_i > 0\) ou situés sur le bord de la marge : les vecteurs de support.

La partie astucieuse est que l’optimisation n’utilise les données qu’à travers des produits scalaires \(x_i^\top x_j\) entre points. Remplacez ce produit scalaire par un noyau \(K(x_i, x_j)\) et vous projetez implicitement les données dans un espace de plus grande dimension où un séparateur droit existe — sans jamais calculer la projection. Les noyaux courants :

\[ K_{lin} = x_i^\top x_j , \qquad K_{poly} = (x_i^\top x_j + c)^{d} , \qquad K_{rbf} = \exp\!\big(-\sigma \, \lVert x_i - x_j \rVert^2 \big). \]

Le noyau radial \(K_{rbf}\) est gouverné par \(\sigma\) (= rbf_sigma) : un grand \(\sigma\) rend l’influence de chaque point locale (une frontière sinueuse), un petit \(\sigma\) la rend globale (une frontière lisse). Parce que chaque noyau compare des distances ou des produits scalaires, les prédicteurs doivent d’abord être standardisés — sinon un prédicteur exprimé dans de grandes unités dominerait le noyau.

🟢 Avec un agent IA

Un problème de classification et vous ne savez pas quel noyau convient ? Demandez à Prova « ajuste des SVM linéaire, radial et polynomial à ces données, règle le coût et le noyau par validation croisée, et dis-moi lequel généralise le mieux » — elle répond avec du code tidymodels + kernlab que vous pouvez exécuter sur vos propres données, puis vous aide à lire la courbe de réglage, les vecteurs de support, et l’exactitude et l’AUC sur le jeu de test. The runtime is the judge. Ask Prova →

Problèmes courants

  • Vous avez oublié de standardiser les prédicteurs. Le SVM mesure des distances, donc un prédicteur exprimé dans de grandes unités (p. ex. insulin) domine la marge et la frontière ignore tout le reste. Utilisez toujours step_normalize(all_numeric_predictors()) dans la recipe (ou appuyez-vous sur le scaled = TRUE de ksvm / e1071::svm(scale = TRUE)).
  • Vos données comportent des valeurs manquantes. na.omit() supprime les lignes incomplètes (utilisé ici), mais vous perdez des données. Préférez l’imputation à l’intérieur de la recipe — step_impute_median(all_numeric_predictors()) — pour qu’aucune ligne ne soit jetée et que l’imputation soit apprise uniquement à partir de l’entraînement.
  • Le SVM RBF surapprend (excellent en entraînement, médiocre en test). Cela signifie généralement que rbf_sigma est trop grand (la frontière est trop sinueuse) ou que cost est trop élevé. Ajustez les deux par validation croisée et préférez l’extrémité plus souple et plus lisse ; ne les fixez pas à la main.
  • Vous avez besoin de probabilités prédites, pas seulement de classes. Un SVM simple renvoie une classe ferme. Demandez au moteur des probabilités — ksvm(..., prob.model = TRUE) puis predict(type = "probabilities"), ou en tidymodels predict(type = "prob") (kernlab ajuste un modèle de probabilité interne). Lisez-les dans l’évaluation des modèles de classification.
  • Deux exécutions donnent des résultats légèrement différents. Le découpage des plis de validation croisée et l’aléa interne de kernlab sont aléatoires. Appelez set.seed() avant vfold_cv() et l’ajustement pour un résultat reproductible.

Questions fréquentes

Utilisez kernlab::ksvm() ou e1071::svm() directement, ou la route tidymodels pour un workflow ajustable. La manière tidymodels : svm_rbf(cost = tune(), rbf_sigma = tune()) |> set_engine("kernlab") |> set_mode("classification") à l’intérieur d’un workflow avec step_normalize(), ajusté par tune_grid() sur des plis de validation croisée et finalisé avec last_fit(). Standardisez toujours d’abord les prédicteurs — le SVM mesure des distances.

Découpez les données (rsample::initial_split()), normalisez les prédicteurs dans une recipe(), choisissez une spécification parsnipsvm_linear(), svm_poly() ou svm_rbf() sur le moteur "kernlab" — regroupez-les dans un workflow(), ajustez le cost (et rbf_sigma/degree) par tune_grid() sur une CV à 10 plis, puis finalize_workflow() et last_fit() pour évaluer l’exactitude et l’AUC ROC une fois sur le jeu de test. Le classique one-liner est ksvm(diabetes ~ ., data = train, kernel = "rbfdot", C = 1).

C contrôle la souplesse de la marge. Un petit C autorise davantage de violations de marge — une frontière plus large, plus indulgente, qui généralise habituellement mieux. Un grand C punit chaque point d’entraînement mal classé — une frontière plus serrée qui colle plus fort aux données d’entraînement et peut surapprendre. Vous choisissez C par validation croisée ; sur des données réelles bruitées, un petit C à marge souple l’emporte souvent (c’est le cas sur les données Pima ici).

Commencez par un noyau linéaire (svm_linear) : il est rapide, robuste, et souvent suffisant, surtout avec de nombreux prédicteurs. S’il sous-apprend, essayez le noyau radial / RBF (svm_rbf) — le choix non linéaire standard, flexible et lisse, en ajustant à la fois cost et rbf_sigma. Utilisez un noyau polynomial (svm_poly) lorsque vous attendez une frontière polynomiale spécifique. Ajustez toujours par validation croisée et comparez sur le même jeu de test mis de côté ; quand les noyaux sont à égalité, préférez le plus simple.

Les vecteurs de support sont les points d’entraînement situés le plus près de la frontière de décision — sur, ou à l’intérieur de, la marge. Ce sont les seuls points qui définissent la frontière : supprimer n’importe quel autre point ne la déplacerait pas du tout. C’est pourquoi les SVM sont robustes et économes en mémoire — le modèle se résume à un petit ensemble de cas frontaliers décisifs. ksvm() indique combien de vecteurs de support il a conservés.

Testez vos connaissances

En utilisant les données PimaIndiansDiabetes2 (mlbench, avec na.omit()), construisez un SVM radial (RBF) à la manière tidymodels : découpez 80/20 stratifié sur diabetes, normalisez les prédicteurs, ajustez cost et rbf_sigma par validation croisée à 10 plis sur l’exactitude, et rapportez l’exactitude et l’AUC ROC sur le jeu de test. Comment se compare-t-il au SVM linéaire de la leçon ?

La forme est identique au workflow linéaire — changez uniquement la spécification en svm_rbf(cost = tune(), rbf_sigma = tune()) et passez une grille remplissant l’espace grid = 15 (ou un grid_regular) à tune_grid(). Vous aurez besoin de rsample, recipes, parsnip, workflows, tune, dials, et yardstick.

library(rsample); library(recipes); library(parsnip)
library(workflows); library(tune); library(dials); library(yardstick)
data("PimaIndiansDiabetes2", package = "mlbench")
pima <- na.omit(PimaIndiansDiabetes2)

set.seed(123)
split      <- initial_split(pima, prop = 0.80, strata = diabetes)
train_data <- training(split)

rec      <- recipe(diabetes ~ ., data = train_data) |> step_normalize(all_numeric_predictors())
rbf_spec <- svm_rbf(cost = tune(), rbf_sigma = tune()) |>
  set_engine("kernlab") |> set_mode("classification")
rbf_wf   <- workflow() |> add_recipe(rec) |> add_model(rbf_spec)

set.seed(123); folds <- vfold_cv(train_data, v = 10, strata = diabetes)
set.seed(123)
tuned <- tune_grid(rbf_wf, resamples = folds, grid = 15,
                   metrics = metric_set(accuracy, roc_auc))
best  <- select_best(tuned, metric = "accuracy")

collect_metrics(finalize_workflow(rbf_wf, best) |> last_fit(split))

Vous obtiendrez une exactitude et une AUC d’environ ~0.72 / ~0.80 pour le noyau radial — un cran en dessous du SVM linéaire à ~0.76 / ~0.84, et non au-dessus. La frontière Pima est proche du linéaire, donc la flexibilité supplémentaire du noyau radial n’apporte rien ici — sa frontière plus sinueuse coûte en fait un peu, et le simple SVM linéaire est le meilleur choix par défaut. Sur des données à frontière véritablement courbe, le noyau RBF prendrait l’avantage.

Vérification rapide. Vous standardisez les prédicteurs et ajustez un SVM radial. Un collègue ajuste le même SVM sur les prédicteurs bruts, non mis à l’échelle, et obtient une frontière bien pire qui semble ignorer la plupart des variables. Qu’est-ce qui a mal tourné, et quel est le correctif ?

Le collègue a sauté la standardisation. Le noyau d’un SVM mesure des distances entre points, donc un prédicteur enregistré dans de grandes unités (p. ex. insulin dans les centaines) domine chaque distance, et les prédicteurs exprimés dans de petites unités (p. ex. pedigree) sont effectivement ignorés. Le correctif est de mettre tous les prédicteurs à la même échelle d’abordstep_normalize(all_numeric_predictors()) dans la recipe, ou scaled = TRUE dans ksvm / scale = TRUE dans e1071::svm. Après la mise à l’échelle, chaque prédicteur contribue équitablement à la marge.

Conclusion

Vous avez construit des classifieurs support vector machine de bout en bout sur les données PimaIndiansDiabetes2. Un SVM trouve la frontière à marge maximale, définie par ses vecteurs de support ; le noyau (linéaire, polynomial, radial) décide si cette frontière est droite ou courbe ; le coût C règle la souplesse de la marge, et le rbf_sigma du RBF règle à quel point la courbe est sinueuse. Vous avez standardisé d’abord, ajusté par validation croisée avec le workflow moderne parsnip + kernlab (et vu les routes classiques ksvm() / svm()), lu la courbe de réglage, tracé la frontière de décision, et évalué l’exactitude et l’AUC ROC sur un jeu de test mis de côté. La règle pratique : standardisez, commencez linéaire, laissez la validation croisée choisir le coût et le noyau, et comparez toujours — ici, le noyau linéaire (et polynomial) a égalé ou battu le radial, donc livrez le plus simple qui tient le coup.

Ensuite, lisez la matrice de confusion, la courbe ROC et l’AUC en profondeur dans l’évaluation des modèles de classification, comparez le SVM à un classifieur linéaire régularisé dans la régression logistique pénalisée, ou revisitez le workflow sur lequel cette leçon s’appuie dans construire des modèles avec tidymodels.

Leçons connexes

Vous préférez un livre ? Machine Learning Essentials est disponible en PDF téléchargeable — toutes les leçons de cette série, hors ligne et à vous.

Obtenir le livre (PDF) →Les leçons restent gratuites en ligne.
Cette page vous a-t-elle été utile ?

Prouvez que vous savez le faire. Maîtrisez toute la série Classification en R — suivez votre parcours, construisez des projets et obtenez un certificat.

Commencer gratuitement →

Passez à Pro — Prova illimité sur vos propres données et un certificat vérifiable qui atteste la compétence.

dès 15 $/mois facturé annuellement

Passer à Pro →

✓ Vous êtes Pro — continuez. The runtime is the judge.

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN
Note

Cette leçon est reproductible : chaque métrique, frontière et figure a été produite par le code montré — copiez n’importe quel bloc et exécutez-le pour les reproduire. The runtime is the judge.

Références

  • James, G., Witten, D., Hastie, T., & Tibshirani, R. (2014). An Introduction to Statistical Learning, with Applications in R. Springer. (Chapitre 9 — Support Vector Machines.)
  • Karatzoglou, A., Smola, A., Hornik, K., & Zeileis, A. (2004). kernlab — An S4 Package for Kernel Methods in R. Journal of Statistical Software, 11(9). (Le package kernlab.)
  • Kuhn, M., & Silge, J. Tidy Modeling with R. En ligne, gratuit (CC BY-NC-SA).

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Support Vector Machine en R : SVM linéaire, polynomial et
    radial},
  date = {2026-06-25},
  url = {https://www.datanovia.com/learn/machine-learning/classification/support-vector-machine},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Support Vector Machine en R : SVM linéaire, polynomial et radial.” 2026. June 25. https://www.datanovia.com/learn/machine-learning/classification/support-vector-machine.