自定义LeNet5,开启你的炼丹之路
????????LeNet5是卷積神經(jīng)網(wǎng)絡(luò)的開(kāi)山之作,在手寫(xiě)數(shù)字識(shí)別上達(dá)到了出色的效果。LeNet5具有5層神經(jīng)網(wǎng)絡(luò),其中2層卷積神經(jīng)網(wǎng)絡(luò),3層全連接。兩層卷積神經(jīng)網(wǎng)絡(luò)之后分別接了一個(gè)激活層和一個(gè)平均池化層。這里通過(guò)對(duì)lenet進(jìn)行解析和修改,實(shí)現(xiàn)對(duì)自定義圖像數(shù)據(jù)的分類。
???????? 首先來(lái)解析lenet5手寫(xiě)數(shù)字識(shí)別模型:
???????? 第一個(gè)卷積層:輸入圖像為32*32大小的灰度圖,圖像通道為1,寬高為32*32,卷積層包含6個(gè)卷積核,每個(gè)卷積核大小為5*5,經(jīng)過(guò)第一個(gè)卷積層后,生成通道為6的寬高為28*28的特征圖,之后通過(guò)sigmoid激活函數(shù)和池化層,池化層為均值池化,采用2*2卷積核,步長(zhǎng)為2進(jìn)行池化卷積,對(duì)特征圖進(jìn)行下采樣,經(jīng)過(guò)池化以后,特征圖變?yōu)?*14*14;
???????? 第二個(gè)卷積層:這一層有16個(gè)卷積核,卷積核大小5*5,經(jīng)過(guò)該層卷積后特征圖大小變?yōu)?6*10*10,池化層同樣采用2*2卷積核,步長(zhǎng)為2,經(jīng)過(guò)池化后,特征圖變?yōu)?6*5*5;
?????? 在全連接層之前需要對(duì)特征圖進(jìn)行打平,16*5*5=400,通過(guò)第一個(gè)全連接層,降維到120,第二個(gè)全連接層后降維到84,第三個(gè)全連接層降維到10,也就是手寫(xiě)數(shù)字識(shí)別的分類數(shù)。
lenet5的pytorch實(shí)現(xiàn):
net = torch.nn.Sequential(nn.Conv2d(1,6,kernel_size=5), #[1,1,28,28]*[6,5*5]-->[1,6,28,28],圖像與6個(gè)5*5的卷積核計(jì)算,得到6個(gè)28*28特征圖nn.Sigmoid(), #激活函數(shù)nn.AvgPool2d(2,stride=2), #[1,6,28,28]-->[1,6,14,14],平均池化,卷積核2*2,步長(zhǎng)2(每四個(gè)像素合并為一個(gè))nn.Conv2d(6,16,kernel_size=5), #[1,6,14,14]*[16,5*5]-->[1,16,10,10],圖像與16個(gè)5*5卷積核計(jì)算,得到16個(gè)10*10特征圖nn.Sigmoid(),nn.AvgPool2d(kernel_size=2, stride=2), #[1,16,10,10]-->[1,16,5,5]nn.Flatten(), #[1,16,5,5]-->[1,400] 打平nn.Linear(400,120), #[1,400]-->[1,120]nn.Sigmoid(),nn.Linear(120,84), #[1,120]-->[1,84]nn.Sigmoid(),nn.Linear(84,10) #[1,84]-->[1,10] )????????使用lenet5訓(xùn)練自定義圖像分類模型,根據(jù)數(shù)據(jù)和分類對(duì)模型進(jìn)行修改。
????????數(shù)據(jù)集路徑類似如下結(jié)構(gòu):
????????????????????????????????dataset/dogs/train/(husky,labrado)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dataset/dogs/val/(husky,labrado)
???????? 由于這里使用的彩色圖像,所以將第一個(gè)卷積層輸入通道改為3,使用的數(shù)據(jù)集只包含兩個(gè)分類,那么需要最后一個(gè)全連接層輸出改為分類數(shù),也就是2。
構(gòu)建網(wǎng)絡(luò):
net = torch.nn.Sequential(nn.Conv2d(3,6,kernel_size=5), nn.Sigmoid(), nn.AvgPool2d(2,stride=2), nn.Conv2d(6,16,kernel_size=5), nn.Sigmoid(),nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(), nn.Linear(400,120), nn.Sigmoid(),nn.Linear(120,84), nn.Sigmoid(),nn.Linear(84,2) )????????定義訓(xùn)練函數(shù),由于是圖像分類,將不同類別圖像放到不同文件夾下,使用ImageFolder加數(shù)據(jù)集并自動(dòng)分配標(biāo)簽。
def train():# 如有GPU,默認(rèn)使用第一塊GPUdevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')print('using device {}'.format(device))#數(shù)據(jù)預(yù)處理data_transform = {"train": transforms.Compose([transforms.RandomResizedCrop(32), #隨機(jī)縮放裁剪transforms.RandomHorizontalFlip(), #隨機(jī)水平翻轉(zhuǎn)transforms.ToTensor(), #轉(zhuǎn)換為Tensortransforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) #歸一化]),"val": transforms.Compose([transforms.RandomResizedCrop(32),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}#加載數(shù)據(jù)集batch_size = 32data_path = 'dataset/dogs'assert os.path.exists(data_path), "{} does not exist".format(data_path)train_dataset = datasets.ImageFolder(root=os.path.join(data_path, 'train'), transform=data_transform['train'])val_dataset = datasets.ImageFolder(root=os.path.join(data_path, 'val'), transform=data_transform['val'])train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=8)val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=5, shuffle=False, num_workers=8)net.load_state_dict(torch.load('lenet.pt'))net.to(device)loss_function = nn.CrossEntropyLoss()optimizer = torch.optim.Adam(net.parameters(), lr=0.01)epochs = 10save_path = 'lenet.pt'best_acc = 0.0steps = len(train_loader)for epoch in range(epochs):net.train()running_loss = 0.0for step, data in enumerate(train_loader):images, labels = dataoptimizer.zero_grad()outputs = net(images.to(device))loss = loss_function(outputs, labels.to(device))loss.backward()optimizer.step()running_loss += loss.item()print('step:{}/{},loss:{}'.format(step + 1, steps, loss))net.eval()acc = 0.0with torch.no_grad():for val_data in val_loader:images, labels = val_dataoutputs = net(images.to(device))predict = torch.max(outputs, dim=1)[1]acc += torch.eq(predict, labels.to(device)).sum().item()val_acc = acc / len(val_dataset)print('epoch:{}, acc:{}'.format(epoch + 1, val_acc))if val_acc > best_acc:best_acc = val_acctorch.save(net.state_dict(), save_path)print("finish train")????????開(kāi)始訓(xùn)練,經(jīng)過(guò)10輪訓(xùn)練后,分類精度達(dá)到82%,最高精度85%。
?????????這里只是簡(jiǎn)單修改了lenet模型的輸入輸出,如果對(duì)模型其他地方進(jìn)行修改,將激活函數(shù)替換為Relu又會(huì)如何呢?
?????????修改以后,經(jīng)過(guò)第一輪訓(xùn)練后,模型精度就達(dá)到了71%,訓(xùn)練過(guò)程最高精度達(dá)到85%,看來(lái)更換激活函數(shù)還是有一定效果??梢圆僮鞯牡胤竭€有很多,比如將平均池化改為最大池化,增加卷積層數(shù),修改卷積核數(shù)量,調(diào)整卷積核大小,使用padding自定義卷積層輸出大小,或者修改全連接層等,直到調(diào)出一個(gè)性能不錯(cuò)的模型,煉丹不就成了。
總結(jié)
以上是生活随笔為你收集整理的自定义LeNet5,开启你的炼丹之路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: YOLOv5训练coco128数据集流程
- 下一篇: AlexNet网络构建与训练