如何使用 pytorch 創建一個神經網絡

我已發布在:如何使用 pytorch 創建一個神經網絡 SapientialM.Github.io

構建神經網絡

1 導入所需包

import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

2 檢查GPU是否可用

device = ("cuda"if torch.cuda.is_available()else "mps"if torch.backends.mps.is_available()else "cpu"
)
print(f"Using {device} device")
print(torch.__version__)
print(torch.cuda.is_available())
print(torch.version.cuda)
Using cuda device
2.2.2+cu121
True
12.1

如果發現GPU不可用,可能是因為torch版本問題(比如我),應該去下載GPU版本。

  • 在CUDA官網找到合適版本的cuda,一般是根據系統平臺和顯卡版本來選擇所需CUDA
  • 查看安裝完成是否
# 版本,看CUDA的版本,比如我的是cuda_11.2
nvcc --version
# 驅動,看Driver Version
nvidia-smi
  • 去PyTorch官網找到合適版本的PyTorch,一般是根據開發環境來選擇,然后復制所給的Commond去shell下安裝即可
# 比如我的命令就是
pip install torch==2.2.2 torchvision==0.17.2 torchaudio==2.2.2 --index-url https://download.pytorch.org/whl/cu121

3 定義我們的神經網絡

pytorch里面一切自定義操作基本上都是繼承nn.Module類來實現的,你可以先不去深入了解這個類,但是要知道,我們一般都是通過繼承和重構nn.Module來定義我們的神經網絡。我們一般重構__init__forward這兩個方法。根據PyTorch官網的說法:__init__初始化神經網絡層;forward層之間的數據操作,也是整個網絡的核心。__init__只會定義層,而forward負責將層連接起來。實際上類的初始化參數一般是一些固有屬性,我們可以將一些帶有訓練參數的層放在__init__,而沒有訓練參數的層是可以加入到forward里面的,或者說我們將沒有訓練參數的層看作是層之間的數據操作。

當然直接這么說,肯定不是很清晰,我們來看一個官網給的例子:

class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits
model = NeuralNetwork().to(device) # 將網絡移入device,并打印結構
print(model)
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)

我們用torchviz可以將神經網絡進行一個簡單的可視化,當然很多參數可選,這里不一一列舉

pip install torchviz
from torchviz import make_dot
X = torch.rand(1, 28, 28, device=device) # 需要對神經網絡進行數據輸入,才是一個完整的網絡
y = model(X)
output = make_dot(y.mean(), params=dict(model.named_parameters())) # 開始繪制
output.format = "png"
output.directory = "."
output.render("torchviz", view=True) # 會在相對路徑下保存一個可視化后的圖片并打開
'torchviz.png'

看起來可能會比較復雜,也看不懂,我們得需要學會看懂神經網絡的結構才能看懂結構圖。當然,還有諸如draw_convnet、NNSVG、netron等可視化工具會更加優秀。

4 神經網絡模型層

想要構建一個神經網絡并進行訓練和預測,我們需要去認識神經網絡的構成。假定你已經了解過感知機、人工神經網絡的基本概念,那么現在就是來了解一下神經網絡的模型層。

我們直接分解一下官網所給出的這個模型,這是一個簡單的前饋神經網絡(Feedforward Neural Network),我們先不去了解它的作用,一點點的分解它,看看它最終實現了什么。

我們先根據官網所說的,取一個大小為 28x28 的 3 張圖像的樣本小批量作為輸入(我們一般將數據的第一個維度看作批量維度并保留):

input_image = torch.rand(3,28,28)
print(input_image.size())
torch.Size([3, 28, 28])

4.1 nn.Flatten

雖然是PyTorch的nn.Flatten,但是Flatten層是神經網絡中常見的組成部分。在神經網絡的訓練和預測過程中,輸入數據通常需要經過一系列的處理和轉換。在這個過程中,Flatten層能夠將多維的輸入數據轉化為一維的線性形式,以便于神經網絡的進一步處理。模型中的nn.Flatten,將我們所輸入的2D 28*28 圖像轉換為一個包含 784 個像素值的連續數組,也就是和它表面的意思一樣展平這個高維數組。

nn.Flatten()默認參數是start_dim=1end_dim=-1,如果你想展平所有維度,可以通過設置start_dim=0來實現)

flatten = nn.Flatten()
flat_image = flatten(input_image) # 將輸入的圖像展平
print(flat_image.size())
torch.Size([3, 784])

在卷積神經網絡(CNN)中,Flatten層可以將卷積層提取到的特征圖展平,便于進一步的特征處理或分類,也便于輸入到全連接層(全連接層通常需要一維的輸入,后面會講到)。在構建復雜網絡時,Flatten層可以幫助不同類型的層之間進行連接。總的來說,Flatten層起到了橋梁的作用,使得卷積神經網絡的層次結構更加靈活和易于設計,并且確保了從卷積層到全連接層的數據傳遞順暢,維持了網絡的整體性能和效率。

4.2 nn.Linear

nn.Linear應該耳熟能詳,我們稱之為線性層(Linear Layer),也可以稱為全連接層(Fully Connected Layer)或密集層(Dense Layer)。線性層是一個使用其存儲的權重和偏差對輸入應用線性變換的模塊,也就是對輸入數據進行線性變換。線性層對數據的處理方式基本上可以表示為:
y = W x + b y = Wx + b y=Wx+b
,其中 W 是權重矩陣,b 是偏置。向量都是可學習的參數。在神經網絡的訓練和預測過程中,Linear層的作用是將輸入數據通過一組權重進行線性變換,然后添加一個偏置項。簡單來說,它能夠將輸入特征映射到輸出特征,從而實現對數據的線性組合和轉換。如下圖是一個單隱藏層的多層感知機(Multilayer Perceptron),一般稱為MLP,隱藏層和輸出層均是由線性層和激活函數組成:

