pytorch實現kaggle貓狗識別(超詳細)

2021-03-02 機器AI學習 數據AI挖掘
想了解更多好玩的人工智慧應用,請關注公眾號「機器AI學習 數據AI挖掘」,」智能應用"菜單中包括:顏值檢測、植物花卉識別、文字識別、人臉美妝等有趣的智能應用。。

kaggle是一個為開發商和數據科學家提供舉辦機器學習競賽、託管資料庫、編寫和分享代碼的平臺,在這上面有非常多的好項目、好資源可供機器學習、深度學習愛好者學習之用。碰巧最近入門了一門非常的深度學習框架:pytorch(如果你對pytorch不甚了解,請點擊這裡),所以今天我和大家一起用pytorch實現一個圖像識別領域的入門項目:貓狗圖像識別。
深度學習的基礎就是數據,咱們先從數據談起。此次使用的貓狗分類圖像一共25000張,貓狗分別有12500張。下載地址:https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data我們先來簡單的看看都是一些什麼圖片。我們從下載文件裡可以看到有兩個文件夾:train和test1,分別用於訓練和測試。打開train文件夾可以看到有25000 張小貓小狗的圖片,圖片名字為cat.0.jpg,cat.1.jpg,dog.0.jpg,dog.1.jpg。


仔細看小貓小狗,可以發現它們姿態不一,有的站著,有的眯著眼睛,有的甚至和其他可識別物體比如桶、人混在一起。同時,小貓們的圖片尺寸也不一致,有的是豎放的長方形,有的是橫放的長方形,但我們最終需要是合理尺寸的圖片。所以需要進行圖片處理,並把圖片轉化成Tensor作為模型的輸入。代碼如下:

data_transform = transforms.Compose([
transforms.Resize(84),
transforms.CenterCrop(84),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

train_dataset = datasets.ImageFolder(root='./data2/train/',
transform=data_transform)
train_loader = torch.utils.data.DataLoader(train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=num_workers)

為方便訓練過程,需要把訓練集進行處理:把貓狗的圖片分別放在cat,dog文件夾中,並劃分出一部分圖片作為測試集(與下載的測試集不同)。代碼如下:


# kaggle原始數據集地址
original_dataset_dir = 'E:\python ese\c_d\data\\train\\train'
total_num = int(len(os.listdir(original_dataset_dir)) / 2)
random_idx = np.array(range(total_num))
np.random.shuffle(random_idx)

# 待處理的數據集地址
base_dir = 'E:\python ese\c_d\data2'
if not os.path.exists(base_dir):
os.mkdir(base_dir)

# 訓練集、測試集的劃分
sub_dirs = ['train', 'test']
animals = ['cats', 'dogs']
train_idx = random_idx[:int(total_num * 0.9)]
test_idx = random_idx[int(total_num * 0.9):]
numbers = [train_idx, test_idx]
for idx, sub_dir in enumerate(sub_dirs):
dir = os.path.join(base_dir, sub_dir)
if not os.path.exists(dir):
os.mkdir(dir)
for animal in animals:
animal_dir = os.path.join(dir, animal) #
if not os.path.exists(animal_dir):
os.mkdir(animal_dir)
fnames = [animal[:-1] + '.{}.jpg'.format(i) for i in numbers[idx]]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(animal_dir, fname)
shutil.copyfile(src, dst)

# 驗證訓練集、驗證集、測試集的劃分的照片數目
print(animal_dir + ' total images : %d' % (len(os.listdir(animal_dir))))

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

緊接著我們了解一下特別適用於圖像識別領域的神經網絡:卷積神經網絡。學習過神經網絡的同學可能或多或少地聽說過卷積神經網絡。這是一種典型的多層神經網絡,擅長處理圖像特別是大圖像的相關機器學習問題。卷積神經網絡通過一系列的方法,成功地將大數據量的圖像識別問題不斷降維,最終使其能夠被訓練。CNN最早由Yann LeCun提出並應用在手寫體識別上。一個典型的CNN網絡架構如下:

這是一個典型的CNN架構,由卷基層、池化層、全連接層組合而成。其中卷基層與池化層配合,組成多個卷積組,逐層提取特徵,最終完成分類。聽到上述一連串的術語如果你有點蒙了,也別怕,因為這些複雜、抽象的技術都已經在pytorch中一一實現,我們要做的不過是正確的調用相關函數。

# 創建模型
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.maxpool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 18 * 18, 1024)
self.fc2 = nn.Linear(1024, 512)
self.fc3 = nn.Linear(512, 2)

def forward(self, x):
x = self.maxpool(F.relu(self.conv1(x)))
x = self.maxpool(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 18 * 18)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)

