pytorch——貓狗識別

貓狗識別

  • 訓練模型
    • 導入需要的包
    • 數據加載
    • 數據預處理
    • 加載數據集并返回對應的圖像和標簽
    • 提取標簽信息
    • 創建訓練和測試的數據加載器
    • 圖像分類
      • CNN的卷積神經網絡模型
      • MYVGG的卷積神經網絡模型
      • AlexNet的卷積神經網絡模型
    • 訓練過程
    • 測試過程
    • 定義了一個主函數
  • 測試模型
    • 導入需要的庫
    • 加載之前訓練好的模型
    • 加載新的測試圖片并進行預處理
    • 對圖片進行預處理
    • 對新圖片進行預處理轉換,并添加一個batch維度
    • 使用訓練好的模型進行推理
    • 顯示新的測試圖片
    • 運行結果:

訓練模型

導入需要的包

import torch:導入PyTorch深度學習框架。 from torch import optim:從torch模塊中導入optim優化器,用于模型的優化。 import torch.nn as nn:導入torch.nn模塊,其中包含了神經網絡的相關函數和類。 from torch.autograd import Variable:從torch.autograd模塊導入Variable類,用于封裝張量并支持自動求導。 from torchvision import transforms:從torchvision模塊導入transforms,用于數據的預處理和增強。
from torch.utils.data import Dataset, DataLoader:從torch.utils.data模塊導入Dataset和DataLoader類,用于自定義數據集和數據加載。
from PIL import Image:從PIL庫中導入Image模塊,用于處理圖像數據。 import torch.nn.functional as F:導入torch.nn.functional模塊并給其命名為F,其中包含了一些常用的函數,如激活函數、損失函數等。

import torch
from torch import optim
import torch.nn as nn
from torch.autograd import Variable
from torchvision import transforms
from torch.utils.data import Dataset, DataLoader
from PIL import Image
import torch.nn.functional as F

數據加載

接收一個路徑 path,使用PIL庫中的 Image.open 方法打開指定路徑的圖像文件,然后通過
convert('RGB') 方法將圖像轉換為RGB格式。最后將轉換后的圖像返回。

def Myloader(path):
return Image.open(path).convert('RGB')

數據預處理

得到一個包含路徑與標簽的列表 該函數用于初始化數據并生成包含路徑和標簽的列表。函數會調用 find_label(path) 函數來獲取路徑
path 對應的標簽。然后通過循環從 lens[0] 到 lens[1],依次生成包含路徑和標簽的列表項,將其添加到 data 列表中。

def init_process(path, lens):data = []name = find_label(path)for i in range(lens[0], lens[1]):data.append([path % i, name])return data

加載數據集并返回對應的圖像和標簽

init 方法:類的初始化方法,接收三個參數 data、transform、loader,分別表示數據集、數據轉換操作、數據加載器。在初始化過程中,將這三個參數保存在類的成員變量中。

getitem 方法:用于獲取數據集中指定索引 item 的數據。首先從 self.data 中根據索引 item 獲取圖像路徑 img 和標簽 label。然后通過 self.loader 加載圖像,并通過 self.transform
進行圖像的轉換操作。最后返回經過加載和轉換后的圖像 img 和對應的標簽 label。

len 方法:返回數據集的長度,即數據集中樣本的數量。

class MyDataset(Dataset):def __init__(self, data, transform, loder):self.data = dataself.transform = transformself.loader = loderdef __getitem__(self, item):img, label = self.data[item]img = self.loader(img)img = self.transform(img)return img, labeldef __len__(self):return len(self.data)

提取標簽信息

函數首先定義了兩個變量first和last,分別用于記錄標簽字符串的起始位置和結束位置,并初始化為0。
然后通過循環遍歷給定的路徑字符串str,從末尾向前查找符號’%‘和’.‘,以及字符’c’或’d’和’/‘進行位置的提取。
最后根據提取的起始和結束位置,截取標簽字符串name。 如果截取的標簽字符串為’dog’,則返回標簽值1;否則返回標簽值0。