MLP

# 定義一個線性層,將28*28維度的向量轉換為20維度的向量
layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image) 
print(hidden1.size())
torch.Size([3, 20])

在這個例子中,in_features=28*28表示輸入特征的維度,out_features=20表示輸出特征的維度。nn.Linear層會自動初始化權重和偏置,并在訓練過程中通過反向傳播算法進行調整。簡單理解就是,該線性層的輸入是784維,而輸出是20維。

4.3 nn.ReLU

ReLU函數,全稱Rectified Linear Unit,是人工神經網絡中常用的一種激活函數.
講到這里,我們就講講常見的激活函數及其作用。

Sigmoid 激活函數

數學表達式:
σ ( x ) = 1 1 + e ? x \sigma(x) = \frac{1}{1 + e^{-x}} σ(x)=1+e?x1?

作用:

  • 將輸入映射到 (0, 1) 之間。
  • 常用于輸出層,尤其是在二分類問題中,輸出概率值。

優點:

  • 輸出范圍在 (0, 1) 之間,可以解釋為概率。
  • 平滑梯度,有助于梯度下降。

缺點:

  • 容易導致梯度消失問題。
  • 輸出不是零中心的,會影響網絡的訓練效率。
import numpy as np
import matplotlib.pyplot as pltdef sigmoid(x):return 1 / (1 + np.exp(-x))x = np.linspace(-10, 10, 100)
y = sigmoid(x)plt.plot(x, y)
plt.title("Sigmoid Activation Function")
plt.xlabel("x")
plt.ylabel("Sigmoid(x)")
plt.grid()
plt.show()

sigmoid

Tanh 激活函數

數學表達式:
tanh ? ( x ) = e x ? e ? x e x + e ? x \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} tanh(x)=ex+e?xex?e?x?

作用:

  • 將輸入映射到 (-1, 1) 之間。
  • 常用于隱藏層,提供零中心的輸出,有助于訓練。

優點:

  • 輸出是零中心的,梯度消失問題較輕。

缺點:

  • 仍然存在梯度消失問題。
import numpy as np
import matplotlib.pyplot as pltdef tanh(x):return np.tanh(x)x = np.linspace(-10, 10, 100)
y = tanh(x)plt.plot(x, y)
plt.title("Tanh Activation Function")
plt.xlabel("x")
plt.ylabel("Tanh(x)")
plt.grid()
plt.show()

Tanh

ReLU (Rectified Linear Unit) 激活函數

數學表達式:
ReLU ( x ) = max ? ( 0 , x ) \text{ReLU}(x) = \max(0, x) ReLU(x)=max(0,x)

作用:

  • 將輸入小于0的部分設為0,大于0的部分保持不變。
  • 常用于隱藏層,特別是深度神經網絡。

優點:

  • 計算簡單,收斂速度快。
  • 減少梯度消失問題。

缺點:

  • 輸出不是零中心的。
  • 輸入小于0時梯度為零,可能導致“神經元死亡”問題。
import numpy as np
import matplotlib.pyplot as pltdef relu(x):return np.maximum(0, x)x = np.linspace(-10, 10, 100)
y = relu(x)plt.plot(x, y)
plt.title("ReLU Activation Function")
plt.xlabel("x")
plt.ylabel("ReLU(x)")
plt.grid()
plt.show()

RELU

Leaky ReLU 激活函數

數學表達式:
Leaky?ReLU ( x ) = { x if? x > 0 α x if? x ≤ 0 \text{Leaky ReLU}(x) = \begin{cases} x & \text{if } x > 0 \\ \alpha x & \text{if } x \leq 0 \end{cases} Leaky?ReLU(x)={xαx?if?x>0if?x0?
其中 (\alpha) 通常是一個很小的常數,如 0.01。

作用:

  • 解決 ReLU 的“神經元死亡”問題。

優點:

  • 輸入小于0時仍有較小梯度,避免神經元死亡。

缺點:

  • 計算稍復雜。
import numpy as np
import matplotlib.pyplot as pltdef leaky_relu(x, alpha=0.01):return np.where(x > 0, x, alpha * x)x = np.linspace(-10, 10, 100)
y = leaky_relu(x)plt.plot(x, y)
plt.title("Leaky ReLU Activation Function")
plt.xlabel("x")
plt.ylabel("Leaky ReLU(x)")
plt.grid()
plt.show()

Leaky RELU

Softmax 激活函數

數學表達式:
Softmax ( x i ) = e x i ∑ j e x j \text{Softmax}(x_i) = \frac{e^{x_i}}{\sum_{j} e^{x_j}} Softmax(xi?)=j?exj?exi??

作用:

  • 將輸入向量轉換為概率分布,總和為1。
  • 常用于多分類問題的輸出層。

優點:

  • 輸出可以解釋為概率,便于分類。

缺點:

  • 計算相對復雜,容易導致數值不穩定。
import numpy as np
import matplotlib.pyplot as pltdef softmax(x):e_x = np.exp(x - np.max(x))return e_x / e_x.sum(axis=0)x = np.linspace(-10, 10, 100)
y = softmax(x)plt.plot(x, y)
plt.title("Softmax Activation Function")
plt.xlabel("x")
plt.ylabel("Softmax(x)")
plt.grid()
plt.show()

Softmax

4.4 nn.Sequential

nn.Sequential是 PyTorch 提供的一個容器模塊,它按順序包含其他子模塊,便于構建和管理簡單的神經網絡結構。通過 nn.Sequential,可以方便地將一系列層(如線性層、激活函數、卷積層等)按順序堆疊在一起,從而簡化模型定義和前向傳播的代碼。簡而言之就是一個包裹的順序容器。

5 理解我們的神經網絡

看完這些,我們再來理解這個官網給的例子:

class NeuralNetwork(nn.Module):# 重構 __init__,定義“固有屬性”def __init__(self):# 這一步操作是調用父類 nn.Module 的構造函數,確保繼承自 nn.Module 的特性正確初始化super().__init__()# 定義一個展開層 flattenself.flatten = nn.Flatten()# 定義一個線性容器,可以簡化在forward中的調用self.linear_relu_stack = nn.Sequential(# 容器內包含一個三層網絡# 這里的512、10都是研究者根據具體任務和數據集進行調試和優化得到的結果# 熟悉的調參nn.Linear(28*28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)# 重構forward,定義前向傳播路徑def forward(self, x):# 在這里定義各個層輸入輸出的順序,即層在網絡里的位置關系x = self.flatten(x)logits = self.linear_relu_stack(x)return logits
model = NeuralNetwork().to(device) # 將網絡移入device,并打印結構
print(model)
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)

6 使用我們的網絡

主要步驟如下:

  • 定義模型
  • 數據載入
  • 損失函數和優化
  • 訓練和評估
  • 預測與可視化

先導入所需包:

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt

6.1 定義模型

# 定義設備,如果有GPU則使用GPU,否則使用CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定義神經網絡模型
class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits# 創建神經網絡模型實例,并移動到設備上
model = NeuralNetwork().to(device)
print(model)
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)

