PS:要轉載請注明出處,本人版權所有。

PS: 這個只是基於《我自己》的理解,

如果和你的原則及想法相沖突,請諒解,勿噴。

前置說明

本文作為本人csdn blog的主站的備份。(BlogID=110)

環境說明
  • Windows 10
  • VSCode
  • Python 3.8.10
  • Pytorch 1.8.1
  • Cuda 10.2

前言


本文是此基礎補全計劃的最終篇,因為從我的角度來說,如果前面這些基礎知識都能够了解及理解,再加上本文的這篇基礎知識,那麼我們算是小半只脚踏入了大門。從這個時候,其實我們就已經可以做圖像上的基本的分類任務了。除了分類任務,我們還有兩類重要的圖像任務是目標檢測和圖像分割,這兩項任務都和分類任務有一定的關聯,可以說,分類可以說是這兩類的基礎。

卷積神經網絡是一個專門為處理圖像數據的網絡。下面我們簡單的來看看卷積、池化的含義和怎麼計算的,然後我們通過一個LeNet5的經典網絡,訓練一個分類模型。

卷積


卷積是一種運算,類似加减乘除。卷積是一種運算,類似加减乘除。卷積是一種運算,類似加减乘除。重要的事情說三次。

在數學上的定義是:連續n的情况\((f*g)(x) = \int f(n)g(x-n)dn\), 離散n的情况\((f*g)(x) = \sum\limits_{n} f(n)g(x-n)\)。從這裏我們可以看到,卷積就是測量函數f和函數g的翻轉且平移x後的重疊。其二維離散a,b的錶達是\((f*g)(x1,x2) = \sum\limits_{a}\sum\limits_{b} f(a, b)g(x1-a, x2-b)\)

卷積是一種運算,類似加减乘除。卷積是一種運算,類似加减乘除。卷積是一種運算,類似加减乘除。重要的事情再說三次。

我們再次想一想,在之前的文章中,我們普遍都建立了一種想法是,把輸入數據拉成一條直線輸入的,這就意味著我們在之前的任務裏面只建立了相鄰輸入數據之間的左右關聯。但是我們可以想一想,是不是所有的數據只建立左右關聯就行了呢?顯而易見的,並不是這樣的,比如我們圖片,可能上下左右4個像素加起來才是一個猫,如果我們只關聯了左右,那麼它可能是狗或者猫。那麼我們應該通過什麼樣的方式來對圖片像素的這種二維關聯性進行描述或者計算呢?這種方法就是卷積運算。

卷積網上有許許多多的介紹,大家都做了許多詳細的解答,包含信號分析、複利、概率以及圖像濾波等等方面的解釋。我個人認為我們可以拋開這些方面,從數據之間的關聯性來看這個問題可能是最好理解的,因為我們之前只關注了數據之間左右關聯,我們應該同時關注上下左右的關聯才對,我們要從空間的角度來考慮數據之間的關聯性。而卷積作為一種數學運算,他恰好是計算了數據的上下左右關聯性,因此卷積這種數學運算很適合拿來代替之前的一條線的線性運算。

下面我們來看一下一個基本的卷積計算過程是什麼樣子的。

圖像邊緣檢測實例

計算代碼如下:

def corr2d(X, K): #@save
"""計算⼆維互相關運算。"""
h, w = K.shape
Y = np.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)) for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y[i, j] = (X[i:i + h, j:j + w] * K).sum() return Y _X = np.ones((6, 8))
_X[0:2, 2:6] = 0
_X[3:, 2:6] = 0
print(_X)
_K = np.array([[1.0, -1.0]])
_Y = corr2d(_X, _K)
print(_Y)
_Y = corr2d(_X, _K.T)
print(_Y)

結果如圖:

我們可以分別的看到,圖像邊緣的數值在經過我們手動構造的濾波器後,成功的檢測到邊緣信息。

在實際情况中,我們可能要學習邊緣,角點等等特征,這個時候我們不可能手動去構造我們的濾波器,那麼我們可不可以通過學習的方式把濾波器學習出來呢?下面通過實例來演示:

