从零开始pytorch手写字母识别
因為研一的人工智能大作業-手寫字母識別,在學習之余,綜合一些文章和代碼實現了本文,針對數據集Chars74K dataset。
數據集介紹:
(1)、數據集來源于Chars74K dataset,本項目選用數據集EnglishFnt中的一部分。Chars74K dataset網址鏈接 http://www.ee.surrey.ac.uk/CVSSP/demos/chars74k/;
(2)、A-Z共26種英文字母,每種字母對應一個文件夾(Sample011對應字母A, Sample012對應字母B,…, Sample036對應字母Z);
(3)、Sample011到Sample036每個文件夾下相同字母不同字體的圖片約1000張,PNG格式;
(4)、本項目數據集請從以下鏈接下載:
https://pan.baidu.com/s/1HEsbvusyYCni7MVGKUk4bA, 提取碼:dhix
要求:
1.每種字母當成一類,利用卷積神經元網絡構建26類分類器;
2.每個類別中隨機選擇80%作為訓練數據集,剩余20%作為測試數據集。采用訓練集進行模型訓練,采用測試集進行模型測試,并給出測試集準確率結果。
Bonus:
1、Bonus文件夾下為手寫A-Z的字母圖片。請將之前訓練好的分類器遷移學習到Bonus數據集上,重新構建分類器,Bonus數據集中隨機選擇80%作為訓練數據集,剩余20%作為測試數據集,并給出測試集準確率結果。
2、將Bonus文件夾下的圖片當作未標注類別的數據,聯合之前的標注圖片,采用半監督學習的方法構建分類器。
其它。
前置知識體系
目前學習的稍微的前置知識
-
安裝虛擬環境
-
安裝pytorch
-
…等一系列前置工作
-
python 基礎語法
- 函數
- 類
- pandas庫等
-
卷積神經網絡基礎 —可見機器學習 -吳恩達-yyq
- 卷積
- 池化
- 全連接
-
pytorch 的基礎使用
-
關于pytorch 對于數據的預處理
-
關于pytroch cnn網絡的構建
pytorch步驟
一、前言
在我們要用pytorch構建自己的深度學習模型的時候,基本上都是下面這個流程步驟,寫在這里讓一些新手童鞋學習的時候有一個大局感覺,無論是從自己寫,還是閱讀他人代碼,按照這個步驟思想(默念4大步驟,
- 找數據定義、
- 找model定義、(找損失函數、優化器定義),
- 主循環代碼邏輯,
- 直接去找對應的代碼塊,會簡單很多。
二、基本步驟思想
所有的深度學習模型過程都可以形式化如下圖:
分為四大步驟:
1、輸入處理模塊 (X 輸入數據,變成網絡能夠處理的Tensor類型)
- 進行預處理 input - dataset - dataloader
2、模型構建模塊 (主要負責從輸入的數據,得到預測的y^, 這就是我們經常說的前向過程)
3、定義代價函數和優化器模塊 (注意,前向過程只會得到模型預測的結果,并不會自動求導和更新,是由這個模塊進行處理)
4、構建訓練過程 (迭代訓練過程,就是上圖表情包的訓練迭代過程)
這幾個模塊分別與上圖的數字標號1,2,3,4進行一一對應!
三、實例講解
知道了上面的宏觀思想之后,后面給出每個模塊稍微具體一點的解釋和具體一個例子,再幫助大家熟悉對應的代碼!
1.數據處理
對于數據處理,最為簡單的?式就是將數據組織成為?個 。但許多訓練需要?到mini-batch,直 接組織成Tensor不便于我們操作。pytorch為我們提供了Dataset和Dataloader兩個類來方便的構建。
torch.utils.data.Dataset
繼承Dataset 類需要override 以下?法:
torch.utils.data.DataLoader
torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False)DataLoader Batch。如果選擇shuffle = True,每?個epoch 后,mini-Batch batch_size 常?的使??法如下:
2. 模型構建
所有的模型都需要繼承torch.nn.Module , 需要實現以下?法:
其中forward() ?法是前向傳播的過程。在實現模型時,我們不需要考慮反向傳播。
3. 定義代價函數和優化器
這部分根據??的需求去參照doc
4、構建訓練過程
pytorch的訓練循環?致如下:
下面再用一個簡單例子,來鞏固一下:
slides來自https://www.bilibili.com/video/BV1Y7411d7Ys?from=search&seid=3765076366663992699
slides來自https://www.bilibili.com/video/BV1Y7411d7Ys?from=search&seid=3765076366663992699
slides來自https://www.bilibili.com/video/BV1Y7411d7Ys?from=search&seid=3765076366663992699
slides來自https://www.bilibili.com/video/BV1Y7411d7Ys?from=search&seid=3765076366663992699
數據集預處理
文件處理
針對文件夾中都是圖片的數據集處理
例如 : data文件夾內包含26個文件夾,分別包含a,b,c,d…各種相關圖片
利用這些圖片做出自己的數據集
-
./data/A/a_0.jpg
-
./data/A/a_1.jpg
-
./data/A/a_2.jpg
-
…
-
./data/B/b_0.jpg
-
./data/B/b_1.jpg
-
…
生成train.txt和test.txt 如下圖 地址與標簽相對應
!!!!!
自己踩得坑,自己解決,數據集預處理問題,先獲取總的數據,打亂,在獲取訓練集和測試集
import os import random''' 處理文件夾中的圖片,并自動分類'''# 定義訓練集和數據集比例 # 訓練集 0.8 # 測試集 0.2 train_ratio = 0.8 test_ratio = 1 - train_ratio# 定義文件路徑 root_path = "./data" DataList = []# 定義訓練列表 trainData_list = [] # 定義測試列表 testData_list = []# 為什么flag=-1 因為第一輪for循環獲取了root路徑下的文件夾,并沒有獲取文件 flag = -1 for root, dirs, files in os.walk(root_path):# 每輪掃描獲得路徑和文件列表# 獲取該輪文件的長度# root 也會隨之改變length = files.__len__()for i in range(0, length):img_path = os.path.join(root, files[i]) + "\t" + str(flag) + "\n"DataList.append(img_path)flag = flag + 1; #打亂數據集 random.shuffle(DataList) length = len(DataList) print(length) print(DataList)for i in range(0, int(length * train_ratio)):trainData_list.append(DataList[i]) for i in range(int(length * train_ratio), length):testData_list.append(DataList[i])# 對列表打亂次序with open("./res/train.txt", "w", encoding="utf-8") as f:for data in trainData_list:f.write(data)with open("./res/test.txt", "w", encoding="utf-8") as f:for data in testData_list:f.write(data)主要使用的函數 os.walk(rootdata)
舉例 :
讀取rootdata=./data
for root,dirs,files in os.walk(root_path):第一輪 :
- root = ./data
- dirs = [sample011…sample038 ]
- files = [] #因為data目錄下沒有文件
第二輪 :
- root = ./data/Sample011
- dirs = [] #因為Sample011里面沒有文件夾
- files = [ a.jpg…a100.jpg ]
第三輪
- root = ./data/Sample012
- dirs = [] #因為Sample012里面沒有文件夾
- files = [ b.jpg…b100.jpg ]
…
DataSet
DataSet需要被繼承
- 實現 __ init __(self)
- 構造器 提前生成一些數據或者獲取一些數據
- 比如 imgPaths列表 [ [imagesPath,label],[imagesPath,label],[imagesPath,label]… ]
- train.txt文件路徑
- 構造器 提前生成一些數據或者獲取一些數據
- 實現 __ getitem __ (self, index)
- 獲取第index號的數據和標簽
- 使用transforms 轉化為–tensor
- 實現 __ len __(self)
- 獲取數據的長度
其中用Dataset實現的類可以直接看作列表使用
[ [ x , y ],[ x , y ] ,[ x , y ], [ x , y ] … ]
- 獲取 x y
- x,y = myDataset[0]
- x=myDataset[0] [0]
- y=myDataset[0] [1]
- 獲取長度
- myDataset.__ len __
知識點
- self 相當于java的this , self.data 為類中的全局變量
- transformer.Compose 注意使用的順序
- PIL -> tensor ->…
DataLoader
#使用DataLoadertrain_dataloader = DataLoader(dataset=train_Dataset, num_workers=4, pin_memory=True, batch_size=batch_size,shuffle=True)test_dataloader = DataLoader(dataset=test_Dataset, num_workers=4, pin_memory=True, batch_size=batch_size,shuffle=True)# 使用Tensorboard --查看每步存放的圖片writer = SummaryWriter("logs")i=1#這里imgs.shape -> (10-照片個數,3-通道數,128-H,128-W)for data in train_dataloader:imgs,label=dataprint(imgs.shape)print(label)#這里是add_imges!!!!writer.add_images("test-dataloader",imgs,i)i+=iTensorBoard
–port 可以修改端口號
from torch.utils.tensorboard import SummaryWriter from PIL import Image import numpy as np writer = SummaryWriter("logs") imgepath=r"D:\機器學習\pytorch\數據預處理\dataset\不導電\不導電20180830131551對照樣本.jpg" img=Image.open(imgepath) img=np.array(img) #參數 tag名稱 tensor ndarray writer.add_image("test",img,2,dataformats="HWC") # y = 2x for i in range(100):writer.add_scalar("y=2x",2*i, i)writer.close()常用的語句
- writer = SummaryWriter(“logs”)
- logs代表文件夾
- writer.add_image(“test”,img,2,dataformats=“HWC”)
- tag 名稱
- img 圖片數據 需要是tensor narray 類型
- dataformats 需要是 hwc
- H 高度 w寬度 c 通道
- writer.add_scalar(“y=2x”,2*i, i)
- 畫圖嘛
- tag
- y
- x
- writer.close()
控制行執行指令
tensorboard --logdir=./study/logs --port=6000DONE!
搭建卷積神經網絡
預訓練模型地址
C:\Users\yyq\.cache\torch\hub\checkpoints可以手動下載放到那里即可
參考搭建網絡-1
上圖少寫了兩個全連接層
- 64@4×4 -Flatten-> 1024 -FC-> 64 -FC-> 10
所有的模型都需要繼承torch.nn.Module , 需要實現以下?法:
其中forward() ?法是前向傳播的過程。在實現模型時,我們不需要考慮反向傳播。
這里使用到的Api
- Sequential
- Module
- nn.Conv2d
- nn.MaxPool2d
- nn.Linear
- SummaryWriter -from torch.utils.tensorboard import SummaryWriter
- 實現 3@ 32 * 32 分類-10卷積神經網絡
參考搭建網絡 -2
此模型用于字母識別-26
參考地址:https://www.cnblogs.com/Liu-xing-wu/p/14770473.html
但是輸入的圖片尺寸不同,所以做了修改 輸入改為 128
網絡結構
實現代碼
import torch from torch import nn from torch.nn import Sequential from torch.utils.tensorboard import SummaryWriterclass yyq_module(nn.Module):def __init__(self):super(yyq_module, self).__init__()self.module=Sequential(#3 * 128 * 128nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5 , padding="same"),nn.BatchNorm2d(16),nn.ReLU(),nn.MaxPool2d(kernel_size=2),#16 * 64 * 64nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5, padding="same"),nn.BatchNorm2d(32),nn.ReLU(),nn.MaxPool2d(kernel_size=2),#32 *32 *32nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding="same"),nn.BatchNorm2d(32),nn.ReLU(),nn.MaxPool2d(kernel_size=2),#32 * 16 * 16nn.Flatten(),# #隱藏層 兩個線性層 即全連接層nn.Linear(in_features=8192,out_features=400),nn.Dropout(p=0.5),nn.ReLU(),nn.Linear(in_features=400, out_features=80),nn.ReLU(),nn.Linear(80, 26))def forward(self,input):output = self.module(input)return output#測試一下網絡 module=yyq_module() x=torch.zeros((64,3,128,128)) print(x.shape) y=module(x) print(module) print(y.shape)網絡模型的修改
- 修改方法一 : 最后加一層全連接
- 修改方法二 :直接在最后一層修改
模型的保存和加載
保存模型
import torch import torchvision from torch import nnvgg16 = torchvision.models.vgg16(pretrained=False) # 保存方式1,模型結構+模型參數 torch.save(vgg16, "vgg16_method1.pth")# 保存方式2,模型參數(官方推薦) torch.save(vgg16.state_dict(), "vgg16_method2.pth")# 陷阱 class Tudui(nn.Module):def __init__(self):super(Tudui, self).__init__()self.conv1 = nn.Conv2d(3, 64, kernel_size=3)def forward(self, x):x = self.conv1(x)return xtudui = Tudui() torch.save(tudui, "tudui_method1.pth")加載模型
import torch from model_save import * # 方式1-》保存方式1,加載模型 # 坑是需要導入定義的模型那個類 from model_save import * import torchvision from torch import nnmodel = torch.load("vgg16_method1.pth") # print(model)# 方式2,加載模型 vgg16 = torchvision.models.vgg16(pretrained=False) vgg16.load_state_dict(torch.load("vgg16_method2.pth")) # model = torch.load("vgg16_method2.pth") # print(vgg16)# 陷阱1 # class Tudui(nn.Module): # def __init__(self): # super(Tudui, self).__init__() # self.conv1 = nn.Conv2d(3, 64, kernel_size=3) # # def forward(self, x): # x = self.conv1(x) # return xmodel = torch.load('tudui_method1.pth') print(model)使用GPU
可以使用GPU的
- 網絡模型
- 損失函數
- 數據(輸入,標注)
- .cuda()
- .to(device)
方法一
if torch.cuda.is_is_available():module = module.cuda()lossFun = lossFun.cuda()imgs = imgs.cuda()tagerts = tagerts.cuda() import torchvision from torch.utils.data import DataLoader from module import * from torch.utils.tensorboard import SummaryWriter import datetime import time start_time = time.time() # 導入數據集 train_dataset = torchvision.datasets.CIFAR10("../study/data", train=True, transform=torchvision.transforms.ToTensor(),download=True) test_dataset = torchvision.datasets.CIFAR10("../study/data", train=False, transform=torchvision.transforms.ToTensor(),download=True) # 數據集長度 train_len=len(train_dataset) test_len=len(test_dataset)print("訓練集數據集長度{}".format(len(train_dataset))) print("測試集數據集長度{}".format(len(test_dataset)))# 創建dataLoader train_dataloader=DataLoader(train_dataset,batch_size=64, shuffle=True) test_dataloader=DataLoader(test_dataset,batch_size=64, shuffle=True)# 引入模型 module=yyq_module() module = module.cuda()# 定義損失函數 lossFun = torch.nn.CrossEntropyLoss() lossFun = lossFun.cuda() # 學習率 learning_rate=1e-2 # 定義優化器 optim = torch.optim.SGD(module.parameters(), lr=learning_rate)# 訓練輪數 每一輪是對整個數據集的一次遍歷 epoch = 10 # 圖像化 指定文件夾 ./yyq/logs writer = SummaryWriter("./logs") # 總的訓練次數 total_train_num = 0 total_test_num = 0 for i in range(0,epoch):# 定義訓練次數total_train_step = 0total_test_step = 0# 訓練for data in train_dataloader:imgs, tagerts = dataimgs = imgs.cuda()tagerts = tagerts.cuda()outputs = module(imgs)loss = lossFun(outputs,tagerts)# 優化器優化optim.zero_grad()loss.backward()optim.step()total_train_step = total_train_step+1total_train_num = total_train_num+1if total_train_step%100==0:print(str(time.time()-start_time)+"s")print("訓練次數:{}, Loss:{}".format(total_train_step, loss))writer.add_scalar("train_loss", loss, total_train_num)# 訓練集的總損失total_test_loss = 0# 預測正確次數total_accuracy = 0# 測試# 不需要梯度 不要更新參數with torch.no_grad():for data in test_dataloader:imgs, targets = dataimgs = imgs.cuda()tagerts = tagerts.cuda()outputs = module(imgs)loss = lossFun(outputs, targets)# 總損失total_test_loss = total_test_loss+loss# 計算準確率accuracy = (outputs.argmax(1) == targets).sum()total_accuracy = total_accuracy + accuracy# 總測試次數total_test_num=total_test_num+1writer.add_scalar("test_loss", loss, total_test_num)print("整體測試集上AvgLoss: {}".format(total_test_loss / len(test_dataloader)))print("整體測試集上的Accuracy: {}%".format(100*total_accuracy / test_len))writer.add_scalar("test_accuracy", 100*total_accuracy / test_len, i)writer.close() torch.save(module, "module_{}_{}.pth".format(epoch, 20211117))方法二
注意:
模型和損失函數可以直接to(device) 而不重新賦值,但是數據必須重新賦值
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print("device:"+device.type) # 引入模型 module=yyq_module() module.to(device)# 定義損失函數 lossFun = torch.nn.CrossEntropyLoss() lossFun.to(device)# 訓練for data in train_dataloader:imgs, tagerts = dataimgs = imgs.to(device)tagerts = tagerts.to(device) import torchvision from torch.utils.data import DataLoader from module import * from torch.utils.tensorboard import SummaryWriter import datetime import time start_time = time.time() device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print("device:"+device.type) # 導入數據集 train_dataset = torchvision.datasets.CIFAR10("../study/data", train=True, transform=torchvision.transforms.ToTensor(),download=True) test_dataset = torchvision.datasets.CIFAR10("../study/data", train=False, transform=torchvision.transforms.ToTensor(),download=True) # 數據集長度 train_len=len(train_dataset) test_len=len(test_dataset)print("訓練集數據集長度{}".format(len(train_dataset))) print("測試集數據集長度{}".format(len(test_dataset)))# 創建dataLoader train_dataloader=DataLoader(train_dataset,batch_size=64, shuffle=True) test_dataloader=DataLoader(test_dataset,batch_size=64, shuffle=True)# 引入模型 module=yyq_module() module.to(device)# 定義損失函數 lossFun = torch.nn.CrossEntropyLoss() lossFun.to(device) # 學習率 learning_rate=1e-2 # 定義優化器 optim = torch.optim.SGD(module.parameters(), lr=learning_rate)# 訓練輪數 每一輪是對整個數據集的一次遍歷 epoch = 10 # 圖像化 指定文件夾 ./yyq/logs writer = SummaryWriter("./logs") # 總的訓練次數 total_train_num = 0 total_test_num = 0 for i in range(0,epoch):# 定義訓練次數total_train_step = 0total_test_step = 0# 訓練for data in train_dataloader:imgs, targets = dataimgs = imgs.to(device)targets = targets.to(device)outputs = module(imgs)loss = lossFun(outputs, targets)# 優化器優化optim.zero_grad()loss.backward()optim.step()total_train_step = total_train_step+1total_train_num = total_train_num+1if total_train_step%100==0:print(str(time.time()-start_time)+"s")print("訓練次數:{}, Loss:{}".format(total_train_step, loss))writer.add_scalar("train_loss", loss, total_train_num)# 訓練集的總損失total_test_loss = 0# 預測正確次數total_accuracy = 0# 測試# 不需要梯度 不要更新參數with torch.no_grad():for data in test_dataloader:imgs, targets = dataimgs = imgs.to(device)targets = targets.to(device)outputs = module(imgs)loss = lossFun(outputs, targets)# 總損失total_test_loss = total_test_loss+loss# 計算準確率accuracy = (outputs.argmax(1) == targets).sum()total_accuracy = total_accuracy + accuracy# 總測試次數total_test_num=total_test_num+1writer.add_scalar("test_loss", loss, total_test_num)print("整體測試集上AvgLoss: {}".format(total_test_loss / len(test_dataloader)))print("整體測試集上的Accuracy: {}%".format(100*total_accuracy / test_len))writer.add_scalar("test_accuracy", 100*total_accuracy / test_len, i)writer.close() torch.save(module, "module_{}_{}.pth".format(epoch, 20211117))各種網絡小問題
注意原來模型的輸入Size
比如原來的圖像尺寸128 * 128
self.tf = transforms.Compose([#嘗試灰度化# transforms.Grayscale(num_output_channels=1), # 彩色圖像轉灰度圖像num_output_channels默認1transforms.Resize((224,224)),transforms.ToTensor(),transforms.Normalize(mean=[0.5, 0.5, 0.5], # 取決于數據集std=[0.5, 0.5, 0.5])])圖片與模型通道數不同
比如:resnet默認輸入尺寸為224X224,三維圖片,但是想輸入的數據集圖片尺寸是32X32,以為圖片
我們可以在處理尺寸大小時在預處理的地方將其resize為32X32,transforms.Resize(224)
然后在使用resnet之前用一次1X1網絡修改圖片通道,conv = nn.Conv2d(1, 3, kernel_size=1)即可傳入
在
resnet之前加一個
優化器和loss函數-反向傳播
損失函數
這部分根據??的需求去參照doc
loss = nn.CrossEntropyLoss()for data in dataloader:imgs,target=dataoutput=module(imgs)result_loss=loss(output,target)result_loss.backward()#進行反向傳播 算出每個參數的梯度 利用優化器去調整參數print(result_loss)優化器的使用
#創建優化器 optimizer=SGD(module.parameters(), lr=0.01) for epoch in range(10):for data in dataloader:imgs,target=dataoutput=module(imgs)result_loss=loss(output, target)# 梯度值清零optimizer.zero_grad()# 計算出新的梯度值result_loss.backward()# 優化參數optimizer.step()print("epoch:" + str(epoch))print(result_loss)添加優化器損失函數后的完整訓練網絡
#優化器 import torch import torchvision from torch import nn from torch.nn import Sequential from torch.optim import SGD from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriterdataset = torchvision.datasets.CIFAR10("./data", train=False, transform=torchvision.transforms.ToTensor(),download=True) dataloader=DataLoader(dataset,batch_size=64, shuffle=True)class yyq_module(nn.Module):def __init__(self):super(yyq_module, self).__init__()self.module=Sequential(nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5 , padding="same"),nn.MaxPool2d(kernel_size=2),nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding="same"),nn.MaxPool2d(kernel_size=2),nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding="same"),nn.MaxPool2d(kernel_size=2),nn.Flatten(),#隱藏層 兩個線性層 即全連接層nn.Linear(in_features=1024,out_features=64),nn.Linear(in_features=64, out_features=10))def forward(self,input):output=self.module(input)return output module = yyq_module() loss = nn.CrossEntropyLoss() optimizer=SGD(module.parameters(), lr=0.01) for epoch in range(10):for data in dataloader:imgs,target=dataoutput=module(imgs)result_loss=loss(output, target)# 梯度值清零optimizer.zero_grad()# 計算出新的梯度值result_loss.backward()# 優化參數optimizer.step()print("epoch:" + str(epoch))print(result_loss)實例 基于CIFAR10數據集的卷積神經網絡
- 數據集 CIFAR10
模型代碼
import torch from torch import nn from torch.nn import Sequentialclass yyq_module(nn.Module):def __init__(self):super(yyq_module, self).__init__()self.module=Sequential(nn.Conv2d(in_channels=3, out_channels=32, kernel_size=5 , padding="same"),nn.MaxPool2d(kernel_size=2),nn.Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding="same"),nn.MaxPool2d(kernel_size=2),nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding="same"),nn.MaxPool2d(kernel_size=2),nn.Flatten(),#隱藏層 兩個線性層 即全連接層nn.Linear(in_features=1024,out_features=64),nn.Linear(in_features=64, out_features=10))def forward(self,input):output=self.module(input)return outputif __name__ == '__main__':# 測試網絡module=yyq_module()input=torch.zeros((64,3,32,32))output=module(input)print(output.shape)訓練代碼-CPU
這里目前是使用cpu進行訓練…
- 更新使用GPU代碼在 搭建卷積神經網絡 -使用GPU章節中
module.train()使用與否看模型中是否有–>官方文檔
module.train() This has any effect only on certain modules. See documentations of particular modules for details of their behaviors in training/evaluation mode, if they are affected, e.g. Dropout, BatchNorm, etc.module.eval() This has any effect only on certain modules. See documentations of particular modules for details of their behaviors in training/evaluation mode, if they are affected, e.g. Dropout, BatchNorm, etc.測試模型
實例 手寫字母識別
圖像文件處理
import os import random''' 處理文件夾中的圖片,并自動分類'''# 定義訓練集和數據集比例 # 訓練集 0.8 # 測試集 0.2 train_ratio = 0.8 test_ratio = 1 - train_ratio# 定義文件路徑 root_path = "./data"# 定義訓練列表 trainData_list = [] # 定義測試列表 testData_list = []# 為什么flag=-1 因為第一輪for循環獲取了root路徑下的文件夾,并沒有獲取文件 flag = -1 for root, dirs, files in os.walk(root_path):print(flag)# 每輪掃描獲得路徑和文件列表# 獲取該輪文件的長度# root 也會隨之改變length = files.__len__()for i in range(0, int(length * train_ratio)):# 拼接 root路徑和文件名 加上分隔符 和標簽值img_path = os.path.join(root, files[i]) + "\t" + str(flag) + "\n"trainData_list.append(img_path)for i in range(int(length * train_ratio), length):img_path = os.path.join(root, files[i]) + "\t" + str(flag) + "\n"testData_list.append(img_path)flag = flag + 1; print(trainData_list)# 對列表打亂次序 random.shuffle(trainData_list)with open("./res/train.txt", "w", encoding="utf-8") as f:for data in trainData_list:f.write(data)with open("./res/test.txt", "w", encoding="utf-8") as f:for data in testData_list:f.write(data)DataSetAndDataLoader
import torch from torch.utils.data import Dataset from torch.utils.data import DataLoader import torchvision.transforms as transforms from torch.utils.tensorboard import SummaryWriter from PIL import Image import numpy as np# 數據歸一化與標準化 # 圖像標準化class Mydataset(Dataset):def getImgInfo(self):imginfo = []with open(self.textpath, "r", encoding="utf-8") as f:img_str = f.readlines()# map( func , list[]) 相當于利用function對list中每個元素進行操作 返回值為函數結果# 這里返回的是一個列表# 參考:https://blog.csdn.net/qq_29666899/article/details/88623026# list()# lambda# list( map(lambda x: x * x, [y for y in range(3)]) )imginfo = list(map(lambda x: x.strip().split("\t"), img_str))return imginfo# 構造器self相當于java-this# 其引用的為全局變量def __init__(self, textpath):# 文件路徑self.textpath = textpath# 獲取圖片list(-list[data,label]----)集合self.imgInfo = self.getImgInfo()# 定義transforms# 需要輸入 PIL img -> tensor -> ..self.tf = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[0.5, 0.5, 0.5], # 取決于數據集std=[0.5, 0.5, 0.5])])# 獲取第 index 的 數據 標簽def __getitem__(self, index):img_path, label = self.imgInfo[index]img = Image.open(img_path)img = img.convert('RGB')data = self.tf(img)lable = int(label)return data, labledef __len__(self):return len(self.imgInfo)if __name__ == '__main__':# 一次傳多少個照片batch_size = 10train_Dataset = Mydataset("./res/train.txt")test_Dataset = Mydataset("./res/test.txt")print(len(train_Dataset))print(len(test_Dataset))訓練
from dataLoader import Mydataset from torch.utils.data.dataloader import DataLoader import torchvision.models as models from torch.utils.tensorboard import SummaryWriter import torch import time import osos.environ["CUDA_VISIBLE_DEVICES"] = "0" start_time = time.time() # 設備 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print("device use {}".format(device))"""構建自己的數據集 """ batchSize = 32 # dataset trainDataSet = Mydataset(textpath="./res/train.txt") testDataSet = Mydataset(textpath="./res/test.txt") train_len = len(trainDataSet) test_len = len(testDataSet) print("訓練集大小{}".format(train_len)) print("測試集大小{}".format(test_len))# 導入dataLoader trainDataLoader = DataLoader(dataset=trainDataSet, batch_size=batchSize, shuffle=True) testDataLoader = DataLoader(dataset=testDataSet, batch_size=batchSize, shuffle=True)"""創建網絡-修改vgg16網絡 """ vgg16 = models.vgg16(pretrained=True, progress=True) # vgg16 classifier多一層全連接 1000 - 26 vgg16.classifier.add_module("7", torch.nn.Linear(in_features=1000, out_features=26, bias=True)) # GPU vgg16.to(device) print("網絡結構") print(vgg16)"""定義損失函數 """ lossFun = torch.nn.CrossEntropyLoss() lossFun.to(device)"""定義優化器 """ learning_rate = 1e-2 optim = torch.optim.SGD(params=vgg16.parameters(), lr=learning_rate)"""訓練 """ # 訓練輪數 每一輪是對整個數據集的一次遍歷 epoch = 10 # 圖像化 指定文件夾 ./logs writer = SummaryWriter("./logs") # 總的訓練次數 total_train_num = 0 total_test_num = 0 for i in range(0, epoch):print("開始第{}輪-epoch".format(i+1))# 定義訓練次數total_train_step = 0total_test_step = 0# 訓練for data in trainDataLoader:imgs, tagerts = dataimgs = imgs.to(device)tagerts = tagerts.to(device)outputs = vgg16(imgs)loss = lossFun(outputs, tagerts)# 優化器優化optim.zero_grad()loss.backward()optim.step()total_train_step = total_train_step + 1total_train_num = total_train_num + 1if total_train_step % 100 == 0:print("訓練次數:{}/{}, Loss:{}".format(total_train_step*batchSize, train_len, loss))writer.add_scalar("train_loss", loss, total_train_num)# 訓練集的總損失total_test_loss = 0# 預測正確次數total_accuracy = 0# 測試# 不需要梯度 不要更新參數with torch.no_grad():for data in testDataLoader:imgs, targets = dataimgs = imgs.to(device)targets = targets.to(device)outputs = vgg16(imgs)loss = lossFun(outputs, targets)# 總損失total_test_loss = total_test_loss + loss# 計算準確率accuracy = (outputs.argmax(1) == targets).sum()total_accuracy = total_accuracy + accuracy# 總測試次數total_test_num = total_test_num + 1writer.add_scalar("test_loss", loss, total_test_num)print("整體測試集上AvgLoss: {}".format(total_test_loss / len(testDataLoader)))print("整體測試集上的Accuracy: {}%".format(100 * total_accuracy / test_len))writer.add_scalar("test_accuracy", 100 * total_accuracy / test_len, i+1)end_time = time.time()print("第{}輪-epoch-用時{:.2f}".format(i+1, end_time-start_time))start_time = end_timetorch.save(vgg16.state_dict(), "vgg16_dict_module_{}.pth".format(i))writer.close()測試模型
import torch import torchvision.models as models from dataLoader import Mydataset from torch.utils.data.dataloader import DataLoader from module import yyq_module"""加載數據 """testDataSet = Mydataset(textpath="./res/test.txt") testDataLoader = DataLoader(dataset=testDataSet, batch_size=16, pin_memory=True) test_len = len(testDataSet)"""加載網絡 """yyq = yyq_module()# path = "yyq_dict_module_ep4_ac93.42.pth" path = "./模型/yyq_dict_module_ep42_ac99.32.pth"yyq.load_state_dict(torch.load(path),strict=False)yyq.cuda() print(yyq)"""開始測試 """ sum_acc = 0.0yyq.eval() with torch.no_grad():for data in testDataLoader:imgs, targets = dataimgs = imgs.cuda()targets = targets.cuda()outputs = yyq(imgs)print(outputs.argmax(1))sum=(outputs.argmax(1) == targets).sum()sum_acc = sum_acc+sum print("準確率:{:.4f}%".format(sum_acc/test_len*100)) import torch import torchvision.models as models from dataLoader import Mydataset from torch.utils.data.dataloader import DataLoader"""加載數據 """testDataSet = Mydataset(textpath="./res/test.txt") testDataLoader = DataLoader(dataset=testDataSet, batch_size=16, pin_memory=True) test_len = len(testDataSet)"""加載網絡 """vgg16 = models.vgg16(pretrained=False) vgg16.classifier.add_module("7", torch.nn.Linear(in_features=1000, out_features=26, bias=True)) vgg16.load_state_dict(torch.load("vgg16_dict_module_0.pth")) vgg16.cuda() print(vgg16)"""開始測試 """ sum_acc = 0.0 vgg16.eval() with torch.no_grad():for data in testDataLoader:imgs, targets = dataimgs = imgs.cuda()targets = targets.cuda()outputs = vgg16(imgs)sum=(outputs.argmax(1) == targets).sum()sum_acc = sum_acc+sum print("準確率:{:.2f}".format(sum_acc/test_len))Bonus-1
bonus-1任務
-
任務1 -測試原先模型在該數據集的準確率
-
任務2 -遷移學習- 把之前訓練好的模型 用在bonus數據集上,接著進行訓練,查看訓練后模型在bonus測試集的準確率
任務一
預處理
這里不用對數據集進行劃分,只要輸出一個文本文件包含測試所需要的全部信息就行了。
import os import random''' 處理文件夾中的圖片,并自動分類''' # 定義文件路徑 root_path = "./data"# 定義測試列表 testData_list = []# 為什么flag=-1 因為第一輪for循環獲取了root路徑下的文件夾,并沒有獲取文件 flag = -1 for root, dirs, files in os.walk(root_path):print(flag)# 每輪掃描獲得路徑和文件列表# 獲取該輪文件的長度# root 也會隨之改變length = files.__len__()for i in range(0, length):img_path = os.path.join(root, files[i]) + "\t" + str(flag) + "\n"testData_list.append(img_path)flag = flag + 1;# 對列表打亂次序with open("./res/test.txt", "w", encoding="utf-8") as f:for data in testData_list:f.write(data)DataSetAndDataLoader
這里需要對dataSet修改,修改圖片size
import torch from torch.utils.data import Dataset from torch.utils.data import DataLoader import torchvision.transforms as transforms from torch.utils.tensorboard import SummaryWriter from PIL import Image import numpy as np# 數據歸一化與標準化 # 圖像標準化class Mydataset(Dataset):def getImgInfo(self):imginfo = []with open(self.textpath, "r", encoding="utf-8") as f:img_str = f.readlines()# map( func , list[]) 相當于利用function對list中每個元素進行操作 返回值為函數結果# 這里返回的是一個列表# 參考:https://blog.csdn.net/qq_29666899/article/details/88623026# list()# lambda# list( map(lambda x: x * x, [y for y in range(3)]) )imginfo = list(map(lambda x: x.strip().split("\t"), img_str))return imginfo# 構造器self相當于java-this# 其引用的為全局變量def __init__(self, textpath):# 文件路徑self.textpath = textpath# 獲取圖片list(-list[data,label]----)集合self.imgInfo = self.getImgInfo()# 定義transforms# 需要輸入 PIL img -> tensor -> ..self.tf = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[0.5, 0.5, 0.5], # 取決于數據集std=[0.5, 0.5, 0.5])])# 獲取第 index 的 數據 標簽def __getitem__(self, index):img_path, label = self.imgInfo[index]img = Image.open(img_path)img = img.convert('RGB')data = self.tf(img)lable = int(label)return data, labledef __len__(self):return len(self.imgInfo)if __name__ == '__main__':test_Dataset = Mydataset("./res/test.txt")print(len(test_Dataset))測試模型準確率
找不到代碼了 簡述:1 加載數據集2 加載模型3 跑模型,并統計正確率4 輸出正確率任務二
與上面源數據集c處理方式雷同不在贅述
Bonus-2
目標: 利用半監督學習,聯合源數據集和bonus數據集,訓練一個新的模型
step 1:利用源數據集訓練的模型,得到bonus數據集的標簽
step 2: 聯合兩個數據集,重新訓練模型
step-1 數據預處理
預處理,獲取所有圖片位置信息,但是假設label = -1
import os import random''' 處理文件夾中的圖片,這里假設標簽都是-1 創建未標記數據集'''# 定義文件路徑 root_path = "../data"# 定義測試列表 testData_list = []# 為什么flag=-1 因為目前不知道label值 flag = -1 for root, dirs, files in os.walk(root_path):print(flag)# 每輪掃描獲得路徑和文件列表# 獲取該輪文件的長度# root 也會隨之改變length = files.__len__()for i in range(0, length):img_path = os.path.join(root, files[i]) + "\t" + str(flag) + "\n"testData_list.append(img_path)# 對列表打亂次序with open("res/alldata.txt", "w", encoding="utf-8") as f:for data in testData_list:f.write(data)獲取數據,利用源數據集訓練的模型獲取bonus數據集的標簽, 并按比例劃分為訓練集和測試集
import torchvision.models as models import torch from torch.utils.data import DataLoader from dataSet import Mydataset from module import yyq_module import random device = torch.device("cuda" if torch.cuda.is_available() else "cpu") """通過半監督學習獲取未標記的數據input : bounsdataoutput : img_path - label """ train_ratio =0.8 test_ratio = 1 - train_ratio train_imgInfoList = [] test_imgInfoList = []dataList = [] """ step 1 : 讀取數據 """ batch_size = 32 textpath = "./res/alldata.txt" allDataset = Mydataset(textpath=textpath) allDataLoader = DataLoader(dataset=allDataset, batch_size=batch_size , pin_memory=True) dataLength = len(allDataset) print("數據集長度{}".format(dataLength))""" step 2 : 讀取模型 """ module_path = "../yyq_dict_module_ep47_ac99.39.pth" yyq = yyq_module() yyq.load_state_dict(torch.load(module_path)) yyq.to(device) print("模型結構") print(yyq)""" step 3 : 讀取數據,并且創建新文件 輸出文件的標簽值 """ yyq.eval() with torch.no_grad():for data in allDataLoader:imgs , img_paths = dataimgs = imgs.to(device)outputs = yyq(imgs)targets = outputs.argmax(1)lenth = len(targets)for i in range(0,lenth):imgInfo = img_paths[i] + "\t" + str(targets[i].item()) + "\n"dataList.append(imgInfo) length = len(dataList) print("datalist長度:{}".format(length)) random.shuffle(dataList) for i in range(0, int( length * train_ratio)):train_imgInfoList.append(dataList[i]) for i in range(int( length * train_ratio), length):test_imgInfoList.append(dataList[i])print(train_imgInfoList[0]) print(test_imgInfoList[0]) with open("DealRes/train.txt", "w", encoding="utf-8") as f:for data in train_imgInfoList:f.write(data) with open("DealRes/test.txt", "w", encoding="utf-8") as f:for data in test_imgInfoList:f.write(data)創建dataset,這里的dataset有所不同,他要獲取 源數據集的train.txt和bonus數據集的train.txt
對兩者進行加和。
import torch from torch.utils.data import Dataset from torch.utils.data import DataLoader import torchvision.transforms as transforms from torch.utils.tensorboard import SummaryWriter from PIL import Image import numpy as np"""這里對數據處理:transforms.Resize((128,128)),transforms.ToTensor(), """class Mydataset(Dataset):def getImgInfo(self):imginfo = []with open(self.textpath, "r", encoding="utf-8") as f:img_str = f.readlines()# map( func , list[]) 相當于利用function對list中每個元素進行操作 返回值為函數結果# 這里返回的是一個列表# 參考:https://blog.csdn.net/qq_29666899/article/details/88623026# list()# lambda# list( map(lambda x: x * x, [y for y in range(3)]) )imginfo = list(map(lambda x: x.strip().split("\t"), img_str))return imginfo# 構造器self相當于java-this# 其引用的為全局變量def __init__(self, textpath):# 文件路徑self.textpath = textpath# 獲取圖片list(-list[data,label]----)集合self.imgInfo = self.getImgInfo()# 定義transforms# 需要輸入 PIL img -> tensor -> ..self.tf = transforms.Compose([transforms.Resize((128,128)),transforms.ToTensor(),transforms.Normalize(mean=[0.5, 0.5, 0.5], # 取決于數據集std=[0.5, 0.5, 0.5])])# 獲取第 index 的 數據 標簽def __getitem__(self, index):img_path, label = self.imgInfo[index]img = Image.open(img_path)img = img.convert('RGB')data = self.tf(img)lable = int(label)return data, img_pathdef __len__(self):return len(self.imgInfo)if __name__ == '__main__':test_Dataset = Mydataset("./res/alldata.txt")print(test_Dataset[0])到這里對數據預處理已經結束。
step -2 訓練模型
大同小異不在贅述
import torch from torch.utils.data import Dataset from torch.utils.data import DataLoader import torchvision.transforms as transforms from torch.utils.tensorboard import SummaryWriter from PIL import Image import numpy as np from torch.utils.data.dataloader import DataLoader import torchvision.models as models from torch.utils.tensorboard import SummaryWriter import torch import time import os from module import yyq_module"""這里對數據處理:transforms.Resize((128,128)),transforms.ToTensor(), """class Mydataset(Dataset):def getImgInfo(self):imginfo = []with open(self.textpath, "r", encoding="utf-8") as f:img_str = f.readlines()# map( func , list[]) 相當于利用function對list中每個元素進行操作 返回值為函數結果# 這里返回的是一個列表# 參考:https://blog.csdn.net/qq_29666899/article/details/88623026# list()# lambda# list( map(lambda x: x * x, [y for y in range(3)]) )imginfo = list(map(lambda x: x.strip().split("\t"), img_str))with open(self.dealedTextPath, "r", encoding="utf-8") as f:img_str = f.readlines()for str in img_str:img=str.split("\t")imginfo.append(img)return imginfo# 構造器self相當于java-this# 其引用的為全局變量def __init__(self, dealedTextPath, textpath):# 文件路徑self.dealedTextPath = dealedTextPathself.textpath = textpath# 獲取圖片list(-list[data,label]----)集合self.imgInfo = self.getImgInfo()# 定義transforms# 需要輸入 PIL img -> tensor -> ..self.tf = transforms.Compose([transforms.Resize((128,128)),transforms.ToTensor(),transforms.Normalize(mean=[0.5, 0.5, 0.5], # 取決于數據集std=[0.5, 0.5, 0.5])])# 獲取第 index 的 數據 標簽def __getitem__(self, index):img_path, label = self.imgInfo[index]img = Image.open(img_path)img = img.convert('RGB')data = self.tf(img)lable = int(label)return data, labledef __len__(self):return len(self.imgInfo)"""訓練模型 """if __name__ == '__main__':os.environ["CUDA_VISIBLE_DEVICES"] = "0"start_time = time.time()# 設備device = torch.device("cuda" if torch.cuda.is_available() else "cpu")print("device use {}".format(device))"""構建自己的數據集"""batchSize = 32# datasettrainDataSet = Mydataset("./DealRes/train.txt","./project2/res/train.txt")testDataSet = Mydataset("./DealRes/test.txt","./project2/res/test.txt")train_len = len(trainDataSet)test_len = len(testDataSet)print("訓練集大小{}".format(train_len))print("測試集大小{}".format(test_len))# 導入dataLoadertrainDataLoader = DataLoader(dataset=trainDataSet, batch_size=batchSize, shuffle=True)testDataLoader = DataLoader(dataset=testDataSet, batch_size=batchSize, shuffle=True)"""創建網絡-修改yyq網絡"""yyq = yyq_module()# GPUyyq.to(device)print("網絡結構")print(yyq)"""定義損失函數"""lossFun = torch.nn.CrossEntropyLoss()lossFun.to(device)"""定義優化器"""learning_rate = 1e-2optim = torch.optim.SGD(params=yyq.parameters(), lr=learning_rate)"""訓練"""# 訓練輪數 每一輪是對整個數據集的一次遍歷epoch = 50# 圖像化 指定文件夾 ./logswriter = SummaryWriter("./logs")# 總的訓練次數total_train_num = 0total_test_num = 0for i in range(0, epoch):print("開始第{}輪-epoch".format(i + 1))# 定義訓練次數total_train_step = 0total_test_step = 0# 訓練yyq.train()for data in trainDataLoader:imgs, tagerts = dataimgs = imgs.to(device)tagerts = tagerts.to(device)outputs = yyq(imgs)loss = lossFun(outputs, tagerts)# 優化器優化optim.zero_grad()loss.backward()optim.step()total_train_step = total_train_step + 1total_train_num = total_train_num + 1if total_train_step % 100 == 0:print("訓練次數:{}/{}, Loss:{}".format(total_train_step * batchSize, train_len, loss))writer.add_scalar("train_loss", loss, total_train_num)# 訓練集的總損失total_test_loss = 0# 預測正確次數total_accuracy = 0# 測試# 不需要梯度 不要更新參數yyq.eval()with torch.no_grad():for data in testDataLoader:imgs, targets = dataimgs = imgs.to(device)targets = targets.to(device)outputs = yyq(imgs)loss = lossFun(outputs, targets)# 總損失total_test_loss = total_test_loss + loss# 計算準確率accuracy = (outputs.argmax(1) == targets).sum()total_accuracy = total_accuracy + accuracy# 總測試次數total_test_num = total_test_num + 1writer.add_scalar("test_loss", loss, total_test_num)print("整體測試集上AvgLoss: {}".format(total_test_loss / len(testDataLoader)))print("整體測試集上的Accuracy: {}%".format(100 * total_accuracy / test_len))writer.add_scalar("test_accuracy", 100 * total_accuracy / test_len, i + 1)end_time = time.time()print("第{}輪-epoch-用時{:.2f}".format(i + 1, end_time - start_time))start_time = end_time# if (100 * total_accuracy / test_len)>99:# torch.save(yyq.state_dict(),# "yyq_dict_module_ep{}_ac{:.2f}.pth".format(i, (100 * total_accuracy / test_len)))writer.close()學習資料
最后附上一些可供學習的資料,強烈推薦土堆B站視頻!
1.PyTorch 深度學習:60分鐘快速入門(官網翻譯)
“PyTorch 深度學習:60分鐘快速入門”為PyTorch官網教程,網上已經有部分翻譯作品,隨著PyTorch1.0版本的公布,這個教程有較大的代碼改動,本人對教程進行重新翻譯,并測試運行了官方代碼,制作成Jupyter Notebook文件(中文注釋)在github予以公布。
本文內容較多,可以在線學習,如果需要本地調試,請到github下載:
https://github.com/fengdu78/Data-Science-Notes/tree/master/8.deep-learning/PyTorch_beginner
此教程為翻譯官方地址:
https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html
**作者:**Soumith Chintala
本教程的目標:
2.土堆github
https://github.com/xiaotudui/pytorch-tutorial/tree/master/src
3.PyTorch 中文手冊(pytorch handbook)(github標星7900+)
資源地址:
https://github.com/zergtant/pytorch-handbook
這是一本開源的書籍,目標是幫助那些希望和使用PyTorch進行深度學習開發和研究的朋友快速入門。我試了一下里面的ipynb代碼,非常全面,值得推薦。
資源目錄:
第一章 :PyTorch入門
第一節 PyTorch 簡介
第二節 PyTorch 環境搭建
第三節 PyTorch 深度學習:60分鐘快速入門(官方)
張量
Autograd:自動求導
神經網絡
訓練一個分類器
選讀:數據并行處理(多GPU)
4.相關資源介紹
第二章 : 基礎
第一節 PyTorch 基礎
張量自動求導神經網絡包nn和優化器optm數據的加載和預處理
第二節 深度學習基礎及數學原理
深度學習基礎及數學原理
第三節 神經網絡簡介
神經網絡簡介
第四節 卷積神經網絡
卷積神經網絡
第五節 循環神經網絡
循環神經網絡
第三章 : 實踐
第一節 logistic回歸
logistic回歸二元分類
第二節 CNN:MNIST數據集手寫數字識別
CNN:MNIST數據集手寫數字識別
第三節 RNN實例:通過Sin預測Cos
RNN實例:通過Sin預測Cos
第四章 : 提高
第一節 Fine-tuning
Fine-tuning
第二節 可視化
visdomtensorboardx可視化理解卷積神經網絡
第三節 Fast.ai
Fast.ai
第五節 多GPU并行訓練
多GPU并行計算
第五章 : 應用
第一節 Kaggle介紹
Kaggle介紹
第二節 結構化數據
第三節 計算機視覺
第四節 自然語言處理
4.Pytorch教程(github標星13600+)
資源地址:
https://github.com/yunjey/pytorch-tutorial
資源介紹:
這個資源為深度學習研究人員提供了學習PyTorch的教程代碼大多數模型都使用少于30行代碼實現。在開始本教程之前,建議先看完Pytorch官方教程。(大部分教程是PyTorch0.4實現的,代碼與1.0+稍微有點不同,總體影響不大)
配置環境:
python 2.7或者3.5以上,pytorch 0.4
資源目錄:
1.基礎知識
- PyTorch基礎知識
- 線性回歸
- Logistic回歸
- 前饋神經網絡
2.中級
- 卷積神經網絡
- 深度殘差網絡
- 遞歸神經網絡
- 雙向遞歸神經網絡
- 語言模型(RNN-LM)
3.高級
- 生成性對抗網絡
- 變分自動編碼器
- 神經風格轉移
- 圖像字幕(CNN-RNN)
4.工具
- PyTorch中的TensorBoard
總結
以上是生活随笔為你收集整理的从零开始pytorch手写字母识别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html-盒子模型及pading和mar
- 下一篇: nuget下载太慢的问题解决方案 多次尝