【CV实战】年轻人的第一个深度学习图像分割项目应该是什么样的(Pytorch框架)?...
我們上次給新手們介紹了第一個(gè)合適入門(mén)的深度學(xué)習(xí)CV項(xiàng)目,可閱讀【CV實(shí)戰(zhàn)】年輕人的第一個(gè)深度學(xué)習(xí)CV項(xiàng)目應(yīng)該是什么樣的?(支持13大深度學(xué)習(xí)開(kāi)源框架),本次我們?cè)俳o大家介紹一個(gè)新的任務(wù),圖像分割,包括數(shù)據(jù)的處理,模型的訓(xùn)練與測(cè)試。
作者&編輯 | 言有三
本文資源與圖像分割結(jié)果展示
本文篇幅:4800字
背景要求:會(huì)使用Python和Pytorch深度學(xué)習(xí)開(kāi)源框架
附帶資料:開(kāi)源代碼一份,支持Pytorch
數(shù)據(jù)一份:文末有獲取方法
1 項(xiàng)目背景
圖像處理中,研究者往往只對(duì)圖像中的某些區(qū)域感興趣,在此基礎(chǔ)上才有可能對(duì)目標(biāo)進(jìn)行后續(xù)的處理與分析。圖像分割技術(shù)就是把圖像中屬于目標(biāo)區(qū)域的感興趣區(qū)域進(jìn)行半自動(dòng)或者自動(dòng)地提取分離出來(lái),屬于計(jì)算機(jī)視覺(jué)領(lǐng)域中最基礎(chǔ)的任務(wù)之一。
為了讓新手們能夠一次性體驗(yàn)一個(gè)圖像分割任務(wù)的完整流程,本次我們選擇帶領(lǐng)大家完成一個(gè)嘴唇圖像分割任務(wù),包括數(shù)據(jù)集的處理,模型的訓(xùn)練和測(cè)試,同時(shí)也將這次的實(shí)驗(yàn)與上一期內(nèi)容結(jié)合起來(lái)。
2?數(shù)據(jù)處理
2.1?數(shù)據(jù)獲取
我們上次已經(jīng)介紹過(guò),如果沒(méi)有開(kāi)源的數(shù)據(jù)集,我們首先要學(xué)會(huì)使用爬蟲(chóng)爬取圖像,然后對(duì)獲得的圖片數(shù)據(jù)進(jìn)行整理,包括重命名,格式統(tǒng)一,如果不清楚整個(gè)流程,可以參考我們上一次【CV實(shí)戰(zhàn)】年輕人的第一個(gè)深度學(xué)習(xí)CV項(xiàng)目應(yīng)該是什么樣的?(支持13大深度學(xué)習(xí)開(kāi)源框架),獲取后整理的圖像如下:
2.2?數(shù)據(jù)標(biāo)注
接下來(lái),我們需要對(duì)數(shù)據(jù)進(jìn)行標(biāo)注。圖像分割任務(wù)要求對(duì)每一個(gè)像素進(jìn)行預(yù)測(cè),所以需要像素級(jí)別的標(biāo)注結(jié)果,當(dāng)然我們實(shí)際標(biāo)注的時(shí)候往往是通過(guò)畫(huà)輪廓形成閉合區(qū)域,開(kāi)源的標(biāo)注工具有很多,我們可以使用LabelMe等工具進(jìn)行標(biāo)注,當(dāng)然你也可以使用其他工具進(jìn)行標(biāo)注,這里我們就不再展開(kāi)講了,因?yàn)楸疚拿嫦虻淖x者已經(jīng)不是純新手了。
標(biāo)注完之后的樣本和結(jié)果如下:
需要注意的是,標(biāo)注的結(jié)果并不是我們用于訓(xùn)練的標(biāo)簽,因?yàn)閳D像分割本身是對(duì)每一個(gè)圖像像素進(jìn)行分類(lèi),在當(dāng)前的開(kāi)源框架中,每一個(gè)像素的類(lèi)別也是從0,1,2,3這樣的順序依次增加。
所以在這里,我們一定需要注意訓(xùn)練時(shí)候的標(biāo)簽處理,這個(gè)大家根據(jù)自己的實(shí)際情況進(jìn)行調(diào)整。
3 數(shù)據(jù)讀取
得到了數(shù)據(jù)之后,接下來(lái)咱們使用Pytorch框架來(lái)進(jìn)行模型的訓(xùn)練,首先需要實(shí)現(xiàn)的就是數(shù)據(jù)讀取。Pytorch本身并沒(méi)有圖像分割任務(wù)的數(shù)據(jù)接口,所以我們需要自己定義,讀取圖像和掩膜,做一些簡(jiǎn)單的數(shù)據(jù)增強(qiáng)操作,我們定義一個(gè)類(lèi)為SegDataset如下。
class SegDataset(Dataset):
? ? def __init__(self,filetxt,imagesize,cropsize,transform=None):
? ? ? ? lines = open(filetxt,'r').readlines()
? ? ? ? self.samples = []
? ? ? ? self.imagesize = imagesize
? ? ? ? self.cropsize = cropsize
? ? ? ? self.transform? = transform
? ? ? ? if self.transform is None:
? ? ? ? ? ? transform = transforms.Compose([
? ? ? ? ? ? ? ? ? ?transforms.ToTensor(),
? ? ? ? ? ? ? ? ? ?transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))
? ? ? ? ? ? ? ? ? ?])?
? ? ? ? for line in lines:
? ? ? ? ? ? line = line.strip()
? ? ? ? ? ? imagepath,labelpath = line.split(' ')
? ? ? ? ? ? self.samples.append((imagepath,labelpath))
? ? def __getitem__(self,index):##獲取數(shù)據(jù)
? ? ? ? imagepath,labelpath = self.samples[index]
? ? ? ? image = cv2.imread(imagepath)
? ? ? ? label = cv2.imread(labelpath,0) ##讀成1通道
? ? ? ?
? ? ? ? ## 添加基本的數(shù)據(jù)增強(qiáng),對(duì)圖片和標(biāo)簽保持一致
? ? ? ? ## 添加固定尺度的隨機(jī)裁剪,使用最近鄰縮放(不產(chǎn)生新的灰度值)+裁剪
? ? ? ? image = cv2.resize(image,(self.imagesize,self.imagesize),interpolation=cv2.INTER_NEAREST)
? ? ? ? label = cv2.resize(label,(self.imagesize,self.imagesize),interpolation=cv2.INTER_NEAREST)
? ? ? ? offsetx = np.random.randint(self.imagesize-self.cropsize)
? ? ? ? offsety = np.random.randint(self.imagesize-self.cropsize)
? ? ? ? image = image[offsety:offsety+self.cropsize,offsetx:offsetx+self.cropsize]
? ? ? ? label = label[offsety:offsety+self.cropsize,offsetx:offsetx+self.cropsize]
? ? ? ?
? ? ? ? return self.transform(image),label ##只對(duì)image做預(yù)處理操作
? ? def __len__(self): ##統(tǒng)計(jì)數(shù)據(jù)集大小
? ? ? ? return len(self.samples)
上述的SegDataset類(lèi)實(shí)現(xiàn)了圖像分割任務(wù)數(shù)據(jù)的讀取,有以下幾個(gè)需要說(shuō)明的地方:
(1) 輸入的filetxt是我們預(yù)先準(zhǔn)備好的文件,其中每一行按照[圖片 標(biāo)簽]的對(duì)應(yīng)格式存儲(chǔ)著數(shù)據(jù)。
(2) 我們這里自己添加了一個(gè)隨機(jī)裁剪的數(shù)據(jù)增強(qiáng)操作,對(duì)于裁剪類(lèi)操作,標(biāo)簽圖也需要在同樣的裁剪參數(shù)下進(jìn)行變換,對(duì)于顏色類(lèi)操作則不需要。更多常見(jiàn)的數(shù)據(jù)增強(qiáng)操作可以查看下文:
【技術(shù)綜述】深度學(xué)習(xí)中的數(shù)據(jù)增強(qiáng)方法都有哪些?
接下來(lái)我們就可以來(lái)測(cè)試一下數(shù)據(jù)集的讀取,分別使用簡(jiǎn)單的方法和Dataloader接口。
## 簡(jiǎn)單方法,根據(jù)index來(lái)讀取某一條數(shù)據(jù)
filetxt = "data/train.txt"
imagesize = 256
cropsize = 224
mydataset = SegDataset(filetxt,imagesize,cropsize)
print(mydataset.__length__())
image,label = mydataset.__getitem__(0)?
## 使用DataLoader來(lái)遍歷,相關(guān)接口我們上期已經(jīng)講過(guò),不再贅述
from torch.utils.data import DataLoader
batchsize = 64
train_dataset = SegDataset(train_data_path,imagesize,cropsize,data_transform)
train_dataloader = DataLoader(train_dataset, batch_size=batchsize, shuffle=True)
確認(rèn)數(shù)據(jù)讀取沒(méi)有問(wèn)題后,我們就可以開(kāi)始來(lái)準(zhǔn)備模型以及訓(xùn)練了。
?
4 模型訓(xùn)練
得到了數(shù)據(jù)之后,接下來(lái)咱們使用Pytorch框架來(lái)進(jìn)行模型的訓(xùn)練,包括模型定義、結(jié)果保存與分析。
4.1?模型定義
接下來(lái)我們定義分割模型,首先是若干卷積層,然后是若干反卷積層。
import torch
from torch import nn
class simpleNet5(nn.Module):
? ? def __init__(self):
? ? ? ? super(simpleNet5, self).__init__()
? ? ? ? self.conv1 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1),
? ? ? ? ? ? nn.BatchNorm2d(32),
? ? ? ? ? ? nn.ReLU(True),
? ? ? ? )
? ? ? ? self.conv2 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1),
? ? ? ? ? ? nn.BatchNorm2d(64),
? ? ? ? ? ? nn.ReLU(True),
? ? ? ? )
? ? ? ? self.conv3 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1),
? ? ? ? ? ? nn.BatchNorm2d(128),
? ? ? ? ? ? nn.ReLU(True),
? ? ? ? )
? ? ? ? self.conv4 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1),
? ? ? ? ? ? nn.BatchNorm2d(256),
? ? ? ? ? ? nn.ReLU(True),
? ? ? ? )
? ? ? ? self.conv5 = nn.Sequential(
? ? ? ? ? ? nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1),
? ? ? ? ? ? nn.BatchNorm2d(512),
? ? ? ? ? ? nn.ReLU(True),
? ? ? ? )
? ? ? ? self.deconv1 = nn.Sequential(
? ? ? ? ? ? nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=3,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?stride=2, padding=1, output_padding=1),
? ? ? ? ? ? nn.BatchNorm2d(256),
? ? ? ? ? ? nn.ReLU(True)
? ? ? ? )
? ? ? ? self.deconv2 = nn.Sequential(
? ? ? ? ? ? nn.ConvTranspose2d(256, 128, 3, 2, 1, 1),
? ? ? ? ? ? nn.BatchNorm2d(128),
? ? ? ? ? ? nn.ReLU(True)
? ? ? ? )
? ? ? ? self.deconv3 = nn.Sequential(
? ? ? ? ? ? nn.ConvTranspose2d(128, 64, 3, 2, 1, 1),
? ? ? ? ? ? nn.BatchNorm2d(64),
? ? ? ? ? ? nn.ReLU(True)
? ? ? ? )
? ? ? ? self.deconv4 = nn.Sequential(
? ? ? ? ? ? nn.ConvTranspose2d(64, 32, 3, 2, 1, 1),
? ? ? ? ? ? nn.BatchNorm2d(32),
? ? ? ? ? ? nn.ReLU(True)
? ? ? ? )
? ? ? ? self.deconv5 = nn.Sequential(
? ? ? ? ? ? nn.ConvTranspose2d(32, 16, 3, 2, 1, 1),
? ? ? ? ? ? nn.BatchNorm2d(16),
? ? ? ? ? ? nn.ReLU(True)
? ? ? ? )
? ? ? ? self.classifier = nn.Conv2d(16, 3, kernel_size=1)
? ? 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.deconv1(out)?
? ? ? ? out = self.deconv2(out)
? ? ? ? out = self.deconv3(out)
? ? ? ? out = self.deconv4(out)
? ? ? ? out = self.deconv5(out)
? ? ? ? out = self.classifier(out)
? ? ? ? return out
if __name__ == '__main__':
? ? img = torch.randn(2, 3, 224, 224)
? ? net = simpleNet5()
? ? sample = net(img)
? ? print(sample.shape)
在這里我們需要知道的是轉(zhuǎn)置卷積的API為nn.ConvTranspose2d(in_channels,out_channels,kernel_size,stride=1, padding=0, output_padding=0,groups=1, bias=True, dilation=1),其中各個(gè)參數(shù)大家可以去查API說(shuō)明。
卷積的具體配置如下:
反卷積層的具體配置如下:
4.2?訓(xùn)練
數(shù)據(jù)集接口準(zhǔn)備好之后我們進(jìn)行訓(xùn)練,完整代碼如下。
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.autograd import Variable
import torchvision
from torchvision import datasets, models, transforms
import time
import os
from net import simpleNet5
from dataset import SegDataset
from tensorboardX import SummaryWriter
from torch.utils.data import DataLoader
import numpy as np
writer = SummaryWriter() #可視化
batchsize = 64
epochs = 200
imagesize = 256 #縮放圖片大小
cropsize = 224 #訓(xùn)練圖片大小
train_data_path = 'data/train.txt' #訓(xùn)練數(shù)據(jù)集
val_data_path = 'data/val.txt' #驗(yàn)證數(shù)據(jù)集
# 數(shù)據(jù)預(yù)處理
data_transform = transforms.Compose([
? ? ? ? ? ? transforms.ToTensor(),
? ? ? ? ? ? transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])])
# 圖像分割數(shù)據(jù)集
train_dataset = SegDataset(train_data_path,imagesize,cropsize,data_transform)
train_dataloader = DataLoader(train_dataset, batch_size=batchsize, shuffle=True)
val_dataset = SegDataset(val_data_path,imagesize,cropsize,data_transform)
val_dataloader = DataLoader(val_dataset, batch_size=val_dataset.__len__(), shuffle=True)
image_datasets = {}
image_datasets['train'] = train_dataset
image_datasets['val'] = val_dataset
dataloaders = {}
dataloaders['train'] = train_dataloader
dataloaders['val'] = val_dataloader
# 定義網(wǎng)絡(luò),優(yōu)化目標(biāo),優(yōu)化方法
device = torch.device('cpu')
net = simpleNet5().to(device)
criterion = nn.CrossEntropyLoss() #使用softmax loss損失,輸入label是圖片
optimizer = optim.SGD(net.parameters(), lr=1e-1, momentum=0.9)
scheduler = lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.1) #每50個(gè)epoch,學(xué)習(xí)率衰減
if not os.path.exists('checkpoints'):
? ? os.mkdir('checkpoints')
for epoch in range(1, epochs+1):
? ? print('Epoch {}/{}'.format(epoch, epochs - 1))
? ? for phase in ['train', 'val']:
? ? ? ? if phase == 'train':
? ? ? ? ? ? scheduler.step()
? ? ? ? ? ? net.train(True)? # Set model to training mode
? ? ? ? else:
? ? ? ? ? ? net.train(False)? # Set model to evaluate mode
? ? ? ? running_loss = 0.0
? ? ? ? running_accs = 0.0
? ? ? ? n = 0
? ? ? ? for data in dataloaders[phase]:
? ? ? ? ? ? imgs, labels = data
? ? ? ? ? ? img, label = imgs.to(device).float(), labels.to(device).float()
? ? ? ? ? ? output = net(img)
? ? ? ? ? ? loss = criterion(output, label.long()) #得到損失
? ? ? ? ? ? output_mask = output.cpu().data.numpy().copy()
? ? ? ? ? ? output_mask = np.argmax(output_mask, axis=1)
? ? ? ? ? ? y_mask = label.cpu().data.numpy().copy()
? ? ? ? ? ? acc = (output_mask == y_mask) #計(jì)算精度
? ? ? ? ? ? acc = acc.mean()
? ? ? ? ? ? optimizer.zero_grad()
? ? ? ? ? ? if phase == 'train':
? ? ? ? ? ? # 梯度置0,反向傳播,參數(shù)更新
? ? ? ? ? ? ? ? loss.backward()
? ? ? ? ? ? ? ? optimizer.step()
? ? ? ? ? ? running_loss += loss.data.item()
? ? ? ? ? ? running_accs += acc
? ? ? ? ? ? n += 1
? ? ? ? epoch_loss = running_loss / n
? ? ? ? epoch_acc = running_accs / n
? ? ? ? if phase == 'train':
? ? ? ? ? ? writer.add_scalar('data/trainloss', epoch_loss, epoch)
? ? ? ? ? ? writer.add_scalar('data/trainacc', epoch_acc, epoch)
? ? ? ? ? ? print('train epoch_{} loss='+str(epoch_loss).format(epoch))
? ? ? ? ? ? print('train epoch_{} acc='+str(epoch_acc).format(epoch))
? ? ? ? else:
? ? ? ? ? ? writer.add_scalar('data/valloss', epoch_loss, epoch)
? ? ? ? ? ? writer.add_scalar('data/valacc', epoch_acc, epoch)
? ? ? ? ? ? print('val epoch_{} loss='+str(epoch_loss).format(epoch))
? ? ? ? ? ? print('val epoch_{} acc='+str(epoch_acc).format(epoch))
? ? if epoch % 10 == 0:
? ? ? ? torch.save(net, 'checkpoints/model_epoch_{}.pth'.format(epoch))
? ? ? ? print('checkpoints/model_epoch_{}.pth saved!'.format(epoch))
writer.export_scalars_to_json("./all_scalars.json")
writer.close()
在上面代碼中,我們使用了帶動(dòng)量項(xiàng)的SGD作為優(yōu)化方法,配置學(xué)習(xí)率為0.1,使用了CrossEntropy損失作為優(yōu)化目標(biāo),相關(guān)代碼如下:
criterion = nn.CrossEntropyLoss() #使用softmax loss損失,輸入label是圖片
optimizer = optim.SGD(net.parameters(), lr=1e-1, momentum=0.9)
scheduler = lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.1) #每50個(gè)epoch,學(xué)習(xí)率衰減
另外我們使用了tensorboardX工具來(lái)進(jìn)行可視化,工具使用我們之前已經(jīng)介紹過(guò)了,大家可以去查看之前圖像分類(lèi)的內(nèi)容,訓(xùn)練的結(jié)果如下:
5 模型測(cè)試
上面已經(jīng)訓(xùn)練好了模型,我們接下來(lái)的目標(biāo),就是要用它來(lái)做推理,真正把模型用起來(lái),下面我們載入一個(gè)圖片,用模型進(jìn)行測(cè)試。
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.autograd import Variable
import torchvision
from torchvision import datasets, models, transforms
import time
import os
import cv2
import sys
import torch.nn.functional as F
import numpy as np
data_transforms =? transforms.Compose([
? ? ? ? ? ? transforms.ToTensor(),
? ? ? ? ? ? transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])])
modelpath = sys.argv[1] #模型目錄
net = torch.load(modelpath,map_location='cpu')
net.eval() #設(shè)置為推理模式,不會(huì)更新模型的k,b參數(shù)
imagepaths = os.listdir(sys.argv[2]) #測(cè)試圖片目錄
torch.no_grad() #停止autograd模塊的工作,加速和節(jié)省顯存
for imagepath in imagepaths:
? ? image = cv2.imread(os.path.join(sys.argv[2],imagepath)) #讀取圖像
? ? image = cv2.resize(image,(224,224),interpolation=cv2.INTER_NEAREST)
? ? imgblob = data_transforms(image).unsqueeze(0) #填充維度,從3維到4維
? ? predict = F.softmax(net(imgblob)).cpu().data.numpy().copy() #獲得原始網(wǎng)絡(luò)輸出,多通道
? ? predict = np.argmax(predict, axis=1) #得到單通道label
? ? result = np.squeeze(predict) #降低維度,從4維到3維
? ? print(np.max(result))?
? ? result = (result*127).astype(np.uint8) #灰度拉伸,方便可視化
? ? resultimage = image.copy()
? ? for y in range(0,result.shape[0]):?
? ? ? ? for x in range(0,result.shape[1]):
? ? ? ? ? ? if result[y][x] == 127:
? ? ? ? ? ? ? ? resultimage[y][x] = (0,0,255)?
? ? ? ? ? ? elif result[y][x] == 254:
? ? ? ? ? ? ? ? resultimage[y][x] = (0,255,255)?
? ? combineresult = np.concatenate([image,resultimage],axis=1)
? ? cv2.imwrite(os.path.join(sys.argv[3],imagepath),combineresult) #寫(xiě)入新的目錄
從上面的代碼可知,使用torch.load函數(shù)載入模型,然后讀取圖像,進(jìn)行與訓(xùn)練相同的預(yù)處理操作,就可以得到網(wǎng)絡(luò)輸出,再進(jìn)行一些維度變換和softmax操作就得到最終的結(jié)果。
下面展示了一些分割結(jié)果。
從圖中我們可以看到,總體的分割結(jié)果還是不錯(cuò)的,不過(guò)本次的任務(wù)還有許多可以提升的空間,包括但不限于:(1) 做更多的數(shù)據(jù)增強(qiáng)。(2) 改進(jìn)模型。這些就留給讀者去進(jìn)行實(shí)驗(yàn)。
然后就可以自己輸入圖片得到推理結(jié)果,index就是預(yù)測(cè)的類(lèi)別。
?6 資源獲取和拓展學(xué)習(xí)
本文的完整代碼,可以在我們的開(kāi)源項(xiàng)目中獲取,項(xiàng)目地址如下。
https://github.com/longpeng2008/yousan.ai
由于數(shù)據(jù)集較大,如果想要獲得數(shù)據(jù)集,請(qǐng)到有三AI知識(shí)星球中下載,當(dāng)然你也完全可以將其替換成自己的數(shù)據(jù)集:
有三AI知識(shí)星球鏈接與介紹如下:
【雜談】有三AI知識(shí)星球指導(dǎo)手冊(cè)出爐!和公眾號(hào)相比又有哪些內(nèi)容?
如果想要學(xué)習(xí)更多圖像分割的內(nèi)容,請(qǐng)關(guān)注下面的視頻課程。
【視頻課】圖像分割最新內(nèi)容來(lái)了(言有三新錄制6大理論部分+1個(gè)案例實(shí)踐講解)
總結(jié)
本次我們完成了一個(gè)圖像分割任務(wù)項(xiàng)目的全部流程,如果小伙伴還記得上一次的表情分類(lèi)任務(wù),那么就可以接著在上一次任務(wù)的基礎(chǔ)上將其做成分割任務(wù),得到本文開(kāi)頭的gif展示結(jié)果。
轉(zhuǎn)載文章請(qǐng)后臺(tái)聯(lián)系
侵權(quán)必究
往期文章
【CV夏季劃】2021年有三AI-CV夏季劃出爐,沖刺秋招,從CV基礎(chǔ)到模型優(yōu)化徹底掌握
【CV秋季劃】模型優(yōu)化很重要,如何循序漸進(jìn)地學(xué)習(xí)好?
【CV秋季劃】人臉?biāo)惴敲炊?#xff0c;如何循序漸進(jìn)地學(xué)習(xí)好?
【CV秋季劃】圖像質(zhì)量提升與編輯有哪些研究和應(yīng)用,如何循序漸進(jìn)地學(xué)習(xí)好?
【CV秋季劃】生成對(duì)抗網(wǎng)絡(luò)GAN有哪些研究和應(yīng)用,如何循序漸進(jìn)地學(xué)習(xí)好?
總結(jié)
以上是生活随笔為你收集整理的【CV实战】年轻人的第一个深度学习图像分割项目应该是什么样的(Pytorch框架)?...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【CV夏季划】2021年有三AI-CV夏
- 下一篇: 【视频课】深度学习必备基础,如何使用好数