吴恩达深度学习课程第四章第二周编程作业(pytorch实现)
文章目錄
- 前言
- 一、問(wèn)題描述
- 二、前置知識(shí)和模型架構(gòu)
- 1.殘差網(wǎng)絡(luò)的基本知識(shí)
- 2.模型架構(gòu)
- 二、編程實(shí)現(xiàn)
- 1.Dataloader加載數(shù)據(jù)
- 2.殘差塊的封裝
- 3.殘差網(wǎng)絡(luò)ResNets
- 4.主控函數(shù)
- 四、結(jié)果
前言
??本博客只是記錄一下本人在深度學(xué)習(xí)過(guò)程中的學(xué)習(xí)筆記和編程經(jīng)驗(yàn),大部分代碼是參考了【中文】【吳恩達(dá)課后編程作業(yè)】Course 4 - 卷積神經(jīng)網(wǎng)絡(luò) - 第二周作業(yè)這篇博客,對(duì)其代碼實(shí)現(xiàn)了復(fù)現(xiàn),但是原博客中代碼使用的是tensorflow,而我在學(xué)習(xí)中主要用到的是pytorch,所以此次作業(yè)我使用pytorch框架來(lái)完成。代碼或文字表述中還存在一些問(wèn)題,請(qǐng)見(jiàn)諒,之前的博客也是主要參考這個(gè)大佬。下文中的完整代碼已經(jīng)上傳到百度網(wǎng)盤(pán)中,提取碼:0qse。
??所以開(kāi)始作業(yè)前,請(qǐng)大家安裝好pytorch的環(huán)境,我代碼是在服務(wù)器上利用gpu加速運(yùn)行的,但是cpu版本的pytorch也能運(yùn)行,只是速度會(huì)比較慢。
一、問(wèn)題描述
??此次作業(yè)的主要目的是使用殘差網(wǎng)絡(luò)實(shí)現(xiàn)深層卷積神經(jīng)網(wǎng)絡(luò)完成分類(lèi)問(wèn)題,這次作業(yè)提供了兩個(gè)分類(lèi)問(wèn)題的數(shù)據(jù)集,一個(gè)是二分類(lèi),一個(gè)是多分類(lèi),其實(shí)分類(lèi)問(wèn)題的解決思路都大同小異,這篇博客主要通過(guò)講述多分類(lèi)問(wèn)題來(lái)對(duì)代碼進(jìn)行講解。
??多分類(lèi)問(wèn)題也還是之前的識(shí)別手勢(shì)代表的數(shù)字,這里就不再過(guò)多的描述。
??原博客中使用50層的殘差網(wǎng)絡(luò),本博客使用的是34層的殘差網(wǎng)絡(luò),pytorch代碼主要參考博客:從頭學(xué)pytorch(二十):殘差網(wǎng)絡(luò)resnet 。
二、前置知識(shí)和模型架構(gòu)
1.殘差網(wǎng)絡(luò)的基本知識(shí)
??殘差網(wǎng)絡(luò)的基本原理和計(jì)算規(guī)則其實(shí)挺容易理解的,我們以一個(gè)殘差塊為基本單位進(jìn)行研究:
??對(duì)于一個(gè)殘差塊而言,它與基本的神經(jīng)網(wǎng)絡(luò)的區(qū)別在于,殘差塊最后通過(guò)非線性函數(shù)激活輸出的時(shí)候需要加上殘差塊最開(kāi)始的輸入,明白這一點(diǎn)后用編程實(shí)現(xiàn)起來(lái)其實(shí)并不復(fù)雜。
2.模型架構(gòu)
??模型架構(gòu)問(wèn)題我并沒(méi)有深究,這里主要通過(guò)論文中的兩張圖來(lái)進(jìn)行簡(jiǎn)單的說(shuō)明一下:
??上圖來(lái)自原論文:Deep Residual Learning for Image Recognition 中截取下來(lái),圖中繪制了三種神經(jīng)網(wǎng)絡(luò)的基本結(jié)構(gòu):VGG-19,傳統(tǒng)的卷積網(wǎng)絡(luò)(Plain)和殘差網(wǎng)絡(luò)(ResNets),從結(jié)構(gòu)來(lái)看,殘差網(wǎng)絡(luò)只是在傳統(tǒng)的卷積網(wǎng)絡(luò)中添加了跳躍連接(skip connection),我們可以以殘差塊為基本單元構(gòu)造神經(jīng)網(wǎng)絡(luò)。
??論文中還給出了不同層數(shù)的殘差網(wǎng)絡(luò)對(duì)應(yīng)的一個(gè)具體結(jié)構(gòu),這也是我編寫(xiě)代碼的一個(gè)基本標(biāo)準(zhǔn)。以34層的殘差網(wǎng)絡(luò)為例,主要分為五個(gè)部分(最后還有全連接層等操作):
二、編程實(shí)現(xiàn)
1.Dataloader加載數(shù)據(jù)
import h5py import numpy as np import torch from torch.utils.data import Dataset, DataLoader import matplotlib.pyplot as pltclass Image_Data(Dataset):def __init__(self, data_path):super(Image_Data, self).__init__()# 讀取數(shù)據(jù)集dataset = h5py.File(data_path, "r")if data_path == "datasets/train_happy.h5" or data_path == "datasets/train_signs.h5":data_set_x_orig = np.array(dataset["train_set_x"][:])data_set_y_orig = np.array(dataset["train_set_y"][:])else:data_set_x_orig = np.array(dataset["test_set_x"][:])data_set_y_orig = np.array(dataset["test_set_y"][:])data_set_x_orig = data_set_x_orig.astype("float32") / 255data_set_y_orig = data_set_y_orig.astype("float32")self.x_data = torch.from_numpy(data_set_x_orig)self.y_data = torch.from_numpy(data_set_y_orig)self.len = self.y_data.size()[0]def __getitem__(self, item):return self.x_data[item], self.y_data[item]def __len__(self):return self.lendef get_shape(self):return self.x_data.size(), self.y_data.size()??我這里使用通過(guò)繼承Dataset類(lèi)對(duì)數(shù)據(jù)進(jìn)行了簡(jiǎn)單的封裝操作,每個(gè)人的編程習(xí)慣不同,可以做相應(yīng)的調(diào)整。
2.殘差塊的封裝
class Residual(torch.nn.Module):def __init__(self, in_channels, out_channels, stride=1):super(Residual, self).__init__()self.stride = stride# 卷積層使用same填充,由于pytorch沒(méi)有提供自動(dòng)填充的操作,需要手算填充的大小self.conv1 = torch.nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)self.bn1 = torch.nn.BatchNorm2d(out_channels)self.relu1 = torch.nn.ReLU(inplace=True)self.conv2 = torch.nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)self.bn2 = torch.nn.BatchNorm2d(out_channels)# 輸入和輸出通道數(shù)不同時(shí),需要通過(guò)1x1卷積改變輸入數(shù)據(jù)的通道數(shù)if in_channels != out_channels:self.conv1x1 = torch.nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride)else:self.conv1x1 = Nonedef forward(self, x):out1 = self.relu1(self.bn1(self.conv1(x)))out2 = self.bn2(self.conv2(out1))if self.conv1x1:x = self.conv1x1(x)out = self.relu1(out2 + x)return out??殘差塊的大致結(jié)構(gòu)為:
1.卷積conv1 2.Batch Normalization層 3.Relu 4.卷積conv2 5.Batch Normalization層 6.Relu(代碼中復(fù)用了Relu,所以只定義了一個(gè))??殘差塊初始化時(shí)我們需要傳入輸入通道數(shù),輸出通道數(shù)和步長(zhǎng),輸入通道數(shù)為你輸入數(shù)據(jù)的通道數(shù),輸出通道數(shù)為想要輸出的通道數(shù),輸出通道數(shù)的設(shè)置按照論文中給出的標(biāo)準(zhǔn)來(lái)實(shí)現(xiàn)。
??需要注意的是殘差塊的輸入和輸出的通道數(shù)可能不同,但是殘差塊內(nèi)部的計(jì)算一般是不會(huì)改變數(shù)據(jù)的維度,所以第二個(gè)卷積函數(shù)初始化時(shí)輸入和輸出的通道數(shù)都為 out_channels 。
??在經(jīng)過(guò)最后的Relu時(shí),我們需要將當(dāng)前的計(jì)算結(jié)果加上最開(kāi)始的輸入,這就要保證這兩個(gè)值的維度相同,所以當(dāng)輸入和輸出的通道數(shù)不同時(shí)我們需要利用1x1卷積改變輸入數(shù)據(jù)的通道數(shù):
3.殘差網(wǎng)絡(luò)ResNets
class ResNet(torch.nn.Module):def __init__(self, in_channels, num_classes):super(ResNet, self).__init__()self.conv1 = torch.nn.Sequential(torch.nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3),torch.nn.BatchNorm2d(64),torch.nn.ReLU(inplace=True))self.conv2 = torch.nn.Sequential(torch.nn.MaxPool2d(kernel_size=3, stride=2, padding=1),Residual(64, 64),Residual(64, 64),Residual(64, 64))self.conv3 = torch.nn.Sequential(Residual(64, 128, stride=2),Residual(128, 128),Residual(128, 128),Residual(128, 128),Residual(128, 128))self.conv4 = torch.nn.Sequential(Residual(128, 256, stride=2),Residual(256, 256),Residual(256, 256),Residual(256, 256),Residual(256, 256),Residual(256, 256))self.conv5 = torch.nn.Sequential(Residual(256, 512, stride=2),Residual(512, 512),Residual(512, 512))self.avg_pool = torch.nn.AdaptiveAvgPool2d(1)self.fc = torch.nn.Linear(512, num_classes)def forward(self, x):out = self.conv1(x)out = self.conv2(out)out = self.conv3(out)out = self.conv4(out)out = self.conv5(out)out = self.avg_pool(out)out = out.view(out.size()[0], -1)out = self.fc(out)return out??設(shè)計(jì)殘差網(wǎng)絡(luò)主體的五個(gè)部分嚴(yán)格按照論文中給出的參數(shù)來(lái)設(shè)計(jì),關(guān)于殘差網(wǎng)絡(luò)的初始化需要告知輸入數(shù)據(jù)的通道數(shù)和多分類(lèi)的類(lèi)別個(gè)數(shù)。在經(jīng)過(guò)五部分處理后,將輸出進(jìn)行平均池化,池化完成后展開(kāi)成一維,并輸入到一個(gè)全連接層中。
4.主控函數(shù)
import torchdevice = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")from data_utils import Image_Data from resnet_model import ResNet import matplotlib.pyplot as plt from torch.utils.data import DataLoadertrain_happy_data_path = "datasets/train_happy.h5" train_signs_data_path = "datasets/train_signs.h5"test_happy_data_path = "datasets/test_happy.h5" test_signs_data_path = "datasets/test_signs.h5"if __name__ == "__main__":# 初始化訓(xùn)練要使用到的參數(shù)num_epoch = 50learning_rate = 0.01batch_size = 32costs = []classes = 6# 加載數(shù)據(jù)train_data = Image_Data(train_signs_data_path)train_data_loader = DataLoader(train_data, shuffle=True, batch_size=batch_size)test_data = Image_Data(test_signs_data_path)test_data_loader = DataLoader(test_data, shuffle=True, batch_size=batch_size)# 初始化網(wǎng)絡(luò),圖像的通道數(shù)為3m = ResNet(3, num_classes=classes)m = m.to(device)# 定義交叉熵?fù)p失函數(shù)loss_fn = torch.nn.CrossEntropyLoss()# 定義優(yōu)化器optimizer = torch.optim.Adam(m.parameters(), lr=learning_rate)# 開(kāi)始訓(xùn)練for epoch in range(num_epoch):cost = 0for i, data in enumerate(train_data_loader):img_data, img_label = dataimg_data = img_data.permute(0, 3, 1, 2)img_data = img_data.to(device)img_label = img_label.to(device)optimizer.zero_grad()y_pred = m.forward(img_data)loss = loss_fn(y_pred, img_label.long())loss.backward()optimizer.step()cost = cost + loss.cpu().detach().numpy()costs.append(cost / (i + 1))if epoch % 5 == 0:print("epoch=" + str(epoch) + ": " + "loss=" + str(cost / (i + 1)))plt.plot(costs)plt.ylabel("cost")plt.xlabel('iterations (per tens)')plt.title("Learning rate =" + str(learning_rate))plt.show()??當(dāng)把模型封裝完成后訓(xùn)練模型的代碼其實(shí)千篇一律,我個(gè)人的處理習(xí)慣為:初始化訓(xùn)練參數(shù),加載數(shù)據(jù)集,初始化模型,定義損失函數(shù),定義優(yōu)化器,開(kāi)始訓(xùn)練,繪制損失曲線圖。
??在模型內(nèi)部我并未定義預(yù)測(cè)函數(shù),訓(xùn)練完成后,在訓(xùn)練集和標(biāo)簽集上做預(yù)測(cè)時(shí),需要將模型的計(jì)算結(jié)果再通過(guò)一個(gè)softmax層。
四、結(jié)果
??從結(jié)果來(lái)看,通過(guò)殘差網(wǎng)絡(luò)加深神經(jīng)網(wǎng)絡(luò)后,模型的表現(xiàn)效果變得非常好。
總結(jié)
以上是生活随笔為你收集整理的吴恩达深度学习课程第四章第二周编程作业(pytorch实现)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 京东线报-京东实时线报-高级京东线报接口
- 下一篇: WinHex license添加(v19