Κοινή χρήση τεχνολογίας

Σχεδιάζοντας ένα νευρωνικό δίκτυο από την αρχή: πραγματοποίηση χειρόγραφης αναγνώρισης ψηφίων

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Πρόλογος

Για να κατανοήσουμε καλύτερα τα νευρωνικά δίκτυα, είναι πολύ σκόπιμο να κατανοήσουμε την αρχή λειτουργίας και τη γενική διαδικασία των νευρωνικών δικτύων επίπεδο προς στρώμα από το μικρό έργο της χειρόγραφης αναγνώρισης ψηφίων.

Αυτό το άρθρο θα ολοκληρώσει μια εργασία αναγνώρισης αριθμών χειρόγραφα για να εξηγήσει πώς να σχεδιάσετε, να εφαρμόσετε και να εκπαιδεύσετε ένα τυπικό νευρωνικό δίκτυο προώθησης τροφοδοσίας, προκειμένου να έχετε μια πιο συγκεκριμένη και αντιληπτική κατανόηση των νευρωνικών δικτύων.

ΣΦΑΙΡΙΚΗ ΕΙΚΟΝΑ

Συγκεκριμένα, πρέπει να σχεδιάσουμε και να εκπαιδεύσουμε ένα νευρωνικό δίκτυο 3 επιπέδων.

Εισαγάγετε την περιγραφή της εικόνας εδώ

Σε αυτή τη διαδικασία, εξηγούνται κυρίως τρεις πτυχές: ο σχεδιασμός και η υλοποίηση νευρωνικών δικτύων, η προετοιμασία και η επεξεργασία των δεδομένων εκπαίδευσης και η διαδικασία εκπαίδευσης και δοκιμής μοντέλων.

Εισαγάγετε την περιγραφή της εικόνας εδώ

Σχεδιασμός και υλοποίηση νευρωνικών δικτύων

Προκειμένου να σχεδιαστεί ένα νευρωνικό δίκτυο για την επεξεργασία δεδομένων εικόνας, είναι απαραίτητο πρώτα να αποσαφηνιστεί το μέγεθος και η μορφή των δεδομένων εικόνας εισόδου.

Εισαγάγετε την περιγραφή της εικόνας εδώ

Η εικόνα που πρόκειται να επεξεργαστούμε είναι μια εικόνα γκρι καναλιού μεγέθους 28 × 28 pixel (η μορφή του ίδιου του συνόλου δεδομένων MNIST).

Αυτή η γκρίζα εικόνα περιλαμβάνει 2828 = 784 σημεία δεδομένων, πρέπει πρώτα να τα ισοπεδώσουμε στο 1Διάνυσμα μεγέθους 784:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Στη συνέχεια, εισάγετε αυτό το διάνυσμα στο νευρωνικό δίκτυο. Θα χρησιμοποιήσουμε ένα νευρωνικό δίκτυο τριών επιπέδων για να επεξεργαστούμε το διάνυσμα x που αντιστοιχεί στην εικόνα νευρωνικό δίκτυο για λήψη, οπότε το επίπεδο εισόδου περιέχει 784 νευρώνες:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Το κρυφό στρώμα χρησιμοποιείται για την εξαγωγή χαρακτηριστικών, επεξεργάζοντας τα διανύσματα χαρακτηριστικών εισόδου σε διανύσματα χαρακτηριστικών υψηλότερου επιπέδου.

Δεδομένου ότι η χειρόγραφη ψηφιακή εικόνα δεν είναι περίπλοκη, ορίζουμε εδώ τον αριθμό των νευρώνων στο κρυφό στρώμα σε 256, έτσι ώστε να υπάρχει ένα γραμμικό στρώμα μεγέθους 784*256 μεταξύ του επιπέδου εισόδου και του κρυφού στρώματος:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Μπορεί να μετατρέψει ένα διάνυσμα εισόδου 784 διαστάσεων σε διάνυσμα εξόδου 256 διαστάσεων, το οποίο θα συνεχίσει να διαδίδεται προς τα εμπρός στο επίπεδο εξόδου.