_X = np.ones((6, 8))
_X[0:2, 2:6] = 0
_X[3:, 2:6] = 0
print(_X)
_K = np.array([[1.0, -1.0]])
_Y = corr2d(_X, _K)
print(_Y)
# _Y = corr2d(_X, _K.T)
# print(_Y) X = torch.from_numpy(_X)
X.requires_grad = True
X = X.to(dtype=torch.float32)
X = X.reshape(1, 1, 6, 8) Y = torch.from_numpy(_Y)
Y.requires_grad = True conv2d = torch.nn.Conv2d(1, 1, (1, 2), bias=False) for i in range(20):
y_train = conv2d(X)
l = (y_train - Y)**2 conv2d.zero_grad() # print(l.shape)
l.backward(torch.ones_like(l)) # print(conv2d.weight)
with torch.no_grad():
# print('grad = ', conv2d.weight.grad)
conv2d.weight[:] -= 0.02 * conv2d.weight.grad
# print(conv2d.weight)
# print(conv2d.weight.shape)
if (i + 1) % 2 == 0:
print(f'batch {i+1}, loss {float(l.sum()):.3f}') print(conv2d.weight)

結果如圖:

我們通過corr2d函數構造出特征Y,然後我們通過訓練特征Y,我們可以看到最終卷積層的權重就是接近與1和-1,恰好等於我們構造的特殊濾波器。

這個實例說明了,我們可以通過學習的方式來學習出一個我們想要的濾波器,不需要手動構造。

此外卷積還有卷積核、步長、填充等等資料,我就不造輪子了,網上有很多大佬寫的很好的,大家去看看。此外這裏有個公式非常有用:N=(W-K+2P)/S+1。

池化


我們在上文知道了卷積的輸出結果代錶了一片上下左右數據的關聯性,比如一個像素和之前的9個像素有關聯,比如一個\(9*9\)的圖,經過一個卷積後,假如還是\(9*9\),這個時候輸出的\(9*9\)裏面的每個像素我們已經和之前對應比特置的一片像素建立了關聯。但是某些時候,我們希望這種關聯性聚合起來,通過求最大值或者平均等等,這就是池化的概念。以之前例子為例:卷積輸出了\(9*9\)的像素,經過池化之後,假如變成了\(3*3\),我們可以看到池化輸出的每個像素代錶之前卷積輸出的\(3*3\)個像素,這代錶我們的信息聚集了,因為一個像素代錶了上一層的多個像素。

注意池化,我們還可以從視野的角度來看待,還是和上面的例子一樣,假如原圖上的猫是\(9*9\)的像素,經過卷積池化之後,假如變成了\(3*3\), 這意味著我們從像素的角度來說,之前81個像素代錶猫,現在9個像素就可以代錶了,也就是之前的一個像素和現在的一個像素代錶的原圖視野不一樣了,形成了視野放大的感覺。但是有一個缺點就是,這可能導致小目標丟失了,這個在目標檢測裏面會關注到。

一個經典神經網絡LeNet5