6.2 數據載入

我們這次用的模型用于簡單圖像分類問題,所以可以使用MNIST數據集,導入用的是PyTorch的datasets。

# 加載MNIST數據集并進行預處理
transform = transforms.Compose([# 對圖片的常用操作,將圖像數據轉換為形狀為 (C, H, W) 的張量transforms.ToTensor(),# 因為數據集是灰度圖像,所以只有單值標準化transforms.Normalize((0.5,), (0.5,))
])# 加載MNIST數據集,并劃分訓練集和測試集(這里會下載下來)
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform)# 定義批量大小和數據加載器
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

6.3 損失函數和優化

損失函數選取交叉熵損失函數(Cross Entropy Loss),它是一種常用的損失函數,能夠有效地衡量預測類別和真實類別之間的差異。它能夠處理模型輸出的logits,并且在計算過程中會自動應用Softmax操作,從而簡化代碼。

優化器選取隨機梯度下降法(Stochastic Gradient Descent, SGD),它是一種簡單而有效的優化方法,特別適用于大規模數據集和模型。結合Momentum算法,SGD優化器可以加速收斂并減小震蕩,從而在一定程度上提高訓練效率和模型性能。

# 定義損失函數
criterion = nn.CrossEntropyLoss()
# 定義優化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

6.4 訓練和評估

# 訓練模型
# 訓練的目的是通過多個訓練周期和批次的數據,不斷調整模型參數以最小化損失函數,從而提高模型的性能。
def train(model, train_loader, optimizer, criterion, epochs=5):# 某些層(如Dropout和BatchNorm)在訓練和評估模式下的行為不同,所以需要顯式地設置模型為訓練模式。model.train()# 開始訓練循環for epoch in range(epochs):# 初始化損失running_loss = 0.0# 遍歷訓練數據加載器中的每個批次for batch_idx, (data, target) in enumerate(train_loader):# 將數據移動到設備上data, target = data.to(device), target.to(device)# 梯度清零optimizer.zero_grad()# 前向傳播,將數據輸入模型進行預測output = model(data)# 計算損失loss = criterion(output, target)# 將損失反向傳播loss.backward()# 使用優化器更新參數optimizer.step() # 累計損失running_loss += loss.item()if batch_idx % 100 == 99:    # 每100個批次打印一次訓練狀態print(f'Epoch [{epoch+1}/{epochs}], Step [{batch_idx+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')running_loss = 0.0# 測試模型
def test(model, test_loader):# 進入評估模式,原因與train同理model.eval()# 累計計數correct = 0total = 0# 測試過程不會進行優化,所以no_grad禁用梯度計算可以加快測試速度with torch.no_grad():for data, target in test_loader:data, target = data.to(device), target.to(device)# 前向傳播,將數據輸入模型進行預測outputs = model(data)# 獲取預測結果_, predicted = torch.max(outputs.data, 1)total += target.size(0)correct += (predicted == target).sum().item()accuracy = 100 * correct / totalprint(f'Test Accuracy: {accuracy:.2f}%')

6.5 預測與可視化

