影片:https://www.bilibili.com/video/BV1Wf421B74f/?spm_id_from=333.880.my_history.page.click
1.1 Model類
import torch
import torch.nn as nn
# 改進的三層神經網路
class MyNet(nn.Module):
def __init__(self):
super().__init__()
# 定義全連線層
self.fc1 = nn.Linear(28 * 28, 256) # 輸入層 輸入是28*28的灰度影像,輸出是256個神經元
self.fc2 = nn.Linear(256, 128) # 第二層,全連線層,輸入256個神經元,輸出128個神經元
self.fc3 = nn.Linear(128, 64) # 第三層,全連線層,輸入128個神經元,輸出64個神經元
self.fc4 = nn.Linear(64, 10) # 第四層,全連線層,輸入64個神經元,輸出10個類別
def forward(self, x):
x = torch.flatten(x, start_dim=1) # 展平資料,方便進行全連線
x = torch.relu(self.fc1(x)) # 第一層+ReLU啟用
x = torch.relu(self.fc2(x)) # 第二層+ReLU啟用
x = torch.relu(self.fc3(x)) # 第三層+ReLU啟用
x = self.fc4(x) # 第四層 輸出層 不需要啟用函式
return x
2 train.py建立
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
from .model import MyNet
# 設定隨機種子
torch.manual_seed(21)
# 檢查是否有可用的GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device:{device}")
transform = transforms.Compose([
transforms.ToTensor(), # 將影像轉為張量
transforms.Normalize((0.5,), (0.5,)) # 標準化影像資料,對於灰度影像,只需要一個通道的標準化
])
# 載入MNIST資料集
train_dataset = datasets.MNIST(root='../../datasets', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='../../datasets', train=False, download=True, 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)
# 初始化模型並將模型移到GPU上
model = MyNet().to(device)
# 定義損失函式和最佳化器
lr = 0.001 # 學習率為0.001
criterion = nn.CrossEntropyLoss() # 交叉熵
optimizer = optim.Adam(model.parameters(), lr=lr) # 使用Adam最佳化器
# 儲存訓練過程中的損失和準確率
train_losses = []
train_accuracies = []
test_accuracies = []
epochs = 10
best_accuracy = 0.0 # 記錄最佳驗證集準確率
best_model_path = 'best_mnist_model.pth'
# 訓練10個epoch
for epoch in range(epochs):
running_loss = 0.0
corrct_train = 0 # 正確預測的數量
total_train = 0 # 樣本總數
# 訓練過程
model.train() # 設定模型為訓練模式
for inputs, labels in train_loader:
inputs, labels = inputs.to(device), labels.to(device) # 將資料移動到GPU上
optimizer.zero_grad() # 梯度清零
outputs = model(inputs) # 前向傳播
loss = criterion(outputs, labels) # 計算損失
loss.backward() # 反向傳播
optimizer.step() # 更新引數
running_loss += loss.item() # 累加損失
# 計算訓練集上的準確率
_, predicted = torch.max(outputs, 1) # 獲取預測結果
total_train += labels.size(0) # 累加樣本數量
corrct_train += (predicted == labels).sum().item() # 累加正確預測的數量
# 計算訓練集上的準確率
train_accuracy = corrct_train / total_train
train_losses.append(running_loss / len(train_loader)) # 記錄每個 epoch 的平均損失,len(train_loader)為批次數,一個epoch結束後
train_accuracies.append(train_accuracy) # 記錄每個 epoch 的訓練集準確率
print(
f"Epoch: {epoch + 1}/{epochs}, Loss: {running_loss / len(train_loader):.4f}, Train Accuracy: {train_accuracy:.2%}")
# 在測試集上評估模型
model.eval() # 設定模型為評估模式
correct = 0 # 正確的預測數量
total = 0 # 樣本總數
with torch.no_grad(): # 關閉梯度計算
for inputs, labels in test_loader:
inputs, labels = inputs.to(device), labels.to(device) # 將資料移動到GPU上
outputs = model(inputs) # 前向傳播
_, predicted = torch.max(outputs, 1) # 獲取預測結果
total += labels.size(0) # 累加樣本數量
correct += (predicted == labels).sum().item() # 累加正確預測的數量
# 計算測試集上的準確率
test_accuracy = correct / total # 記錄每個 epoch 的測試集準確率
test_accuracies.append(test_accuracy)
print(f"Epoch: {epoch + 1}/{epochs},Test Accuracy: {test_accuracy:.2%}")
# 如果測試集準確率提高,儲存當前模型的權重
if test_accuracy > best_accuracy:
best_accuracy = test_accuracy
torch.save(model.state_dict(), best_model_path)
print(f"Best model saved with accuracy: {best_accuracy:.2%}")
print(f"Best Accuracy on test set: {best_accuracy:.2%}")
# 繪製並儲存損失和準確率曲線
plt.figure(figsize=(12, 5))
# 繪製損失曲線
plt.subplot(1, 2, 1) # 選擇第一個子圖
plt.plot(train_losses, label='Training Loss') # 傳入資料、設定標籤為Training Loss
plt.xlabel('Epoch') # x軸資料
plt.ylabel('Loss')
plt.title('Training Loss over Epochs') # 設定標題
plt.legend() # 新增圖例
plt.grid(True) # 新增網格
# 繪製訓練集和測試集準確率曲線
plt.subplot(1, 2, 2)
plt.plot(train_accuracies, label='Train Accuracy')
plt.plot(test_accuracies, label='Test Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Train and Test Accuracy over Epochs')
plt.legend()
plt.grid(True)
# 儲存影像
plt.tight_layout()
plt.savefig('loss_and_accuracy.png')
plt.show()
3 predict.py 建立
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
from .model import MyNet
# 設定隨機種子
torch.manual_seed(21)
# 檢查是否有可用的GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device:{device}")
# 定義資料預處理
transform = transforms.Compose([
transforms.ToTensor(), # 將影像轉為張量
transforms.Normalize((0.5,), (0.5,)) # 標準化影像資料,對於灰度影像,只需要一個通道的標準化
])
# 載入MNIST資料集
test_dataset = datasets.MNIST(root='../../datasets', train=False, download=True, transform=transform)
# 建立資料載入器
batch_size = 10
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
# 初始化模型並將模型移到GPU上
model = MyNet().to(device)
# 載入儲存的權重
best_model_path = 'best_mnist_model.pth'
model.load_state_dict(torch.load(best_model_path))
# 設定模型為評估模式
model.eval()
# 從測試集中取10張圖片
examples = enumerate(test_loader) # 從 test loader 中獲取一個帶索引的迭代器
# #使用 next()函式從迭代器中獲取下一個批次的資料。
# next()返回一個元組,第一個元素是批次的索引(batch idx),
# #第二個元素是一個包含輸入資料和目標標籤的元組(example_data,example_targets)
batch_idx, (example_data, example_targets) = next(examples)
# 將資料移動到指定的裝置上(例如,CPU或GPU),
# 以便利用裝置的計算能力進行進一步的操作(如模型推理或訓練)
example_data, example_targets = example_data.to(device), example_targets.to(device)
# 進行推理
with torch.no_grad(): # 使用 torch.no_grad()上下文管理器,表示在此上下文中不計算梯度。
outputs = model(example_data) # 使用模型對 example_data 進行前向傳播,獲取輸出結果。
# 使用 torch.max()函式,獲取 outputs 張量中每行的最大值及其索引。
# 1 表示在第一個維度上(通常是類別維度)尋找最大值。
_, predicted = torch.max(outputs, 1)
# 視覺化並顯示預測結果
fig, axes = plt.subplots(1, 10, figsize=(15, 2)) # 建立一個包含1行10列的子圖(subplots)的圖形,並指定每個子圖的大小
for i in range(10):
axes[i].imshow(example_data[i].cpu().squeeze(), cmap='gray') # 在指定的子圖(axes[i])中顯示影像
axes[i].set_title(f"Pred:{predicted[i].item()}") # 在每個子圖(axes[i])上設定標題,標題內容顯示模型對該樣本的預測結果
axes[i].axis('off') # 關閉第 i個子圖(axes[i])的座標軸顯示
plt.show()
# 輸出批次中部分樣本的真實標籤和模型的預測標籤
print(f"True labels: {example_targets.cpu().numpy()}")
print(f"Predicted labels: {predicted.cpu().numpy()}")