informasi kontak saya
Surat[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Untuk lebih memahami jaringan saraf, sangat tepat untuk memahami prinsip kerja dan proses umum jaringan saraf lapis demi lapis dari tugas kecil pengenalan digit tulisan tangan.
Artikel ini akan menyelesaikan tugas pengenalan angka dengan tulisan tangan untuk menjelaskan cara merancang, mengimplementasikan, dan melatih jaringan saraf feedforward standar, untuk mendapatkan pemahaman yang lebih spesifik dan perseptual tentang jaringan saraf.
Secara khusus, kita perlu merancang dan melatih jaringan saraf 3 lapis. Jaringan saraf ini akan mengambil gambar digital sebagai masukan.
Dalam proses ini, ada tiga aspek utama yang dijelaskan: desain dan implementasi jaringan saraf, persiapan dan pemrosesan data pelatihan, serta proses pelatihan dan pengujian model.
Untuk merancang jaringan saraf untuk memproses data gambar, pertama-tama perlu diperjelas ukuran dan format data gambar masukan.
Gambar yang akan kita proses adalah gambar saluran abu-abu berukuran 28×28 piksel (format dari dataset MNIST itu sendiri).
Gambar abu-abu ini mencakup 2828 = 784 titik data, kita perlu meratakannya menjadi 1 terlebih dahuluVektor ukuran 784:
Kemudian masukkan vektor ini ke dalam jaringan saraf. Kita akan menggunakan jaringan saraf tiga lapis untuk memproses vektor x yang sesuai dengan gambar. Lapisan masukan perlu menerima vektor gambar 784 dimensi x jaringan saraf. neuron untuk menerima, sehingga lapisan input berisi 784 neuron:
Lapisan tersembunyi digunakan untuk ekstraksi fitur, memproses vektor fitur masukan menjadi vektor fitur tingkat yang lebih tinggi.
Karena gambar digit tulisan tangan tidak rumit, disini kita atur jumlah neuron pada lapisan tersembunyi menjadi 256, sehingga akan terdapat lapisan linier berukuran 784*256 antara lapisan masukan dan lapisan tersembunyi:
Ia dapat mengubah vektor masukan 784 dimensi menjadi vektor keluaran 256 dimensi, yang akan terus merambat ke lapisan keluaran.
Karena gambar digital pada akhirnya akan dikenali sebagai sepuluh kemungkinan angka dari 0 hingga 9, lapisan keluaran perlu mendefinisikan 10 neuron agar sesuai dengan sepuluh angka berikut:
Setelah vektor 256 dimensi dihitung melalui lapisan linier antara lapisan tersembunyi dan lapisan keluaran, diperoleh hasil keluaran 10 dimensi ini yang mewakili skor prediksi 10 angka:
Untuk terus mendapatkan prediksi probabilitas 10 angka, kita juga perlu memasukkan output dari lapisan output ke lapisan softmax. Lapisan softmax akan mengubah vektor 10 dimensi menjadi 10 nilai probabilitas masing-masing P0 hingga P9 nilai probabilitas sesuai dengan suatu angka. Artinya, kemungkinan gambar masukan adalah angka tertentu. Selain itu, jumlah 10 nilai probabilitas dari P0 hingga P9 adalah 1. Hal ini ditentukan oleh sifat-sifatnya. fungsi softmax:
Di atas adalah ide desain jaringan saraf. Selanjutnya kita menggunakan framework PyTorch untuk mengimplementasikannya.
Pertama-tama implementasikan jaringan saraf kita:
kode tampil seperti di bawah ini:
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 计算结果
Selanjutnya siapkan dataset:
Untuk mendapatkan kumpulan data, Anda juga dapat menggunakan cara berikut untuk mendownloadnya dari situs resmi PyTorch:
# 准备数据集
train_data = torchvision.datasets.MNIST(root='data', train=True, download=True)
test_data = torchvision.datasets.MNIST(root='data', train=False, download=True)
Data yang diunduh dengan cara ini akan secara otomatis disimpan di train_data dan test_data. Sejalan dengan itu, paket alat akan diunduh di direktori yang kami tentukan dalam kode:
Namun ini bukanlah metode universal. Dalam pekerjaan dan studi di masa depan, akan ada banyak kumpulan data yang perlu kita operasikan dan proses sendiri, yang tidak akan senyaman metode di atas. Oleh karena itu, metode yang lebih umum diperkenalkan di sini. yaitu mengunduh data asli dari situs resminya. Kumpulan data adalah, ya, sekumpulan gambar!
Kami menyimpan data yang diunduh ke dalam dua folder berikut:
Kami menyimpan data ke dalam dua direktori, masing-masing train dan test, di mana train memiliki 60.000 data dan test memiliki 10.000 data, yang masing-masing digunakan untuk pelatihan model dan pengujian.
Direktori train dan test mencakup sepuluh subdirektori, dan nama subdirektori sesuai dengan angka pada gambar. Misalnya gambar angka 3 disimpan dalam folder bernama 3:
dimana nama gambarnya adalah tanda tangan string acak.
Setelah persiapan data selesai, fungsi pembacaan data diterapkan. Pemula hanya perlu mengetahui proses pengolahan data secara umum saat mempelajari bagian ini.
Kode ini diimplementasikan sebagai berikut:
# 首先实现图像的预处理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))
Lihatlah hasil yang berjalan:
Anda dapat melihat bahwa data pelatihan dan data pengujian telah diperoleh, yang sepenuhnya konsisten dengan apa yang kami katakan sebelumnya.
Kemudian kita menggunakan train_loader untuk mengimplementasikan pembacaan data batch kecil:
# 使用 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 个数据
Hasil yang berjalan adalah sebagai berikut:
Kita kemudian dapat mengulang train_loader untuk mendapatkan setiap kumpulan data mini:
# 循环遍历 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)
Berikut penjelasan sederhana dari pernyataan loop di atas agar lebih jelas:
menghitung(): Ini adalah fungsi bawaan Python, yang digunakan untuk menggabungkan objek data yang dapat dilintasi (seperti daftar, tuple, atau string) ke dalam urutan indeks, dan mencantumkan data dan subskrip data pada saat yang sama, yaitu, dapatkan indeks dan nilai secara bersamaan. Di sini, digunakan untuk melakukan iterasi setiap batch di train_loader, dengan batch_idx adalah indeks batch saat ini (mulai dari 0), (data, label) adalah data dan label batch saat ini.
untuk batch_idx, (data, label) di enumerate(train_loader):: Arti dari pernyataan loop ini adalah untuk setiap batch di train_loader, kode di badan loop dieksekusi.Dalam setiap iterasi loop, batch_idx adalah indeks dari batch saat ini, (data, label) adalah data dan label dari batch saat ini . data biasanya berupa tensor yang berisi beberapa titik data, setiap titik data adalah label sampel adalah label tensor yang sesuai dengan titik data tersebut, yang digunakan untuk nilai target dalam pembelajaran yang diawasi.
Efek yang berjalan adalah sebagai berikut:
Dapat dilihat dari hasil runningnya:
1. batch_idx = 0 berarti data batch pertama
2. data.shape menunjukkan bahwa ukuran datanya adalah [64, 1, 28, 28]
Ukuran di atas berarti setiap kumpulan data mencakup 64 gambar, setiap gambar memiliki 1 saluran abu-abu, dan ukuran gambar adalah 28*28.
3. label.shape artinya ada 64 nomor dalam satu batch dan jumlah label yang bersesuaian adalah 64. Setiap nomor mempunyai label.
Perhatikan perbedaannya, kategori label realnya harus ada 9 saja, karena angkanya hanya 1 sampai 9, dan disini maksudnya ada 64 nilai label.
4. Array tensor mewakili nilai label yang sesuai dengan masing-masing 64 gambar digital ini.
Dengan persiapan sebelumnya, kita dapat memulai pelatihan dan pengujian model.
Berikut kode pelatihannya:
# 在使用 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') # 最后保存训练好的模型
Efek yang berjalan adalah sebagai berikut:
Hilangkan bagian tengah…
Terlihat nilai kerugian akhir sangat-sangat kecil yaitu 0,0239.
Langkah terakhir adalah pengujian. Proses pengujian pada dasarnya sama dengan pelatihan.
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))
Hasil yang berjalan adalah sebagai berikut:
Terlihat akurasi tesnya adalah 98%, masih sangat tinggi.
Di atas adalah proses merancang dan melatih jaringan saraf dari awal.
Di atas dijelaskan dan dijelaskan sesuai dengan bagian fungsionalnya masing-masing, yang mungkin sedikit membingungkan. Kami merangkum kode di atas sebagai berikut.
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))