# 可視化預測結果
def visualize_predictions(model, test_loader, num_images=10):# 這里和上面定義的test相似,主要是在執行過程中添加了可視化代碼和限制了測試數量model.eval()images_so_far = 0plt.figure(figsize=(12, 8))with torch.no_grad():for i, (inputs, labels) in enumerate(test_loader):inputs = inputs.to(device)labels = labels.to(device)# 獲取每張圖的預測結果,并將數據繪制出來進行比對outputs = model(inputs)_, preds = torch.max(outputs, 1)for j in range(inputs.size(0)):images_so_far += 1ax = plt.subplot(num_images // 5, 5, images_so_far)ax.axis('off')ax.set_title(f'Predicted: {preds[j]} (Label: {labels[j]})')# imshow用于在繪圖窗口中顯示圖像ax.imshow(inputs.cpu().data[j].numpy().squeeze(), cmap='gray')if images_so_far == num_images:model.train()returnmodel.train()# 執行訓練
train(model, train_loader, optimizer, criterion)# 執行測試
test(model, test_loader)# 可視化預測結果
visualize_predictions(model, test_loader, num_images=10)
plt.suptitle('Model Predictions')
plt.tight_layout()
plt.show()
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)
Epoch [1/5], Step [100/938], Loss: 1.1812
Epoch [1/5], Step [200/938], Loss: 0.4243
Epoch [1/5], Step [300/938], Loss: 0.3437
Epoch [1/5], Step [400/938], Loss: 0.3305
Epoch [1/5], Step [500/938], Loss: 0.2820
Epoch [1/5], Step [600/938], Loss: 0.2634
Epoch [1/5], Step [700/938], Loss: 0.2482
Epoch [1/5], Step [800/938], Loss: 0.2131
Epoch [1/5], Step [900/938], Loss: 0.2161
Epoch [2/5], Step [100/938], Loss: 0.1853
Epoch [2/5], Step [200/938], Loss: 0.1658
Epoch [2/5], Step [300/938], Loss: 0.1766
Epoch [2/5], Step [400/938], Loss: 0.1507
Epoch [2/5], Step [500/938], Loss: 0.1606
Epoch [2/5], Step [600/938], Loss: 0.1347
Epoch [2/5], Step [700/938], Loss: 0.1407
Epoch [2/5], Step [800/938], Loss: 0.1371
Epoch [2/5], Step [900/938], Loss: 0.1283
Epoch [3/5], Step [100/938], Loss: 0.1027
Epoch [3/5], Step [200/938], Loss: 0.1169
Epoch [3/5], Step [300/938], Loss: 0.1150
Epoch [3/5], Step [400/938], Loss: 0.1077
Epoch [3/5], Step [500/938], Loss: 0.0986
Epoch [3/5], Step [600/938], Loss: 0.1139
Epoch [3/5], Step [700/938], Loss: 0.1110
Epoch [3/5], Step [800/938], Loss: 0.0986
Epoch [3/5], Step [900/938], Loss: 0.0927
Epoch [4/5], Step [100/938], Loss: 0.0908
Epoch [4/5], Step [200/938], Loss: 0.0834
Epoch [4/5], Step [300/938], Loss: 0.0957
Epoch [4/5], Step [400/938], Loss: 0.0742
Epoch [4/5], Step [500/938], Loss: 0.0873
Epoch [4/5], Step [600/938], Loss: 0.0786
Epoch [4/5], Step [700/938], Loss: 0.0901
Epoch [4/5], Step [800/938], Loss: 0.0828
Epoch [4/5], Step [900/938], Loss: 0.0810
Epoch [5/5], Step [100/938], Loss: 0.0682
Epoch [5/5], Step [200/938], Loss: 0.0729
Epoch [5/5], Step [300/938], Loss: 0.0601
Epoch [5/5], Step [400/938], Loss: 0.0684
Epoch [5/5], Step [500/938], Loss: 0.0755
Epoch [5/5], Step [600/938], Loss: 0.0706
Epoch [5/5], Step [700/938], Loss: 0.0733
Epoch [5/5], Step [800/938], Loss: 0.0579
Epoch [5/5], Step [900/938], Loss: 0.0621
Test Accuracy: 97.45%

在這里插入圖片描述

