Precisión de clasificación de clases múltiples por clase usando PyTorch — Visual Studio Magazine
Laboratorio de ciencia de datos
Precisión de clasificación de clases múltiples por clase usando PyTorch
Dr. de Microsoft Research. James McCaffrey: Cuando los datos de varias clases están sesgados hacia una o más clases, es muy importante analizar la precisión de clase.
Un problema de clasificación multiclase es aquel cuyo objetivo es predecir un valor discreto donde hay tres o más posibilidades. Por ejemplo, es posible que desee predecir la inclinación política de una persona (conservadora, moderada, liberal) en función de su sexo, edad, estado en el que vive e ingresos.
Un enfoque ingenuo para evaluar el rendimiento de un modelo multiclase entrenado es calcular la precisión del modelo en todos los datos de prueba. Pero suponga que sus datos están sesgados hacia una clase en particular. Por ejemplo, si la mayoría de los elementos de datos son de clase media (por ejemplo, 900 de 1000) y solo unos pocos son conservadores (por ejemplo, 40 de 1000) y la clase es liberal (60 de 1000), entonces el modelo predictivo para cualquier entrada dada, la clase promedio obtendrá un 90 por ciento de precisión.
Este artículo muestra cómo calcular la precisión de clasificación para una clase. Una buena manera de ver a dónde va este artículo es mirar una captura de pantalla del programa de demostración. Figura 1. La demostración comienza cargando un conjunto de datos de entrenamiento de 200 elementos y un conjunto de datos de prueba de 40 elementos.
La precisión de clasificación general del modelo en los datos de prueba después del entrenamiento es del 75,00 por ciento. Luego, la demostración calcula la precisión para cada una de las tres clases: conservadora (54,55 por ciento), moderada (92,86 por ciento) y liberal (73,33 por ciento). Interpretar la precisión por clase es algo subjetivo, pero los resultados de la demostración son razonables.
Python y PyTorch deben estar instalados en su máquina para ejecutar la demostración. Las demostraciones se desarrollaron en Windows 10/11 utilizando la distribución Anaconda 2020.02 de 64 bits (que contiene Python 3.7.6) y PyTorch versión 1.12.1 para CPU instalada a través de pip. Las instrucciones de instalación detalladas paso a paso para esta configuración se pueden encontrar en las publicaciones de mi blog. aquí y aquí.
Puede encontrar el código fuente del programa de demostración completo aquí y también se adjunta al artículo como un archivo comprimido descargable.
Información
El formato de los datos de entrenamiento y prueba se ve así:
1 0.24 1 0 0 0.2950 2 -1 0.39 0 0 1 0.5120 1 1 0.63 0 1 0 0.7580 0 -1 0.36 1 0 0 0.4450 1 1 0.27 0 1 0 0.2860 2 . . .
Cada línea de datos separados por tabuladores representa a un individuo. Los campos son sexo (masculino = -1, femenino = +1), edad (dividido por 100), estado (Michigan = 100, Nebraska = 010, Oklahoma = 001), ingreso (dividido por $100 000) y tipo político (conservador = 0, moderado = 1, liberal = 2).
Puede encontrar información completa sobre capacitación y pruebas aquí.
La demostración define la clase PeopleDataset Lista 1. Una instancia de un objeto PeopleDataset se puede pasar al DataLoader para entrenamiento o usarse directamente para calcular la precisión de la clasificación.
Listado 1: Definición de clase PeopleDataset
class PeopleDataset(T.utils.data.Dataset): def __init__(self, src_file): all_xy = np.loadtxt(src_file, usecols=range(0,7), delimiter="\t", comments="#", dtype=np.float32) tmp_x = all_xy[:,0:6] # cols [0,6) = [0,5] tmp_y = all_xy[:,6] # 1-D self.x_data = T.tensor(tmp_x, dtype=T.float32).to(device) self.y_data = T.tensor(tmp_y, dtype=T.int64).to(device) # 1-D def __len__(self): return len(self.x_data) def __getitem__(self, idx): preds = self.x_data[idx] trgts = self.y_data[idx] return preds, trgts # as a Tuple
Programa de demostración
Se presenta la estructura del programa de demostración.
Lista 2. Prefiero ingresar mis programas de Python con dos espacios en lugar de los cuatro más comunes. En Python, la barra invertida se usa para la continuación de línea.
Listado 2: Estructura general del programa de demostración
# people_politics.py # predict politics type from sex, age, state, income # PyTorch 1.12.1-CPU Anaconda3-2020.02 Python 3.7.6 # Windows 10/11 # compute accuracy by class import numpy as np import torch as T device = T.device('cpu') # apply to Tensor or Module # ----------------------------------------------------------- class PeopleDataset(T.utils.data.Dataset): . . . # ----------------------------------------------------------- class Net(T.nn.Module): def __init__(self): super(Net, self).__init__() self.hid1 = T.nn.Linear(6, 10) # 6-(10-10)-3 self.hid2 = T.nn.Linear(10, 10) self.oupt = T.nn.Linear(10, 3) T.nn.init.xavier_uniform_(self.hid1.weight) T.nn.init.zeros_(self.hid1.bias) T.nn.init.xavier_uniform_(self.hid2.weight) T.nn.init.zeros_(self.hid2.bias) T.nn.init.xavier_uniform_(self.oupt.weight) T.nn.init.zeros_(self.oupt.bias) def forward(self, x): z = T.tanh(self.hid1(x)) z = T.tanh(self.hid2(z)) z = T.log_softmax(self.oupt(z), dim=1) # NLLLoss() return z # ----------------------------------------------------------- def accuracy(model, ds): # assumes model.eval() # item-by-item version n_correct = 0; n_wrong = 0 for i in range(len(ds)): X = ds[i][0].reshape(1,-1) # make it a batch Y = ds[i][1].reshape(1) # 0 1 or 2, 1D with T.no_grad(): oupt = model(X) # logits form big_idx = T.argmax(oupt) # 0 or 1 or 2 if big_idx == Y: n_correct += 1 else: n_wrong += 1 acc = (n_correct * 1.0) / (n_correct + n_wrong) return acc # ----------------------------------------------------------- def do_acc(model, dataset, n_classes): X = dataset[0:len(dataset)][0] # all X values Y = dataset[0:len(dataset)][1] # all Y values with T.no_grad(): oupt = model(X) # [40,3] all logits for c in range(n_classes): idxs = np.where(Y==c) # indices where Y is c logits_c = oupt[idxs] # logits corresponding to Y == c arg_maxs_c = T.argmax(logits_c, dim=1) # predicted class num_correct = T.sum(arg_maxs_c == c) acc_c = num_correct.item() / len(arg_maxs_c) print("%0.4f " % acc_c) # ----------------------------------------------------------- def main(): # 0. get started print("Begin People predict politics type ") T.manual_seed(1) np.random.seed(1) # 1. create DataLoader objects print("Creating People Datasets ") train_file = ".\\Data\\people_train.txt" train_ds = PeopleDataset(train_file) # 200 rows test_file = ".\\Data\\people_test.txt" test_ds = PeopleDataset(test_file) # 40 rows bat_size = 10 train_ldr = T.utils.data.DataLoader(train_ds, batch_size=bat_size, shuffle=True) # ----------------------------------------------------------- # 2. create network print("Creating 6-(10-10)-3 neural network ") net = Net().to(device) net.train() # ----------------------------------------------------------- # 3. train model max_epochs = 1000 ep_log_interval = 100 lrn_rate = 0.01 loss_func = T.nn.NLLLoss() # assumes log_softmax() optimizer = T.optim.SGD(net.parameters(), lr=lrn_rate) print("bat_size = %3d " % bat_size) print("loss = " + str(loss_func)) print("optimizer = SGD") print("max_epochs = %3d " % max_epochs) print("lrn_rate = %0.3f " % lrn_rate) print("Starting training") for epoch in range(0, max_epochs): epoch_loss = 0 # for one full epoch for (batch_idx, batch) in enumerate(train_ldr): X = batch[0] # inputs Y = batch[1] # correct class/label/politics optimizer.zero_grad() oupt = net(X) loss_val = loss_func(oupt, Y) # a tensor epoch_loss += loss_val.item() # accumulate loss_val.backward() optimizer.step() if epoch % ep_log_interval == 0: print("epoch = %5d | loss = %10.4f" % \ (epoch, epoch_loss)) print("Training done ") # ----------------------------------------------------------- # 4. evaluate model accuracy print("Computing overall model accuracy") net.eval() acc_train = accuracy(net, train_ds) # item-by-item print("Accuracy on training data = %0.4f" % acc_train) acc_test = accuracy(net, test_ds) print("Accuracy on test data = %0.4f" % acc_test) print("Accuracy on test by class (fast technique): ") do_acc(net, test_ds, 3) # 5. make a prediction print("Predicting for M 30 oklahoma $50,000: ") X = np.array([[-1, 0.30, 0,0,1, 0.5000]], dtype=np.float32) X = T.tensor(X, dtype=T.float32).to(device) with T.no_grad(): logits = net(X) # do not sum to 1.0 probs = T.exp(logits) # sum to 1.0 probs = probs.numpy() # numpy vector prints better np.set_printoptions(precision=4, suppress=True) print(probs) # 6. save model (state_dict approach) print("Saving trained model state") # fn = ".\\Models\\people_model.pt" # T.save(net.state_dict(), fn) print("End People predict politics demo") if __name__ == "__main__": main() main()
Toda la lógica de control está en la función main() definida por el programa. El programa comienza con algunas declaraciones preparatorias:
import numpy as np import torch as T device = T.device('cpu') # apply to Tensor or Module def main(): # 0. get started print("Begin People predict politics type ") T.manual_seed(1) np.random.seed(1) . . .
Una vez que los datos se cargan en la memoria, se crea y entrena un clasificador multiclase. El entrenamiento se lleva a cabo utilizando técnicas estándar.
Evaluación de la precisión por clase
Se proporciona una función clave do_acc() que define la precisión por clase Lista 3. La función acepta un objeto Dataset que almacena los datos de entrenamiento o prueba.
Listado 3: Precisión por función de clase
def do_acc(model, dataset, n_classes): X = dataset[0:len(dataset)][0] # all X values Y = dataset[0:len(dataset)][1] # all Y values with T.no_grad(): oupt = model(X) # [40,3] all logits for c in range(n_classes): idxs = np.where(Y==c) # indices where Y is c logits_c = oupt[idxs] # logits corresponding to Y == c arg_maxs_c = T.argmax(logits_c, dim=1) # predicted class num_correct = T.sum(arg_maxs_c == c) acc_c = num_correct.item() / len(arg_maxs_c) print("%0.4f " % acc_c)
Primero, todas las entradas se almacenan en X y todas las salidas objetivo se almacenan en Y. Las salidas tienen la forma de logits, donde la ubicación de la salida más grande corresponde a la clase (0, 1, 2). La salida de do_acc() es una instrucción print() que resume la clasificación de cada clase. En un escenario que no sea de demostración, es posible que desee devolver una matriz acc_c que contenga los resultados.
no envolver
Cuando los datos de varias clases están sesgados en una o más clases, es muy importante analizar la precisión de las clases. Esto significa que antes de comenzar a trabajar en un sistema de predicción multiclase, casi siempre es una buena idea analizar los datos de entrenamiento para determinar si hay datos sesgados por clases.
Sobre el Autor
El Dr. James McCaffrey trabaja para Microsoft Research en Redmond, Washington. Ha trabajado en varios productos de Microsoft, incluidos Azure y Bing. James puede ser contactado [email protected].