Δεδομένου ότι η ψηφιακή εικόνα θα αναγνωριστεί τελικά ως δέκα πιθανοί αριθμοί από το 0 έως το 9, το επίπεδο εξόδου πρέπει να ορίσει 10 νευρώνες για να αντιστοιχούν σε αυτούς τους δέκα αριθμούς:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Αφού υπολογιστεί το διάνυσμα 256 διαστάσεων μέσω του γραμμικού στρώματος μεταξύ του κρυφού στρώματος και του στρώματος εξόδου, λαμβάνεται ένα 10-διάστατο αποτέλεσμα εξόδου Αυτό το 10-διάστατο διάνυσμα αντιπροσωπεύει τη βαθμολογία πρόβλεψης 10 αριθμών:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Για να συνεχίσουμε να λαμβάνουμε την προβλεπόμενη πιθανότητα των 10 αριθμών, πρέπει επίσης να εισάγουμε την έξοδο του επιπέδου εξόδου στο επίπεδο softmax Το επίπεδο softmax θα μετατρέψει το διάνυσμα 10 διαστάσεων σε 10 τιμές πιθανότητας P0 Η τιμή πιθανότητας αντιστοιχεί σε έναν αριθμό, δηλαδή, η πιθανότητα η εικόνα εισόδου να είναι ένας συγκεκριμένος αριθμός. Επιπλέον, το άθροισμα των 10 τιμών πιθανότητας από P0 έως P9. Λειτουργία softmax:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Το παραπάνω είναι η ιδέα σχεδιασμού του νευρωνικού δικτύου Στη συνέχεια, χρησιμοποιούμε το πλαίσιο PyTorch για να το υλοποιήσουμε.

Πρώτα εφαρμόστε το νευρωνικό μας δίκτυο:

Εισαγάγετε την περιγραφή της εικόνας εδώ

ο κώδικας εμφανίζεται ως εξής:

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 计算结果
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Προετοιμασία και επεξεργασία δεδομένων εκπαίδευσης

Στη συνέχεια προετοιμάστε το σύνολο δεδομένων:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Για να αποκτήσετε το σύνολο δεδομένων, μπορείτε επίσης να χρησιμοποιήσετε την ακόλουθη μέθοδο για να το κατεβάσετε από τον επίσημο ιστότοπο του PyTorch:

# 准备数据集
train_data = torchvision.datasets.MNIST(root='data', train=True, download=True)
test_data = torchvision.datasets.MNIST(root='data', train=False, download=True)
  • 1
  • 2
  • 3

Τα δεδομένα που λαμβάνονται με αυτόν τον τρόπο θα αποθηκευτούν αυτόματα στο train_data και στο test_data Αντίστοιχα, θα γίνει λήψη ενός πακέτου εργαλείων στον κατάλογο που καθορίσαμε στον κώδικα:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Αλλά αυτή δεν είναι μια καθολική μέθοδος Στη μελλοντική εργασία και μελέτη, θα υπάρξουν πολλά σύνολα δεδομένων που πρέπει να λειτουργήσουμε και να επεξεργαστούμε οι ίδιοι, τα οποία δεν θα είναι τόσο βολικά όσο η παραπάνω, που είναι να κατεβάσετε τα εγγενή δεδομένα από τον επίσημο ιστότοπο Ένα σύνολο δεδομένων είναι, ναι, ένα σωρό εικόνες!

Αποθηκεύουμε τα ληφθέντα δεδομένα στους ακόλουθους δύο φακέλους:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Αποθηκεύουμε τα δεδομένα σε δύο καταλόγους, train και test αντίστοιχα, όπου το train έχει 60.000 δεδομένα και το test έχει 10.000 δεδομένα, τα οποία χρησιμοποιούνται για εκπαίδευση μοντέλων και δοκιμές αντίστοιχα.

Τόσο ο κατάλογος τρένου όσο και ο κατάλογος δοκιμής περιλαμβάνουν δέκα υποκαταλόγους και τα ονόματα των υποκαταλόγων αντιστοιχούν στους αριθμούς της εικόνας. Για παράδειγμα, μια εικόνα του αριθμού 3 αποθηκεύεται σε ένα φάκελο με το όνομα 3:

Εισαγάγετε την περιγραφή της εικόνας εδώ

όπου το όνομα της εικόνας είναι μια τυχαία υπογραφή συμβολοσειράς.

Μετά την ολοκλήρωση της προετοιμασίας δεδομένων, εφαρμόζεται η λειτουργία ανάγνωσης δεδομένων Οι αρχάριοι χρειάζεται μόνο να γνωρίζουν τη γενική διαδικασία επεξεργασίας δεδομένων όταν μαθαίνουν αυτό το μέρος.

Ο κώδικας υλοποιείται ως εξής:

# 首先实现图像的预处理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))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Ρίξτε μια ματιά στα αποτελέσματα του τρεξίματος:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Μπορείτε να δείτε ότι έχουν ληφθεί τόσο τα δεδομένα εκπαίδευσης όσο και τα δεδομένα των δοκιμών, κάτι που είναι απολύτως συνεπές με αυτό που είπαμε πριν.

Στη συνέχεια χρησιμοποιούμε το train_loader για να εφαρμόσουμε ανάγνωση δεδομένων μικρής παρτίδας:

# 使用 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 个数据
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Τα αποτελέσματα του τρεξίματος έχουν ως εξής:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Στη συνέχεια, μπορούμε να κάνουμε βρόχο μέσω του train_loader για να λάβουμε κάθε μίνι παρτίδα δεδομένων:

# 循环遍历 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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Ακολουθεί μια απλή εξήγηση της παραπάνω δήλωσης βρόχου που θα την καταστήσει σαφέστερη:

