Klassificering
Följande delkapitel exemplifierar hur övervakad inlärning, där vetskap om de sanna utfallen finns, fungerar i R med de tidigare presenterade paketen.
Uppdelning av datamaterial
För att motverka överanpassning vid övervakad inlärning används en datauppdelning som delar upp det insamlade materialet till en tränings-, validerings- och/eller testmängd. Om tre datamängder ska skapas kan ytterligare en uppdelning av någon av de första skapade mängderna göras.
# Laddar ett datamaterial som ska delas upp
data(iris)
# Uppdelningen sker slumpmässigt vilket innebär att set.seed() bör användas för att få samma observationer i materialen om koden körs igen
set.seed(100)
train_index <- createDataPartition(
# Ange en vektor av samma längd som antalet observationer i datamaterialet som ska delas upp
y = iris$Species,
# Antalet uppdelningar som ska ske. Måste vara 1!
times = 1,
# Anger andelen observationer som ska väljas ut
p = 0.80,
# Anger att de resulterande observationsindex ska vara en vektor
list = F)
# Lägger in de p valda observationerna till ett material och 1-p till det andra
train_data <- iris[train_index,]
other_data <- iris[-train_index,]
Beslutsträd
För att anpassa ett beslutsträd i R används rpart()
. I koden nedan anges ett urval av de viktigaste inställningarna som styr hur detta träd skapas som bör användas som mall för era modeller. Med summary()
skrivs trädet ut i text och innehåller mycket information.
tree <- rpart(
formula = y ~ .,
data = data_class_train,
method = "class",
# Anger föroreningsmått
parms = list(split = "gini"),
control = list(
## Stopkriterier
# Anger att antalet observationer som krävs för att en förgrening ska ske
minsplit = 10,
# Anger maxdjupet av träder, där 0 är rotnoden
maxdepth = 30,
# Anger den minsta tillåtna förbättringen som måste ske för att en förgrening ska ske
cp = 0,
# Två inställningar som inte används mer i detalj
maxcompete = 0,
maxsurrogate = 0,
## Trädanpassning
# Anger antalet korsvalideringar som ska ske medan modellens tränas, intern validering
xval = 0,
# Tillåter att förgreningar har surrogatregler som kan användas vid saknade värden
# Ska vara 2 om saknade värden finns i datamaterialet
usesurrogate = 0
)
)
## Sammanfattar alla noder, deras klassindelningar och fel
summary(tree, digits = 3)
För att undersöka ifall efterbeskärning ska göras av trädet kan följande kod användas.
### Beskärning av trädet
## Visar komplexiteten vid varje split (C_alpha(T)), ger indikation på huruvida cp argument i rpart bör användas för att reducera komplexitet
printcp(tree, digits = 3)
# Söker ut det cp-värde med minsta validerings-felet för beskärning av trädet
cp <- tree$cptable[which.min(tree$cptable[, "xerror"]), "CP"]
# Beskär trädet
tree_pruned <- prune(tree, cp = cp)
För att göra resultatet av beslutsträdet mer visuellt tilltalande kan plot.tree()
användas. Det finns även andra paket som kan producera ännu snyggare diagram som kanske är lättare att läsa ut, bl.a. rpart.plot
.
## Visualiserar trädet
plot(tree,
# Justerar placeringen av noder
uniform = TRUE,
# Lägger till vita kanter för att inte texten ska försvinna utanför diagrammet
margin = 0.05)
## Lägger till information vid varje split i trädet
text(tree,
# Lägger till all information i förgreningen
all = TRUE,
# Styr storleken av texten som läggs till
cex = 0.6)
Genom att använda den egenskapade funktionen för modellutvärdering enligt nedan fås en sammanställd utskrift med all information.
## Utvärdera modellen på träning och validering
class_evaluation(new_data = data_class_train,
model = tree,
true_y = data_class_train$y,
digits = 3)
class_evaluation(new_data = data_class_test,
model = tree,
true_y = data_class_test$y,
digits = 3)
K-närmaste-grannar
#### K närmaste grannar ####
## Skapar datamaterial
data("iris")
set.seed(100)
old_index <- createDataPartition(y = iris$Species, times = 1, p = 0.80, list = F)
old_data <- iris[old_index,]
new_data <- iris[-old_index,]
## Tränar modellen
knn_model <- train(form = Species ~ .,
data = old_data,
method = "knn",
# Standardiserar förklarande variabler
preProcess = c("center", "scale"),
trControl = trainControl(
# Anger att korsvalidering ska köras
method = "repeatedcv",
# Anger antalet k i k-fold korsvalidering, alltså inte k för KNN
number = 10,
# Repeterar valideringen tre gånger
repeats = 3,
# Anger att manuell val av utforskade k kommer ges, anges i tuneGrid
search = "grid"),
# Anger vilka k som ska letas igenom i valideringen
tuneGrid = expand.grid(k = c(5, 7, 9, 11, 15, 21, 23))
)
## Predikterar ny data på skattad modell
new_pred <- predict(knn_model,
newdata = new_data,
# "raw" ger majoritetsklassen,
# "prob" ger sannolikheterna (andelen) av grannar
type = "raw")
## Utvärderar modellen
classevaluation(new_data = new_data, model = knn_model, true_y = new_data$Species, type = "raw")
## K-närmaste grannar med viktad avstånd (UNDER UPPBYGGNAD)
wknn_model <- train(form = Species ~ ., # Anger responsvariabeln
data = old_data,
method = "kknn",
preProcess = c("center", "scale"),
trControl = trainControl(
# Anger att korsvalidering ska köras
method = "repeatedcv",
# Anger antalet k i k-fold korsvalidering, alltså inte k för KNN
number = 10,
# Repeterar valideringen tre gånger
repeats = 3)
)
new_pred <- predict(wknn_model,
newdata = new_data,
# "raw" ger majoritetsklassen,
# "prob" ger sannolikheterna (andelen) av grannar
type = "raw")
class_evaluation(new_data = new_data, model = wknn_model, true_y = new_data$Species, type = "raw")
Neurala nätverk
Genom att använda keras
-paketet möjliggörs större och mer flexibla nätverk, men paketet har vissa krav som ställs på data och sättet som modellen kodas. Först måste data vara angivet som en matris och responsvariabeln för klassificering behöver transformeras till binär form genom to_categorical()
.
När nätverket sedan skapas bygger vi först upp en arkitektur med olika lager, ex. layer_dense()
, och sedan anger vi hur modellen ska anpassas m.h.a. compile()
. Själva anpassningen startas sedan med fit()
.
#### Neurala Nätverk ####
# Laddar data likt Keras vill ha det strukturerat, måste vara matriser!
x_train <- as.matrix(data_class_train[, -which(colnames(data_class_train) == "y")])
x_test <- as.matrix(data_class_test[, -which(colnames(data_class_test) == "y")])
# Omstrukturerar kategorsk variabel till binär form
y_train <- to_categorical(data_class_train[, which(colnames(data_class_train) == "y")], num_classes = 10)
y_test <- to_categorical(data_class_test[, which(colnames(data_class_test) == "y")], num_classes = 10)
## Modell
# Skapar grundmodell
nn_model <- keras_model_sequential()
# Skapar arkitekturen
nn_model %>%
## Definierar första lagret, input_shape ska anges för att definiera hur många förklarande variabler som finns i data
layer_dense(
# Anger antal gömda noder i det GÖMDA lagret
units = 256,
# Anger aktiveringsfunktionen i lagret
activation = "relu",
# Det första gömda lagret anger hur många förklarande variabler som ska kopplas till lagret
# Notera att input_shape styr input-lagret, units styr det gömda lagret
input_shape = c(ncol(x_train)),
# Anger att vi vill ha med en bias-term i lagret
use_bias = TRUE,
name = "First"
) %>%
## Definierar andra lagret
layer_dense(
units = 128,
activation = "relu",
use_bias = TRUE,
name = "Second"
) %>%
## Anger det sista lagret där antalet units är antalet kategorier
layer_dense(
units = 10,
activation = "softmax",
name = "Final/Output"
)
## Anger en översiktsbild av den angivna arkitekturen
summary(nn_model)
## Definierar vilken anpassningsalgoritm som modellen ska skattas med
nn_model %>% compile(
# Anger förlustfunktionen som vi vill använda. Denna styrs direkt av vilken typ av y-variabel som finns i data
loss = 'categorical_crossentropy',
# Inlärningstakt av inkrementell inlärning
optimizer = optimizer_sgd(lr = 0.01),
# Anger att träffsäkerheten ska användas som mått för utvärdering
metrics = c('accuracy')
)
## Anpassar modellen
history <- nn_model %>% fit(
# Anger data
x = x_train, y = y_train,
# Maximala antalet gånger alla observationer i träningsmängden ska gås igenom innan anpassningen avslutas
epochs = 30,
# Anger hur många observationer som ska gås igenom innan vikterna uppdateras, här kan man ändra till batch-learning genom att ange nrow(x_train)
batch_size = 128,
# Anger hur mycket av träningsdatat som ska användas som valideringsmängd.
# Plockar ur data indexerat från slutet, så var vaksam på att data måste vara slumpat innan
# Om man har en separat valideringsmängd används validation_data = data
validation_split = 0.25
)
plot(history)
# Notera att för denna funktion måste y vara i klass-form, inte binärt som används i keras-funktionerna ovan
class_evaluation(new_data = x_train,
model = nn_model,
true_y = data_class_train$y)
class_evaluation(new_data = x_test,
model = nn_model,
true_y = data_class_test$y)