Problema de regresión logística multinomial usando Python

1. Descripción

        La regresión logística multinomial es un método estadístico que se utiliza para predecir resultados de clasificación para más de dos categorías. Es particularmente útil cuando la variable dependiente es categórica en lugar de continua.

2. Predicción de clasificación

        En la regresión logística multinomial , el modelo predice la probabilidad de que una observación pertenezca a cada categoría de la variable dependiente. Estas probabilidades pueden interpretarse como la probabilidad de que una observación pertenezca a cada categoría. La clase predicha suele ser la clase con mayor probabilidad, lo que la convierte en una predicción categórica en lugar de una predicción continua .

Por el contrario, la regresión logística estándar, también conocida como regresión logística binomial ( un caso especial de regresión logística binomial )         , se utiliza cuando la variable dependiente tiene solo dos categorías . Predice la probabilidad de que una observación pertenezca a una categoría frente a otra. Las predicciones en regresión logística binaria son 0probabilidades continuas entre y 1.

3. Principio de nivel inferior

        Aquí están las matemáticas detrás de la regresión logística multinomial. Estas ecuaciones representan un conjunto de modelos log-lineales en los que el logaritmo de la relación de probabilidades para cada categoría está $X_i$relacionado linealmente con la variable predictiva, a través de un parámetro de coeficiente (o pendiente), denotado $\beta_1$ por \beta_2,,, \beta_{K-1}. Los símbolos $\texto{P}(Y_i = K)$  representan la probabilidad de que una observación $yo$pertenezca a $K$una clase, donde $K$es una representación entera de cada clase, comenzando desde 1.

        A partir de estas ecuaciones, podemos simplificar a las siguientes ecuaciones, que son formas alternativas de las ecuaciones que vimos anteriormente. Surgen del hecho de que las probabilidades de todas las clases K deben sumar 1. En estas ecuaciones, el numerador se utiliza como categoría de referencia y las probabilidades de las otras categorías K – 1 se expresan exponencialmente con respecto a esta categoría de referencia en función de los predictores Xi y los coeficientes correspondientes βk .

        La función exponencial $e$ en estas ecuaciones se usa para convertir el predictor lineal (es decir, βk  •  Xi ) en una probabilidad, que siempre es positiva y está entre 0 y 1. Aplicando la función exponencial e a la ecuación bilateral anterior, obtenemos la siguiente ecuación de primer tipo:

        Estas ecuaciones se utilizan normalmente en la regresión logística multinomial, donde el objetivo es predecir la probabilidad de que una observación pertenezca a cada una de las K categorías en función de una o más variables predictivas.

4. Acerca de los datos

        En este ejemplo, utilizaremos el conjunto de datos de abulón de UC Irvine para predecir el sexo del abulón. Se pueden utilizar múltiples modelos de regresión lineal para predecir la edad, pero en este caso estamos prediciendo el sexo de un abulón determinado en función de varias características diferentes.

        Usando el paquete Python  pandaspodemos ver la forma de los datos, así como las primeras filas.

import pandas as pd df = pd.read_csv("https://raw.githubusercontent.com/s-lasch/CIS-280/main/abalone.csv") # read csv df.shape # get shape
(4177, 9)
df.head() # show first 5 rows
| sex | length | diameter | height | whole_weight | shucked_weight | viscera_weight | shell_weight | rings | 
------------------------------------------------------------------------------------------------------------ 
| M   | 0.455  | 0.365    | 0.095  | 0.5140       | 0.2245         | 0.1010         |0.150         | 15    |
| M   | 0.350  | 0.265    | 0.090  | 0.2255       | 0.0995         | 0.0485         | 0.070        | 7     |
| F   | 0.530  | 0.420    | 0.135  | 0.6770       | 0.2565         | 0.1415         | 0.210        | 9     |
| M   | 0.440  | 0.365    | 0.125  | 0.5160       | 0.2155         | 0.1140         | 0.155        | 10    |
| I   | 0.330  | 0.255    | 0.080  | 0.2050       | 0.0895         | 0.0395         | 0.055        | 7     |

