1d8yzndchld95fzu1yvktga.png

ALGORITMO DE CLASIFICACIÓN

Encontrar los pesos perfectos para ajustar los datos

Si bien algunos modelos de aprendizaje automático basados ​​en probabilidad (como Bayes ingenuo) hacen suposiciones audaces sobre la independencia de las características, la regresión logística adopta un enfoque más mesurado. Piense en ello como en dibujar una línea (o plano) que separa dos resultados, lo que nos permite predecir probabilidades con un poco más de flexibilidad.

Todas las imágenes fueron creadas por el autor con Canva Pro. Optimizadas para dispositivos móviles; pueden verse demasiado grandes en computadoras de escritorio.

La regresión logística es un método estadístico que se utiliza para predecir resultados binarios. A pesar de su nombre, se utiliza para la clasificación en lugar de la regresión. Calcula la probabilidad de que una instancia pertenezca a una clase en particular. Si la probabilidad estimada es mayor del 50 %, el modelo predice que la instancia pertenece a esa clase (o viceversa).

A lo largo de este artículo, utilizaremos este conjunto de datos de golf artificial (inspirado en [1]) como ejemplo. Este conjunto de datos predice si una persona jugará al golf en función de las condiciones climáticas.

Igual que en KNNLa regresión logística requiere que primero se escalen los datos. Convertir columnas categóricas en 0 y 1 y también escalar las características numéricas para que ninguna característica domine la métrica de la distancia.

Columnas: ‘Perspectiva’, ‘Temperatura’, ‘Humedad’, ‘Viento’ y ‘Reproducción’ (función de destino). Las columnas categóricas (Perspectiva y Viento) se codifican mediante codificación one-hot, mientras que las columnas numéricas se escalan mediante escala estándar (normalización z).
# Import required libraries
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
import pandas as pd
import numpy as np

# Create dataset from dictionary
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rainy', 'rainy', 'rainy', 'overcast', 'sunny', 'sunny', 'rainy', 'sunny', 'overcast', 'overcast', 'rainy', 'sunny', 'overcast', 'rainy', 'sunny', 'sunny', 'rainy', 'overcast', 'rainy', 'sunny', 'overcast', 'sunny', 'overcast', 'rainy', 'overcast'],
'Temperature': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0, 72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0, 81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0, 73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humidity': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0, 70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0, 92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0, 65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False, False, False, True, True, False, True, True, False, False, True, False, True, True, False, True, False, False, True, False, False],
'Play': ['No', 'No', 'Yes', 'Yes', 'Yes', 'No', 'Yes', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'No', 'Yes', 'Yes', 'No', 'No', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'Yes']
}
df = pd.DataFrame(dataset_dict)

# Prepare data: encode categorical variables
df = pd.get_dummies(df, columns=['Outlook'], prefix='', prefix_sep='', dtype=int)
df['Wind'] = df['Wind'].astype(int)
df['Play'] = (df['Play'] == 'Yes').astype(int)

# Rearrange columns
column_order = ['sunny', 'overcast', 'rainy', 'Temperature', 'Humidity', 'Wind', 'Play']
df = df[column_order]

# Split data into features and target
X, y = df.drop(columns='Play'), df['Play']

# Split data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, shuffle=False)

# Scale numerical features
scaler = StandardScaler()
X_train[['Temperature', 'Humidity']] = scaler.fit_transform(X_train[['Temperature', 'Humidity']])
X_test[['Temperature', 'Humidity']] = scaler.transform(X_test[['Temperature', 'Humidity']])

# Print results
print("Training set:")
print(pd.concat([X_train, y_train], axis=1), '\n')
print("Test set:")
print(pd.concat([X_test, y_test], axis=1))

La regresión logística funciona aplicando la función logística a una combinación lineal de las características de entrada. Así es como funciona:

  1. Calcular una suma ponderada de las características de entrada (similar a la regresión lineal).
  2. Aplique la función logística (también llamada función sigmoidea) a esta suma, que asigna cualquier número real a un valor entre 0 y 1.
  3. Interprete este valor como la probabilidad de pertenecer a la clase positiva.
  4. Utilice un umbral (normalmente 0,5) para tomar la decisión de clasificación final.
