第三次作业:卷积神经网络基础
視頻學(xué)習(xí)
邱琦
講述了卷積神經(jīng)網(wǎng)絡(luò)的應(yīng)用:分類,檢索,檢測,分割,人臉識別,表情識別,圖像生成,自動駕駛。卷積神經(jīng)網(wǎng)絡(luò)相對傳統(tǒng)神經(jīng)網(wǎng)絡(luò)的優(yōu)點:局部關(guān)聯(lián),參數(shù)共享。卷積神經(jīng)網(wǎng)絡(luò)存在卷積層,RELU激活層,池化層,全連接層。其中卷積是對兩個實變函數(shù)的一種數(shù)學(xué)操作,將圖片進(jìn)行參數(shù)化。池化層保留了主要特征的同時減少參數(shù)和計算量,防止過擬合,提高模型泛化能力。全連接層兩層之間所有神經(jīng)元都有權(quán)重鏈接,通常在卷積神經(jīng)網(wǎng)絡(luò)尾部,參數(shù)量通常最大。卷積神經(jīng)網(wǎng)絡(luò)典型結(jié)構(gòu)包括AlexNet,ZFNet,VGG,GoogleNet,ResNet。這次視頻最后舉的例子,能夠幫助較好的理解,再加上自己的實驗,能夠?qū)φn程有更好的理解。
陳江棟
對于計算機(jī)視覺來說,每?個圖像是由?個個像素點構(gòu)成,每個像素點有三個通道,分別
代表RGB三種顏?(不計算透明度),我們以?寫識別的數(shù)據(jù)集MNIST舉例,每個圖像的是 ?個?寬均為28,channel(通道數(shù))為1的單?圖像,如果使?全連接的?絡(luò)結(jié)構(gòu), 即,?絡(luò)中的神經(jīng)與相鄰層上的每個神經(jīng)元均連接,那就意味著我們的?絡(luò)有
28×28=784個神經(jīng)元(RGB3?的話還要乘3),hidden層如果使?了15個神經(jīng)元,需要 的參數(shù)個數(shù)(w和b)就有:28×28×15×10+15+10=117625個,這個數(shù)量級到現(xiàn)在為?也是 ?個很恐怖的數(shù)量級,?次反向傳播計算量都是巨?的,這還只是?個單?的28像素? ?的圖?,如果我們使?更?的像素,計算量可想?知。 上?說到傳統(tǒng)的?絡(luò)需要?量的參數(shù),但是這些參數(shù)是否重復(fù)了呢,例如,我們識別?個
?,只要看到他的眼睛,??,嘴,還有臉基本上就知道這個?是誰了,只是?這些局部 的特征就能做做判斷了,并不需要所有的特征。 另外?點就是我們上?說的可以有效提 取了輸?圖像的平移不變特征,就好像我們看到了這是個眼睛,這個眼睛在左邊還是在右 邊他都是眼睛,這就是平移不變性。并且我們也?般認(rèn)為兩個靠近的物品他倆都有相似的 屬性。這也是圖像本?的特性,這?特性在把NLP中transformer模型遷移到CV領(lǐng)域?產(chǎn) ?的swin-transformer這?篇論?中起到了很?的影響?。 我們通過卷積的計算操作來提 取圖像局部的特征,每?層都會計算出?些局部特征,這些局部特征再匯總到下?層,這 樣?層?層的傳遞下去,特征由初級變?yōu)?級,最后在通過這些局部的特征對圖?進(jìn)?處 理,這樣??提?了計算效率,也提?了準(zhǔn)確度。
李智杰
通過視頻,學(xué)習(xí)到了許多關(guān)于卷積神經(jīng)網(wǎng)絡(luò)的知識,學(xué)習(xí)到了卷積網(wǎng)絡(luò)的基本結(jié)構(gòu),卷積、池化和一些經(jīng)典的網(wǎng)絡(luò)與結(jié)構(gòu),AlexNet、VGG16、VGG19,GoogleNet的Inception和ResNet,通過后面的代碼練習(xí)也了解到了相比傳統(tǒng)的神經(jīng)網(wǎng)絡(luò),卷積網(wǎng)絡(luò)具有巨大的優(yōu)勢。
宋子昂
卷積神經(jīng)網(wǎng)絡(luò)的應(yīng)用非常廣泛,包括分類、檢索、檢測、分割、人臉識別、圖像生成等,這些應(yīng)用在我們現(xiàn)在看來都非常普遍;由此可以看出卷積神經(jīng)網(wǎng)絡(luò)的重要性。而與傳統(tǒng)神經(jīng)網(wǎng)絡(luò)相比,卷積神經(jīng)網(wǎng)絡(luò)主要突出在局部關(guān)聯(lián)、參數(shù)共享,它不像傳統(tǒng)的網(wǎng)絡(luò)那樣需要大量的參數(shù)。這樣可以在訓(xùn)練的時候大大節(jié)省時間。CNN在圖像處理問題上優(yōu)勢明顯,它是一個可以自動提取特征,而且待訓(xùn)練參數(shù)相對不是很多的神經(jīng)網(wǎng)絡(luò)。卷積神經(jīng)網(wǎng)絡(luò)是一個由卷積層、激活層、池化層、全連接層聚合而成的網(wǎng)絡(luò)結(jié)構(gòu)。卷積運(yùn)用了局部連接的思想,它對圖片的處理方式是一塊一塊的,并不是所有像素值一起處理,因此可以極大的降低參數(shù)值的總量。通過視頻的學(xué)習(xí),我覺得發(fā)現(xiàn)卷積這一個點的人也是相當(dāng)厲害了,通過矩陣相乘來實現(xiàn)了提取特征這一過程。但同時池化層的思想,我認(rèn)為在網(wǎng)絡(luò)中的使用有待商榷,我認(rèn)為應(yīng)該分情況來決定是否使用池化層,這可以說是一種丟失了細(xì)節(jié)來提升模型不變性的方法,是存在一定弊端的,以后的研究可以提高池化層的準(zhǔn)確率,避免在訓(xùn)練時丟失掉重要的細(xì)節(jié)。而全連接層實際上就是一個分類器!在CNN有不少優(yōu)勢的同時,我們也需正視其局限性:沒有考慮圖像中特征的依賴關(guān)系,且對特征的全局位置不敏感等在以后的研究中,希望可以有這些問題的解決。
韋境
通過本次對神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí),我深刻了解到了這一學(xué)科的嚴(yán)謹(jǐn)性和思維發(fā)散性。最令我感觸的時googlenet模型中,對1×1卷積的應(yīng)用。當(dāng)某個卷積層輸入的特征數(shù)較多,對這個輸入進(jìn)行卷積運(yùn)算將產(chǎn)生巨大的計算量;如果對輸入先進(jìn)行降維到1×1,減少特征數(shù)后再做卷積計算量就會顯著減少。它更高效的利用計算資源,在相同的計算量下能提取到更多的特征。
陳曉政
卷積神經(jīng)網(wǎng)絡(luò)是一種深度學(xué)習(xí)模型或類似于人工神經(jīng)網(wǎng)絡(luò)的多層感知器,卷積神經(jīng)網(wǎng)絡(luò)依舊是層級網(wǎng)絡(luò),只是層的功能和形式做了變化,是傳統(tǒng)神經(jīng)網(wǎng)絡(luò)的一個改進(jìn)。卷積神經(jīng)網(wǎng)絡(luò)是仿照生物的視知覺來構(gòu)建的,可以進(jìn)行監(jiān)督學(xué)習(xí)和非監(jiān)督學(xué)習(xí)。
卷積神經(jīng)網(wǎng)絡(luò)主要由 5 層組成:數(shù)據(jù)輸入層,卷積計算層,ReLU 激勵層 ,池化層 ,全連接層 。
優(yōu)點:共享卷積核,對高維數(shù)據(jù)處理無壓力,無需手動選取特征,訓(xùn)練好權(quán)重,即得特征分類效果好。
缺點:需要調(diào)參,需要大樣本量,訓(xùn)練最好要 GPU,物理含義不明確,也就是說,我們并不知道沒個卷積層到底提取到的是什么特征,而且神經(jīng)網(wǎng)絡(luò)本身就是一種難以解釋的“黑箱模型”。
代碼練習(xí)
MNIST 數(shù)據(jù)集分類
構(gòu)建簡單的CNN對 mnist 數(shù)據(jù)集進(jìn)行分類。同時,還會在實驗中學(xué)習(xí)池化與卷積操作的基本作用。
1. 引入庫文件
mport torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import numpy# 一個函數(shù),用來計算模型中有多少參數(shù)
def get_n_params(model):np=0for p in list(model.parameters()):np += p.nelement()return np# 使用GPU訓(xùn)練,可以在菜單 "代碼執(zhí)行工具" -> "更改運(yùn)行時類型" 里進(jìn)行設(shè)置
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
2. 加載數(shù)據(jù)
PyTorch里包含了 MNIST, CIFAR10 等常用數(shù)據(jù)集,調(diào)用 torchvision.datasets 即可把這些數(shù)據(jù)由遠(yuǎn)程下載到本地,下面給出MNIST的使用方法:
torchvision.datasets.MNIST(root, train=True, transform=None, target_transform=None, download=False)
- root 為數(shù)據(jù)集下載到本地后的根目錄,包括 training.pt 和 test.pt 文件
- train,如果設(shè)置為True,從training.pt創(chuàng)建數(shù)據(jù)集就是為訓(xùn)練集,否則從test.pt創(chuàng)建即測試集。
- download,如果設(shè)置為True, 從互聯(lián)網(wǎng)下載數(shù)據(jù)并放到root文件夾下
- transform, 一種函數(shù)或變換,輸入PIL圖片,返回變換之后的數(shù)據(jù)。
- target_transform 一種函數(shù)或變換,輸入目標(biāo),進(jìn)行變換。
配合DataLoader進(jìn)行使用
loader_data=DataLoader(test_set,batch_size=64,shuffle=True,drop_last=True)
- 首先傳入一個dataset數(shù)據(jù)類型的數(shù)據(jù)
- batch_size:可以理解為一副牌,每個人手里拿幾張牌
- shuffle:是否每次隨機(jī)抓取,就是是否重新洗牌
- drop_last:總共的dataset的數(shù)量除以batch_size的余數(shù)是否保留
- num_workers:加載數(shù)據(jù)的時候使用幾個子進(jìn)程
input_size = 28*28 # MNIST上的圖像尺寸是 28x28
output_size = 10 # 類別為 0 到 9 的數(shù)字,因此為十類train_loader = torch.utils.data.DataLoader(datasets.MNIST('./data', train=True, download=True,transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,), (0.3081,))])),batch_size=64, shuffle=True)test_loader = torch.utils.data.DataLoader(datasets.MNIST('./data', train=False, transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,), (0.3081,))])),batch_size=1000, shuffle=True)
2.1 查看數(shù)據(jù)
除了使用老師提供的plt,還可以使用tensorboard展示
#顯示數(shù)據(jù)集中的部分圖像
#除了老師使用的plt還可以使用SummaryWriter
from torch.utils.tensorboard import SummaryWriter
writer=SummaryWriter('logs')#日志文件的存放地址
step=0
for data in train_loader:img,target=datawriter.add_images('train_loader',img,step)step=step+1
%load_ext tensorboard
%tensorboard --logdir logs
writer.close()
這里的圖像呈現(xiàn)灰色,是因為前面做了標(biāo)準(zhǔn)化處理transforms.Normalize
3. 創(chuàng)建網(wǎng)絡(luò)
定義網(wǎng)絡(luò)時,需要繼承nn.Module,并實現(xiàn)它的forward方法,把網(wǎng)絡(luò)中具有可學(xué)習(xí)參數(shù)的層放在構(gòu)造函數(shù)init中。
注:這兩個方法必須重寫!
只要在nn.Module的子類中定義了forward函數(shù),backward函數(shù)就會自動被實現(xiàn)(利用autograd)。
class FC2Layer(nn.Module):def __init__(self, input_size, n_hidden, output_size):# nn.Module子類的函數(shù)必須在構(gòu)造函數(shù)中執(zhí)行父類的構(gòu)造函數(shù)# 下式等價于nn.Module.__init__(self) super(FC2Layer, self).__init__()self.input_size = input_size# 這里直接用 Sequential 就定義了網(wǎng)絡(luò),注意要和下面 CNN 的代碼區(qū)分開self.network = nn.Sequential(nn.Linear(input_size, n_hidden), nn.ReLU(), nn.Linear(n_hidden, n_hidden), nn.ReLU(), nn.Linear(n_hidden, output_size), nn.LogSoftmax(dim=1))def forward(self, x):# view一般出現(xiàn)在model類的forward函數(shù)中,用于改變輸入或輸出的形狀# x.view(-1, self.input_size) 的意思是多維的數(shù)據(jù)展成二維# 代碼指定二維數(shù)據(jù)的列數(shù)為 input_size=784,行數(shù) -1 表示我們不想算,電腦會自己計算對應(yīng)的數(shù)字# 在 DataLoader 部分,我們可以看到 batch_size 是64,所以得到 x 的行數(shù)是64# 大家可以加一行代碼:print(x.cpu().numpy().shape)# 訓(xùn)練過程中,就會看到 (64, 784) 的輸出,和我們的預(yù)期是一致的# forward 函數(shù)的作用是,指定網(wǎng)絡(luò)的運(yùn)行過程,這個全連接網(wǎng)絡(luò)可能看不啥意義,# 下面的CNN網(wǎng)絡(luò)可以看出 forward 的作用。x = x.view(-1, self.input_size)return self.network(x)
class CNN(nn.Module):def __init__(self, input_size, n_feature, output_size):# 執(zhí)行父類的構(gòu)造函數(shù),所有的網(wǎng)絡(luò)都要這么寫super(CNN, self).__init__()# 下面是網(wǎng)絡(luò)里典型結(jié)構(gòu)的一些定義,一般就是卷積和全連接# 池化、ReLU一類的不用在這里定義self.n_feature = n_featureself.conv1 = nn.Conv2d(in_channels=1, out_channels=n_feature, kernel_size=5)self.conv2 = nn.Conv2d(n_feature, n_feature, kernel_size=5)self.fc1 = nn.Linear(n_feature*4*4, 50)self.fc2 = nn.Linear(50, 10) # 下面的 forward 函數(shù),定義了網(wǎng)絡(luò)的結(jié)構(gòu),按照一定順序,把上面構(gòu)建的一些結(jié)構(gòu)組織起來# 意思就是,conv1, conv2 等等的,可以多次重用def forward(self, x, verbose=False):x = self.conv1(x)x = F.relu(x)x = F.max_pool2d(x, kernel_size=2)x = self.conv2(x)x = F.relu(x)x = F.max_pool2d(x, kernel_size=2)x = x.view(-1, self.n_feature*4*4)x = self.fc1(x)x = F.relu(x)x = self.fc2(x)x = F.log_softmax(x, dim=1)return x
3.2 定義訓(xùn)練和測試函數(shù)
# 訓(xùn)練函數(shù)
def train(model):model.train()# 主里從train_loader里,64個樣本一個batch為單位提取樣本進(jìn)行訓(xùn)練for batch_idx, (data, target) in enumerate(train_loader):# 把數(shù)據(jù)送到GPU中data, target = data.to(device), target.to(device)optimizer.zero_grad()output = model(data)loss = F.nll_loss(output, target)loss.backward()optimizer.step()if batch_idx % 100 == 0:print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(batch_idx * len(data), len(train_loader.dataset),100. * batch_idx / len(train_loader), loss.item()))def test(model):model.eval()test_loss = 0correct = 0for data, target in test_loader:# 把數(shù)據(jù)送到GPU中data, target = data.to(device), target.to(device)# 把數(shù)據(jù)送入模型,得到預(yù)測結(jié)果output = model(data)# 計算本次batch的損失,并加到 test_loss 中test_loss += F.nll_loss(output, target, reduction='sum').item()# get the index of the max log-probability,最后一層輸出10個數(shù),# 值最大的那個即對應(yīng)著分類結(jié)果,然后把分類結(jié)果保存在 pred 里pred = output.data.max(1, keepdim=True)[1]# 將 pred 與 target 相比,得到正確預(yù)測結(jié)果的數(shù)量,并加到 correct 中# 這里需要注意一下 view_as ,意思是把 target 變成維度和 pred 一樣的意思 correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()test_loss /= len(test_loader.dataset)accuracy = 100. * correct / len(test_loader.dataset)print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(test_loss, correct, len(test_loader.dataset),accuracy))
4.1 在小型全連接網(wǎng)絡(luò)上訓(xùn)練(Fully-connected network)
n_hidden = 8 # number of hidden unitsmodel_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))train(model_fnn)
test(model_fnn)
運(yùn)行結(jié)果
4.2 在卷積神經(jīng)網(wǎng)絡(luò)上訓(xùn)練
#@title 3.2 在卷積神經(jīng)網(wǎng)絡(luò)上訓(xùn)練
# Training settings
n_features = 6 # number of feature mapsmodel_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))train(model_cnn)
test(model_cnn)
運(yùn)行結(jié)果
通過上面的測試結(jié)果,可以發(fā)現(xiàn),含有相同參數(shù)的 CNN 效果要明顯優(yōu)于 簡單的全連接網(wǎng)絡(luò),是因為 CNN 能夠更好的挖掘圖像中的信息,主要通過兩個手段:
- 卷積:Locality and stationarity in images
- 池化:Builds in some translation invariance
5. 打亂像素順序再次在兩個網(wǎng)絡(luò)上訓(xùn)練與測試
5.1 圖像的形態(tài)
考慮到CNN在卷積與池化上的優(yōu)良特性,如果我們把圖像中的像素打亂順序,這樣 卷積 和 池化 就難以發(fā)揮作用了,為了驗證這個想法,我們把圖像中的像素打亂順序再試試。
首先下面代碼展示隨機(jī)打亂像素順序后,圖像的形態(tài):
#@title 4. 打亂像素順序再次在兩個網(wǎng)絡(luò)上訓(xùn)練與測試
# 這里解釋一下 torch.randperm 函數(shù),給定參數(shù)n,返回一個從0到n-1的隨機(jī)整數(shù)排列
perm = torch.randperm(784)
plt.figure(figsize=(8, 4))
for i in range(10):image, _ = train_loader.dataset.__getitem__(i)# permute pixelsimage_perm = image.view(-1, 28*28).clone()image_perm = image_perm[:, perm]image_perm = image_perm.view(-1, 1, 28, 28)plt.subplot(4, 5, i + 1)plt.imshow(image.squeeze().numpy(), 'gray')plt.axis('off')plt.subplot(4, 5, i + 11)plt.imshow(image_perm.squeeze().numpy(), 'gray')plt.axis('off')
# 對每個 batch 里的數(shù)據(jù),打亂像素順序的函數(shù)
def perm_pixel(data, perm):# 轉(zhuǎn)化為二維矩陣data_new = data.view(-1, 28*28)# 打亂像素順序data_new = data_new[:, perm]# 恢復(fù)為原來4維的 tensordata_new = data_new.view(-1, 1, 28, 28)return data_new# 訓(xùn)練函數(shù)
def train_perm(model, perm):model.train()for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)# 像素打亂順序data = perm_pixel(data, perm)optimizer.zero_grad()output = model(data)loss = F.nll_loss(output, target)loss.backward()optimizer.step()if batch_idx % 100 == 0:print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(batch_idx * len(data), len(train_loader.dataset),100. * batch_idx / len(train_loader), loss.item()))# 測試函數(shù)
def test_perm(model, perm):model.eval()test_loss = 0correct = 0for data, target in test_loader:data, target = data.to(device), target.to(device)# 像素打亂順序data = perm_pixel(data, perm)output = model(data)test_loss += F.nll_loss(output, target, reduction='sum').item()pred = output.data.max(1, keepdim=True)[1] correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()test_loss /= len(test_loader.dataset)accuracy = 100. * correct / len(test_loader.dataset)print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(test_loss, correct, len(test_loader.dataset),accuracy))
重新定義訓(xùn)練與測試函數(shù),我們寫了兩個函數(shù) train_perm 和 test_perm,分別對應(yīng)著加入像素打亂順序的訓(xùn)練函數(shù)與測試函數(shù)。
與之前的訓(xùn)練與測試函數(shù)基本上完全相同,只是對 data 加入了打亂順序操作。
5.2 在全連接網(wǎng)絡(luò)上訓(xùn)練與測試:
#@title 4.1 在全連接網(wǎng)絡(luò)上訓(xùn)練與測試:
perm = torch.randperm(784)
n_hidden = 8 # number of hidden unitsmodel_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))train_perm(model_fnn, perm)
test_perm(model_fnn, perm)
5.3 在卷積神經(jīng)網(wǎng)絡(luò)上訓(xùn)練與測試:
#@title 4.2 在卷積神經(jīng)網(wǎng)絡(luò)上訓(xùn)練與測試:
perm = torch.randperm(784)
n_features = 6 # number of feature mapsmodel_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))train_perm(model_cnn, perm)
test_perm(model_cnn, perm)
從打亂像素順序的實驗結(jié)果來看,全連接網(wǎng)絡(luò)的性能基本上沒有發(fā)生變化,但是 卷積神經(jīng)網(wǎng)絡(luò)的性能明顯下降。
這是因為對于卷積神經(jīng)網(wǎng)絡(luò),會利用像素的局部關(guān)系,但是打亂順序以后,這些像素間的關(guān)系將無法得到利用。
關(guān)于實驗中的一些問題
- dataloader 里面 shuffle 取不同值有什么區(qū)別
當(dāng)shuffle取True時就是每次取為亂序
取False就不會重新隨機(jī)排序
- transform 里,取了不同值,這個有什么區(qū)別?
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,), (0.3081,))])
首先理解transform是對數(shù)據(jù)進(jìn)行一些預(yù)處理的操作
1. Compose為一個流水線操作,就是依次執(zhí)行數(shù)組里的操作
2. ToTensor將數(shù)據(jù)轉(zhuǎn)為tensor數(shù)據(jù)類型
3. Normalize進(jìn)行歸一化處理,這就是為什么查看圖片會是灰色的
- epoch 和 batch 的區(qū)別?
- Epoch:所有訓(xùn)練樣本在神經(jīng)網(wǎng)絡(luò)中都進(jìn)行了一次正向傳播和一次反向傳播。也就是1個epoch等于使用訓(xùn)練集中的全部樣本訓(xùn)練一次。
- Batch:將整個訓(xùn)練樣本分成若干個Batch。
- 1x1的卷積和 FC 有什么區(qū)別?主要起什么作用?
- 1x1的卷積是針對矩陣的卷積操作,1x1卷積不改變寬度和高度,只改變channel數(shù),主要作用是升維、降維以及提升網(wǎng)絡(luò)的非線性;
- FC是針對神經(jīng)元的操作。FC的作用主要是將局部特征進(jìn)行整合,并得到分類標(biāo)簽。
CIFAR10 數(shù)據(jù)集分類
使用 CNN 對 CIFAR10 數(shù)據(jù)集進(jìn)行分類:
運(yùn)用totchvision包來進(jìn)行視覺數(shù)據(jù)的處理,數(shù)據(jù)集選用CIFAR10,包含十個類別:‘a(chǎn)irplane’, ‘a(chǎn)utomobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。CIFAR-10 中的圖像尺寸為3x32x32,也就是RGB的3層顏色通道,每層通道內(nèi)的尺寸為32*32。
首先,加載并歸一化 CIFAR10 使用 torchvision 。torchvision 數(shù)據(jù)集的輸出是范圍在[0,1]之間的 PILImage,我們將他們轉(zhuǎn)換成歸一化范圍為[-1,1]之間的張量 Tensors。
導(dǎo)入相關(guān)包,下載相關(guān)數(shù)據(jù)集并使用GPU訓(xùn)練:
通過書寫imshow函數(shù)來展示CIFAR10里面的一些圖片:
定義網(wǎng)絡(luò)、損失函數(shù)和優(yōu)化器:
訓(xùn)練網(wǎng)絡(luò),該過程耗時2分鐘:
從測試集中取出8張圖片:
把圖片輸入建立好的模型,查看CNN模型的識別效果,可以發(fā)現(xiàn)識別準(zhǔn)確度不高,出現(xiàn)了一些錯誤:
再來查看網(wǎng)絡(luò)在整個數(shù)據(jù)集上的表現(xiàn),我們發(fā)現(xiàn)準(zhǔn)確率只能說是可以接受:
所以我們需要建立更好的網(wǎng)絡(luò)模型,總結(jié)CNN的使用效果,我們可以得到一些缺點:
- 當(dāng)網(wǎng)絡(luò)層次太深時,采用BP傳播修改參數(shù)會使靠近輸入層的參數(shù)改動較慢;
- 采用梯度下降算法很容易使訓(xùn)練結(jié)果收斂于局部最小值而非全局最小值;
- 池化層會丟失大量有價值信息,忽略局部與整體之間關(guān)聯(lián)性;
- 由于特征提取的封裝,為網(wǎng)絡(luò)性能的改進(jìn)罩了一層黑盒
使用 VGG16 對 CIFAR10 分類
1. 重新定義 dataloader
2. VGG 網(wǎng)絡(luò)定義
原代碼中出現(xiàn)“cfg”無法識別問題,將參數(shù)layers(cfg)改為layers(self.cfg)后正常運(yùn)行。
3. 網(wǎng)絡(luò)訓(xùn)練
原代碼出現(xiàn)RuntimeError: mat1 and mat2 shapes cannot be multiplied (128x512 and 2048x10)報錯,因此對nn.Linear 參數(shù)進(jìn)行修改,把2048改成了512。
4. 測試驗證準(zhǔn)確率
使用一個簡化版的 VGG 網(wǎng)絡(luò),就能將準(zhǔn)確率提升到 83.18%
改為訓(xùn)練15次后,準(zhǔn)確率提升到87.64%。
問題回答
1、dataloader 里面 shuffle 取不同值有什么區(qū)別?
dataloader是一個加載數(shù)據(jù)到模型中訓(xùn)練的方法,其中的參數(shù)shuffle參數(shù)有True/False兩種取值;官方文檔的解釋如下:
· 當(dāng)shuffle=false時,每次的訓(xùn)練不打亂數(shù)據(jù)的順序,然后以batch為單位取數(shù)據(jù)
· 當(dāng)shuffle=true時,每次訓(xùn)練前打亂所有的數(shù)據(jù)次序,然后以batch為單位取數(shù)據(jù)
2、transform 里,取了不同值,這個有什么區(qū)別?
transform里可以通過不同參數(shù)實現(xiàn)一些常用的數(shù)據(jù)預(yù)處理操作。
transforms.normalize()使得數(shù)據(jù)服從01分布。
加速了收斂,其使用的公式為x=(x-mean)/std
3、epoch 和 batch 的區(qū)別?
一個epoch中包含了多個batch,每一次對整個數(shù)據(jù)集的訓(xùn)練都是一次epoch,而batch則表示單個epoch中一次批處理數(shù)據(jù)所包含的數(shù)據(jù)單位。
4、1x1的卷積和 FC 有什么區(qū)別?主要起什么作用?
1×1卷積核是對二維情況下的簡化,使得其僅僅反映在深度上的變化,起到了降維的作用。
相對于FC,1×1卷積核需要的參數(shù)量會比FC層所使用的參數(shù)量少,計算速度更快。
5、residual leanring 為什么能夠提升準(zhǔn)確率?
因為它引入了殘差網(wǎng)絡(luò)結(jié)構(gòu)的概念,殘差網(wǎng)絡(luò)借鑒了高速網(wǎng)絡(luò)的跨層鏈接思想,對其進(jìn)行改進(jìn),打破了傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)n-1層的輸出只能給n層作為輸入的慣例,使某一層的輸出可以直接跨過幾層作為后面某一層的輸入,從而解決了深度增加后的“退化”問題,達(dá)到了提高準(zhǔn)確率的目的。
6、代碼練習(xí)二里,網(wǎng)絡(luò)和1989年 Lecun 提出的 LeNet 有什么區(qū)別?
代碼練習(xí)二中使用的是最大池化和ReLU激活函數(shù),而LeNet使用的是平均池化,激活函數(shù)是sigmoid;
7、代碼練習(xí)二里,卷積以后feature map 尺寸會變小,如何應(yīng)用 Residual Learning?
通過線性變換將原圖像縮小為和feature map大小相同的圖像;當(dāng)輸入輸出維度上升時有兩種處理方式:第一種是仍使用恒等映射,多出來的通道使用零矩陣填充,這樣做的好處是不會帶來額外的參數(shù);第二種是添加變換方程,通常來說會使用 1*1 卷積來完成升維。
8、有什么方法可以進(jìn)一步提升準(zhǔn)確率?
調(diào)整網(wǎng)絡(luò)結(jié)構(gòu);增加數(shù)據(jù)集的大小,使數(shù)據(jù)更加豐富;增加深度與節(jié)點數(shù);使用不同的激活函數(shù)、交叉熵函數(shù);增加訓(xùn)練輪數(shù)
總結(jié)
以上是生活随笔為你收集整理的第三次作业:卷积神经网络基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统第四章习题
- 下一篇: 芝麻盐的功效与作用、禁忌和食用方法