{ "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-06T13:26:17+00:00", "source_file": "2-Regression/3-Linear/solution/R/lesson_3-R.ipynb", "language_code": "vi" } }, "cells": [ { "cell_type": "markdown", "source": [], "metadata": { "id": "EgQw8osnsUV-" } }, { "cell_type": "markdown", "source": [ "## Hồi quy tuyến tính và hồi quy đa thức để định giá bí ngô - Bài học 3\n", "

\n", " \n", "

Đồ họa thông tin bởi Dasani Madipalli
\n", "\n", "\n", "\n", "\n", "#### Giới thiệu\n", "\n", "Cho đến nay, bạn đã tìm hiểu về hồi quy với dữ liệu mẫu được thu thập từ tập dữ liệu định giá bí ngô mà chúng ta sẽ sử dụng xuyên suốt bài học này. Bạn cũng đã trực quan hóa nó bằng cách sử dụng `ggplot2`.💪\n", "\n", "Bây giờ bạn đã sẵn sàng đi sâu hơn vào hồi quy trong học máy. Trong bài học này, bạn sẽ tìm hiểu thêm về hai loại hồi quy: *hồi quy tuyến tính cơ bản* và *hồi quy đa thức*, cùng với một số toán học cơ bản liên quan đến các kỹ thuật này.\n", "\n", "> Xuyên suốt chương trình học này, chúng tôi giả định rằng bạn có kiến thức toán học tối thiểu và cố gắng làm cho nó dễ tiếp cận hơn đối với học viên đến từ các lĩnh vực khác, vì vậy hãy chú ý đến các ghi chú, 🧮 các điểm nhấn, sơ đồ và các công cụ học tập khác để hỗ trợ việc hiểu bài.\n", "\n", "#### Chuẩn bị\n", "\n", "Như đã nhắc lại, bạn đang tải dữ liệu này để đặt câu hỏi về nó.\n", "\n", "- Khi nào là thời điểm tốt nhất để mua bí ngô?\n", "\n", "- Giá của một thùng bí ngô nhỏ sẽ là bao nhiêu?\n", "\n", "- Tôi nên mua chúng trong giỏ nửa bushel hay trong hộp 1 1/9 bushel? Hãy tiếp tục khám phá dữ liệu này.\n", "\n", "Trong bài học trước, bạn đã tạo một `tibble` (một cách tái hiện hiện đại của khung dữ liệu) và điền vào đó một phần của tập dữ liệu gốc, chuẩn hóa giá theo bushel. Tuy nhiên, bằng cách làm như vậy, bạn chỉ có thể thu thập khoảng 400 điểm dữ liệu và chỉ trong các tháng mùa thu. Có lẽ chúng ta có thể tìm hiểu thêm chi tiết về bản chất của dữ liệu bằng cách làm sạch nó hơn nữa? Chúng ta sẽ xem... 🕵️‍♀️\n", "\n", "Để thực hiện nhiệm vụ này, chúng ta sẽ cần các gói sau:\n", "\n", "- `tidyverse`: [tidyverse](https://www.tidyverse.org/) là một [bộ sưu tập các gói R](https://www.tidyverse.org/packages) được thiết kế để làm cho khoa học dữ liệu nhanh hơn, dễ dàng hơn và thú vị hơn!\n", "\n", "- `tidymodels`: [tidymodels](https://www.tidymodels.org/) là một [khung làm việc](https://www.tidymodels.org/packages/) bao gồm các gói dành cho mô hình hóa và học máy.\n", "\n", "- `janitor`: [gói janitor](https://github.com/sfirke/janitor) cung cấp các công cụ đơn giản để kiểm tra và làm sạch dữ liệu bẩn.\n", "\n", "- `corrplot`: [gói corrplot](https://cran.r-project.org/web/packages/corrplot/vignettes/corrplot-intro.html) cung cấp một công cụ trực quan để khám phá ma trận tương quan, hỗ trợ sắp xếp lại biến tự động nhằm giúp phát hiện các mẫu ẩn giữa các biến.\n", "\n", "Bạn có thể cài đặt chúng bằng lệnh:\n", "\n", "`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"corrplot\"))`\n", "\n", "Đoạn mã dưới đây kiểm tra xem bạn đã có các gói cần thiết để hoàn thành module này chưa và cài đặt chúng cho bạn nếu chúng bị thiếu.\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": [ "Chúng ta sẽ tải các gói tuyệt vời này và làm cho chúng khả dụng trong phiên làm việc R hiện tại của chúng ta. (Đây chỉ là minh họa, `pacman::p_load()` đã làm điều này cho bạn)\n", "\n", "## 1. Đường hồi quy tuyến tính\n", "\n", "Như bạn đã học trong Bài học 1, mục tiêu của bài tập hồi quy tuyến tính là vẽ được một *đường* *phù hợp nhất* để:\n", "\n", "- **Hiển thị mối quan hệ giữa các biến**. Hiển thị mối quan hệ giữa các biến.\n", "\n", "- **Dự đoán**. Dự đoán chính xác vị trí mà một điểm dữ liệu mới sẽ nằm trong mối quan hệ với đường đó.\n", "\n", "Để vẽ loại đường này, chúng ta sử dụng một kỹ thuật thống kê gọi là **Hồi quy Bình phương Tối thiểu**. Thuật ngữ `bình phương tối thiểu` có nghĩa là tất cả các điểm dữ liệu xung quanh đường hồi quy được bình phương và sau đó cộng lại. Lý tưởng nhất, tổng cuối cùng này càng nhỏ càng tốt, vì chúng ta muốn số lỗi thấp, hay `bình phương tối thiểu`. Do đó, đường phù hợp nhất là đường cho chúng ta giá trị thấp nhất của tổng các lỗi bình phương - vì vậy mới có tên gọi *hồi quy bình phương tối thiểu*.\n", "\n", "Chúng ta làm điều này vì muốn mô hình hóa một đường có khoảng cách tích lũy nhỏ nhất từ tất cả các điểm dữ liệu của chúng ta. Chúng ta cũng bình phương các giá trị trước khi cộng chúng lại vì chúng ta quan tâm đến độ lớn của chúng hơn là hướng của chúng.\n", "\n", "> **🧮 Hiển thị toán học**\n", ">\n", "> Đường này, gọi là *đường phù hợp nhất*, có thể được biểu diễn bằng [một phương trình](https://en.wikipedia.org/wiki/Simple_linear_regression):\n", ">\n", "> Y = a + bX\n", ">\n", "> `X` là '`biến giải thích` hoặc `biến dự đoán`'. `Y` là '`biến phụ thuộc` hoặc `kết quả`'. Độ dốc của đường là `b` và `a` là giao điểm trục y, tức là giá trị của `Y` khi `X = 0`.\n", ">\n", "\n", "> ![](../../../../../../2-Regression/3-Linear/solution/images/slope.png \"slope = $y/x$\")\n", " Đồ họa thông tin bởi Jen Looper\n", ">\n", "> Đầu tiên, tính độ dốc `b`.\n", ">\n", "> Nói cách khác, và liên hệ với câu hỏi ban đầu về dữ liệu bí ngô của chúng ta: \"dự đoán giá của một giạ bí ngô theo tháng\", `X` sẽ là giá và `Y` sẽ là tháng bán.\n", ">\n", "> ![](../../../../../../2-Regression/3-Linear/solution/images/calculation.png)\n", " Đồ họa thông tin bởi Jen Looper\n", "> \n", "> Tính giá trị của Y. Nếu bạn đang trả khoảng \\$4, chắc hẳn là tháng Tư!\n", ">\n", "> Phép toán tính toán đường này phải thể hiện độ dốc của đường, điều này cũng phụ thuộc vào giao điểm, hoặc vị trí của `Y` khi `X = 0`.\n", ">\n", "> Bạn có thể quan sát phương pháp tính toán các giá trị này trên trang web [Math is Fun](https://www.mathsisfun.com/data/least-squares-regression.html). Cũng hãy ghé thăm [máy tính hồi quy bình phương tối thiểu này](https://www.mathsisfun.com/data/least-squares-calculator.html) để xem cách các giá trị số ảnh hưởng đến đường.\n", "\n", "Không quá đáng sợ, đúng không? 🤓\n", "\n", "#### Tương quan\n", "\n", "Một thuật ngữ nữa cần hiểu là **Hệ số Tương quan** giữa các biến X và Y cho trước. Sử dụng biểu đồ phân tán, bạn có thể nhanh chóng hình dung hệ số này. Một biểu đồ với các điểm dữ liệu phân tán theo một đường gọn gàng có tương quan cao, nhưng một biểu đồ với các điểm dữ liệu phân tán khắp nơi giữa X và Y có tương quan thấp.\n", "\n", "Một mô hình hồi quy tuyến tính tốt sẽ là mô hình có Hệ số Tương quan cao (gần 1 hơn 0) sử dụng phương pháp Hồi quy Bình phương Tối thiểu với một đường hồi quy.\n" ], "metadata": { "id": "cdX5FRpvsoP5" } }, { "cell_type": "markdown", "source": [ "## **2. Một điệu nhảy với dữ liệu: tạo một khung dữ liệu sẽ được sử dụng để mô hình hóa**\n", "\n", "

\n", " \n", "

Tác phẩm nghệ thuật của @allison_horst
\n", "\n", "\n", "\n" ], "metadata": { "id": "WdUKXk7Bs8-V" } }, { "cell_type": "markdown", "source": [ "Tải các thư viện cần thiết và tập dữ liệu. Chuyển dữ liệu thành một khung dữ liệu chứa một phần của tập dữ liệu:\n", "\n", "- Chỉ lấy bí ngô được định giá theo đơn vị bushel\n", "\n", "- Chuyển đổi ngày thành tháng\n", "\n", "- Tính giá trung bình dựa trên giá cao và giá thấp\n", "\n", "- Chuyển đổi giá để phản ánh mức giá theo số lượng bushel\n", "\n", "> Chúng ta đã đề cập đến các bước này trong [bài học trước](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": [ "Với tinh thần phiêu lưu thuần túy, hãy cùng khám phá [`gói janitor`](../../../../../../2-Regression/3-Linear/solution/R/github.com/sfirke/janitor) cung cấp các hàm đơn giản để kiểm tra và làm sạch dữ liệu bẩn. Ví dụ, hãy xem qua tên các cột trong dữ liệu của chúng ta:\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": [ "🤔 Chúng ta có thể làm tốt hơn. Hãy làm cho các tên cột này `friendR` bằng cách chuyển chúng sang quy ước [snake_case](https://en.wikipedia.org/wiki/Snake_case) bằng cách sử dụng `janitor::clean_names`. Để tìm hiểu thêm về hàm này: `?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": [ "Rất gọn gàng 🧹! Bây giờ, hãy nhảy múa với dữ liệu bằng cách sử dụng `dplyr` như trong bài học trước! 💃\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": [ "Làm tốt lắm!👌 Giờ bạn đã có một tập dữ liệu sạch sẽ, gọn gàng để xây dựng mô hình hồi quy mới của mình!\n", "\n", "Bạn có muốn xem biểu đồ phân tán không?\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": [ "Một biểu đồ phân tán nhắc nhở chúng ta rằng chúng ta chỉ có dữ liệu tháng từ tháng Tám đến tháng Mười Hai. Chúng ta có lẽ cần thêm dữ liệu để có thể đưa ra kết luận theo cách tuyến tính.\n", "\n", "Hãy cùng xem lại dữ liệu mô hình của chúng ta:\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": [ "Nếu chúng ta muốn dự đoán `price` của một quả bí ngô dựa trên các cột `city` hoặc `package`, vốn thuộc loại ký tự, thì sao? Hoặc đơn giản hơn, làm thế nào để tìm mối tương quan (yêu cầu cả hai đầu vào phải là số) giữa, ví dụ, `package` và `price`? 🤷🤷\n", "\n", "Các mô hình học máy hoạt động tốt nhất với các đặc trưng dạng số thay vì giá trị văn bản, vì vậy bạn thường cần chuyển đổi các đặc trưng phân loại thành các biểu diễn dạng số.\n", "\n", "Điều này có nghĩa là chúng ta phải tìm cách định dạng lại các biến dự đoán của mình để làm cho chúng dễ sử dụng hơn đối với mô hình, một quá trình được gọi là `feature engineering`.\n" ], "metadata": { "id": "7hsHoxsStyjJ" } }, { "cell_type": "markdown", "source": [ "## 3. Tiền xử lý dữ liệu để mô hình hóa với recipes 👩‍🍳👨‍🍳\n", "\n", "Các hoạt động định dạng lại giá trị dự đoán để giúp mô hình sử dụng hiệu quả hơn được gọi là `kỹ thuật đặc trưng`.\n", "\n", "Các mô hình khác nhau có yêu cầu tiền xử lý khác nhau. Ví dụ, phương pháp bình phương tối thiểu yêu cầu `mã hóa các biến phân loại` như tháng, loại và tên thành phố. Điều này đơn giản chỉ là `chuyển đổi` một cột với các `giá trị phân loại` thành một hoặc nhiều `cột số` thay thế cho cột ban đầu.\n", "\n", "Ví dụ, giả sử dữ liệu của bạn bao gồm đặc trưng phân loại sau:\n", "\n", "| thành phố |\n", "|:------------:|\n", "| Denver |\n", "| Nairobi |\n", "| Tokyo |\n", "\n", "Bạn có thể áp dụng *mã hóa thứ tự* để thay thế một giá trị số nguyên duy nhất cho mỗi danh mục, như sau:\n", "\n", "| thành phố |\n", "|:---------:|\n", "| 0 |\n", "| 1 |\n", "| 2 |\n", "\n", "Và đó chính là điều chúng ta sẽ làm với dữ liệu của mình!\n", "\n", "Trong phần này, chúng ta sẽ khám phá một gói tuyệt vời khác của Tidymodels: [recipes](https://tidymodels.github.io/recipes/) - được thiết kế để giúp bạn tiền xử lý dữ liệu **trước khi** huấn luyện mô hình. Cốt lõi của nó, một recipe là một đối tượng định nghĩa các bước cần áp dụng cho một tập dữ liệu để chuẩn bị cho việc mô hình hóa.\n", "\n", "Bây giờ, hãy tạo một recipe để chuẩn bị dữ liệu của chúng ta cho việc mô hình hóa bằng cách thay thế một số nguyên duy nhất cho tất cả các quan sát trong các cột dự đoá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": [ "Tuyệt vời! 👏 Chúng ta vừa tạo ra công thức đầu tiên để xác định một kết quả (giá cả) và các yếu tố dự đoán tương ứng, đồng thời tất cả các cột dự đoán đều được mã hóa thành một tập hợp các số nguyên 🙌! Hãy cùng phân tích nhanh:\n", "\n", "- Lệnh gọi `recipe()` với một công thức cho phép công thức xác định *vai trò* của các biến bằng cách sử dụng dữ liệu `new_pumpkins` làm tham chiếu. Ví dụ, cột `price` được gán vai trò là `outcome`, trong khi các cột còn lại được gán vai trò là `predictor`.\n", "\n", "- `step_integer(all_predictors(), zero_based = TRUE)` chỉ định rằng tất cả các yếu tố dự đoán sẽ được chuyển đổi thành một tập hợp các số nguyên, bắt đầu đánh số từ 0.\n", "\n", "Chúng tôi chắc chắn rằng bạn có thể đang nghĩ: \"Thật tuyệt vời!! Nhưng nếu tôi cần xác nhận rằng các công thức đang thực hiện đúng như mong đợi thì sao? 🤔\"\n", "\n", "Đó là một ý tưởng tuyệt vời! Bạn thấy đấy, một khi công thức của bạn được định nghĩa, bạn có thể ước tính các tham số cần thiết để thực sự tiền xử lý dữ liệu, sau đó trích xuất dữ liệu đã được xử lý. Bạn thường không cần làm điều này khi sử dụng Tidymodels (chúng ta sẽ thấy cách thông thường ngay sau đây-\\> `workflows`), nhưng nó có thể rất hữu ích khi bạn muốn kiểm tra nhanh để xác nhận rằng các công thức đang hoạt động đúng như mong đợi.\n", "\n", "Để làm điều đó, bạn sẽ cần hai động từ nữa: `prep()` và `bake()`. Và như mọi khi, những người bạn nhỏ R của chúng ta từ [`Allison Horst`](https://github.com/allisonhorst/stats-illustrations) sẽ giúp bạn hiểu rõ hơn về điều này!\n", "\n", "

\n", " \n", "

Tác phẩm nghệ thuật của @allison_horst
\n" ], "metadata": { "id": "KEiO0v7kuC9O" } }, { "cell_type": "markdown", "source": [ "[`prep()`](https://recipes.tidymodels.org/reference/prep.html): ước tính các tham số cần thiết từ tập dữ liệu huấn luyện, sau đó có thể áp dụng cho các tập dữ liệu khác. Ví dụ, đối với một cột dự đoán cụ thể, quan sát nào sẽ được gán giá trị nguyên 0, 1, 2, v.v.\n", "\n", "[`bake()`](https://recipes.tidymodels.org/reference/bake.html): sử dụng công thức đã được chuẩn bị và áp dụng các thao tác cho bất kỳ tập dữ liệu nào.\n", "\n", "Vậy nên, hãy chuẩn bị và áp dụng công thức của chúng ta để thực sự xác nhận rằng, trong quá trình xử lý, các cột dự đoán sẽ được mã hóa trước khi mô hình được huấn luyệ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": [ "Woo-hoo!🥳 Dữ liệu đã xử lý `baked_pumpkins` đã được mã hóa tất cả các biến dự đoán, xác nhận rằng các bước tiền xử lý được định nghĩa trong công thức của chúng ta hoạt động đúng như mong đợi. Điều này khiến việc đọc dữ liệu trở nên khó khăn hơn đối với bạn nhưng lại dễ hiểu hơn nhiều đối với Tidymodels! Hãy dành chút thời gian để tìm hiểu xem quan sát nào đã được ánh xạ thành số nguyên tương ứng.\n", "\n", "Cũng cần lưu ý rằng `baked_pumpkins` là một khung dữ liệu mà chúng ta có thể thực hiện các phép tính trên đó.\n", "\n", "Ví dụ, hãy thử tìm mối tương quan tốt giữa hai điểm dữ liệu của bạn để có thể xây dựng một mô hình dự đoán tốt. Chúng ta sẽ sử dụng hàm `cor()` để làm điều này. Gõ `?cor()` để tìm hiểu thêm về hàm này.\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": [ "Hóa ra, mối tương quan giữa Thành phố và Giá chỉ ở mức yếu. Tuy nhiên, có một mối tương quan tốt hơn giữa Gói hàng và Giá của nó. Điều này hợp lý, đúng không? Thông thường, hộp sản phẩm càng lớn thì giá càng cao.\n", "\n", "Nhân tiện, hãy thử hình dung ma trận tương quan của tất cả các cột bằng cách sử dụng gói `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": [ "🤩🤩 Tuyệt vời hơn nhiều.\n", "\n", "Một câu hỏi hay để đặt ra từ dữ liệu này sẽ là: '`Giá mà tôi có thể mong đợi cho một gói bí ngô nhất định là bao nhiêu?`' Hãy bắt đầu ngay thôi!\n", "\n", "> Lưu ý: Khi bạn **`bake()`** công thức đã chuẩn bị **`pumpkins_prep`** với **`new_data = NULL`**, bạn sẽ trích xuất dữ liệu huấn luyện đã được xử lý (tức là đã được mã hóa). Nếu bạn có một tập dữ liệu khác, ví dụ như tập kiểm tra, và muốn xem cách công thức xử lý trước nó, bạn chỉ cần bake **`pumpkins_prep`** với **`new_data = test_set`**\n", "\n", "## 4. Xây dựng mô hình hồi quy tuyến tính\n", "\n", "

\n", " \n", "

Đồ họa thông tin bởi Dasani Madipalli
\n", "\n", "\n", "\n" ], "metadata": { "id": "YqXjLuWavNxW" } }, { "cell_type": "markdown", "source": [ "Bây giờ chúng ta đã xây dựng một công thức và thực sự xác nhận rằng dữ liệu sẽ được tiền xử lý một cách phù hợp, hãy cùng xây dựng một mô hình hồi quy để trả lời câu hỏi: `Giá của một gói bí ngô cụ thể sẽ là bao nhiêu?`\n", "\n", "#### Huấn luyện mô hình hồi quy tuyến tính bằng tập huấn luyện\n", "\n", "Như bạn có thể đã nhận ra, cột *price* là biến `kết quả` trong khi cột *package* là biến `dự đoán`.\n", "\n", "Để thực hiện điều này, trước tiên chúng ta sẽ chia dữ liệu sao cho 80% vào tập huấn luyện và 20% vào tập kiểm tra, sau đó định nghĩa một công thức để mã hóa cột dự đoán thành một tập hợp các số nguyên, rồi xây dựng một đặc tả mô hình. Chúng ta sẽ không chuẩn bị và nướng công thức của mình vì đã biết rằng nó sẽ tiền xử lý dữ liệu như mong đợi.\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": [ "Làm tốt lắm! Bây giờ chúng ta đã có một công thức và một mô tả mô hình, chúng ta cần tìm cách kết hợp chúng lại thành một đối tượng để trước tiên xử lý dữ liệu (prep+bake phía sau hậu trường), huấn luyện mô hình trên dữ liệu đã được xử lý và cũng cho phép các hoạt động xử lý hậu kỳ tiềm năng. Thật tuyệt vời để bạn yên tâm, đúng không!🤩\n", "\n", "Trong Tidymodels, đối tượng tiện lợi này được gọi là [`workflow`](https://workflows.tidymodels.org/) và nó tiện lợi lưu giữ các thành phần mô hình của bạn! Đây là thứ mà chúng ta gọi là *pipelines* trong *Python*.\n", "\n", "Vậy hãy gói gọn mọi thứ vào một workflow nhé!📦\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": [ "👌 Thêm vào đó, một quy trình làm việc có thể được điều chỉnh/huấn luyện theo cách tương tự như một mô hình.\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": [ "Từ đầu ra của mô hình, chúng ta có thể thấy các hệ số được học trong quá trình huấn luyện. Chúng đại diện cho các hệ số của đường hồi quy tốt nhất, giúp giảm thiểu tổng lỗi giữa biến thực tế và biến dự đoán.\n", "\n", "#### Đánh giá hiệu suất mô hình bằng tập kiểm tra\n", "\n", "Đã đến lúc xem mô hình hoạt động như thế nào 📏! Chúng ta làm điều này như thế nào?\n", "\n", "Bây giờ chúng ta đã huấn luyện xong mô hình, có thể sử dụng nó để đưa ra dự đoán cho `test_set` bằng cách dùng `parsnip::predict()`. Sau đó, chúng ta có thể so sánh các dự đoán này với các giá trị nhãn thực tế để đánh giá mức độ hiệu quả (hoặc không hiệu quả!) của mô hình.\n", "\n", "Hãy bắt đầu bằng cách tạo dự đoán cho tập kiểm tra, sau đó gắn các cột vào tập kiểm tra.\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": [ "Vâng, bạn vừa huấn luyện một mô hình và sử dụng nó để đưa ra dự đoán! 🔮 Nó có tốt không? Hãy đánh giá hiệu suất của mô hình nhé!\n", "\n", "Trong Tidymodels, chúng ta thực hiện việc này bằng cách sử dụng `yardstick::metrics()`! Đối với hồi quy tuyến tính, hãy tập trung vào các chỉ số sau:\n", "\n", "- `Root Mean Square Error (RMSE)`: Căn bậc hai của [MSE](https://en.wikipedia.org/wiki/Mean_squared_error). Đây là một chỉ số tuyệt đối có cùng đơn vị với nhãn (trong trường hợp này là giá của một quả bí ngô). Giá trị càng nhỏ, mô hình càng tốt (hiểu một cách đơn giản, nó đại diện cho mức giá trung bình mà dự đoán bị sai lệch!).\n", "\n", "- `Coefficient of Determination (thường được gọi là R-squared hoặc R2)`: Một chỉ số tương đối, trong đó giá trị càng cao, mô hình càng phù hợp. Về cơ bản, chỉ số này thể hiện mức độ mà mô hình có thể giải thích được sự biến thiên giữa giá trị dự đoán và giá trị thực tế của nhãn.\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": [ "Hiệu suất của mô hình giảm đi. Hãy xem liệu chúng ta có thể có được một chỉ báo tốt hơn bằng cách trực quan hóa biểu đồ phân tán của gói hàng và giá, sau đó sử dụng các dự đoán đã thực hiện để vẽ một đường phù hợp nhất.\n", "\n", "Điều này có nghĩa là chúng ta sẽ phải chuẩn bị và xử lý tập kiểm tra để mã hóa cột gói hàng, sau đó kết hợp nó với các dự đoán được tạo ra bởi mô hình của chúng ta.\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": [ "Thật tuyệt! Như bạn có thể thấy, mô hình hồi quy tuyến tính không thực sự khái quát tốt mối quan hệ giữa một gói hàng và giá tương ứng của nó.\n", "\n", "🎃 Chúc mừng, bạn vừa tạo ra một mô hình có thể giúp dự đoán giá của một vài loại bí ngô. Vườn bí ngô cho kỳ nghỉ của bạn sẽ thật đẹp. Nhưng có lẽ bạn có thể tạo ra một mô hình tốt hơn!\n", "\n", "## 5. Xây dựng mô hình hồi quy đa thức\n", "\n", "

\n", " \n", "

Đồ họa thông tin bởi Dasani Madipalli
\n", "\n", "\n", "\n" ], "metadata": { "id": "HOCqJXLTwtWI" } }, { "cell_type": "markdown", "source": [ "Đôi khi dữ liệu của chúng ta không có mối quan hệ tuyến tính, nhưng chúng ta vẫn muốn dự đoán kết quả. Hồi quy đa thức có thể giúp chúng ta đưa ra dự đoán cho các mối quan hệ phi tuyến phức tạp hơn.\n", "\n", "Hãy xem xét mối quan hệ giữa kích thước gói và giá cả trong bộ dữ liệu bí ngô của chúng ta. Mặc dù đôi khi có mối quan hệ tuyến tính giữa các biến - bí ngô có thể tích lớn hơn thì giá cao hơn - nhưng đôi khi những mối quan hệ này không thể được biểu diễn dưới dạng mặt phẳng hoặc đường thẳng.\n", "\n", "> ✅ Đây là [một số ví dụ khác](https://online.stat.psu.edu/stat501/lesson/9/9.8) về dữ liệu có thể sử dụng hồi quy đa thức \n", "> \n", "> Hãy xem lại mối quan hệ giữa Loại bí ngô và Giá cả trong biểu đồ trước đó. Biểu đồ phân tán này có nhất thiết phải được phân tích bằng một đường thẳng không? Có lẽ là không. Trong trường hợp này, bạn có thể thử hồi quy đa thức. \n", "> \n", "> ✅ Đa thức là các biểu thức toán học có thể bao gồm một hoặc nhiều biến và hệ số \n", "\n", "#### Huấn luyện mô hình hồi quy đa thức bằng tập dữ liệu huấn luyện\n", "\n", "Hồi quy đa thức tạo ra một *đường cong* để phù hợp hơn với dữ liệu phi tuyến.\n", "\n", "Hãy xem liệu mô hình đa thức có hoạt động tốt hơn trong việc đưa ra dự đoán hay không. Chúng ta sẽ thực hiện quy trình tương tự như trước:\n", "\n", "- Tạo một công thức chỉ định các bước tiền xử lý cần thực hiện trên dữ liệu để chuẩn bị cho việc mô hình hóa, ví dụ: mã hóa các biến dự đoán và tính toán đa thức với bậc *n*\n", "\n", "- Xây dựng một mô hình cụ thể\n", "\n", "- Kết hợp công thức và mô hình cụ thể vào một quy trình làm việc\n", "\n", "- Tạo mô hình bằng cách khớp quy trình làm việc\n", "\n", "- Đánh giá mức độ hiệu quả của mô hình trên dữ liệu kiểm tra\n", "\n", "Hãy bắt đầu ngay thôi!\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": [ "#### Đánh giá hiệu suất mô hình\n", "\n", "👏👏Bạn đã xây dựng một mô hình đa thức, hãy thực hiện dự đoán trên tập kiểm tra!\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": [ "Woo-hoo, hãy đánh giá cách mô hình hoạt động trên test_set bằng cách sử dụng `yardstick::metrics()`.\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": [ "🤩🤩 Hiệu suất tốt hơn nhiều.\n", "\n", "`rmse` đã giảm từ khoảng 7 xuống khoảng 3, cho thấy lỗi giữa giá thực tế và giá dự đoán đã giảm. Bạn có thể *hiểu một cách đơn giản* rằng trung bình, các dự đoán sai lệch khoảng \\$3. `rsq` đã tăng từ khoảng 0.4 lên 0.8.\n", "\n", "Tất cả các chỉ số này đều cho thấy rằng mô hình đa thức hoạt động tốt hơn nhiều so với mô hình tuyến tính. Làm tốt lắm!\n", "\n", "Hãy xem liệu chúng ta có thể trực quan hóa điều này không!\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": [ "Bạn có thể thấy một đường cong phù hợp với dữ liệu của mình tốt hơn! 🤩\n", "\n", "Bạn có thể làm cho đường này mượt mà hơn bằng cách truyền một công thức đa thức vào `geom_smooth` như sau:\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": [ "Giống như một đường cong mượt mà!🤩\n", "\n", "Đây là cách bạn tạo một dự đoán mới:\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": [ "Dự đoán của `polynomial model` thực sự hợp lý, dựa trên biểu đồ phân tán của `price` và `package`! Và nếu đây là một mô hình tốt hơn so với mô hình trước đó, khi nhìn vào cùng một dữ liệu, bạn cần dự trù ngân sách cho những quả bí ngô đắt tiền hơn này!\n", "\n", "🏆 Làm tốt lắm! Bạn đã tạo hai mô hình hồi quy trong một bài học. Trong phần cuối cùng về hồi quy, bạn sẽ học về hồi quy logistic để xác định các danh mục.\n", "\n", "## **🚀Thử thách**\n", "\n", "Kiểm tra một số biến khác nhau trong notebook này để xem cách mối tương quan ảnh hưởng đến độ chính xác của mô hình.\n", "\n", "## [**Câu hỏi sau bài giảng**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/14/)\n", "\n", "## **Ôn tập & Tự học**\n", "\n", "Trong bài học này, chúng ta đã học về Hồi quy tuyến tính. Có những loại hồi quy quan trọng khác. Hãy tìm hiểu về các kỹ thuật Stepwise, Ridge, Lasso và Elasticnet. Một khóa học hay để học thêm là [Khóa học Stanford Statistical Learning](https://online.stanford.edu/courses/sohs-ystatslearning-statistical-learning).\n", "\n", "Nếu bạn muốn tìm hiểu thêm về cách sử dụng framework Tidymodels tuyệt vời, hãy tham khảo các tài nguyên sau:\n", "\n", "- Trang web Tidymodels: [Bắt đầu với Tidymodels](https://www.tidymodels.org/start/)\n", "\n", "- Max Kuhn và Julia Silge, [*Tidy Modeling with R*](https://www.tmwr.org/)*.*\n", "\n", "###### **CẢM ƠN ĐẾN:**\n", "\n", "[Allison Horst](https://twitter.com/allison_horst?lang=en) vì đã tạo ra những hình minh họa tuyệt vời giúp R trở nên thân thiện và hấp dẫn hơn. Tìm thêm các hình minh họa tại [bộ sưu tập của cô ấy](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**Tuyên bố miễn trừ trách nhiệm**: \nTài liệu này đã được dịch bằng dịch vụ dịch thuật AI [Co-op Translator](https://github.com/Azure/co-op-translator). Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng các bản dịch tự động có thể chứa lỗi hoặc không chính xác. Tài liệu gốc bằng ngôn ngữ bản địa nên được coi là nguồn tham khảo chính thức. Đối với các thông tin quan trọng, chúng tôi khuyến nghị sử dụng dịch vụ dịch thuật chuyên nghiệp từ con người. Chúng tôi không chịu trách nhiệm cho bất kỳ sự hiểu lầm hoặc diễn giải sai nào phát sinh từ việc sử dụng bản dịch này.\n" ] } ] }