def find_label(str):first, last = 0, 0for i in range(len(str) - 1, -1, -1):if str[i] == '%' and str[i - 1] == '.':last = i - 1if (str[i] == 'c' or str[i] == 'd') and str[i - 1] == '/':first = ibreakname = str[first:last]if name == 'dog':return 1else:return 0

創建訓練和測試的數據加載器

首先創建了一個圖像轉換操作transform,包括隨機水平翻轉、隨機垂直翻轉、調整大小為(256, 256)、轉換為張量以及標準化的操作。

然后定義了四個路徑變量path1、path2、path3、path4,分別表示訓練集中貓的圖像路徑模板、訓練集中狗的圖像路徑模板、測試集中貓的圖像路徑模板、測試集中狗的圖像路徑模板。

對每個路徑使用init_process函數初始化數據,數據范圍為[0, 500]和[1000,
1200],并將數據存儲在相應的data1、data2、data3、data4列表中。

組合一部分訓練數據和標簽數據,創建訓練數據集train實例,剩余數據用于測試數據集test實例。
使用DataLoader類分別創建訓練數據加載器train_data和測試數據加載器test_data,設置批量大小、是否打亂數據和工作進程數。

最后返回訓練數據加載器train_data和測試數據加載器test_data。

def load_data():transform = transforms.Compose([transforms.RandomHorizontalFlip(p=0.3),transforms.RandomVerticalFlip(p=0.3),transforms.Resize((256, 256)),transforms.ToTensor(),transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))])path1 = 'D:/probject/pythonProject1/pytorch/data/training_data/cats/cat.%d.jpg'data1 = init_process(path1, [0, 500])path2 = 'D:/probject/pythonProject1/pytorch/data/training_data/dogs/dog.%d.jpg'data2 = init_process(path2, [0, 500])path3 = 'D:/probject/pythonProject1/pytorch/data/testing_data/cats/cat.%d.jpg'data3 = init_process(path3, [1000, 1200])path4 = 'D:/probject/pythonProject1/pytorch/data/testing_data/dogs/dog.%d.jpg'data4 = init_process(path4, [1000, 1200])# 1300個訓練train_data = data1 + data2 + data3[0: 150] + data4[0: 150]train = MyDataset(train_data, transform=transform, loder=Myloader)# 100個測試test_data = data3[150: 200] + data4[150: 200]test = MyDataset(test_data, transform=transform, loder=Myloader)train_data = DataLoader(dataset=train, batch_size=10, shuffle=True, num_workers=0)test_data = DataLoader(dataset=test, batch_size=1, shuffle=True, num_workers=0)return train_data, test_data

圖像分類

CNN的卷積神經網絡模型

這段代碼定義了一個名為CNN的卷積神經網絡模型,用于圖像分類任務,并定義了其前向傳播方法forward。
在__init__方法中,定義了卷積層conv1和conv2,池化層pool,以及全連接層output和dp1(Dropout層)。

在forward方法中,對輸入數據x進行前向傳播計算,首先通過第一個卷積層conv1和池化層進行特征提取和下采樣,然后經過第二個卷積層conv2和池化層繼續提取特征和下采樣。

接著將特征張量展平為一維向量,通過dp1(Dropout)進行正則化處理,最后通過全連接層output生成最終輸出,其中output的輸出維度是二分類(2個類別)。

# 三種網絡擇優選擇其中一種即可
class CNN(nn.Module):def __init__(self, num_classes=2):super(CNN, self).__init__()self.conv1 = nn.Conv2d(3, 16, 3, padding=1) self.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(16, 16, 3, padding=1)self.pool = nn.MaxPool2d(2, 2)self.output = nn.Linear(16 * 64 * 64, 2)  self.dp1 = nn.Dropout(p=0.5)def forward(self, x):x = self.pool(F.relu(self.conv1(x)))x = self.pool(F.relu(self.conv2(x)))temp = x.view(x.size()[0], -1)x = self.dp1(x)output = self.output(temp)return output, x

這段代碼是一個定義了名為MYVGG的卷積神經網絡模型,類似于VGG網絡結構,用于圖像分類任務。在該模型中,包括8個卷積層和池化層的組合,最后連接一個全連接層和Dropout層。