Para nuestro conjunto de datos de golf, la regresión logística podría combinar los factores climáticos en una única puntuación y luego transformar esta puntuación en una probabilidad de jugar al golf.

El proceso de entrenamiento para la regresión logística implica encontrar los mejores pesos para las características de entrada. Este es el esquema general:

  1. Inicializar los pesos (a menudo en pequeños valores aleatorios).
# Initialize weights (including bias) to 0.1
initial_weights = np.full(X_train_np.shape[1], 0.1)

# Create and display DataFrame for initial weights
print(f"Initial Weights: {initial_weights}")

2. Para cada ejemplo de entrenamiento:
a. Calcule la probabilidad prevista utilizando los pesos actuales.

def sigmoid(z):
return 1 / (1 + np.exp(-z))

def calculate_probabilities(X, weights):
z = np.dot(X, weights)
return sigmoid(z)

def calculate_log_loss(probabilities, y):
return -y * np.log(probabilities) - (1 - y) * np.log(1 - probabilities)

def create_output_dataframe(X, y, weights):
probabilities = calculate_probabilities(X, weights)
log_losses = calculate_log_loss(probabilities, y)

df = pd.DataFrame({
'Probability': probabilities,
'Label': y,
'Log Loss': log_losses
})

return df

def calculate_average_log_loss(X, y, weights):
probabilities = calculate_probabilities(X, weights)
log_losses = calculate_log_loss(probabilities, y)
return np.mean(log_losses)

# Convert X_train and y_train to numpy arrays for easier computation
X_train_np = X_train.to_numpy()
y_train_np = y_train.to_numpy()

# Add a column of 1s to X_train_np for the bias term
X_train_np = np.column_stack((np.ones(X_train_np.shape[0]), X_train_np))

# Create and display DataFrame for initial weights
initial_df = create_output_dataframe(X_train_np, y_train_np, initial_weights)
print(initial_df.to_string(index=False, float_format=lambda x: f"{x:.6f}"))
print(f"\nAverage Log Loss: {calculate_average_log_loss(X_train_np, y_train_np, initial_weights):.6f}")

b. Compare esta probabilidad con la etiqueta de clase real calculando su pérdida logarítmica.

3. Actualice los pesos para minimizar la pérdida (generalmente utilizando algún algoritmo de optimización, como el descenso de gradiente. Esto incluye repetir el Paso 2 hasta que la pérdida logarítmica no pueda reducirse).

def gradient_descent_step(X, y, weights, learning_rate):
m = len(y)
probabilities = calculate_probabilities(X, weights)
gradient = np.dot(X.T, (probabilities - y)) / m
new_weights = weights - learning_rate * gradient # Create new array for updated weights
return new_weights

# Perform one step of gradient descent (one of the simplest optimization algorithm)
learning_rate = 0.1
updated_weights = gradient_descent_step(X_train_np, y_train_np, initial_weights, learning_rate)

# Print initial and updated weights
print("\nInitial weights:")
for feature, weight in zip(['Bias'] + list(X_train.columns), initial_weights):
print(f"{feature:11}: {weight:.2f}")

print("\nUpdated weights after one iteration:")
for feature, weight in zip(['Bias'] + list(X_train.columns), updated_weights):
print(f"{feature:11}: {weight:.2f}")

# With sklearn, you can get the final weights (coefficients)
# and final bias (intercepts) easily.
# The result is almost the same as doing it manually above.

from sklearn.linear_model import LogisticRegression

lr_clf = LogisticRegression(penalty=None, solver='saga')
lr_clf.fit(X_train, y_train)

coefficients = lr_clf.coef_
intercept = lr_clf.intercept_

y_train_prob = lr_clf.predict_proba(X_train)[:, 1]
loss = -np.mean(y_train * np.log(y_train_prob) + (1 - y_train) * np.log(1 - y_train_prob))

print(f"Weights & Bias Final: {coefficients[0].round(2)}, {round(intercept[0],2)}")
print("Loss Final:", loss.round(3))

Una vez entrenado el modelo:
1. Para una nueva instancia, calcule la probabilidad con los pesos finales (también llamados coeficientes), tal como durante el paso de entrenamiento.