神經網絡(尤其是深度神經網絡)的一個非常吸引人的特點就是:它們具有很強的通用性,可以通過不同的數據集進行訓練,以解決各種不同的任務。我們可以將該模型使用另外的數據集進行訓練和測試,仍然有不低的準確率。

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt# 定義設備,如果有GPU則使用GPU,否則使用CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 定義神經網絡模型
class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits# 創建神經網絡模型實例,并移動到設備上
model = NeuralNetwork().to(device)
print(model)# 加載FashionMNIST數據集并進行預處理
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))
])train_dataset = datasets.FashionMNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.FashionMNIST(root='./data', train=False, transform=transform)# 定義批量大小和數據加載器
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)# 定義損失函數和優化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)# 訓練模型
def train(model, train_loader, optimizer, criterion, epochs=5):model.train()for epoch in range(epochs):running_loss = 0.0for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)optimizer.zero_grad()output = model(data)loss = criterion(output, target)loss.backward()optimizer.step()running_loss += loss.item()if batch_idx % 100 == 99:    # 每100個批次打印一次訓練狀態print(f'Epoch [{epoch+1}/{epochs}], Step [{batch_idx+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')running_loss = 0.0# 測試模型
def test(model, test_loader):model.eval()correct = 0total = 0with torch.no_grad():for data, target in test_loader:data, target = data.to(device), target.to(device)outputs = model(data)_, predicted = torch.max(outputs.data, 1)total += target.size(0)correct += (predicted == target).sum().item()accuracy = 100 * correct / totalprint(f'Test Accuracy: {accuracy:.2f}%')# 可視化預測結果
def visualize_predictions(model, test_loader, num_images=10):model.eval()images_so_far = 0plt.figure(figsize=(12, 8))with torch.no_grad():for i, (inputs, labels) in enumerate(test_loader):inputs = inputs.to(device)labels = labels.to(device)outputs = model(inputs)_, preds = torch.max(outputs, 1)for j in range(inputs.size(0)):images_so_far += 1ax = plt.subplot(num_images // 5, 5, images_so_far)ax.axis('off')ax.set_title(f'Predicted: {preds[j]} (Label: {labels[j]})')ax.imshow(inputs.cpu().data[j].numpy().squeeze(), cmap='gray')if images_so_far == num_images:model.train()returnmodel.train()# 執行訓練
train(model, train_loader, optimizer, criterion)# 執行測試
test(model, test_loader)# 可視化預測結果
visualize_predictions(model, test_loader, num_images=10)
plt.suptitle('Model Predictions')
plt.tight_layout()
plt.show()
NeuralNetwork((flatten): Flatten(start_dim=1, end_dim=-1)(linear_relu_stack): Sequential((0): Linear(in_features=784, out_features=512, bias=True)(1): ReLU()(2): Linear(in_features=512, out_features=512, bias=True)(3): ReLU()(4): Linear(in_features=512, out_features=10, bias=True))
)
Epoch [1/5], Step [100/938], Loss: 1.1111
Epoch [1/5], Step [200/938], Loss: 0.6047
Epoch [1/5], Step [300/938], Loss: 0.5097
Epoch [1/5], Step [400/938], Loss: 0.4919
Epoch [1/5], Step [500/938], Loss: 0.4808
Epoch [1/5], Step [600/938], Loss: 0.4519
Epoch [1/5], Step [700/938], Loss: 0.4558
Epoch [1/5], Step [800/938], Loss: 0.4473
Epoch [1/5], Step [900/938], Loss: 0.4138
Epoch [2/5], Step [100/938], Loss: 0.3960
Epoch [2/5], Step [200/938], Loss: 0.3889
Epoch [2/5], Step [300/938], Loss: 0.4075
Epoch [2/5], Step [400/938], Loss: 0.3719
Epoch [2/5], Step [500/938], Loss: 0.3819
Epoch [2/5], Step [600/938], Loss: 0.3858
Epoch [2/5], Step [700/938], Loss: 0.3838
Epoch [2/5], Step [800/938], Loss: 0.3564
Epoch [2/5], Step [900/938], Loss: 0.3616
Epoch [3/5], Step [100/938], Loss: 0.3488
Epoch [3/5], Step [200/938], Loss: 0.3507
Epoch [3/5], Step [300/938], Loss: 0.3522
Epoch [3/5], Step [400/938], Loss: 0.3363
Epoch [3/5], Step [500/938], Loss: 0.3375
Epoch [3/5], Step [600/938], Loss: 0.3445
Epoch [3/5], Step [700/938], Loss: 0.3378
Epoch [3/5], Step [800/938], Loss: 0.3208
Epoch [3/5], Step [900/938], Loss: 0.3163
Epoch [4/5], Step [100/938], Loss: 0.3189
Epoch [4/5], Step [200/938], Loss: 0.3005
Epoch [4/5], Step [300/938], Loss: 0.3071
Epoch [4/5], Step [400/938], Loss: 0.3240
Epoch [4/5], Step [500/938], Loss: 0.3147
Epoch [4/5], Step [600/938], Loss: 0.2946
Epoch [4/5], Step [700/938], Loss: 0.3150
Epoch [4/5], Step [800/938], Loss: 0.3024
Epoch [4/5], Step [900/938], Loss: 0.3152
Epoch [5/5], Step [100/938], Loss: 0.2723
Epoch [5/5], Step [200/938], Loss: 0.2969
Epoch [5/5], Step [300/938], Loss: 0.2963
Epoch [5/5], Step [400/938], Loss: 0.2835
Epoch [5/5], Step [500/938], Loss: 0.2910
Epoch [5/5], Step [600/938], Loss: 0.2990
Epoch [5/5], Step [700/938], Loss: 0.2990
Epoch [5/5], Step [800/938], Loss: 0.3039
Epoch [5/5], Step [900/938], Loss: 0.3005
Test Accuracy: 87.60%

在這里插入圖片描述

7 優化與調參

顯然,同樣的模型對于不同的數據集的適配程度是不一樣的。對于MNIST數據集準確率可以達到97%,但對于FashionMNIST只能達到86%。所以我們可以來探索一下為什么會有這樣的偏差,以及如何優化該模型才能讓FashionMNIST也可以達到90%以上的準確率。

7.1 可能的問題

  1. 數據集的復雜性
  • MNIST 數據集:包含手寫數字的灰度圖像(0-9),這些圖像相對簡單,特征明顯,模式較少。
  • FashionMNIST 數據集:包含服裝物品的灰度圖像(例如 T 恤、褲子、鞋子等),這些圖像的特征更加復雜,類別之間的差異較小。
  1. 模型的復雜性
  • 我們使用的是一個簡單的全連接神經網絡,它可能足以在 MNIST 數據集上達到高準確率,但在處理更復雜的 FashionMNIST 數據集時會表現不佳。
  1. 超參數調整:
  • 我們的模型可能需要在不同的數據集上進行不同的超參數調整。例如,學習率、批量大小、正則化參數等可能需要重新調整以適應 FashionMNIST 的復雜性。
  1. 數據預處理:
  • 數據的預處理步驟(如標準化、歸一化、數據增強等)對不同的數據集可能有不同的效果。我們可能需要針對 FashionMNIST 數據集嘗試不同的預處理方法。

7.2 解決方案

7.2.1 增加神經網絡層數和神經元容量

通過增加模型的容量,模型能夠學習更多的特征。如下,我們添加兩個線性層進一步提高模型對復雜特征的處理能力。

class NeuralNetwork_v1(nn.Module):def __init__(self):super().__init__()self.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28*28, 1024),nn.ReLU(),nn.Linear(1024, 1024),nn.ReLU(),nn.Linear(1024, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10))def forward(self, x):x = self.flatten(x)logits = self.linear_relu_stack(x)return logits

可能因為線性層在處理FashionMNIST數據集時,難以處理和學習更多的特征,在添加了線性層后預測準確率沒有明顯的提高,仍然是87%左右。所以需要嘗試其他的方法。

7.2.2 使用卷積神經網絡(CNN)

FashionMNIST 數據集涉及到服裝和配件的圖像分類,每個圖像都是單通道的灰度圖像,分辨率為 28x28 像素。盡管 FashionMNIST 數據集相對于真實世界的圖像數據集如 CIFAR-10 或 ImageNet 來說較為簡單,但仍然涉及到一定程度的空間特征。且如紋理圖案、形狀輪廓等復雜特征,線性層更加難以處理和識別。所以在局部相關性和空間結構處理占優的情況下,使用卷積神經網絡(CNN)來處理是更優的選擇。