return x


net = Net()

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

我們從conv1說起。conv1實際上就是定義一個卷積層,3,6,5分別是什麼意思?3代表的是輸入圖像的像素數組的層數,一般來說就是你輸入的圖像的通道數,比如這裡使用的小貓圖像都是彩色圖像,由R、G、B三個通道組成,所以數值為3;6代表的是我們希望進行6次卷積,每一次卷積都能生成不同的特徵映射數組,用於提取小貓和小狗的6種特徵。每一個特徵映射結果最終都會被堆疊在一起形成一個圖像輸出,再作為下一步的輸入;5就是過濾框架的尺寸,表示我們希望用一個5 * 5的矩陣去和圖像中相同尺寸的矩陣進行點乘再相加,形成一個值。定義好了卷基層,我們接著定義池化層。池化層所做的事說來簡單,其實就是因為大圖片生成的像素矩陣實在太大了,我們需要用一個合理的方法在降維的同時又不失去物體特徵,所以深度學習學者們想出了一個稱為池化的技術,說白了就是從左上角開始,每四個元素(2 * 2)合併成一個元素,用這一個元素去代表四個元素的值,所以圖像體積一下子降為原來的四分之一。再往下一行,我們又一次碰見了一個卷積層:conv2,和conv1一樣,它的輸入也是一個多層像素數組,輸出也是一個多層像素數組,不同的是這一次完成的計算量更大了,我們看這裡面的參數分別是6,16,5。之所以為6是因為conv1的輸出層數為6,所以這裡輸入的層數就是6;16代表conv2的輸出層數,和conv1一樣,16代表著這一次卷積操作將會學習小貓小狗的16種映射特徵,特徵越多理論上能學習的效果就越好,大家可以嘗試一下別的值,看看效果是否真的編變好。conv2使用的過濾框尺寸和conv1一樣,所以不再重複。最後三行代碼都是用於定義全連接網絡的,接觸過神經網絡的應該就不再陌生了,主要是需要解釋一下fc1。之前在學習的時候比較不理解的也是這一行,為什麼是16 * 18 * 18呢?16很好理解,因為最後一次卷積生成的圖像矩陣的高度就是16層,那18 * 18是怎麼來的呢?我們回過頭去看一行代碼

transforms.CenterCrop(84)
在這行代碼裡我們把訓練圖像裁剪成一個84 * 84的正方形尺寸,所以圖像最早輸入就是一個3 * 84 * 84的數組。經過第一次5 * 5的卷積之後,我們可以得出卷積的結果是一個6 * 80 * 80的矩陣,這裡的80就是因為我們使用了一個5 * 5的過濾框,當它從左上角第一個元素開始卷積後,過濾框的中心是從2到78,並不是從0到79,所以結果就是一個80 * 80的圖像了。經過一個池化層之後,圖像尺寸的寬和高都分別縮小到原來的1/2,所以變成40 * 40。緊接著又進行了一次卷積,和上一次一樣,長寬都減掉4,變成36 * 36,然後應用了最後一層的池化,最終尺寸就是18 * 18。所以第一層全連接層的輸入數據的尺寸是16 * 18 * 18。三個全連接層所做的事很類似,就是不斷訓練,最後輸出一個二分類數值。net類的forward函數表示前向計算的整個過程。forward接受一個input,返回一個網絡輸出值,中間的過程就是一個調用init函數中定義的層的過程。F.relu是一個激活函數,把所有的非零值轉化成零值。此次圖像識別的最後關鍵一步就是真正的循環訓練操作。

下面就可以開始訓練了!


# 定義loss和optimizer
cirterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.0001, momentum=0.9)

def train():

for epoch in range(epochs):
running_loss = 0.0
train_correct = 0
train_total = 0
for i, data in enumerate(train_loader, 0):
inputs, train_labels = data
if use_gpu:
inputs, labels = Variable(inputs.cuda()), Variable(train_labels.cuda())
else:
inputs, labels = Variable(inputs), Variable(train_labels)
optimizer.zero_grad()
outputs = net(inputs)
_, train_predicted = torch.max(outputs.data, 1)
train_correct += (train_predicted == labels.data).sum()
loss = cirterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
train_total += train_labels.size(0)