__init__方法中初始化了模型的網絡層。每個卷積層后面跟著一個最大池化層。卷積核數量和大小逐漸增加,通過池化層逐步減小特征圖的尺寸。

最后一個卷積層的輸出經過展平操作后,經過一個Dropout層進行正則化處理。

輸出通過全連接層output生成最終的分類結果。

forward方法定義了模型的前向傳播過程。輸入數據經過一系列的卷積、激活函數ReLU和池化操作,最終通過全連接層和Dropout層得到輸出。

MYVGG的卷積神經網絡模型

class MYVGG(nn.Module):def __init__(self, num_classes=2):super(MYVGG, self).__init__()self.conv1 = nn.Conv2d(3, 64, 3,padding=1)self.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(64, 64, 3,padding=1)self.pool = nn.MaxPool2d(2, 2)self.conv3 = nn.Conv2d(64, 128, 3,padding=1)self.pool = nn.MaxPool2d(2, 2)self.conv4 = nn.Conv2d(128, 128, 3,padding=1)self.pool = nn.MaxPool2d(2, 2)self.conv5 = nn.Conv2d(128, 256, 3,padding=1)self.pool = nn.MaxPool2d(2, 2)self.conv6 = nn.Conv2d(256, 256, 3,padding=1)self.pool = nn.MaxPool2d(2, 2)self.conv7 = nn.Conv2d(256, 512, 3,padding=1)self.pool = nn.MaxPool2d(2, 2)self.conv8 = nn.Conv2d(512, 512, 3, padding=1)self.pool = nn.MaxPool2d(2, 2)self.output = nn.Linear(512, num_classes)self.dp1 = nn.Dropout(p=0.5)def forward(self, x):x = self.pool(F.relu(self.conv1(x)))x = self.pool(F.relu(self.conv2(x)))x = self.pool(F.relu(self.conv3(x)))x = self.pool(F.relu(self.conv4(x)))x = self.pool(F.relu(self.conv5(x)))x = self.pool(F.relu(self.conv6(x)))x = self.pool(F.relu(self.conv7(x)))x = self.pool(F.relu(self.conv8(x)))temp = x.view(x.size()[0], -1)x = self.dp1(x)output = self.output(temp)return output, x

AlexNet的卷積神經網絡模型

AlexNet的卷積神經網絡模型,類似于AlexNet網絡結構,用于圖像分類任務。在該模型中,包括5個卷積層和池化層的組合,最后連接一個全連接層和Dropout層。

__init__方法中初始化了模型的網絡層。每個卷積層后面跟著一個最大池化層。卷積核數量和大小逐漸增加,通過池化層逐步減小特征圖的尺寸。

最后一個卷積層的輸出經過展平操作后,經過一個Dropout層進行正則化處理。

輸出通過全連接層output生成最終的分類結果。

forward方法定義了模型的前向傳播過程。輸入數據經過一系列的卷積、激活函數ReLU和池化操作,最終通過全連接層和Dropout層得到輸出。

class AlexNet(nn.Module):def __init__(self):super(AlexNet,self).__init__()self.conv1 = nn.Conv2d(3, 32, 3)self.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(32, 64, 3)self.pool = nn.MaxPool2d(2, 2)self.conv3 = nn.Conv2d(64, 128, 3)self.pool = nn.MaxPool2d(2, 2)self.conv4 = nn.Conv2d(128, 256, 3)self.pool = nn.MaxPool2d(2, 2)self.conv5 = nn.Conv2d(256, 512, 3)self.pool = nn.MaxPool2d(2, 2)self.output = nn.Linear(in_features=512 * 6 * 6,out_features=2)self.dp1 = nn.Dropout(p=0.5)def forward(self,x):x = self.pool(F.relu(self.conv1(x)))x = self.pool(F.relu(self.conv2(x)))x = self.pool(F.relu(self.conv3(x)))x = self.pool(F.relu(self.conv4(x)))x = self.pool(F.relu(self.conv5(x)))temp = x.view(x.shape[0], -1)x = self.dp1(x)output = self.output(temp)return output, x

訓練過程

