貓狗識別
- 訓練模型
- 導入需要的包
- 數據加載
- 數據預處理
- 加載數據集并返回對應的圖像和標簽
- 提取標簽信息
- 創建訓練和測試的數據加載器
- 圖像分類
- 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()