import torch
import torch.nn as nn
import torch.nn.functional as Fclass ConvNeuralNetwork(nn.Module):def __init__(self):super(ConvNeuralNetwork, self).__init__()# 卷積層定義self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)self.conv3 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)# 扁平化層self.flatten = nn.Flatten()# 全連接層self.fc1 = nn.Linear(128*3*3, 512)self.fc2 = nn.Linear(512, 512)self.fc3 = nn.Linear(512, 10)# Dropout 正則化層,用于隨機丟棄一定比例的神經元,防止過擬合self.dropout = nn.Dropout(0.4)# 批量歸一化層,對每個卷積層的輸出進行歸一化,有助于加速收斂和提高模型泛化能力self.batch_norm1 = nn.BatchNorm2d(32)self.batch_norm2 = nn.BatchNorm2d(64)self.batch_norm3 = nn.BatchNorm2d(128)# 批量歸一化層,對全連接層的輸出進行歸一化self.batch_norm_fc1 = nn.BatchNorm1d(512)self.batch_norm_fc2 = nn.BatchNorm1d(512)def forward(self, x):# 卷積層和激活函數# 依次進行卷積、ReLU 激活函數、批量歸一化和最大池化操作x = self.conv1(x)x = F.relu(self.batch_norm1(x))x = F.max_pool2d(x, 2)x = self.conv2(x)x = F.relu(self.batch_norm2(x))x = F.max_pool2d(x, 2)x = self.conv3(x)x = F.relu(self.batch_norm3(x))x = F.max_pool2d(x, 2)# 進行全連接和正則化x = self.flatten(x)x = self.fc1(x)x = F.relu(self.batch_norm_fc1(x))x = self.dropout(x)x = self.fc2(x)x = F.relu(self.batch_norm_fc2(x))x = self.dropout(x)# 輸出層logits = self.fc3(x)return logits# 創建CNN實例,并移動到設備上
model_v1 = ConvNeuralNetwork().to(device)# 定義損失函數和優化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model_v1.parameters(), lr=0.01, momentum=0.9)# train_loader、test_loader 在之前已初始化# 訓練模型
def train_v1(model, train_loader, optimizer, criterion, epochs=5):model.train()for epoch in range(epochs):running_loss = 0.0for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)optimizer.zero_grad()output = model(data)loss = criterion(output, target)loss.backward()optimizer.step()running_loss += loss.item()if batch_idx % 100 == 99:    # 每100個批次打印一次訓練狀態print(f'Epoch [{epoch+1}/{epochs}], Step [{batch_idx+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')running_loss = 0.0# 獲取類別名稱
class_names_v1 = train_dataset.classes# 設置中文字體
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用SimHei字體
plt.rcParams['axes.unicode_minus'] = False  # 解決負號'-'顯示為方塊的問題def visualize_predictions_v1(model, dataloader, class_names):model.eval()num_classes = len(class_names)confusion_matrix = np.zeros((num_classes, num_classes), dtype=np.int32)with torch.no_grad():for inputs, labels in dataloader:inputs, labels = inputs.to(device), labels.to(device)  # 將數據移動到設備上outputs = model(inputs)_, preds = torch.max(outputs, 1)for t, p in zip(labels.view(-1), preds.view(-1)):confusion_matrix[t.long(), p.long()] += 1# 歸一化混淆矩陣confusion_matrix = confusion_matrix.astype('float') / confusion_matrix.sum(axis=1)[:, np.newaxis]# 繪圖fig, ax = plt.subplots(figsize=(10, 8))im = ax.imshow(confusion_matrix, interpolation='nearest', cmap=plt.cm.Blues)ax.figure.colorbar(im, ax=ax)ax.set(xticks=np.arange(confusion_matrix.shape[1]),yticks=np.arange(confusion_matrix.shape[0]),xticklabels=class_names, yticklabels=class_names,title='歸一化混淆矩陣',ylabel='真實標簽',xlabel='預測標簽')# 旋轉標簽并設置對齊plt.setp(ax.get_xticklabels(), rotation=45, ha="right",rotation_mode="anchor")# 遍歷數據維度并創建文本注釋fmt = '.2f'thresh = confusion_matrix.max() / 2.for i in range(confusion_matrix.shape[0]):for j in range(confusion_matrix.shape[1]):ax.text(j, i, format(confusion_matrix[i, j], fmt),ha="center", va="center",color="white" if confusion_matrix[i, j] > thresh else "black")fig.tight_layout()plt.show()# 執行訓練
train_v1(model_v1, train_loader, optimizer, criterion)# 執行測試
test(model_v1, test_loader)# 可視化預測結果
visualize_predictions_v1(model_v1, test_loader, class_names_v1)
plt.suptitle('Model Predictions_v1')
plt.tight_layout()
plt.show()
Epoch [1/5], Step [100/938], Loss: 0.7875
Epoch [1/5], Step [200/938], Loss: 0.4787
Epoch [1/5], Step [300/938], Loss: 0.4455
Epoch [1/5], Step [400/938], Loss: 0.3960
Epoch [1/5], Step [500/938], Loss: 0.3657
Epoch [1/5], Step [600/938], Loss: 0.3557
Epoch [1/5], Step [700/938], Loss: 0.3363
Epoch [1/5], Step [800/938], Loss: 0.3495
Epoch [1/5], Step [900/938], Loss: 0.3140
Epoch [2/5], Step [100/938], Loss: 0.2842
Epoch [2/5], Step [200/938], Loss: 0.3065
Epoch [2/5], Step [300/938], Loss: 0.2671
Epoch [2/5], Step [400/938], Loss: 0.2750
Epoch [2/5], Step [500/938], Loss: 0.2874
Epoch [2/5], Step [600/938], Loss: 0.2722
Epoch [2/5], Step [700/938], Loss: 0.2639
Epoch [2/5], Step [800/938], Loss: 0.2840
Epoch [2/5], Step [900/938], Loss: 0.2630
Epoch [3/5], Step [100/938], Loss: 0.2359
Epoch [3/5], Step [200/938], Loss: 0.2461
Epoch [3/5], Step [300/938], Loss: 0.2350
Epoch [3/5], Step [400/938], Loss: 0.2337
Epoch [3/5], Step [500/938], Loss: 0.2453
Epoch [3/5], Step [600/938], Loss: 0.2247
Epoch [3/5], Step [700/938], Loss: 0.2354
Epoch [3/5], Step [800/938], Loss: 0.2351
Epoch [3/5], Step [900/938], Loss: 0.2333
Epoch [4/5], Step [100/938], Loss: 0.2045
Epoch [4/5], Step [200/938], Loss: 0.2206
Epoch [4/5], Step [300/938], Loss: 0.2161
Epoch [4/5], Step [400/938], Loss: 0.2125
Epoch [4/5], Step [500/938], Loss: 0.2003
Epoch [4/5], Step [600/938], Loss: 0.2060
Epoch [4/5], Step [700/938], Loss: 0.1919
Epoch [4/5], Step [800/938], Loss: 0.2012
Epoch [4/5], Step [900/938], Loss: 0.2138
Epoch [5/5], Step [100/938], Loss: 0.1789
Epoch [5/5], Step [200/938], Loss: 0.1724
Epoch [5/5], Step [300/938], Loss: 0.1737
Epoch [5/5], Step [400/938], Loss: 0.1883
Epoch [5/5], Step [500/938], Loss: 0.1921
Epoch [5/5], Step [600/938], Loss: 0.1982
Epoch [5/5], Step [700/938], Loss: 0.2055
Epoch [5/5], Step [800/938], Loss: 0.1865
Epoch [5/5], Step [900/938], Loss: 0.1930
Test Accuracy: 91.53%

