minhas informações de contato
Correspondência[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Para compreender melhor as redes neurais, é muito apropriado compreender o princípio de funcionamento e o processo geral das redes neurais, camada por camada, a partir da pequena tarefa de reconhecimento de dígitos manuscritos.
Este artigo irá completar uma tarefa de reconhecimento de números por escrita manual para explicar como projetar, implementar e treinar uma rede neural feedforward padrão, a fim de ter uma compreensão mais específica e perceptiva das redes neurais.
Especificamente, precisamos projetar e treinar uma rede neural de 3 camadas. Esta rede neural receberá imagens digitais como entrada. Após o cálculo pela rede neural, ela identificará os números na imagem, realizando assim a classificação das imagens digitais:
Neste processo, três aspectos são explicados principalmente: o projeto e implementação de redes neurais, a preparação e processamento de dados de treinamento e o processo de treinamento e teste do modelo.
Para projetar uma rede neural para processar dados de imagem, é necessário primeiro esclarecer o tamanho e o formato dos dados de imagem de entrada.
A imagem que vamos processar é uma imagem de canal cinza de tamanho 28 × 28 pixels (o formato do próprio conjunto de dados MNIST).
Esta imagem cinza inclui 2828 = 784 pontos de dados, precisamos nivelá-los para 1 primeiroVetor de tamanho 784:
Em seguida, insira esse vetor na rede neural. Usaremos uma rede neural de três camadas para processar o vetor x correspondente à imagem. A camada de entrada precisa receber o vetor de imagem x de 784 dimensões. rede neural a receber, então a camada de entrada contém 784 neurônios:
A camada oculta é usada para extração de recursos, processando os vetores de recursos de entrada em vetores de recursos de nível superior.
Como a imagem do dígito manuscrito não é complexa, aqui definimos o número de neurônios na camada oculta para 256, de modo que haja uma camada linear de tamanho 784*256 entre a camada de entrada e a camada oculta:
Ele pode converter um vetor de entrada de 784 dimensões em um vetor de saída de 256 dimensões, que continuará a se propagar para a camada de saída.
Como a imagem digital será finalmente reconhecida como dez números possíveis de 0 a 9, a camada de saída precisa definir 10 neurônios para corresponder a estes dez números:
Depois que o vetor de 256 dimensões é calculado através da camada linear entre a camada oculta e a camada de saída, um resultado de saída de 10 dimensões é obtido. Este vetor de 10 dimensões representa a pontuação de previsão de 10 números:
Para continuar a obter a probabilidade prevista de 10 números, também precisamos inserir a saída da camada de saída na camada softmax. A camada softmax converterá o vetor de 10 dimensões em 10 valores de probabilidade P0 a P9. o valor de probabilidade corresponde a um número. Ou seja, a possibilidade de a imagem de entrada ser um determinado número. Além disso, a soma dos 10 valores de probabilidade de P0 a P9 é 1. Isso é determinado pelas propriedades do. Função softmax:
A ideia acima é o design da rede neural. A seguir, usamos a estrutura PyTorch para implementá-la.
Primeiro implemente nossa rede neural:
código mostrado abaixo:
import torch
from torch import nn
# 定义神经网络
class NetWork(nn.Module):
def __init__(self):
super().__init__()
# 线性层1,输入层和隐藏层之间的线性层
self.layer1 = nn.Linear(784, 256)
# 线性层2,隐藏层和输出层之间的线性层
self.layer2 = nn.Linear(256, 10)
# 这里没有直接定义 softmax 层
# 这是因为后面会使用 CrossEntropyLoss 损失函数
# 在这个损失函数中会实现 softmax 的计算
def forward(self, x):
x = x.view(-1, 28 * 28) # 使用view 函数将 x 展平
x = self.layer1(x) # 将 x 输入至 layer1
x = torch.relu(x) # 使用 ReLu 函数激活
return self.layer2(x) # 输入至 layer2 计算结果
Em seguida, prepare o conjunto de dados:
Para obter o conjunto de dados, você também pode usar o seguinte método para baixá-lo do site oficial do PyTorch:
# 准备数据集
train_data = torchvision.datasets.MNIST(root='data', train=True, download=True)
test_data = torchvision.datasets.MNIST(root='data', train=False, download=True)
Os dados baixados desta forma serão armazenados automaticamente em train_data e test_data. Da mesma forma, um pacote de ferramentas será baixado no diretório que especificamos no código:
Mas este não é um método universal. No trabalho e estudo futuros, haverá muitos conjuntos de dados que precisaremos para operar e processar nós mesmos, o que não será tão conveniente quanto o acima. Portanto, um método mais geral é introduzido aqui. que consiste em baixar os dados nativos do site oficial. Um conjunto de dados é, sim, um monte de imagens!
Salvamos os dados baixados nas duas pastas a seguir:
Salvamos os dados em dois diretórios, train e test respectivamente, onde train possui 60.000 dados e test possui 10.000 dados, que são usados para treinamento e teste de modelo respectivamente.
Os diretórios train e test incluem dez subdiretórios, e os nomes dos subdiretórios correspondem aos números na imagem. Por exemplo, uma imagem do número 3 é salva em uma pasta chamada 3:
onde o nome da imagem é uma assinatura de string aleatória.
Após concluir a preparação dos dados, a função de leitura de dados é implementada. Os iniciantes só precisam conhecer o processo geral de processamento de dados ao aprender esta parte.
O código é implementado da seguinte forma:
# 首先实现图像的预处理pipeline
transform = torchvision.transforms.Compose([
torchvision.transforms.Grayscale(num_output_channels=1), # 转换为单通道灰度图像
torchvision.transforms.ToTensor() # 转换为PyTorch支持的张量
])
# 这是方式一准备数据集的方式嗷
train_data = torchvision.datasets.MNIST(root='data', train=True, download=True, transform=transform)
test_data = torchvision.datasets.MNIST(root='data', train=False, download=True, transform=transform)
# 下面这是方式二准备数据集的方式
# 使用 ImageFolder 函数读取数据文件夹,构建数据集 dataset
# 这个函数会将保存数据的文件夹的名字,作为数据的标签,组织数据
# 例如,对于名字为 3 的文件夹
# 就会将 3 作为文件夹中图像数据的标签,和图像配对用于后续的训练,使用起来非常方便
train_dataset = torchvision.datasets.ImageFolder(root='./mnist/train', transform=transform)
test_dataset = torchvision.datasets.ImageFolder(root='./mnist/test', transform=transform)
# 不管使用哪种准备数据集的方式,最后的效果都是一样的
# 打印它们的长度看一下
print(len(train_data))
print(len(test_data))
Dê uma olhada nos resultados da corrida:
Você pode ver que tanto os dados de treinamento quanto os dados de teste foram obtidos, o que é totalmente consistente com o que dissemos antes.
Em seguida, usamos train_loader para implementar a leitura de dados em pequenos lotes:
# 使用 train_loader 实现小批量的数据读取
# 这里设置小批量的大小,batch_size = 64。
# 也就是每个批次包括 64 个数据
train_loader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
# 打印 train_loader 的长度
print(" train loader length: ", len(train_loader))
# 60000 个训练数据,如果每个小批量读入 64 个样本,那么 60000 个数据会被分为 938 组
# 计算 938 * 64 = 60032,这说明最后一组会不够 64 个数据
Os resultados da execução são os seguintes:
Podemos então percorrer train_loader para obter cada minilote de dados:
# 循环遍历 train_loader
# 每一次循环,都会取出 64 个图像数据,作为一个小批量 batch
for batch_idx, (data, label) in enumerate(train_loader):
if batch_idx == 3: # 打印前三个 batch 观察
break
print("batch_idx: ", batch_idx)
print("data.shape: ", data.shape) # 数据的尺寸
print("label: ", label.shape) # 图像中的数字
print(label)
Aqui está uma explicação simples da instrução de loop acima que a tornará mais clara:
enumerar(): Esta é a função integrada do Python, que é usada para combinar um objeto de dados percorrível (como uma lista, tupla ou string) em uma sequência de índice e listar os dados e subscritos de dados ao mesmo tempo, ou seja, obter o índice e valor ao mesmo tempo. Aqui, ele é usado para iterar cada lote no train_loader, onde batch_idx é o índice do lote atual (começando em 0), (dados, rótulo) são os dados e o rótulo do lote atual.
para batch_idx, (dados, rótulo) em enumerate(train_loader):: O significado desta instrução de loop é que para cada lote em train_loader, o código no corpo do loop é executado.Em cada iteração do loop, batch_idx é o índice do lote atual, (dados, rótulo) são os dados e o rótulo do lote atual . os dados são geralmente um tensor contendo vários pontos de dados, cada ponto de dados é uma amostra é o tensor de rótulo correspondente a esses pontos de dados, que é usado para o valor alvo na aprendizagem supervisionada;
O efeito de execução é o seguinte:
Isso pode ser visto nos resultados da execução:
1. batch_idx = 0 significa que é o primeiro lote de dados
2. data.shape indica que o tamanho dos dados é [64, 1, 28, 28]
O tamanho acima significa que cada lote de dados inclui 64 imagens, cada imagem possui 1 canal cinza e o tamanho da imagem é 28*28.
3. label.shape significa que existem 64 números no lote e o número total de rótulos correspondentes a eles é 64. Cada número possui um rótulo.
Preste atenção na diferença. Deve haver apenas 9 categorias de rótulos reais, porque os números são apenas de 1 a 9, e aqui significa que existem 64 valores de rótulos.
4. A matriz tensorial representa o valor do rótulo correspondente a cada uma dessas 64 imagens digitais.
Com os preparativos anteriores feitos, podemos começar a treinar e testar o modelo.
Aqui está o código de treinamento:
# 在使用 PyTorch 训练模型时,需要创建三个对象
model = NetWork() # 1、模型本身,它就是我们设计的神经网络
optimizer = torch.optim.Adam(model.parameters()) # 2、优化器,优化模型中的参数
criterion = nn.CrossEntropyLoss() # 3、损失函数,分类问题使用交叉熵损失误差
# 开始训练模型
for epoch in range(10): # 外层循环,代表了整个训练数据集的遍历次数
# 整个训练集要循环多少轮,是10次还是20次还是100次都是有可能的
# 内层循环使用train_loader 进行小批量的数据读取
for batch_idx, (data, label) in enumerate(train_loader):
# 内层每循环一次,就会进行一次梯度下降算法
# 包括五个步骤:
output = model(data) # 1、计算神经网络的前向传播结果
loss = criterion(output, label) # 2、计算 output 和标签 label 之间的误差损失 loss
loss.backward() # 3、使用 backward 计算梯度
optimizer.step() # 4、使用 optimizer.step 更新参数
optimizer.zero_grad() # 5、将梯度清零
# 这五个步骤是使用 PyTorch 框架训练模型的定式,初学的时候记住就可以了
# 每迭代 100 个小批量,就打印一次模型的损失,观察训练的过程
if batch_idx % 100 == 0:
print(f"Epoch {epoch + 1} / 10 "
f"| Batch {batch_idx} / {len(train_loader)}"
f"| Loss: {loss.item():.4f}")
torch.save(model.state_dict(), 'mnist.pth') # 最后保存训练好的模型
O efeito de execução é o seguinte:
Omita o meio…
Pode-se observar que o valor final da perda é muito, muito pequeno, 0,0239.
A última etapa é o teste. O processo de teste é basicamente igual ao treinamento.
model = NetWork() # 定义神经网络模型
model.load_state_dict(torch.load('mnist.pth')) # 加载刚刚训练好的模型文件
right = 0 # 保存正确识别的数量
for i, (x, y) in enumerate(test_data):
output = model(x) # 将其中的数据 x 输入到模型中
predict = output.argmax(1).item() # 选择概率最大的标签作为预测结果
# 对比预测值 predict 和真实标签 y
if predict == y:
right += 1
else:
# 将识别错误的样例打印出来
print(f"wrong case: predict = {predict}, but y = {y}")
# 计算出测试结果
sample_num = len(test_data)
accuracy = right * 1.0 / sample_num
print("test accuracy = %d / %d = %.3lf" % (right, sample_num, accuracy))
Os resultados da execução são os seguintes:
Percebe-se que a precisão do teste é de 98%, o que ainda é muito alto.
O texto acima é o processo de projetar e treinar uma rede neural do zero.
O acima é explicado e descrito de acordo com cada parte funcional, o que pode ser um pouco confuso. Encapsulamos o código acima da seguinte maneira.
import torch
import torchvision.datasets
from torch import nn
# ----------------1、定义神经网络-------------------
class NetWork(nn.Module):
def __init__(self):
super().__init__()
# 线性层1,输入层和隐藏层之间的线性层
self.layer1 = nn.Linear(784, 256)
# 线性层2,隐藏层和输出层之间的线性层
self.layer2 = nn.Linear(256, 10)
# 这里没有直接定义 softmax 层
# 这是因为后面会使用 CrossEntropyLoss 损失函数
# 在这个损失函数中会实现 softmax 的计算
def forward(self, x):
x = x.view(-1, 28 * 28) # 使用view 函数将 x 展平
x = self.layer1(x) # 将 x 输入至 layer1
x = torch.relu(x) # 使用 ReLu 函数激活
return self.layer2(x) # 输入至 layer2 计算结果
# ----------------2、图像预处理-------------------
# 实现图像的预处理的pipeline
transform = torchvision.transforms.Compose([
torchvision.transforms.Grayscale(num_output_channels=1), # 转换为单通道灰度图像
torchvision.transforms.ToTensor() # 转换为PyTorch支持的张量
])
# ----------------3、数据集准备-------------------
# 准备训练数据集
train_data = torchvision.datasets.MNIST(root='data', train=True, download=True, transform=transform)
# ----------------4、数据集加载-------------------
# 使用 train_loader 实现小批量的数据读取
# 这里设置小批量的大小,batch_size = 64。
# 也就是每个批次包括 64 个数据
train_loader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
# ----------------5、训练神经网络-------------------
# 在使用 PyTorch 训练模型时,需要创建三个对象
model = NetWork() # 1、模型本身,它就是我们设计的神经网络
optimizer = torch.optim.Adam(model.parameters()) # 2、优化器,优化模型中的参数
criterion = nn.CrossEntropyLoss() # 3、损失函数,分类问题使用交叉熵损失误差
# 开始训练模型
for epoch in range(10): # 外层循环,代表了整个训练数据集的遍历次数
# 整个训练集要循环多少轮,是10次还是20次还是100次都是有可能的
# 内层循环使用train_loader 进行小批量的数据读取
for batch_idx, (data, label) in enumerate(train_loader):
# 内层每循环一次,就会进行一次梯度下降算法
# 包括五个步骤:
output = model(data) # 1、计算神经网络的前向传播结果
loss = criterion(output, label) # 2、计算 output 和标签 label 之间的误差损失 loss
loss.backward() # 3、使用 backward 计算梯度
optimizer.step() # 4、使用 optimizer.step 更新参数
optimizer.zero_grad() # 5、将梯度清零
# 这五个步骤是使用 PyTorch 框架训练模型的定式,初学的时候记住就可以了
# 每迭代 100 个小批量,就打印一次模型的损失,观察训练的过程
if batch_idx % 100 == 0:
print(f"Epoch {epoch + 1} / 10 "
f"| Batch {batch_idx} / {len(train_loader)}"
f"| Loss: {loss.item():.4f}")
torch.save(model.state_dict(), 'mnist.pth') # 最后保存训练好的模型
import torch
import torchvision.datasets
from torch import nn
# ----------------1、定义神经网络-------------------
class NetWork(nn.Module):
def __init__(self):
super().__init__()
# 线性层1,输入层和隐藏层之间的线性层
self.layer1 = nn.Linear(784, 256)
# 线性层2,隐藏层和输出层之间的线性层
self.layer2 = nn.Linear(256, 10)
# 这里没有直接定义 softmax 层
# 这是因为后面会使用 CrossEntropyLoss 损失函数
# 在这个损失函数中会实现 softmax 的计算
def forward(self, x):
x = x.view(-1, 28 * 28) # 使用view 函数将 x 展平
x = self.layer1(x) # 将 x 输入至 layer1
x = torch.relu(x) # 使用 ReLu 函数激活
return self.layer2(x) # 输入至 layer2 计算结果
# ----------------2、图像预处理-------------------
# 实现图像的预处理的pipeline
transform = torchvision.transforms.Compose([
torchvision.transforms.Grayscale(num_output_channels=1), # 转换为单通道灰度图像
torchvision.transforms.ToTensor() # 转换为PyTorch支持的张量
])
# ----------------3、数据集准备-------------------
# 准备测试数据集
test_data = torchvision.datasets.MNIST(root='data', train=False, download=True, transform=transform)
# ----------------4、测试神经网络-------------------
model = NetWork() # 定义神经网络模型
model.load_state_dict(torch.load('mnist.pth')) # 加载刚刚训练好的模型文件
right = 0 # 保存正确识别的数量
for i, (x, y) in enumerate(test_data):
output = model(x) # 将其中的数据 x 输入到模型中
predict = output.argmax(1).item() # 选择概率最大的标签作为预测结果
# 对比预测值 predict 和真实标签 y
if predict == y:
right += 1
else:
# 将识别错误的样例打印出来
print(f"wrong case: predict = {predict}, but y = {y}")
# 计算出测试结果
sample_num = len(test_data)
accuracy = right * 1.0 / sample_num
print("test accuracy = %d / %d = %.3lf" % (right, sample_num, accuracy))