2. Interprete el resultado observando la probabilidad: si pag ≥ 0,5, predecir clase 1; de lo contrario, predecir clase 0

# Calculate prediction probability
predicted_probs = lr_clf.predict_proba(X_test)[:, 1]

z_values = np.log(predicted_probs / (1 - predicted_probs))

result_df = pd.DataFrame({
'ID': X_test.index,
'Z-Values': z_values.round(3),
'Probabilities': predicted_probs.round(3)
}).set_index('ID')

print(result_df)

# Make predictions
y_pred = lr_clf.predict(X_test)
print(y_pred)

Paso de evaluación

result_df = pd.DataFrame({
'ID': X_test.index,
'Label': y_test,
'Probabilities': predicted_probs.round(2),
'Prediction': y_pred,
}).set_index('ID')

print(result_df)

La regresión logística tiene varios parámetros importantes que controlan su comportamiento:

1.Penalización: El tipo de regularización que se utilizará (‘l1’, ‘l2’, ‘elasticnet’ o ‘none’). La regularización en la regresión logística evita el sobreajuste al agregar un término de penalización a la función de pérdida del modelo, lo que fomenta modelos más simples.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

regs = [None, 'l1', 'l2']
coeff_dict = {}

for reg in regs:
lr_clf = LogisticRegression(penalty=reg, solver='saga')
lr_clf.fit(X_train, y_train)
coefficients = lr_clf.coef_
intercept = lr_clf.intercept_
predicted_probs = lr_clf.predict_proba(X_train)[:, 1]
loss = -np.mean(y_train * np.log(predicted_probs) + (1 - y_train) * np.log(1 - predicted_probs))
predictions = lr_clf.predict(X_test)
accuracy = accuracy_score(y_test, predictions)

coeff_dict[reg] = {
'Coefficients': coefficients,
'Intercept': intercept,
'Loss': loss,
'Accuracy': accuracy
}

for reg, vals in coeff_dict.items():
print(f"{reg}: Coeff: {vals['Coefficients'][0].round(2)}, Intercept: {vals['Intercept'].round(2)}, Loss: {vals['Loss'].round(3)}, Accuracy: {vals['Accuracy'].round(3)}")

2. Fuerza de regularización (C): Controla el equilibrio entre ajustar los datos de entrenamiento y mantener el modelo simple. Un valor C más pequeño significa una regularización más fuerte.

# List of regularization strengths to try for L1
strengths = [0.001, 0.01, 0.1, 1, 10, 100]

coeff_dict = {}

for strength in strengths:
lr_clf = LogisticRegression(penalty='l1', C=strength, solver='saga')
lr_clf.fit(X_train, y_train)

coefficients = lr_clf.coef_
intercept = lr_clf.intercept_

predicted_probs = lr_clf.predict_proba(X_train)[:, 1]
loss = -np.mean(y_train * np.log(predicted_probs) + (1 - y_train) * np.log(1 - predicted_probs))
predictions = lr_clf.predict(X_test)

accuracy = accuracy_score(y_test, predictions)

coeff_dict[f'L1_{strength}'] = {
'Coefficients': coefficients[0].round(2),
'Intercept': round(intercept[0],2),
'Loss': round(loss,3),
'Accuracy': round(accuracy*100,2)
}

print(pd.DataFrame(coeff_dict).T)

# List of regularization strengths to try for L2
strengths = [0.001, 0.01, 0.1, 1, 10, 100]

coeff_dict = {}

for strength in strengths:
lr_clf = LogisticRegression(penalty='l2', C=strength, solver='saga')
lr_clf.fit(X_train, y_train)

coefficients = lr_clf.coef_
intercept = lr_clf.intercept_

predicted_probs = lr_clf.predict_proba(X_train)[:, 1]
loss = -np.mean(y_train * np.log(predicted_probs) + (1 - y_train) * np.log(1 - predicted_probs))
predictions = lr_clf.predict(X_test)
accuracy = accuracy_score(y_test, predictions)

coeff_dict[f'L2_{strength}'] = {
'Coefficients': coefficients[0].round(2),
'Intercept': round(intercept[0],2),
'Loss': round(loss,3),
'Accuracy': round(accuracy*100,2)
}