print('train %d epoch loss: %.3f acc: %.3f ' % (
epoch + 1, running_loss / train_total, 100 * train_correct / train_total))
# 模型測試
correct = 0
test_loss = 0.0
test_total = 0
test_total = 0
net.eval()
for data in test_loader:
images, labels = data
if use_gpu:
images, labels = Variable(images.cuda()), Variable(labels.cuda())
else:
images, labels = Variable(images), Variable(labels)
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
loss = cirterion(outputs, labels)
test_loss += loss.item()
test_total += labels.size(0)
correct += (predicted == labels.data).sum()

print('test %d epoch loss: %.3f acc: %.3f ' % (epoch + 1, test_loss / test_total, 100 * correct / test_total))

torch.save(net, 'model.pt')


train()

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

完整代碼:

# coding=utf-8
import os
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
from torch.utils.data import Dataset
from torchvision import transforms, datasets, models

# 隨機種子設置
random_state = 42
np.random.seed(random_state)

# kaggle原始數據集地址
original_dataset_dir = 'E:\python ese\c_d\data\\train\\train'
total_num = int(len(os.listdir(original_dataset_dir)) / 2)
random_idx = np.array(range(total_num))
np.random.shuffle(random_idx)

# 待處理的數據集地址
base_dir = 'E:\python ese\c_d\data2'
if not os.path.exists(base_dir):
os.mkdir(base_dir)

# 訓練集、測試集的劃分
sub_dirs = ['train', 'test']
animals = ['cats', 'dogs']
train_idx = random_idx[:int(total_num * 0.9)]
test_idx = random_idx[int(total_num * 0.9):]
numbers = [train_idx, test_idx]
for idx, sub_dir in enumerate(sub_dirs):
dir = os.path.join(base_dir, sub_dir)
if not os.path.exists(dir):
os.mkdir(dir)
for animal in animals:
animal_dir = os.path.join(dir, animal) #
if not os.path.exists(animal_dir):
os.mkdir(animal_dir)
fnames = [animal[:-1] + '.{}.jpg'.format(i) for i in numbers[idx]]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(animal_dir, fname)
shutil.copyfile(src, dst)

# 驗證訓練集、驗證集、測試集的劃分的照片數目
print(animal_dir + ' total images : %d' % (len(os.listdir(animal_dir))))
# coding=utf-8

# 配置參數
random_state = 1
torch.manual_seed(random_state) # 設置隨機數種子,確保結果可重複
torch.cuda.manual_seed(random_state)
torch.cuda.manual_seed_all(random_state)
np.random.seed(random_state)
# random.seed(random_state)

epochs = 10 # 訓練次數
batch_size = 4 # 批處理大小
num_workers = 0 # 多線程的數目
use_gpu = torch.cuda.is_available()
PATH='E:\python ese\c_d\model.pt'
# 對加載的圖像作歸一化處理, 並裁剪為[224x224x3]大小的圖像
data_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

train_dataset = datasets.ImageFolder(root='./data2/train/',
transform=data_transform)
train_loader = torch.utils.data.DataLoader(train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=num_workers)

test_dataset = datasets.ImageFolder(root='./data2/test/', transform=data_transform)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers)


# 創建模型
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.maxpool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 53 * 53, 1024)
self.fc2 = nn.Linear(1024, 512)
self.fc3 = nn.Linear(512, 2)

def forward(self, x):
x = self.maxpool(F.relu(self.conv1(x)))
x = self.maxpool(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 53 * 53)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)

return x


net = Net()
if(os.path.exists('model.pt')):
net=torch.load('model.pt')

if use_gpu:
net = net.cuda()
print(net)

# 定義loss和optimizer
cirterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.0001, momentum=0.9)

def train():

for epoch in range(epochs):
running_loss = 0.0
train_correct = 0
train_total = 0
for i, data in enumerate(train_loader, 0):
inputs, train_labels = data
if use_gpu:
inputs, labels = Variable(inputs.cuda()), Variable(train_labels.cuda())
else:
inputs, labels = Variable(inputs), Variable(train_labels)
optimizer.zero_grad()
outputs = net(inputs)
_, train_predicted = torch.max(outputs.data, 1)
train_correct += (train_predicted == labels.data).sum()
loss = cirterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
train_total += train_labels.size(0)

