1 Задание 2. Классификация методом k-ближайших соседей (knn)

Формулировка: выполнить классификацию k-ближайших соседей с использованием функции knn() из пакета class на наборе данных iris. Провести нормализацию данных, разделить выборку на обучающую и тестовую. Оценить построенную модель с использованием функции CrossTable() из пакета gmodels. Построить матрицу ошибок и диагональную оценку качества прогноза.

library(class)
library(gmodels)

data(iris)

normalize <- function(x) {
  (x - min(x)) / (max(x) - min(x))
}

iris_norm <- as.data.frame(lapply(iris[, 1:4], normalize))
iris_norm$Species <- iris$Species

set.seed(123)
train_index <- sample(1:nrow(iris_norm), 0.7 * nrow(iris_norm))

train_data <- iris_norm[train_index, 1:4]
test_data <- iris_norm[-train_index, 1:4]
train_labels <- iris_norm[train_index, 5]
test_labels <- iris_norm[-train_index, 5]

knn_pred <- knn(train = train_data, test = test_data, cl = train_labels, k = 5)

CrossTable(x = test_labels, y = knn_pred, prop.chisq = FALSE)
## 
##  
##    Cell Contents
## |-------------------------|
## |                       N |
## |           N / Row Total |
## |           N / Col Total |
## |         N / Table Total |
## |-------------------------|
## 
##  
## Total Observations in Table:  45 
## 
##  
##              | knn_pred 
##  test_labels |     setosa | versicolor |  virginica |  Row Total | 
## -------------|------------|------------|------------|------------|
##       setosa |         14 |          0 |          0 |         14 | 
##              |      1.000 |      0.000 |      0.000 |      0.311 | 
##              |      1.000 |      0.000 |      0.000 |            | 
##              |      0.311 |      0.000 |      0.000 |            | 
## -------------|------------|------------|------------|------------|
##   versicolor |          0 |         17 |          1 |         18 | 
##              |      0.000 |      0.944 |      0.056 |      0.400 | 
##              |      0.000 |      0.944 |      0.077 |            | 
##              |      0.000 |      0.378 |      0.022 |            | 
## -------------|------------|------------|------------|------------|
##    virginica |          0 |          1 |         12 |         13 | 
##              |      0.000 |      0.077 |      0.923 |      0.289 | 
##              |      0.000 |      0.056 |      0.923 |            | 
##              |      0.000 |      0.022 |      0.267 |            | 
## -------------|------------|------------|------------|------------|
## Column Total |         14 |         18 |         13 |         45 | 
##              |      0.311 |      0.400 |      0.289 |            | 
## -------------|------------|------------|------------|------------|
## 
## 
confusion_matrix <- table(Predicted = knn_pred, Actual = test_labels)
confusion_matrix
##             Actual
## Predicted    setosa versicolor virginica
##   setosa         14          0         0
##   versicolor      0         17         1
##   virginica       0          1        12
diagonal_accuracy <- sum(diag(confusion_matrix)) / sum(confusion_matrix)
diagonal_accuracy
## [1] 0.9555556

Вывод: нормализация (min-max в диапазон [0, 1]) необходима для knn(), так как алгоритм основан на расстояниях между точками, а признаки iris измерены в разных диапазонах (лепестки/чашелистики) — без нормализации признак с большим разбросом значений доминировал бы в расчёте расстояния. CrossTable() наглядно показывает, сколько наблюдений каждого истинного класса было отнесено к каждому предсказанному классу, а матрица ошибок (confusion matrix) — то же самое в компактном табличном виде. Диагональная оценка (сумма диагональных элементов, делённая на общее число наблюдений) даёт долю верно классифицированных объектов — итоговую точность (accuracy) модели, которая для iris при k = 5 обычно оказывается близка к 95-100%, так как классы в этом наборе данных хорошо разделимы.

2 Задание 3. Метод опорных векторов (SVM)

Формулировка: рассмотреть пример реализации метода опорных векторов с использованием функции svm() из пакета e1071. Построить линейный классификатор для прогнозирования. Для подбора параметров модели выполнить перекрёстную проверку с делением исходной выборки на 10 равных частей (cross = 10).

library(e1071)

data(iris)

