entrenar un clasificador
Aquí has visto cómo definir una red neuronal, calcular la pérdida y actualizar los pesos de la red. ahora podrías estar pensando
¿Qué son los datos?
En general, cuando se trata de datos de imagen, texto, audio o video, puede usar paquetes estándar de Python para cargar los datos en matrices numpy. Luego puede convertir esta matriz en antorcha. * Tensor.
- Para imágenes, puede usar Pillow, OpenCV
- Para audio, puedes usar scipy y librosa
- Para el texto, use Python o Cython sin formato o NLTK y SpaCy
Especialmente para la visión, creamos un paquete llamado torchvision, que tiene cargadores de datos para conjuntos de datos públicos, como ImageNet, CIRFAR10, MNIST, etc. Imagen a datos. torchvision.datasets y torch.utils.data.DataLoader.
Esto proporciona una gran comodidad y evita escribir código repetitivo.
Para este tutorial, usamos el conjunto de datos CIFAR10. Tiene categorías: 'avión', 'automóvil', 'pájaro', 'gato', 'venado', 'perro', 'rana', 'caballo', 'barco', 'camión'. La imagen de CIFAR10 es 3 * 32 * 32
entrenar un clasificador de imágenes
Realizaremos los siguientes pasos:
- Cargue y normalice los conjuntos de datos de prueba y entrenamiento de CIFAR10 con torchvision
- Definir una red neuronal convolucional
- definir una función de pérdida
- Entrenar la red en el conjunto de entrenamiento
- Probar la red en el equipo de prueba
Cargar y normalizar CIFAR10
Usando torchvision, cargar CIFAR10 es muy simple
import torch
import torchvision
import torchvision.transforms as transforms
El conjunto de datos de salida de torchvision es un PILImage en el rango [0,1]. Los convertimos al rango estándar [-1,1] para tensores.
**Nota:** Si hay un BrokenPipeError en la plataforma de Windows, establezca num_worker en torch.utils.data.DataLoader() en 0
import matplotlib.pyplot as plt
import numpy as np
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
)
batch_size = 4
trainset = torchvision.datasets.CIFAR10(root='./cifar10',
train=True,
download=True,
transform=transform)
trainloader = Data.DataLoader(trainset,
batch_size=batch_size,
shuffle=True,
num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./cifar10', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
afuera:
Descargando https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz a ./cifar10\cifar-10-python.tar.gz
170500096it [02:27, 1156941.89it/s]
Muestra algunas imágenes del entrenamiento.
afuera:
Archivos ya descargados y verificados
perro barco avión barco
Definir una red neuronal convolucional
Copie la red neuronal del capítulo de redes neuronales y modifíquela para obtener una imagen de 3 canales.
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 单通道图像输入, 输出6通道, 5x5 卷积核
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2,2)
self.conv2 = nn.Conv2d(6, 16, 5)
# 一个仿射变换操作(Affine) : y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120) # 5*5 来自图像的维度
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max池化,窗口为(2,2)
x = self.pool(F.relu(self.conv1(x)))
# 如果尺寸是正方形, 你可以用一个数字来指定
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # 批处理除外,所有数据降维展平,意思就是二维图像转成一行数组。
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
Definir la función de pérdida y el optimizador
Usamos error de entropía cruzada y SGD con impulso
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
red de formación
Aquí es donde se pone interesante, recorremos el iterador de datos, alimentamos los datos a la red y optimizamos.
for epoch in range(2):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# 获取输入;数据是一个list类型的[inputs, labels]
inputs, labels = data
# 梯度参数设为0
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 打印统计数据
running_loss += loss.item()
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))
print('Finished Training')
afuera:
[1, 2000] pérdida: 2,211
[1, 4000] pérdida: 1,825
[1, 6000] pérdida: 1,648
[1, 8000] pérdida: 1,562
[1, 10000] pérdida: 1,504
[1, 12000] pérdida: 1,448
[2 , 2000] pérdida: 1,397
[2, 4000] pérdida: 1,353
[2, 6000] pérdida: 1,341
[2, 8000] pérdida: 1,313
[2, 10000] pérdida: 1,270
[2, 12000] pérdida: 1,280
Entrenamiento terminado
Guardemos rápidamente nuestro modelo entrenado
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
Probar la red en el equipo de prueba
Entrenamos la red 2 veces en el conjunto de datos de entrenamiento. Pero tenemos que comprobar que la red ha aprendido todo. Verificaremos esto prediciendo las etiquetas de clase generadas por la red neuronal y validándolas contra la verdad básica. Si la predicción es correcta, agregamos esa muestra a la lista de predicciones correctas.
El primer paso es mostrar una imagen del conjunto de prueba
dataiter = iter(testloader)
images, labels = dataiter.next()
# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
afuera:
GroundTruth: gato barco barco avión
A continuación, cargamos el modelo previamente guardado (nota: no es necesario guardar y volver a cargar el modelo aquí, lo hacemos solo para ilustrar cómo hacerlo)
net = Net()
net.load_state_dict(torch.load(PATH))
Ahora veamos cómo ve la red neuronal los ejemplos anteriores:
outputs = net(images)
La salida es la energía de 10 clases. Cuanto mayor sea la energía de una clase, más pensará la red que la imagen pertenece a esta clase. Entonces, obtengamos el índice de mayor energía:
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))
afuera:
Predicted: frog ship ship ship
Vea cómo funciona la red en todo el conjunto de datos
correct = 0
total = 0
# 由于我们没有训练,我们不需要为我们的输出计算梯度
with torch.no_grad():
for data in testloader:
images, labels = data
# 图像通过网络计算输出
outputs = net(images)
# 我们选择能量最高的类型作为预测 _,
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))
afuera:
Precisión de la red en las 10000 imágenes de prueba: 54 %
Esto se ve mucho mejor que probabilístico (10% de precisión) (elegir al azar un tipo de 10). Parece que la red ha aprendido algo.
Hmmm, qué tipos funcionan bien y qué tipos no.
# 对每个类型准备计数
correct_pred = {
classname: 0 for classname in classes}
total_pred = {
classname: 0 for classname in classes}
# 不需要梯度
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predictions = torch.max(outputs, 1)
# 对每一个类型预测正确解收集
for label, prediction in zip(labels, predictions):
if label == prediction:
correct_pred[classes[label]] += 1
total_pred[classes[label]] += 1
# 打印每个类型的准确率
for classname, correct_count in correct_pred.items():
accuracy = 100 * float(correct_count) / total_pred[classname]
print("Accuracy for class {:5s} is: {:.1f} %".format(classname, accuracy))
afuera:
La precisión para la clase avión es: 60,1 %
La precisión para la clase coche es: 69,8 %
La precisión para la clase pájaro es: 45,2 %
La precisión para la clase gato es: 26,4 % La
precisión para la clase ciervo es: 30,5 % La
precisión para la clase perro es: 60,4 % La
precisión para la clase rana es: 70,7 %
La precisión para la clase caballo es: 69,5 %
La precisión para la clase barco es: 53,3 %
La precisión para la clase camión es: 61,0 %
entrenar en GPU
Al igual que coloca tensores en la GPU, coloque la red neuronal en la GPU.
Primero defina nuestro dispositivo como cuda primero visible, si cuda está disponible:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")# 假设我们在CUDA机器,这里应该打印CUDA设备print(device)
afuera:
cuda:0
Estos métodos luego pasarán recursivamente por todos los módulos y convertirán sus argumentos y búferes en tensores CUDA:
net.to(device)
Recuerde que también envía la entrada y el destino a la GPU en cada paso
inputs, labels = data[0].to(device), data[1].to(device)
¿Por qué no hay énfasis en la aceleración de MASSIVE en comparación con la CPU? Porque su red es demasiado pequeña.