代碼實現了一個簡單的訓練過程,包括數據加載、模型定義、優化器設置、損失函數定義、循環訓練和參數保存等步驟。 train_loader, test_loader = load_data():加載訓練和測試數據集。 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu'):檢測是否有可用的GPU,將模型放到對應的設備上。
model = AlexNet().to(device):實例化AlexNet模型并將其移動到指定的設備上。 optimizer = optim.Adam(model.parameters(), lr=0.00004):定義Adam優化器,并傳入模型參數和學習率。
criterion = nn.CrossEntropyLoss().to(device):定義交叉熵損失函數,用于計算預測值和目標值之間的損失。

訓練循環中,遍歷每個epoch和每個batch: optimizer.zero_grad():梯度清零。 output = model(data)[0]:通過模型進行前向傳播。 loss = criterion(output, target):計算損失值。
loss.backward():反向傳播計算梯度。 optimizer.step():更新模型參數。 打印每個迭代的訓練損失信息。
通過torch.save()函數,模型在訓練完成后會保存在指定路徑下。

def train():train_loader, test_loader = load_data()epoch_num = 20# GPU計算device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')model = AlexNet().to(device)optimizer = optim.Adam(model.parameters(), lr=0.00004)criterion = nn.CrossEntropyLoss().to(device)for epoch in range(epoch_num):for batch_idx, (data, target) in enumerate(train_loader, 0):data, target = Variable(data).to(device), Variable(target.long()).to(device)optimizer.zero_grad()  # 梯度清0output = model(data)[0]  # 前向傳播loss = criterion(output, target)  # 計算誤差loss.backward()  # 反向傳播optimizer.step()  # 更新參數if batch_idx % 10 == 0:print('Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(epoch, batch_idx * len(data), len(train_loader.dataset),100. * batch_idx / len(train_loader), loss.item()))torch.save(model, 'D:/probject/pythonProject1/pytorch/cnn.pkl')

測試過程

用于評估訓練好的模型在測試集上的準確率 test()函數首先加載訓練和測試數據集,并檢測可用的設備(GPU或CPU)。
model =torch.load('D:/probject/pythonProject1/pytorch/cnn.pkl'):加載之前訓練好的模型。

在測試循環中,遍歷測試集中的每個數據樣本: 將圖像數據和標簽移動到指定的設備上。 通過加載的模型進行前向傳播,得到預測輸出。
通過torch.max(outputs.data, 1)[1].data獲取預測的類別。
統計總樣本數total和預測正確的樣本數current。

最后,計算模型在測試集上的準確率,并打印輸出。準確率的計算方式是正確預測的樣本數除以總樣本數,然后乘以100以得到百分比表示。

def test():train_loader, test_loader = load_data()device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')model = torch.load('D:/probject/pythonProject1/pytorch/cnn.pkl')  # load modeltotal = 0current = 0for data in test_loader:images, labels = dataimages, labels = images.to(device), labels.to(device)outputs = model(images)[0]predicted = torch.max(outputs.data, 1)[1].datatotal += labels.size(0)current += (predicted == labels).sum()print('Accuracy: %d %%' % (100 * current / total))

定義了一個主函數

用于執行訓練和測試過程 train()和test()函數是前面解釋過的訓練和測試過程,分別進行模型訓練和測試。
if __name__ == '__main__'::判斷是否是主程序入口,即在作為主程序運行時執行以下代碼。
train():首先執行訓練過程。
test():之后執行測試過程,評估訓練好的模型在測試集上的準確率。

if __name__ == '__main__':train()
test()

運行結果:
在這里插入圖片描述

測試模型

導入需要的庫

torch:PyTorch深度學習框架。 transforms:用于圖像預處理的模塊。 Image:用于處理圖像的PIL庫。
torch.nn.functional as F:包含了各種神經網絡的函數接口。
matplotlib.pyplot as plt:用于可視化的庫。

import torch
from torchvision import transforms
from PIL import Image
import torch.nn.functional as F
import matplotlib.pyplot as plt

加載之前訓練好的模型

# 加載模型
model = torch.load('D:/probject/pythonProject1/pytorch/cnn.pkl')

加載新的測試圖片并進行預處理

