{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Rakenna logistinen regressiomalli - Oppitunti 4\n", "\n", "![Logistinen vs. lineaarinen regressio - infografiikka](../../../../../../2-Regression/4-Logistic/images/linear-vs-logistic.png)\n", "\n", "#### **[Esiluennon kysely](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/15/)**\n", "\n", "#### Johdanto\n", "\n", "Tässä viimeisessä oppitunnissa regressiosta, joka on yksi perus *klassinen* ML-tekniikka, tarkastelemme logistista regressiota. Tätä tekniikkaa käytetään mallintamaan ja ennustamaan binäärisiä kategorioita. Onko tämä karkki suklaata vai ei? Onko tämä tauti tarttuva vai ei? Valitseeko asiakas tämän tuotteen vai ei?\n", "\n", "Tässä oppitunnissa opit:\n", "\n", "- Tekniikoita logistiseen regressioon\n", "\n", "✅ Syvennä ymmärrystäsi tämän tyyppisen regression käytöstä tässä [Learn-moduulissa](https://learn.microsoft.com/training/modules/introduction-classification-models/?WT.mc_id=academic-77952-leestott)\n", "\n", "## Esitiedot\n", "\n", "Työskenneltyämme kurpitsadataa kanssa olemme nyt riittävän tuttuja sen kanssa huomataksemme, että siinä on yksi binäärinen kategoria, jonka kanssa voimme työskennellä: `Color`.\n", "\n", "Rakennetaan logistinen regressiomalli ennustamaan, annettujen muuttujien perusteella, *minkä värinen tietty kurpitsa todennäköisesti on* (oranssi 🎃 vai valkoinen 👻).\n", "\n", "> Miksi puhumme binäärisestä luokittelusta regressio-oppituntien yhteydessä? Ainoastaan kielellisen mukavuuden vuoksi, sillä logistinen regressio on [todellisuudessa luokittelumenetelmä](https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression), vaikkakin lineaarinen. Opit muita tapoja luokitella dataa seuraavassa oppituntiryhmässä.\n", "\n", "Tätä oppituntia varten tarvitsemme seuraavat paketit:\n", "\n", "- `tidyverse`: [tidyverse](https://www.tidyverse.org/) on [R-pakettien kokoelma](https://www.tidyverse.org/packages), joka tekee data-analytiikasta nopeampaa, helpompaa ja hauskempaa!\n", "\n", "- `tidymodels`: [tidymodels](https://www.tidymodels.org/) -kehys on [pakettikokoelma](https://www.tidymodels.org/packages/) mallinnukseen ja koneoppimiseen.\n", "\n", "- `janitor`: [janitor-paketti](https://github.com/sfirke/janitor) tarjoaa yksinkertaisia työkaluja likaisen datan tarkasteluun ja puhdistamiseen.\n", "\n", "- `ggbeeswarm`: [ggbeeswarm-paketti](https://github.com/eclarke/ggbeeswarm) tarjoaa menetelmiä beeswarm-tyylisten kaavioiden luomiseen ggplot2:lla.\n", "\n", "Voit asentaa ne seuraavasti:\n", "\n", "`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"ggbeeswarm\"))`\n", "\n", "Vaihtoehtoisesti alla oleva skripti tarkistaa, onko sinulla tarvittavat paketit tämän moduulin suorittamiseen, ja asentaa ne puolestasi, jos ne puuttuvat.\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": [ "## **Määrittele kysymys**\n", "\n", "Tässä tapauksessa ilmaisemme sen binäärisesti: 'Valkoinen' tai 'Ei valkoinen'. Datasetissämme on myös 'raidallinen' kategoria, mutta sen esiintymiä on vähän, joten emme käytä sitä. Se katoaa joka tapauksessa, kun poistamme datasetistä null-arvot.\n", "\n", "> 🎃 Hauska fakta: kutsumme joskus valkoisia kurpitsoja 'haamukurpitsoiksi'. Niitä ei ole kovin helppo kaivertaa, joten ne eivät ole yhtä suosittuja kuin oranssit, mutta ne näyttävät siisteiltä! Voisimme siis muotoilla kysymyksemme myös näin: 'Haamu' vai 'Ei haamu'. 👻\n", "\n", "## **Tietoa logistisesta regressiosta**\n", "\n", "Logistinen regressio eroaa lineaarisesta regressiosta, josta opit aiemmin, muutamalla tärkeällä tavalla.\n", "\n", "#### **Binääriluokittelu**\n", "\n", "Logistinen regressio ei tarjoa samoja ominaisuuksia kuin lineaarinen regressio. Edellinen antaa ennusteen `binäärisestä kategoriasta` (\"oranssi tai ei oranssi\"), kun taas jälkimmäinen pystyy ennustamaan `jatkuvia arvoja`, esimerkiksi kurpitsan alkuperän ja sadonkorjuuajan perusteella, *kuinka paljon sen hinta nousee*.\n", "\n", "![Infografiikka: Dasani Madipalli](../../../../../../2-Regression/4-Logistic/images/pumpkin-classifier.png)\n", "\n", "### Muut luokittelut\n", "\n", "Logistisessa regressiossa on myös muita tyyppejä, kuten multinomiaalinen ja ordinaalinen:\n", "\n", "- **Multinomiaalinen**, jossa on useampi kuin yksi kategoria - \"Oranssi, Valkoinen ja Raidallinen\".\n", "\n", "- **Ordinaalinen**, jossa on järjestettyjä kategorioita. Tämä on hyödyllistä, jos haluamme järjestää tulokset loogisesti, kuten kurpitsat, jotka on järjestetty rajallisen määrän kokoluokkien mukaan (mini, sm, med, lg, xl, xxl).\n", "\n", "![Multinomiaalinen vs ordinaalinen regressio](../../../../../../2-Regression/4-Logistic/images/multinomial-vs-ordinal.png)\n", "\n", "#### **Muuttujien EI tarvitse korreloida**\n", "\n", "Muistatko, kuinka lineaarinen regressio toimi paremmin, kun muuttujat korreloivat enemmän? Logistinen regressio on päinvastainen - muuttujien ei tarvitse olla linjassa. Tämä sopii tälle datalle, jossa korrelaatiot ovat melko heikkoja.\n", "\n", "#### **Tarvitset paljon puhdasta dataa**\n", "\n", "Logistinen regressio antaa tarkempia tuloksia, jos käytät enemmän dataa; pieni datasetimme ei ole optimaalinen tähän tehtävään, joten pidä tämä mielessä.\n", "\n", "✅ Mieti, millaiset datatyypit sopisivat hyvin logistiseen regressioon.\n", "\n", "## Harjoitus - siivoa data\n", "\n", "Ensiksi siivoa dataa hieman, poista null-arvot ja valitse vain osa sarakkeista:\n", "\n", "1. Lisää seuraava koodi:\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": [ "Voit aina vilkaista uutta dataframeasi käyttämällä [*glimpse()*](https://pillar.r-lib.org/reference/glimpse.html)-funktiota seuraavasti:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "vscode": { "languageId": "r" } }, "outputs": [], "source": [ "pumpkins_select %>% \n", " glimpse()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Varmistetaan, että kyseessä on todellakin binääriluokitusongelma:\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": [ "### Visualisointi - kategorinen kaavio\n", "Olet nyt ladannut kurpitsadatan uudelleen ja siivonnut sen niin, että datasettiin on säilytetty muutamia muuttujia, mukaan lukien Väri. Visualisoidaan dataframe muistikirjassa käyttämällä ggplot-kirjastoa.\n", "\n", "Ggplot-kirjasto tarjoaa käteviä tapoja visualisoida dataasi. Esimerkiksi voit vertailla datan jakaumia jokaisen Lajikkeen ja Värin osalta kategorisessa kaaviossa.\n", "\n", "1. Luo tällainen kaavio käyttämällä geombar-funktiota, hyödyntäen kurpitsadataamme, ja määritä värikartoitus jokaiselle kurpitsakategorialle (oranssi tai valkoinen):\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": [ "Tarkastelemalla tietoja voit nähdä, miten Väri-data liittyy Lajikkeeseen.\n", "\n", "✅ Kun tarkastelet tätä kategorista kaaviota, millaisia mielenkiintoisia tutkimusideoita voit kuvitella?\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Datan esikäsittely: piirteiden koodaus\n", "\n", "Kurpitsadatasetissamme kaikki sarakkeet sisältävät merkkijonoarvoja. Kategorisen datan käsittely on ihmisille intuitiivista, mutta koneille ei. Koneoppimisalgoritmit toimivat hyvin numeroiden kanssa. Siksi koodaus on erittäin tärkeä vaihe datan esikäsittelyssä, sillä sen avulla voimme muuntaa kategorisen datan numeeriseksi menettämättä tietoa. Hyvä koodaus auttaa rakentamaan hyvän mallin.\n", "\n", "Piirteiden koodaukseen on kaksi päätyyppiä:\n", "\n", "1. Ordinaalikoodaus: Tämä sopii hyvin ordinaalisille muuttujille, jotka ovat kategorisia muuttujia, joiden data noudattaa loogista järjestystä, kuten `item_size`-sarake datasetissämme. Se luo kartoituksen, jossa jokainen kategoria esitetään numerolla, joka vastaa kategorian järjestystä sarakkeessa.\n", "\n", "2. Kategorinen koodaus: Tämä sopii hyvin nominaalisille muuttujille, jotka ovat kategorisia muuttujia, joiden data ei noudata loogista järjestystä, kuten kaikki muut piirteet datasetissamme paitsi `item_size`. Tämä on niin sanottu one-hot-koodaus, mikä tarkoittaa, että jokainen kategoria esitetään binäärisarakkeena: koodattu muuttuja on arvoltaan 1, jos kurpitsa kuuluu kyseiseen Varietyyn, ja 0 muuten.\n", "\n", "Tidymodels tarjoaa vielä yhden kätevän paketin: [recipes](https://recipes.tidymodels.org/) - paketti datan esikäsittelyyn. Määrittelemme `recipe`-objektin, joka määrittää, että kaikki ennustajasarakkeet tulisi koodata kokonaisluvuiksi, `prep`-toiminnolla arvioimme tarvittavat määrät ja tilastot operaatioita varten, ja lopuksi `bake`-toiminnolla sovellamme laskelmat uuteen dataan.\n", "\n", "> Yleensä recipes-pakettia käytetään esiprosessorina mallinnuksessa, jossa se määrittää, mitä vaiheita dataan tulisi soveltaa, jotta se olisi valmis mallinnukseen. Tässä tapauksessa on **erittäin suositeltavaa**, että käytät `workflow()`-toimintoa sen sijaan, että arvioisit reseptin manuaalisesti prep- ja bake-toiminnoilla. Näemme tämän kaiken hetken kuluttua.\n", ">\n", "> Tällä kertaa käytämme kuitenkin recipes + prep + bake -yhdistelmää määrittääksemme, mitä vaiheita dataan tulisi soveltaa, jotta se olisi valmis data-analyysiin, ja sitten poimimme esikäsitellyn datan sovelletuilla vaiheilla.\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": [ "✅ Mitkä ovat edut käyttää järjestysenkooderia Item Size -sarakkeelle?\n", "\n", "### Analysoi muuttujien välisiä suhteita\n", "\n", "Kun olemme esikäsitelleet datamme, voimme analysoida ominaisuuksien ja tunnisteen välisiä suhteita saadaksemme käsityksen siitä, kuinka hyvin malli pystyy ennustamaan tunnisteen ominaisuuksien perusteella. Paras tapa suorittaa tällainen analyysi on datan visualisointi. \n", "Käytämme jälleen ggplotin geom_boxplot_-funktiota, jotta voimme visualisoida Item Size-, Variety- ja Color-sarakkeiden välisiä suhteita kategorisessa kaaviossa. Jotta voimme paremmin visualisoida dataa, käytämme enkoodattua Item Size -sarakeetta ja enkoodaamatonta Variety-saraketta.\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": [ "#### Käytä parviplottia\n", "\n", "Koska Väri on binäärinen kategoria (Valkoinen tai Ei), sen visualisointiin tarvitaan '[erikoistunut lähestymistapa](https://github.com/rstudio/cheatsheets/blob/main/data-visualization.pdf)'.\n", "\n", "Kokeile `parviplottia` näyttääksesi värin jakautumisen suhteessa item_size-arvoon.\n", "\n", "Käytämme [ggbeeswarm-pakettia](https://github.com/eclarke/ggbeeswarm), joka tarjoaa menetelmiä parvityylisten plottien luomiseen ggplot2:n avulla. Parviplotit ovat tapa esittää pisteitä, jotka normaalisti menisivät päällekkäin, niin että ne asettuvat vierekkäin.\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": [ "Nyt kun meillä on käsitys värin binääristen kategorioiden ja suuremman kokoryhmän välisestä suhteesta, tutkitaan logistista regressiota määrittääksemme todennäköisen värin tietylle kurpitsalle.\n", "\n", "## Rakenna mallisi\n", "\n", "Valitse muuttujat, joita haluat käyttää luokittelumallissasi, ja jaa data koulutus- ja testijoukkoihin. [rsample](https://rsample.tidymodels.org/), Tidymodels-paketti, tarjoaa infrastruktuurin tehokkaaseen datan jakamiseen ja uudelleennäytteistämiseen:\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": [ "🙌 Olemme nyt valmiita kouluttamaan mallin sovittamalla harjoitusominaisuudet harjoitusmerkkiin (väri).\n", "\n", "Aloitamme luomalla reseptin, joka määrittelee esikäsittelyvaiheet, jotka datalle tulee suorittaa, jotta se olisi valmis mallinnusta varten, esimerkiksi: kategoristen muuttujien koodaaminen kokonaisluvuiksi. Aivan kuten `baked_pumpkins`, luomme `pumpkins_recipe`, mutta emme käytä `prep`- ja `bake`-toimintoja, koska ne sisällytetään työnkulkuun, jonka näet muutaman askeleen päästä.\n", "\n", "Tidymodelsissa on useita tapoja määritellä logistinen regressiomalli. Katso `?logistic_reg()`. Tällä kertaa määrittelemme logistisen regressiomallin oletusarvoisen `stats::glm()`-moottorin avulla.\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": [ "Nyt kun meillä on resepti ja mallin määrittely, meidän täytyy löytää tapa yhdistää ne yhdeksi objektiksi, joka ensin esikäsittelee datan (valmistelu + leivonta kulissien takana), sovittaa mallin esikäsiteltyyn dataan ja mahdollistaa myös mahdolliset jälkikäsittelytoimet.\n", "\n", "Tidymodels-paketissa tämä kätevä objekti kutsutaan [`workflow`](https://workflows.tidymodels.org/), ja se pitää kätevästi sisällään mallinnuskomponenttisi.\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": [ "Kun työnkulku on *määritelty*, mallin voi `kouluttaa` käyttämällä [`fit()`](https://tidymodels.github.io/parsnip/reference/fit.html)-funktiota. Työnkulku arvioi reseptin ja esikäsittelee datan ennen koulutusta, joten meidän ei tarvitse tehdä sitä manuaalisesti käyttämällä prep- ja bake-funktioita.\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": [ "Mallin tulostus näyttää koulutuksen aikana opitut kertoimet.\n", "\n", "Nyt kun olemme kouluttaneet mallin käyttämällä harjoitusaineistoa, voimme tehdä ennusteita testiaineistosta käyttäen [parsnip::predict()](https://parsnip.tidymodels.org/reference/predict.model_fit.html). Aloitetaan käyttämällä mallia ennustamaan testijoukon luokkia ja kunkin luokan todennäköisyyksiä. Kun todennäköisyys on yli 0.5, ennustettu luokka on `WHITE`, muuten `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": [ "Todella hienoa! Tämä antaa lisää näkemyksiä siitä, miten logistinen regressio toimii.\n", "\n", "### Parempi ymmärrys sekaannusmatriisin avulla\n", "\n", "Jokaisen ennusteen vertaaminen vastaavaan \"todelliseen\" arvoon ei ole kovin tehokas tapa arvioida, kuinka hyvin malli ennustaa. Onneksi Tidymodels tarjoaa muutamia lisätyökaluja: [`yardstick`](https://yardstick.tidymodels.org/) - paketti, jota käytetään mallien tehokkuuden mittaamiseen suorituskykymittareiden avulla.\n", "\n", "Yksi suorituskykymittari, joka liittyy luokittelutehtäviin, on [`sekaannusmatriisi`](https://wikipedia.org/wiki/Confusion_matrix). Sekaannusmatriisi kuvaa, kuinka hyvin luokittelumalli toimii. Sekaannusmatriisi laskee, kuinka monta esimerkkiä kustakin luokasta malli luokitteli oikein. Meidän tapauksessamme se näyttää, kuinka monta oranssia kurpitsaa luokiteltiin oransseiksi ja kuinka monta valkoista kurpitsaa luokiteltiin valkoisiksi; sekaannusmatriisi näyttää myös, kuinka monta luokiteltiin **väärin** kategorioihin.\n", "\n", "[**`conf_mat()`**](https://tidymodels.github.io/yardstick/reference/conf_mat.html)-funktio yardstick-paketista laskee tämän havaittujen ja ennustettujen luokkien ristiintaulukoinnin.\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": [ "Tulkitkaamme sekaannusmatriisia. Mallimme tehtävänä on luokitella kurpitsat kahteen binaariseen kategoriaan: kategoriaan `valkoinen` ja kategoriaan `ei-valkoinen`.\n", "\n", "- Jos mallisi ennustaa kurpitsan olevan valkoinen ja se kuuluu todellisuudessa kategoriaan 'valkoinen', kutsumme sitä `tosipositiviseksi`, joka näkyy vasemmassa yläkulmassa.\n", "\n", "- Jos mallisi ennustaa kurpitsan olevan ei-valkoinen ja se kuuluu todellisuudessa kategoriaan 'valkoinen', kutsumme sitä `vääräksi negatiiviseksi`, joka näkyy vasemmassa alakulmassa.\n", "\n", "- Jos mallisi ennustaa kurpitsan olevan valkoinen ja se kuuluu todellisuudessa kategoriaan 'ei-valkoinen', kutsumme sitä `vääräksi positiviseksi`, joka näkyy oikeassa yläkulmassa.\n", "\n", "- Jos mallisi ennustaa kurpitsan olevan ei-valkoinen ja se kuuluu todellisuudessa kategoriaan 'ei-valkoinen', kutsumme sitä `tosinegatiiviseksi`, joka näkyy oikeassa alakulmassa.\n", "\n", "| Totuus |\n", "|:-----:|\n", "\n", "\n", "| | | |\n", "|---------------|--------|-------|\n", "| **Ennustettu** | VALKOINEN | ORANSSI |\n", "| VALKOINEN | TP | FP |\n", "| ORANSSI | FN | TN |\n", "\n", "Kuten ehkä arvasit, on suotavaa, että tosi-positiivisten ja tosi-negatiivisten määrä on suuri ja väärien positiivisten ja väärien negatiivisten määrä pieni, mikä tarkoittaa, että malli suoriutuu paremmin.\n", "\n", "Sekaannusmatriisi on hyödyllinen, koska sen avulla voidaan laskea muita mittareita, jotka auttavat arvioimaan luokittelumallin suorituskykyä paremmin. Käydään läpi joitakin niistä:\n", "\n", "🎓 Tarkkuus (Precision): `TP/(TP + FP)` määritellään ennustettujen positiivisten osuutena, jotka ovat oikeasti positiivisia. Kutsutaan myös nimellä [positiivinen ennustearvo](https://en.wikipedia.org/wiki/Positive_predictive_value \"Positive predictive value\").\n", "\n", "🎓 Herkkyys (Recall): `TP/(TP + FN)` määritellään positiivisten tulosten osuutena todellisuudessa positiivisista näytteistä. Tunnetaan myös nimellä `sensitiivisyys`.\n", "\n", "🎓 Spesifisyys: `TN/(TN + FP)` määritellään negatiivisten tulosten osuutena todellisuudessa negatiivisista näytteistä.\n", "\n", "🎓 Tarkkuus (Accuracy): `TP + TN/(TP + TN + FP + FN)` Oikein ennustettujen näytteiden prosenttiosuus kaikista näytteistä.\n", "\n", "🎓 F-mitta: Tarkkuuden ja herkkyyden painotettu keskiarvo, jossa paras arvo on 1 ja huonoin 0.\n", "\n", "Lasketaan nämä mittarit!\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": [ "## Visualisoi tämän mallin ROC-käyrä\n", "\n", "Tehdään vielä yksi visualisointi, jotta voimme tarkastella niin kutsuttua [`ROC-käyrää`](https://en.wikipedia.org/wiki/Receiver_operating_characteristic):\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-käyrät ovat usein käytössä, kun halutaan tarkastella luokittelijan tuloksia todellisten ja väärien positiivisten osalta. ROC-käyrissä Y-akselilla on yleensä `True Positive Rate`/herkkyys ja X-akselilla `False Positive Rate`/1-spesifisyys. Käyrän jyrkkyys ja etäisyys keskilinjasta ovat tärkeitä: halutaan käyrä, joka nopeasti nousee ylös ja ylittää linjan. Meidän tapauksessamme alussa on vääriä positiivisia, mutta sen jälkeen käyrä nousee ja ylittää linjan asianmukaisesti.\n", "\n", "Lopuksi käytetään `yardstick::roc_auc()`-funktiota laskemaan todellinen käyrän alle jäävä pinta-ala (Area Under the Curve). Yksi tapa tulkita AUC-arvoa on pitää sitä todennäköisyytenä, että malli sijoittaa satunnaisen positiivisen esimerkin korkeammalle kuin satunnaisen negatiivisen esimerkin.\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": [ "Tulos on noin `0.975`. Koska AUC vaihtelee välillä 0–1, haluat korkean pistemäärän, sillä malli, joka on 100 % oikeassa ennusteissaan, saa AUC-arvon 1; tässä tapauksessa malli on *melko hyvä*.\n", "\n", "Tulevissa luokittelua käsittelevissä oppitunneissa opit, kuinka parantaa mallisi tuloksia (kuten käsitellä epätasapainoista dataa tässä tapauksessa).\n", "\n", "## 🚀Haaste\n", "\n", "Logistiseen regressioon liittyy paljon enemmän kuin tässä käsiteltiin! Paras tapa oppia on kokeilla. Etsi datasetti, joka sopii tämän tyyppiseen analyysiin, ja rakenna malli sen avulla. Mitä opit? vinkki: kokeile [Kagglea](https://www.kaggle.com/search?q=logistic+regression+datasets) löytääksesi mielenkiintoisia datasettejä.\n", "\n", "## Kertaus & Itseopiskelu\n", "\n", "Lue muutama ensimmäinen sivu [tästä Stanfordin artikkelista](https://web.stanford.edu/~jurafsky/slp3/5.pdf), jossa käsitellään logistisen regression käytännön sovelluksia. Mieti tehtäviä, jotka sopivat paremmin joko yhdelle tai toiselle regressiotyypille, joita olemme tähän mennessä opiskelleet. Mikä toimisi parhaiten?\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n---\n\n**Vastuuvapauslauseke**: \nTämä asiakirja on käännetty käyttämällä tekoälypohjaista käännöspalvelua [Co-op Translator](https://github.com/Azure/co-op-translator). Vaikka pyrimme tarkkuuteen, huomioithan, että automaattiset käännökset voivat sisältää virheitä tai epätarkkuuksia. Alkuperäistä asiakirjaa sen alkuperäisellä kielellä tulee pitää ensisijaisena lähteenä. Kriittisen tiedon osalta suositellaan ammattimaista ihmiskääntämistä. Emme ole vastuussa tämän käännöksen käytöstä aiheutuvista väärinkäsityksistä tai virhetulkinnoista.\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-04T06:52:22+00:00", "source_file": "2-Regression/4-Logistic/solution/R/lesson_4-R.ipynb", "language_code": "fi" } }, "nbformat": 4, "nbformat_minor": 1 }