5. Procesamiento de datos

        Podemos ver que hay tres categorías diferentes en esta columna sex: M, F e I, que representan hombres, mujeres e infantes respectivamente. Estos representan las clases que nuestro modelo predecirá en función de otras columnas del conjunto de datos.

df['sex'].value_counts().sort_values(ascending=False) # count the number of distinct classes
M    1528
I    1342
F    1307
Name: sex, dtype: int64

        Esto significa que nuestros datos y serán sexcolumnas y nuestros datos X serán todas las columnas excepto sex.

X = df.drop(['sex'], axis=1) 
y = df['sex']

Ahora estamos listos para dividir los datos en entrenamiento y prueba. Podemos scikitlearnhacer esto usando un paquete como el siguiente:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.20, random_state = 5)

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(3341, 8)
(836, 8)
(3341,)
(836,)

6. Codificación única

        Ahora que tenemos datos de entrenamiento y prueba, es importante recordar que cualquier modelo de regresión requiere entradas de números enteros o de punto flotante. Dado que la sexcolumna es una columna categórica, debemos aplicar codificación de enteros para usarla en la regresión. Para hacer esto, aplicaremos codificación one-hot , que asignará un número entero a cada clase diferente.

y_train = y_train.apply(lambda x: 0 if x == "M" else 1 if x == "F" else 2)
y_test = y_test.apply(lambda x: 0 if x == "M" else 1 if x == "F" else 2)

        Ahora que los datos y están codificados, debemos convertir cada conjunto de datos de tren/prueba a torch.tensorEsto es crucial para cualquier regresión que utilice Pytorch, ya que solo puede tomar tensores.

X_train_tensor = torch.tensor(X_train.to_numpy()).float()
X_test_tensor = torch.tensor(X_test.to_numpy()).float()
y_train_tensor = torch.tensor(y_train.to_numpy()).long()
y_test_tensor = torch.tensor(y_test.to_numpy()).long()

Para obtener más información sobre los tensores, puede encontrar un recurso muy detallado aquí. Steven Rush

7. El modelo

        Una vez completado el procesamiento de datos, ahora podemos comenzar el proceso de creación del modelo. Para implementar este modelo, usaremos la biblioteca Pytorch. Dado que se utilizan 8 funciones para determinar el género, debemos establecerlo en in_features8. Dado que el modelo sólo puede predecir tres clases posibles, se out_featuresestablecerá en 3. Para obtener más información sobre torch.nnlos módulos de Pytorch, consulte la documentación .

import torch
import torch.nn as nn
from torch.nn import Linear
import torch.nn.functional as F


torch.manual_seed(348965)                                 # keep random values consistent

model = Linear(in_features=8, out_features=3)             # define the model

# define the loss function and optimizer
criterion = nn.CrossEntropyLoss()                         # use cross-entropy loss for multi-class classification
optimizer = torch.optim.SGD(model.parameters(), lr=.01)   # learning rate of 0.01, and Stocastic Gradient descent optimizer

8. Modelo de formación

num_epochs = 2500    # loop iterations

for epoch in range(num_epochs):
    # forward pass
    outputs = model(X_train_tensor)
    loss = criterion(outputs, y_train_tensor)

    # backward and optimize
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    # print progress every 100 epochs
    if (epoch+1) % 100 == 0:
        print('Epoch [{}/{}]\tLoss: {:.4f}'.format(epoch+1, num_epochs, loss.item()))
Epoch [100/2500]     Loss: 1.1178
Epoch [200/2500]     Loss: 1.1006
Epoch [300/2500]     Loss: 1.0850
Epoch [400/2500]     Loss: 1.0708
Epoch [500/2500]     Loss: 1.0579
Epoch [600/2500]     Loss: 1.0460
Epoch [700/2500]     Loss: 1.0352
Epoch [800/2500]     Loss: 1.0252
Epoch [900/2500]     Loss: 1.0161
Epoch [1000/2500]    Loss: 1.0077
Epoch [1100/2500]    Loss: 0.9999
Epoch [1200/2500]    Loss: 0.9927
Epoch [1300/2500]    Loss: 0.9860
Epoch [1400/2500]    Loss: 0.9799
Epoch [1500/2500]    Loss: 0.9741
Epoch [1600/2500]    Loss: 0.9688
Epoch [1700/2500]    Loss: 0.9638
Epoch [1800/2500]    Loss: 0.9592
Epoch [1900/2500]    Loss: 0.9549
Epoch [2000/2500]    Loss: 0.9509
Epoch [2100/2500]    Loss: 0.9471
Epoch [2200/2500]    Loss: 0.9435
Epoch [2300/2500]    Loss: 0.9402
Epoch [2400/2500]    Loss: 0.9371
Epoch [2500/2500]    Loss: 0.9342

