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.
686 lines
32 KiB
686 lines
32 KiB
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 로지스틱 회귀 모델 구축 - 레슨 4\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"#### **[강의 전 퀴즈](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/15/)**\n",
|
|
"\n",
|
|
"#### 소개\n",
|
|
"\n",
|
|
"이번 회귀에 대한 마지막 레슨에서는 기본적인 *클래식* 머신러닝 기법 중 하나인 로지스틱 회귀(Logistic Regression)를 살펴보겠습니다. 이 기법은 이진 범주를 예측하기 위해 패턴을 발견하는 데 사용됩니다. 이 사탕이 초콜릿인가 아닌가? 이 질병이 전염성인가 아닌가? 이 고객이 이 제품을 선택할 것인가 아닌가?\n",
|
|
"\n",
|
|
"이 레슨에서 배우게 될 내용:\n",
|
|
"\n",
|
|
"- 로지스틱 회귀 기법\n",
|
|
"\n",
|
|
"✅ 이 유형의 회귀를 다루는 방법에 대한 이해를 [Learn 모듈](https://learn.microsoft.com/training/modules/introduction-classification-models/?WT.mc_id=academic-77952-leestott)에서 심화하세요.\n",
|
|
"\n",
|
|
"## 사전 요구 사항\n",
|
|
"\n",
|
|
"호박 데이터를 다루면서, 이제 우리는 이 데이터에서 작업할 수 있는 이진 범주가 하나 있다는 것을 충분히 알게 되었습니다: `Color`.\n",
|
|
"\n",
|
|
"이제 로지스틱 회귀 모델을 구축하여 몇 가지 변수를 기반으로 *주어진 호박의 색상이 무엇일 가능성이 높은지* 예측해 봅시다 (주황색 🎃 또는 흰색 👻).\n",
|
|
"\n",
|
|
"> 왜 회귀에 대한 레슨에서 이진 분류를 이야기할까요? 단지 언어적 편의를 위해서입니다. 로지스틱 회귀는 [사실 분류 방법](https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression)이며, 선형 기반 기법이긴 하지만요. 데이터 분류의 다른 방법에 대해서는 다음 레슨 그룹에서 배울 수 있습니다.\n",
|
|
"\n",
|
|
"이 레슨에서는 다음 패키지가 필요합니다:\n",
|
|
"\n",
|
|
"- `tidyverse`: [tidyverse](https://www.tidyverse.org/)는 데이터 과학을 더 빠르고, 쉽고, 재미있게 만들어주는 [R 패키지 모음](https://www.tidyverse.org/packages)입니다.\n",
|
|
"\n",
|
|
"- `tidymodels`: [tidymodels](https://www.tidymodels.org/) 프레임워크는 모델링과 머신러닝을 위한 [패키지 모음](https://www.tidymodels.org/packages/)입니다.\n",
|
|
"\n",
|
|
"- `janitor`: [janitor 패키지](https://github.com/sfirke/janitor)는 더러운 데이터를 검사하고 정리하는 간단한 도구를 제공합니다.\n",
|
|
"\n",
|
|
"- `ggbeeswarm`: [ggbeeswarm 패키지](https://github.com/eclarke/ggbeeswarm)는 ggplot2를 사용하여 벌떼 스타일의 플롯을 생성하는 방법을 제공합니다.\n",
|
|
"\n",
|
|
"다음 명령어로 패키지를 설치할 수 있습니다:\n",
|
|
"\n",
|
|
"`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"ggbeeswarm\"))`\n",
|
|
"\n",
|
|
"또는 아래 스크립트는 이 모듈을 완료하는 데 필요한 패키지가 있는지 확인하고, 없는 경우 설치해 줍니다.\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": [
|
|
"## **질문 정의하기**\n",
|
|
"\n",
|
|
"우리의 목적을 위해, 질문을 이진 형태로 표현하겠습니다: 'White' 또는 'Not White'. 데이터셋에는 'striped'라는 카테고리도 있지만, 해당 사례가 거의 없으므로 사용하지 않을 것입니다. 어차피 데이터셋에서 null 값을 제거하면 이 카테고리는 사라집니다.\n",
|
|
"\n",
|
|
"> 🎃 재미있는 사실: 우리는 때때로 흰색 호박을 '유령' 호박이라고 부릅니다. 흰색 호박은 조각하기가 쉽지 않아서 주황색 호박만큼 인기가 많지는 않지만, 멋지게 생겼습니다! 따라서 질문을 이렇게 바꿔볼 수도 있습니다: 'Ghost' 또는 'Not Ghost'. 👻\n",
|
|
"\n",
|
|
"## **로지스틱 회귀에 대하여**\n",
|
|
"\n",
|
|
"로지스틱 회귀는 이전에 배운 선형 회귀와 몇 가지 중요한 점에서 다릅니다.\n",
|
|
"\n",
|
|
"#### **이진 분류**\n",
|
|
"\n",
|
|
"로지스틱 회귀는 선형 회귀와 동일한 기능을 제공하지 않습니다. 로지스틱 회귀는 `이진 카테고리` (\"주황색 또는 주황색이 아님\")에 대한 예측을 제공하는 반면, 선형 회귀는 예를 들어 호박의 원산지와 수확 시기를 기반으로 *가격이 얼마나 오를지*와 같은 `연속적인 값`을 예측할 수 있습니다.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"### 기타 분류\n",
|
|
"\n",
|
|
"로지스틱 회귀에는 다항 및 순서형을 포함한 다른 유형도 있습니다:\n",
|
|
"\n",
|
|
"- **다항 회귀**: 여러 카테고리가 포함된 경우 - \"주황색, 흰색, 줄무늬\".\n",
|
|
"\n",
|
|
"- **순서형 회귀**: 순서가 있는 카테고리를 포함하며, 결과를 논리적으로 정렬해야 할 때 유용합니다. 예를 들어, 호박을 크기별로 정렬하는 경우 (미니, 소, 중, 대, 특대, 초대형).\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"#### **변수는 반드시 상관관계를 가질 필요가 없습니다**\n",
|
|
"\n",
|
|
"선형 회귀가 변수 간 상관관계가 높을수록 더 잘 작동했던 것을 기억하시나요? 로지스틱 회귀는 그 반대입니다 - 변수들이 반드시 정렬될 필요가 없습니다. 이는 상관관계가 약한 이 데이터에 적합합니다.\n",
|
|
"\n",
|
|
"#### **많은 양의 깨끗한 데이터가 필요합니다**\n",
|
|
"\n",
|
|
"로지스틱 회귀는 더 많은 데이터를 사용할수록 더 정확한 결과를 제공합니다. 우리의 작은 데이터셋은 이 작업에 최적화되어 있지 않으니, 이 점을 염두에 두세요.\n",
|
|
"\n",
|
|
"✅ 로지스틱 회귀에 적합한 데이터 유형에 대해 생각해 보세요.\n",
|
|
"\n",
|
|
"## 연습 - 데이터 정리하기\n",
|
|
"\n",
|
|
"먼저 데이터를 약간 정리하고, null 값을 제거한 후 일부 열만 선택하세요:\n",
|
|
"\n",
|
|
"1. 다음 코드를 추가하세요:\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": [
|
|
"새 데이터프레임을 확인하려면 아래와 같이 [*glimpse()*](https://pillar.r-lib.org/reference/glimpse.html) 함수를 사용할 수 있습니다:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"pumpkins_select %>% \n",
|
|
" glimpse()\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"우리가 실제로 이진 분류 문제를 다루고 있는지 확인해 봅시다:\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": [
|
|
"### 시각화 - 범주형 플롯\n",
|
|
"지금까지 호박 데이터를 다시 불러오고, Color를 포함한 몇 가지 변수를 포함하는 데이터셋을 유지하도록 정리했습니다. 이제 ggplot 라이브러리를 사용하여 노트북에서 데이터프레임을 시각화해 봅시다.\n",
|
|
"\n",
|
|
"ggplot 라이브러리는 데이터를 시각화하는 멋진 방법들을 제공합니다. 예를 들어, 범주형 플롯에서 각 Variety와 Color에 대한 데이터 분포를 비교할 수 있습니다.\n",
|
|
"\n",
|
|
"1. geombar 함수를 사용하여 호박 데이터를 기반으로 하고, 각 호박 카테고리(주황색 또는 흰색)에 대한 색상 매핑을 지정하여 이러한 플롯을 생성하세요:\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": [
|
|
"데이터를 살펴보면 Color 데이터가 Variety와 어떻게 연관되어 있는지 알 수 있습니다.\n",
|
|
"\n",
|
|
"✅ 이 범주형 플롯을 통해 어떤 흥미로운 탐구를 상상할 수 있나요?\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### 데이터 전처리: 특성 인코딩\n",
|
|
"\n",
|
|
"우리의 호박 데이터셋은 모든 열에 문자열 값을 포함하고 있습니다. 범주형 데이터를 다루는 것은 사람에게는 직관적이지만, 기계에게는 그렇지 않습니다. 머신러닝 알고리즘은 숫자 데이터와 잘 작동합니다. 따라서 범주형 데이터를 숫자 데이터로 변환하는 인코딩은 데이터 전처리 단계에서 매우 중요한 과정입니다. 이 과정에서 정보를 잃지 않고 변환하는 것이 핵심입니다. 좋은 인코딩은 좋은 모델을 구축하는 데 기여합니다.\n",
|
|
"\n",
|
|
"특성 인코딩에는 두 가지 주요 유형의 인코더가 있습니다:\n",
|
|
"\n",
|
|
"1. **순서형 인코더(Ordinal encoder)**: 순서형 변수에 적합합니다. 순서형 변수는 데이터가 논리적인 순서를 따르는 범주형 변수입니다. 예를 들어, 데이터셋의 `item_size` 열이 이에 해당합니다. 순서형 인코더는 각 범주를 숫자로 매핑하며, 이 숫자는 해당 열에서 범주의 순서를 나타냅니다.\n",
|
|
"\n",
|
|
"2. **범주형 인코더(Categorical encoder)**: 명목형 변수에 적합합니다. 명목형 변수는 데이터가 논리적인 순서를 따르지 않는 범주형 변수입니다. 데이터셋에서 `item_size`를 제외한 모든 특성이 이에 해당합니다. 이 인코딩 방식은 원-핫 인코딩(one-hot encoding)으로, 각 범주가 이진 열로 표현됩니다. 즉, 특정 호박이 해당 품종에 속하면 인코딩된 변수는 1이 되고, 그렇지 않으면 0이 됩니다.\n",
|
|
"\n",
|
|
"Tidymodels는 또 다른 유용한 패키지를 제공합니다: [recipes](https://recipes.tidymodels.org/) - 데이터 전처리를 위한 패키지입니다. 우리는 모든 예측 열이 정수 집합으로 인코딩되도록 지정하는 `recipe`를 정의하고, 필요한 양과 통계를 추정하기 위해 `prep`을 실행한 후, 새로운 데이터에 계산을 적용하기 위해 `bake`를 실행할 것입니다.\n",
|
|
"\n",
|
|
"> 일반적으로 recipes는 모델링을 위한 전처리기로 사용되며, 데이터셋을 모델링에 적합하게 준비하기 위해 어떤 단계가 적용되어야 하는지를 정의합니다. 이 경우, `prep`과 `bake`를 수동으로 사용하는 대신 **`workflow()`를 사용하는 것이 강력히 권장**됩니다. 이 모든 과정을 곧 살펴보겠습니다.\n",
|
|
">\n",
|
|
"> 하지만 지금은 데이터를 분석할 준비를 하기 위해 recipes + prep + bake를 사용하여 데이터셋에 적용할 단계를 지정하고, 적용된 단계를 포함한 전처리된 데이터를 추출할 것입니다.\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": [
|
|
"✅ 항목 크기(Item Size) 열에 대해 순서형 인코더를 사용하는 장점은 무엇인가요?\n",
|
|
"\n",
|
|
"### 변수 간의 관계 분석\n",
|
|
"\n",
|
|
"이제 데이터를 전처리했으니, 특징(feature)과 라벨(label) 간의 관계를 분석하여 주어진 특징으로 모델이 라벨을 얼마나 잘 예측할 수 있을지에 대한 아이디어를 얻을 수 있습니다. 이러한 분석을 수행하는 가장 좋은 방법은 데이터를 시각화하는 것입니다. \n",
|
|
"우리는 다시 ggplot의 geom_boxplot_ 함수를 사용하여, 항목 크기(Item Size), 품종(Variety), 색상(Color) 간의 관계를 범주형 플롯으로 시각화할 것입니다. 데이터를 더 잘 시각화하기 위해 인코딩된 항목 크기(Item Size) 열과 인코딩되지 않은 품종(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": [
|
|
"#### 스웜 플롯 사용하기\n",
|
|
"\n",
|
|
"Color는 이진 카테고리(White 또는 Not)이므로 시각화를 위해 '[특화된 접근법](https://github.com/rstudio/cheatsheets/blob/main/data-visualization.pdf)'이 필요합니다.\n",
|
|
"\n",
|
|
"item_size에 따라 Color의 분포를 보여주기 위해 `스웜 플롯`을 사용해 보세요.\n",
|
|
"\n",
|
|
"우리는 [ggbeeswarm 패키지](https://github.com/eclarke/ggbeeswarm)를 사용할 것입니다. 이 패키지는 ggplot2를 사용하여 비스웜 스타일의 플롯을 생성하는 방법을 제공합니다. 비스웜 플롯은 일반적으로 겹칠 수 있는 점들을 서로 옆에 배치하여 시각화하는 방법입니다.\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": [
|
|
"이제 색상의 이진 범주와 더 큰 크기 그룹 간의 관계를 이해했으니, 로지스틱 회귀를 사용하여 특정 호박의 색상을 예측해 봅시다.\n",
|
|
"\n",
|
|
"## 모델 구축하기\n",
|
|
"\n",
|
|
"분류 모델에 사용할 변수를 선택하고 데이터를 학습용 세트와 테스트 세트로 나누세요. [rsample](https://rsample.tidymodels.org/)은 Tidymodels의 패키지로, 효율적인 데이터 분할과 재샘플링을 위한 인프라를 제공합니다:\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": [
|
|
"🙌 이제 훈련 특징을 훈련 레이블(색상)에 맞추어 모델을 학습시킬 준비가 되었습니다.\n",
|
|
"\n",
|
|
"먼저 데이터를 모델링에 적합하게 준비하기 위해 수행해야 할 전처리 단계를 지정하는 레시피를 만들어 보겠습니다. 예를 들어 범주형 변수를 정수 집합으로 인코딩하는 작업입니다. `baked_pumpkins`와 마찬가지로 `pumpkins_recipe`를 생성하지만, 몇 단계 후에 워크플로에 포함될 것이므로 `prep`과 `bake`는 수행하지 않습니다.\n",
|
|
"\n",
|
|
"Tidymodels에서 로지스틱 회귀 모델을 지정하는 방법은 여러 가지가 있습니다. `?logistic_reg()`를 참조하세요. 지금은 기본 `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": [
|
|
"이제 레시피와 모델 사양을 준비했으니, 이를 하나의 객체로 묶어 데이터를 먼저 전처리(백그라운드에서 prep+bake 수행), 전처리된 데이터에 모델을 학습시키고, 추가적인 후처리 작업도 가능하게 하는 방법을 찾아야 합니다.\n",
|
|
"\n",
|
|
"Tidymodels에서는 이러한 편리한 객체를 [`workflow`](https://workflows.tidymodels.org/)라고 하며, 모델링 구성 요소를 간편하게 관리할 수 있습니다.\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": [
|
|
"워크플로가 *지정*된 후, [`fit()`](https://tidymodels.github.io/parsnip/reference/fit.html) 함수를 사용하여 모델을 `훈련`시킬 수 있습니다. 워크플로는 레시피를 평가하고 훈련 전에 데이터를 전처리하므로, 우리가 prep과 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": [
|
|
"모델 학습 과정에서 학습된 계수를 출력합니다.\n",
|
|
"\n",
|
|
"이제 학습 데이터를 사용해 모델을 훈련했으니, [parsnip::predict()](https://parsnip.tidymodels.org/reference/predict.model_fit.html)를 사용하여 테스트 데이터에 대한 예측을 수행할 수 있습니다. 먼저, 모델을 사용해 테스트 세트의 레이블과 각 레이블에 대한 확률을 예측해 봅시다. 확률이 0.5를 초과하면 예측 클래스는 `WHITE`이고, 그렇지 않으면 `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": [
|
|
"정말 좋네요! 로지스틱 회귀가 어떻게 작동하는지에 대한 추가적인 통찰을 제공합니다.\n",
|
|
"\n",
|
|
"### 혼동 행렬을 통한 더 나은 이해\n",
|
|
"\n",
|
|
"각 예측값을 해당 \"실제값\"과 비교하는 것은 모델이 얼마나 잘 예측하고 있는지를 판단하는 데 있어 효율적인 방법이 아닙니다. 다행히도, Tidymodels에는 이를 보완할 몇 가지 유용한 도구가 있습니다. 그중 하나가 [`yardstick`](https://yardstick.tidymodels.org/)입니다. 이 패키지는 성능 지표를 사용하여 모델의 효과를 측정하는 데 사용됩니다.\n",
|
|
"\n",
|
|
"분류 문제와 관련된 성능 지표 중 하나는 [`혼동 행렬`](https://wikipedia.org/wiki/Confusion_matrix)입니다. 혼동 행렬은 분류 모델이 얼마나 잘 작동하는지를 설명합니다. 혼동 행렬은 각 클래스에서 모델이 올바르게 분류한 예제의 수를 표로 정리합니다. 우리의 경우, 혼동 행렬은 주황색 호박이 주황색으로 분류된 수와 흰색 호박이 흰색으로 분류된 수를 보여줍니다. 또한, **잘못된** 카테고리로 분류된 수 역시 보여줍니다.\n",
|
|
"\n",
|
|
"yardstick의 [**`conf_mat()`**](https://tidymodels.github.io/yardstick/reference/conf_mat.html) 함수는 관측된 클래스와 예측된 클래스 간의 교차 표를 계산합니다.\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": [
|
|
"혼동 행렬을 해석해 봅시다. 우리의 모델은 호박을 두 가지 이진 카테고리, `white`와 `not-white`로 분류하도록 요청받았습니다.\n",
|
|
"\n",
|
|
"- 모델이 호박을 white로 예측했을 때 실제로 'white' 카테고리에 속한다면 이를 `true positive`라고 하며, 이는 왼쪽 상단 숫자로 표시됩니다.\n",
|
|
"\n",
|
|
"- 모델이 호박을 not white로 예측했을 때 실제로 'white' 카테고리에 속한다면 이를 `false negative`라고 하며, 이는 왼쪽 하단 숫자로 표시됩니다.\n",
|
|
"\n",
|
|
"- 모델이 호박을 white로 예측했을 때 실제로 'not-white' 카테고리에 속한다면 이를 `false positive`라고 하며, 이는 오른쪽 상단 숫자로 표시됩니다.\n",
|
|
"\n",
|
|
"- 모델이 호박을 not white로 예측했을 때 실제로 'not-white' 카테고리에 속한다면 이를 `true negative`라고 하며, 이는 오른쪽 하단 숫자로 표시됩니다.\n",
|
|
"\n",
|
|
"| 실제값 |\n",
|
|
"|:-----:|\n",
|
|
"\n",
|
|
"\n",
|
|
"| | | |\n",
|
|
"|---------------|--------|-------|\n",
|
|
"| **예측값** | WHITE | ORANGE |\n",
|
|
"| WHITE | TP | FP |\n",
|
|
"| ORANGE | FN | TN |\n",
|
|
"\n",
|
|
"예상하셨겠지만, `true positive`와 `true negative`의 숫자가 많고, `false positive`와 `false negative`의 숫자가 적을수록 모델의 성능이 더 좋다는 것을 의미합니다.\n",
|
|
"\n",
|
|
"혼동 행렬은 분류 모델의 성능을 더 잘 평가할 수 있도록 도와주는 다른 지표를 도출할 수 있기 때문에 유용합니다. 몇 가지를 살펴보겠습니다:\n",
|
|
"\n",
|
|
"🎓 정밀도(Precision): `TP/(TP + FP)`로 정의되며, 예측된 양성 중 실제로 양성인 비율을 나타냅니다. [양성 예측 값](https://en.wikipedia.org/wiki/Positive_predictive_value \"Positive predictive value\")이라고도 합니다.\n",
|
|
"\n",
|
|
"🎓 재현율(Recall): `TP/(TP + FN)`로 정의되며, 실제로 양성인 샘플 중 양성 결과의 비율을 나타냅니다. `민감도(sensitivity)`라고도 합니다.\n",
|
|
"\n",
|
|
"🎓 특이도(Specificity): `TN/(TN + FP)`로 정의되며, 실제로 음성인 샘플 중 음성 결과의 비율을 나타냅니다.\n",
|
|
"\n",
|
|
"🎓 정확도(Accuracy): `TP + TN/(TP + TN + FP + FN)`로 샘플에 대해 정확히 예측된 레이블의 비율을 나타냅니다.\n",
|
|
"\n",
|
|
"🎓 F 측정(F Measure): 정밀도와 재현율의 가중 평균으로, 최상의 값은 1이고 최악의 값은 0입니다.\n",
|
|
"\n",
|
|
"이 지표들을 계산해 봅시다!\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": [
|
|
"## 이 모델의 ROC 곡선 시각화\n",
|
|
"\n",
|
|
"이제 한 가지 더 시각화를 진행하여 [`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": [
|
|
"ROC 곡선은 분류기의 출력 결과를 참 양성 대 거짓 양성의 관점에서 살펴보는 데 자주 사용됩니다. ROC 곡선은 일반적으로 Y축에 `True Positive Rate`/민감도, X축에 `False Positive Rate`/1-특이도를 표시합니다. 따라서 곡선의 가파름과 중간선과 곡선 사이의 공간이 중요합니다. 곡선이 빠르게 위로 올라가 중간선을 넘어가는 형태가 이상적입니다. 우리의 경우, 처음에는 거짓 양성이 존재하지만 이후 곡선이 올바르게 위로 올라가 중간선을 넘어갑니다.\n",
|
|
"\n",
|
|
"마지막으로, 실제 곡선 아래 면적(Area Under the Curve)을 계산하기 위해 `yardstick::roc_auc()`를 사용해 봅시다. AUC를 해석하는 한 가지 방법은 모델이 임의의 긍정적인 예제를 임의의 부정적인 예제보다 더 높은 순위로 평가할 확률로 보는 것입니다.\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": [
|
|
"결과는 약 `0.975`입니다. AUC는 0에서 1 사이의 값을 가지며, 점수가 클수록 좋습니다. 모델이 예측을 100% 정확히 수행한다면 AUC는 1이 됩니다. 이 경우, 모델은 *꽤 괜찮은* 성능을 보이고 있습니다.\n",
|
|
"\n",
|
|
"앞으로의 분류 관련 수업에서, 이 점수를 개선하는 방법(예: 이 경우 불균형 데이터 처리)을 배우게 될 것입니다.\n",
|
|
"\n",
|
|
"## 🚀도전 과제\n",
|
|
"\n",
|
|
"로지스틱 회귀에는 더 많은 내용이 있습니다! 하지만 배우는 가장 좋은 방법은 직접 실험해 보는 것입니다. 이 분석 유형에 적합한 데이터셋을 찾아 모델을 만들어 보세요. 무엇을 배우게 될까요? 팁: [Kaggle](https://www.kaggle.com/search?q=logistic+regression+datasets)에서 흥미로운 데이터셋을 찾아보세요.\n",
|
|
"\n",
|
|
"## 복습 및 자기 학습\n",
|
|
"\n",
|
|
"[스탠포드의 이 논문](https://web.stanford.edu/~jurafsky/slp3/5.pdf)의 첫 몇 페이지를 읽어보세요. 로지스틱 회귀의 실질적인 활용 사례에 대해 다루고 있습니다. 지금까지 공부한 회귀 유형 중 어떤 작업에 더 적합할지 생각해 보세요. 어떤 방법이 가장 효과적일까요?\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**면책 조항**: \n이 문서는 AI 번역 서비스 [Co-op Translator](https://github.com/Azure/co-op-translator)를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있으나, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서를 해당 언어로 작성된 상태에서 권위 있는 자료로 간주해야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 당사는 책임을 지지 않습니다. \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-04T01:23:48+00:00",
|
|
"source_file": "2-Regression/4-Logistic/solution/R/lesson_4-R.ipynb",
|
|
"language_code": "ko"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 1
|
|
} |