在2017年12月份,我的這篇文章中《LeNet-5 論文及原理分析(笨鳥角度)》 ( https://blog.csdn.net/u011728480/article/details/78799672 )其實當時我為了學習一些基本知識,也對LeNet5的論文中網絡結構部分做了細致的分析。

注意本文中的C3層和論文中的C3層不一樣。本文的C3層是\(16*6*(5*5+1) = 2496\)個參數。論文原文是\(6*(3*5*5+1)+6*(4*5*5+1)+3*(4*5*5+1)+1* (6*5*5+1)=1516\)個參數。

訓練代碼如下:

import numpy as np
from numpy.lib.utils import lookfor
import torch from torchvision.transforms import ToTensor
import os
import torch
from torch import nn
from torch.nn.modules import activation
from torch.nn.modules import linear
from torch.nn.modules.linear import Linear
from torch.utils.data import DataLoader
from torchvision import datasets, transforms import visdom vis = visdom.Visdom(env='main') title = 'LeNet5 on ' + 'FashionMNIST'
legend = ['TrainLoss', 'TestLoss', 'TestAcc'] epoch_plot_window = vis.line(
X=torch.zeros((1, 3)).cpu(),
Y=torch.zeros((1, 3)).cpu(),
win='epoch_win',
opts=dict(
xlabel='Epoch',
ylabel='Loss/Acc',
title=title,
legend=legend
)) def corr2d(X, W): #@save
"""計算⼆維互相關運算。"""
h, w = W.shape
Y = np.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)) for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y[i, j] = (X[i:i + h, j:j + w] * W).sum() return Y def TrainConv2d():
_X = np.ones((6, 8))
_X[0:2, 2:6] = 0
_X[3:, 2:6] = 0
print(_X)
_K = np.array([[1.0, -1.0]])
_Y = corr2d(_X, _K)
print(_Y)
# _Y = corr2d(_X, _K.T)
# print(_Y) X = torch.from_numpy(_X)
X.requires_grad = True
X = X.to(dtype=torch.float32)
X = X.reshape(1, 1, 6, 8) Y = torch.from_numpy(_Y)
Y.requires_grad = True conv2d = torch.nn.Conv2d(1, 1, (1, 2), bias=False) for i in range(20):
y_train = conv2d(X)
l = (y_train - Y)**2 conv2d.zero_grad() # print(l.shape)
l.backward(torch.ones_like(l)) # print(conv2d.weight)
with torch.no_grad():
# print('grad = ', conv2d.weight.grad)
conv2d.weight[:] -= 0.02 * conv2d.weight.grad
# print(conv2d.weight)
# print(conv2d.weight.shape)
if (i + 1) % 2 == 0:
print(f'batch {i+1}, loss {float(l.sum()):.3f}') print(conv2d.weight) class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.lenet5 = nn.Sequential(
# 6*28*28---->6*28*28
nn.Conv2d(1, 6, (5, 5), stride=1, padding=2),
nn.Sigmoid(), # 6*28*28----->6*14*14
nn.AvgPool2d((2, 2), stride=2, padding=0), # 6*14*14----->16*10*10
nn.Conv2d(6, 16, (5, 5), stride=1),
nn.Sigmoid(), # 16*10*10------>16*5*5
nn.AvgPool2d((2, 2), stride=2, padding=0), nn.Flatten(), nn.Linear(16*5*5, 1*120),
nn.Sigmoid(), nn.Linear(1*120, 1*84),
nn.Sigmoid(), nn.Linear(1*84, 1*10)
) def forward(self, x):
logits = self.lenet5(x)
return logits def LoadFashionMNISTByTorchApi():
# 60000*28*28
training_data = datasets.FashionMNIST(
root="..\data",
train=True,
download=True,
transform=ToTensor()
) # 10000*28*28
test_data = datasets.FashionMNIST(
root="..\data",
train=False,
download=True,
transform=ToTensor()
) # labels_map = {
# 0: "T-Shirt",
# 1: "Trouser",
# 2: "Pullover",
# 3: "Dress",
# 4: "Coat",
# 5: "Sandal",
# 6: "Shirt",
# 7: "Sneaker",
# 8: "Bag",
# 9: "Ankle Boot",
# }
# figure = plt.figure(figsize=(8, 8))
# cols, rows = 3, 3
# for i in range(1, cols * rows + 1):
# sample_idx = torch.randint(len(training_data), size=(1,)).item()
# img, label = training_data[sample_idx]
# figure.add_subplot(rows, cols, i)
# plt.title(labels_map[label])
# plt.axis("off")
# plt.imshow(img.squeeze(), cmap="gray")
# plt.show()
return training_data, test_data def train_loop(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
num_batches = len(dataloader)
loss_sum = 0
for batch, (X, y) in enumerate(dataloader):
# move X, y to gpu
if torch.cuda.is_available():
X = X.to('cuda')
y = y.to('cuda')
# Compute prediction and loss
pred = model(X)
loss = loss_fn(pred, y) # Backpropagation
optimizer.zero_grad()
loss.backward()
optimizer.step() loss_sum += loss.item()
if batch % 100 == 0:
loss1, current = loss.item(), batch * len(X)
print(f"loss: {loss1:>7f} [{current:>5d}/{size:>5d}]") return loss_sum/num_batches def test_loop(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
test_loss, correct = 0, 0 with torch.no_grad():
for X, y in dataloader:
# move X, y to gpu
if torch.cuda.is_available():
X = X.to('cuda')
y = y.to('cuda')
pred = model(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).type(torch.float).sum().item() test_loss /= num_batches
correct /= size
print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n") return test_loss, correct if __name__ == '__main__':
# TrainConv2d() device = 'cuda' if torch.cuda.is_available() else 'cpu'
print('Using {} device'.format(device)) def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight) model = NeuralNetwork()
model.apply(init_weights)
model = model.to(device)
print(model) batch_size = 200
learning_rate = 0.9 training_data, test_data = LoadFashionMNISTByTorchApi()
train_dataloader = DataLoader(training_data, batch_size, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size, shuffle=True)
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) epochs = 1000 model.train()
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
train_loss = train_loop(train_dataloader, model, loss_fn, optimizer)
test_loss, test_acc = test_loop(test_dataloader, model, loss_fn) vis.line(np.array([train_loss, test_loss, test_acc]).reshape(1, 3),
np.ones((1, 3))*t,
win='epoch_win',
update=None if t == 0 else 'append',
opts=dict(
xlabel='Epoch',
ylabel='Loss/Acc',
title=title,
legend=legend
)
)
print("Done!") # only save param
torch.save(model.state_dict(), 'lenet5.pth') # save param and net
torch.save(model, 'lenet5-all.pth') # export onnx
input_image = torch.zeros((1,1,28,28))
input_image = input_image.to(device)
torch.onnx.export(model, input_image, 'model.onnx')

