avatar
Publicado por

Modelos Supervisionados - Estudo sobre Regressão Logística e Gradientes

Authors
  • avatar
    Name
    Gabriel Gava Pinheiro
    Twitter

Introdução

Neste artigo, exploraremos os conceitos por trás dos algoritmos de aprendizado de máquina, demonstrando como podemos empregar a matemática pura para predizer valores em:

  • Regressão Linear
  • Classificação

Não se preocupe excessivamente com a matemática; os cálculos apresentados são didáticos e muitas vezes simplificados, uma vez que, na prática, bibliotecas especializadas abstraem essas complexidades.

Recomendamos um conhecimento sólido em álgebra linear e cálculo diferencial. Você pode aprofundar seus conhecimentos nesses temas através do curso disponível neste [link](Colocar o link aqui).

Estudo de Caso

Imagine que você está iniciando sua carreira em engenharia de dados e recebe o desafio de prever preços de imóveis para uma corretora. A corretora fornece uma série de dados, incluindo valores de imóveis e padrões de preço, conforme exemplificado abaixo:

Bairro, Altura_Andares, Preco
Pinheiros, 110, 400000
Pinheiros, 150, 600000
Pinheiros, 180, 800000
Pinheiros, 200, 900000
Pinheiros, 210, 990000
Pinheiros, 300, 1200000
...

Esses dados mostram uma relação simples entre o preço e a altura do imóvel, onde percebemos que a tendência é de aumento de preço com o aumento da altura.

Como fazer a máquina entender esse comportamento?

Para esse conjunto de dados, podemos verificar a imagem do gráfico abaixo, que já nos dá uma ideia da linearidade do conjunto.

Gráfico Preços

Vamos analisar as fórmulas matemáticas que utilizaremos e algumas anotações importantes para este artigo. A função de linha será representada pela seguinte fórmula:

f(w,b)=wcdotx+bf(w, b) = w cdot x + b

Previsão:

f(y)=wcdotx+bf(y) = w cdot x + b

Para entender como fazer a predição, precisamos examinar a matemática envolvida e alguns conceitos fundamentais.

Primeiro Conceito

Para calcular a distância entre dois pontos, podemos utilizar (R2(yhaty)2)(R^2 (y - hat{y})^2).

A função de custo baseada no logaritmo de (R^2) é frequentemente utilizada para estabilizar a variância e normalizar a distribuição dos resíduos, conforme a fórmula:

log(R2)=log(1fracsum(yhaty)2sum(ybary)2)log(R^2) = log(1 - frac{sum(y - hat{y})^2}{sum(y - bar{y})^2})

Derivada em Relação a (w) (peso)

Para otimizar o modelo, precisamos calcular a derivada da função de custo em relação a (w), que é o peso em nosso modelo. A derivada é dada por:

fracpartialpartialwlog(R2)=text[Inserirexpressa~oderivadaemrelac\ca~oa(w)]frac{partial}{partial w} log(R^2) = text{[Inserir expressão derivada em relação a (w)]}

Derivada em Relação a (b) (viés)

Da mesma forma, a derivada da função de custo em relação a (b), que é o viés em nosso modelo, é necessária para a otimização. A derivada é:

fracpartialpartialblog(R2)=text[Inserirexpressa~oderivadaemrelac\ca~oa(b)]frac{partial}{partial b} log(R^2) = text{[Inserir expressão derivada em relação a (b)]}

Estas derivadas são utilizadas no algoritmo de gradiente descendente para atualizar os valores de (w) e (b) iterativamente, com o objetivo de minimizar a função de custo.

Parte praticas para implementar um codigo de regressão linear


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Funções de normalização e desnormalização
def zscore(X):
    mean = np.mean(X, axis=0)
    std_dev = np.std(X, axis=0)
    X_normalized = (X - mean) / std_dev
    return X_normalized, mean, std_dev 

def desnormalize(z, mean, std_dev):
    x = z * std_dev + mean
    return x

# Função de custo
def cost(x_t, y_t, w, b, lambda_=0.8):
    m = len(x_t)
    total_error = ((np.dot(w, x_t.T) + b - y_t) ** 2).sum()
    cost = total_error / (2 * m)
    return cost

# Cálculo do gradiente
def gradient(w, b, x_t, y_t, lambda_):
    m = len(x_t)
    error = np.dot(w, x_t.T) + b - y_t
    sum_dw = (error * x_t.T).sum(axis=1) / m
    sum_db = error.sum() / m
    return sum_dw, sum_db

# Gradiente descendente
def gradient_desc(w, b, x_t, y_t, alfa, tax, lambda_):
    new_w = w.copy()
    new_b = b
    for i in range(tax):
        dw, db = gradient(new_w, new_b, x_t, y_t, lambda_)
        new_w = new_w - alfa * dw
        new_b = new_b - alfa * db
        if i % 10 == 0:
            cost_f = cost(x_t, y_t, new_w, new_b, lambda_)
            print(f"Cost function at iteration {i}: {cost_f}")
    return new_w, new_b

# Função para previsões com normalização
def withNorm(year, area, x2_t, y2_t, alfa, tax):
    w = np.array([0.0, 0.0])  # Valor inicial para w
    b = 0.0  # Valor inicial para b

    # Normalização das características
    x_norm, mean, std = zscore(x2_t)
    n_w, n_b = gradient_desc(w, b, x_norm, y2_t, alfa, tax, lambda_=0.8)

    # Normalização dos novos dados para previsão
    new_data = np.array([year, area])
    new_data_norm = (new_data - mean) / std

    # Realização da previsão
    p = np.dot(n_w, new_data_norm) + n_b
    print("################################")
    return p, n_w, n_b  

Vamos analisar agora problemas de classificação.

Levando em conta os conceitos dentro de um problema de classificação nos deparamos com dados não lineares, o que nos remete a uma função não linear, e padrões mais complexos continua...