You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ML-For-Beginners/translations/pl/2-Regression/4-Logistic/solution/R/lesson_4-R.ipynb

685 lines
31 KiB

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Zbuduj model regresji logistycznej - Lekcja 4\n",
"\n",
"![Infografika: regresja logistyczna vs. liniowa](../../../../../../translated_images/linear-vs-logistic.ba180bf95e7ee66721ba10ebf2dac2666acbd64a88b003c83928712433a13c7d.pl.png)\n",
"\n",
"#### **[Quiz przed wykładem](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/15/)**\n",
"\n",
"#### Wprowadzenie\n",
"\n",
"W tej ostatniej lekcji dotyczącej regresji, jednej z podstawowych *klasycznych* technik uczenia maszynowego, przyjrzymy się regresji logistycznej. Technika ta pozwala odkrywać wzorce w celu przewidywania binarnych kategorii. Czy ten cukierek to czekolada, czy nie? Czy ta choroba jest zaraźliwa, czy nie? Czy ten klient wybierze ten produkt, czy nie?\n",
"\n",
"W tej lekcji dowiesz się:\n",
"\n",
"- Technik stosowanych w regresji logistycznej\n",
"\n",
"✅ Pogłęb swoją wiedzę na temat pracy z tego typu regresją w tym [module Learn](https://learn.microsoft.com/training/modules/introduction-classification-models/?WT.mc_id=academic-77952-leestott)\n",
"\n",
"## Wymagania wstępne\n",
"\n",
"Pracując z danymi dotyczącymi dyń, zdążyliśmy się już z nimi na tyle zapoznać, by zauważyć, że istnieje jedna binarna kategoria, z którą możemy pracować: `Color`.\n",
"\n",
"Zbudujmy model regresji logistycznej, aby przewidzieć, na podstawie pewnych zmiennych, *jaki kolor może mieć dana dynia* (pomarańczowy 🎃 lub biały 👻).\n",
"\n",
"> Dlaczego mówimy o klasyfikacji binarnej w lekcji dotyczącej regresji? Wyłącznie dla wygody językowej, ponieważ regresja logistyczna jest [tak naprawdę metodą klasyfikacji](https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression), choć opartą na modelu liniowym. Dowiedz się więcej o innych sposobach klasyfikacji danych w kolejnej grupie lekcji.\n",
"\n",
"Do tej lekcji będziemy potrzebować następujących pakietów:\n",
"\n",
"- `tidyverse`: [tidyverse](https://www.tidyverse.org/) to [zbiór pakietów R](https://www.tidyverse.org/packages), które sprawiają, że analiza danych staje się szybsza, łatwiejsza i bardziej przyjemna!\n",
"\n",
"- `tidymodels`: [tidymodels](https://www.tidymodels.org/) to [framework](https://www.tidymodels.org/packages/) składający się z pakietów do modelowania i uczenia maszynowego.\n",
"\n",
"- `janitor`: Pakiet [janitor](https://github.com/sfirke/janitor) oferuje proste narzędzia do przeglądania i czyszczenia \"brudnych\" danych.\n",
"\n",
"- `ggbeeswarm`: Pakiet [ggbeeswarm](https://github.com/eclarke/ggbeeswarm) umożliwia tworzenie wykresów w stylu \"beeswarm\" za pomocą ggplot2.\n",
"\n",
"Możesz je zainstalować za pomocą:\n",
"\n",
"`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"ggbeeswarm\"))`\n",
"\n",
"Alternatywnie, poniższy skrypt sprawdzi, czy masz zainstalowane wymagane pakiety, i zainstaluje je, jeśli ich brakuje.\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": [
"## **Zdefiniuj pytanie**\n",
"\n",
"Na potrzeby naszego zadania wyrazimy to jako binarną kategorię: 'Biały' lub 'Nie Biały'. W naszym zbiorze danych znajduje się również kategoria 'paski', ale jest ona reprezentowana przez niewiele przypadków, więc jej nie użyjemy. Znika ona zresztą, gdy usuniemy wartości null ze zbioru danych.\n",
"\n",
"> 🎃 Ciekawostka: czasami nazywamy białe dynie 'dyniami-duchami'. Nie są one łatwe do rzeźbienia, więc nie są tak popularne jak pomarańczowe, ale wyglądają naprawdę fajnie! Możemy więc również sformułować nasze pytanie jako: 'Duch' czy 'Nie Duch'. 👻\n",
"\n",
"## **O regresji logistycznej**\n",
"\n",
"Regresja logistyczna różni się od regresji liniowej, którą poznaliście wcześniej, w kilku istotnych aspektach.\n",
"\n",
"#### **Klasyfikacja binarna**\n",
"\n",
"Regresja logistyczna nie oferuje tych samych funkcji co regresja liniowa. Pierwsza z nich daje możliwość przewidywania `binarnych kategorii` (\"pomarańczowy lub nie pomarańczowy\"), podczas gdy druga pozwala przewidywać `wartości ciągłe`, na przykład na podstawie pochodzenia dyni i czasu zbioru, *o ile wzrośnie jej cena*.\n",
"\n",
"![Infografika autorstwa Dasani Madipalli](../../../../../../translated_images/pumpkin-classifier.562771f104ad5436b87d1c67bca02a42a17841133556559325c0a0e348e5b774.pl.png)\n",
"\n",
"### Inne klasyfikacje\n",
"\n",
"Istnieją inne rodzaje regresji logistycznej, w tym wielomianowa i porządkowa:\n",
"\n",
"- **Wielomianowa**, która obejmuje więcej niż jedną kategorię - \"Pomarańczowy, Biały i Paski\".\n",
"\n",
"- **Porządkowa**, która obejmuje uporządkowane kategorie, przydatne, jeśli chcemy uporządkować nasze wyniki logicznie, na przykład dynie według skończonej liczby rozmiarów (mini, mały, średni, duży, XL, XXL).\n",
"\n",
"![Regresja wielomianowa vs porządkowa](../../../../../../translated_images/multinomial-vs-ordinal.36701b4850e37d86c9dd49f7bef93a2f94dbdb8fe03443eb68f0542f97f28f29.pl.png)\n",
"\n",
"#### **Zmienne NIE muszą być skorelowane**\n",
"\n",
"Pamiętacie, jak regresja liniowa działała lepiej przy bardziej skorelowanych zmiennych? Regresja logistyczna jest odwrotna - zmienne nie muszą być ze sobą powiązane. To dobrze pasuje do naszych danych, które mają raczej słabe korelacje.\n",
"\n",
"#### **Potrzebujesz dużo czystych danych**\n",
"\n",
"Regresja logistyczna daje bardziej dokładne wyniki, jeśli użyjesz większej ilości danych; nasz mały zbiór danych nie jest optymalny do tego zadania, więc miej to na uwadze.\n",
"\n",
"✅ Zastanów się nad rodzajami danych, które dobrze nadają się do regresji logistycznej.\n",
"\n",
"## Ćwiczenie - uporządkuj dane\n",
"\n",
"Najpierw trochę oczyść dane, usuwając wartości null i wybierając tylko niektóre kolumny:\n",
"\n",
"1. Dodaj następujący kod:\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": [
"Możesz zawsze rzucić okiem na swój nowy dataframe, używając funkcji [*glimpse()*](https://pillar.r-lib.org/reference/glimpse.html) w sposób pokazany poniżej:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"vscode": {
"languageId": "r"
}
},
"outputs": [],
"source": [
"pumpkins_select %>% \n",
" glimpse()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Potwierdźmy, że faktycznie będziemy rozwiązywać problem klasyfikacji binarnej:\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": [
"### Wizualizacja - wykres kategoryczny\n",
"Do tej pory ponownie załadowałeś dane o dyniach i oczyściłeś je, aby zachować zestaw danych zawierający kilka zmiennych, w tym Kolor. Zobaczmy, jak można zwizualizować ten dataframe w notebooku za pomocą biblioteki ggplot.\n",
"\n",
"Biblioteka ggplot oferuje ciekawe sposoby wizualizacji danych. Na przykład możesz porównać rozkłady danych dla każdej Odmiany i Koloru na wykresie kategorycznym.\n",
"\n",
"1. Stwórz taki wykres, używając funkcji geombar, korzystając z naszych danych o dyniach i określając mapowanie kolorów dla każdej kategorii dyni (pomarańczowej lub białej):\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": [
"Obserwując dane, można zauważyć, jak dane dotyczące Koloru odnoszą się do Odmiany.\n",
"\n",
"✅ Patrząc na ten wykres kategorii, jakie ciekawe analizy można sobie wyobrazić?\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Przetwarzanie danych: kodowanie cech\n",
"\n",
"Nasz zbiór danych o dyniach zawiera wartości tekstowe we wszystkich kolumnach. Praca z danymi kategorycznymi jest intuicyjna dla ludzi, ale nie dla maszyn. Algorytmy uczenia maszynowego działają dobrze z liczbami. Dlatego kodowanie jest bardzo ważnym krokiem w fazie przetwarzania danych, ponieważ pozwala nam przekształcić dane kategoryczne w dane numeryczne, nie tracąc przy tym żadnych informacji. Dobre kodowanie prowadzi do stworzenia dobrego modelu.\n",
"\n",
"W przypadku kodowania cech istnieją dwa główne typy kodowania:\n",
"\n",
"1. Kodowanie porządkowe (Ordinal encoder): dobrze nadaje się do zmiennych porządkowych, czyli zmiennych kategorycznych, w których dane mają logiczny porządek, jak kolumna `item_size` w naszym zbiorze danych. Tworzy mapowanie, w którym każda kategoria jest reprezentowana przez liczbę, odpowiadającą kolejności kategorii w kolumnie.\n",
"\n",
"2. Kodowanie kategoryczne (Categorical encoder): dobrze nadaje się do zmiennych nominalnych, czyli zmiennych kategorycznych, w których dane nie mają logicznego porządku, jak wszystkie cechy inne niż `item_size` w naszym zbiorze danych. Jest to kodowanie typu one-hot, co oznacza, że każda kategoria jest reprezentowana przez binarną kolumnę: zakodowana zmienna jest równa 1, jeśli dynia należy do danej odmiany, a 0 w przeciwnym razie.\n",
"\n",
"Tidymodels oferuje jeszcze jeden przydatny pakiet: [recipes](https://recipes.tidymodels.org/) - pakiet do przetwarzania danych. Zdefiniujemy `recipe`, który określa, że wszystkie kolumny predyktorów powinny być zakodowane jako zestaw liczb całkowitych, `prep`, aby oszacować wymagane ilości i statystyki potrzebne do operacji, a na końcu `bake`, aby zastosować obliczenia do nowych danych.\n",
"\n",
"> Zazwyczaj recipes jest używany jako narzędzie do wstępnego przetwarzania danych w modelowaniu, gdzie definiuje, jakie kroki powinny być zastosowane do zbioru danych, aby przygotować go do modelowania. W takim przypadku **zdecydowanie zaleca się** użycie `workflow()` zamiast ręcznego szacowania recepty za pomocą prep i bake. Zobaczymy to wszystko za chwilę.\n",
">\n",
"> Jednak na razie używamy recipes + prep + bake, aby określić, jakie kroki powinny być zastosowane do zbioru danych, aby przygotować go do analizy danych, a następnie wyodrębnić przetworzone dane z zastosowanymi krokami.\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": [
"✅ Jakie są zalety używania kodera porządkowego dla kolumny Item Size?\n",
"\n",
"### Analiza relacji między zmiennymi\n",
"\n",
"Teraz, gdy wstępnie przetworzyliśmy nasze dane, możemy przeanalizować relacje między cechami a etykietą, aby zrozumieć, jak dobrze model będzie w stanie przewidzieć etykietę na podstawie cech. Najlepszym sposobem na przeprowadzenie takiej analizy jest wizualizacja danych. \n",
"Ponownie skorzystamy z funkcji ggplot geom_boxplot_, aby zobrazować relacje między Item Size, Variety i Color na wykresie kategorycznym. Aby lepiej przedstawić dane, użyjemy zakodowanej kolumny Item Size oraz niezakodowanej kolumny 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": [
"#### Użyj wykresu rojowego\n",
"\n",
"Ponieważ Color jest kategorią binarną (Biały lub Nie), wymaga '[specjalnego podejścia](https://github.com/rstudio/cheatsheets/blob/main/data-visualization.pdf) do wizualizacji'.\n",
"\n",
"Spróbuj użyć `wykresu rojowego`, aby pokazać rozkład koloru w odniesieniu do item_size.\n",
"\n",
"Skorzystamy z [pakietu ggbeeswarm](https://github.com/eclarke/ggbeeswarm), który oferuje metody tworzenia wykresów w stylu rojowym za pomocą ggplot2. Wykresy rojowe to sposób na przedstawienie punktów, które normalnie by się na siebie nakładały, tak aby znajdowały się obok siebie.\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": [
"Teraz, gdy mamy już pojęcie o związku między binarnymi kategoriami koloru a większą grupą rozmiarów, przejdźmy do regresji logistycznej, aby określić prawdopodobny kolor danego dyni.\n",
"\n",
"## Zbuduj swój model\n",
"\n",
"Wybierz zmienne, które chcesz wykorzystać w modelu klasyfikacji, i podziel dane na zestawy treningowe i testowe. [rsample](https://rsample.tidymodels.org/), pakiet w Tidymodels, zapewnia infrastrukturę do efektywnego podziału danych i ich próbkowania:\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": [
"🙌 Jesteśmy teraz gotowi, aby wytrenować model, dopasowując cechy treningowe do etykiety treningowej (kolor).\n",
"\n",
"Zaczniemy od stworzenia przepisu, który określa kroki przetwarzania wstępnego, jakie należy wykonać na naszych danych, aby przygotować je do modelowania, np. kodowanie zmiennych kategorycznych na zestaw liczb całkowitych. Podobnie jak w przypadku `baked_pumpkins`, tworzymy `pumpkins_recipe`, ale nie używamy `prep` ani `bake`, ponieważ zostanie to włączone do przepływu pracy, co zobaczysz za kilka kroków.\n",
"\n",
"Istnieje wiele sposobów na określenie modelu regresji logistycznej w Tidymodels. Zobacz `?logistic_reg()`. Na razie określimy model regresji logistycznej za pomocą domyślnego silnika `stats::glm()`.\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": [
"Teraz, gdy mamy przepis i specyfikację modelu, musimy znaleźć sposób na połączenie ich w jeden obiekt, który najpierw przetworzy dane (prep+bake w tle), dopasuje model do przetworzonych danych, a także umożliwi potencjalne działania związane z postprocessingiem.\n",
"\n",
"W Tidymodels ten wygodny obiekt nazywa się [`workflow`](https://workflows.tidymodels.org/) i wygodnie przechowuje komponenty modelowania.\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": [
"Po określeniu *przepływu pracy*, model można `wytrenować` za pomocą funkcji [`fit()`](https://tidymodels.github.io/parsnip/reference/fit.html). Przepływ pracy oszacuje recepturę i wstępnie przetworzy dane przed treningiem, dzięki czemu nie będziemy musieli robić tego ręcznie za pomocą funkcji prep i 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": [
"Model pokazuje współczynniki wyuczone podczas treningu.\n",
"\n",
"Teraz, gdy przeszkoliliśmy model na danych treningowych, możemy dokonać predykcji na danych testowych, używając [parsnip::predict()](https://parsnip.tidymodels.org/reference/predict.model_fit.html). Zacznijmy od użycia modelu do przewidywania etykiet dla naszego zestawu testowego oraz prawdopodobieństw dla każdej etykiety. Jeśli prawdopodobieństwo wynosi więcej niż 0.5, przewidywana klasa to `WHITE`, w przeciwnym razie `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": [
"Bardzo dobrze! To dostarcza dodatkowych informacji na temat działania regresji logistycznej.\n",
"\n",
"### Lepsze zrozumienie dzięki macierzy pomyłek\n",
"\n",
"Porównywanie każdej prognozy z odpowiadającą jej rzeczywistą wartością (\"ground truth\") nie jest zbyt efektywnym sposobem oceny, jak dobrze model przewiduje. Na szczęście Tidymodels ma kilka dodatkowych sztuczek: [`yardstick`](https://yardstick.tidymodels.org/) - pakiet służący do oceny skuteczności modeli za pomocą metryk wydajności.\n",
"\n",
"Jedną z metryk wydajności związanych z problemami klasyfikacji jest [`macierz pomyłek`](https://wikipedia.org/wiki/Confusion_matrix). Macierz pomyłek opisuje, jak dobrze działa model klasyfikacyjny. Macierz pomyłek zestawia, ile przykładów w każdej klasie zostało poprawnie sklasyfikowanych przez model. W naszym przypadku pokaże, ile pomarańczowych dyń zostało sklasyfikowanych jako pomarańczowe, a ile białych dyń jako białe; macierz pomyłek pokaże również, ile zostało sklasyfikowanych do **niewłaściwych** kategorii.\n",
"\n",
"Funkcja [**`conf_mat()`**](https://tidymodels.github.io/yardstick/reference/conf_mat.html) z pakietu yardstick oblicza tę tabelę krzyżową zaobserwowanych i przewidzianych klas.\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": [
"Przeanalizujmy macierz pomyłek. Nasz model ma za zadanie klasyfikować dynie na dwie kategorie binarne: kategorię `białe` i kategorię `nie-białe`.\n",
"\n",
"- Jeśli model przewiduje, że dynia jest biała i w rzeczywistości należy do kategorii 'białe', nazywamy to `prawdziwym pozytywem` (true positive), co jest pokazane w lewym górnym rogu.\n",
"\n",
"- Jeśli model przewiduje, że dynia nie jest biała, a w rzeczywistości należy do kategorii 'białe', nazywamy to `fałszywym negatywem` (false negative), co jest pokazane w lewym dolnym rogu.\n",
"\n",
"- Jeśli model przewiduje, że dynia jest biała, a w rzeczywistości należy do kategorii 'nie-białe', nazywamy to `fałszywym pozytywem` (false positive), co jest pokazane w prawym górnym rogu.\n",
"\n",
"- Jeśli model przewiduje, że dynia nie jest biała, a w rzeczywistości należy do kategorii 'nie-białe', nazywamy to `prawdziwym negatywem` (true negative), co jest pokazane w prawym dolnym rogu.\n",
"\n",
"| Rzeczywistość |\n",
"|:-------------:|\n",
"\n",
"| | | |\n",
"|---------------|--------|-------|\n",
"| **Przewidywane** | BIAŁE | POMARAŃCZOWE |\n",
"| BIAŁE | TP | FP |\n",
"| POMARAŃCZOWE | FN | TN |\n",
"\n",
"Jak można się domyślić, preferowane jest, aby liczba prawdziwych pozytywów i prawdziwych negatywów była jak największa, a liczba fałszywych pozytywów i fałszywych negatywów jak najmniejsza, co oznacza, że model działa lepiej.\n",
"\n",
"Macierz pomyłek jest przydatna, ponieważ pozwala obliczyć inne wskaźniki, które pomagają lepiej ocenić wydajność modelu klasyfikacyjnego. Przyjrzyjmy się niektórym z nich:\n",
"\n",
"🎓 Precyzja: `TP/(TP + FP)` zdefiniowana jako proporcja przewidywanych pozytywów, które faktycznie są pozytywne. Nazywana również [wartością predykcyjną dodatnią](https://en.wikipedia.org/wiki/Positive_predictive_value \"Positive predictive value\").\n",
"\n",
"🎓 Czułość: `TP/(TP + FN)` zdefiniowana jako proporcja wyników pozytywnych w stosunku do liczby próbek, które faktycznie są pozytywne. Znana również jako `czułość` (sensitivity).\n",
"\n",
"🎓 Specyficzność: `TN/(TN + FP)` zdefiniowana jako proporcja wyników negatywnych w stosunku do liczby próbek, które faktycznie są negatywne.\n",
"\n",
"🎓 Dokładność: `TP + TN/(TP + TN + FP + FN)` Procent etykiet poprawnie przewidzianych dla próbki.\n",
"\n",
"🎓 Miara F: Średnia ważona precyzji i czułości, gdzie najlepszy wynik to 1, a najgorszy to 0.\n",
"\n",
"Policzmy te wskaźniki!\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": [
"## Zobrazuj krzywą ROC tego modelu\n",
"\n",
"Zróbmy jeszcze jedną wizualizację, aby zobaczyć tzw. [`krzywą ROC`](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": [
"Krzywe ROC są często używane do oceny wyników klasyfikatora w kontekście jego trafnych i fałszywych trafień. Krzywe ROC zazwyczaj przedstawiają `True Positive Rate`/Czułość na osi Y oraz `False Positive Rate`/1-Specyficzność na osi X. Dlatego ważne są stromość krzywej oraz przestrzeń między linią środkową a krzywą: zależy nam na krzywej, która szybko wznosi się i oddala od linii. W naszym przypadku na początku pojawiają się fałszywe trafienia, a następnie linia poprawnie się wznosi i oddala.\n",
"\n",
"Na koniec użyjmy `yardstick::roc_auc()`, aby obliczyć rzeczywisty obszar pod krzywą (Area Under the Curve). Jednym ze sposobów interpretacji AUC jest traktowanie go jako prawdopodobieństwa, że model oceni losowy pozytywny przykład wyżej niż losowy negatywny przykład.\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": [
"Wynik wynosi około `0.975`. Biorąc pod uwagę, że AUC mieści się w przedziale od 0 do 1, chcesz uzyskać wysoki wynik, ponieważ model, który jest w 100% poprawny w swoich przewidywaniach, będzie miał AUC równy 1; w tym przypadku model jest *całkiem dobry*.\n",
"\n",
"W przyszłych lekcjach dotyczących klasyfikacji dowiesz się, jak poprawić wyniki swojego modelu (na przykład, jak radzić sobie z niezrównoważonymi danymi w tym przypadku).\n",
"\n",
"## 🚀Wyzwanie\n",
"\n",
"Logistyczna regresja kryje w sobie wiele ciekawych aspektów! Najlepszym sposobem na naukę jest eksperymentowanie. Znajdź zbiór danych, który nadaje się do tego typu analizy, i zbuduj na jego podstawie model. Czego się nauczysz? wskazówka: sprawdź [Kaggle](https://www.kaggle.com/search?q=logistic+regression+datasets) w poszukiwaniu interesujących zbiorów danych.\n",
"\n",
"## Przegląd i samodzielna nauka\n",
"\n",
"Przeczytaj pierwsze kilka stron [tego artykułu ze Stanfordu](https://web.stanford.edu/~jurafsky/slp3/5.pdf) na temat praktycznych zastosowań regresji logistycznej. Zastanów się nad zadaniami, które lepiej nadają się do jednego lub drugiego typu regresji, które omawialiśmy do tej pory. Co sprawdziłoby się najlepiej?\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Zastrzeżenie**: \nTen dokument został przetłumaczony za pomocą usługi tłumaczenia AI [Co-op Translator](https://github.com/Azure/co-op-translator). Chociaż dokładamy wszelkich starań, aby zapewnić poprawność tłumaczenia, prosimy pamiętać, że automatyczne tłumaczenia mogą zawierać błędy lub nieścisłości. Oryginalny dokument w jego języku źródłowym powinien być uznawany za wiarygodne źródło. W przypadku informacji o kluczowym znaczeniu zaleca się skorzystanie z profesjonalnego tłumaczenia przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z użycia tego tłumaczenia.\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-03T19:34:02+00:00",
"source_file": "2-Regression/4-Logistic/solution/R/lesson_4-R.ipynb",
"language_code": "pl"
}
},
"nbformat": 4,
"nbformat_minor": 1
}