You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
686 lines
31 KiB
686 lines
31 KiB
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Construir um modelo de regressão logística - Aula 4\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"#### **[Questionário pré-aula](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/15/)**\n",
|
|
"\n",
|
|
"#### Introdução\n",
|
|
"\n",
|
|
"Nesta última aula sobre Regressão, uma das técnicas básicas *clássicas* de ML, vamos explorar a Regressão Logística. Esta técnica é usada para descobrir padrões e prever categorias binárias. Este doce é chocolate ou não? Esta doença é contagiosa ou não? Este cliente vai escolher este produto ou não?\n",
|
|
"\n",
|
|
"Nesta aula, irá aprender:\n",
|
|
"\n",
|
|
"- Técnicas para regressão logística\n",
|
|
"\n",
|
|
"✅ Aprofunde o seu entendimento sobre como trabalhar com este tipo de regressão neste [módulo de aprendizagem](https://learn.microsoft.com/training/modules/introduction-classification-models/?WT.mc_id=academic-77952-leestott)\n",
|
|
"\n",
|
|
"## Pré-requisito\n",
|
|
"\n",
|
|
"Depois de trabalhar com os dados das abóboras, já estamos suficientemente familiarizados para perceber que há uma categoria binária com a qual podemos trabalhar: `Color`.\n",
|
|
"\n",
|
|
"Vamos construir um modelo de regressão logística para prever, com base em algumas variáveis, *qual é a cor provável de uma determinada abóbora* (laranja 🎃 ou branca 👻).\n",
|
|
"\n",
|
|
"> Por que estamos a falar de classificação binária numa aula sobre regressão? Apenas por conveniência linguística, já que a regressão logística é [na verdade um método de classificação](https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression), embora baseado em métodos lineares. Aprenda sobre outras formas de classificar dados no próximo grupo de aulas.\n",
|
|
"\n",
|
|
"Para esta aula, vamos precisar dos seguintes pacotes:\n",
|
|
"\n",
|
|
"- `tidyverse`: O [tidyverse](https://www.tidyverse.org/) é uma [coleção de pacotes R](https://www.tidyverse.org/packages) projetada para tornar a ciência de dados mais rápida, fácil e divertida!\n",
|
|
"\n",
|
|
"- `tidymodels`: O [tidymodels](https://www.tidymodels.org/) é uma [coleção de pacotes](https://www.tidymodels.org/packages/) para modelagem e aprendizagem automática.\n",
|
|
"\n",
|
|
"- `janitor`: O pacote [janitor](https://github.com/sfirke/janitor) oferece ferramentas simples para examinar e limpar dados desorganizados.\n",
|
|
"\n",
|
|
"- `ggbeeswarm`: O pacote [ggbeeswarm](https://github.com/eclarke/ggbeeswarm) fornece métodos para criar gráficos estilo \"beeswarm\" usando ggplot2.\n",
|
|
"\n",
|
|
"Pode instalá-los com o seguinte comando:\n",
|
|
"\n",
|
|
"`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"ggbeeswarm\"))`\n",
|
|
"\n",
|
|
"Alternativamente, o script abaixo verifica se tem os pacotes necessários para completar este módulo e instala-os caso estejam em falta.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"suppressWarnings(if (!require(\"pacman\"))install.packages(\"pacman\"))\n",
|
|
"\n",
|
|
"pacman::p_load(tidyverse, tidymodels, janitor, ggbeeswarm)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## **Definir a pergunta**\n",
|
|
"\n",
|
|
"Para os nossos propósitos, vamos expressar isto como um binário: 'Branco' ou 'Não Branco'. Existe também uma categoria 'listado' no nosso conjunto de dados, mas há poucos exemplos dela, por isso não a utilizaremos. De qualquer forma, ela desaparece quando removemos os valores nulos do conjunto de dados.\n",
|
|
"\n",
|
|
"> 🎃 Curiosidade: às vezes chamamos as abóboras brancas de abóboras 'fantasma'. Elas não são muito fáceis de esculpir, por isso não são tão populares quanto as laranjas, mas têm um visual interessante! Assim, poderíamos reformular a nossa pergunta como: 'Fantasma' ou 'Não Fantasma'. 👻\n",
|
|
"\n",
|
|
"## **Sobre regressão logística**\n",
|
|
"\n",
|
|
"A regressão logística difere da regressão linear, que você aprendeu anteriormente, em alguns aspectos importantes.\n",
|
|
"\n",
|
|
"#### **Classificação binária**\n",
|
|
"\n",
|
|
"A regressão logística não oferece as mesmas funcionalidades que a regressão linear. A primeira fornece uma previsão sobre uma `categoria binária` (\"laranja ou não laranja\"), enquanto a segunda é capaz de prever `valores contínuos`, por exemplo, dado a origem de uma abóbora e o momento da colheita, *quanto o seu preço irá aumentar*.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"### Outras classificações\n",
|
|
"\n",
|
|
"Existem outros tipos de regressão logística, incluindo multinomial e ordinal:\n",
|
|
"\n",
|
|
"- **Multinomial**, que envolve mais de uma categoria - \"Laranja, Branco e Listado\".\n",
|
|
"\n",
|
|
"- **Ordinal**, que envolve categorias ordenadas, útil se quisermos organizar os resultados logicamente, como as nossas abóboras que são ordenadas por um número finito de tamanhos (mini,pequeno,médio,grande,xl,xxl).\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"#### **As variáveis NÃO precisam estar correlacionadas**\n",
|
|
"\n",
|
|
"Lembra como a regressão linear funcionava melhor com variáveis mais correlacionadas? A regressão logística é o oposto - as variáveis não precisam estar alinhadas. Isso funciona para este conjunto de dados, que tem correlações relativamente fracas.\n",
|
|
"\n",
|
|
"#### **Você precisa de muitos dados limpos**\n",
|
|
"\n",
|
|
"A regressão logística fornecerá resultados mais precisos se você usar mais dados; nosso pequeno conjunto de dados não é ideal para esta tarefa, então tenha isso em mente.\n",
|
|
"\n",
|
|
"✅ Pense nos tipos de dados que seriam adequados para regressão logística\n",
|
|
"\n",
|
|
"## Exercício - organizar os dados\n",
|
|
"\n",
|
|
"Primeiro, limpe os dados um pouco, removendo valores nulos e selecionando apenas algumas das colunas:\n",
|
|
"\n",
|
|
"1. Adicione o seguinte código:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Load the core tidyverse packages\n",
|
|
"library(tidyverse)\n",
|
|
"\n",
|
|
"# Import the data and clean column names\n",
|
|
"pumpkins <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/2-Regression/data/US-pumpkins.csv\") %>% \n",
|
|
" clean_names()\n",
|
|
"\n",
|
|
"# Select desired columns\n",
|
|
"pumpkins_select <- pumpkins %>% \n",
|
|
" select(c(city_name, package, variety, origin, item_size, color)) \n",
|
|
"\n",
|
|
"# Drop rows containing missing values and encode color as factor (category)\n",
|
|
"pumpkins_select <- pumpkins_select %>% \n",
|
|
" drop_na() %>% \n",
|
|
" mutate(color = factor(color))\n",
|
|
"\n",
|
|
"# View the first few rows\n",
|
|
"pumpkins_select %>% \n",
|
|
" slice_head(n = 5)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Pode sempre dar uma olhadela ao seu novo dataframe utilizando a função [*glimpse()*](https://pillar.r-lib.org/reference/glimpse.html), como mostrado abaixo:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"pumpkins_select %>% \n",
|
|
" glimpse()\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Vamos confirmar que, de facto, estaremos a resolver um problema de classificação binária:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Subset distinct observations in outcome column\n",
|
|
"pumpkins_select %>% \n",
|
|
" distinct(color)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Visualização - gráfico categórico\n",
|
|
"Neste momento, já carregaste novamente os dados das abóboras e limpaste-os para preservar um conjunto de dados contendo algumas variáveis, incluindo Cor. Vamos visualizar o dataframe no notebook utilizando a biblioteca ggplot.\n",
|
|
"\n",
|
|
"A biblioteca ggplot oferece algumas formas interessantes de visualizar os teus dados. Por exemplo, podes comparar as distribuições dos dados para cada Variedade e Cor num gráfico categórico.\n",
|
|
"\n",
|
|
"1. Cria um gráfico deste tipo utilizando a função geombar, com os dados das abóboras, e especifica um mapeamento de cores para cada categoria de abóbora (laranja ou branca):\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "python"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Specify colors for each value of the hue variable\n",
|
|
"palette <- c(ORANGE = \"orange\", WHITE = \"wheat\")\n",
|
|
"\n",
|
|
"# Create the bar plot\n",
|
|
"ggplot(pumpkins_select, aes(y = variety, fill = color)) +\n",
|
|
" geom_bar(position = \"dodge\") +\n",
|
|
" scale_fill_manual(values = palette) +\n",
|
|
" labs(y = \"Variety\", fill = \"Color\") +\n",
|
|
" theme_minimal()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Ao observar os dados, pode ver como os dados de Cor se relacionam com a Variedade.\n",
|
|
"\n",
|
|
"✅ Dado este gráfico categórico, que explorações interessantes consegue imaginar?\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Pré-processamento de dados: codificação de características\n",
|
|
"\n",
|
|
"O nosso conjunto de dados de abóboras contém valores em formato de texto para todas as suas colunas. Trabalhar com dados categóricos é intuitivo para humanos, mas não para máquinas. Os algoritmos de aprendizagem automática funcionam bem com números. É por isso que a codificação é um passo muito importante na fase de pré-processamento de dados, pois permite transformar dados categóricos em dados numéricos, sem perder nenhuma informação. Uma boa codificação contribui para a construção de um bom modelo.\n",
|
|
"\n",
|
|
"Para a codificação de características, existem dois tipos principais de codificadores:\n",
|
|
"\n",
|
|
"1. Codificador ordinal: é adequado para variáveis ordinais, que são variáveis categóricas cujos dados seguem uma ordem lógica, como a coluna `item_size` no nosso conjunto de dados. Ele cria um mapeamento em que cada categoria é representada por um número, que corresponde à ordem da categoria na coluna.\n",
|
|
"\n",
|
|
"2. Codificador categórico: é adequado para variáveis nominais, que são variáveis categóricas cujos dados não seguem uma ordem lógica, como todas as características diferentes de `item_size` no nosso conjunto de dados. Trata-se de uma codificação one-hot, o que significa que cada categoria é representada por uma coluna binária: a variável codificada é igual a 1 se a abóbora pertence àquela variedade e 0 caso contrário.\n",
|
|
"\n",
|
|
"O Tidymodels oferece mais uma ferramenta interessante: [recipes](https://recipes.tidymodels.org/) - um pacote para pré-processamento de dados. Vamos definir uma `recipe` que especifica que todas as colunas preditoras devem ser codificadas em um conjunto de números inteiros, `prep` para estimar as quantidades e estatísticas necessárias para qualquer operação e, finalmente, `bake` para aplicar os cálculos a novos dados.\n",
|
|
"\n",
|
|
"> Normalmente, o recipes é usado como um pré-processador para modelagem, onde define quais passos devem ser aplicados a um conjunto de dados para prepará-lo para a modelagem. Nesse caso, é **altamente recomendado** que utilize um `workflow()` em vez de estimar manualmente uma receita usando prep e bake. Veremos tudo isso em breve.\n",
|
|
">\n",
|
|
"> No entanto, por agora, estamos a usar recipes + prep + bake para especificar quais passos devem ser aplicados a um conjunto de dados para prepará-lo para análise de dados e, em seguida, extrair os dados pré-processados com os passos aplicados.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Preprocess and extract data to allow some data analysis\n",
|
|
"baked_pumpkins <- recipe(color ~ ., data = pumpkins_select) %>%\n",
|
|
" # Define ordering for item_size column\n",
|
|
" step_mutate(item_size = ordered(item_size, levels = c('sml', 'med', 'med-lge', 'lge', 'xlge', 'jbo', 'exjbo'))) %>%\n",
|
|
" # Convert factors to numbers using the order defined above (Ordinal encoding)\n",
|
|
" step_integer(item_size, zero_based = F) %>%\n",
|
|
" # Encode all other predictors using one hot encoding\n",
|
|
" step_dummy(all_nominal(), -all_outcomes(), one_hot = TRUE) %>%\n",
|
|
" prep(data = pumpkin_select) %>%\n",
|
|
" bake(new_data = NULL)\n",
|
|
"\n",
|
|
"# Display the first few rows of preprocessed data\n",
|
|
"baked_pumpkins %>% \n",
|
|
" slice_head(n = 5)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"✅ Quais são as vantagens de usar um codificador ordinal para a coluna Item Size?\n",
|
|
"\n",
|
|
"### Analisar relações entre variáveis\n",
|
|
"\n",
|
|
"Agora que pré-processámos os nossos dados, podemos analisar as relações entre as características e o rótulo para ter uma ideia de quão bem o modelo será capaz de prever o rótulo com base nas características. A melhor forma de realizar este tipo de análise é através da visualização dos dados. \n",
|
|
"Vamos utilizar novamente a função ggplot geom_boxplot_ para visualizar as relações entre Item Size, Variety e Color num gráfico categórico. Para representar melhor os dados, utilizaremos a coluna codificada Item Size e a coluna não codificada Variety.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Define the color palette\n",
|
|
"palette <- c(ORANGE = \"orange\", WHITE = \"wheat\")\n",
|
|
"\n",
|
|
"# We need the encoded Item Size column to use it as the x-axis values in the plot\n",
|
|
"pumpkins_select_plot<-pumpkins_select\n",
|
|
"pumpkins_select_plot$item_size <- baked_pumpkins$item_size\n",
|
|
"\n",
|
|
"# Create the grouped box plot\n",
|
|
"ggplot(pumpkins_select_plot, aes(x = `item_size`, y = color, fill = color)) +\n",
|
|
" geom_boxplot() +\n",
|
|
" facet_grid(variety ~ ., scales = \"free_x\") +\n",
|
|
" scale_fill_manual(values = palette) +\n",
|
|
" labs(x = \"Item Size\", y = \"\") +\n",
|
|
" theme_minimal() +\n",
|
|
" theme(strip.text = element_text(size = 12)) +\n",
|
|
" theme(axis.text.x = element_text(size = 10)) +\n",
|
|
" theme(axis.title.x = element_text(size = 12)) +\n",
|
|
" theme(axis.title.y = element_blank()) +\n",
|
|
" theme(legend.position = \"bottom\") +\n",
|
|
" guides(fill = guide_legend(title = \"Color\")) +\n",
|
|
" theme(panel.spacing = unit(0.5, \"lines\"))+\n",
|
|
" theme(strip.text.y = element_text(size = 4, hjust = 0)) \n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"#### Usar um gráfico de enxame\n",
|
|
"\n",
|
|
"Como a cor é uma categoria binária (Branco ou Não), é necessário 'uma [abordagem especializada](https://github.com/rstudio/cheatsheets/blob/main/data-visualization.pdf) para visualização'.\n",
|
|
"\n",
|
|
"Experimente um `gráfico de enxame` para mostrar a distribuição da cor em relação ao tamanho do item.\n",
|
|
"\n",
|
|
"Vamos usar o [pacote ggbeeswarm](https://github.com/eclarke/ggbeeswarm), que fornece métodos para criar gráficos no estilo enxame usando ggplot2. Gráficos de enxame são uma forma de plotar pontos que normalmente se sobreporiam, posicionando-os lado a lado.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Create beeswarm plots of color and item_size\n",
|
|
"baked_pumpkins %>% \n",
|
|
" mutate(color = factor(color)) %>% \n",
|
|
" ggplot(mapping = aes(x = color, y = item_size, color = color)) +\n",
|
|
" geom_quasirandom() +\n",
|
|
" scale_color_brewer(palette = \"Dark2\", direction = -1) +\n",
|
|
" theme(legend.position = \"none\")\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Agora que temos uma ideia da relação entre as categorias binárias de cor e o grupo maior de tamanhos, vamos explorar a regressão logística para determinar a provável cor de uma abóbora.\n",
|
|
"\n",
|
|
"## Construir o seu modelo\n",
|
|
"\n",
|
|
"Selecione as variáveis que deseja usar no seu modelo de classificação e divida os dados em conjuntos de treino e teste. [rsample](https://rsample.tidymodels.org/), um pacote do Tidymodels, fornece infraestrutura para uma divisão e reamostragem de dados eficiente:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Split data into 80% for training and 20% for testing\n",
|
|
"set.seed(2056)\n",
|
|
"pumpkins_split <- pumpkins_select %>% \n",
|
|
" initial_split(prop = 0.8)\n",
|
|
"\n",
|
|
"# Extract the data in each split\n",
|
|
"pumpkins_train <- training(pumpkins_split)\n",
|
|
"pumpkins_test <- testing(pumpkins_split)\n",
|
|
"\n",
|
|
"# Print out the first 5 rows of the training set\n",
|
|
"pumpkins_train %>% \n",
|
|
" slice_head(n = 5)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"🙌 Estamos prontos para treinar um modelo ajustando as características de treino ao rótulo de treino (cor).\n",
|
|
"\n",
|
|
"Vamos começar por criar uma receita que especifica os passos de pré-processamento que devem ser realizados nos nossos dados para os preparar para a modelação, ou seja: codificar variáveis categóricas em um conjunto de inteiros. Tal como `baked_pumpkins`, criamos uma `pumpkins_recipe`, mas não usamos `prep` e `bake`, já que isso será incorporado num fluxo de trabalho, como verá em apenas alguns passos.\n",
|
|
"\n",
|
|
"Existem várias formas de especificar um modelo de regressão logística no Tidymodels. Consulte `?logistic_reg()`. Por agora, vamos especificar um modelo de regressão logística através do motor padrão `stats::glm()`.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Create a recipe that specifies preprocessing steps for modelling\n",
|
|
"pumpkins_recipe <- recipe(color ~ ., data = pumpkins_train) %>% \n",
|
|
" step_mutate(item_size = ordered(item_size, levels = c('sml', 'med', 'med-lge', 'lge', 'xlge', 'jbo', 'exjbo'))) %>%\n",
|
|
" step_integer(item_size, zero_based = F) %>% \n",
|
|
" step_dummy(all_nominal(), -all_outcomes(), one_hot = TRUE)\n",
|
|
"\n",
|
|
"# Create a logistic model specification\n",
|
|
"log_reg <- logistic_reg() %>% \n",
|
|
" set_engine(\"glm\") %>% \n",
|
|
" set_mode(\"classification\")\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Agora que temos uma receita e uma especificação do modelo, precisamos encontrar uma forma de combiná-las num único objeto que, primeiro, pré-processe os dados (prep+bake nos bastidores), ajuste o modelo aos dados pré-processados e também permita possíveis atividades de pós-processamento.\n",
|
|
"\n",
|
|
"No Tidymodels, este objeto prático é chamado de [`workflow`](https://workflows.tidymodels.org/) e armazena convenientemente os seus componentes de modelação.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Bundle modelling components in a workflow\n",
|
|
"log_reg_wf <- workflow() %>% \n",
|
|
" add_recipe(pumpkins_recipe) %>% \n",
|
|
" add_model(log_reg)\n",
|
|
"\n",
|
|
"# Print out the workflow\n",
|
|
"log_reg_wf\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Depois de um fluxo de trabalho ter sido *especificado*, um modelo pode ser `treinado` utilizando a função [`fit()`](https://tidymodels.github.io/parsnip/reference/fit.html). O fluxo de trabalho irá estimar uma receita e pré-processar os dados antes do treino, por isso não será necessário fazer isso manualmente utilizando prep e bake.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Train the model\n",
|
|
"wf_fit <- log_reg_wf %>% \n",
|
|
" fit(data = pumpkins_train)\n",
|
|
"\n",
|
|
"# Print the trained workflow\n",
|
|
"wf_fit\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"O modelo imprime os coeficientes aprendidos durante o treino.\n",
|
|
"\n",
|
|
"Agora que treinámos o modelo utilizando os dados de treino, podemos fazer previsões nos dados de teste usando [parsnip::predict()](https://parsnip.tidymodels.org/reference/predict.model_fit.html). Vamos começar por usar o modelo para prever etiquetas para o nosso conjunto de teste e as probabilidades para cada etiqueta. Quando a probabilidade é superior a 0.5, a classe prevista é `WHITE`, caso contrário, é `ORANGE`.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Make predictions for color and corresponding probabilities\n",
|
|
"results <- pumpkins_test %>% select(color) %>% \n",
|
|
" bind_cols(wf_fit %>% \n",
|
|
" predict(new_data = pumpkins_test)) %>%\n",
|
|
" bind_cols(wf_fit %>%\n",
|
|
" predict(new_data = pumpkins_test, type = \"prob\"))\n",
|
|
"\n",
|
|
"# Compare predictions\n",
|
|
"results %>% \n",
|
|
" slice_head(n = 10)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Muito bom! Isto fornece mais detalhes sobre como funciona a regressão logística.\n",
|
|
"\n",
|
|
"### Melhor compreensão através de uma matriz de confusão\n",
|
|
"\n",
|
|
"Comparar cada previsão com o respetivo valor real (\"ground truth\") não é uma forma muito eficiente de determinar o quão bem o modelo está a prever. Felizmente, o Tidymodels tem mais algumas ferramentas úteis: [`yardstick`](https://yardstick.tidymodels.org/) - um pacote utilizado para medir a eficácia dos modelos através de métricas de desempenho.\n",
|
|
"\n",
|
|
"Uma métrica de desempenho associada a problemas de classificação é a [`matriz de confusão`](https://wikipedia.org/wiki/Confusion_matrix). Uma matriz de confusão descreve o desempenho de um modelo de classificação. Ela tabula quantos exemplos de cada classe foram corretamente classificados por um modelo. No nosso caso, mostrará quantas abóboras laranjas foram classificadas como laranjas e quantas abóboras brancas foram classificadas como brancas; a matriz de confusão também indica quantas foram classificadas nas categorias **erradas**.\n",
|
|
"\n",
|
|
"A função [**`conf_mat()`**](https://tidymodels.github.io/yardstick/reference/conf_mat.html) do yardstick calcula esta tabela cruzada de classes observadas e previstas.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Confusion matrix for prediction results\n",
|
|
"conf_mat(data = results, truth = color, estimate = .pred_class)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Vamos interpretar a matriz de confusão. O nosso modelo foi solicitado a classificar abóboras entre duas categorias binárias, categoria `branca` e categoria `não-branca`.\n",
|
|
"\n",
|
|
"- Se o seu modelo prevê que uma abóbora é branca e ela pertence à categoria 'branca' na realidade, chamamos isso de um `verdadeiro positivo`, representado pelo número no canto superior esquerdo.\n",
|
|
"\n",
|
|
"- Se o seu modelo prevê que uma abóbora não é branca e ela pertence à categoria 'branca' na realidade, chamamos isso de um `falso negativo`, representado pelo número no canto inferior esquerdo.\n",
|
|
"\n",
|
|
"- Se o seu modelo prevê que uma abóbora é branca e ela pertence à categoria 'não-branca' na realidade, chamamos isso de um `falso positivo`, representado pelo número no canto superior direito.\n",
|
|
"\n",
|
|
"- Se o seu modelo prevê que uma abóbora não é branca e ela pertence à categoria 'não-branca' na realidade, chamamos isso de um `verdadeiro negativo`, representado pelo número no canto inferior direito.\n",
|
|
"\n",
|
|
"| Verdade |\n",
|
|
"|:-------:|\n",
|
|
"\n",
|
|
"\n",
|
|
"| | | |\n",
|
|
"|---------------|--------|-------|\n",
|
|
"| **Previsto** | BRANCA | LARANJA |\n",
|
|
"| BRANCA | VP | FP |\n",
|
|
"| LARANJA | FN | VN |\n",
|
|
"\n",
|
|
"Como pode imaginar, é preferível ter um número maior de verdadeiros positivos e verdadeiros negativos e um número menor de falsos positivos e falsos negativos, o que implica que o modelo tem um desempenho melhor.\n",
|
|
"\n",
|
|
"A matriz de confusão é útil porque dá origem a outras métricas que podem ajudar-nos a avaliar melhor o desempenho de um modelo de classificação. Vamos analisar algumas delas:\n",
|
|
"\n",
|
|
"🎓 Precisão: `VP/(VP + FP)` definida como a proporção de positivos previstos que são realmente positivos. Também chamada de [valor preditivo positivo](https://en.wikipedia.org/wiki/Positive_predictive_value \"Positive predictive value\").\n",
|
|
"\n",
|
|
"🎓 Recall: `VP/(VP + FN)` definida como a proporção de resultados positivos em relação ao número de amostras que eram realmente positivas. Também conhecida como `sensibilidade`.\n",
|
|
"\n",
|
|
"🎓 Especificidade: `VN/(VN + FP)` definida como a proporção de resultados negativos em relação ao número de amostras que eram realmente negativas.\n",
|
|
"\n",
|
|
"🎓 Exatidão: `(VP + VN)/(VP + VN + FP + FN)` A percentagem de etiquetas previstas corretamente para uma amostra.\n",
|
|
"\n",
|
|
"🎓 Medida F: Uma média ponderada da precisão e do recall, sendo o melhor valor 1 e o pior valor 0.\n",
|
|
"\n",
|
|
"Vamos calcular estas métricas!\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Combine metric functions and calculate them all at once\n",
|
|
"eval_metrics <- metric_set(ppv, recall, spec, f_meas, accuracy)\n",
|
|
"eval_metrics(data = results, truth = color, estimate = .pred_class)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Visualizar a curva ROC deste modelo\n",
|
|
"\n",
|
|
"Vamos fazer mais uma visualização para observar a chamada [`curva ROC`](https://en.wikipedia.org/wiki/Receiver_operating_characteristic):\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Make a roc_curve\n",
|
|
"results %>% \n",
|
|
" roc_curve(color, .pred_ORANGE) %>% \n",
|
|
" autoplot()\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"As curvas ROC são frequentemente utilizadas para obter uma visão do desempenho de um classificador em termos de verdadeiros positivos vs. falsos positivos. As curvas ROC geralmente apresentam a `Taxa de Verdadeiros Positivos`/Sensibilidade no eixo Y e a `Taxa de Falsos Positivos`/1-Especificidade no eixo X. Assim, a inclinação da curva e o espaço entre a linha do meio e a curva são importantes: o ideal é ter uma curva que rapidamente suba e ultrapasse a linha. No nosso caso, há falsos positivos no início, e depois a linha sobe e ultrapassa adequadamente.\n",
|
|
"\n",
|
|
"Por fim, vamos usar `yardstick::roc_auc()` para calcular a Área Sob a Curva. Uma forma de interpretar a AUC é como a probabilidade de o modelo classificar um exemplo positivo aleatório mais alto do que um exemplo negativo aleatório.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"vscode": {
|
|
"languageId": "r"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Calculate area under curve\n",
|
|
"results %>% \n",
|
|
" roc_auc(color, .pred_ORANGE)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"O resultado é cerca de `0.975`. Dado que o AUC varia de 0 a 1, é desejável um valor elevado, já que um modelo que acerta 100% das suas previsões terá um AUC de 1; neste caso, o modelo é *bastante bom*.\n",
|
|
"\n",
|
|
"Nas próximas lições sobre classificações, aprenderás como melhorar os resultados do teu modelo (como lidar com dados desequilibrados neste caso).\n",
|
|
"\n",
|
|
"## 🚀Desafio\n",
|
|
"\n",
|
|
"Há muito mais para explorar sobre regressão logística! Mas a melhor forma de aprender é experimentar. Encontra um conjunto de dados que se preste a este tipo de análise e constrói um modelo com ele. O que aprendes? dica: experimenta [Kaggle](https://www.kaggle.com/search?q=logistic+regression+datasets) para conjuntos de dados interessantes.\n",
|
|
"\n",
|
|
"## Revisão & Estudo Individual\n",
|
|
"\n",
|
|
"Lê as primeiras páginas [deste artigo de Stanford](https://web.stanford.edu/~jurafsky/slp3/5.pdf) sobre algumas utilizações práticas da regressão logística. Reflete sobre tarefas que são mais adequadas para um ou outro tipo de regressão entre as que estudámos até agora. O que funcionaria melhor?\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Aviso Legal**: \nEste documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos pela precisão, esteja ciente de que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. Para informações críticas, recomenda-se uma tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas decorrentes do uso desta tradução.\n"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"anaconda-cloud": "",
|
|
"kernelspec": {
|
|
"display_name": "R",
|
|
"langauge": "R",
|
|
"name": "ir"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": "r",
|
|
"file_extension": ".r",
|
|
"mimetype": "text/x-r-source",
|
|
"name": "R",
|
|
"pygments_lexer": "r",
|
|
"version": "3.4.1"
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "feaf125f481a89c468fa115bf2aed580",
|
|
"translation_date": "2025-09-03T19:35:13+00:00",
|
|
"source_file": "2-Regression/4-Logistic/solution/R/lesson_4-R.ipynb",
|
|
"language_code": "pt"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 1
|
|
} |