print('train %d epoch loss: %.3f acc: %.3f ' % (
epoch + 1, running_loss / train_total, 100 * train_correct / train_total))
# 模型測試
correct = 0
test_loss = 0.0
test_total = 0
test_total = 0
net.eval()
for data in test_loader:
images, labels = data
if use_gpu:
images, labels = Variable(images.cuda()), Variable(labels.cuda())
else:
images, labels = Variable(images), Variable(labels)
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
loss = cirterion(outputs, labels)
test_loss += loss.item()
test_total += labels.size(0)
correct += (predicted == labels.data).sum()

print('test %d epoch loss: %.3f acc: %.3f ' % (epoch + 1, test_loss / test_total, 100 * correct / test_total))

torch.save(net, 'model.pt')


train()

相關焦點

  • Deep CARs:使用Pytorch學習框架實現遷移學習
    本文將介紹一個能識別196種類型汽車的模型。本模型將通過神經網絡來實現目標。更準確地說,是使用一個深度神經網絡,因此得名Deep CARs(深度計算機自動額定值系統)。想要實現這一目標,需要完成兩部分的學習,第1部分:構建汽車分類器;第2部分:部署分類器。本文將著重論述第1部分內容。
  • Github 2.2K星的超全PyTorch資源列表
    該部分項目涉及語音識別、多說話人語音處理、機器翻譯、共指消解、情感分類、詞嵌入/表徵、語音生成、文本語音轉換、視覺問答等任務,其中有一些是具體論文的 PyTorch 復現,此外還包括一些任務更廣泛的庫、工具集、框架。
  • 圖像分類:13個Kaggle項目的經驗總結
    通過大量的隨機轉換生成新的樣本,這些轉換不僅可以生成可信的圖像,而且還反映了真實的場景 —— 稍後將對此進行詳細介紹。這種技術得到了廣泛的應用,不僅僅是在訓練模型的數據樣本太少的情況下。在這種情況下,模型開始記憶訓練集,但無法泛化(在從未見過的數據上表現很差)。通常,當一個模型在訓練數據上表現很好,但在驗證數據上表現很差時,我們稱之為過擬合。
  • 庫、教程、論文實現,這是一份超全的PyTorch資源列表(Github 2.2K星)
    該部分項目涉及語音識別、多說話人語音處理、機器翻譯、共指消解、情感分類、詞嵌入/表徵、語音生成、文本語音轉換、視覺問答等任務,其中有一些是具體論文的 PyTorch 復現,此外還包括一些任務更廣泛的庫、工具集、框架。
  • Kaggle知識點:偽標籤Pseudo Label
    在競賽中如果沒有其他漲分的方法,再建議嘗試偽標籤,否則不建議嘗試;偽標籤適合用在深度學習方法中,且一般選擇預測執行度高的樣本加入訓練;Instant Gratification,kernel賽https://www.kaggle.com
  • 語音識別開源工具PyTorch-Kaldi:兼顧Kaldi效率與PyTorch靈活性
    高性能的語音識別給我們帶來了更多的生活體驗,我們擁有了可以對話的智能數字助手;它也在逐步改善相關領域的生產力水平。和很多偉大技術的應用一樣,語音識別技術的背後也是很多模塊的組合。對其實現流程的改進往往會從一定程度上節省開發成本,並且加快技術迭代的速度。Pytorch-Kaldi 的出現就是基於這樣的動力。
  • 參賽3年,斬獲6金3銀2銅:Kaggle Grandmaster親授實戰經驗
    kaggle profile:https://www.kaggle.com/senkin13他 3 年前開始參加 kaggle 競賽,曾多次取的前十名的成績。截至目前共斬獲 6 金 3 銀 2 銅,kaggle 最高排名全球第 91。以下是本期活動的問答集錦:Q1: 首先來個自我介紹?
  • 用Java實現目標檢測|PyTorch
    這篇來自AWS軟體工程師的投稿,結合實例,詳細介紹了DJL這個為Java開發者設計的深度學習庫:5分鐘,你就能在PyTorch上,用Java實現目標檢測。5分鐘,用Java實現目標檢測文 / 知乎用戶@LankingPyTorch在深度學習領域中的應用日趨廣泛,得益於它獨到的設計。無論是數據的並行處理還是動態計算圖,一切都為Python做出了很多簡化。很多論文都選擇使用PyTorch去實現也證明了它在訓練方面的效率以及易用性。
  • onnx實現對pytorch模型推理加速
    對於硬體供應商來說,也可以簡化神經網絡計算的複雜度,實現優化算法。訓練過程調用gpu,則轉換過程如下:1 如果保存的是整個模型import torchdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")model = torch.load("test.pth") # pytorch
  • NLP-入門實體命名識別(NER)+Bilstm-CRF模型原理Pytorch代碼詳解——最全攻略
    希望能夠以這篇文章為載體,幫助其他跟我一樣的學習者梳理、串起NER的各個小知識點,最後上手NER的主流模型(Bilstm+CRF)(文中講的是pytorch,但是懂了pytorch去看keras十分容易相信我哈)二、主流模型Bilstm-CRF實現詳解(Pytorch篇)一、NER資料參考:NLP之CRF應用篇(序列標註任務)(CRF++的詳細解析、Bi-LSTM+CRF
  • 這可能是關於Pytorch底層算子擴展最詳細的總結了!
    只需要定義新算子的kernel實現,然後添加配置信息,就可以自動生成:torch.xxx()、torch.nn.functional.xxx()以及tensor.xxx()方法,而不用去關注算子與pytorch是如何銜接,以及如何把算子添加到tensor的屬性中等其他細節。
  • 9 大主題卷積神經網絡(CNN)的 PyTorch 實現
    本文將介紹它們的 PyTorch 實現,非常有用!這份資源已經開源在了 GitHub 上,連結如下:https://github.com/shanglianlm0525/PyTorch-Networks先來個總結介紹,該系列的卷積神經網絡實現包含了 9 大主題,目錄如下:1.
  • 【NER】NLP-入門實體命名識別(NER)+Bilstm-CRF模型原理Pytorch代碼詳解——最全攻略
    希望能夠以這篇文章為載體,幫助其他跟我一樣的學習者梳理、串起NER的各個小知識點,最後上手NER的主流模型(Bilstm+CRF)(文中講的是pytorch,但是懂了pytorch去看keras十分容易相信我哈)全文結構:一、NER資料(主要介紹NER)二、主流模型Bilstm-CRF實現詳解(Pytorch篇)三、實現代碼的拓展(在第二點的基礎上進行拓展
  • 【乾貨】史上最全的PyTorch學習資源匯總
    · PyTorch中文官方文檔(https://pytorch-cn.readthedocs.io/zh/latest/):閱讀上述英文文檔比較困難的同學也不要緊,我們為大家準備了比較官方的PyTorch中文文檔,文檔非常詳細的介紹了各個函數,可作為一份PyTorch的速查寶典。
  • 競賽實戰|kaggle圖像分類Doodle Recognition Challenge總結
    本文作者從數據處理、梯度累積trick、多gpu訓練,自己遇到的一些坑等都做了總結,並且結束後還對一些比較優秀的top方案作了詳細的對比分析,感覺作者總結的很詳細,讀後收穫很大,作者代碼見底部閱讀原文。」   1. 賽題簡述還記得前段時間很火的微信小程序「猜畫小歌」嗎,這個的數據就來自這個小程序的網頁版Quick, Draw!
  • 半年5戰5金:Kaggle史上最快GrandMaster是如何煉成的
    Kaggle profile:https://www.kaggle.com/shentaoSueTao 擅長計算機視覺(Computer Vision),半年 5 戰 5 金,也許是史上最快的 GrandMaster。截至目前共斬獲 9 金 3 銀,kaggle 最高排名全球第 10。
  • 超算安裝GPU-based軟體 (以pytorch為例)
    一般的超算的拓撲結構是若干個登陸節點+若干個交換機+大量計算CPU節點+大量GPU計算節點+一個(或若干個)存儲節點+管理節點。其中存儲節點的共享存儲可以被所有節點訪問。一般運作方式是,如果我的計算依賴非常共性的軟體,我可以找超算管理員安裝,使用的時候可以通過module load來初始化自己所需軟體。
  • 模型堆疊(Stacking)和模型融合的原理與實現以及一個庫heamy的介紹
    看下面的連結https://github.com/emanuele/kaggle_pbr/blob/master/blend.py說是blend,貌似是stakcing,whatever,能用就行,自己心裡明白。好生mark下,有大用處。下面介紹一個比較不錯的庫heamy。
  • 新手必備 | 史上最全的PyTorch學習資源匯總
    該文檔詳細的介紹了從基礎知識到如何使用PyTorch構建深層神經網絡,以及PyTorch語法和一些高質量的案例。(2)PyTorch中文官方文檔:https://pytorch-cn.readthedocs.io/zh/latest/。