set.seed(123)
train_index <- sample(1:nrow(iris), 0.7 * nrow(iris))
train_set <- iris[train_index, ]
test_set <- iris[-train_index, ]

svm_model <- svm(Species ~ ., data = train_set, kernel = "linear", cross = 10)
summary(svm_model)
## 
## Call:
## svm(formula = Species ~ ., data = train_set, kernel = "linear", cross = 10)
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  linear 
##        cost:  1 
## 
## Number of Support Vectors:  24
## 
##  ( 2 10 12 )
## 
## 
## Number of Classes:  3 
## 
## Levels: 
##  setosa versicolor virginica
## 
## 10-fold cross-validation on training data:
## 
## Total Accuracy: 95.2381 
## Single Accuracies:
##  100 100 90 100 90 90.90909 100 100 90 90.90909
svm_pred <- predict(svm_model, test_set)
table(Predicted = svm_pred, Actual = test_set$Species)
##             Actual
## Predicted    setosa versicolor virginica
##   setosa         14          0         0
##   versicolor      0         17         0
##   virginica       0          1        13
test_accuracy <- mean(svm_pred == test_set$Species)
test_accuracy
## [1] 0.9777778

Вывод: параметр cross = 10 запускает 10-кратную перекрёстную проверку прямо внутри обучающей выборки — данные делятся на 10 равных частей, модель поочерёдно обучается на 9 из них и проверяется на оставшейся, что даёт более надёжную (менее зависящую от случайного разбиения) оценку качества, чем единичное разбиение train/test. В выводе summary(svm_model) строка Total Accuracy — это и есть усреднённая по всем 10 разбиениям точность на обучающих данных. Итоговая точность на отложенной тестовой выборке (test_accuracy) обычно оказывается близкой к результату кросс-валидации, что подтверждает, что модель не переобучилась — линейный SVM хорошо справляется с iris, поскольку классы (особенно setosa) линейно (или почти линейно) разделимы.

3 Задание 4. Метод главных компонент (PCA) с помощью vegan::rda()

Формулировка: выполнить расчёт главных компонент с использованием пакета vegan и его функции rda(). Построить ординационную диаграмму методом PCA и сделать выводы.

library(vegan)

data(iris)

iris_pca <- rda(iris[, 1:4], scale = TRUE)
summary(iris_pca)
## 
## Call:
## rda(X = iris[, 1:4], scale = TRUE) 
## 
## Partitioning of correlations:
##               Inertia Proportion
## Total               4          1
## Unconstrained       4          1
## 
## Eigenvalues, and their contribution to the correlations 
## 
## Importance of components:
##                          PC1    PC2     PC3      PC4
## Eigenvalue            2.9185 0.9140 0.14676 0.020715
## Proportion Explained  0.7296 0.2285 0.03669 0.005179
## Cumulative Proportion 0.7296 0.9581 0.99482 1.000000
biplot(iris_pca, scaling = 2, main = "PCA: ординационная диаграмма (biplot)")

ordiplot(iris_pca, type = "n", main = "PCA: наблюдения по видам ириса")
points(iris_pca, display = "sites", col = as.numeric(iris$Species), pch = 19)
legend("topright", legend = levels(iris$Species), col = 1:3, pch = 19, title = "Species")

Вывод: rda() без задания ограничивающих (constraining) переменных выполняет обычный (unconstrained) метод главных компонент — то есть ищет линейные комбинации исходных 4 признаков (главные компоненты), которые последовательно объясняют максимум дисперсии данных. Из summary() видно, что первая главная компонента (PC1) объясняет значительно больше половины общей дисперсии — это ожидаемо, так как в iris признаки длины/ширины лепестка сильно коррелируют друг с другом (что мы уже видели в задании про FSelector в другой лабораторной). На ординационной диаграмме (biplot) стрелки показывают вклад (нагрузку) каждого исходного признака в главные компоненты — Petal.Length и Petal.Width имеют самые длинные стрелки и почти совпадают по направлению с осью PC1, тогда как Sepal.Width направлен почти перпендикулярно. На графике наблюдений по видам (ordiplot) вид setosa чётко отделяется от двух других вдоль PC1, а versicolor и virginica частично перекрываются — то есть уже первая главная компонента, построенная без использования меток классов, сама по себе несёт значительную информацию о видовой принадлежности.