Noviembre de 2017
Volumen 33, número 11
Serie de pruebas: regresión logística del kernel con C#
Por James McCaffrey
La regresión logística del kernel (KLR) es una técnica de aprendizaje automático que se puede usar para realizar predicciones binarias. Por ejemplo, KLR podría predecir si una persona devolverá un préstamo (incumplimiento de la devolución = 0, cumplimiento de la devolución = 1) en función de variables predictoras, como la edad, los ingresos y el importe de la deuda existente. KLR es una variación avanzada de la regresión logística ordinaria.
Una buena manera de ver hacia dónde se dirige este artículo es echar un vistazo al programa de demostración de la Figura 1 y a los datos asociados de la Figura 2. El objetivo del programa de demostración es predecir la clase, 0 o 1, de los datos ficticios que tiene solo dos variables predictoras (a veces denominadas características), x0 y x1. Por ejemplo, el primer elemento de datos de aprendizaje es (2.0, 3.0, 0), lo que significa que si los valores de predictor son x0 = 2.0 y x1 = 3.0, la clase correcta es 0. KLR puede controlar los datos con un número indefinido de variables de predictor, aunque usando solo dos se puede visualizar la técnica fácilmente.
Los 21 puntos de datos de aprendizaje tienen una geometría circular, lo que significa que técnicas de clasificación lineal simples, como la regresión logística ordinaria, son ineficaces. Estos datos se denominan ahora no separables linealmente.
Entre bastidores, KLR usa una función que se conoce como kernel de función de base radial (RBF). La función de kernel de RBF presenta un parámetro denominado sigma. El valor de sigma debe determinarse por prueba y error, y está establecido en 1,0 en la demostración. El entrenamiento de un modelo KLR es un proceso iterativo y la demostración establece el número máximo de iteraciones en 1000 y una velocidad de aprendizaje, eta, de 0,001.
El entrenamiento de un modelo KLR crea un conjunto de "valores de alpha", uno para cada elemento de datos de aprendizaje, además de un valor de "bias" adicional. El programa de demostración muestra los valores de alpha para los tres primeros elementos de aprendizaje (-0.3071, -0.3043 y -0.3071) y los dos últimos elementos (0.8999, 0.6108) y el valor de bias (-1.0722).
Después del entrenamiento, el modelo KLR predice la totalidad de los 21 elementos de datos correctamente. A continuación, el modelo se aplica a los cuatro elementos de datos de prueba, que se muestran como puntos negros en la Figura 2. El primer elemento de prueba tiene entradas (1.5 y 4.5) y una clase correcta de 0. El modelo de predicción predice correctamente ese elemento y también los otros tres elementos de prueba.
En este artículo se supone que tiene conocimientos intermedios o altos de programación, pero no se supone que sepa nada acerca de KLR. El programa de demostración se programa con C#, pero no debería resultar difícil refactorizarlo a otro lenguaje, como Java o Python, si lo desea. El programa de demostración es demasiado largo para presentarlo en su totalidad, pero el código fuente completo está disponible en la descarga de archivos que acompaña a este artículo.
Kernel de RBF
Una función de kernel mide la similitud de dos vectores o matrices. La función de kernel de RBF que mencioné anteriormente es la más común y es el tipo usado en el programa de demostración. Un valor de RBF de 1.0 significa que los dos vectores son idénticos. Los valores de RBF inferiores indican que dos vectores son menos parecidos.
La ecuación de RBF es:
K(v1, v2) = exp( - || v1 - v2 ||^2 / (2 * sigma^2) )
Aquí, K significa kernel; v1 y v2 son dos vectores con la misma longitud; sigma es un parámetro con un valor como 1.0 o 1.5; || indica la distancia euclidiana; y la función exp es el número de Euler (e = 2.71828) elevado a una potencia.
La función RBF se explica mejor a través de un ejemplo. Supongamos que v1 = (3.0, 1.0, 2.0), v2 = (1.0, 0.0, 5.0) y sigma es 1.5. En primer lugar, calcule la distancia euclidiana cuadrada:
|| v1 - v2 ||^2 = (3.0 - 1.0)^2 + (1.0 - 0.0)^2 + (2.0 - 5.0)^2
= 4.0 + 1.0 + 9.0
= 14.0
A continuación, divida la distancia cuadrada por 2 veces sigma al cuadrado:
14.0 / (2 * (1.5)^2) = 14.0 / 4.5 = 3.11
Por último, tome el número de Euler y elévelo al valor negativo del resultado anterior:
K(v1, v2) = e^(-3.11) = 0.0446
El valor pequeño del kernel indica que v1 y v2 no se parecen demasiado. El programa de demostración define la función de kernel RBF de la siguiente manera:
static double Kernel(double[] v1, double[] v2,
double sigma)
{
double num = 0.0;
for (int i = 0; i < v1.Length - 1; ++i)
num += (v1[i] - v2[i]) * (v1[i] - v2[i]);
double denom = 2.0 * sigma * sigma;
double z = num / denom;
return Math.Exp(-z);
}
La función supone que la última celda de cada matriz contiene la etiqueta de clase (0 o 1) y, por tanto, la última celda no se incluye en el cálculo. KLR usa la función de kernel para comparar un elemento de datos determinado con todos los elementos de aprendizaje y usa esa información para determinar una etiqueta de clase de predicción.
Regresión logística ordinaria
La regresión de logística ordinaria (LR) se explica mejor a través de un ejemplo. Supongamos que tiene tres variables predictoras: x0 = 2.5, x1 = 1.7 y x2 = 3.4. Un modelo LR regular crea un conjunto de constantes numéricas denominadas ponderaciones (wi), una para cada variable predictora y una constante numérica adicional denominada sesgo (b). Tenga en cuenta que el valor de sesgo de LR regular no es el mismo que el valor de sesgo de KLR que se muestra en la Figura 1.
Figura 1 Demostración de regresión de logística del kernel
Supongamos que w0 = 0.11, w1 = 0.33, w2 = 0.22 y b = 0.44. Para predecir la etiqueta de clase, 0 o 1, para los datos de entrada (2.5, 1.7, 3.4), calcule primero la suma de los productos de cada x y su valor w asociado, y agregue el sesgo:
z = (2.5)(0.11) + (1.7)(0.33) + (3.4)(0.22) + 0.44
= 2.024
Ahora, calcule p = 1.0 / (1.0 + exp(-z)):
p = 1.0 / (1.0 + exp(-2.024))
= 0.8833
El valor de p es la probabilidad de que el elemento de datos tenga la etiqueta de clase = 1, de modo que, si p es inferior a 0.5, su predicción es 0 y, si p es mayor que 0.5 (como en el ejemplo), su predicción es 1.
Bien. Sin embargo, ¿los valores de ponderaciones y sesgos se incluyen en LR regular? La idea es que determine los valores de ponderaciones y sesgos mediante un conjunto de datos de aprendizaje con valores de entrada conocidos y etiquetas de clase conocidas correctas, y que use un algoritmo de optimización para buscar los valores de ponderaciones y sesgos, para que las etiquetas de clase de predicción coincidan con los valores de etiquetas conocidos correctos. Existen muchos algoritmos que se pueden usar para buscar los valores de ponderación y sesgo de LR regular, incluido el ascenso de degradado con la probabilidad de logaritmo, el descenso de degradado con el error cuadrático, método Newton-Raphson iterado, optimización símplex, optimización por enjambre de partículas y L-BFGS.
La principal desventaja del LR regular es que solo puede controlar datos que sean separables linealmente. LR regular no puede controlar datos que no sean separables linealmente, como los datos de demostración de la Figura 2.
Figura 2 Datos de aprendizaje de regresión logística del kernel
Explicación de la regresión logística del kernel
El concepto KLR se explica mejor a través de un ejemplo. Permítame declarar por anticipado que, a primera vista, el concepto KLR no está muy estrechamente relacionado con el concepto LR ordinario. No obstante, las dos técnicas están muy relacionadas matemáticamente.
Supongamos que solo existen cuatro elementos de datos de aprendizaje:
td[0] = (2.0, 4.0, 0)
td[1] = (4.0, 1.0, 1)
td[2] = (5.0, 3.0, 0)
td[3] = (6.0, 7.0, 1)
Su objetivo es predecir la etiqueta de clase de x = (3.0, 5.0). Supongamos que el modelo KLR entrenado le proporcionó los siguientes valores de alpha y bias: alpha[0] = -0.3, alpha[1] = 0.4, alpha[2] = -0.2, alpha[3] =0.6, b = 0.1.
El primer paso es calcular la similitud de RBF entre el elemento de datos para predecir cada uno de los elementos de aprendizaje:
K(td[0], x) = 0.3679
K(td[1], x) = 0.0002
K(td[2], x) = 0.0183
K(td[3], x) = 0.0015
Observe que, en este punto, x se parece más a td[0] y td[2], ambos con la etiqueta de clase 0. A continuación, calcule la suma de los productos de cada valor de K y el valor de alpha asociado, y agregue el valor de bias:
z = (0.3679)(-0.3) + (0.0002)(0.4) + (0.0183)(-0.2) + (0.0015)(0.6) + 0.1
= -0.1120
Ahora, calcule p = 1.0 / (1.0 + exp(-z)):
p = 1.0 / (1.0 + exp(0.1120))
= 0.4720
Si el valor de p es mayor que 0.5, la clase de predicción es 1 y, si el valor de p es menor que 0.5, la clase de predicción es 0, como se puede observar (apenas) en este ejemplo.
Entrenamiento de un modelo KLR
El entrenamiento de un modelo KLR es el proceso de usar datos de aprendizaje para buscar los valores de alpha y el valor de bias. Expresado en pseudocódigo de muy alto nivel, el algoritmo de aprendizaje de KRL es el siguiente:
compute K(td[i], td[j]) for all i, j
loop maxIter times
for-each curr training item, i
for-each j: sum += alphas[j] * K(i j)
sum += bias
y = 1.0 / (1.0 + exp(-sum))
t = target class (0 or 1)
for-each j:
alpha[j] += eta * (t - y) * K(i, j)
bias += eta * (t - y) * 1.0
end-loop
La instrucción clave del código de demostración es alphas[j] += eta * (t - y) * kernelMatrix[i][j], que actualiza el valor de alpha del elemento de datos de aprendizaje en el índice [j] de acuerdo con el elemento de datos de aprendizaje en el índice [i]. Aquí, t es la clase de destino conocida correcta, 0 o 1, e y es una probabilidad calculada de que el elemento en [i] tenga la clase 1.
Por ejemplo, supongamos que un valor de alpha de un elemento de aprendizaje es actualmente 0.1234, la clase de destino es 1 y la probabilidad calculada es 0.60. La predicción actual sería correcta, pero le gustaría que el valor de p se aproximara más a 1. Supongamos que la similitud entre los dos elementos es K(i, j) = 0.70 y que la velocidad de aprendizaje eta es 0.10. El nuevo valor de alpha sería:
alpha = 0.1234 + 0.10 * (1 - 0.60) * 0.70
= 0.1234 + 0.0280
= 0.1514
Dado que el valor de alpha es un multiplicador en el cálculo de la probabilidad, el nuevo valor ligeramente más alto de alpha aumentará un poco el valor de p, con lo que la predicción será más precisa.
El programa de demostración
Para programar el programa de demostración, inicié Visual Studio, creé un nuevo programa de aplicación de consola de C# y lo denominé KernelLogistic. Usé Visual Studio 2015, pero el programa de demostración no tiene dependencias significativas de .NET Framework, por lo que cualquier versión reciente de Visual Studio funcionará.
Después de cargar el código de plantilla en la ventana del editor, hice clic con el botón derecho en el archivo Program.cs en la ventana del Explorador de soluciones y cambié el nombre del archivo a KernelLogisticProgram.cs. Luego, permití que Visual Studio cambiara automáticamente el nombre de la clase Program por mí. En la parte superior del código generado por la plantilla, eliminé todas las instrucciones using innecesarias y dejé solo la que hace referencia al espacio de nombres System de nivel superior. A continuación, creé la instancia de un objeto Random:
using System;
namespace KernelLogistic
{
class KernelLogisticProgram
{
static Random rnd = new Random(0);
static void Main(string[] args)
{
Console.WriteLine(“Begin KLR demo”);
int numFeatures = 2;
...
Para simplificar el proceso, programé la demostración con un enfoque de método estático (en lugar de usar la programación orientada a objetos) y quité todas las comprobaciones de errores normales. El método Main configura los 21 elementos de aprendizaje y los 4 elementos de prueba de la siguiente manera:
double[][] trainData = new double[21][];
trainData[0] = new double[] { 2.0, 3.0, 0 };
...
trainData[20] = new double[] { 5.0, 6.0, 1 };
double[][] testData = new double[4][];
testData[0] = new double[] { 1.5, 4.5, 0 };
...
testData[3] = new double[] { 5.5, 5.5, 1 };
En un escenario que no sea de demostración, probablemente leería los datos de un archivo de texto. A continuación, se inicializan los valores de alpha:
int numTrain = trainData.Length;
int numTest = testData.Length;
double[] alphas = new double[numTrain + 1];
for (int i = 0; i < alphas.Length; ++i)
alphas[i] = 0.0;
Al programar sistemas de aprendizaje automático, suele haber varias maneras de tratar los valores de sesgo. Aquí, almaceno el valor de sesgo de KLR en la última celda de la matriz de alphas. Un diseño alternativo es crear una variable independiente aparte. A continuación, se calculan las similitudes del kernel entre todos los pares de elementos de aprendizaje:
double[][] kernelMatrix = new double[numTrain][];
for (int i = 0; i < kernelMatrix.Length; ++i)
kernelMatrix[i] = new double[numTrain];
double sigma = 1.0;
for (int i = 0; i < numTrain; ++i) {
for (int j = 0; j < numTrain; ++j) {
double k = Kernel(trainData[i], trainData[j], sigma);
kernelMatrix[i][j] = kernelMatrix[j][i] = k;
}
}
Dado que solo existen 21 elementos de datos, renuncio a la eficacia en favor de la simplicidad. Podría haber reducido el número de cálculos del kernel usando los datos K(v1, v2) = K(v2, v1) y K(v, v) = 1. A continuación, el programa de demostración se prepara para el aprendizaje:
double eta = 0.001;
int iter = 0;
int maxIter = 1000;
int[] indices = new int[numTrain];
for (int i = 0; i < indices.Length; ++i)
indices[i] = i;
Los valores de eta y maxIter se determinaron mediante prueba y error. La idea tras la matriz denominada indices es que, durante el aprendizaje, es importante visitar los elementos de aprendizaje en orden aleatorio en cada pasada, para evitar llegar a una situación en que el aprendizaje se detenga o vaya de un lado a otro. El bucle de aprendizaje principal comienza de la siguiente manera:
while (iter < maxIter) {
Shuffle(indices);
for (int idx = 0; idx < indices.Length; ++idx) {
int i = indices[idx];
El método Shuffle es una aplicación auxiliar que codifica el orden de los elementos de aprendizaje con el minialgoritmo de Fisher-Yates. La etiqueta de clase de destino y la probabilidad de predicción del elemento de aprendizaje actual se calculan de la siguiente manera:
double sum = 0.0;
for (int j = 0; j < alphas.Length-1; ++j)
sum += alphas[j] * kernelMatrix[i][j];
sum += alphas[alphas.Length - 1];
double y = 1.0 / (1.0 + Math.Exp(-sum));
double t = trainData[i][numFeatures];
Observe que en este diseño se supone que la etiqueta de clase se encuentra en la última celda de una matriz de datos de aprendizaje. A continuación, los valores de alphas y beta se actualizan:
for (int j = 0; j < alphas.Length - 1; ++j)
alphas[j] = alphas[j] +
(eta * (t - y) * kernelMatrix[i][j]);
alphas[alphas.Length-1] = alphas[alphas.Length - 1] +
(eta * (t - y)) * 1;
}
++iter;
} // While (train)
La actualización del valor de sesgo usa un valor ficticio de 1 en lugar del valor de similitud del kernel, solo para que la simetría de la relación resulte más clara. Evidentemente, puede quitar la multiplicación por 1 porque no tiene ningún efecto. Después del aprendizaje, aparecen algunos de los valores de alphas y el valor de bias como se muestra en la Figura 1.
El programa de demostración concluye con el cálculo de la precisión de clasificación del modelo KLR entrenado en los datos de aprendizaje y de prueba:
double accTrain = Accuracy(trainData, trainData,
alphas, sigma, false);
Console.WriteLine(“accuracy = “ +
accTrain.ToString(“F4”) + “\n”);
double accTest = Accuracy(testData, trainData,
alphas, sigma, true); // Verbose
El argumento booleano pasado al método Accuracy indica si el cálculo se realiza en modo detallado (con mensajes de diagnóstico) o en modo silencioso.
Resumen
La regresión logística del kernel no se usa muy a menudo, como mínimo en mi entorno. Su principal ventaja es la simplicidad. La principal desventaja de KLR es que no se escala bien en grandes conjuntos de datos, ya que se tienen que calcular previamente todos los valores de similitud del kernel entre elementos y guardarlos, o bien se deben mantener todos los datos de aprendizaje y, a continuación, calcular los valores de similitud sobre la marcha para cada predicción.
KLR está diseñado para la clasificación de valores binarios. KLR se puede extender para abordar problemas de clasificación con tres o más valores de clase, pero, en mi opinión, existen mejores alternativas, como una red neuronal con realimentación de una capa oculta. KLR presenta algunas similitudes con el algoritmo de clasificación de los vecinos más próximos de K (K-NN), y también con la clasificación de máquinas de vectores de soporte (SVM).
El Dr. James McCaffrey trabaja para Microsoft Research en Redmond, Washington. Ha colaborado en el desarrollo de varios productos de Microsoft como, por ejemplo, Internet Explorer y Bing. Puede Puede ponerse en contacto con McCaffrey en jamccaff@microsoft.com.
Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: Chris Lee y Adith Swaminathan