在這里插入圖片描述

<Figure size 640x480 with 0 Axes>
7.2.3 調整超參數

超參數是模型訓練過程中需要預先設定的參數,學習率、批次大小和迭代次數等都稱之為超參數。通過調整這些超參數,我們可以提高模型的性能和準確性。

  • 學習率是最重要的超參數之一。我們可以嘗試不同的學習率,觀察其對模型性能的影響:
learning_rates = [0.1, 0.01, 0.001]for lr in learning_rates:optimizer = optim.SGD(model.parameters(), lr=lr)# 訓練模型并記錄性能
  • 當然我們還可以通過學習率調度器在訓練過程中動態調整學習率
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)for epoch in range(num_epochs):# 訓練模型scheduler.step()
  • 批量大小會影響訓練的穩定性和速度:
batch_sizes = [32, 64, 128]for batch_size in batch_sizes:train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)# 訓練模型并記錄性能
  • 不同的優化器可能會對模型的收斂速度和最終性能產生影響。我們可以嘗試不同的優化器,如SGD和Adam:
optimizers = {'SGD': optim.SGD(model.parameters(), lr=0.01),'Adam': optim.Adam(model.parameters(), lr=0.01)
}

參考

  • PyTorch-構建神經網絡
  • PyTorch中文文檔
  • d2L-多層感知機

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/42016.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/42016.shtml
英文地址,請注明出處:http://en.pswp.cn/web/42016.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ffmpeg濾鏡創建過程

1、創建一個濾鏡圖 AVFilterGraph *filter_graph avfilter_graph_alloc(); 2、創建濾鏡的輸入和輸出 AVFilterInOut *inputs avfilter_inout_alloc(); AVFilterInOut *outputs avfilter_inout_alloc(); 3、每個濾鏡創建上下文 AVFilterContext *filter1_ctx avfilter_…

Yolov10訓練,轉化onnx,推理

yolov10對于大目標的效果好&#xff0c;小目標不好 一、如果你訓練過yolov5&#xff0c;yolov8&#xff0c;的話那么你可以直接用之前的環境就行 目錄 一、如果你訓練過yolov5&#xff0c;yolov8&#xff0c;的話那么你可以直接用之前的環境就行 二、配置好后就可以配置文件…

android webview 遠程調試

打開遠程調試選項 MainActivity super.onCreate(savedInstanceState);// enable Cordova apps to be started in the backgroundBundle extras getIntent().getExtras();if (extras ! null && extras.getBoolean("cdvStartInBackground", false)) {moveT…

前端JS特效第24集:jquery css3實現瀑布流照片墻特效

jquery css3實現瀑布流照片墻特效&#xff0c;先來看看效果&#xff1a; 部分核心的代碼如下(全部代碼在文章末尾)&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8" /> <title>jquerycss3實現瀑…

Nginx:負載均衡小專題

運維專題 Nginx&#xff1a;負載均衡小專題 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/…

在Conda環境中高效使用Kubernetes:跨平臺容器化實踐指南

摘要 Conda 是一個流行的跨平臺包和環境管理器&#xff0c;廣泛用于Python社區。而 Kubernetes 是一個開源的容器編排系統&#xff0c;用于自動化部署、擴展和管理容器化應用程序。本文將探討如何在 Conda 環境中使用 Kubernetes&#xff0c;包括設置 Conda 環境、容器化應用程…

【專項刷題】— 位運算

常見類型介紹&#xff1a; & &#xff1a;有 0 就是 0 | &#xff1a;有 1 就是 1 ^ &#xff1a;相同為 0 &#xff0c;相異為 1 或者 無進位相加給定一個數確定它的二進制位的第x個數是0還是1&#xff1a;將一個數的二進制的第x位改成1&#xff1a;將一個數的二進制的第x…