9. Verificación

Ahora para comprobar la precisión podemos ejecutar el siguiente código:

outputs = model(X_test_tensor)
_, preds = torch.max(outputs, dim=1)
accuracy = torch.mean((preds == y_test_tensor).float())
print('\nAccuracy: {:.2f}%'.format(accuracy.item()*100))
Accuracy: 52.63%

Esto significa que nuestro modelo identificó con precisión el sexo del abulón basándose en 8 características diferentes casi el 53% de las veces, lo cual no es nada bueno.

10. Código completo

        Aquí está el código completo:

import torch
import torch.nn as nn
from torch.nn import Linear
import torch.nn.functional as F


torch.manual_seed(348965)                                 # keep random values consistent

model = Linear(in_features=8, out_features=3)             # define the model

# define the loss function and optimizer
criterion = nn.CrossEntropyLoss()                         # use cross-entropy loss for multi-class classification
optimizer = torch.optim.SGD(model.parameters(), lr=.01)   # learning rate of 0.01, and Stocastic Gradient descent optimizer

# convert the data to PyTorch tensors
X_train_tensor = torch.tensor(X_train.to_numpy()).float()
X_test_tensor = torch.tensor(X_test.to_numpy()).float()
y_train_tensor = torch.tensor(y_train.to_numpy()).long()
y_test_tensor = torch.tensor(y_test.to_numpy()).long()

# train the model
num_epochs = 2500    # loop iterations

for epoch in range(num_epochs):
    # forward pass
    outputs = model(X_train_tensor)
    loss = criterion(outputs, y_train_tensor)

    # backward and optimize
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    # print progress every 100 epochs
    if (epoch+1) % 100 == 0:
        print('Epoch [{}/{}]\tLoss: {:.4f}'.format(epoch+1, num_epochs, loss.item()))


outputs = model(X_test_tensor)
_, preds = torch.max(outputs, dim=1)
accuracy = torch.mean((preds == y_test_tensor).float())
print('\nAccuracy: {:.2f}%'.format(accuracy.item()*100))
Epoch [100/2500]    Loss: 1.1178
Epoch [200/2500]    Loss: 1.1006
Epoch [300/2500]    Loss: 1.0850
Epoch [400/2500]    Loss: 1.0708
Epoch [500/2500]    Loss: 1.0579
Epoch [600/2500]    Loss: 1.0460
Epoch [700/2500]    Loss: 1.0352
Epoch [800/2500]    Loss: 1.0252
Epoch [900/2500]    Loss: 1.0161
Epoch [1000/2500]    Loss: 1.0077
Epoch [1100/2500]    Loss: 0.9999
Epoch [1200/2500]    Loss: 0.9927
Epoch [1300/2500]    Loss: 0.9860
Epoch [1400/2500]    Loss: 0.9799
Epoch [1500/2500]    Loss: 0.9741
Epoch [1600/2500]    Loss: 0.9688
Epoch [1700/2500]    Loss: 0.9638
Epoch [1800/2500]    Loss: 0.9592
Epoch [1900/2500]    Loss: 0.9549
Epoch [2000/2500]    Loss: 0.9509
Epoch [2100/2500]    Loss: 0.9471
Epoch [2200/2500]    Loss: 0.9435
Epoch [2300/2500]    Loss: 0.9402
Epoch [2400/2500]    Loss: 0.9371
Epoch [2500/2500]    Loss: 0.9342

Publicado originalmente en https://s-lasch.github.io el 1 de mayo de 2023 .

Supongo que te gusta

Origin blog.csdn.net/gongdiwudu/article/details/133420378
Recomendado
Clasificación