結果如圖:

我們從訓練可視化界面上可以看到,我們的模型確實是收斂了,但是不幸的是准確率大概有90%左右,而且存在過擬合現象。注意這裏我們這個模型,由於有Sigmoid層,導致了很容易出現梯度消失的情况,為了加快訓練,所以學習率設置的很大。

後記


整理本系列的基礎知識的原因是需要加深對深度學習的理解。同時跟著參考資料,重複試驗,重複運行。對我個人而言,只有真實的寫了代碼之後,才能够理解的更加透徹。

本文也是此系列的終篇,以後更新隨緣。

參考文獻


打賞、訂閱、收藏、丟香蕉、硬幣,請關注公眾號(攻城獅的搬磚之路)

PS: 請尊重原創,不喜勿噴。

PS: 要轉載請注明出處,本人版權所有。

PS: 有問題請留言,看到後我會第一時間回複。

DL基礎補全計劃(六)---卷積和池化的更多相關文章

  1. DL基礎補全計劃(一)---線性回歸及示例(Pytorch,平方損失)

    PS:要轉載請注明出處,本人版權所有. PS: 這個只是基於<我自己>的理解, 如果和你的原則及想法相沖突,請諒解,勿噴. 前置說明 本文作為本人csdn blog的主站的備份.(Bl ...

  2. OSPF補全計劃-0 preface

    哇靠,一看日曆嚇了我一跳,我這一個月都沒寫任何東西,好吧,事情的確多了點兒,同事離職,我需要處理很多untechnical的東西,弄得我很煩,中間學的一點小東西(關於Linux的)也沒往這裏記,但是我 ...

  3. CNN中卷積層 池化層反向傳播

    參考:https://blog.csdn.net/kyang624823/article/details/78633897 卷積層 池化層反向傳播: 1,CNN的前向傳播 a)對於卷積層,卷積核與輸入 ...

  4. tensorflow的卷積和池化層(二):記實踐之cifar10

    在tensorflow中的卷積和池化層(一)和各種卷積類型Convolution這兩篇博客中,主要講解了卷積神經網絡的核心層,同時也結合當下流行的Caffe和tf框架做了介紹,本篇博客將接著tenso ...

  5. 卷積和池化的區別、圖像的上采樣(upsampling)與下采樣(subsampled)

    1.卷積 當從一個大尺寸圖像中隨機選取一小塊,比如說 8x8 作為樣本,並且從這個小塊樣本中學習到了一些特征,這時我們可以把從這個 8x8 樣本中學習到的特征作為探測器,應用到這個圖像的任意地方中去. ...

  6. 【DeepLearning】基本概念:卷積、池化、Backpropagation

    終於有了2個月的空閑時間,給自己消化沉澱,希望別有太多的雜事打擾.在很多課程中,我都學過卷積.池化.dropout等基本內容,但目前在腦海中還都是零散的概念,缺乏整體性框架,本系列博客就希望進行一定的 ...

  7. tensorflow中的卷積和池化層(一)

    在官方tutorial的幫助下,我們已經使用了最簡單的CNN用於Mnist的問題,而其實在這個過程中,主要的問題在於如何設置CNN網絡,這和Caffe等框架的原理是一樣的,但是tf的設置似乎更加簡潔. ...

  8. UFLDL教程筆記及練習答案五(自編碼線性解碼器與處理大型圖像**卷積與池化)

    自己主動編碼線性解碼器 自己主動編碼線性解碼器主要是考慮到稀疏自己主動編碼器最後一層輸出假設用sigmoid函數.因為稀疏自己主動編碼器學習是的輸出等於輸入.simoid函數的值域在[0,1]之間,這 ...

  9. 轉載:cnn學習之卷積或者池化後輸出的map的size計算

    相信各比特在學習cnn的時候,常常對於卷積或者池化後所得map的的大小具體是多少,不知道怎麼算.尤其涉及到邊界的時候.   首先需要了解對於一個輸入的input_height*input_widtht的 ...

  10. Deep Learning 學習隨記(七)Convolution and Pooling --卷積和池化

    圖像大小與參數個數: 前面幾章都是針對小圖像塊處理的,這一章則是針對大圖像進行處理的.兩者在這的區別還是很明顯的,小圖像(如8*8,MINIST的28*28)可以采用全連接的方式(即輸入層和隱含層直接 ...

隨機推薦

  1. Java 驗證碼、二維碼

    Java 驗證碼.二維碼 資源 需要:   jelly-core-1.7.0.GA.jar網站:   http://lychie.github.io/products.html將下載下來的 jelly ...

  2. 內存溢出OOM與內存泄漏ML

    附, 微信團隊原創分享:Android內存泄漏監控和優化技巧總結 一.如何避免OOM 异常 想要避免OOM 异常首先我們要知道什麼情况下會導致OOM 异常. 1.圖片過大導致OOM Android 中 ...

  3. 【轉】HBase原理和設計

    簡介 HBase —— Hadoop Database的簡稱,Google BigTable的另一種開源實現方式,從問世之初,就為了解决用大量廉價的機器高速存取海量數據.實現數據分布式存儲提供可靠的方 ...

  4. Objective-C 高性能的循環遍曆 forin - NSEnumerator - 枚舉 優化

    Cocoa編程的一個通常的任務是要去循環遍曆一個對象的集合  (例如,一個 NSArray, NSSet 或者是 NSDictionary). 這個看似簡單的問題有廣泛數量的解决方案,它們中的許多不乏 ...

  5. Android開發筆記:SQLite導入導出數據

    SQLite是Android中最方便使用的數據庫了,現在看下如何快速的在SQLite中導入導出數據. 首先由於是.NET項目轉Android,原有數據庫使用的是SQLSERVER,由於項目相同部分結構 ...

  6. Marriage is Stable

    Marriage is Stable Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...

  7. 團隊作業7——第二次項目沖刺(Beta版本計劃及安排)

     Beta版本沖刺       需要改進完善的功能 1.尋找BUG.並解决問題 2.界面的優化 下一階段新增的功能' 1.個人信息頭像上傳 2.頭像裁剪功能 需要改進的團隊分工 1.之前產品的主要工作 ...

  8. 幾張圖幫你理解 docker 基本原理及快速入門

    寫的非常好的一篇文章,不知道為什麼被删除了.  利用Google快照,做個存檔. 快照地址:地址 作者地址:青牛 什麼是docker Docker 是一個開源項目,誕生於 2013 年初,最初是 do ...

  9. Hibernate使用sql語句實現多錶關聯查詢

    /** * <查找list> * * @return 返回頁面需要顯示的數據 */ @SuppressWarnings("unchecked") public List ...

  10. numpy數據集練習

    #1. 安裝scipy,numpy,sklearn包 import numpy as np #2. 從sklearn包自帶的數據集中讀出鳶尾花數據集data from sklearn.datasets ...