{ "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:08:35+00:00", "source_file": "2-Regression/3-Linear/solution/R/lesson_3-R.ipynb", "language_code": "ja" } }, "cells": [ { "cell_type": "markdown", "source": [], "metadata": { "id": "EgQw8osnsUV-" } }, { "cell_type": "markdown", "source": [ "## かぼちゃの価格設定における線形回帰と多項式回帰 - レッスン3\n", "

\n", " \n", "

Dasani Madipalliによるインフォグラフィック
\n", "\n", "\n", "\n", "\n", "#### はじめに\n", "\n", "これまでに、かぼちゃの価格設定データセットから収集したサンプルデータを使って回帰分析が何であるかを探求しました。また、それを`ggplot2`を使って視覚化しました。💪\n", "\n", "これからは、機械学習における回帰分析をさらに深く掘り下げていきます。このレッスンでは、*基本的な線形回帰*と*多項式回帰*の2種類の回帰分析について、これらの手法の基礎となる数学も含めて学びます。\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/)フレームワークは、モデリングと機械学習のための[Rパッケージのコレクション](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", "レッスン1で学んだように、線形回帰の目的は、以下のために*最適な直線*をプロットできるようにすることです:\n", "\n", "- **変数間の関係を示す**。変数間の関係を明らかにする。\n", "\n", "- **予測を行う**。新しいデータポイントがその直線に対してどこに位置するかを正確に予測する。\n", "\n", "この種の直線を描くために、**最小二乗回帰**と呼ばれる統計的手法を使用します。`最小二乗`という用語は、回帰直線の周囲にあるすべてのデータポイントを二乗して合計することを意味します。理想的には、その最終的な合計が可能な限り小さいことが望ましいです。なぜなら、エラーの数を少なくしたい、つまり`最小二乗`にしたいからです。このようにして、最適な直線は、二乗誤差の合計が最も小さい値を与える直線となります。そのため、この手法は*最小二乗回帰*と呼ばれます。\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", "> ![](../../../../../../2-Regression/3-Linear/solution/images/slope.png \"slope = $y/x$\")\n", " ジェン・ルーパーによるインフォグラフィック\n", ">\n", "> まず、傾き`b`を計算します。\n", ">\n", "> 言い換えると、かぼちゃデータの元の質問「月ごとに1ブッシェルあたりのかぼちゃの価格を予測する」に関連して、`X`は価格を指し、`Y`は販売月を指します。\n", ">\n", "> ![](../../../../../../translated_images/calculation.989aa7822020d9d0ba9fc781f1ab5192f3421be86ebb88026528aef33c37b0d8.ja.png)\n", " ジェン・ルーパーによるインフォグラフィック\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", "もう1つ理解すべき用語は、与えられたXとY変数間の**相関係数**です。散布図を使用すると、この係数をすぐに視覚化できます。データポイントがきれいな直線に散らばっているプロットは高い相関を持ちますが、データポイントがXとYの間で無秩序に散らばっているプロットは低い相関を持ちます。\n", "\n", "良い線形回帰モデルは、最小二乗回帰法を使用して回帰直線を引いた際に、相関係数が高い(1に近い)ものです。\n" ], "metadata": { "id": "cdX5FRpvsoP5" } }, { "cell_type": "markdown", "source": [ "## **2. データとのダンス: モデリングに使用するデータフレームの作成**\n", "\n", "

\n", " \n", "

イラスト: @allison_horst
\n", "\n", "\n", "\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": [ "🤔 私たちはもっと良くできます。これらの列名を [snake_case](https://en.wikipedia.org/wiki/Snake_case) 規約に変換して `janitor::clean_names` を使用して `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": [ "もし、`city` や `package` のような文字型の列を基にしてカボチャの `price` を予測したい場合はどうすればよいでしょうか? あるいはもっと簡単に言えば、例えば `package` と `price` の間の相関(どちらも数値である必要があります)を見つけるにはどうすればよいでしょうか? 🤷🤷\n", "\n", "機械学習モデルは、テキスト値よりも数値の特徴量を使う方が効果的に動作するため、通常はカテゴリカルな特徴量を数値表現に変換する必要があります。\n", "\n", "つまり、モデルが効果的に利用できるように予測変数を再フォーマットする方法を見つける必要があります。このプロセスは `特徴量エンジニアリング` として知られています。\n" ], "metadata": { "id": "7hsHoxsStyjJ" } }, { "cell_type": "markdown", "source": [ "## 3. モデリングのためのデータ前処理 - recipes 👩‍🍳👨‍🍳\n", "\n", "予測値をモデルが効果的に利用できるように再構成する活動は、`特徴量エンジニアリング`と呼ばれています。\n", "\n", "異なるモデルには異なる前処理の要件があります。例えば、最小二乗法では、月、品種、都市名などの`カテゴリ変数のエンコード`が必要です。これは単に、`カテゴリ値`を持つ列を、元の列の代わりとなる1つ以上の`数値列`に`変換`することを意味します。\n", "\n", "例えば、データに以下のカテゴリ特徴量が含まれているとします:\n", "\n", "| city |\n", "|:-------:|\n", "| Denver |\n", "| Nairobi |\n", "| Tokyo |\n", "\n", "これに*順序エンコーディング*を適用して、各カテゴリにユニークな整数値を代入すると、次のようになります:\n", "\n", "| city |\n", "|:----:|\n", "| 0 |\n", "| 1 |\n", "| 2 |\n", "\n", "これをデータに適用していきます!\n", "\n", "このセクションでは、もう一つの素晴らしいTidymodelsパッケージ:[recipes](https://tidymodels.github.io/recipes/)を探求します。このパッケージは、モデルをトレーニングする**前**にデータを前処理するために設計されています。レシピの中心的な役割は、データセットをモデリングに適した状態にするためにどのステップを適用すべきかを定義するオブジェクトです。\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()`への呼び出しで式を使用すると、`new_pumpkins`データを参照として変数の*役割*をレシピに伝えます。例えば、`price`列は`outcome`の役割に割り当てられ、残りの列は`predictor`の役割に割り当てられています。\n", "\n", "- `step_integer(all_predictors(), zero_based = TRUE)`は、すべての予測因子を0から始まる番号付けで整数のセットに変換することを指定しています。\n", "\n", "きっとこう思っているかもしれませんね:「これってすごくクールだけど、レシピが本当に期待通りに動作しているか確認したい場合はどうすればいいの?🤔」\n", "\n", "それは素晴らしい考えです!レシピが定義されると、データを実際に前処理するために必要なパラメータを推定し、処理されたデータを抽出することができます。通常、Tidymodelsを使用する場合にはこれを行う必要はありません(すぐに通常の方法である`workflows`を見ていきます)が、レシピが期待通りに動作しているか確認するための簡単なチェックを行いたい場合には役立ちます。\n", "\n", "そのためには、もう2つの動詞が必要です:`prep()`と`bake()`。そしていつものように、[`Allison Horst`](https://github.com/allisonhorst/stats-illustrations)による小さなRの仲間たちがこれをよりよく理解する手助けをしてくれます!\n", "\n", "

\n", " \n", "

Artwork by @allison_horst
\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", "例えば、データの2つのポイント間で良い相関を見つけて、良い予測モデルを構築する可能性を探ることができます。そのために、`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": [ "実際のところ、CityとPriceの間には弱い相関しかありません。しかし、PackageとそのPriceの間にはもう少し強い相関があります。それは納得できますよね?通常、箱が大きければ大きいほど、価格も高くなるものです。\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: **`pumpkins_prep`** という準備済みのレシピを **`bake()`** し、**`new_data = NULL`** を指定すると、処理済み(つまりエンコードされた)トレーニングデータを抽出できます。例えば、テストセットのような別のデータセットがあり、そのデータがレシピでどのように前処理されるかを確認したい場合は、**`new_data = test_set`** を指定して **`pumpkins_prep`** を単純に bake すればよいのです。\n", "\n", "## 4. 線形回帰モデルを構築する\n", "\n", "

\n", " \n", "

Dasani Madipalliによるインフォグラフィック
\n", "\n", "\n", "\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", "では、すべてをワークフローにまとめましょう!📦\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()`を使ってテストセットに対する予測を行うことができます。その後、これらの予測値を実際のラベル値と比較して、モデルがどれだけうまく(またはうまくいっていないか)動作しているかを評価します。\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", "

\n", " \n", "

Dasani Madipalliによるインフォグラフィック
\n", "\n", "\n", "\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", "> ✅ 多項式とは、1つ以上の変数と係数から成る数学的表現です。\n", "\n", "#### トレーニングセットを使って多項式回帰モデルを訓練する\n", "\n", "多項式回帰は、非線形データにより適合するような*曲線*を作成します。\n", "\n", "多項式モデルが予測性能を向上させるかどうかを確認してみましょう。以前とほぼ同じ手順に従います:\n", "\n", "- データをモデリングに適した状態にするための前処理手順を指定するレシピを作成します。具体的には、予測変数のエンコードや次数 *n* の多項式の計算などです。\n", "\n", "- モデル仕様を構築します。\n", "\n", "- レシピとモデル仕様をワークフローにまとめます。\n", "\n", "- ワークフローを適合させてモデルを作成します。\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", "🏆 よくできました!1つのレッスンで2つの回帰モデルを作成しましたね。回帰の最終セクションでは、カテゴリを決定するためのロジスティック回帰について学びます。\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", "[Allison Horst](https://twitter.com/allison_horst?lang=en) に、Rをより親しみやすく魅力的にしてくれる素晴らしいイラストを作成していただきました。彼女の[ギャラリー](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" ] } ] }