3  Regresión

Los modelos de aprendizaje basados en regresión son modelos bastante simples que pueden utilizarse para predecir variables cuantitativas (regresión lineal) o cualitativas (regresión logística). Esta práctica contiene ejercicios que muestran como construir modelos de aprendizaje de regresión lineal y regresión logística con Julia.

3.1 Ejercicios Resueltos

Para la realización de esta práctica se requieren los siguientes paquetes:

using CSV  # Para la lectura de archivos CSV.
using DataFrames  # Para el manejo de datos tabulares.
using PrettyTables  # Para mostrar tablas formateadas.
using Plots  # Para el dibujo de gráficas.
using GLMakie  # Para obtener gráficos interactivos.

Ejercicio 3.1 El conjunto de datos viviendas.csv contiene información sobre el precio de venta de viviendas en una ciudad.

  1. Cargar los datos del archivo viviendas.csv en un data frame.

    using CSV, DataFrames
    df = CSV.read(download("https://aprendeconalf.es/aprendizaje-automatico-practicas-julia/datos/viviendas.csv"), DataFrame)
    first(df, 5)
    5×13 DataFrame
    Row precio area dormitorios baños habitaciones calleprincipal huespedes sotano calentador climatizacion garaje centrico amueblado
    Int64 Int64 Int64 Int64 Int64 String3 String3 String3 String3 String3 Int64 String3 String15
    1 13300000 7420 4 2 3 si no no no si 2 si amueblado
    2 12250000 8960 4 4 4 si no no no si 3 no amueblado
    3 12250000 9960 3 2 2 si no si no no 2 si semi-amueblado
    4 12215000 7500 4 2 2 si no si no si 3 si amueblado
    5 11410000 7420 4 1 2 si si si no si 2 no amueblado
  2. Dibujar un diagrama de dispersión entre el precio y el area de las viviendas.

    using Plots
    plt = scatter(df.area, df.precio, xlabel="Area", ylabel="Precio", title="Precio vs Area", label = "Ejemplos", fmt=:png,)
  3. Definir un modelo lineal que explique el precio en función del área de las viviendas.

    Un modelo lineal tiene ecuación y=θ1+θ2x.

    precio(area, θ) = θ[1] .+ θ[2] * area
    precio (generic function with 1 method)

    Observa que la función precio está vectorizada, lo que significa que puede recibir un vector de áreas y devolver un vector de precios.

  4. Inicializar los parámetros del modelo lineal con valores nulos y dibujar el modelo sobre el diagrama de dispersión.

    θ = [0.0, 0.0]
    plot!(df.area, precio(df.area, θ), label = "Modelo 0")
  5. Definir una función de costo para el modelo lineal y evaluar el coste para el modelo lineal construido con los parámetros iniciales. A la vista del coste obtenido, ¿cómo de bueno es el modelo?

    La función de coste para un modelo lineal es el error cuadrático medio.

    J(θ)=12mi=1m(hθ(x(i))y(i))2

    donde hθ es el modelo, hθ(x(i)) es la predicción del modelo para el ejemplo i-ésimo, y(i) es el valor real observado para el ejemplo i-ésimo, y m es el número de ejemplos.

    function coste(θ, X, Y)
        m = length(Y)
        return sum((precio(X, θ) .- Y).^2) / (2 * m)
    end
    
    coste(θ, df.area, df.precio)
    1.3106916364659266e13

    La función de coste nos da una medida de lo lejos que están las predicciones del modelo de los valores reales observados. En este caso, el coste es muy alto, lo que indica que el modelo no es bueno.

  6. ¿En qué dirección debemos modificar los parámetros del modelo para mejorar el modelo?

    Para minimizar la función de coste, debemos modificar los parámetros del modelo en la dirección opuesta al gradiente de la función de coste, ya que el gradiente de una función indica la dirección de mayor crecimiento de la función.

  7. Crear una función para modificar los pesos del modelo lineal mediante el algoritmo del gradiente descendente, y aplicarla a los parámetros actuales tomando una tasa de aprendizaje de 108. ¿Cómo han cambiado los parámetros del modelo? Dibujar el modelo actualizado sobre el diagrama de dispersión. ¿Cómo ha cambiado el coste?

    El algoritmo del gradiente descendente actualiza los parámetros del modelo de acuerdo a la siguiente regla:

    θj=θjηJ(θ)θj

    donde η es la tasa de aprendizaje y J(θ)θj es la derivada parcial de la función de coste con respecto al parámetro θj.

    function gradiente_descendente!(θ, X, Y, η)
        # Calculamos el número de ejemplos
        m = length(Y)
        # Actualizamos el término independiente del modelo lineal.
        θ[1] -= η * sum(precio(X, θ) - Y) / m
        # Actualizamos la pendiente del modelo lineal.
        θ[2] -= η * sum((precio(X, θ) - Y) .* X) / m
        return θ
    end
    gradiente_descendente! (generic function with 1 method)

    Aplicamos la función a los parámetros del modelo actual y mostramos los nuevos parámetros.

    gradiente_descendente!(θ, df.area, df.precio, 1e-8)
    θ
    2-element Vector{Float64}:
       0.04766729247706422
     267.22919804579385

    Dibujamos el nuevo modelo.

    plot!(df.area, precio(df.area, θ), label = "Modelo 1")

    Se observa que ahora la recta está más cerca de la nube de puntos, por lo que el modelo ha mejorado. Calculamos el coste del nuevo modelo.

    coste(θ, df.area, df.precio)
    7.080823787113201e12
  8. Repetir el proceso de actualización de los parámetros del modelo mediante el algoritmo del gradiente descendente durante 9 iteraciones más y dibujar los modelos actualizados.

    for i = 2:10
        gradiente_descendente!(θ, df.area, df.precio, 1e-8)
        plot!(df.area, precio(df.area, θ), label = "Modelo $i", legend = true)
    end
    plt

  9. Dibujar un gráfico con la evolución del coste del modelo a lo largo de las iteraciones. ¿Cómo se comporta el coste a lo largo de las iteraciones?

    costes = Float64[]
    for i = 1:10
        gradiente_descendente!(θ, df.area, df.precio, 1e-8)
        push!(costes, coste(θ, df.area, df.precio))
    end
    costes
    10-element Vector{Float64}:
     4.230808760870044e12
     2.882906194020343e12
     2.2454213686913755e12
     1.9439256128790886e12
     1.8013344680594421e12
     1.7338965877160208e12
     1.7020021263374993e12
     1.6869177748236997e12
     1.6797836937723748e12
     1.6764096595632322e12

    El coste del modelo disminuye en cada iteración, lo que indica que el modelo está mejorando. Esto se debe a que el algoritmo del gradiente descendente modifica los parámetros del modelo en la dirección que minimiza la función de coste.

  10. ¿Hasta qué iteración habrá que llegar para conseguir un reducción del coste menor de un 0.0001%?

    θ = [0.0, 0.0]
    costes = [0, coste(θ, df.area, df.precio)]
    i = 1
    while abs(costes[end] - costes[end-1]) / costes[end-1] > 0.000001
        i += 1
        gradiente_descendente!(θ, df.area, df.precio, 1e-8)
        push!(costes, coste(θ, df.area, df.precio))
    end
    i
    23

    En este caso, el algoritmo del gradiente descendente converge en 1000 iteraciones.

  11. ¿Qué sucede si se utiliza una tasa de aprendizaje η=0.0001? ¿Cómo afecta al coste y a la convergencia del modelo?

    θ = [0.0, 0.0]
    costes = [coste(θ, df.area, df.precio)]
    for i = 1:10
        gradiente_descendente!(θ, df.area, df.precio, 0.0001)
        push!(costes, coste(θ, df.area, df.precio))
    end
    costes
    11-element Vector{Float64}:
     1.3106916364659266e13
     1.114133369099188e20
     1.0856750832581238e27
     1.05794371802143e34
     1.0309206941949286e41
     1.004587918634273e48
     9.789277603492545e54
     9.539230386975057e61
     9.29557011881276e68
     9.058133657380397e75
     8.826762028174244e82

    Si la tasa de aprendizaje es demasiado grande, el algoritmo del gradiente descendente puede no converger y el coste puede oscilar en lugar de disminuir. En este caso, el coste aumenta en cada iteración, lo que indica que la tasa de aprendizaje es demasiado grande.