快速入门PyTorch(3)--训练一个图片分类器和多 GPUs 训练
2019 第?44?篇,總第?68?篇文章
本文大約14000字,建議收藏閱讀
快速入門 PyTorch 教程前兩篇文章:
快速入門Pytorch(1)--安裝、張量以及梯度
快速入門PyTorch(2)--如何構建一個神經網絡
這是快速入門 PyTorch?的第三篇教程也是最后一篇教程,這次將會在 CIFAR10 數據集上簡單訓練一個圖片分類器,將會簡單實現一個分類器從網絡定義、數據處理和加載到訓練網絡模型,最后測試模型性能的流程。以及如何使用多 GPUs 訓練網絡模型。
本文的目錄如下:
4. 訓練分類器
上一節介紹了如何構建神經網絡、計算?loss?和更新網絡的權值參數,接下來需要做的就是實現一個圖片分類器。
4.1 訓練數據
在訓練分類器前,當然需要考慮數據的問題。通常在處理如圖片、文本、語音或者視頻數據的時候,一般都采用標準的 Python 庫將其加載并轉成 Numpy 數組,然后再轉回為 PyTorch 的張量。
對于圖像,可以采用?Pillow, OpenCV?庫;
對于語音,有?scipy?和?librosa;
對于文本,可以選擇原生 Python 或者 Cython 進行加載數據,或者使用?NLTK?和?SpaCy?。
PyTorch 對于計算機視覺,特別創建了一個?torchvision?的庫,它包含一個數據加載器(data loader),可以加載比較常見的數據集,比如?Imagenet, CIFAR10, MNIST?等等,然后還有一個用于圖像的數據轉換器(data transformers),調用的庫是?torchvision.datasets?和?torch.utils.data.DataLoader?。
在本教程中,將采用?CIFAR10?數據集,它包含 10 個類別,分別是飛機、汽車、鳥、貓、鹿、狗、青蛙、馬、船和卡車。數據集中的圖片都是?3x32x32。一些例子如下所示:
4.2 訓練圖片分類器
訓練流程如下:
通過調用?torchvision?加載和歸一化?CIFAR10?訓練集和測試集;
構建一個卷積神經網絡;
定義一個損失函數;
在訓練集上訓練網絡;
在測試集上測試網絡性能。
4.2.1 加載和歸一化 CIFAR10
首先導入必須的包:
import?torch import?torchvision import?torchvision.transforms?as?transformstorchvision?的數據集輸出的圖片都是?PILImage?,即取值范圍是?[0, 1]?,這里需要做一個轉換,變成取值范圍是?[-1, 1]?, 代碼如下所示:
#?將圖片數據從?[0,1]?歸一化為?[-1,?1]?的取值范圍 transform?=?transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,?0.5,?0.5),?(0.5,?0.5,?0.5))])trainset?=?torchvision.datasets.CIFAR10(root='./data',?train=True,download=True,?transform=transform) trainloader?=?torch.utils.data.DataLoader(trainset,?batch_size=4,shuffle=True,?num_workers=2)testset?=?torchvision.datasets.CIFAR10(root='./data',?train=False,download=True,?transform=transform) testloader?=?torch.utils.data.DataLoader(testset,?batch_size=4,shuffle=False,?num_workers=2)classes?=?('plane',?'car',?'bird',?'cat','deer',?'dog',?'frog',?'horse',?'ship',?'truck')這里下載好數據后,可以可視化部分訓練圖片,代碼如下:
import?matplotlib.pyplot?as?plt import?numpy?as?np#?展示圖片的函數 def?imshow(img):img?=?img?/?2?+?0.5?????#?非歸一化npimg?=?img.numpy()plt.imshow(np.transpose(npimg,?(1,?2,?0)))plt.show()#?隨機獲取訓練集圖片 dataiter?=?iter(trainloader) images,?labels?=?dataiter.next()#?展示圖片 imshow(torchvision.utils.make_grid(images)) #?打印圖片類別標簽 print('?'.join('%5s'?%?classes[labels[j]]?for?j?in?range(4)))展示圖片如下所示:
其類別標簽為:
?frog?plane???dog??ship4.2.2 構建一個卷積神經網絡
這部分內容其實直接采用上一節定義的網絡即可,除了修改?conv1?的輸入通道,從 1 變為 3,因為這次接收的是 3 通道的彩色圖片。
import?torch.nn?as?nn import?torch.nn.functional?as?Fclass?Net(nn.Module):def?__init__(self):super(Net,?self).__init__()self.conv1?=?nn.Conv2d(3,?6,?5)self.pool?=?nn.MaxPool2d(2,?2)self.conv2?=?nn.Conv2d(6,?16,?5)self.fc1?=?nn.Linear(16?*?5?*?5,?120)self.fc2?=?nn.Linear(120,?84)self.fc3?=?nn.Linear(84,?10)def?forward(self,?x):x?=?self.pool(F.relu(self.conv1(x)))x?=?self.pool(F.relu(self.conv2(x)))x?=?x.view(-1,?16?*?5?*?5)x?=?F.relu(self.fc1(x))x?=?F.relu(self.fc2(x))x?=?self.fc3(x)return?xnet?=?Net()4.2.3 定義損失函數和優化器
這里采用類別交叉熵函數和帶有動量的 SGD 優化方法:
import?torch.optim?as?optimcriterion?=?nn.CrossEntropyLoss() optimizer?=?optim.SGD(net.parameters(),?lr=0.001,?momentum=0.9)4.2.4 訓練網絡
第四步自然就是開始訓練網絡,指定需要迭代的 epoch,然后輸入數據,指定次數打印當前網絡的信息,比如?loss?或者準確率等性能評價標準。
import?time start?=?time.time() for?epoch?in?range(2):running_loss?=?0.0for?i,?data?in?enumerate(trainloader,?0):#?獲取輸入數據inputs,?labels?=?data#?清空梯度緩存optimizer.zero_grad()outputs?=?net(inputs)loss?=?criterion(outputs,?labels)loss.backward()optimizer.step()#?打印統計信息running_loss?+=?loss.item()if?i?%?2000?==?1999:#?每?2000?次迭代打印一次信息print('[%d,?%5d]?loss:?%.3f'?%?(epoch?+?1,?i+1,?running_loss?/?2000))running_loss?=?0.0 print('Finished?Training!?Total?cost?time:?',?time.time()-start)這里定義訓練總共 2 個 epoch,訓練信息如下,大概耗時為 77s。
[1,??2000]?loss:?2.226 [1,??4000]?loss:?1.897 [1,??6000]?loss:?1.725 [1,??8000]?loss:?1.617 [1,?10000]?loss:?1.524 [1,?12000]?loss:?1.489 [2,??2000]?loss:?1.407 [2,??4000]?loss:?1.376 [2,??6000]?loss:?1.354 [2,??8000]?loss:?1.347 [2,?10000]?loss:?1.324 [2,?12000]?loss:?1.311Finished?Training!?Total?cost?time:??77.246967554092414.2.5 測試模型性能
訓練好一個網絡模型后,就需要用測試集進行測試,檢驗網絡模型的泛化能力。對于圖像分類任務來說,一般就是用準確率作為評價標準。
首先,我們先用一個?batch?的圖片進行小小測試,這里?batch=4?,也就是 4 張圖片,代碼如下:
dataiter?=?iter(testloader) images,?labels?=?dataiter.next()#?打印圖片 imshow(torchvision.utils.make_grid(images)) print('GroundTruth:?',?'?'.join('%5s'?%?classes[labels[j]]?for?j?in?range(4)))圖片和標簽分別如下所示:
GroundTruth:????cat??ship??ship?plane然后用這四張圖片輸入網絡,看看網絡的預測結果:
#?網絡輸出 outputs?=?net(images)#?預測結果 _,?predicted?=?torch.max(outputs,?1) print('Predicted:?',?'?'.join('%5s'?%?classes[predicted[j]]?for?j?in?range(4)))輸出為:
Predicted:????cat??ship??ship??ship前面三張圖片都預測正確了,第四張圖片錯誤預測飛機為船。
接著,讓我們看看在整個測試集上的準確率可以達到多少吧!
correct?=?0 total?=?0 with?torch.no_grad():for?data?in?testloader:images,?labels?=?dataoutputs?=?net(images)_,?predicted?=?torch.max(outputs.data,?1)total?+=?labels.size(0)correct?+=?(predicted?==?labels).sum().item()print('Accuracy?of?the?network?on?the?10000?test?images:?%d?%%'?%?(100?*?correct?/?total))輸出結果如下
Accuracy?of?the?network?on?the?10000?test?images:?55?%這里可能準確率并不一定一樣,教程中的結果是?51%?,因為權重初始化問題,可能多少有些浮動,相比隨機猜測 10 個類別的準確率(即 10%),這個結果是不錯的,當然實際上是非常不好,不過我們僅僅采用 5 層網絡,而且僅僅作為教程的一個示例代碼。
然后,還可以再進一步,查看每個類別的分類準確率,跟上述代碼有所不同的是,計算準確率部分是?c = (predicted == labels).squeeze(),這段代碼其實會根據預測和真實標簽是否相等,輸出 1 或者 0,表示真或者假,因此在計算當前類別正確預測數量時候直接相加,預測正確自然就是加 1,錯誤就是加 0,也就是沒有變化。
class_correct?=?list(0.?for?i?in?range(10)) class_total?=?list(0.?for?i?in?range(10)) with?torch.no_grad():for?data?in?testloader:images,?labels?=?dataoutputs?=?net(images)_,?predicted?=?torch.max(outputs,?1)c?=?(predicted?==?labels).squeeze()for?i?in?range(4):label?=?labels[i]class_correct[label]?+=?c[i].item()class_total[label]?+=?1for?i?in?range(10):print('Accuracy?of?%5s?:?%2d?%%'?%?(classes[i],?100?*?class_correct[i]?/?class_total[i]))輸出結果,可以看到貓、鳥、鹿是錯誤率前三,即預測最不準確的三個類別,反倒是船和卡車最準確。
Accuracy?of?plane?:?58?% Accuracy?of???car?:?59?% Accuracy?of??bird?:?40?% Accuracy?of???cat?:?33?% Accuracy?of??deer?:?39?% Accuracy?of???dog?:?60?% Accuracy?of??frog?:?54?% Accuracy?of?horse?:?66?% Accuracy?of??ship?:?70?% Accuracy?of?truck?:?72?%4.3 在 GPU 上訓練
深度學習自然需要 GPU 來加快訓練速度的。所以接下來介紹如果是在 GPU 上訓練,應該如何實現。
首先,需要檢查是否有可用的 GPU 來訓練,代碼如下:
device?=?torch.device("cuda:0"?if?torch.cuda.is_available()?else?"cpu") print(device)輸出結果如下,這表明你的第一塊 GPU 顯卡或者唯一的 GPU 顯卡是空閑可用狀態,否則會打印?cpu?。
cuda:0既然有可用的 GPU ,接下來就是在 GPU 上進行訓練了,其中需要修改的代碼如下,分別是需要將網絡參數和數據都轉移到 GPU 上:
net.to(device) inputs,?labels?=?inputs.to(device),?labels.to(device)修改后的訓練部分代碼:
import?time #?在?GPU?上訓練注意需要將網絡和數據放到?GPU?上 net.to(device) criterion?=?nn.CrossEntropyLoss() optimizer?=?optim.SGD(net.parameters(),?lr=0.001,?momentum=0.9)start?=?time.time() for?epoch?in?range(2):running_loss?=?0.0for?i,?data?in?enumerate(trainloader,?0):#?獲取輸入數據inputs,?labels?=?datainputs,?labels?=?inputs.to(device),?labels.to(device)#?清空梯度緩存optimizer.zero_grad()outputs?=?net(inputs)loss?=?criterion(outputs,?labels)loss.backward()optimizer.step()#?打印統計信息running_loss?+=?loss.item()if?i?%?2000?==?1999:#?每?2000?次迭代打印一次信息print('[%d,?%5d]?loss:?%.3f'?%?(epoch?+?1,?i+1,?running_loss?/?2000))running_loss?=?0.0 print('Finished?Training!?Total?cost?time:?',?time.time()?-?start)注意,這里調用?net.to(device)?后,需要定義下優化器,即傳入的是 CUDA 張量的網絡參數。訓練結果和之前的類似,而且其實因為這個網絡非常小,轉移到 GPU 上并不會有多大的速度提升,而且我的訓練結果看來反而變慢了,也可能是因為我的筆記本的 GPU 顯卡問題。
如果需要進一步提升速度,可以考慮采用多 GPUs,也就是下一小節的內容。
本小節教程:
https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html
本小節的代碼:
https://github.com/ccc013/DeepLearning_Notes/blob/master/Pytorch/practise/train_classifier_example.ipynb
5. 數據并行
這部分教程將學習如何使用?DataParallel?來使用多個 GPUs 訓練網絡。
首先,在 GPU 上訓練模型的做法很簡單,如下代碼所示,定義一個?device?對象,然后用?.to()?方法將網絡模型參數放到指定的 GPU 上。
device?=?torch.device("cuda:0") model.to(device)接著就是將所有的張量變量放到 GPU 上:
mytensor?=?my_tensor.to(device)注意,這里?my_tensor.to(device)?是返回一個?my_tensor?的新的拷貝對象,而不是直接修改?my_tensor?變量,因此你需要將其賦值給一個新的張量,然后使用這個張量。
Pytorch 默認只會采用一個 GPU,因此需要使用多個 GPU,需要采用?DataParallel?,代碼如下所示:
model?=?nn.DataParallel(model)這代碼也就是本節教程的關鍵,接下來會繼續詳細介紹。
5.1 導入和參數
首先導入必須的庫以及定義一些參數:
import?torch import?torch.nn?as?nn from?torch.utils.data?import?Dataset,?DataLoader#?Parameters?and?DataLoaders input_size?=?5 output_size?=?2batch_size?=?30 data_size?=?100device?=?torch.device("cuda:0"?if?torch.cuda.is_available()?else?"cpu")這里主要定義網絡輸入大小和輸出大小,batch?以及圖片的大小,并定義了一個?device?對象。
5.2 構建一個假數據集
接著就是構建一個假的(隨機)數據集。實現代碼如下:
class?RandomDataset(Dataset):def?__init__(self,?size,?length):self.len?=?lengthself.data?=?torch.randn(length,?size)def?__getitem__(self,?index):return?self.data[index]def?__len__(self):return?self.lenrand_loader?=?DataLoader(dataset=RandomDataset(input_size,?data_size),batch_size=batch_size,?shuffle=True)5.3 簡單的模型
接下來構建一個簡單的網絡模型,僅僅包含一層全連接層的神經網絡,加入?print()?函數用于監控網絡輸入和輸出?tensors?的大小:
class?Model(nn.Module):#?Our?modeldef?__init__(self,?input_size,?output_size):super(Model,?self).__init__()self.fc?=?nn.Linear(input_size,?output_size)def?forward(self,?input):output?=?self.fc(input)print("\tIn?Model:?input?size",?input.size(),"output?size",?output.size())return?output5.4 創建模型和數據平行
這是本節的核心部分。首先需要定義一個模型實例,并且檢查是否擁有多個 GPUs,如果是就可以將模型包裹在?nn.DataParallel?,并調用?model.to(device)?。代碼如下:
model?=?Model(input_size,?output_size) if?torch.cuda.device_count()?>?1:print("Let's?use",?torch.cuda.device_count(),?"GPUs!")#?dim?=?0?[30,?xxx]?->?[10,?...],?[10,?...],?[10,?...]?on?3?GPUsmodel?=?nn.DataParallel(model)model.to(device)5.5 運行模型
接著就可以運行模型,看看打印的信息:
for?data?in?rand_loader:input?=?data.to(device)output?=?model(input)print("Outside:?input?size",?input.size(),"output_size",?output.size())輸出如下:
In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([5,?5])?output?size?torch.Size([5,?2])In?Model:?input?size?torch.Size([5,?5])?output?size?torch.Size([5,?2]) Outside:?input?size?torch.Size([10,?5])?output_size?torch.Size([10,?2])5.6 運行結果
如果僅僅只有 1 個或者沒有 GPU ,那么?batch=30?的時候,模型會得到輸入輸出的大小都是 30。但如果有多個 GPUs,那么結果如下:
2 GPUs
#?on?2?GPUs Let's?use?2?GPUs!In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2])In?Model:?input?size?torch.Size([15,?5])?output?size?torch.Size([15,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([5,?5])?output?size?torch.Size([5,?2])In?Model:?input?size?torch.Size([5,?5])?output?size?torch.Size([5,?2]) Outside:?input?size?torch.Size([10,?5])?output_size?torch.Size([10,?2])3 GPUs
Let's?use?3?GPUs!In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2])In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2])In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2])In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2])In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2])In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2])In?Model:?input?size?torch.Size([10,?5])?output?size?torch.Size([10,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2]) Outside:?input?size?torch.Size([10,?5])?output_size?torch.Size([10,?2])8 GPUs
Let's?use?8?GPUs!In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([4,?5])?output?size?torch.Size([4,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2]) Outside:?input?size?torch.Size([30,?5])?output_size?torch.Size([30,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2])In?Model:?input?size?torch.Size([2,?5])?output?size?torch.Size([2,?2]) Outside:?input?size?torch.Size([10,?5])?output_size?torch.Size([10,?2])5.7 總結
DataParallel?會自動分割數據集并發送任務給多個 GPUs 上的多個模型。然后等待每個模型都完成各自的工作后,它又會收集并融合結果,然后返回。
更詳細的數據并行教程:
https://pytorch.org/tutorials/beginner/former_torchies/parallelism_tutorial.html
本小節教程:
https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html
小結
第三篇主要是簡單實現了一個圖像分類的流程,選擇數據集,構建網絡模型,定義損失函數和優化方法,訓練網絡,測試網絡性能,并檢查每個類別的準確率,當然這只是很簡單的過一遍流程。
然后就是使用多 GPUs 訓練網絡的操作。
接下來你可以選擇:
訓練一個神經網絡來玩視頻游戲:https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html
在 imagenet 上訓練 ResNet:https://github.com/pytorch/examples/tree/master/imagenet
采用 GAN 訓練一個人臉生成器:https://github.com/pytorch/examples/tree/master/dcgan
采用循環 LSTM 網絡訓練一個詞語級別的語言模型:https://github.com/pytorch/examples/tree/master/word_language_model
更多的例子:https://github.com/pytorch/examples
更多的教程:https://pytorch.org/tutorials
在 Forums 社區討論 PyTorch:https://discuss.pytorch.org/
歡迎關注我的微信公眾號--機器學習與計算機視覺,或者掃描下方的二維碼,大家一起交流,學習和進步!
往期精彩推薦
機器學習系列
初學者的機器學習入門實戰教程!
模型評估、過擬合欠擬合以及超參數調優方法
常用機器學習算法匯總比較(完)
常用機器學習算法匯總比較(上)
機器學習入門系列(2)--如何構建一個完整的機器學習項目(一)
特征工程之數據預處理(上)
來了解下計算機視覺的八大應用
Github項目 & 資源教程推薦
[Github 項目推薦] 一個更好閱讀和查找論文的網站
[資源分享] TensorFlow 官方中文版教程來了
必讀的AI和深度學習博客
[教程]一份簡單易懂的 TensorFlow 教程
[資源]推薦一些Python書籍和教程,入門和進階的都有!
[Github項目推薦] 機器學習& Python 知識點速查表
[Github項目推薦] 推薦三個助你更好利用Github的工具
Github上的各大高校資料以及國外公開課視頻
這些單詞你都念對了嗎?順便推薦三份程序員專屬英語教程!
總結
以上是生活随笔為你收集整理的快速入门PyTorch(3)--训练一个图片分类器和多 GPUs 训练的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里的26款大神级的Java开源项目
- 下一篇: pb利用pdf虚拟打印机将datawin