2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Hermoverkkojen ymmärtämiseksi paremmin on erittäin sopivaa ymmärtää hermoverkkojen toimintaperiaate ja yleinen prosessi kerros kerrokselta pienestä käsinkirjoitettujen numeroiden tunnistuksen tehtävästä.
Tämä artikkeli täydentää numeroiden tunnistustehtävän käsialalla selittääkseen, kuinka suunnitella, toteuttaa ja kouluttaa standardi feedforward-hermoverkko, jotta saadaan tarkempi ja havainnollisempi käsitys hermoverkoista.
Tarkemmin sanottuna meidän on suunniteltava ja koulutettava 3-kerroksinen hermoverkko. Tämä hermoverkko ottaa digitaaliset kuvat syötteeksi.
Tässä prosessissa selitetään pääasiassa kolmea näkökohtaa: neuroverkkojen suunnittelu ja toteutus, koulutusdatan valmistelu ja käsittely sekä mallin koulutus- ja testausprosessi.
Jotta voidaan suunnitella hermoverkko käsittelemään kuvadataa, on ensin selvitettävä syöttökuvadatan koko ja muoto.
Käsittelemämme kuva on harmaa kanavakuva, jonka koko on 28 × 28 pikseliä (itse MNIST-tietojoukon muoto).
Tämä harmaa kuva sisältää 2828 = 784 datapistettä, meidän on ensin litistettävä se 1:ksiVektori, jonka koko on 784:
Syötä tämä vektori hermoverkkoon. Käytämme kolmikerroksista hermoverkkoa käsittelemään kuvaa vastaavaa vektoria. Syöttökerroksen tulee vastaanottaa 784-ulotteinen kuvavektori x:ssä hermoverkko vastaanottaa, joten syöttökerros sisältää 784 neuronia:
Piilotettua kerrosta käytetään piirteiden poimimiseen, prosessoimalla syötetyt piirrevektorit korkeamman tason piirrevektoreiksi.
Koska käsin kirjoitettu numerokuva ei ole monimutkainen, asetamme tässä piilotetun kerroksen neuronien lukumääräksi 256, jotta syöttökerroksen ja piilokerroksen väliin tulee lineaarinen kerros, jonka koko on 784*256:
Se voi muuntaa 784-ulotteisen tulovektorin 256-ulotteiseksi lähtövektoriksi, joka jatkaa etenemistä eteenpäin lähtökerrokseen.
Koska digitaalinen kuva tunnistetaan lopulta kymmeneksi mahdolliseksi numeroksi 0-9, lähtökerroksen on määriteltävä 10 neuronia vastaamaan näitä kymmentä numeroa:
Kun 256-ulotteinen vektori on laskettu piilotetun kerroksen ja tulostekerroksen välisen lineaarisen kerroksen läpi, saadaan 10-ulotteinen tulos. Tämä 10-ulotteinen vektori edustaa 10 luvun ennustepistettä:
Jotta voimme jatkaa 10 luvun ennustetun todennäköisyyden saamista, meidän on myös syötettävä ulostulokerroksen tulos softmax-kerrokseen todennäköisyysarvo vastaa lukua, eli mahdollisuutta, että syötetty kuva on tietty luku. Lisäksi 10 todennäköisyysarvon summa P0:sta P9:ään on 1. Tämä määräytyy softmax-toiminto:
Yllä oleva on hermoverkon suunnitteluidea. Seuraavaksi käytämme sen toteuttamiseen PyTorch-kehystä.
Ota ensin käyttöön neuroverkkomme:
koodi näytetään alla:
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 计算结果
Valmistele seuraavaksi tietojoukko:
Tietojoukon hankkimiseksi voit myös ladata sen PyTorchin viralliselta verkkosivustolta seuraavalla tavalla:
# 准备数据集
train_data = torchvision.datasets.MNIST(root='data', train=True, download=True)
test_data = torchvision.datasets.MNIST(root='data', train=False, download=True)
Tällä tavalla ladatut tiedot tallennetaan automaattisesti hakemistoon train_data ja test_data. Vastaavasti työkalupaketti ladataan koodissa määrittämäämme hakemistoon:
Mutta tämä ei ole universaali menetelmä. Tulevassa työssä ja tutkimuksessa tulee olemaan monia tietojoukkoja, joita meidän on käytettävä ja prosessoitava, mikä ei ole yhtä kätevää kuin yllä oleva. joka on alkuperäisten tietojen lataaminen viralliselta verkkosivustolta. Tietojoukko on kyllä, joukko kuvia.
Tallennamme ladatut tiedot kahteen seuraavaan kansioon:
Tallennamme tiedot kahteen hakemistoon, juna- ja testihakemistoon, joissa junassa on 60 000 dataa ja testissä 10 000 dataa, joita käytetään vastaavasti mallikoulutukseen ja testaukseen.
Sekä juna- että testihakemistossa on kymmenen alihakemistoa, ja alihakemistojen nimet vastaavat kuvan numeroita. Esimerkiksi kuva numerosta 3 tallennetaan kansioon nimeltä 3:
jossa kuvan nimi on satunnainen merkkijonoallekirjoitus.
Tietojen valmistelun jälkeen otetaan käyttöön tietojen lukutoiminto.
Koodi toteutetaan seuraavasti:
# 首先实现图像的预处理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))
Katso juoksutulokset:
Voit nähdä, että sekä harjoitustiedot että testitiedot on saatu, mikä on täysin yhdenmukainen aiemmin sanomamme kanssa.
Sitten käytämme train_loaderia toteuttamaan pienen erän datan lukemisen:
# 使用 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 个数据
Juoksun tulokset ovat seuraavat:
Voimme sitten kiertää train_loaderin kautta saadaksemme jokaisen datan minierän:
# 循环遍历 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)
Tässä on yksinkertainen selitys yllä olevasta silmukkalauseesta, joka tekee siitä selkeämmän:
luetella(): Tämä on Pythonin sisäänrakennettu toiminto, jota käytetään yhdistämään läpikulkukelpoinen tietoobjekti (kuten luettelo, monikko tai merkkijono) indeksisekvenssiksi ja listaamaan tiedot ja datan alaindeksit samanaikaisesti, eli hankkimaan indeksi ja arvo samaan aikaan. Tässä sitä käytetään iteroitaessa jokaisen erän läpi train_loaderissa, jossa batch_idx on nykyisen erän indeksi (alkaen 0:sta), (data, etiketti) on nykyisen erän tiedot ja nimike.
batch_idx, (data, etiketti) kohdassa enumerate(train_loader):: Tämän silmukkakäskyn tarkoitus on, että jokaiselle train_loaderin erälle suoritetaan silmukan rungossa oleva koodi.Jokaisessa silmukan iteraatiossa batch_idx on nykyisen erän indeksi, (data, etiketti) on nykyisen erän tiedot ja nimike . data on yleensä tensori, joka sisältää useita datapisteitä, jokainen datapiste on näytenimike on näitä datapisteitä vastaava tarratensori, jota käytetään kohdearvona ohjatussa oppimisessa.
Juoksuvaikutus on seuraava:
Se näkyy juoksutuloksista:
1. batch_idx = 0 tarkoittaa, että se on ensimmäinen tietoerä
2. data.shape osoittaa, että datan koko on [64, 1, 28, 28]
Yllä oleva koko tarkoittaa, että jokainen tietoerä sisältää 64 kuvaa, jokaisessa kuvassa on 1 harmaa kanava ja kuvan koko on 28*28.
3. etiketti.muoto tarkoittaa, että erässä on 64 numeroa ja niitä vastaavien tarrojen kokonaismäärä on 64. Jokaisella numerolla on etiketti.
Kiinnitä huomiota eroon Varsinaisia tarrakategorioita saa olla vain 9, koska numerot ovat vain 1 - 9, ja tässä se tarkoittaa, että etikettiarvoja on 64.
4. Tensorimatriisi edustaa kutakin näistä 64 digitaalisesta kuvasta vastaavan nimiön arvoa.
Aikaisempien valmistelujen jälkeen voimme aloittaa mallin harjoittelun ja testauksen.
Tässä koulutuskoodi:
# 在使用 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') # 最后保存训练好的模型
Juoksuvaikutus on seuraava:
Jätä väli pois…
Voidaan nähdä, että lopullinen häviöarvo on hyvin, hyvin pieni, 0,0239.
Viimeinen vaihe on testaus. Testausprosessi on periaatteessa sama kuin koulutus.
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))
Juoksun tulokset ovat seuraavat:
Voidaan nähdä, että testin tarkkuus on 98%, mikä on edelleen erittäin korkea.
Yllä oleva on prosessi, jossa hermoverkko suunnitellaan ja koulutetaan tyhjästä.
Yllä oleva selitetään ja kuvataan kunkin toiminnallisen osan mukaan, mikä voi olla hieman hämmentävää. Kapseloimme yllä olevan koodin seuraavasti.
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))