print(pd.DataFrame(coeff_dict).T)

3. Solucionador: El algoritmo que se utilizará para la optimización (‘liblinear’, ‘newton-cg’, ‘lbfgs’, ‘sag’, ‘saga’). Algunas regularizaciones pueden requerir un algoritmo particular.

4. Iteraciones máximas:El número máximo de iteraciones para que el solucionador converja.

Para nuestro conjunto de datos de golf, podríamos comenzar con la penalización ‘l2’, el solucionador ‘liblinear’ y C = 1,0 como línea de base.

Como cualquier algoritmo de aprendizaje automático, la regresión logística tiene sus fortalezas y limitaciones.

Ventajas:

  1. Sencillez:Fácil de implementar y comprender.
  2. Interpretabilidad:Los pesos muestran directamente la importancia de cada característica.
  3. Eficiencia:No requiere demasiado poder computacional.
  4. Salida probabilística:Proporciona probabilidades en lugar de sólo clasificaciones.

Contras:

  1. Supuesto de linealidad:Supone una relación lineal entre las características y las probabilidades logarítmicas del resultado.
  2. Independencia de características:Supone que las características no están altamente correlacionadas.
  3. Complejidad limitada:Puede no ser adecuado en casos donde el límite de decisión es altamente no lineal.
  4. Requiere más datos:Se necesita un tamaño de muestra relativamente grande para obtener resultados estables.

En nuestro ejemplo del golf, la regresión logística podría proporcionar un modelo claro e interpretable de cómo cada factor meteorológico influye en la decisión de jugar al golf. Sin embargo, podría resultar difícil si la decisión implica interacciones complejas entre condiciones meteorológicas que no pueden captarse mediante un modelo lineal.

La regresión logística destaca como una herramienta de clasificación potente y sencilla. Se destaca por su capacidad para manejar datos complejos y, al mismo tiempo, seguir siendo fácil de interpretar. algunos otros modelos básicosProporciona estimaciones de probabilidad uniformes y funciona bien con muchas funciones. En el mundo real, desde la predicción del comportamiento del cliente hasta los diagnósticos médicos, la regresión logística suele tener un rendimiento sorprendentemente bueno. No es solo un trampolín, es un modelo confiable que puede igualar modelos más complejos en muchas situaciones.

# Import required libraries
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score

# Load the dataset
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rainy', 'rainy', 'rainy', 'overcast', 'sunny', 'sunny', 'rainy', 'sunny', 'overcast', 'overcast', 'rainy', 'sunny', 'overcast', 'rainy', 'sunny', 'sunny', 'rainy', 'overcast', 'rainy', 'sunny', 'overcast', 'sunny', 'overcast', 'rainy', 'overcast'],
'Temperature': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0, 72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0, 81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0, 73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humidity': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0, 70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0, 92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0, 65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False, False, False, True, True, False, True, True, False, False, True, False, True, True, False, True, False, False, True, False, False],
'Play': ['No', 'No', 'Yes', 'Yes', 'Yes', 'No', 'Yes', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'No', 'Yes', 'Yes', 'No', 'No', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'Yes']
}
df = pd.DataFrame(dataset_dict)

# Prepare data: encode categorical variables
df = pd.get_dummies(df, columns=['Outlook'], prefix='', prefix_sep='', dtype=int)
df['Wind'] = df['Wind'].astype(int)
df['Play'] = (df['Play'] == 'Yes').astype(int)

# Split data into training and testing sets
X, y = df.drop(columns='Play'), df['Play']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, shuffle=False)

# Scale numerical features
scaler = StandardScaler()
float_cols = X_train.select_dtypes(include=['float64']).columns
X_train[float_cols] = scaler.fit_transform(X_train[float_cols])
X_test[float_cols] = scaler.transform(X_test[float_cols])

# Train the model
lr_clf = LogisticRegression(penalty='l2', C=1, solver='saga')
lr_clf.fit(X_train, y_train)

# Make predictions
y_pred = lr_clf.predict(X_test)

# Evaluate the model
print(f"Accuracy: {accuracy_score(y_test, y_pred)}")