# 加載新圖片
new_image_path = 'noise.jpg'  # 請替換為新圖片的路徑
img = Image.open(new_image_path).convert('RGB')

對圖片進行預處理

transforms.Compose()定義了一系列的圖像預處理操作,包括調整大小、轉換為張量、歸一化等。

transform = transforms.Compose([transforms.Resize((256, 256)),transforms.ToTensor(),transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))
])

對新圖片進行預處理轉換,并添加一個batch維度

img = transform(img)
img = img.unsqueeze(0)  # 添加batch維度

使用訓練好的模型進行推理

output, _ = model(img):使用模型進行前向傳播,并獲取輸出結果。
通過torch.max(output, 1)[1].item()獲取預測類別。

output, _ = model(img)
predicted_class = torch.max(output, 1)[1].item()

展示分類結果和圖片

class_names = ['cat', 'dog']
print("Predicted class:", class_names[predicted_class])

顯示新的測試圖片

plt.imshow(img.squeeze().numpy().transpose((1, 2, 0)))
plt.show()

運行結果:

在這里插入圖片描述

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

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

相關文章

無線模塊應用晶振SG5032VAN

隨著物聯網(IoT)和無線通信技術的迅速發展,無線模塊已經成為各種智能設備的重要組成部分。在無線模塊中,選擇高性能的時鐘源對于確保系統的穩定性和可靠性至關重要。愛普生SG5032VAN是一款LVDS差分晶振,作為一款高性能…

WINUI——Behavior(行為)小結

前言 在使用MVVM進行WINUI或WPF開發時,Command在某些時候并不能滿足邏輯與UI分離的要求。這時肯定就需要其它技術的支持,Behavior就是一種。在WPF中是有Behavior直接支持的,轉到WINUI后,相對有一些麻煩,于是在此記錄之…

Enscape 4.1.0 軟件安裝教程+離線資源庫

軟件介紹 Enscape 是專門為建筑、規劃、景觀及室內設計師打造的渲染產品,無需導入導出文件,在常用的軟件內部即可看到逼真的渲染效果。 你無需了解記憶各種參數的用法,一切都是傻瓜式的一鍵渲染,你可以把精力更多地投入到設計中…

歸并排序——二路歸并排序

目錄 1、簡述 2、復雜度 3、穩定性 4、例子 1、簡述 二路歸并排序(Merge Sort)是一種基于分治法的排序算法,通過將數組遞歸地拆分成兩部分,分別排序后再合并,從而實現整個數組的有序。二路歸并排序具有穩定性和高…

ElementUI之el-tooltip顯示多行內容

ElementUI之el-tooltip顯示多行內容 文章目錄 ElementUI之el-tooltip顯示多行內容1. 多行文本實現2. 實現代碼3. 展示效果 1. 多行文本實現 展示多行文本或者是設置文本內容的格式,使用具名 slot 分發content,替代tooltip中的content屬性。 2. 實現代碼 …

Sui主網升級至V1.26.2版本

Sui主網現已升級至V1.26.2版本,同時Sui協議升級至48版本。 其他升級要點如下所示: 協議 #17881 Sui原生隨機性功能現在已在測試網啟用。 索引器 #17649 JSON-RPC:現在JSON-RPC在查詢時將返回正確的幣種元數據和總供應量信息。 索引器…

【圖像處理與機器視覺】灰度變化與空間濾波

基礎 空間域與變換域 空間域:認為是圖像本身,對于空間域的操作就是對圖像中的像素直接進行修改 變換域:變換系數處理,不直接對于圖像的像素進行處理 鄰域 圖像中某點的鄰域被認為是包含該點的小區域,也被稱為窗口 …

在IDEA中使用Git在將多次commit合并為一次commit

案例: 我想要將master分支中的 測試一、測試二、測試三三次commit合并為一次commit 1. 點擊Git 2. 雙擊點擊commit所在的分支 3. 右鍵要合并的多個commit中的第一次提交的commit 4. 點擊右鍵后彈出的菜單中的Interactively Rebase From Here選項 5. 點擊測試二…

常用Linux命令的具體使用示例

