{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Bouw een logistiek regressiemodel - Les 4\n", "\n", "![Infographic logistieke vs. lineaire regressie](../../../../../../2-Regression/4-Logistic/images/linear-vs-logistic.png)\n", "\n", "#### **[Pre-lecture quiz](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/15/)**\n", "\n", "#### Introductie\n", "\n", "In deze laatste les over regressie, een van de basis *klassieke* ML-technieken, gaan we kijken naar logistieke regressie. Je gebruikt deze techniek om patronen te ontdekken en binaire categorieën te voorspellen. Is dit snoep chocolade of niet? Is deze ziekte besmettelijk of niet? Zal deze klant dit product kiezen of niet?\n", "\n", "In deze les leer je:\n", "\n", "- Technieken voor logistieke regressie\n", "\n", "✅ Verdiep je kennis over het werken met dit type regressie in deze [Learn module](https://learn.microsoft.com/training/modules/introduction-classification-models/?WT.mc_id=academic-77952-leestott)\n", "\n", "## Vereisten\n", "\n", "Nu we met de pompoendata hebben gewerkt, zijn we er inmiddels vertrouwd genoeg mee om te beseffen dat er één binaire categorie is waarmee we kunnen werken: `Color`.\n", "\n", "Laten we een logistiek regressiemodel bouwen om te voorspellen, gegeven enkele variabelen, *welke kleur een bepaalde pompoen waarschijnlijk heeft* (oranje 🎃 of wit 👻).\n", "\n", "> Waarom hebben we het over binaire classificatie in een lesgroep over regressie? Alleen om taalkundige redenen, aangezien logistieke regressie [eigenlijk een classificatiemethode](https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression) is, zij het een lineair gebaseerde. Leer meer over andere manieren om data te classificeren in de volgende lesgroep.\n", "\n", "Voor deze les hebben we de volgende pakketten nodig:\n", "\n", "- `tidyverse`: Het [tidyverse](https://www.tidyverse.org/) is een [collectie van R-pakketten](https://www.tidyverse.org/packages) ontworpen om data science sneller, eenvoudiger en leuker te maken!\n", "\n", "- `tidymodels`: Het [tidymodels](https://www.tidymodels.org/) framework is een [collectie van pakketten](https://www.tidymodels.org/packages/) voor modelleren en machine learning.\n", "\n", "- `janitor`: Het [janitor-pakket](https://github.com/sfirke/janitor) biedt eenvoudige tools voor het onderzoeken en opschonen van rommelige data.\n", "\n", "- `ggbeeswarm`: Het [ggbeeswarm-pakket](https://github.com/eclarke/ggbeeswarm) biedt methoden om beeswarm-stijl grafieken te maken met ggplot2.\n", "\n", "Je kunt ze installeren met:\n", "\n", "`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"ggbeeswarm\"))`\n", "\n", "Alternatief controleert het onderstaande script of je de benodigde pakketten hebt om deze module te voltooien en installeert ze voor je als ze ontbreken.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "suppressWarnings(if (!require(\"pacman\"))install.packages(\"pacman\"))\n", "\n", "pacman::p_load(tidyverse, tidymodels, janitor, ggbeeswarm)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## **Definieer de vraag**\n", "\n", "Voor onze doeleinden zullen we dit uitdrukken als een binair: 'Wit' of 'Niet Wit'. Er is ook een 'gestreepte' categorie in onze dataset, maar er zijn weinig gevallen van, dus we zullen deze niet gebruiken. Het verdwijnt sowieso zodra we de null-waarden uit de dataset verwijderen.\n", "\n", "> 🎃 Leuk weetje: we noemen witte pompoenen soms 'spookpompoenen'. Ze zijn niet zo makkelijk te snijden, dus ze zijn niet zo populair als de oranje, maar ze zien er wel gaaf uit! We zouden onze vraag dus ook kunnen herformuleren als: 'Spook' of 'Niet Spook'. 👻\n", "\n", "## **Over logistische regressie**\n", "\n", "Logistische regressie verschilt op een aantal belangrijke manieren van lineaire regressie, waar je eerder over hebt geleerd.\n", "\n", "#### **Binaire classificatie**\n", "\n", "Logistische regressie biedt niet dezelfde mogelijkheden als lineaire regressie. De eerste biedt een voorspelling over een `binaire categorie` (\"oranje of niet oranje\"), terwijl de laatste in staat is om `continue waarden` te voorspellen, bijvoorbeeld gegeven de herkomst van een pompoen en het tijdstip van oogsten, *hoeveel de prijs zal stijgen*.\n", "\n", "![Infographic door Dasani Madipalli](../../../../../../2-Regression/4-Logistic/images/pumpkin-classifier.png)\n", "\n", "### Andere classificaties\n", "\n", "Er zijn andere soorten logistische regressie, waaronder multinomiaal en ordinaal:\n", "\n", "- **Multinomiaal**, waarbij er meer dan één categorie is - \"Oranje, Wit en Gestreept\".\n", "\n", "- **Ordinaal**, waarbij er geordende categorieën zijn, handig als we onze uitkomsten logisch willen ordenen, zoals onze pompoenen die geordend zijn op een eindig aantal maten (mini,sm,med,lg,xl,xxl).\n", "\n", "![Multinomiaal vs ordinale regressie](../../../../../../2-Regression/4-Logistic/images/multinomial-vs-ordinal.png)\n", "\n", "#### **Variabelen HOEVEN NIET te correleren**\n", "\n", "Weet je nog dat lineaire regressie beter werkte met meer gecorreleerde variabelen? Logistische regressie is het tegenovergestelde - de variabelen hoeven niet te correleren. Dat werkt voor deze data, die enigszins zwakke correlaties heeft.\n", "\n", "#### **Je hebt veel schone data nodig**\n", "\n", "Logistische regressie geeft nauwkeurigere resultaten als je meer data gebruikt; onze kleine dataset is niet optimaal voor deze taak, dus houd dat in gedachten.\n", "\n", "✅ Denk na over de soorten data die zich goed lenen voor logistische regressie.\n", "\n", "## Oefening - maak de data schoon\n", "\n", "Maak eerst de data een beetje schoon door null-waarden te verwijderen en alleen enkele kolommen te selecteren:\n", "\n", "1. Voeg de volgende code toe:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Load the core tidyverse packages\n", "library(tidyverse)\n", "\n", "# Import the data and clean column names\n", "pumpkins <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/2-Regression/data/US-pumpkins.csv\") %>% \n", " clean_names()\n", "\n", "# Select desired columns\n", "pumpkins_select <- pumpkins %>% \n", " select(c(city_name, package, variety, origin, item_size, color)) \n", "\n", "# Drop rows containing missing values and encode color as factor (category)\n", "pumpkins_select <- pumpkins_select %>% \n", " drop_na() %>% \n", " mutate(color = factor(color))\n", "\n", "# View the first few rows\n", "pumpkins_select %>% \n", " slice_head(n = 5)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Je kunt altijd een kijkje nemen in je nieuwe dataframe door de functie [*glimpse()*](https://pillar.r-lib.org/reference/glimpse.html) te gebruiken, zoals hieronder:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "pumpkins_select %>% \n", " glimpse()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Laten we bevestigen dat we daadwerkelijk een binair classificatieprobleem gaan aanpakken:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Subset distinct observations in outcome column\n", "pumpkins_select %>% \n", " distinct(color)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Visualisatie - categorische plot\n", "Je hebt inmiddels de pompoendata opnieuw geladen en schoongemaakt, zodat je een dataset hebt met een paar variabelen, waaronder Kleur. Laten we de dataframe in het notebook visualiseren met behulp van de ggplot-bibliotheek.\n", "\n", "De ggplot-bibliotheek biedt handige manieren om je data te visualiseren. Bijvoorbeeld, je kunt de verdelingen van de data vergelijken voor elke Variëteit en Kleur in een categorische plot.\n", "\n", "1. Maak zo'n plot door de geombar-functie te gebruiken, met onze pompoendata, en specificeer een kleurmapping voor elke pompoencategorie (oranje of wit):\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "# Specify colors for each value of the hue variable\n", "palette <- c(ORANGE = \"orange\", WHITE = \"wheat\")\n", "\n", "# Create the bar plot\n", "ggplot(pumpkins_select, aes(y = variety, fill = color)) +\n", " geom_bar(position = \"dodge\") +\n", " scale_fill_manual(values = palette) +\n", " labs(y = \"Variety\", fill = \"Color\") +\n", " theme_minimal()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Door de gegevens te bekijken, kun je zien hoe de Kleuren-gegevens zich verhouden tot de Variëteit.\n", "\n", "✅ Gezien deze categorische plot, welke interessante onderzoeken kun je bedenken?\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Voorbewerking van data: feature encoding\n", "\n", "Onze dataset met pompoenen bevat stringwaarden voor alle kolommen. Werken met categorische data is intuïtief voor mensen, maar niet voor machines. Machine learning-algoritmes werken goed met getallen. Daarom is encoding een zeer belangrijke stap in de fase van data-voorbewerking, omdat het ons in staat stelt om categorische data om te zetten in numerieke data, zonder informatie te verliezen. Goede encoding leidt tot het bouwen van een goed model.\n", "\n", "Voor feature encoding zijn er twee hoofdtypen encoders:\n", "\n", "1. Ordinale encoder: dit is geschikt voor ordinale variabelen, wat categorische variabelen zijn waarbij de data een logische volgorde volgt, zoals de kolom `item_size` in onze dataset. Het maakt een mapping waarbij elke categorie wordt weergegeven door een getal, dat overeenkomt met de volgorde van de categorie in de kolom.\n", "\n", "2. Categorische encoder: dit is geschikt voor nominale variabelen, wat categorische variabelen zijn waarbij de data geen logische volgorde volgt, zoals alle kenmerken die anders zijn dan `item_size` in onze dataset. Dit is een one-hot encoding, wat betekent dat elke categorie wordt weergegeven door een binaire kolom: de gecodeerde variabele is gelijk aan 1 als de pompoen tot die Variety behoort en anders 0.\n", "\n", "Tidymodels biedt nog een ander handig pakket: [recipes](https://recipes.tidymodels.org/) - een pakket voor het voorbewerken van data. We definiëren een `recipe` die aangeeft dat alle voorspellerkolommen moeten worden gecodeerd in een reeks gehele getallen, `prep` om de benodigde hoeveelheden en statistieken te schatten die nodig zijn voor bewerkingen, en uiteindelijk `bake` om de berekeningen toe te passen op nieuwe data.\n", "\n", "> Normaal gesproken wordt recipes meestal gebruikt als een voorbewerker voor modellering, waarbij het definieert welke stappen moeten worden toegepast op een dataset om deze klaar te maken voor modellering. In dat geval wordt het **ten zeerste aanbevolen** om een `workflow()` te gebruiken in plaats van handmatig een recipe te schatten met prep en bake. We zullen dit allemaal zo meteen zien.\n", ">\n", "> Voor nu gebruiken we echter recipes + prep + bake om te specificeren welke stappen moeten worden toegepast op een dataset om deze klaar te maken voor data-analyse en vervolgens de voorbewerkte data met de toegepaste stappen te extraheren.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Preprocess and extract data to allow some data analysis\n", "baked_pumpkins <- recipe(color ~ ., data = pumpkins_select) %>%\n", " # Define ordering for item_size column\n", " step_mutate(item_size = ordered(item_size, levels = c('sml', 'med', 'med-lge', 'lge', 'xlge', 'jbo', 'exjbo'))) %>%\n", " # Convert factors to numbers using the order defined above (Ordinal encoding)\n", " step_integer(item_size, zero_based = F) %>%\n", " # Encode all other predictors using one hot encoding\n", " step_dummy(all_nominal(), -all_outcomes(), one_hot = TRUE) %>%\n", " prep(data = pumpkin_select) %>%\n", " bake(new_data = NULL)\n", "\n", "# Display the first few rows of preprocessed data\n", "baked_pumpkins %>% \n", " slice_head(n = 5)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "✅ Wat zijn de voordelen van het gebruik van een ordinale encoder voor de kolom Item Size?\n", "\n", "### Analyseer relaties tussen variabelen\n", "\n", "Nu we onze gegevens hebben voorbewerkt, kunnen we de relaties tussen de kenmerken en het label analyseren om een idee te krijgen van hoe goed het model in staat zal zijn om het label te voorspellen op basis van de kenmerken. De beste manier om dit soort analyse uit te voeren is door de gegevens te visualiseren. \n", "We zullen opnieuw de ggplot-functie geom_boxplot_ gebruiken om de relaties tussen Item Size, Variety en Color in een categorische plot te visualiseren. Om de gegevens beter te plotten, gebruiken we de gecodeerde kolom Item Size en de niet-gecodeerde kolom Variety.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Define the color palette\n", "palette <- c(ORANGE = \"orange\", WHITE = \"wheat\")\n", "\n", "# We need the encoded Item Size column to use it as the x-axis values in the plot\n", "pumpkins_select_plot<-pumpkins_select\n", "pumpkins_select_plot$item_size <- baked_pumpkins$item_size\n", "\n", "# Create the grouped box plot\n", "ggplot(pumpkins_select_plot, aes(x = `item_size`, y = color, fill = color)) +\n", " geom_boxplot() +\n", " facet_grid(variety ~ ., scales = \"free_x\") +\n", " scale_fill_manual(values = palette) +\n", " labs(x = \"Item Size\", y = \"\") +\n", " theme_minimal() +\n", " theme(strip.text = element_text(size = 12)) +\n", " theme(axis.text.x = element_text(size = 10)) +\n", " theme(axis.title.x = element_text(size = 12)) +\n", " theme(axis.title.y = element_blank()) +\n", " theme(legend.position = \"bottom\") +\n", " guides(fill = guide_legend(title = \"Color\")) +\n", " theme(panel.spacing = unit(0.5, \"lines\"))+\n", " theme(strip.text.y = element_text(size = 4, hjust = 0)) \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Gebruik een zwermplot\n", "\n", "Omdat Kleur een binaire categorie is (Wit of Niet), heeft het 'een [gespecialiseerde aanpak](https://github.com/rstudio/cheatsheets/blob/main/data-visualization.pdf) nodig voor visualisatie'.\n", "\n", "Probeer een `zwermplot` om de verdeling van kleur ten opzichte van de item_grootte te laten zien.\n", "\n", "We gebruiken het [ggbeeswarm-pakket](https://github.com/eclarke/ggbeeswarm), dat methoden biedt om zwermachtige plots te maken met ggplot2. Zwermplots zijn een manier om punten te plotten die normaal gesproken overlappen, zodat ze naast elkaar worden geplaatst.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Create beeswarm plots of color and item_size\n", "baked_pumpkins %>% \n", " mutate(color = factor(color)) %>% \n", " ggplot(mapping = aes(x = color, y = item_size, color = color)) +\n", " geom_quasirandom() +\n", " scale_color_brewer(palette = \"Dark2\", direction = -1) +\n", " theme(legend.position = \"none\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nu we een idee hebben van de relatie tussen de binaire categorieën van kleur en de grotere groep van maten, laten we logistieke regressie verkennen om de waarschijnlijke kleur van een bepaalde pompoen te bepalen.\n", "\n", "## Bouw je model\n", "\n", "Selecteer de variabelen die je wilt gebruiken in je classificatiemodel en splits de data in trainings- en testsets. [rsample](https://rsample.tidymodels.org/), een pakket in Tidymodels, biedt infrastructuur voor efficiënte datasplitsing en herhaald steekproefnemen:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Split data into 80% for training and 20% for testing\n", "set.seed(2056)\n", "pumpkins_split <- pumpkins_select %>% \n", " initial_split(prop = 0.8)\n", "\n", "# Extract the data in each split\n", "pumpkins_train <- training(pumpkins_split)\n", "pumpkins_test <- testing(pumpkins_split)\n", "\n", "# Print out the first 5 rows of the training set\n", "pumpkins_train %>% \n", " slice_head(n = 5)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "🙌 We zijn nu klaar om een model te trainen door de trainingskenmerken aan het trainingslabel (kleur) te koppelen.\n", "\n", "We beginnen met het maken van een recept dat de preprocessiestappen specificeert die op onze gegevens moeten worden uitgevoerd om ze klaar te maken voor modellering, bijvoorbeeld: het coderen van categorische variabelen naar een reeks gehele getallen. Net zoals `baked_pumpkins` maken we een `pumpkins_recipe`, maar we gebruiken geen `prep` en `bake`, omdat dit wordt opgenomen in een workflow, zoals je over een paar stappen zult zien.\n", "\n", "Er zijn behoorlijk wat manieren om een logistisch regressiemodel te specificeren in Tidymodels. Zie `?logistic_reg()`. Voor nu specificeren we een logistisch regressiemodel via de standaard `stats::glm()` engine.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Create a recipe that specifies preprocessing steps for modelling\n", "pumpkins_recipe <- recipe(color ~ ., data = pumpkins_train) %>% \n", " step_mutate(item_size = ordered(item_size, levels = c('sml', 'med', 'med-lge', 'lge', 'xlge', 'jbo', 'exjbo'))) %>%\n", " step_integer(item_size, zero_based = F) %>% \n", " step_dummy(all_nominal(), -all_outcomes(), one_hot = TRUE)\n", "\n", "# Create a logistic model specification\n", "log_reg <- logistic_reg() %>% \n", " set_engine(\"glm\") %>% \n", " set_mode(\"classification\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nu we een recept en een modelspecificatie hebben, moeten we een manier vinden om deze samen te bundelen in een object dat eerst de gegevens zal voorbewerken (prep+bake achter de schermen), het model zal trainen op de voorbewerkte gegevens en ook ruimte biedt voor eventuele nabewerkingsactiviteiten.\n", "\n", "In Tidymodels wordt dit handige object een [`workflow`](https://workflows.tidymodels.org/) genoemd en het bevat op een handige manier je modelcomponenten.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Bundle modelling components in a workflow\n", "log_reg_wf <- workflow() %>% \n", " add_recipe(pumpkins_recipe) %>% \n", " add_model(log_reg)\n", "\n", "# Print out the workflow\n", "log_reg_wf\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nadat een workflow is *gespecificeerd*, kan een model worden `getraind` met behulp van de [`fit()`](https://tidymodels.github.io/parsnip/reference/fit.html) functie. De workflow zal een recept inschatten en de gegevens vooraf verwerken voordat het trainen begint, zodat we dat niet handmatig hoeven te doen met prep en bake.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Train the model\n", "wf_fit <- log_reg_wf %>% \n", " fit(data = pumpkins_train)\n", "\n", "# Print the trained workflow\n", "wf_fit\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Het model toont de coëfficiënten die tijdens de training zijn geleerd.\n", "\n", "Nu we het model hebben getraind met de trainingsdata, kunnen we voorspellingen doen op de testdata met behulp van [parsnip::predict()](https://parsnip.tidymodels.org/reference/predict.model_fit.html). Laten we beginnen met het gebruiken van het model om labels voor onze testset te voorspellen en de waarschijnlijkheden voor elk label te berekenen. Wanneer de waarschijnlijkheid groter is dan 0,5, is de voorspelde klasse `WHITE`, anders `ORANGE`.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Make predictions for color and corresponding probabilities\n", "results <- pumpkins_test %>% select(color) %>% \n", " bind_cols(wf_fit %>% \n", " predict(new_data = pumpkins_test)) %>%\n", " bind_cols(wf_fit %>%\n", " predict(new_data = pumpkins_test, type = \"prob\"))\n", "\n", "# Compare predictions\n", "results %>% \n", " slice_head(n = 10)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Heel goed! Dit biedt wat meer inzicht in hoe logistische regressie werkt.\n", "\n", "### Betere begrip via een confusion matrix\n", "\n", "Het vergelijken van elke voorspelling met de bijbehorende \"ground truth\" werkelijke waarde is niet de meest efficiënte manier om te bepalen hoe goed het model voorspelt. Gelukkig heeft Tidymodels nog een paar handige tools: [`yardstick`](https://yardstick.tidymodels.org/) - een pakket dat wordt gebruikt om de effectiviteit van modellen te meten met behulp van prestatie-indicatoren.\n", "\n", "Een prestatie-indicator die vaak wordt gebruikt bij classificatieproblemen is de [`confusion matrix`](https://wikipedia.org/wiki/Confusion_matrix). Een confusion matrix beschrijft hoe goed een classificatiemodel presteert. Het geeft weer hoeveel voorbeelden in elke klasse correct zijn geclassificeerd door een model. In ons geval laat het zien hoeveel oranje pompoenen correct als oranje zijn geclassificeerd en hoeveel witte pompoenen correct als wit zijn geclassificeerd; de confusion matrix laat ook zien hoeveel voorbeelden in de **verkeerde** categorieën zijn ingedeeld.\n", "\n", "De functie [**`conf_mat()`**](https://tidymodels.github.io/yardstick/reference/conf_mat.html) van yardstick berekent deze kruistabel van waargenomen en voorspelde klassen.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Confusion matrix for prediction results\n", "conf_mat(data = results, truth = color, estimate = .pred_class)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Laten we de verwarringsmatrix interpreteren. Ons model wordt gevraagd om pompoenen te classificeren in twee binaire categorieën: categorie `wit` en categorie `niet-wit`.\n", "\n", "- Als je model een pompoen voorspelt als wit en deze behoort in werkelijkheid tot de categorie 'wit', noemen we dit een `true positive`, weergegeven door het getal linksboven.\n", "\n", "- Als je model een pompoen voorspelt als niet-wit en deze behoort in werkelijkheid tot de categorie 'wit', noemen we dit een `false negative`, weergegeven door het getal linksonder.\n", "\n", "- Als je model een pompoen voorspelt als wit en deze behoort in werkelijkheid tot de categorie 'niet-wit', noemen we dit een `false positive`, weergegeven door het getal rechtsboven.\n", "\n", "- Als je model een pompoen voorspelt als niet-wit en deze behoort in werkelijkheid tot de categorie 'niet-wit', noemen we dit een `true negative`, weergegeven door het getal rechtsonder.\n", "\n", "| Waarheid |\n", "|:--------:|\n", "\n", "\n", "| | | |\n", "|---------------|--------|-------|\n", "| **Voorspeld** | WIT | ORANJE |\n", "| WIT | TP | FP |\n", "| ORANJE | FN | TN |\n", "\n", "Zoals je misschien al vermoedt, is het wenselijk om een groter aantal true positives en true negatives te hebben, en een lager aantal false positives en false negatives. Dit impliceert dat het model beter presteert.\n", "\n", "De verwarringsmatrix is nuttig omdat deze leidt tot andere statistieken die ons kunnen helpen de prestaties van een classificatiemodel beter te evalueren. Laten we enkele van deze statistieken doornemen:\n", "\n", "🎓 Precisie: `TP/(TP + FP)` gedefinieerd als het aandeel voorspelde positieven dat daadwerkelijk positief is. Ook wel [positieve voorspellende waarde](https://en.wikipedia.org/wiki/Positive_predictive_value \"Positive predictive value\") genoemd.\n", "\n", "🎓 Recall: `TP/(TP + FN)` gedefinieerd als het aandeel positieve resultaten uit het aantal monsters dat daadwerkelijk positief was. Ook bekend als `gevoeligheid`.\n", "\n", "🎓 Specificiteit: `TN/(TN + FP)` gedefinieerd als het aandeel negatieve resultaten uit het aantal monsters dat daadwerkelijk negatief was.\n", "\n", "🎓 Nauwkeurigheid: `TP + TN/(TP + TN + FP + FN)` Het percentage labels dat correct is voorspeld voor een monster.\n", "\n", "🎓 F-Score: Een gewogen gemiddelde van de precisie en recall, waarbij 1 het beste is en 0 het slechtste.\n", "\n", "Laten we deze statistieken berekenen!\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Combine metric functions and calculate them all at once\n", "eval_metrics <- metric_set(ppv, recall, spec, f_meas, accuracy)\n", "eval_metrics(data = results, truth = color, estimate = .pred_class)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualiseer de ROC-curve van dit model\n", "\n", "Laten we nog een visualisatie maken om de zogenaamde [`ROC-curve`](https://en.wikipedia.org/wiki/Receiver_operating_characteristic) te bekijken:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Make a roc_curve\n", "results %>% \n", " roc_curve(color, .pred_ORANGE) %>% \n", " autoplot()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ROC-curves worden vaak gebruikt om een beeld te krijgen van de prestaties van een classifier in termen van echte versus valse positieven. ROC-curves tonen meestal de `True Positive Rate`/Sensitiviteit op de Y-as en de `False Positive Rate`/1-Specificiteit op de X-as. De steilheid van de curve en de ruimte tussen de middellijn en de curve zijn belangrijk: je wilt een curve die snel omhoog gaat en de lijn overschrijdt. In ons geval zijn er in het begin valse positieven, waarna de lijn correct omhoog en over de lijn gaat.\n", "\n", "Laten we tot slot `yardstick::roc_auc()` gebruiken om de daadwerkelijke Area Under the Curve te berekenen. Een manier om AUC te interpreteren is als de kans dat het model een willekeurig positief voorbeeld hoger rangschikt dan een willekeurig negatief voorbeeld.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "# Calculate area under curve\n", "results %>% \n", " roc_auc(color, .pred_ORANGE)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Het resultaat is ongeveer `0.975`. Aangezien de AUC varieert van 0 tot 1, wil je een hoge score, omdat een model dat 100% correct is in zijn voorspellingen een AUC van 1 zal hebben; in dit geval is het model *best goed*.\n", "\n", "In toekomstige lessen over classificaties leer je hoe je de scores van je model kunt verbeteren (zoals het omgaan met onevenwichtige data in dit geval).\n", "\n", "## 🚀Uitdaging\n", "\n", "Er is nog veel meer te ontdekken over logistische regressie! Maar de beste manier om te leren is door te experimenteren. Zoek een dataset die zich leent voor dit type analyse en bouw er een model mee. Wat leer je? tip: probeer [Kaggle](https://www.kaggle.com/search?q=logistic+regression+datasets) voor interessante datasets.\n", "\n", "## Review & Zelfstudie\n", "\n", "Lees de eerste paar pagina's van [dit artikel van Stanford](https://web.stanford.edu/~jurafsky/slp3/5.pdf) over enkele praktische toepassingen van logistische regressie. Denk na over taken die beter geschikt zijn voor het ene of het andere type regressietaken die we tot nu toe hebben bestudeerd. Wat zou het beste werken?\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n---\n\n**Disclaimer**: \nDit document is vertaald met behulp van de AI-vertalingsservice [Co-op Translator](https://github.com/Azure/co-op-translator). Hoewel we streven naar nauwkeurigheid, willen we u erop wijzen dat geautomatiseerde vertalingen fouten of onnauwkeurigheden kunnen bevatten. Het originele document in de oorspronkelijke taal moet worden beschouwd als de gezaghebbende bron. Voor kritieke informatie wordt professionele menselijke vertaling aanbevolen. Wij zijn niet aansprakelijk voor misverstanden of verkeerde interpretaties die voortvloeien uit het gebruik van deze vertaling.\n" ] } ], "metadata": { "anaconda-cloud": "", "kernelspec": { "display_name": "R", "langauge": "R", "name": "ir" }, "language_info": { "codemirror_mode": "r", "file_extension": ".r", "mimetype": "text/x-r-source", "name": "R", "pygments_lexer": "r", "version": "3.4.1" }, "coopTranslator": { "original_hash": "feaf125f481a89c468fa115bf2aed580", "translation_date": "2025-09-06T11:45:34+00:00", "source_file": "2-Regression/4-Logistic/solution/R/lesson_4-R.ipynb", "language_code": "nl" } }, "nbformat": 4, "nbformat_minor": 1 }