Windows10/11家庭版開啟Hyper-V虛擬機功能詳解

Hyper-V是微軟的一款虛擬機軟件&#xff0c;可以使我們在一臺Windows PC上&#xff0c;在虛擬環境下同時運行多個互相之間完全隔離的操作系統&#xff0c;這就實現了在Windows環境下運行Linux以及其他OS的可能性。和第三方虛擬機軟件&#xff0c;如VMware等相比&#xff0c;Hyp…

Linux應用編程IO基礎

Linux應用編程基本IO操作 一、main 函數1、main 函數寫法之無傳參2、main 函數寫法之有傳參 二、open 打開文件三、write 寫文件四、read 讀文件五、close 關閉文件六、 lseek七、 返回錯誤處理與 errno7.1 strerror 函數7.2 perror 函數 八、 exit、_exit、_Exit8.1_exit()和_…

零基礎自學爬蟲技術該從哪里入手?

零基礎學習Python并不一定是困難的&#xff0c;這主要取決于個人的學習方法、投入的時間以及學習目標的設定。Python是一門相對容易入門的編程語言&#xff0c;它有著簡潔的語法、豐富的庫和廣泛的應用領域&#xff08;如數據分析、Web開發、人工智能等&#xff09;&#xff0c…

大模型知識問答: 文本分塊要點總結

節前&#xff0c;我們組織了一場算法崗技術&面試討論會&#xff0c;邀請了一些互聯網大廠朋友、今年參加社招和校招面試的同學。 針對大模型技術趨勢、算法項目落地經驗分享、新手如何入門算法崗、該如何準備面試攻略、面試常考點等熱門話題進行了深入的討論。 總結鏈接如…

C++ 信號量和鎖的區別

網上關于信號量和鎖的區別&#xff0c;寫的比較官方晦澀難懂&#xff0c;對于這個知識點吸收難&#xff0c;通過示例&#xff0c;我們看到信號量&#xff0c;可以控制同一時刻的線程數量&#xff0c;就算同時開啟很多線程&#xff0c;依然可以的達到線程數可控 #include <i…

初識c++(命名空間,缺省參數,函數重載)

一、命名空間 1、namespace的意義 在C/C中&#xff0c;變量、函數和后面要學到的類都是大量存在的&#xff0c;這些變量、函數和類的名稱將都存在于全 局作用域中&#xff0c;可能會導致很多沖突。使用命名空間的目的是對標識符的名稱進行本地化&#xff0c;以避免命名 沖突…

GEE代碼實例教程詳解:MODIS土地覆蓋分類與面積計算

簡介 在本篇博客中&#xff0c;我們將使用Google Earth Engine (GEE) 對MODIS土地覆蓋數據進行分析。通過MODIS/061/MCD12Q1數據集&#xff0c;我們可以識別不同的土地覆蓋類型&#xff0c;并計算每種類型的總面積。 背景知識 MODIS MCD12Q1數據集 MODIS/061/MCD12Q1是NASA…

每天一個數據分析題(四百十五)- 線性回歸模型

線性回歸模型中誤差項的數學期望為 A. 0 B. 1 C. 2 D. 3 數據分析認證考試介紹&#xff1a;點擊進入 題目來源于CDA模擬題庫 點擊此處獲取答案 數據分析專項練習題庫 內容涵蓋Python&#xff0c;SQL&#xff0c;統計學&#xff0c;數據分析理論&#xff0c;深度學習&am…

世界商用飛機機型大全-使用Java抓取FlightAware后的答案

目錄 前言 一、數據說明 1、實時航班飛機機型數據 2、網頁結構分析 二、使用Java進行信息抓取 1、定義頁面PageVO對象 2、爬取屬性定義 3、啟動信息抓取組件 三、成果分析 1、商業飛行的飛機機型的種類 2、飛機種類排名前十名 3、航班數排名后十名 4、看中國國產大飛…

【網絡安全】一文帶你了解什么是【網絡劫持】

網絡劫持&#xff08;Network Hijacking&#xff09;是一種網絡攻擊&#xff0c;攻擊者通過非法手段劫持網絡通信&#xff0c;導致合法用戶的數據流被攔截、篡改或重定向到攻擊者控制的系統。這種攻擊可以在各種網絡層面上進行&#xff0c;包括域名系統&#xff08;DNS&#xf…

你真的會信息收集嘛,4k字滲透測試信息收集10大技巧

前言 在滲透測試中&#xff0c;信息收集是非常關鍵的一步&#xff0c;它為后續的漏洞發現和利用提供了重要的基礎。以下是非常詳細的信息收集方式&#xff1a; 一、被動信息收集 被動信息收集是指在不與目標系統直接交互的情況下&#xff0c;通過公開渠道獲取目標系統的相關…

基于51單片機的四路搶答器Protues仿真設計

一、設計背景 近年來隨著科技的飛速發展&#xff0c;單片機的應用正在不斷的走向深入。本文闡述了基于51單片機的八路搶答器設計。本設計中&#xff0c;51單片機充當了核心控制器的角色&#xff0c;通過IO口與各個功能模塊相連接。按鍵模塊負責檢測參與者的搶答動作&#xff0c…

線程交互現象

線程交互現象 小明對自家的狗子有個規定,就是在狗狗還沒吃完的時候,可以繼續給他加飯 不好的解決方式 狗狗感覺一千年沒吃飯了,狼吞虎咽起來,最后飯只剩下最后一點點,吃飯線程中使用while循環判斷是否是1,如果是1那么就一直循環,知道加飯又重新回到了起點,這雖然是狗狗…