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.
1086 lines
45 KiB
1086 lines
45 KiB
{
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2,
|
|
"metadata": {
|
|
"colab": {
|
|
"name": "lesson_3-R.ipynb",
|
|
"provenance": [],
|
|
"collapsed_sections": [],
|
|
"toc_visible": true
|
|
},
|
|
"kernelspec": {
|
|
"name": "ir",
|
|
"display_name": "R"
|
|
},
|
|
"language_info": {
|
|
"name": "R"
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "5015d65d61ba75a223bfc56c273aa174",
|
|
"translation_date": "2025-09-04T01:10:16+00:00",
|
|
"source_file": "2-Regression/3-Linear/solution/R/lesson_3-R.ipynb",
|
|
"language_code": "ko"
|
|
}
|
|
},
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [],
|
|
"metadata": {
|
|
"id": "EgQw8osnsUV-"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"## 호박 가격 책정을 위한 선형 및 다항 회귀 - 3강\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/linear-polynomial.png\"\n",
|
|
" width=\"800\"/>\n",
|
|
" <figcaption>다사니 마디팔리의 인포그래픽</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"<!--{width=\"800\"}-->\n",
|
|
"\n",
|
|
"#### 소개\n",
|
|
"\n",
|
|
"지금까지 여러분은 이번 강의에서 사용할 호박 가격 데이터셋을 활용하여 회귀 분석이 무엇인지 탐구해 보았습니다. 또한, 이를 `ggplot2`를 사용해 시각화해 보았습니다. 💪\n",
|
|
"\n",
|
|
"이제 머신러닝을 위한 회귀 분석을 더 깊이 탐구할 준비가 되었습니다. 이번 강의에서는 *기본 선형 회귀*와 *다항 회귀*라는 두 가지 회귀 유형과 이 기술들에 기반한 일부 수학적 개념에 대해 배울 것입니다.\n",
|
|
"\n",
|
|
"> 이 커리큘럼 전반에 걸쳐, 우리는 수학에 대한 최소한의 지식을 가정하며, 다른 분야에서 온 학생들도 이해할 수 있도록 접근성을 높이고자 합니다. 따라서 주석, 🧮 참고 사항, 다이어그램 및 기타 학습 도구를 활용하여 이해를 돕고자 합니다.\n",
|
|
"\n",
|
|
"#### 준비\n",
|
|
"\n",
|
|
"다시 한 번 상기하자면, 이 데이터를 로드하는 이유는 질문을 던지고 답을 찾기 위함입니다.\n",
|
|
"\n",
|
|
"- 호박을 사기에 가장 좋은 시기는 언제일까요?\n",
|
|
"\n",
|
|
"- 미니어처 호박 한 상자의 예상 가격은 얼마일까요?\n",
|
|
"\n",
|
|
"- 반 버셸 바구니로 사는 것이 좋을까요, 아니면 1과 1/9 버셸 상자로 사는 것이 좋을까요? 이 데이터를 더 깊이 파헤쳐 봅시다.\n",
|
|
"\n",
|
|
"이전 강의에서, 여러분은 `tibble`(데이터 프레임의 현대적 재구성)을 생성하고, 원본 데이터셋의 일부를 활용하여 이를 채웠으며, 버셸 단위로 가격을 표준화했습니다. 하지만 그렇게 함으로써 약 400개의 데이터 포인트만 수집할 수 있었고, 그것도 가을철 데이터에 한정되었습니다. 데이터를 더 깨끗하게 정리하면 데이터의 성격에 대해 더 많은 세부 정보를 얻을 수 있을까요? 한번 알아봅시다... 🕵️♀️\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",
|
|
"- `corrplot`: [corrplot 패키지](https://cran.r-project.org/web/packages/corrplot/vignettes/corrplot-intro.html)는 변수 간 숨겨진 패턴을 감지하는 데 도움을 주는 자동 변수 재정렬을 지원하는 상관 행렬 시각화 도구입니다.\n",
|
|
"\n",
|
|
"다음 명령어로 패키지를 설치할 수 있습니다:\n",
|
|
"\n",
|
|
"`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"corrplot\"))`\n",
|
|
"\n",
|
|
"아래 스크립트는 이 모듈을 완료하는 데 필요한 패키지가 설치되어 있는지 확인하고, 누락된 경우 자동으로 설치합니다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "WqQPS1OAsg3H"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"suppressWarnings(if (!require(\"pacman\")) install.packages(\"pacman\"))\n",
|
|
"\n",
|
|
"pacman::p_load(tidyverse, tidymodels, janitor, corrplot)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "tA4C2WN3skCf",
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"outputId": "c06cd805-5534-4edc-f72b-d0d1dab96ac0"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"우리는 나중에 이 멋진 패키지들을 로드하여 현재 R 세션에서 사용할 수 있도록 만들 것입니다. (`pacman::p_load()` 함수가 이미 이 작업을 수행했으므로, 이는 단순한 예시일 뿐입니다.)\n",
|
|
"\n",
|
|
"## 1. 선형 회귀선\n",
|
|
"\n",
|
|
"Lesson 1에서 배운 것처럼, 선형 회귀의 목표는 *최적의 적합선*을 그리는 것입니다. 이를 통해:\n",
|
|
"\n",
|
|
"- **변수 간의 관계를 보여줍니다**. 변수들 간의 관계를 시각적으로 나타냅니다.\n",
|
|
"\n",
|
|
"- **예측을 수행합니다**. 새로운 데이터 포인트가 이 선과의 관계에서 어디에 위치할지 정확히 예측합니다.\n",
|
|
"\n",
|
|
"이러한 유형의 선을 그리기 위해 **최소제곱 회귀법(Least-Squares Regression)**이라는 통계 기법을 사용합니다. `최소제곱`이라는 용어는 회귀선을 둘러싼 모든 데이터 포인트의 거리를 제곱한 후 이를 합산하는 것을 의미합니다. 이상적으로는 이 최종 합계가 가능한 한 작아야 합니다. 왜냐하면 우리는 오류가 적은, 즉 `최소제곱`을 원하기 때문입니다. 따라서 최적의 적합선은 제곱된 오류 합계가 가장 낮은 값을 제공하는 선입니다. 이것이 *최소제곱 회귀법*이라는 이름의 유래입니다.\n",
|
|
"\n",
|
|
"우리는 데이터 포인트와 선 사이의 누적 거리가 가장 적은 선을 모델링하고자 합니다. 또한, 방향보다는 크기에 관심이 있기 때문에 합산하기 전에 각 항목을 제곱합니다.\n",
|
|
"\n",
|
|
"> **🧮 수학적으로 살펴보기**\n",
|
|
">\n",
|
|
"> 이 선, 즉 *최적의 적합선*은 [다음 방정식](https://en.wikipedia.org/wiki/Simple_linear_regression)으로 표현됩니다:\n",
|
|
">\n",
|
|
"> Y = a + bX\n",
|
|
">\n",
|
|
"> `X`는 '`설명 변수` 또는 `예측 변수`'를 의미합니다. `Y`는 '`종속 변수` 또는 `결과 변수`'를 의미합니다. 선의 기울기는 `b`이고, `a`는 y절편으로, 이는 `X = 0`일 때의 `Y` 값을 나타냅니다.\n",
|
|
">\n",
|
|
"\n",
|
|
"> \n",
|
|
" Jen Looper 제작 인포그래픽\n",
|
|
">\n",
|
|
"> 먼저, 기울기 `b`를 계산합니다.\n",
|
|
">\n",
|
|
"> 다시 말해, 우리의 호박 데이터의 원래 질문인 \"월별로 부셸당 호박 가격을 예측하라\"를 참조하면, `X`는 가격을, `Y`는 판매 월을 나타냅니다.\n",
|
|
">\n",
|
|
"> \n",
|
|
" Jen Looper 제작 인포그래픽\n",
|
|
"> \n",
|
|
"> `Y` 값을 계산합니다. 만약 약 \\$4를 지불하고 있다면, 이는 4월일 것입니다!\n",
|
|
">\n",
|
|
"> 이 선을 계산하는 수학은 선의 기울기를 보여주어야 하며, 이는 또한 절편, 즉 `X = 0`일 때 `Y`의 위치에 따라 달라집니다.\n",
|
|
">\n",
|
|
"> 이러한 값들의 계산 방법은 [Math is Fun](https://www.mathsisfun.com/data/least-squares-regression.html) 웹사이트에서 확인할 수 있습니다. 또한, [최소제곱 계산기](https://www.mathsisfun.com/data/least-squares-calculator.html)를 방문하여 숫자 값이 선에 어떤 영향을 미치는지 확인해 보세요.\n",
|
|
"\n",
|
|
"그렇게 무섭지 않죠? 🤓\n",
|
|
"\n",
|
|
"#### 상관관계\n",
|
|
"\n",
|
|
"이제 이해해야 할 또 다른 용어는 주어진 X와 Y 변수 간의 **상관계수(Correlation Coefficient)**입니다. 산점도를 사용하면 이 계수를 빠르게 시각화할 수 있습니다. 데이터 포인트가 깔끔한 선으로 흩어져 있는 플롯은 높은 상관관계를 가지지만, 데이터 포인트가 X와 Y 사이에 아무렇게나 흩어져 있는 플롯은 낮은 상관관계를 가집니다.\n",
|
|
"\n",
|
|
"좋은 선형 회귀 모델은 최소제곱 회귀법을 사용하여 회귀선을 그릴 때 상관계수가 높고(1에 가까운 값) 0에 가까운 값이 아닌 모델입니다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "cdX5FRpvsoP5"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"## **2. 데이터와의 춤: 모델링에 사용할 데이터 프레임 생성하기**\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/janitor.jpg\"\n",
|
|
" width=\"700\"/>\n",
|
|
" <figcaption>@allison_horst의 작품</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"<!--{width=\"700\"}-->\n"
|
|
],
|
|
"metadata": {
|
|
"id": "WdUKXk7Bs8-V"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"필요한 라이브러리와 데이터셋을 로드합니다. 데이터를 특정 부분만 포함하는 데이터 프레임으로 변환합니다:\n",
|
|
"\n",
|
|
"- 부셸 단위로 가격이 매겨진 호박만 가져옵니다.\n",
|
|
"\n",
|
|
"- 날짜를 월로 변환합니다.\n",
|
|
"\n",
|
|
"- 높은 가격과 낮은 가격의 평균을 계산하여 가격을 산출합니다.\n",
|
|
"\n",
|
|
"- 가격을 부셸 단위의 수량에 맞게 변환합니다.\n",
|
|
"\n",
|
|
"> 이러한 단계는 [이전 수업](https://github.com/microsoft/ML-For-Beginners/blob/main/2-Regression/2-Data/solution/lesson_2-R.ipynb)에서 다뤘습니다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "fMCtu2G2s-p8"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Load the core Tidyverse packages\n",
|
|
"library(tidyverse)\n",
|
|
"library(lubridate)\n",
|
|
"\n",
|
|
"# Import the pumpkins data\n",
|
|
"pumpkins <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/2-Regression/data/US-pumpkins.csv\")\n",
|
|
"\n",
|
|
"\n",
|
|
"# Get a glimpse and dimensions of the data\n",
|
|
"glimpse(pumpkins)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Print the first 50 rows of the data set\n",
|
|
"pumpkins %>% \n",
|
|
" slice_head(n = 5)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "ryMVZEEPtERn"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"순수한 모험 정신으로, 더러운 데이터를 조사하고 정리하는 간단한 기능을 제공하는 [`janitor 패키지`](../../../../../../2-Regression/3-Linear/solution/R/github.com/sfirke/janitor)를 탐구해 봅시다. 예를 들어, 우리의 데이터에 대한 열 이름을 살펴보겠습니다:\n"
|
|
],
|
|
"metadata": {
|
|
"id": "xcNxM70EtJjb"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Return column names\n",
|
|
"pumpkins %>% \n",
|
|
" names()"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "5XtpaIigtPfW"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"🤔 우리는 더 잘할 수 있습니다. 이 열 이름들을 `janitor::clean_names`를 사용하여 [snake_case](https://en.wikipedia.org/wiki/Snake_case) 규칙으로 변환하여 `friendR`로 만들어 봅시다. 이 함수에 대해 더 알아보려면: `?clean_names`\n"
|
|
],
|
|
"metadata": {
|
|
"id": "IbIqrMINtSHe"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Clean names to the snake_case convention\n",
|
|
"pumpkins <- pumpkins %>% \n",
|
|
" clean_names(case = \"snake\")\n",
|
|
"\n",
|
|
"# Return column names\n",
|
|
"pumpkins %>% \n",
|
|
" names()"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "a2uYvclYtWvX"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"더 깔끔하게 정리하기 🧹! 이제 이전 강의에서처럼 `dplyr`을 사용해 데이터를 다뤄봅시다! 💃\n"
|
|
],
|
|
"metadata": {
|
|
"id": "HfhnuzDDtaDd"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Select desired columns\n",
|
|
"pumpkins <- pumpkins %>% \n",
|
|
" select(variety, city_name, package, low_price, high_price, date)\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"# Extract the month from the dates to a new column\n",
|
|
"pumpkins <- pumpkins %>%\n",
|
|
" mutate(date = mdy(date),\n",
|
|
" month = month(date)) %>% \n",
|
|
" select(-date)\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"# Create a new column for average Price\n",
|
|
"pumpkins <- pumpkins %>% \n",
|
|
" mutate(price = (low_price + high_price)/2)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Retain only pumpkins with the string \"bushel\"\n",
|
|
"new_pumpkins <- pumpkins %>% \n",
|
|
" filter(str_detect(string = package, pattern = \"bushel\"))\n",
|
|
"\n",
|
|
"\n",
|
|
"# Normalize the pricing so that you show the pricing per bushel, not per 1 1/9 or 1/2 bushel\n",
|
|
"new_pumpkins <- new_pumpkins %>% \n",
|
|
" mutate(price = case_when(\n",
|
|
" str_detect(package, \"1 1/9\") ~ price/(1.1),\n",
|
|
" str_detect(package, \"1/2\") ~ price*2,\n",
|
|
" TRUE ~ price))\n",
|
|
"\n",
|
|
"# Relocate column positions\n",
|
|
"new_pumpkins <- new_pumpkins %>% \n",
|
|
" relocate(month, .before = variety)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Display the first 5 rows\n",
|
|
"new_pumpkins %>% \n",
|
|
" slice_head(n = 5)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "X0wU3gQvtd9f"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"좋아요!👌 이제 새로운 회귀 모델을 구축할 수 있는 깔끔하고 정돈된 데이터 세트를 얻었습니다!\n",
|
|
"\n",
|
|
"산점도는 어떠신가요?\n"
|
|
],
|
|
"metadata": {
|
|
"id": "UpaIwaxqth82"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Set theme\n",
|
|
"theme_set(theme_light())\n",
|
|
"\n",
|
|
"# Make a scatter plot of month and price\n",
|
|
"new_pumpkins %>% \n",
|
|
" ggplot(mapping = aes(x = month, y = price)) +\n",
|
|
" geom_point(size = 1.6)\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "DXgU-j37tl5K"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"산점도를 보면 우리가 8월부터 12월까지의 월별 데이터만 가지고 있다는 것을 상기시켜줍니다. 선형적으로 결론을 내리기 위해서는 더 많은 데이터가 필요할 가능성이 높습니다.\n",
|
|
"\n",
|
|
"모델링 데이터를 다시 한번 살펴봅시다:\n"
|
|
],
|
|
"metadata": {
|
|
"id": "Ve64wVbwtobI"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Display first 5 rows\n",
|
|
"new_pumpkins %>% \n",
|
|
" slice_head(n = 5)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "HFQX2ng1tuSJ"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"우리가 호박의 `price`를 예측하려고 하는데, `city`나 `package` 같은 문자형 열을 기반으로 한다면 어떻게 해야 할까요? 아니면 더 간단하게, 예를 들어 `package`와 `price` 간의 상관관계를 찾고 싶다면 어떻게 해야 할까요? (상관관계는 두 입력 값이 모두 숫자형이어야 합니다.) 🤷🤷\n",
|
|
"\n",
|
|
"머신러닝 모델은 텍스트 값보다는 숫자형 특징과 더 잘 작동하므로, 일반적으로 범주형 특징을 숫자형 표현으로 변환해야 합니다.\n",
|
|
"\n",
|
|
"즉, 모델이 효과적으로 사용할 수 있도록 예측 변수를 재구성하는 방법을 찾아야 하는데, 이를 `feature engineering`이라고 합니다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "7hsHoxsStyjJ"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"## 3. 모델링을 위한 데이터 전처리 with recipes 👩🍳👨🍳\n",
|
|
"\n",
|
|
"모델이 데이터를 효과적으로 활용할 수 있도록 예측 변수 값을 재구성하는 작업을 `특징 공학(feature engineering)`이라고 합니다.\n",
|
|
"\n",
|
|
"모델마다 요구하는 전처리 방식이 다릅니다. 예를 들어, 최소제곱법(least squares)은 `월(month)`, `품종(variety)`, `도시 이름(city_name)`과 같은 `범주형 변수`를 `인코딩`해야 합니다. 이는 단순히 `범주형 값`이 포함된 열을 하나 이상의 `숫자 열`로 변환하여 원래 열을 대체하는 작업을 의미합니다.\n",
|
|
"\n",
|
|
"예를 들어, 데이터에 다음과 같은 범주형 특징이 포함되어 있다고 가정해 봅시다:\n",
|
|
"\n",
|
|
"| city |\n",
|
|
"|:-------:|\n",
|
|
"| Denver |\n",
|
|
"| Nairobi |\n",
|
|
"| Tokyo |\n",
|
|
"\n",
|
|
"여기에 *순서 인코딩(ordinal encoding)*을 적용하여 각 범주에 고유한 정수 값을 대체할 수 있습니다. 결과는 다음과 같습니다:\n",
|
|
"\n",
|
|
"| city |\n",
|
|
"|:----:|\n",
|
|
"| 0 |\n",
|
|
"| 1 |\n",
|
|
"| 2 |\n",
|
|
"\n",
|
|
"이제 우리의 데이터에도 동일한 작업을 적용해 보겠습니다!\n",
|
|
"\n",
|
|
"이 섹션에서는 또 다른 멋진 Tidymodels 패키지인 [recipes](https://tidymodels.github.io/recipes/)를 살펴볼 것입니다. 이 패키지는 모델을 학습시키기 **전에** 데이터를 전처리하는 데 도움을 주기 위해 설계되었습니다. 기본적으로, 레시피(recipe)는 데이터 세트를 모델링에 적합하도록 준비하기 위해 어떤 단계를 적용해야 하는지 정의하는 객체입니다.\n",
|
|
"\n",
|
|
"이제 예측 변수 열의 모든 관측값에 고유한 정수를 대체하여 데이터를 모델링에 적합하게 준비하는 레시피를 만들어 봅시다:\n"
|
|
],
|
|
"metadata": {
|
|
"id": "AD5kQbcvt3Xl"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Specify a recipe\n",
|
|
"pumpkins_recipe <- recipe(price ~ ., data = new_pumpkins) %>% \n",
|
|
" step_integer(all_predictors(), zero_based = TRUE)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Print out the recipe\n",
|
|
"pumpkins_recipe"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "BNaFKXfRt9TU"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"우와! 👏 우리는 방금 결과값(가격)과 그에 해당하는 예측 변수들을 지정하고, 모든 예측 변수 열을 정수 집합으로 인코딩하도록 하는 첫 번째 레시피를 만들었어요 🙌! 이제 빠르게 내용을 살펴볼게요:\n",
|
|
"\n",
|
|
"- `recipe()` 호출 시 공식(formula)을 사용하면 `new_pumpkins` 데이터를 참조하여 변수들의 *역할*을 레시피에 알려줍니다. 예를 들어, `price` 열은 `outcome` 역할로 지정되었고, 나머지 열들은 `predictor` 역할로 지정되었습니다.\n",
|
|
"\n",
|
|
"- `step_integer(all_predictors(), zero_based = TRUE)`는 모든 예측 변수를 0부터 시작하는 번호로 정수 집합으로 변환하도록 지정합니다.\n",
|
|
"\n",
|
|
"아마 이런 생각이 들 수도 있어요: \"이거 정말 멋지다!! 그런데 레시피가 내가 기대한 대로 작동하는지 확인하려면 어떻게 해야 하지? 🤔\"\n",
|
|
"\n",
|
|
"정말 좋은 질문이에요! 보세요, 레시피가 정의되면 데이터를 실제로 전처리하는 데 필요한 매개변수를 추정하고, 처리된 데이터를 추출할 수 있습니다. 일반적으로 Tidymodels를 사용할 때는 이렇게 할 필요가 없어요(곧 `workflows`라는 일반적인 사용 방식을 살펴볼 거예요). 하지만 레시피가 예상대로 작동하는지 확인하고 싶을 때는 유용할 수 있답니다.\n",
|
|
"\n",
|
|
"이를 위해 두 가지 동사가 더 필요해요: `prep()`과 `bake()`. 그리고 항상 그렇듯이, [`Allison Horst`](https://github.com/allisonhorst/stats-illustrations)의 작은 R 친구들이 이를 더 잘 이해할 수 있도록 도와줄 거예요!\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/recipes.png\"\n",
|
|
" width=\"550\"/>\n",
|
|
" <figcaption>@allison_horst의 작품</figcaption>\n"
|
|
],
|
|
"metadata": {
|
|
"id": "KEiO0v7kuC9O"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"[`prep()`](https://recipes.tidymodels.org/reference/prep.html): 훈련 데이터 세트에서 필요한 매개변수를 추정하여 이후 다른 데이터 세트에 적용할 수 있도록 합니다. 예를 들어, 특정 예측 변수 열에서 어떤 관측값이 정수 0, 1, 2 등으로 할당될지 결정합니다.\n",
|
|
"\n",
|
|
"[`bake()`](https://recipes.tidymodels.org/reference/bake.html): 준비된 레시피를 사용하여 어떤 데이터 세트에든 작업을 적용합니다.\n",
|
|
"\n",
|
|
"그렇다면, 우리의 레시피를 준비하고 적용하여 내부적으로 예측 변수 열이 모델이 적합되기 전에 먼저 인코딩된다는 것을 확실히 확인해 봅시다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "Q1xtzebuuTCP"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Prep the recipe\n",
|
|
"pumpkins_prep <- prep(pumpkins_recipe)\n",
|
|
"\n",
|
|
"# Bake the recipe to extract a preprocessed new_pumpkins data\n",
|
|
"baked_pumpkins <- bake(pumpkins_prep, new_data = NULL)\n",
|
|
"\n",
|
|
"# Print out the baked data set\n",
|
|
"baked_pumpkins %>% \n",
|
|
" slice_head(n = 10)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "FGBbJbP_uUUn"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"우와! 🥳 처리된 데이터 `baked_pumpkins`의 모든 예측 변수가 인코딩되어, 우리가 정의한 전처리 단계(레시피)가 예상대로 작동한다는 것을 확인할 수 있습니다. 이로 인해 읽기는 조금 더 어려워졌지만, Tidymodels에게는 훨씬 더 이해하기 쉬운 데이터가 되었습니다! 어떤 관측값이 어떤 정수로 매핑되었는지 시간을 내어 확인해 보세요.\n",
|
|
"\n",
|
|
"또한, `baked_pumpkins`는 우리가 계산을 수행할 수 있는 데이터 프레임이라는 점도 언급할 가치가 있습니다.\n",
|
|
"\n",
|
|
"예를 들어, 데이터의 두 지점 간에 좋은 상관관계를 찾아서 잠재적으로 훌륭한 예측 모델을 구축해 봅시다. 이를 위해 함수 `cor()`를 사용할 것입니다. 함수에 대해 더 알고 싶다면 `?cor()`를 입력해 보세요.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "1dvP0LBUueAW"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Find the correlation between the city_name and the price\n",
|
|
"cor(baked_pumpkins$city_name, baked_pumpkins$price)\n",
|
|
"\n",
|
|
"# Find the correlation between the package and the price\n",
|
|
"cor(baked_pumpkins$package, baked_pumpkins$price)\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "3bQzXCjFuiSV"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"도시와 가격 사이에는 약한 상관관계만 있는 것으로 나타났습니다. 하지만 패키지와 가격 사이에는 조금 더 나은 상관관계가 있습니다. 그럴듯하지 않나요? 일반적으로, 생산물 상자가 클수록 가격이 높아지는 것이 당연하니까요.\n",
|
|
"\n",
|
|
"이왕 하는 김에, `corrplot` 패키지를 사용하여 모든 열의 상관관계 행렬을 시각화해 봅시다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "BToPWbgjuoZw"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Load the corrplot package\n",
|
|
"library(corrplot)\n",
|
|
"\n",
|
|
"# Obtain correlation matrix\n",
|
|
"corr_mat <- cor(baked_pumpkins %>% \n",
|
|
" # Drop columns that are not really informative\n",
|
|
" select(-c(low_price, high_price)))\n",
|
|
"\n",
|
|
"# Make a correlation plot between the variables\n",
|
|
"corrplot(corr_mat, method = \"shade\", shade.col = NA, tl.col = \"black\", tl.srt = 45, addCoef.col = \"black\", cl.pos = \"n\", order = \"original\")"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "ZwAL3ksmutVR"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"🤩🤩 훨씬 더 좋네요.\n",
|
|
"\n",
|
|
"이 데이터에 대해 이제 물어볼 좋은 질문은 다음과 같습니다: '`특정 호박 패키지의 가격은 얼마일까?`' 바로 시작해봅시다!\n",
|
|
"\n",
|
|
"> Note: **`new_data = NULL`**로 **`pumpkins_prep`** 레시피를 **`bake()`**하면, 처리된(즉, 인코딩된) 훈련 데이터를 추출할 수 있습니다. 예를 들어 테스트 세트와 같은 다른 데이터 세트가 있고 레시피가 이를 어떻게 전처리할지 확인하고 싶다면, **`new_data = test_set`**로 **`pumpkins_prep`**을 간단히 bake하면 됩니다.\n",
|
|
"\n",
|
|
"## 4. 선형 회귀 모델 구축하기\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/linear-polynomial.png\"\n",
|
|
" width=\"800\"/>\n",
|
|
" <figcaption>Dasani Madipalli의 인포그래픽</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"<!--{width=\"800\"}-->\n"
|
|
],
|
|
"metadata": {
|
|
"id": "YqXjLuWavNxW"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"이제 레시피를 작성하고 데이터가 적절히 전처리될 것임을 확인했으니, 회귀 모델을 만들어 다음 질문에 답해봅시다: `주어진 호박 패키지의 예상 가격은 얼마인가?`\n",
|
|
"\n",
|
|
"#### 훈련 세트를 사용하여 선형 회귀 모델 학습하기\n",
|
|
"\n",
|
|
"이미 짐작했겠지만, *price* 열은 `결과` 변수이고 *package* 열은 `예측자` 변수입니다.\n",
|
|
"\n",
|
|
"이를 위해 먼저 데이터를 80%는 훈련 세트로, 20%는 테스트 세트로 나누고, 예측자 열을 정수 집합으로 인코딩하는 레시피를 정의한 뒤 모델 사양을 작성할 것입니다. 레시피를 준비하고 굽는 과정은 생략할 텐데, 이미 데이터가 예상대로 전처리될 것임을 알고 있기 때문입니다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "Pq0bSzCevW-h"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"set.seed(2056)\n",
|
|
"# Split the data into training and test sets\n",
|
|
"pumpkins_split <- new_pumpkins %>% \n",
|
|
" initial_split(prop = 0.8)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Extract training and test data\n",
|
|
"pumpkins_train <- training(pumpkins_split)\n",
|
|
"pumpkins_test <- testing(pumpkins_split)\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"# Create a recipe for preprocessing the data\n",
|
|
"lm_pumpkins_recipe <- recipe(price ~ package, data = pumpkins_train) %>% \n",
|
|
" step_integer(all_predictors(), zero_based = TRUE)\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"# Create a linear model specification\n",
|
|
"lm_spec <- linear_reg() %>% \n",
|
|
" set_engine(\"lm\") %>% \n",
|
|
" set_mode(\"regression\")"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "CyoEh_wuvcLv"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"좋아요! 이제 레시피와 모델 사양을 준비했으니, 이를 하나의 객체로 묶어 데이터를 먼저 전처리(백그라운드에서 prep+bake 수행), 전처리된 데이터로 모델을 학습시키고, 추가적인 후처리 작업도 가능하게 만들어야 합니다. 마음이 한결 편안해지지 않나요!🤩\n",
|
|
"\n",
|
|
"Tidymodels에서는 이 편리한 객체를 [`workflow`](https://workflows.tidymodels.org/)라고 부르며, 모델링 구성 요소를 간편하게 담을 수 있습니다! 이는 *Python*에서 흔히 말하는 *파이프라인*과 같은 개념입니다.\n",
|
|
"\n",
|
|
"그럼 이제 모든 것을 workflow로 묶어봅시다!📦\n"
|
|
],
|
|
"metadata": {
|
|
"id": "G3zF_3DqviFJ"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Hold modelling components in a workflow\n",
|
|
"lm_wf <- workflow() %>% \n",
|
|
" add_recipe(lm_pumpkins_recipe) %>% \n",
|
|
" add_model(lm_spec)\n",
|
|
"\n",
|
|
"# Print out the workflow\n",
|
|
"lm_wf"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "T3olroU3v-WX"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"게다가, 워크플로우는 모델을 학습시키거나 훈련시키는 방식과 거의 동일한 방식으로 설정하거나 훈련시킬 수 있습니다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "zd1A5tgOwEPX"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Train the model\n",
|
|
"lm_wf_fit <- lm_wf %>% \n",
|
|
" fit(data = pumpkins_train)\n",
|
|
"\n",
|
|
"# Print the model coefficients learned \n",
|
|
"lm_wf_fit"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "NhJagFumwFHf"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"모델 학습 중에 얻어진 계수를 보면, 이는 실제 값과 예측 값 간의 전체 오차를 최소화하는 최적의 선형 적합선의 계수를 나타냅니다.\n",
|
|
"\n",
|
|
"#### 테스트 세트를 사용하여 모델 성능 평가하기\n",
|
|
"\n",
|
|
"이제 모델이 얼마나 잘 작동했는지 확인해볼 시간입니다 📏! 어떻게 하면 될까요?\n",
|
|
"\n",
|
|
"모델을 학습시켰으니, 이제 `parsnip::predict()`를 사용하여 test_set에 대한 예측을 수행할 수 있습니다. 그런 다음, 이 예측값을 실제 레이블 값과 비교하여 모델이 얼마나 잘 작동하는지 (혹은 잘 작동하지 않는지!) 평가할 수 있습니다.\n",
|
|
"\n",
|
|
"우선 테스트 세트에 대한 예측을 수행한 다음, 해당 열을 테스트 세트에 결합해 봅시다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "_4QkGtBTwItF"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Make predictions for the test set\n",
|
|
"predictions <- lm_wf_fit %>% \n",
|
|
" predict(new_data = pumpkins_test)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Bind predictions to the test set\n",
|
|
"lm_results <- pumpkins_test %>% \n",
|
|
" select(c(package, price)) %>% \n",
|
|
" bind_cols(predictions)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Print the first ten rows of the tibble\n",
|
|
"lm_results %>% \n",
|
|
" slice_head(n = 10)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "UFZzTG0gwTs9"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"네, 이제 모델을 훈련시키고 이를 사용해 예측을 수행했습니다! 🔮 모델이 얼마나 잘 작동하는지 평가해봅시다!\n",
|
|
"\n",
|
|
"Tidymodels에서는 `yardstick::metrics()`를 사용해 이를 수행합니다! 선형 회귀의 경우, 다음 지표들에 초점을 맞춰봅시다:\n",
|
|
"\n",
|
|
"- `Root Mean Square Error (RMSE)`: [MSE](https://en.wikipedia.org/wiki/Mean_squared_error)의 제곱근입니다. 이는 절대적인 지표로, 라벨(이 경우 호박의 가격)과 동일한 단위를 가집니다. 값이 작을수록 모델이 더 좋습니다 (단순히 말해, 예측이 평균적으로 얼마나 틀렸는지를 나타냅니다!)\n",
|
|
"\n",
|
|
"- `Coefficient of Determination (일반적으로 R-squared 또는 R2로 알려짐)`: 상대적인 지표로, 값이 클수록 모델의 적합도가 더 좋습니다. 본질적으로, 이 지표는 예측값과 실제 라벨 값 간의 분산 중 모델이 설명할 수 있는 비율을 나타냅니다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "0A5MjzM7wW9M"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Evaluate performance of linear regression\n",
|
|
"metrics(data = lm_results,\n",
|
|
" truth = price,\n",
|
|
" estimate = .pred)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "reJ0UIhQwcEH"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"모델 성능이 저하되었습니다. 패키지와 가격의 산점도를 시각화한 다음, 예측값을 사용해 최적의 적합선을 덧씌워 더 나은 지표를 확인해 봅시다.\n",
|
|
"\n",
|
|
"이를 위해 테스트 세트를 준비하고 처리하여 패키지 열을 인코딩한 후, 이를 모델이 생성한 예측값과 결합해야 합니다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "fdgjzjkBwfWt"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Encode package column\n",
|
|
"package_encode <- lm_pumpkins_recipe %>% \n",
|
|
" prep() %>% \n",
|
|
" bake(new_data = pumpkins_test) %>% \n",
|
|
" select(package)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Bind encoded package column to the results\n",
|
|
"lm_results <- lm_results %>% \n",
|
|
" bind_cols(package_encode %>% \n",
|
|
" rename(package_integer = package)) %>% \n",
|
|
" relocate(package_integer, .after = package)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Print new results data frame\n",
|
|
"lm_results %>% \n",
|
|
" slice_head(n = 5)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Make a scatter plot\n",
|
|
"lm_results %>% \n",
|
|
" ggplot(mapping = aes(x = package_integer, y = price)) +\n",
|
|
" geom_point(size = 1.6) +\n",
|
|
" # Overlay a line of best fit\n",
|
|
" geom_line(aes(y = .pred), color = \"orange\", size = 1.2) +\n",
|
|
" xlab(\"package\")\n",
|
|
" \n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "R0nw719lwkHE"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"훌륭합니다! 보시다시피, 선형 회귀 모델은 패키지와 해당 가격 간의 관계를 잘 일반화하지 못합니다.\n",
|
|
"\n",
|
|
"🎃 축하합니다! 이제 몇 가지 종류의 호박 가격을 예측할 수 있는 모델을 만들었습니다. 덕분에 당신의 휴일 호박밭은 아름다울 것입니다. 하지만 아마 더 나은 모델을 만들 수도 있을 거예요!\n",
|
|
"\n",
|
|
"## 5. 다항 회귀 모델 구축하기\n",
|
|
"\n",
|
|
"<p >\n",
|
|
" <img src=\"../../images/linear-polynomial.png\"\n",
|
|
" width=\"800\"/>\n",
|
|
" <figcaption>다사니 마디팔리의 인포그래픽</figcaption>\n",
|
|
"\n",
|
|
"\n",
|
|
"<!--{width=\"800\"}-->\n"
|
|
],
|
|
"metadata": {
|
|
"id": "HOCqJXLTwtWI"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"때로는 데이터가 선형 관계를 가지지 않을 수 있지만, 여전히 결과를 예측하고 싶을 때가 있습니다. 다항 회귀는 더 복잡한 비선형 관계에 대해 예측을 도와줄 수 있습니다.\n",
|
|
"\n",
|
|
"예를 들어, 호박 데이터 세트에서 패키지와 가격 간의 관계를 생각해봅시다. 변수 간에 선형 관계가 있을 때도 있지만(예: 호박의 부피가 클수록 가격이 높아지는 경우), 이러한 관계가 항상 평면이나 직선으로 나타낼 수 있는 것은 아닙니다.\n",
|
|
"\n",
|
|
"> ✅ [다항 회귀를 사용할 수 있는 데이터의 몇 가지 예시](https://online.stat.psu.edu/stat501/lesson/9/9.8)를 확인해보세요.\n",
|
|
">\n",
|
|
"> 이전 그래프에서 품종(Variety)과 가격(Price) 간의 관계를 다시 살펴보세요. 이 산점도가 반드시 직선으로 분석되어야 할 것처럼 보이나요? 아마도 그렇지 않을 겁니다. 이 경우, 다항 회귀를 시도해볼 수 있습니다.\n",
|
|
">\n",
|
|
"> ✅ 다항식은 하나 이상의 변수와 계수로 구성될 수 있는 수학적 표현식입니다.\n",
|
|
"\n",
|
|
"#### 훈련 세트를 사용하여 다항 회귀 모델 훈련하기\n",
|
|
"\n",
|
|
"다항 회귀는 비선형 데이터를 더 잘 맞추기 위해 *곡선*을 생성합니다.\n",
|
|
"\n",
|
|
"다항 모델이 예측 성능을 더 잘 발휘할 수 있는지 확인해봅시다. 이전에 했던 절차와 약간 유사한 과정을 따를 것입니다:\n",
|
|
"\n",
|
|
"- 데이터를 모델링에 적합하도록 준비하기 위해 수행해야 할 전처리 단계를 지정하는 레시피를 생성합니다. 예: 예측 변수를 인코딩하고 차수 *n*의 다항식을 계산하기\n",
|
|
"\n",
|
|
"- 모델 사양을 작성합니다.\n",
|
|
"\n",
|
|
"- 레시피와 모델 사양을 워크플로우에 묶습니다.\n",
|
|
"\n",
|
|
"- 워크플로우를 맞춤(fitting)으로써 모델을 생성합니다.\n",
|
|
"\n",
|
|
"- 테스트 데이터를 사용하여 모델 성능을 평가합니다.\n",
|
|
"\n",
|
|
"바로 시작해봅시다!\n"
|
|
],
|
|
"metadata": {
|
|
"id": "VcEIpRV9wzYr"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Specify a recipe\r\n",
|
|
"poly_pumpkins_recipe <-\r\n",
|
|
" recipe(price ~ package, data = pumpkins_train) %>%\r\n",
|
|
" step_integer(all_predictors(), zero_based = TRUE) %>% \r\n",
|
|
" step_poly(all_predictors(), degree = 4)\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"# Create a model specification\r\n",
|
|
"poly_spec <- linear_reg() %>% \r\n",
|
|
" set_engine(\"lm\") %>% \r\n",
|
|
" set_mode(\"regression\")\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"# Bundle recipe and model spec into a workflow\r\n",
|
|
"poly_wf <- workflow() %>% \r\n",
|
|
" add_recipe(poly_pumpkins_recipe) %>% \r\n",
|
|
" add_model(poly_spec)\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"# Create a model\r\n",
|
|
"poly_wf_fit <- poly_wf %>% \r\n",
|
|
" fit(data = pumpkins_train)\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"# Print learned model coefficients\r\n",
|
|
"poly_wf_fit\r\n",
|
|
"\r\n",
|
|
" "
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "63n_YyRXw3CC"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"#### 모델 성능 평가\n",
|
|
"\n",
|
|
"👏👏 다항식 모델을 만들었으니, 이제 테스트 세트에 대해 예측을 해봅시다!\n"
|
|
],
|
|
"metadata": {
|
|
"id": "-LHZtztSxDP0"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Make price predictions on test data\r\n",
|
|
"poly_results <- poly_wf_fit %>% predict(new_data = pumpkins_test) %>% \r\n",
|
|
" bind_cols(pumpkins_test %>% select(c(package, price))) %>% \r\n",
|
|
" relocate(.pred, .after = last_col())\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"# Print the results\r\n",
|
|
"poly_results %>% \r\n",
|
|
" slice_head(n = 10)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "YUFpQ_dKxJGx"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"우후, `yardstick::metrics()`를 사용하여 test_set에서 모델 성능을 평가해 봅시다.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "qxdyj86bxNGZ"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"metrics(data = poly_results, truth = price, estimate = .pred)"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "8AW5ltkBxXDm"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"🤩🤩 훨씬 더 나아진 성능.\n",
|
|
"\n",
|
|
"`rmse`가 약 7에서 약 3으로 감소했습니다. 이는 실제 가격과 예측 가격 간의 오차가 줄어들었다는 것을 나타냅니다. 이를 *대략적으로* 해석하면, 평균적으로 잘못된 예측이 약 \\$3 정도의 오차를 가진다는 의미입니다. `rsq`는 약 0.4에서 0.8로 증가했습니다.\n",
|
|
"\n",
|
|
"이 모든 지표는 다항식 모델이 선형 모델보다 훨씬 더 잘 작동한다는 것을 보여줍니다. 잘했어요!\n",
|
|
"\n",
|
|
"이제 이를 시각화할 수 있는지 확인해봅시다!\n"
|
|
],
|
|
"metadata": {
|
|
"id": "6gLHNZDwxYaS"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Bind encoded package column to the results\r\n",
|
|
"poly_results <- poly_results %>% \r\n",
|
|
" bind_cols(package_encode %>% \r\n",
|
|
" rename(package_integer = package)) %>% \r\n",
|
|
" relocate(package_integer, .after = package)\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"# Print new results data frame\r\n",
|
|
"poly_results %>% \r\n",
|
|
" slice_head(n = 5)\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"# Make a scatter plot\r\n",
|
|
"poly_results %>% \r\n",
|
|
" ggplot(mapping = aes(x = package_integer, y = price)) +\r\n",
|
|
" geom_point(size = 1.6) +\r\n",
|
|
" # Overlay a line of best fit\r\n",
|
|
" geom_line(aes(y = .pred), color = \"midnightblue\", size = 1.2) +\r\n",
|
|
" xlab(\"package\")\r\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "A83U16frxdF1"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"데이터에 더 잘 맞는 곡선을 볼 수 있습니다! 🤩\n",
|
|
"\n",
|
|
"`geom_smooth`에 다항식 공식을 전달하여 이를 더 부드럽게 만들 수 있습니다. 예를 들어:\n"
|
|
],
|
|
"metadata": {
|
|
"id": "4U-7aHOVxlGU"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Make a scatter plot\r\n",
|
|
"poly_results %>% \r\n",
|
|
" ggplot(mapping = aes(x = package_integer, y = price)) +\r\n",
|
|
" geom_point(size = 1.6) +\r\n",
|
|
" # Overlay a line of best fit\r\n",
|
|
" geom_smooth(method = lm, formula = y ~ poly(x, degree = 4), color = \"midnightblue\", size = 1.2, se = FALSE) +\r\n",
|
|
" xlab(\"package\")"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "5vzNT0Uexm-w"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"마치 부드러운 곡선처럼!🤩\n",
|
|
"\n",
|
|
"새로운 예측을 만드는 방법은 다음과 같습니다:\n"
|
|
],
|
|
"metadata": {
|
|
"id": "v9u-wwyLxq4G"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Make a hypothetical data frame\r\n",
|
|
"hypo_tibble <- tibble(package = \"bushel baskets\")\r\n",
|
|
"\r\n",
|
|
"# Make predictions using linear model\r\n",
|
|
"lm_pred <- lm_wf_fit %>% predict(new_data = hypo_tibble)\r\n",
|
|
"\r\n",
|
|
"# Make predictions using polynomial model\r\n",
|
|
"poly_pred <- poly_wf_fit %>% predict(new_data = hypo_tibble)\r\n",
|
|
"\r\n",
|
|
"# Return predictions in a list\r\n",
|
|
"list(\"linear model prediction\" = lm_pred, \r\n",
|
|
" \"polynomial model prediction\" = poly_pred)\r\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"id": "jRPSyfQGxuQv"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"`polynomial model` 예측은 `price`와 `package`의 산점도를 보면 타당해 보입니다! 그리고 이전 모델보다 더 나은 모델이라면, 동일한 데이터를 보았을 때 더 비싼 호박에 대한 예산을 세워야 할 것입니다!\n",
|
|
"\n",
|
|
"🏆 잘하셨습니다! 한 수업에서 두 개의 회귀 모델을 만들었습니다. 회귀에 대한 마지막 섹션에서는 범주를 결정하기 위해 로지스틱 회귀에 대해 배울 것입니다.\n",
|
|
"\n",
|
|
"## **🚀도전 과제**\n",
|
|
"\n",
|
|
"이 노트북에서 여러 다른 변수를 테스트하여 상관관계가 모델 정확도에 어떻게 영향을 미치는지 확인하세요.\n",
|
|
"\n",
|
|
"## [**강의 후 퀴즈**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/14/)\n",
|
|
"\n",
|
|
"## **복습 및 자기 학습**\n",
|
|
"\n",
|
|
"이번 수업에서는 선형 회귀에 대해 배웠습니다. 다른 중요한 회귀 유형도 있습니다. Stepwise, Ridge, Lasso, Elasticnet 기법에 대해 읽어보세요. 더 배우고 싶다면 [Stanford Statistical Learning course](https://online.stanford.edu/courses/sohs-ystatslearning-statistical-learning)가 좋은 과정입니다.\n",
|
|
"\n",
|
|
"Tidymodels 프레임워크를 사용하는 방법에 대해 더 배우고 싶다면 다음 리소스를 확인하세요:\n",
|
|
"\n",
|
|
"- Tidymodels 웹사이트: [Tidymodels 시작하기](https://www.tidymodels.org/start/)\n",
|
|
"\n",
|
|
"- Max Kuhn과 Julia Silge, [*Tidy Modeling with R*](https://www.tmwr.org/)*.*\n",
|
|
"\n",
|
|
"###### **감사합니다:**\n",
|
|
"\n",
|
|
"[R의 놀라운 삽화를 만들어 R을 더 친근하고 매력적으로 만들어 주신 Allison Horst](https://twitter.com/allison_horst?lang=en)에게 감사드립니다. 그녀의 [갤러리](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM)에서 더 많은 삽화를 찾아보세요.\n"
|
|
],
|
|
"metadata": {
|
|
"id": "8zOLOWqMxzk5"
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**면책 조항**: \n이 문서는 AI 번역 서비스 [Co-op Translator](https://github.com/Azure/co-op-translator)를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있지만, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서를 해당 언어로 작성된 상태에서 권위 있는 자료로 간주해야 합니다. 중요한 정보의 경우, 전문 번역가에 의한 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 당사는 책임을 지지 않습니다. \n"
|
|
]
|
|
}
|
|
]
|
|
} |