απαριθμώ(): Αυτή είναι η ενσωματωμένη συνάρτηση της Python, η οποία χρησιμοποιείται για να συνδυάσει ένα αντικείμενο δεδομένων με δυνατότητα διέλευσης (όπως μια λίστα, μια πλειάδα ή μια συμβολοσειρά) σε μια ακολουθία ευρετηρίου και να παραθέσει τα δεδομένα και τους δείκτες δεδομένων ταυτόχρονα, δηλαδή για να πάρει το δείκτη και αξία ταυτόχρονα. Εδώ, χρησιμοποιείται για επανάληψη σε κάθε παρτίδα στο train_loader, όπου batch_idx είναι ο δείκτης της τρέχουσας παρτίδας (ξεκινώντας από το 0), (δεδομένα, ετικέτα) είναι τα δεδομένα και η ετικέτα της τρέχουσας παρτίδας.

για batch_idx, (δεδομένα, ετικέτα) στο enumerate(train_loader):: Το νόημα αυτής της δήλωσης βρόχου είναι ότι για κάθε παρτίδα στο train_loader, εκτελείται ο κώδικας στο σώμα του βρόχου.Σε κάθε επανάληψη βρόχου, batch_idx είναι ο δείκτης της τρέχουσας παρτίδας, (δεδομένα, ετικέτα) είναι τα δεδομένα και η ετικέτα της τρέχουσας παρτίδας . Τα δεδομένα είναι συνήθως ένας τανυστής που περιέχει πολλά σημεία δεδομένων, κάθε σημείο δεδομένων είναι ένα δείγμα ετικέτας είναι ο τανυστής ετικέτας που αντιστοιχεί σε αυτά τα σημεία δεδομένων, ο οποίος χρησιμοποιείται για την τιμή-στόχο στην εποπτευόμενη μάθηση.

Το εφέ λειτουργίας είναι το εξής:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Μπορεί να φανεί από τα αποτελέσματα εκτέλεσης:

1. batch_idx = 0 σημαίνει ότι είναι η πρώτη παρτίδα δεδομένων

2. Το data.shape υποδεικνύει ότι το μέγεθος των δεδομένων είναι [64, 1, 28, 28]

Το παραπάνω μέγεθος σημαίνει ότι κάθε παρτίδα δεδομένων περιλαμβάνει 64 εικόνες, κάθε εικόνα έχει 1 γκρι κανάλι και το μέγεθος της εικόνας είναι 28*28.

3. label.shape σημαίνει ότι υπάρχουν 64 αριθμοί στην παρτίδα και ο συνολικός αριθμός των ετικετών που αντιστοιχούν σε αυτούς είναι 64. Κάθε αριθμός έχει μια ετικέτα.

Προσοχή στη διαφορά Πρέπει να υπάρχουν μόνο 9 πραγματικές κατηγορίες ετικετών, γιατί οι αριθμοί είναι μόνο 1 έως 9, και εδώ σημαίνει ότι υπάρχουν 64 τιμές ετικετών.

4. Ο πίνακας τανυστών αντιπροσωπεύει την τιμή ετικέτας που αντιστοιχεί σε καθεμία από αυτές τις 64 ψηφιακές εικόνες.

Διαδικασία εκπαίδευσης και δοκιμής μοντέλου

Με τις προηγούμενες προετοιμασίες, μπορούμε να ξεκινήσουμε την εκπαίδευση και τη δοκιμή του μοντέλου.

Εδώ είναι ο κώδικας εκπαίδευσης:

# 在使用 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')  # 最后保存训练好的模型
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

Το εφέ λειτουργίας είναι το εξής:

Εισαγάγετε την περιγραφή της εικόνας εδώ
Παραλείψτε τη μέση…
Εισαγάγετε την περιγραφή της εικόνας εδώ

Μπορεί να φανεί ότι η τελική τιμή απώλειας είναι πολύ, πολύ μικρή, 0,0239.

Το τελευταίο βήμα είναι η δοκιμή Η διαδικασία δοκιμής είναι βασικά η ίδια με την εκπαίδευση.

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))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Τα αποτελέσματα του τρεξίματος έχουν ως εξής:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Μπορεί να φανεί ότι η ακρίβεια της δοκιμής είναι 98%, η οποία εξακολουθεί να είναι πολύ υψηλή.

Τα παραπάνω είναι η διαδικασία σχεδιασμού και εκπαίδευσης ενός νευρωνικού δικτύου από την αρχή.

Περίληψη και ενθυλάκωση κώδικα

Τα παραπάνω εξηγούνται και περιγράφονται σύμφωνα με κάθε λειτουργικό μέρος, το οποίο μπορεί να είναι λίγο μπερδεμένο. Ενσωματώνουμε τον παραπάνω κώδικα ως εξής.

κώδικα εκπαίδευσης

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')  # 最后保存训练好的模型

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

κωδικός δοκιμής

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))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55