文件操作類: ls -l: 列出當前目錄下所有文件和目錄的詳細信息。cd /home: 切換到/home目錄。pwd: 顯示當前工作目錄的完整路徑。cp source.txt destination.txt: 將source.txt文件復制到destination.txt。mv oldname.txt newname.txt: 將文件oldname.txt重命名為ne…

MySQL排序操作

025排序操作 select .. from .. order by 字段 asc/descselect empno, ename, sal from emp order by sal asc;asc 不寫的話,默認升序 多個字段排序 查詢員工的編號、姓名、薪資,按照薪資升序排列,如果薪資相同的,再按照姓名升…

二叉樹的順序結構(堆的實現)

前言 普通的二叉樹是不適合用數組來存儲的,因為可能會存在大量的空間浪費。而完全二叉樹更適合使用順序結 構存儲。 現實中我們通常把堆 ( 一種二叉樹 ) 使用順序結構的數組來存儲,需要注意的是這里的堆和操作系統 虛擬進程地址空間中的堆是兩回事&…

問題:8255A的端口A工作在方式2時,使用端口C的______作為與CPU和外部設備的聯絡信號。 #媒體#經驗分享#其他

問題:8255A的端口A工作在方式2時,使用端口C的______作為與CPU和外部設備的聯絡信號。 參考答案如圖所示

郵件安全證書,保障通信安全的必備利器

在數字通信日益普及的今天,電子郵件的安全性越來越受到人們的關注。郵件安全證書,作為保障郵件通信安全的重要工具,逐漸走進了大眾的視野。本文將為大家揭秘郵件安全證書,解答關于它的常見問題,幫助大家更好地了解和使…

學習好困-合理調整下

學習時感到困倦是一種常見現象,可能由多種因素引起,如環境、身體狀況、學習方法等。以下是一些原因及其解決方法: 可能的原因 環境因素: - 光線不足:昏暗的環境容易讓人感到疲倦。 - 空氣不流通:缺乏…

浮點數的精度和精度丟失,如何規避,有簡單操作

在日常工作中,如果做財務軟件相關肯定會遇到這種問題,憑證金額表面上看是相等的,但程序運算出的結果卻是FALSE。 例如:驗證憑證金額借方總金額是否等于貸方總金額? 直接sum(借方分錄金額1.1借方分錄金額2.…

RK3568技術筆記之一 RK3568總體介紹

RK3568是瑞芯微開發出一款很好用的芯片。我先把ROCKCHIP的原廠信息搬過來看看。 首先聲明一下,這篇文章里的資訊版權歸瑞芯微電子股份有限公司。畢竟是我轉過來的嘛。 我自己的心得,版權就歸我啦。 主要特性 Quad-core Cortex-A55 up to 2.0GHzMali-G…

認識HTTP狀態碼(status code)

目錄 1、200 OK(訪問成功)👇 2、404 Not Found (沒有找到資源)👇 3、403 Forbidden (訪問拒絕)👇 4、405 Method Not Allowed👇 6、504 Gateway Timeout…

CV Method:經典CNN Backbone總結

文章目錄 前言一、ResNet二、ResNeXt三、Res2Net四、SeNet五、ResNeSt六、DenseNet七、CSPNetPytorch Model Code總結 前言 Backbone作為一切深度學習任務的基礎,不論是理論還是實際應用都有重要的意義,本文針對經典Backbone進行總結,這些Ba…

[word] word怎樣轉換成pdf #職場發展#經驗分享#職場發展

word怎樣轉換成pdf word怎樣轉換成pdf?word格式是辦公中常會用到的格式,word格式編輯好了要想轉換成pdf格式再來傳輸的話需要怎么操作呢?小編這就給大家分享下操作方法,一起來學習下吧! 1、安裝得力PDF轉換器&#x…

【stm32/CubeMX、HAL庫】swjtu嵌入式實驗七 ADC 實驗

相關電路與IO引腳 注意&#xff1a;串口打印重定向后使用printf打印需要在keil里勾選 Use MicroLIB &#xff0c;否則會卡住。 參看&#xff1a;https://zhuanlan.zhihu.com/p/565613666 串口重定向&#xff1a; /* USER CODE BEGIN Includes */#include <stdio.h>//…