目錄
1. Pytorch環(huán)境的配置及安裝
如何管理項目環(huán)境?
如何看自己電腦cuda版本?
安裝Pytorch?
2. Python編輯器的選擇、安裝及配置
PyCharm?
PyCharm神器
Jupyter(可交互)?
3. Python學(xué)習(xí)中的兩大法寶函數(shù)
說明?
實戰(zhàn)操作
總結(jié)
4. Pycharm及Jupyter使用及對比
如何在PyCharm中新建項目?
Python控制臺?編輯
如何在Jupyter中新建項目?
三種運行方式(PyCharm、PyCharm的Python控制臺、Jupyter Notebook)的適用場景:
5. PyTorch加載數(shù)據(jù)初認(rèn)識
PyTorch 讀取數(shù)據(jù)涉及兩個類:Dataset & Dataloader
數(shù)據(jù)集的幾種組織形式
Dataset類?
6. Dataset類代碼實戰(zhàn)
兩種方法讀圖片
控制臺讀取&可視化圖片
(一)數(shù)據(jù)格式1?
讀數(shù)據(jù)
(二)數(shù)據(jù)格式2
7. TensorBoard的使用
SummaryWriter類
安裝TensorBoard
add_scalar() 方法的使用
add_image() 的使用
利用numpy.array(),對PIL圖片進(jìn)行轉(zhuǎn)換
8. 圖像變換,torchvision中transforms的使用
transforms的結(jié)構(gòu)及用法
結(jié)構(gòu)
一些類
使用
兩個問題
1、transforms 該如何使用(python)
2、為什么我們需要 Tensor 數(shù)據(jù)類型
兩種讀取圖片的方式
9. 常見的Transforms的使用
Compose 的使用
Python中 __call__ 的用法
ToTensor 的使用
ToPILImage 的使用
Normalize 的使用
Resize() 的使用
Compose() 的使用
RandomCrop() 的使用
總結(jié)使用方法
10. torchvision 中的數(shù)據(jù)集使用
CIFAR10數(shù)據(jù)集
標(biāo)準(zhǔn)數(shù)據(jù)集如何下載、查看、使用
如何把數(shù)據(jù)集(多張圖片)和 transforms 結(jié)合在一起
11. DataLoader 的使用
參數(shù)介紹?
示例?
batch_size
drop_last
shuffle
12. 神經(jīng)網(wǎng)絡(luò)的基本骨架 - nn.Module 的使用
Containers
debug看流程?
13. 土堆說卷積操作
stride(步進(jìn))
要求輸入的維度?& reshape函數(shù)
padding(填充)
14. 神經(jīng)網(wǎng)絡(luò) - 卷積層
kernel_size
in_channels & out_channels
CIFAR10數(shù)據(jù)集實例?
卷積層 vgg16
卷積前后維度計算公式
15. 神經(jīng)網(wǎng)絡(luò) - 最大池化的使用
參數(shù)?
ceil_mode參數(shù)
輸入輸出維度計算公式
代碼實現(xiàn)?
為什么要進(jìn)行最大池化?最大池化的作用是什么?
用數(shù)據(jù)集 CIFAR10 實現(xiàn)最大池化
16. 神經(jīng)網(wǎng)絡(luò) - 非線性激活?
最常見:RELU? ?
代碼舉例:RELU
Sigmoid
代碼舉例:Sigmoid(數(shù)據(jù)集CIFAR10)
關(guān)于inplace
17. 神經(jīng)網(wǎng)絡(luò) - 線性層及其他層介紹?
批標(biāo)準(zhǔn)化層(不難,自學(xué))? ?
Recurrent Layers(特定網(wǎng)絡(luò)中使用,自學(xué))
Transformer Layers(特定網(wǎng)絡(luò)中使用,自學(xué))
Linear Layers(本節(jié)講解)
代碼實例
flatten 攤平
Dropout Layers(不難,自學(xué))
Sparse Layers(特定網(wǎng)絡(luò)中使用,自學(xué))
Embedding
Distance Functions
Loss Functions
pytorch提供的一些網(wǎng)絡(luò)模型
18. 神經(jīng)網(wǎng)絡(luò) - 搭建小實戰(zhàn)和 Sequential 的使用
對 CIFAR10 進(jìn)行分類的簡單神經(jīng)網(wǎng)絡(luò)
直接搭建,實現(xiàn)上圖 CIFAR10 model 的代碼
實際過程中如何檢查網(wǎng)絡(luò)的正確性?
若不知道flatten之后的維度是多少該怎么辦?
用 Sequential?搭建,實現(xiàn)上圖 CIFAR10 model 的代碼
引入 tensorboard 可視化模型結(jié)構(gòu)
19. 損失函數(shù)與反向傳播
L1LOSS
代碼
MSELOSS(均方誤差)
代碼
CROSSENTROPYLOSS(交叉熵)
如何在之前寫的神經(jīng)網(wǎng)絡(luò)中用到 Loss Function(損失函數(shù))
backward? 反向傳播
20. 優(yōu)化器
如何使用優(yōu)化器?
算法
SGD為例?
完整代碼
21. 現(xiàn)有網(wǎng)絡(luò)模型的使用及修改
數(shù)據(jù)集 ImageNet
參數(shù)及下載?
VGG16 模型
參數(shù) pretrained=True/False?
vgg16 網(wǎng)絡(luò)架構(gòu)?
如何利用現(xiàn)有網(wǎng)絡(luò)去改動它的結(jié)構(gòu)?
添加?
修改?
22. 網(wǎng)絡(luò)模型的保存與讀取
兩種方式保存模型
兩種方式加載模型
方式1 有陷阱(自己定義網(wǎng)絡(luò)結(jié)構(gòu),沒有用 vgg16 時)
問題描述?
解決
解決另法:
23. 完整的模型訓(xùn)練套路
model.py 文件代碼
train.py 文件代碼
如何知道模型是否訓(xùn)練好,或達(dá)到需求?
完整代碼
與 TensorBoard 結(jié)合
保存每一輪訓(xùn)練的模型?
正確率的實現(xiàn)(對分類問題)?
train.py 完整代碼
model.train() 和 model.eval()
作用?
回顧案例
24. 利用GPU訓(xùn)練
第一種使用GPU訓(xùn)練的方式
比較CPU/GPU訓(xùn)練時間?
對于CPU
對于GPU
查看GPU信息?
Google Colab
如何使用GPU?
查看GPU配置?
第二種使用GPU訓(xùn)練的方式(更常用)
25. 完整的模型驗證(測試、demo)套路
示例?
test.py(把訓(xùn)練模型運用到實際環(huán)境中)完整代碼
示例二
26. 看看開源項目
README.md?
train.py
訓(xùn)練參數(shù)設(shè)置
1. Pytorch環(huán)境的配置及安裝
首先需要下載Anaconda(包含了大量的package/工具包)
深度學(xué)習(xí)離不開顯卡(TensorFlow/Pytorch支持英偉達(dá)的顯卡),起到訓(xùn)練加速的作用
- 顯卡的配置:驅(qū)動 + CUDA Toolkit(CUDA工具包)
- CUDA Toolkit 會跟隨Pytorch一鍵安裝
- 主要檢查顯卡的驅(qū)動是否正確安裝:任務(wù)管理器—性能—GPU,若能看到GPU的型號,即意味著顯卡的驅(qū)動已經(jīng)正確安裝
如何管理項目環(huán)境?
一個package相當(dāng)于一個工具包,把環(huán)境理解成一個房子,初始環(huán)境base,不同環(huán)境可以有不同版本的工具包,切換環(huán)境即可
conda指令創(chuàng)建pytorch環(huán)境(如果開了代理服務(wù)器,務(wù)必關(guān)閉?。?!)
以下命令在 Anaconda Prompt 中輸入:
conda create -n pytorch python=3.8 # -n是name的意思
conda activate pytorch # 激活環(huán)境
左邊括號里的環(huán)境由base變成了pytorch(環(huán)境名稱)
看一下環(huán)境中的工具包:
pip list
?
并沒有pytorch,接下來:安裝pytorch
網(wǎng)址:PyTorch
如何看自己電腦cuda版本?
- 任務(wù)管理器—性能—GPU
- 或:設(shè)備管理器—顯示適配器
獨立顯卡
在cmd里輸入:
nvidia-smi
安裝Pytorch?
找到自己適合的版本,復(fù)制命令:?
- Stable 穩(wěn)定版:1.1版本以后加入了TensorBoard,可以看到訓(xùn)練過程中的數(shù)據(jù),如損失函數(shù)的變化
- Package:一般Windows下推薦使用Conda,Linux下推薦使用pip
- Compute Platform:
- 無英偉達(dá)顯卡或只有集顯:CPU
- 有英偉達(dá)顯卡:30系列顯卡不支持11.1以下的CUDA
在剛剛激活了pytorch環(huán)境的窗口里輸入:?
conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch
?等待安裝成功之后,再
pip list
可以看到已經(jīng)安裝成功
檢驗安裝是否成功:先輸入
python
再輸入
import torch
若沒有報錯則安裝成功,如圖
檢驗pytorch是否可以用GPU:
torch.cuda.is_available()
返回True即可
2. Python編輯器的選擇、安裝及配置
PyCharm?
PyCharm: the Python IDE for Professional Developers by JetBrains
在Python工作臺/控制臺驗證:
?先輸入
import torch
再輸入
torch.cuda.is_available()
返回True即可,如上圖
PyCharm神器
在左邊輸入,右邊有變量的屬性,很直觀?
---------------------------------------------------------------------------------------------------------------------------------
Jupyter(可交互)?
Project Jupyter | Home
建議直接安裝Anaconda,Jupyter會隨著Anaconda一起安裝
Jupyter 默認(rèn)安裝在 base 環(huán)境中,但是base環(huán)境中沒有安裝 Pytorch,導(dǎo)致Jupyter無法使用Pytorch,兩種解決方式:
- 在base環(huán)境中再安裝一遍Pytorch
- 在Pytorch環(huán)境中安裝Jupyter(選擇這種)
打開Anaconda Prompt是base環(huán)境,從base環(huán)境啟用Jupyter需要一個package:ipykernel,如圖:
輸入
conda list
可以看到要用到的 package是 ipykernel
進(jìn)入Pytorch環(huán)境中:
conda activate pytorch
再輸入
conda list
可以發(fā)現(xiàn)沒有?ipykernel ,安裝這個 package(同樣需要關(guān)閉代理服務(wù)器?。?!)
conda install nb_conda
等到done之后,再輸入
jupyter notebook
?等到頁面跳轉(zhuǎn)到瀏覽器,New — Python[conda env:pytorch]*
?同樣,先輸入
import torch
再輸入
torch.cuda.is_available()
返回True即可,如下圖
意外插曲:
網(wǎng)頁可以正常跳轉(zhuǎn),但是會有這個窗口彈出:
解決方式:按照地址刪去 pythoncom38.dll 文件即可(可能需要刪多個 pythoncom38.dll 文件)
3. Python學(xué)習(xí)中的兩大法寶函數(shù)
說明?
package(名稱為 pytorch)就像一個工具箱,有不同的分隔區(qū),分隔區(qū)里有不同的工具
探索工具箱,兩個道具:
- dir() 函數(shù):能讓我們知道工具箱以及工具箱中的分隔區(qū)有什么東西(打開,看見)
- help() 函數(shù):能讓我們知道每個工具是如何使用的,工具的使用方法(說明書)
例:
dir(pytorch),就能看到輸出1、2、3、4等分隔區(qū);想繼續(xù)探索第3個分隔區(qū)里有什么的話,dir(pytorch.3),輸出會是a,b,c(3號分隔區(qū)里有a,b,c等工具),如何使用?
help(pytorch.3.a)? 輸出:將此扳手放在特定地方,然后擰動
---------------------------------------------------------------------------------------------------------------------------------
實戰(zhàn)操作
打開PyCharm,輸入
dir(torch)
?可以看到輸出了大量的分隔區(qū)(或理解為更小的工具箱),ctrl+F找到cuda
看cuda分隔區(qū)里有什么,輸入
dir(torch.cuda)
可以看到又輸出了大量的分隔區(qū)
繼續(xù)輸入:
dir(torch.cuda.is_available)
可以看到輸出的前后都帶有雙下劃線__,這是一種規(guī)范,說明這個變量不容許篡改,即它不再是一個分隔區(qū),而是一個確確實實的函數(shù)(相當(dāng)于工具,可以用help()函數(shù))
?對工具使用 help():
help(torch.cuda.is_available)
注意 is_available 不帶括號
?返回一個布爾值(True或False),表明cuda是否可用
總結(jié)
- dir() 函數(shù):打開package
- help() 函數(shù):官方的解釋文檔,看函數(shù)怎么用
4. Pycharm及Jupyter使用及對比
如何在PyCharm中新建項目?
(1)新建文件夾 Learn_torch(右鍵:New Folder)
File —> Settings
(2)新建 Python文件,命名 first_demo
?
(3)運行程序:為該Python文件設(shè)置相應(yīng)的Python解釋器后,點擊綠色的運行符號即可
或直接右鍵:
?
Python控制臺
Python控制臺(Python Console)是以每一行作為一個塊進(jìn)行執(zhí)行?
---------------------------------------------------------------------------------------------------------------------------------
如何在Jupyter中新建項目?
?首先在開始菜單打開anaconda的命令行,進(jìn)入pytorch的conda環(huán)境,再打開Jupyter
?
以每一個塊為一個運行整體
---------------------------------------------------------------------------------------------------------------------------------
三種運行方式(PyCharm、PyCharm的Python控制臺、Jupyter Notebook)的適用場景:
代碼是以塊為一個整體運行的話,Python文件的塊是所有行的代碼,即在PyCharm運行時出現(xiàn)錯誤時,修改后要從頭開始運行
PyCharm的Python控制臺是一行一行代碼運行的,即以每一行為一個塊來運行的(也可以以任意行為塊運行,按Shift+回車,輸入下一行代碼,再按回車運行)
- 優(yōu)點:Python控制臺可以看到每個變量的屬性
- 缺點:出現(xiàn)錯誤后代碼的可閱讀性大大降低
- 一般調(diào)試時使用
Jupyter以任意行為塊運行
Python控制臺和Jupyter的好處是:某一塊發(fā)生錯誤時,修改這一塊時不會影響前面已經(jīng)運行的塊
PyCharm | 從頭開始運行 | 通用,傳播方便,適用于大型項目 | 需要從頭運行 |
PyCharm的Python控制臺 | 一行一行運行(也可以以任意行) | 可以顯示每個變量的屬性,一般調(diào)試時使用 | 出錯時會破壞整體閱讀性,不利于代碼閱讀及修改 |
Jupyter Notebook | 以任意行為塊運行 | 代碼閱讀性較高,利于代碼的閱讀及修改 | 環(huán)境需要配置 |
5. PyTorch加載數(shù)據(jù)初認(rèn)識
對于神經(jīng)網(wǎng)絡(luò)訓(xùn)練,要從數(shù)據(jù)海洋里找到有用的數(shù)據(jù)
PyTorch 讀取數(shù)據(jù)涉及兩個類:Dataset & Dataloader
Dataset:提供一種方式,獲取其中需要的數(shù)據(jù)及其對應(yīng)的真實的 label 值,并完成編號。主要實現(xiàn)以下兩個功能:
- 如何獲取每一個數(shù)據(jù)及其label
- 告訴我們總共有多少的數(shù)據(jù)
Dataloader:打包(batch_size),為后面的神經(jīng)網(wǎng)絡(luò)提供不同的數(shù)據(jù)形式
---------------------------------------------------------------------------------------------------------------------------------
數(shù)據(jù)集的幾種組織形式
數(shù)據(jù)集 hymenoptera_data(識別螞蟻和蜜蜂的數(shù)據(jù)集,二分類)
- train 里有兩個文件夾:ants 和 bees,其中分別都是一些螞蟻和蜜蜂的圖片
- train_images是一個文件夾,train_labels是另一個文件夾,如OCR數(shù)據(jù)集
- label直接為圖片的名稱
---------------------------------------------------------------------------------------------------------------------------------
Dataset類?
from torch.utils.data import Dataset
查看Dataset類的介紹:
help(Dataset)
或者:
Dataset??
Dataset 是一個抽象類,所有數(shù)據(jù)集都需要繼承這個類,所有子類都需要重寫 __getitem__ 的方法,這個方法主要是獲取每個數(shù)據(jù)集及其對應(yīng) label,還可以重寫長度類 __len__
def __getitem__(self, index):
raise NotImplementedError
def __add__(self, other):
return ConcatDataset([self, other])
6. Dataset類代碼實戰(zhàn)
兩種方法讀圖片
# 法1
from PIL import Image
img_path = "xxx"
img = Image.open(img_path)
img.show()
# 法2:利用opencv讀取圖片,獲得numpy型圖片數(shù)據(jù)
import cv2
cv_img=cv2.imread(img_path)
安裝opencv:?
在 PyCharm的 Terminal 里輸入(關(guān)閉代理服務(wù)器!??!)
pip install opencv-python
再
import cv2
---------------------------------------------------------------------------------------------------------------------------------
控制臺讀取&可視化圖片
(一)數(shù)據(jù)格式1?
- 絕對路徑:E:\學(xué)完了學(xué)完了\PyTorch\土堆-Pytorch\Learn_torch\dataset\train\ants
- 相對路徑:dataset/train/ants?
注意,路徑引號前加 r 可以防止轉(zhuǎn)義,或使用雙斜杠??
在Python控制臺讀取數(shù)據(jù),可以看到圖片的屬性:
from PIL import Image
img_path = r"E:\學(xué)完了學(xué)完了\PyTorch\土堆-Pytorch\Learn_torch\dataset\train\ants\0013035.jpg"
img = Image.open(img_path)
?
輸入 img.size 就可以返回屬性的數(shù)值
可視化圖片輸入:img.show()
---------------------------------------------------------------------------------------------------------------------------------
讀數(shù)據(jù)
想要獲取圖片地址(通過索引),需要os庫
import os
dir_path = "dataset/train/ants"
img_path_list = os.listdir(dir_path) # 將文件夾下的東西變成一個列表
???
import os
root_dir = "dataset/train"
label_dir = "ants"
path = os.path.join(root_dir, label_dir) # 把兩個路徑拼接在一起
完整代碼:?
from torch.utils.data import Dataset
from PIL import Image #讀取圖片
import os #想要獲得所有圖片的地址,需要導(dǎo)入os(系統(tǒng)庫)
#創(chuàng)建一個class,繼承Dataset類
class MyData(Dataset):
def __init__(self,root_dir,label_dir): #創(chuàng)建初始化類,即根據(jù)這個類去創(chuàng)建一個實例時需要運行的函數(shù)
#通過索引獲取圖片的地址,需要先創(chuàng)建圖片地址的list
#self可以把其指定的變量給后面的函數(shù)使用,相當(dāng)于為整個class提供全局變量
self.root_dir=root_dir
self.label_dir=label_dir
self.path=os.path.join(self.root_dir,self.label_dir)
self.img_path=os.listdir(self.path) #獲得圖片下所有的地址
def __getitem__(self, idx): #idx為編號
#獲取每一個圖片
img_name=self.img_path[idx] #名稱
img_item_path=os.path.join(self.root_dir,self.label_dir,img_name) # 每張圖片的相對路徑
img=Image.open(img_item_path) #讀取圖片
label=self.label_dir
return img,label
def __len__(self): #數(shù)據(jù)集的長度
return len(self.img_path)
#用類創(chuàng)建實例
root_dir="dataset/train"
ants_label_dir="ants"
bees_label_dir="bees"
ants_dataset=MyData(root_dir,ants_label_dir)
bees_dataset=MyData(root_dir,bees_label_dir)
img, label = ants_dataset[0]
img.show() # 可視化第一張圖片
#將ants(124張)和bees(121張)兩個數(shù)據(jù)集進(jìn)行拼接
train_dataset=ants_dataset+bees_dataset
(二)數(shù)據(jù)格式2
當(dāng)label比較復(fù)雜,存儲數(shù)據(jù)比較多時,不可能以文件夾命名的方式,而是以每張圖片對應(yīng)一個txt文件,txt里存儲label信息的方式
rename_dataset.py代碼如下:?
import os
root_dir=r"E:\學(xué)完了學(xué)完了\PyTorch\土堆-Pytorch\Learn_torch\dataset\train"
# 把原來的ants重命名為ants_image
target_dir="ants_image"
img_path=os.listdir(os.path.join(root_dir,target_dir))
label=target_dir.split('_')[0] # ants
out_dir="ants_label"
for i in img_path:
file_name=i.split('.jpg')[0]
with open(os.path.join(root_dir,out_dir,"{}.txt".format(file_name)),'w') as f:
f.write(label)
運行效果如圖:?
7. TensorBoard的使用
- 安裝
- add_scalar() 的使用(常用來繪制 train/val loss)
- add_image() 的使用(常用來觀察訓(xùn)練結(jié)果)
transforms 在 Dataset 類中很常用,對圖像進(jìn)行變換,如圖像要統(tǒng)一到某一個尺寸,或圖像中的每一個數(shù)據(jù)進(jìn)行類的轉(zhuǎn)換
學(xué) TensorBoard 探究:
- 訓(xùn)練過程中l(wèi)oss是如何變化的
- 模型在不同階段的輸出
---------------------------------------------------------------------------------------------------------------------------------
SummaryWriter類
from torch.utils.tensorboard import SummaryWriter
查看一個類如何使用:在PyCharm中,按住Ctrl鍵,把鼠標(biāo)移到類上
是一個直接向 log_dir 文件夾寫入的事件文件,可以被 TensorBoard 進(jìn)行解析
初始化函數(shù):
def __init__(self, log_dir=None):
# 實例化SummaryWriter類
writer = SummaryWriter("logs") # 把對應(yīng)的事件文件存儲到logs文件夾下
主要用到兩個方法:
writer.add_image()
writer.add_scalar()
writer.close()
---------------------------------------------------------------------------------------------------------------------------------
安裝TensorBoard
在 Anaconda 命令行中激活 pytorch 環(huán)境,或直接在 PyCharm 的 Terminal 的 pytorch 環(huán)境中安裝
輸入:
pip install tensorboard
即可安裝成功
---------------------------------------------------------------------------------------------------------------------------------
Ctrl + /? ? 注釋
add_scalar() 方法的使用
def add_scalar(
self,
tag,
scalar_value,
global_step=None,
walltime=None,
new_style=False,
double_precision=False,
):
添加一個標(biāo)量數(shù)據(jù)到 Summary 當(dāng)中,需要參數(shù)
- tag:Data指定方式,類似于圖表的title
- scalar_value:需要保存的數(shù)值(y軸)
- global_step:訓(xùn)練到多少步(x軸)
例:y=x
from torch.utils.tensorboard import SummaryWriter #導(dǎo)入SummaryWriter類
#創(chuàng)建實例
writer=SummaryWriter("logs") #把對應(yīng)的事件文件存儲到logs文件夾下
#兩個方法
# writer.add_image()
# y=x
for i in range(100):
writer.add_scalar("y=x",i,i)
writer.close()
運行后多了一個logs文件夾,下面是TensorBoard的一些事件文件,如圖:
如何打開事件文件?
在 Terminal 里輸入:
tensorboard --logdir=logs # logdir=事件文件所在文件夾名
結(jié)果如圖:
為了防止和別人沖突(一臺服務(wù)器上有好幾個人訓(xùn)練,默認(rèn)打開的都是6006端口),也可以指定端口,命令如下:
?tensorboard --logdir=logs --port=6007
例:y=2x
from torch.utils.tensorboard import SummaryWriter #導(dǎo)入SummaryWriter類
#創(chuàng)建實例
writer=SummaryWriter("logs") #把對應(yīng)的事件文件存儲到logs文件夾下
#兩個方法
# writer.add_image()
# y=2x
for i in range(100):
writer.add_scalar("y=2x",2*i,i) # 標(biāo)題、y軸、x軸
writer.close()
?
title 和 y 不一致的情況:
?
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-52.png)
每向 writer 中寫入新的事件,也記錄了上一個事件
如何解決?
把logs文件夾下的所有文件刪掉,程序刪掉,重新開始
或:重新寫一個子文件,即創(chuàng)建新的 SummaryWriter("新文件夾")
刪掉logs下的文件,重新運行代碼,在 Terminal 里按 Ctrl+C ,再輸入命令:
?tensorboard --logdir=logs --port=6007
就可以出現(xiàn)名字為y=2x,但實際縱坐標(biāo)是y=3x數(shù)值的圖像
以上即是顯示 train_loss 的一個方式
---------------------------------------------------------------------------------------------------------------------------------
add_image() 的使用
def add_image(self, tag, img_tensor, global_step=None):
- tag:對應(yīng)圖像的title
- img_tensor:圖像的數(shù)據(jù)類型,只能是torch.Tensor、numpy.array、string/blobname
- global_step:訓(xùn)練步驟,int 類型
例子:
在Python控制臺輸入:
# 打開控制臺,其位置就是項目文件夾所在的位置
# 故只需復(fù)制相對地址
image_path = "data/train/ants_image/0013035.jpg"
from PIL import Image
img = Image.open(image_path)
print(type(img))
---------------------------------------------------------------------------------------------------------------------------------
利用numpy.array(),對PIL圖片進(jìn)行轉(zhuǎn)換
在Python控制臺,把PIL類型的img變量轉(zhuǎn)換為numpy類型(add_image() 函數(shù)所需要的圖像的數(shù)據(jù)類型),重新賦值給img_array:
import numpy as np
img_array=np.array(img)
print(type(img_array)) # numpy.ndarray
從PIL到numpy,需要在add_image()中指定shape中每一個數(shù)字/維表示的含義?
step1:螞蟻為例?
from torch.utils.tensorboard import SummaryWriter #導(dǎo)入SummaryWriter類
import numpy as np
from PIL import Image
#創(chuàng)建實例
writer=SummaryWriter("logs") #把對應(yīng)的事件文件存儲到logs文件夾下
image_path="data/train/ants_image/0013035.jpg"
img_PIL=Image.open(image_path)
img_array=np.array(img_PIL)
print(type(img_array))
print(img_array.shape) #(512,768,3) 即(H,W,C)(高度,寬度,通道)
writer.add_image("test",img_array,1, dataformats='HWC') # 第1步
writer.close()
結(jié)果:
step2:蜜蜂為例?
from torch.utils.tensorboard import SummaryWriter #導(dǎo)入SummaryWriter類
import numpy as np
from PIL import Image
#創(chuàng)建實例
writer=SummaryWriter("logs") #把對應(yīng)的事件文件存儲到logs文件夾下
image_path="data/train/bees_image/16838648_415acd9e3f.jpg"
img_PIL=Image.open(image_path)
img_array=np.array(img_PIL)
print(type(img_array))
print(img_array.shape) #(512,768,3) 即(H,W,C)(高度,寬度,通道)
writer.add_image("test",img_array,2, dataformats='HWC') # 第2步
writer.close()
結(jié)果:
在一個title下,通過滑塊顯示每一步的圖形,可以直觀地觀察訓(xùn)練中給model提供了哪些數(shù)據(jù),或者想對model進(jìn)行測試時,可以看到每個階段的輸出結(jié)果
如果想要單獨顯示,重命名一下title即可,即 writer.add_image() 的第一個字符串類型的參數(shù)
8. 圖像變換,torchvision中transforms的使用
對圖片進(jìn)行一些變換
transforms的結(jié)構(gòu)及用法
from torchvision import transforms
結(jié)構(gòu)
按住Ctrl,看?transforms.py文件(工具箱),它定義了很多 class文件(工具)
搜索快捷鍵:
File—> Settings—> Keymap—> 搜索 structure(快捷鍵 Alt+7)
一些類
-
Compose類:結(jié)合不同的transforms
?圖片先經(jīng)過中心裁剪,再變成Tensor返回 - ToTensor類:把一個PIL的Image或者numpy數(shù)據(jù)類型的圖片轉(zhuǎn)換成 tensor 的數(shù)據(jù)類型
- ToPILImage類:把一個圖片轉(zhuǎn)換成PIL Image
- Normalize類:歸一化
- Resize類:尺寸變換
- CenterCrop類:中心裁剪
使用
- transforms.py? ?工具箱
- totensor / resize等類? ?工具
拿一些特定格式的圖片,經(jīng)過工具(class文件)后,就會輸出我們想要的圖片變換的結(jié)果
---------------------------------------------------------------------------------------------------------------------------------
兩個問題
python的用法 ——> tensor數(shù)據(jù)類型
通過 transforms.ToTensor去解決兩個問題
- Transforms該如何使用
- Tensor數(shù)據(jù)類型與其他圖片數(shù)據(jù)類型有什么區(qū)別?為什么需要Tensor數(shù)據(jù)類型?
from PIL import Image
from torchvision import transforms
# 絕對路徑 C:\Users\11842\Desktop\Learn_torch\data\train\ants_image\0013035.jpg
# 相對路徑 data/train/ants_image/0013035.jpg
img_path="data/train/ants_image/0013035.jpg" #用相對路徑,絕對路徑里的\在Windows系統(tǒng)下會被當(dāng)做轉(zhuǎn)義符
# img_path_abs="C:\Users\11842\Desktop\Learn_torch\data\train\ants_image\0013035.jpg",雙引號前加r表示轉(zhuǎn)義
img = Image.open(img_path) #Image是Python中內(nèi)置的圖片的庫
print(img) # PIL類型
1、transforms 該如何使用(python)
從transforms中選擇一個class,對它進(jìn)行創(chuàng)建,對創(chuàng)建的對象傳入圖片,即可返回出結(jié)果?
ToTensor將一個 PIL Image 或 numpy.ndarray 轉(zhuǎn)換為 tensor的數(shù)據(jù)類型?
# 1、Transforms該如何使用
tensor_trans = transforms.ToTensor() #從工具箱transforms里取出ToTensor類,返回tensor_trans對象
tensor_img=tensor_trans(img) #創(chuàng)建出tensor_trans后,傳入其需要的參數(shù),即可返回結(jié)果
print(tensor_img)
Ctrl+P可以提示函數(shù)里需要填什么參數(shù)?
2、為什么我們需要 Tensor 數(shù)據(jù)類型
在Python Console輸入:
from PIL import Image
from torchvision import transforms
img_path= "data/train/ants_image/0013035.jpg"
img = Image.open(img_path)
tensor_trans = transforms.ToTensor()
tensor_img = tensor_trans(img)
打開img,即用Python內(nèi)置的函數(shù)讀取的圖片,具有的參數(shù)有:
再打開tensor_img,看一下它有哪些參數(shù):
Tensor 數(shù)據(jù)類型包裝了反向神經(jīng)網(wǎng)絡(luò)所需要的一些理論基礎(chǔ)的參數(shù),如:_backward_hooks、_grad等(先轉(zhuǎn)換成Tensor數(shù)據(jù)類型,再訓(xùn)練)
---------------------------------------------------------------------------------------------------------------------------------
兩種讀取圖片的方式
-
PIL Image
from PIL import Image img_path = "xxx" img = Image.open(img_path) img.show()
-
numpy.ndarray(通過opencv)
import cv2 cv_img=cv2.imread(img_path)
上節(jié)課以 numpy.array 類型為例,這節(jié)課使用 torch.Tensor 類型:
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
# python的用法 ——> tensor數(shù)據(jù)類型
# 通過 transforms.ToTensor去解決兩個問題
# 1、Transforms該如何使用
# 2、Tensor數(shù)據(jù)類型與其他圖片數(shù)據(jù)類型有什么區(qū)別?為什么需要Tensor數(shù)據(jù)類型
# 絕對路徑 C:\Users\11842\Desktop\Learn_torch\data\train\ants_image\0013035.jpg
# 相對路徑 data/train/ants_image/0013035.jpg
img_path="data/train/ants_image/0013035.jpg" #用相對路徑,絕對路徑里的\在Windows系統(tǒng)下會被當(dāng)做轉(zhuǎn)義符
# img_path_abs="C:\Users\11842\Desktop\Learn_torch\data\train\ants_image\0013035.jpg",雙引號前加r表示轉(zhuǎn)義
img = Image.open(img_path) #Image是Python中內(nèi)置的圖片的庫
#print(img)
writer = SummaryWriter("logs")
# 1、Transforms該如何使用
tensor_trans = transforms.ToTensor() #從工具箱transforms里取出ToTensor類,返回tensor_trans對象
tensor_img = tensor_trans(img) #創(chuàng)建出tensor_trans后,傳入其需要的參數(shù),即可返回結(jié)果
#print(tensor_img)
writer.add_image("Tensor_img",tensor_img) # .add_image(tag, img_tensor, global_step)
# tag即名稱
# img_tensor的類型為torch.Tensor/numpy.array/string/blobname
# global_step為int類型
writer.close()
運行后,在 Terminal 里輸入:
tensorboard --logdir=logs
進(jìn)入網(wǎng)址后可以看到圖片:
9. 常見的Transforms的使用
- 輸入
- 輸出
- 作用
圖片有不同的格式,打開方式也不同
圖片格式 | 打開方式 |
PIL | Image.open()? ?——Python自帶的圖片打開方式 |
tensor | ToTensor() |
narrays | cv.imread()? ?——Opencv |
---------------------------------------------------------------------------------------------------------------------------------
Compose 的使用
把不同的 transforms 結(jié)合在一起,后面接一個數(shù)組,里面是不同的transforms
Example:圖片首先要經(jīng)過中心裁剪,再轉(zhuǎn)換成Tensor數(shù)據(jù)類型
>>> transforms.Compose([
>>> transforms.CenterCrop(10),
>>> transforms.PILToTensor(),
>>> transforms.ConvertImageDtype(torch.float),
>>> ])
---------------------------------------------------------------------------------------------------------------------------------
Python中 __call__ 的用法
內(nèi)置函數(shù) __call__ ,不用.的方式調(diào)用方法,可以直接拿對象名,加上需要的參數(shù),即可調(diào)用方法
按 Ctrl+p,會提示需要什么參數(shù)
class Person:
def __call__(self, name): #下劃線__表示為內(nèi)置函數(shù)
print("__call__"+"Hello "+name)
def hello(self,name):
print("hello"+name)
person = Person()
person("zhangsan")
person.hello("lisi")
輸出結(jié)果如下:
__call__Hello zhangsan
hellolisi
---------------------------------------------------------------------------------------------------------------------------------
ToTensor 的使用
把 PIL Image 或 numpy.ndarray 類型轉(zhuǎn)換為 tensor 類型(TensorBoard 必須是 tensor 的數(shù)據(jù)類型)
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
writer = SummaryWriter("logs")
img=Image.open("images/people.jpg")
print(img) #可以看到類型是PIL
#ToTensor的使用
trans_totensor = transforms.ToTensor() #將類型轉(zhuǎn)換為tensor
img_tensor = trans_totensor(img) #img變?yōu)閠ensor類型后,就可以放入TensorBoard當(dāng)中
writer.add_image("ToTensor",img_tensor)
writer.close()
(運行前要先把之前的logs進(jìn)行刪除)運行后,在 Terminal 里輸入:
tensorboard --logdir=logs
進(jìn)入網(wǎng)址后可以看到圖片:
---------------------------------------------------------------------------------------------------------------------------------
ToPILImage 的使用
把 tensor 數(shù)據(jù)類型或 ndarray 類型轉(zhuǎn)換成 PIL Image
---------------------------------------------------------------------------------------------------------------------------------
Normalize 的使用
用平均值/標(biāo)準(zhǔn)差歸一化 tensor 類型的 image(輸入)
圖片RGB三個信道,將每個信道中的輸入進(jìn)行歸一化
output[channel] = (input[channel] - mean[channel]) / std[channel]
設(shè)置 mean 和 std 都為0.5,則 output= 2*input -1。如果 input 圖片像素值為0~1范圍內(nèi),那么結(jié)果就是 -1~1之間
#Normalize的使用
print(img_tensor[0][0][0]) # 第0層第0行第0列
trans_norm = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5]) # mean,std,因為圖片是RGB三信道,故傳入三個數(shù)
img_norm = trans_norm(img_tensor) # 輸入的類型要是tensor
print(img_norm[0][0][0])
writer.add_image("Normalize",img_norm)
輸出結(jié)果:?
tensor(0.6549)
tensor(0.3098)
?刷新網(wǎng)頁:
?
加入step值:
#Normalize的使用
print(img_tensor[0][0][0])
trans_norm = transforms.Normalize([6,3,2],[9,3,5])
img_norm = trans_norm(img_tensor)
print(img_norm[0][0][0])
writer.add_image("Normalize",img_norm,2) # 第二步
writer.close()
---------------------------------------------------------------------------------------------------------------------------------
Resize() 的使用
輸入:PIL Image? ? ? 將輸入轉(zhuǎn)變到給定尺寸
- 序列:(h,w)高度,寬度
- 一個整數(shù):不改變高和寬的比例,只單純改變最小邊和最長邊之間的大小關(guān)系。之前圖里最小的邊將會匹配這個數(shù)(等比縮放)
PyCharm小技巧設(shè)置:忽略大小寫,進(jìn)行提示匹配
- 一般情況下,你需要輸入R,才能提示出Resize
- 我們想設(shè)置,即便你輸入的是r,也能提示出Resize,也就是忽略了大小寫進(jìn)行匹配提示
File—> Settings—> 搜索case—> Editor-General-Code Completion-去掉Match case前的√
—>Apply—>OK
返回值還是 PIL Image
#Resize的使用
print(img.size) # 輸入是PIL.Image
trans_resize = transforms.Resize((512,512))
#img:PIL --> resize --> img_resize:PIL
img_resize = trans_resize(img) #輸出還是PIL Image
#img_resize:PIL --> totensor --> img_resize:tensor(同名,覆蓋)
img_resize = trans_totensor(img_resize)
writer.add_image("Resize",img_resize,0)
print(img_resize)
---------------------------------------------------------------------------------------------------------------------------------
Compose() 的使用
Compose() 中的參數(shù)需要是一個列表,Python中列表的表示形式為[數(shù)據(jù)1,數(shù)據(jù)2,...]
在Compose中,數(shù)據(jù)需要是transforms類型,所以得到Compose([transforms參數(shù)1,transforms參數(shù)2,...])
#Compose的使用(將輸出類型從PIL變?yōu)閠ensor類型,第二種方法)
trans_resize_2 = transforms.Resize(512) # 將圖片短邊縮放至512,長寬比保持不變
# PIL --> resize --> PIL --> totensor --> tensor
#compose()就是把兩個參數(shù)功能整合,第一個參數(shù)是改變圖像大小,第二個參數(shù)是轉(zhuǎn)換類型,前者的輸出類型與后者的輸入類型必須匹配
trans_compose = transforms.Compose([trans_resize_2,trans_totensor])
img_resize_2 = trans_compose(img) # 輸入需要是PIL Image
writer.add_image("Resize",img_resize_2,1)
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-74.png)
---------------------------------------------------------------------------------------------------------------------------------
RandomCrop() 的使用
隨機(jī)裁剪,輸入PIL Image
參數(shù)size:
- sequence:(h,w) 高,寬
- int:裁剪一個該整數(shù)×該整數(shù)的圖像
(1)以 int 為例:?
#RandomCrop()的使用
trans_random = transforms.RandomCrop(512)
trans_compose_2 = transforms.Compose([trans_random,trans_totensor])
for i in range(10): #裁剪10個
img_crop = trans_compose_2(img) # 輸入需要是PIL Image
writer.add_image("RandomCrop",img_crop,i)
(2)以 sequence 為例:
#RandomCrop()的使用
trans_random = transforms.RandomCrop((500,1000))
trans_compose_2 = transforms.Compose([trans_random,trans_totensor])
for i in range(10): #裁剪10個
img_crop = trans_compose_2(img)
writer.add_image("RandomCropHW",img_crop,i)
?
---------------------------------------------------------------------------------------------------------------------------------
總結(jié)使用方法
- 關(guān)注輸入和輸出類型
- 多看官方文檔
- 關(guān)注方法需要什么參數(shù):參數(shù)如果設(shè)置了默認(rèn)值,保留默認(rèn)值即可,沒有默認(rèn)值的需要指定(看一下要求傳入什么類型的參數(shù))
- 不知道變量的輸出類型可以
- 直接print該變量
- print(type()),看結(jié)果里顯示什么類型
- 斷點調(diào)試 dubug
- 最后要 totensor,在 tensorboard 看一下結(jié)果(tensorboard需要tensor數(shù)據(jù)類型進(jìn)行顯示)
10. torchvision 中的數(shù)據(jù)集使用
- 如何把數(shù)據(jù)集(多張圖片)和 transforms 結(jié)合在一起
- 標(biāo)準(zhǔn)數(shù)據(jù)集如何下載、查看、使用
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-77.png)
?torchvision — Torchvision 0.11.0 documentation
(1)torchvision.datasets
如:COCO 目標(biāo)檢測、語義分割;MNIST 手寫文字;CIFAR 物體識別
(2)torchvision.io
?輸入輸出模塊,不常用
(3)torchvision.models
提供一些比較常見的神經(jīng)網(wǎng)絡(luò),有的已經(jīng)預(yù)訓(xùn)練好,如分類、語義分割、目標(biāo)檢測、視頻分類
(4)torchvision.ops
torchvision提供的一些比較少見的特殊的操作,不常用
(5)torchvision.transforms
(6)torchvision.utils
提供一些常用的小工具,如TensorBoard
---------------------------------------------------------------------------------------------------------------------------------
本節(jié)主要講解torchvision.datasets,以及它如何跟transforms聯(lián)合使用?
CIFAR10數(shù)據(jù)集
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-78.png)
CIFAR10 數(shù)據(jù)集包含了6萬張32×32像素的彩色圖片,圖片有10個類別,每個類別有6千張圖像,其中有5萬張圖像為訓(xùn)練圖片,1萬張為測試圖片
標(biāo)準(zhǔn)數(shù)據(jù)集如何下載、查看、使用
#如何使用torchvision提供的標(biāo)準(zhǔn)數(shù)據(jù)集
import torchvision
train_set=torchvision.datasets.CIFAR10(root="./dataset",train=True,download=True) #root使用相對路徑,會在該.py所在位置創(chuàng)建一個叫dataset的文件夾,同時把數(shù)據(jù)保存進(jìn)去
test_set=torchvision.datasets.CIFAR10(root="./dataset",train=False,download=True)
print(test_set[0]) # 查看測試集中的第一個數(shù)據(jù),是一個元組:(img, target)
print(test_set.classes) # 列表
img,target = test_set[0]
print(img)
print(target) # 3
print(test_set.classes[target]) # cat
img.show()
數(shù)據(jù)集下載過慢時:
獲得下載鏈接后,把下載鏈接放到迅雷中,會首先下載壓縮文件tar.gz,之后會對該壓縮文件進(jìn)行解壓,里面會有相應(yīng)的數(shù)據(jù)集
采用迅雷下載完畢后,在PyCharm里新建directory,名字也叫dataset,再將下載好的壓縮包復(fù)制進(jìn)去,download依然為True,運行后,會自動解壓該數(shù)據(jù)
沒有顯示下載地址時:
按住Ctrl鍵,查看數(shù)據(jù)集的源代碼,若其中有 url地址,可直接復(fù)制到迅雷中進(jìn)行下載
--------------------------------------------------------------------------------------------------------------------------------?
如何把數(shù)據(jù)集(多張圖片)和 transforms 結(jié)合在一起
CIFAR10數(shù)據(jù)集原始圖片是PIL Image,如果要給pytorch使用,需要轉(zhuǎn)為tensor數(shù)據(jù)類型(轉(zhuǎn)成tensor后,就可以用tensorboard了)
transforms?更多地是用在 datasets 里 transform?的選項中
import torchvision
from torch.utils.tensorboard import SummaryWriter
#把dataset_transform運用到數(shù)據(jù)集中的每一張圖片,都轉(zhuǎn)為tensor數(shù)據(jù)類型
dataset_transform = torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
train_set=torchvision.datasets.CIFAR10(root="./dataset",train=True,transform=dataset_transform,download=True) #root使用相對路徑,會在該.py所在位置創(chuàng)建一個叫dataset的文件夾,同時把數(shù)據(jù)保存進(jìn)去
test_set=torchvision.datasets.CIFAR10(root="./dataset",train=False,transform=dataset_transform,download=True)
# print(test_set[0])
writer = SummaryWriter("p10")
#顯示測試數(shù)據(jù)集中的前10張圖片
for i in range(10):
img,target = test_set[i]
writer.add_image("test_set",img,i) # img已經(jīng)轉(zhuǎn)成了tensor類型
writer.close()
運行后在 terminal 里輸入
tensorboard --logdir="p10"
11. DataLoader 的使用
- dataset:告訴程序中數(shù)據(jù)集的位置,數(shù)據(jù)集中索引,數(shù)據(jù)集中有多少數(shù)據(jù)(想象成一疊撲克牌)
- dataloader:將數(shù)據(jù)加載到神經(jīng)網(wǎng)絡(luò)中,每次從dataset中取數(shù)據(jù),通過dataloader中的參數(shù)可以設(shè)置如何取數(shù)據(jù)(想象成抓的一組牌)
torch.utils.data — PyTorch 1.10 documentation
參數(shù)介紹?
參數(shù)如下(大部分有默認(rèn)值,實際中只需要設(shè)置少量的參數(shù)即可):
- dataset:只有dataset沒有默認(rèn)值,只需要將之前自定義的dataset實例化,再放到dataloader中即可?
- batch_size:每次抓牌抓幾張
- shuffle:打亂與否,值為True的話兩次打牌時牌的順序是不一樣。默認(rèn)為False,但一般用True
- num_workers:加載數(shù)據(jù)時采用單個進(jìn)程還是多個進(jìn)程,多進(jìn)程的話速度相對較快,默認(rèn)為0(主進(jìn)程加載)。Windows系統(tǒng)下該值>0會有問題(報錯提示:BrokenPipeError)
- drop_last:100張牌每次取3張,最后會余下1張,這時剩下的這張牌是舍去還是不舍去。值為True代表舍去這張牌、不取出,F(xiàn)alse代表要取出該張牌
---------------------------------------------------------------------------------------------------------------------------------
示例?
# 測試數(shù)據(jù)集中第一張圖片及target
img,target = test_data[0]
print(img.shape)
print(target)
輸出結(jié)果:
torch.Size([3, 32, 32]) #三通道,32×32大小
3 #類別為3
dataset?
- __getitem()__:return img,target
dataloader(batch_size=4):從dataset中取4個數(shù)據(jù)
- img0,target0 = dataset[0]
- img1,target1 = dataset[1]
- img2,target2 = dataset[2]
- img3,target3 = dataset[3]
把 img 0-3 進(jìn)行打包,記為imgs;target 0-3 進(jìn)行打包,記為targets;作為dataloader中的返回
for data in test_loader:
imgs,targets = data
print(imgs.shape)
print(targets)
輸出結(jié)果:
torch.Size([4, 3, 32, 32]) #4張圖片,三通道,32×32
tensor([0, 4, 4, 8]) #4個target進(jìn)行一個打包
數(shù)據(jù)是隨機(jī)取的(斷點debug一下,可以看到采樣器sampler是隨機(jī)采樣的),所以兩次的 target 0 并不一樣
---------------------------------------------------------------------------------------------------------------------------------
batch_size
# 用上節(jié)課torchvision提供的自定義的數(shù)據(jù)集
# CIFAR10原本是PIL Image,需要轉(zhuǎn)換成tensor
import torchvision.datasets
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# 準(zhǔn)備的測試數(shù)據(jù)集
test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())
# 加載測試集
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0,drop_last=False)
#batch_size=4,意味著每次從test_data中取4個數(shù)據(jù)進(jìn)行打包
writer = SummaryWriter("dataloader")
step=0
for data in test_loader:
imgs,targets = data #imgs是tensor數(shù)據(jù)類型
writer.add_images("test_data",imgs,step)
step=step+1
writer.close()
運行后在 terminal 里輸入:
tensorboard --logdir="dataloader"
運行結(jié)果如圖,滑動滑塊即是每一次取數(shù)據(jù)時的batch_size張圖片:
由于 drop_last 設(shè)置為 False,所以最后16張圖片(沒有湊齊64張)顯示如下:
---------------------------------------------------------------------------------------------------------------------------------
drop_last
若將 drop_last 設(shè)置為 True,最后16張圖片(step 156)會被舍去,結(jié)果如圖:
---------------------------------------------------------------------------------------------------------------------------------
shuffle
一個 for data in test_loader 循環(huán),就意味著打完一輪牌(抓完一輪數(shù)據(jù)),在下一輪再進(jìn)行抓取時,第二次數(shù)據(jù)是否與第一次數(shù)據(jù)一樣。值為True的話,會重新洗牌(一般都設(shè)置為True)
shuffle為False的話兩輪取的圖片是一樣的
在外面再套一層 for epoch in range(2) 的循環(huán)
# shuffle為True
for epoch in range(2):
step=0
for data in test_loader:
imgs,targets = data #imgs是tensor數(shù)據(jù)類型
writer.add_images("Epoch:{}".format(epoch),imgs,step)
step=step+1
結(jié)果如下:
可以看出兩次 step 155 的圖片不一樣
12. 神經(jīng)網(wǎng)絡(luò)的基本骨架 - nn.Module 的使用
Pytorch官網(wǎng)左側(cè):Python API(相當(dāng)于package,提供了一些不同的工具)
關(guān)于神經(jīng)網(wǎng)絡(luò)的工具主要在torch.nn里?
torch.nn — PyTorch 1.10 documentation
---------------------------------------------------------------------------------------------------------------------------------
Containers
包含6個模塊:
- Module
- Sequential
- ModuleList
- ModuleDict
- ParameterList
- ParameterDict
其中最常用的是 Module 模塊(為所有神經(jīng)網(wǎng)絡(luò)提供基本骨架)
CLASS torch.nn.Module #搭建的 Model都必須繼承該類
模板:?
import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module): #搭建的神經(jīng)網(wǎng)絡(luò) Model繼承了 Module類(父類)
def __init__(self): #初始化函數(shù)
super(Model, self).__init__() #必須要這一步,調(diào)用父類的初始化函數(shù)
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)
def forward(self, x): #前向傳播(為輸入和輸出中間的處理過程),x為輸入
x = F.relu(self.conv1(x)) #conv為卷積,relu為非線性處理
return F.relu(self.conv2(x))
- 前向傳播 forward(在所有子類中進(jìn)行重寫)
- 反向傳播 backward
---------------------------------------------------------------------------------------------------------------------------------
?
Code —> Generate —> Override Methods? 可以自動補全代碼
?例子:
import torch
from torch import nn
class Tudui(nn.Module):
def __init__(self):
super().__init__()
# def __init__(self):
# super(Tudui, self).__init__()
def forward(self,input):
output = input + 1
return output
tudui = Tudui() #拿Tudui模板創(chuàng)建出的神經(jīng)網(wǎng)絡(luò)
x = torch.tensor(1.0) #將1.0這個數(shù)轉(zhuǎn)換成tensor類型
output = tudui(x)
print(output)
結(jié)果如下:
tensor(2.)
debug看流程?
在下列語句前打斷點:
tudui = Tudui() #整個程序的開始
然后點擊蜘蛛,點擊 Step into My Code,可以看到代碼每一步的執(zhí)行過程
13. 土堆說卷積操作
torch.nn — PyTorch 1.10 documentation
Convolution Layers?
Conv2d — PyTorch 1.10 documentation
torch.nn 和 torch.nn.functional 的區(qū)別:前者是后者的封裝,更利于使用
點擊 torch.nn.functional - Convolution functions - conv2d?
stride(步進(jìn))
可以是單個數(shù),或元組(sH,sW) — 控制橫向步進(jìn)和縱向步進(jìn)
當(dāng) stride = 2 時,橫向和縱向都是2,輸出是一個2×2的矩陣
--------------------------------------------------------------------------------------------------------------------------------
要求輸入的維度?& reshape函數(shù)
- input:尺寸要求是batch,幾個通道,高,寬(4個參數(shù))
- weight:尺寸要求是輸出,in_channels(groups一般為1),高,寬(4個參數(shù))
使用 torch.reshape 函數(shù),將輸入改變?yōu)橐筝斎氲木S度
實現(xiàn)上圖的代碼:
import torch
import torch.nn.functional as F
input =torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]]) #將二維矩陣轉(zhuǎn)為tensor數(shù)據(jù)類型
# 卷積核kernel
kernel = torch.tensor([[1,2,1],
[0,1,0],
[2,1,0]])
# 尺寸只有高和寬,不符合要求
print(input.shape) #5×5
print(kernel.shape) #3×3
# 尺寸變換為四個數(shù)字
input = torch.reshape(input,(1,1,5,5)) #通道數(shù)為1,batch大小為1
kernel = torch.reshape(kernel,(1,1,3,3))
print(input.shape)
print(kernel.shape)
output = F.conv2d(input,kernel,stride=1) # .conv2d(input:Tensor, weight:Tensor, stride)
print(output)
結(jié)果為:
當(dāng)將步進(jìn) stride 改為 2 時:
output2 = F.conv2d(input,kernel,stride=2)
print(output2)
---------------------------------------------------------------------------------------------------------------------------------
padding(填充)
在輸入圖像左右兩邊進(jìn)行填充,決定填充有多大。可以為一個數(shù)或一個元組(分別指定高和寬,即縱向和橫向每次填充的大?。DJ(rèn)情況下不進(jìn)行填充
padding=1:將輸入圖像左右上下兩邊都拓展一個像素,空的地方默認(rèn)為0
代碼實現(xiàn):?
output3 = F.conv2d(input,kernel,stride=1,padding=1)
print(output3)
輸出結(jié)果如下:可以看出輸出尺寸變大?
14. 神經(jīng)網(wǎng)絡(luò) - 卷積層
torch.nn — PyTorch 1.10 documentation
Convolution Layers
nn.Conv1d 一維卷積 |
Applies a 1D convolution over an input signal composed of several input planes. |
nn.Conv2d 二維卷積 |
Applies a 2D convolution over an input signal composed of several input planes. |
nn.Conv3d 三維卷積 |
Applies a 3D convolution over an input signal composed of several input planes. |
圖像為二維矩陣,所以講解 nn.Conv2d
Conv2d — PyTorch 1.10 documentation
CLASS torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
# in_channels 輸入通道數(shù)
# out_channels 輸出通道數(shù)
# kernel_size 卷積核大小
#以上參數(shù)需要設(shè)置
#以下參數(shù)提供了默認(rèn)值
# stride=1 卷積過程中的步進(jìn)大小
# padding=0 卷積過程中對原始圖像進(jìn)行padding的選項
# dilation=1 每一個卷積核對應(yīng)位的距離
# groups=1 一般設(shè)置為1,很少改動,改動的話為分組卷積
# bias=True 通常為True,對卷積后的結(jié)果是否加減一個常數(shù)的偏置
# padding_mode='zeros' 選擇padding填充的模式
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-99.png)
動圖:?
conv_arithmetic/README.md at master · vdumoulin/conv_arithmetic · GitHub
kernel_size
定義了一個卷積核的大小,若為3則生成一個3×3的卷積核
- 卷積核的參數(shù)是從一些分布中進(jìn)行采樣得到的
- 實際訓(xùn)練過程中,卷積核中的值會不斷進(jìn)行調(diào)整
in_channels & out_channels
- in_channels:輸入圖片的channel數(shù)(彩色圖像 in_channels 值為3)
- out_channels:輸出圖片的channel數(shù)
in_channels 和?out_channels 都為 1 時,拿一個卷積核在輸入圖像中進(jìn)行卷積
out_channels 為 2 時,卷積層會生成兩個卷積核(不一定一樣),得到兩個輸出,疊加后作為最后輸出
---------------------------------------------------------------------------------------------------------------------------------
CIFAR10數(shù)據(jù)集實例?
# CIFAR10數(shù)據(jù)集
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10("../data",train=False,transform=torchvision.transforms.ToTensor(),download=True) # 這里用測試數(shù)據(jù)集,因為訓(xùn)練數(shù)據(jù)集太大了
dataloader = DataLoader(dataset,batch_size=64)
# 搭建神經(jīng)網(wǎng)絡(luò)Tudui
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
# 因為是彩色圖片,所以in_channels=3
self.conv1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0) #卷積層conv1
def forward(self,x): #輸出為x
x = self.conv1(x)
return x
tudui = Tudui() # 初始化網(wǎng)絡(luò)
# 打印一下網(wǎng)絡(luò)結(jié)構(gòu)
print(tudui) #Tudui((conv1): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1)))
writer = SummaryWriter("../logs")
step = 0
for data in dataloader:
imgs,targets = data #經(jīng)過ToTensor轉(zhuǎn)換,成為tensor數(shù)據(jù)類型,可以直接送到網(wǎng)絡(luò)中
output = tudui(imgs)
print(imgs.shape) #輸入大小 torch.Size([64, 3, 32, 32]) batch_size=64,in_channels=3(彩色圖像),每張圖片是32×32的
print(output.shape) #經(jīng)過卷積后的輸出大小 torch.Size([64, 6, 30, 30]) 卷積后變成6個channels,但原始圖像減小,所以是30×30的
writer.add_images("input",imgs,step)
# 6個channel無法顯示。torch.Size([64, 6, 30, 30]) ——> [xxx,3,30,30] 第一個值不知道為多少時寫-1,會根據(jù)后面值的大小進(jìn)行計算
output = torch.reshape(output,(-1,3,30,30))
writer.add_images("output",output,step)
step = step + 1
運行后,在 Terminal 里啟動 pytorch 環(huán)境:
conda activate pytorch
打開 tensorboard:
tensorboard --logdir=logs
打開網(wǎng)址 http://localhost:6006/:
---------------------------------------------------------------------------------------------------------------------------------
卷積層 vgg16
---------------------------------------------------------------------------------------------------------------------------------
卷積前后維度計算公式
15. 神經(jīng)網(wǎng)絡(luò) - 最大池化的使用
torch.nn — PyTorch 1.10 documentation
Pooling layers
- MaxPool:最大池化(下采樣)
- MaxUnpool:上采樣
- AvgPool:平均池化
- AdaptiveMaxPool2d:自適應(yīng)最大池化
最常用:MaxPool2d — PyTorch 1.10 documentation
參數(shù)?
CLASS torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
# kernel_size 池化核
注意,卷積中stride默認(rèn)為1,而池化中stride默認(rèn)為kernel_size?
--------------------------------------------------------------------------------------------------------------------------------
ceil_mode參數(shù)
Ceil_mode 默認(rèn)情況下為 False,對于最大池化一般只需設(shè)置 kernel_size 即可?
--------------------------------------------------------------------------------------------------------------------------------
輸入輸出維度計算公式
--------------------------------------------------------------------------------------------------------------------------------?
代碼實現(xiàn)?
要求的 input 必須是四維的,參數(shù)依次是:batch_size、channel、高、寬?
上述圖用代碼實現(xiàn):(以 Ceil_mode = True 為例)
import torch
from torch import nn
from torch.nn import MaxPool2d
input = torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]],dtype=torch.float32) #最大池化無法對long數(shù)據(jù)類型進(jìn)行實現(xiàn),將input變成浮點數(shù)的tensor數(shù)據(jù)類型
input = torch.reshape(input,(-1,1,5,5)) #-1表示torch計算batch_size
print(input.shape)
# 搭建神經(jīng)網(wǎng)絡(luò)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.maxpool1 = MaxPool2d(kernel_size=3,ceil_mode=True)
def forward(self,input):
output = self.maxpool1(input)
return output
# 創(chuàng)建神經(jīng)網(wǎng)絡(luò)
tudui = Tudui()
output = tudui(input)
print(output)
運行結(jié)果如下:
?
--------------------------------------------------------------------------------------------------------------------------------?
為什么要進(jìn)行最大池化?最大池化的作用是什么?
最大池化的目的是保留輸入的特征,同時把數(shù)據(jù)量減小(數(shù)據(jù)維度變?。?,對于整個網(wǎng)絡(luò)來說,進(jìn)行計算的參數(shù)變少,會訓(xùn)練地更快
- 如上面案例中輸入是5x5的,但輸出是3x3的,甚至可以是1x1的
- 類比:1080p的視頻為輸入圖像,經(jīng)過池化可以得到720p,也能滿足絕大多數(shù)需求,傳達(dá)視頻內(nèi)容的同時,文件尺寸會大大縮小
池化一般跟在卷積后,卷積層是用來提取特征的,一般有相應(yīng)特征的位置是比較大的數(shù)字,最大池化可以提取出這一部分有相應(yīng)特征的信息
池化不影響通道數(shù)
池化后一般再進(jìn)行非線性激活
--------------------------------------------------------------------------------------------------------------------------------?
用數(shù)據(jù)集 CIFAR10 實現(xiàn)最大池化
import torch
import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10("../data",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,batch_size=64)
# 搭建神經(jīng)網(wǎng)絡(luò)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.maxpool1 = MaxPool2d(kernel_size=3,ceil_mode=True)
def forward(self,input):
output = self.maxpool1(input)
return output
# 創(chuàng)建神經(jīng)網(wǎng)絡(luò)
tudui = Tudui()
writer = SummaryWriter("../logs_maxpool")
step = 0
for data in dataloader:
imgs,targets = data
writer.add_images("input",imgs,step)
output = tudui(imgs) #output尺寸池化后不會有多個channel,原來是3維的圖片,經(jīng)過最大池化后還是3維的,不需要像卷積一樣還要reshape操作(影響通道數(shù)的是卷積核個數(shù))
writer.add_images("output",output,step)
step = step + 1
writer.close()
運行后在 terminal 里輸入(注意是在pytorch環(huán)境下):
tensorboard --logdir=logs_maxpool
打開網(wǎng)址:
16. 神經(jīng)網(wǎng)絡(luò) - 非線性激活?
-
Padding Layers(對輸入圖像進(jìn)行填充的各種方式)
幾乎用不到,nn.ZeroPad2d(在輸入tensor數(shù)據(jù)類型周圍用0填充)
nn.ConstantPad2d(用常數(shù)填充)
在 Conv2d 中可以實現(xiàn),故不常用 -
Non-linear Activations (weighted sum, nonlinearity)
-
Non-linear Activations (other)
非線性激活:給神經(jīng)網(wǎng)絡(luò)引入一些非線性的特征
非線性越多,才能訓(xùn)練出符合各種曲線或特征的模型(提高泛化能力)
--------------------------------------------------------------------------------------------------------------------------------?
最常見:RELU? ?
ReLU — PyTorch 1.10 documentation
輸入:(N,*)? ? N 為 batch_size,*不限制
代碼舉例:RELU
import torch
from torch import nn
from torch.nn import ReLU
input = torch.tensor([[1,-0.5],
[-1,3]])
input = torch.reshape(input,(-1,1,2,2)) #input必須要指定batch_size,-1表示batch_size自己算,1表示是1維的
print(input.shape) #torch.Size([1, 1, 2, 2])
# 搭建神經(jīng)網(wǎng)絡(luò)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.relu1 = ReLU() #inplace默認(rèn)為False
def forward(self,input):
output = self.relu1(input)
return output
# 創(chuàng)建網(wǎng)絡(luò)
tudui = Tudui()
output = tudui(input)
print(output)
運行結(jié)果:
跟輸入對比可以看到:小于0的值被0截斷,大于0的值仍然保留
--------------------------------------------------------------------------------------------------------------------------------?
Sigmoid
Sigmoid — PyTorch 1.10 documentation
?輸入:(N,*)? ? N 為 batch_size,*不限制
代碼舉例:Sigmoid(數(shù)據(jù)集CIFAR10)
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset = torchvision.datasets.CIFAR10("../data",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,batch_size=64)
# 搭建神經(jīng)網(wǎng)絡(luò)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.sigmoid1 = Sigmoid() #inplace默認(rèn)為False
def forward(self,input):
output = self.sigmoid1(input)
return output
# 創(chuàng)建網(wǎng)絡(luò)
tudui = Tudui()
writer = SummaryWriter("../logs_sigmoid")
step = 0
for data in dataloader:
imgs,targets = data
writer.add_images("input",imgs,global_step=step)
output = tudui(imgs)
writer.add_images("output",output,step)
step = step + 1
writer.close()
運行后在 terminal 里輸入:
tensorboard --logdir=logs_sigmoid
打開網(wǎng)址:
--------------------------------------------------------------------------------------------------------------------------------?
關(guān)于inplace
17. 神經(jīng)網(wǎng)絡(luò) - 線性層及其他層介紹?
批標(biāo)準(zhǔn)化層(不難,自學(xué))? ?
Normalization Layers
torch.nn — PyTorch 1.10 documentation
BatchNorm2d — PyTorch 1.10 documentation
對輸入采用Batch Normalization,可以加快神經(jīng)網(wǎng)絡(luò)的訓(xùn)練速度
CLASS torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
# num_features C-輸入的channel
# With Learnable Parameters
m = nn.BatchNorm2d(100)
# Without Learnable Parameters
m = nn.BatchNorm2d(100, affine=False) # 正則化層num_feature等于channel,即100
input = torch.randn(20, 100, 35, 45) #batch_size=20,100個channel,35x45的輸入
output = m(input)
---------------------------------------------------------------------------------------------------------------------------------
Recurrent Layers(特定網(wǎng)絡(luò)中使用,自學(xué))
RNN、LSTM等,用于文字識別中,特定的網(wǎng)絡(luò)結(jié)構(gòu)
torch.nn — PyTorch 1.13 documentation
---------------------------------------------------------------------------------------------------------------------------------
Transformer Layers(特定網(wǎng)絡(luò)中使用,自學(xué))
特定網(wǎng)絡(luò)結(jié)構(gòu)
torch.nn — PyTorch 1.13 documentation
---------------------------------------------------------------------------------------------------------------------------------
Linear Layers(本節(jié)講解)
Linear — PyTorch 1.10 documentation
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-119.png)
--------------------------------------------------------------------------------------------------------------------------------?
代碼實例
vgg16 model
---------------------------------------------------------------------------------------------------------------------------------
flatten 攤平
torch.flatten — PyTorch 1.10 documentation
# Example
>>> t = torch.tensor([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]]) #3個中括號,所以是3維的
>>> torch.flatten(t) #攤平
tensor([1, 2, 3, 4, 5, 6, 7, 8])
>>> torch.flatten(t, start_dim=1) #變?yōu)?行
tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])
- reshape():可以指定尺寸進(jìn)行變換
- flatten():變成1行,攤平
output = torch.flatten(imgs)
# 等價于
output = torch.reshape(imgs,(1,1,1,-1))
for data in dataloader:
imgs,targets = data
print(imgs.shape) #torch.Size([64, 3, 32, 32])
output = torch.reshape(imgs,(1,1,1,-1)) # 想把圖片展平
print(output.shape) # torch.Size([1, 1, 1, 196608])
output = tudui(output)
print(output.shape) # torch.Size([1, 1, 1, 10])
for data in dataloader:
imgs,targets = data
print(imgs.shape) #torch.Size([64, 3, 32, 32])
output = torch.flatten(imgs) #攤平
print(output.shape) #torch.Size([196608])
output = tudui(output)
print(output.shape) #torch.Size([10])
?--------------------------------------------------------------------------------------------------------------------------------
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=64,drop_last=True)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.linear1 = Linear(196608,10)
def forward(self,input):
output = self.linear1(input)
return output
tudui = Tudui()
for data in dataloader:
imgs,targets = data
print(imgs.shape) #torch.Size([64, 3, 32, 32])
# output = torch.reshape(imgs,(1,1,1,-1)) # 想把圖片展平
# print(output.shape) # torch.Size([1, 1, 1, 196608])
# output = tudui(output)
# print(output.shape) # torch.Size([1, 1, 1, 10])
output = torch.flatten(imgs) #攤平
print(output.shape) #torch.Size([196608])
output = tudui(output)
print(output.shape) #torch.Size([10])
?運行結(jié)果如下:
---------------------------------------------------------------------------------------------------------------------------------
Dropout Layers(不難,自學(xué))
Dropout — PyTorch 1.10 documentation
在訓(xùn)練過程中,隨機(jī)把一些 input(輸入的tensor數(shù)據(jù)類型)中的一些元素變?yōu)?,變?yōu)?的概率為p
目的:防止過擬合
---------------------------------------------------------------------------------------------------------------------------------
Sparse Layers(特定網(wǎng)絡(luò)中使用,自學(xué))
Embedding
Embedding — PyTorch 1.10 documentation
用于自然語言處理?
--------------------------------------------------------------------------------------------------------------------------------?
Distance Functions
計算兩個值之間的誤差
torch.nn — PyTorch 1.13 documentation
--------------------------------------------------------------------------------------------------------------------------------?
Loss Functions
loss 誤差大小
torch.nn — PyTorch 1.13 documentation
---------------------------------------------------------------------------------------------------------------------------------
pytorch提供的一些網(wǎng)絡(luò)模型
-
圖片相關(guān):torchvision? ?torchvision.models — Torchvision 0.11.0 documentation
分類、語義分割、目標(biāo)檢測、實例分割、人體關(guān)鍵節(jié)點識別(姿態(tài)估計)等等 - 文本相關(guān):torchtext? ?無
- 語音相關(guān):torchaudio??torchaudio.models — Torchaudio 0.10.0 documentation
下一節(jié):Container ——> Sequential(序列)
18. 神經(jīng)網(wǎng)絡(luò) - 搭建小實戰(zhàn)和 Sequential 的使用
Containers中有Module、Sequential等?
Sequential — PyTorch 1.10 documentation
Example:
# Using Sequential to create a small model. When `model` is run,
# input will first be passed to `Conv2d(1,20,5)`. The output of
# `Conv2d(1,20,5)` will be used as the input to the first
# `ReLU`; the output of the first `ReLU` will become the input
# for `Conv2d(20,64,5)`. Finally, the output of
# `Conv2d(20,64,5)` will be used as input to the second `ReLU`
model = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
# Using Sequential with OrderedDict. This is functionally the
# same as the above code
model = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(1,20,5)),
('relu1', nn.ReLU()),
('conv2', nn.Conv2d(20,64,5)),
('relu2', nn.ReLU())
]))
好處:代碼簡潔易懂
---------------------------------------------------------------------------------------------------------------------------------
對 CIFAR10 進(jìn)行分類的簡單神經(jīng)網(wǎng)絡(luò)
CIFAR 10:根據(jù)圖片內(nèi)容,識別其究竟屬于哪一類(10代表有10個類別)
CIFAR-10 and CIFAR-100 datasets
第一次卷積:首先加了幾圈 padding(圖像大小不變,還是32x32),然后卷積了32次
- Conv2d — PyTorch 1.10 documentation
- 輸入尺寸是32x32,經(jīng)過卷積后尺寸不變,如何設(shè)置參數(shù)?? —— padding=2,stride=1
- 計算公式:
幾個卷積核就是幾通道的,一個卷積核作用于RGB三個通道后會把得到的三個矩陣的對應(yīng)值相加,也就是說會合并,所以一個卷積核會產(chǎn)生一個通道
任何卷積核在設(shè)置padding的時候為保持輸出尺寸不變都是卷積核大小的一半
通道變化時通過調(diào)整卷積核的個數(shù)(即輸出通道)來實現(xiàn)的,在 nn.conv2d 的參數(shù)中有 out_channel 這個參數(shù),就是對應(yīng)輸出通道
kernel 的內(nèi)容是不一樣的,可以理解為不同的特征抓取,因此一個核會產(chǎn)生一個channel
直接搭建,實現(xiàn)上圖 CIFAR10 model 的代碼
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2) #第一個卷積
self.maxpool1 = MaxPool2d(kernel_size=2) #池化
self.conv2 = Conv2d(32,32,5,padding=2) #維持尺寸不變,所以padding仍為2
self.maxpool2 = MaxPool2d(2)
self.conv3 = Conv2d(32,64,5,padding=2)
self.maxpool3 = MaxPool2d(2)
self.flatten = Flatten() #展平為64x4x4=1024個數(shù)據(jù)
# 經(jīng)過兩個線性層:第一個線性層(1024為in_features,64為out_features)、第二個線性層(64為in_features,10為out_features)
self.linear1 = Linear(1024,64)
self.linear2 = Linear(64,10) #10為10個類別,若預(yù)測的是概率,則取最大概率對應(yīng)的類別,為該圖片網(wǎng)絡(luò)預(yù)測到的類別
def forward(self,x): #x為input
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.maxpool3(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.linear2(x)
return x
tudui = Tudui()
print(tudui)
可以看到網(wǎng)絡(luò)結(jié)構(gòu):
---------------------------------------------------------------------------------------------------------------------------------
實際過程中如何檢查網(wǎng)絡(luò)的正確性?
核心:一定尺寸的數(shù)據(jù)經(jīng)過網(wǎng)絡(luò)后,能夠得到我們想要的輸出
對網(wǎng)絡(luò)結(jié)構(gòu)進(jìn)行檢驗的代碼:?
input = torch.ones((64,3,32,32)) #全是1,batch_size=64,3通道,32x32
output = tudui(input)
print(output.shape)
運行結(jié)果:
torch.Size([64, 10])
--------------------------------------------------------------------------------------------------------------------------------?
若不知道flatten之后的維度是多少該怎么辦?
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2) #第一個卷積
self.maxpool1 = MaxPool2d(kernel_size=2) #池化
self.conv2 = Conv2d(32,32,5,padding=2) #維持尺寸不變,所以padding仍為2
self.maxpool2 = MaxPool2d(2)
self.conv3 = Conv2d(32,64,5,padding=2)
self.maxpool3 = MaxPool2d(2)
self.flatten = Flatten() #展平為64x4x4=1024個數(shù)據(jù)
# 經(jīng)過兩個線性層:第一個線性層(1024為in_features,64為out_features)、第二個線性層(64為in_features,10為out_features)
self.linear1 = Linear(1024,64)
self.linear2 = Linear(64,10) #10為10個類別,若預(yù)測的是概率,則取最大概率對應(yīng)的類別,為該圖片網(wǎng)絡(luò)預(yù)測到的類別
def forward(self,x): #x為input
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.maxpool3(x)
x = self.flatten(x)
return x
tudui = Tudui()
print(tudui)
input = torch.ones((64,3,32,32)) #全是1,batch_size=64(64張圖片),3通道,32x32
output = tudui(input)
print(output.shape) # torch.Size([64,1024])
?看到輸出的維度是(64,1024),64可以理解為64張圖片,1024就是flatten之后的維度了
---------------------------------------------------------------------------------------------------------------------------------
用 Sequential?搭建,實現(xiàn)上圖 CIFAR10 model 的代碼
作用:代碼更加簡潔
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024,64),
Linear(64,10)
)
def forward(self,x): #x為input
x = self.model1(x)
return x
tudui = Tudui()
print(tudui)
input = torch.ones((64,3,32,32)) #全是1,batch_size=64,3通道,32x32
output = tudui(input)
print(output.shape)
運行結(jié)果:
--------------------------------------------------------------------------------------------------------------------------------?
引入 tensorboard 可視化模型結(jié)構(gòu)
在上述代碼后面加上以下代碼:
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("../logs_seq")
writer.add_graph(tudui,input) # add_graph 計算圖
writer.close()
運行后在 terminal 里輸入:
tensorboard --logdir=logs_seq
?打開網(wǎng)址,雙擊圖片中的矩形,可以放大每個部分:
19. 損失函數(shù)與反向傳播
torch.nn 里的 loss function 衡量誤差,在使用過程中根據(jù)需求使用,注意輸入形狀和輸出形狀即可
loss 衡量實際神經(jīng)網(wǎng)絡(luò)輸出 output 與真實想要結(jié)果 target 的差距,越小越好
作用:
- 計算實際輸出和目標(biāo)之間的差距
- 為我們更新輸出提供一定的依據(jù)(反向傳播):給每一個卷積核中的參數(shù)提供了梯度 grad,采用反向傳播時,每一個要更新的參數(shù)都會計算出對應(yīng)的梯度,優(yōu)化過程中根據(jù)梯度對參數(shù)進(jìn)行優(yōu)化,最終達(dá)到整個 loss 進(jìn)行降低的目的
梯度下降法:
---------------------------------------------------------------------------------------------------------------------------------
L1LOSS
input:(N,*)? ? N是batch_size,即有多少個數(shù)據(jù);*可以是任意維度?
CLASS torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
代碼
- 取平均的方式:?
import torch
from torch.nn import L1Loss
# 實際數(shù)據(jù)或網(wǎng)絡(luò)默認(rèn)情況下就是float類型,不寫測試案例的話一般不需要加dtype
inputs = torch.tensor([1,2,3],dtype=torch.float32) # 計算時要求數(shù)據(jù)類型為浮點數(shù),不能是整型的long
targets = torch.tensor([1,2,5],dtype=torch.float32)
inputs = torch.reshape(inputs,(1,1,1,3)) # 1 batch_size, 1 channel, 1行3列
targets = torch.reshape(targets,(1,1,1,3))
loss = L1Loss()
result = loss(inputs,targets)
print(result)
運行結(jié)果:
tensor(0.6667)
- 求和的方式:
修改上述代碼中的一句即可
loss = L1Loss(reduction='sum')
運行結(jié)果:
tensor(2.)
---------------------------------------------------------------------------------------------------------------------------------
MSELOSS(均方誤差)
input:(N,*)? ? N是batch_size,即有多少個數(shù)據(jù);*可以是任意維度?
CLASS torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
代碼
import torch
from torch import nn
# 實際數(shù)據(jù)或網(wǎng)絡(luò)默認(rèn)情況下就是float類型,不寫測試案例的話一般不需要加dtype
inputs = torch.tensor([1,2,3],dtype=torch.float32) # 計算時要求數(shù)據(jù)類型為浮點數(shù),不能是整型的long
targets = torch.tensor([1,2,5],dtype=torch.float32)
inputs = torch.reshape(inputs,(1,1,1,3)) # 1 batch_size, 1 channel, 1行3列
targets = torch.reshape(targets,(1,1,1,3))
loss_mse = nn.MSELoss()
result_mse = loss_mse(inputs,targets)
print(result_mse)
結(jié)果:
tensor(1.3333)
---------------------------------------------------------------------------------------------------------------------------------
CROSSENTROPYLOSS(交叉熵)
適用于訓(xùn)練分類問題,有C個類別
例:三分類問題,person,dog,cat
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-137.png)
這里的output不是概率,是評估分?jǐn)?shù)
input 為沒有進(jìn)行處理過的對每一類的得分
代碼:
x = torch.tensor([0.1,0.2,0.3])
y = torch.tensor([1])
x = torch.reshape(x,(1,3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x,y)
print(result_cross)
結(jié)果:
tensor(1.1019)
---------------------------------------------------------------------------------------------------------------------------------
如何在之前寫的神經(jīng)網(wǎng)絡(luò)中用到 Loss Function(損失函數(shù))
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
dataset = torchvision.datasets.CIFAR10("../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=1)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024,64),
Linear(64,10)
)
def forward(self,x): # x為input
x = self.model1(x)
return x
loss = nn.CrossEntropyLoss()
tudui = Tudui()
for data in dataloader:
imgs,targets = data # imgs為輸入,放入神經(jīng)網(wǎng)絡(luò)中
outputs = tudui(imgs) # outputs為輸入通過神經(jīng)網(wǎng)絡(luò)得到的輸出,targets為實際輸出
result_loss = loss(outputs,targets)
print(result_loss) # 神經(jīng)網(wǎng)絡(luò)輸出與真實輸出的誤差
結(jié)果:
---------------------------------------------------------------------------------------------------------------------------------
backward? 反向傳播
計算出每一個節(jié)點參數(shù)的梯度
在上述代碼后加一行:
result_loss.backward() # backward反向傳播,是對result_loss,而不是對loss
在這一句代碼前打上斷點(運行到該行代碼的前一行,該行不運行),debug 后:
?tudui ——> model 1 ——> Protected Attributes ——> _modules ——> '0' ——> bias / weight——> grad(是None)
點擊Step into My Code,運行完該行后,可以發(fā)現(xiàn)剛剛的None有值了(損失函數(shù)一定要經(jīng)過 .backward() 后才能反向傳播,才能有每個需要調(diào)節(jié)的參數(shù)的grad的值)
下一節(jié):選擇合適的優(yōu)化器,利用梯度對網(wǎng)絡(luò)中的參數(shù)進(jìn)行優(yōu)化更新,以達(dá)到整個 loss最低的目的
20. 優(yōu)化器
當(dāng)使用損失函數(shù)時,可以調(diào)用損失函數(shù)的 backward,得到反向傳播,反向傳播可以求出每個需要調(diào)節(jié)的參數(shù)對應(yīng)的梯度,有了梯度就可以利用優(yōu)化器,優(yōu)化器根據(jù)梯度對參數(shù)進(jìn)行調(diào)整,以達(dá)到整體誤差降低的目的
torch.optim — PyTorch 1.10 documentation
---------------------------------------------------------------------------------------------------------------------------------
如何使用優(yōu)化器?
(1)構(gòu)造
# Example:
# SGD為構(gòu)造優(yōu)化器的算法,Stochastic Gradient Descent 隨機(jī)梯度下降
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9) #模型參數(shù)、學(xué)習(xí)速率、特定優(yōu)化器算法中需要設(shè)定的參數(shù)
optimizer = optim.Adam([var1, var2], lr=0.0001)
(2)調(diào)用優(yōu)化器的step方法
利用之前得到的梯度對參數(shù)進(jìn)行更新
for input, target in dataset:
optimizer.zero_grad() #把上一步訓(xùn)練的每個參數(shù)的梯度清零
output = model(input)
loss = loss_fn(output, target) # 輸出跟真實的target計算loss
loss.backward() #調(diào)用反向傳播得到每個要更新參數(shù)的梯度
optimizer.step() #每個參數(shù)根據(jù)上一步得到的梯度進(jìn)行優(yōu)化
---------------------------------------------------------------------------------------------------------------------------------
算法
如Adadelta、Adagrad、Adam、RMSProp、SGD等等,不同算法前兩個參數(shù):params、lr 都是一致的,后面的參數(shù)不同
CLASS torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0)
# params為模型的參數(shù)、lr為學(xué)習(xí)速率(learning rate)
# 后續(xù)參數(shù)都是特定算法中需要設(shè)置的
學(xué)習(xí)速率不能太大(太大模型訓(xùn)練不穩(wěn)定)也不能太小(太小模型訓(xùn)練慢),一般建議先采用較大學(xué)習(xí)速率,后采用較小學(xué)習(xí)速率
--------------------------------------------------------------------------------------------------------------------------------?
SGD為例?
以 SGD(隨機(jī)梯度下降法)為例進(jìn)行說明:?
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
# 加載數(shù)據(jù)集并轉(zhuǎn)為tensor數(shù)據(jù)類型
dataset = torchvision.datasets.CIFAR10("../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
# 加載數(shù)據(jù)集
dataloader = DataLoader(dataset,batch_size=1)
# 創(chuàng)建網(wǎng)絡(luò)名叫Tudui
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024,64),
Linear(64,10)
)
def forward(self,x): # x為input,forward前向傳播
x = self.model1(x)
return x
# 計算loss
loss = nn.CrossEntropyLoss()
# 搭建網(wǎng)絡(luò)
tudui = Tudui()
# 設(shè)置優(yōu)化器
optim = torch.optim.SGD(tudui.parameters(),lr=0.01) # SGD隨機(jī)梯度下降法
for data in dataloader:
imgs,targets = data # imgs為輸入,放入神經(jīng)網(wǎng)絡(luò)中
outputs = tudui(imgs) # outputs為輸入通過神經(jīng)網(wǎng)絡(luò)得到的輸出,targets為實際輸出
result_loss = loss(outputs,targets)
optim.zero_grad() # 把網(wǎng)絡(luò)模型中每一個可以調(diào)節(jié)的參數(shù)對應(yīng)梯度設(shè)置為0
result_loss.backward() # backward反向傳播求出每一個節(jié)點的梯度,是對result_loss,而不是對loss
optim.step() # 對每個參數(shù)進(jìn)行調(diào)優(yōu)
可以在以下地方打斷點,debug:
tudui ——> Protected Attributes ——> _modules ——> 'model1' ——> Protected Attributes ——> _modules ——> '0' ——> weight ——> data 或 grad
通過每次按箭頭所指的按鈕(點一次運行一行),觀察 data 和 grad 值的變化
- 第一行 optim.zero_grad() 是讓grad清零
- 第三行 optim.step() 會通過grad更新data
--------------------------------------------------------------------------------------------------------------------------------?
完整代碼
在 data 循環(huán)外又套一層 epoch 循環(huán),一次 data 循環(huán)相當(dāng)于對數(shù)據(jù)訓(xùn)練一次,加了 epoch 循環(huán)相當(dāng)于對數(shù)據(jù)訓(xùn)練 20 次
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
# 加載數(shù)據(jù)集并轉(zhuǎn)為tensor數(shù)據(jù)類型
dataset = torchvision.datasets.CIFAR10("../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=1)
# 創(chuàng)建網(wǎng)絡(luò)名叫Tudui
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
Conv2d(3,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,32,5,padding=2),
MaxPool2d(2),
Conv2d(32,64,5,padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024,64),
Linear(64,10)
)
def forward(self,x): # x為input,forward前向傳播
x = self.model1(x)
return x
# 計算loss
loss = nn.CrossEntropyLoss()
# 搭建網(wǎng)絡(luò)
tudui = Tudui()
# 設(shè)置優(yōu)化器
optim = torch.optim.SGD(tudui.parameters(),lr=0.01) # SGD隨機(jī)梯度下降法
for epoch in range(20):
running_loss = 0.0 # 在每一輪開始前將loss設(shè)置為0
for data in dataloader: # 該循環(huán)相當(dāng)于只對數(shù)據(jù)進(jìn)行了一輪學(xué)習(xí)
imgs,targets = data # imgs為輸入,放入神經(jīng)網(wǎng)絡(luò)中
outputs = tudui(imgs) # outputs為輸入通過神經(jīng)網(wǎng)絡(luò)得到的輸出,targets為實際輸出
result_loss = loss(outputs,targets)
optim.zero_grad() # 把網(wǎng)絡(luò)模型中每一個可以調(diào)節(jié)的參數(shù)對應(yīng)梯度設(shè)置為0
result_loss.backward() # backward反向傳播求出每一個節(jié)點的梯度,是對result_loss,而不是對loss
optim.step() # 對每個參數(shù)進(jìn)行調(diào)優(yōu)
running_loss = running_loss + result_loss # 每一輪所有l(wèi)oss的和
print(running_loss)
部分運行結(jié)果:
優(yōu)化器對模型參數(shù)不斷進(jìn)行優(yōu)化,每一輪的 loss 在不斷減小
實際過程中模型在整個數(shù)據(jù)集上的訓(xùn)練次數(shù)(即最外層的循環(huán))都是成百上千/萬的,本例僅以 20 次為例
21. 現(xiàn)有網(wǎng)絡(luò)模型的使用及修改
本節(jié)主要講解 torchvision
本節(jié)主要講解 Classification 里的 VGG 模型,數(shù)據(jù)集仍為 CIFAR10 數(shù)據(jù)集(主要用于分類)
torchvision.models — Torchvision 0.11.0 documentation
---------------------------------------------------------------------------------------------------------------------------------
數(shù)據(jù)集 ImageNet
注意:必須要先有 package scipy
在 Terminal 里輸入
pip list
尋找是否有 scipy,若沒有的話輸入
pip install scipy
(注意關(guān)閉代理?。。。?/strong>
---------------------------------------------------------------------------------------------------------------------------------
參數(shù)及下載?
下載 ImageNet 數(shù)據(jù)集:
import torchvision.datasets
train_data = torchvision.datasets.ImageNet("../data_image_net",split='train',download=True,
transform=torchvision.transforms.ToTensor())
./xxx表示當(dāng)前路徑下,../xxx表示返回上一級目錄?
多行注釋快捷鍵:ctrl+/
運行后會報錯:
RuntimeError: The dataset is no longer publicly accessible. You need to download the archives externally and place them in the root directory.
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-147.png)
下載地址:
Imagenet 完整數(shù)據(jù)集下載_wendell 的博客-CSDN博客_imagenet下載
100 多個G,太大... 放棄
按住 Ctrl 鍵,點擊 ImageNet,查看其源碼:
--------------------------------------------------------------------------------------------------------------------------------?
VGG16 模型
VGG 11/13/16/19? 常用16和19
參數(shù) pretrained=True/False?
- pretrained 為 False 的情況下,只是加載網(wǎng)絡(luò)模型,參數(shù)都為默認(rèn)參數(shù),不需要下載
- 為 True 時需要從網(wǎng)絡(luò)中下載,卷積層、池化層對應(yīng)的參數(shù)等等(在ImageNet數(shù)據(jù)集中訓(xùn)練好的)
import torchvision.models
vgg16_false = torchvision.models.vgg16(pretrained=False) # 另一個參數(shù)progress顯示進(jìn)度條,默認(rèn)為True
vgg16_true = torchvision.models.vgg16(pretrained=True)
print('ok')
斷點打在 print('ok') 前,debug 一下,結(jié)果如圖:
?
vgg16_true ——> classifier ——> Protected Attributes ——> modules ——> '0'(線性層)?——> weight
為 false 的情況,同理找到 weight 值:
總結(jié):?
- 設(shè)置為 False 的情況,相當(dāng)于網(wǎng)絡(luò)模型中的參數(shù)都是初始化的、默認(rèn)的
- 設(shè)置為 True 時,網(wǎng)絡(luò)模型中的參數(shù)在數(shù)據(jù)集上是訓(xùn)練好的,能達(dá)到比較好的效果
---------------------------------------------------------------------------------------------------------------------------------
vgg16 網(wǎng)絡(luò)架構(gòu)?
import torchvision.models
vgg16_false = torchvision.models.vgg16(pretrained=False) # 另一個參數(shù)progress顯示進(jìn)度條,默認(rèn)為True
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)
輸出:
VGG(
(features): Sequential(
# 輸入圖片先經(jīng)過卷積,輸入是3通道的、輸出是64通道的,卷積核大小是3×3的
(0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
# 非線性
(1): ReLU(inplace=True)
# 卷積、非線性、池化...
(2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(3): ReLU(inplace=True)
(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(6): ReLU(inplace=True)
(7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(8): ReLU(inplace=True)
(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): ReLU(inplace=True)
(12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(13): ReLU(inplace=True)
(14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(15): ReLU(inplace=True)
(16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(18): ReLU(inplace=True)
(19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(20): ReLU(inplace=True)
(21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(22): ReLU(inplace=True)
(23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(25): ReLU(inplace=True)
(26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(27): ReLU(inplace=True)
(28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(29): ReLU(inplace=True)
(30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(avgpool): AdaptiveAvgPool2d(output_size=(7, 7))
(classifier): Sequential(
(0): Linear(in_features=25088, out_features=4096, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=4096, out_features=4096, bias=True)
(4): ReLU(inplace=True)
(5): Dropout(p=0.5, inplace=False)
# 最后線性層輸出為1000(vgg16也是一個分類模型,能分出1000個類別)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
)
?ImageNet
所以 out_features = 1000
---------------------------------------------------------------------------------------------------------------------------------
如何利用現(xiàn)有網(wǎng)絡(luò)去改動它的結(jié)構(gòu)?
train_data = torchvision.datasets.CIFAR10(root="./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)
CIFAR10 把數(shù)據(jù)分成了10類,而 vgg16 模型把數(shù)據(jù)分成了 1000 類,如何應(yīng)用這個網(wǎng)絡(luò)模型呢?
- 把最后線性層的 out_features 從1000改為10
- 在最后的線性層下面再加一層,in_features為1000,out_features為10
利用現(xiàn)有網(wǎng)絡(luò)去改動它的結(jié)構(gòu),避免寫 vgg16
很多框架會把 vgg16 當(dāng)做前置的網(wǎng)絡(luò)結(jié)構(gòu),提取一些特殊的特征,再在后面加一些網(wǎng)絡(luò)結(jié)構(gòu),實現(xiàn)功能
--------------------------------------------------------------------------------------------------------------------------------?
添加?
以 vgg16_true 為例講解,實現(xiàn)上面的第二種思路:
# 給 vgg16 添加一個線性層,輸入1000個類別,輸出10個類別
vgg16_true.add_module('add_linear',nn.Linear(in_features=1000,out_features=10))
print(vgg16_true)
結(jié)果如圖:
如果想將 module 添加至 classifier 里:
# 給 vgg16 添加一個線性層,輸入1000個類別,輸出10個類別
vgg16_true.classifier.add_module('add_linear',nn.Linear(in_features=1000,out_features=10))
print(vgg16_true)
?結(jié)果如圖:
---------------------------------------------------------------------------------------------------------------------------------
修改?
以上為添加,那么如何修改呢?
以 vgg16_false 為例:
vgg16_false = torchvision.models.vgg16(pretrained=False) # 另一個參數(shù)progress顯示進(jìn)度條,默認(rèn)為True
print(vgg16_false)
結(jié)果如下:
(classifier): Sequential(
(0): Linear(in_features=25088, out_features=4096, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=4096, out_features=4096, bias=True)
(4): ReLU(inplace=True)
(5): Dropout(p=0.5, inplace=False)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
)
想將最后一層 Linear 的 out_features 改為10:
vgg16_false.classifier[6] = nn.Linear(4096,10)
print(vgg16_false)
結(jié)果如下:
本節(jié):
- 如何加載現(xiàn)有的一些 pytorch 提供的網(wǎng)絡(luò)模型
- 如何對網(wǎng)絡(luò)模型中的結(jié)構(gòu)進(jìn)行修改,包括添加自己想要的一些網(wǎng)絡(luò)模型結(jié)構(gòu)
22. 網(wǎng)絡(luò)模型的保存與讀取
后續(xù)內(nèi)容:
---------------------------------------------------------------------------------------------------------------------------------
兩種方式保存模型
import torch
import torchvision.models
vgg16 = torchvision.models.vgg16(pretrained=False) # 網(wǎng)絡(luò)中模型的參數(shù)是沒有經(jīng)過訓(xùn)練的、初始化的參數(shù)
方式1:不僅保存了網(wǎng)絡(luò)模型的結(jié)構(gòu),也保存了網(wǎng)絡(luò)模型的參數(shù)
# 保存方式1:模型結(jié)構(gòu)+模型參數(shù)
torch.save(vgg16,"vgg16_method1.pth")
方式2:網(wǎng)絡(luò)模型的參數(shù)保存為字典,不保存網(wǎng)絡(luò)模型的結(jié)構(gòu)(官方推薦的保存方式,用的空間?。?/span>
# 保存方式2:模型參數(shù)(官方推薦)
torch.save(vgg16.state_dict(),"vgg16_method2.pth")
# 把vgg16的狀態(tài)保存為字典形式(Python中的一種數(shù)據(jù)格式)
運行后 src 文件夾底下會多出以下兩個文件:
---------------------------------------------------------------------------------------------------------------------------------
兩種方式加載模型
方式1:對應(yīng)保存方式1,打印出的是網(wǎng)絡(luò)模型的結(jié)構(gòu)
# 方式1 對應(yīng) 保存方式1,加載模型
model = torch.load("vgg16_method1.pth",)
print(model) # 打印出的只是模型的結(jié)構(gòu),其實它的參數(shù)也被保存下來了
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-159.png)
在print這句打上斷點,debug一下,可以看一下模型參數(shù)?
方式2:對應(yīng)保存方式2,打印出的是參數(shù)的字典形式
# 方式2 加載模型
model = torch.load("vgg16_method2.pth")
print(model)
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-160.png)
如何恢復(fù)網(wǎng)絡(luò)模型結(jié)構(gòu)?
import torchvision.models
vgg16 = torchvision.models.vgg16(pretrained=False) # 預(yù)訓(xùn)練設(shè)置為False
vgg16.load_state_dict(torch.load("vgg16_method2.pth")) # vgg16通過字典形式,加載狀態(tài)即參數(shù)
print(vgg16)
---------------------------------------------------------------------------------------------------------------------------------
方式1 有陷阱(自己定義網(wǎng)絡(luò)結(jié)構(gòu),沒有用 vgg16 時)
用方式1保存的話,加載時要讓程序能夠訪問到其定義模型的一種方式?
問題描述?
首先在 model_save.py 中寫以下代碼:
# 陷阱
from torch import nn
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv = nn.Conv2d(3,64,kernel_size=3)
def forward(self,x): # x為輸入
x = self.conv(x)
return x
tudui = Tudui() # 有一個卷積層和一些初始化的參數(shù)
torch.save(tudui,"tudui_method1.pth")
運行后 src 文件夾底下多出一個文件:
再在 model_load.py 中寫以下代碼:
# 陷阱
import torch
model = torch.load("tudui_method1.pth")
print(model)
運行后發(fā)現(xiàn)報錯:
AttributeError: Can't get attribute 'Tudui' on <module '__main__' from 'C:/Users/11842/Desktop/Learn_torch/src/model_load.py'>
# 不能得到'Tudui'這個屬性,因為沒有這個類
--------------------------------------------------------------------------------------------------------------------------------?
解決
還是需要將 model_save.py 中的網(wǎng)絡(luò)結(jié)構(gòu)復(fù)制到 model_load.py 中,即下列代碼需要復(fù)制到 model_load.py 中(為了確保加載的網(wǎng)絡(luò)模型是想要的網(wǎng)絡(luò)模型):
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv = nn.Conv2d(3,64,kernel_size=3)
def forward(self,x): # x為輸入
x = self.conv(x)
return x
但是不需要創(chuàng)建了,即在 model_load.py 中不需要寫:
tudui = Tudui()
此時 model_load.py 完整代碼為:
# 陷阱
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.conv = nn.Conv2d(3,64,kernel_size=3)
def forward(self,x): # x為輸入
x = self.conv(x)
return x
model = torch.load("tudui_method1.pth")
print(model)
運行結(jié)果如下:
解決另法:
實際寫項目過程中,直接定義在一個單獨的文件中(如model_save.py),再在 model_load.py 中:
from model_save import *
23. 完整的模型訓(xùn)練套路
以 CIFAR10 數(shù)據(jù)集為例,分類問題(10分類)
在語句后面按 Ctrl + d 可以復(fù)制這條語句
---------------------------------------------------------------------------------------------------------------------------------
model.py 文件代碼
import torch
from torch import nn
# 搭建神經(jīng)網(wǎng)絡(luò)(10分類網(wǎng)絡(luò))
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
# 把網(wǎng)絡(luò)放到序列中
self.model = nn.Sequential(
nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入是32x32的,輸出還是32x32的(padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入輸出都是16x16的(同理padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
nn.MaxPool2d(kernel_size=2),
nn.Flatten(), # 展平
nn.Linear(in_features=64*4*4,out_features=64),
nn.Linear(in_features=64,out_features=10)
)
def forward(self,x):
x = self.model(x)
return x
if __name__ == '__main__':
# 測試網(wǎng)絡(luò)的驗證正確性
tudui = Tudui()
input = torch.ones((64,3,32,32)) # batch_size=64(代表64張圖片),3通道,32x32
output = tudui(input)
print(output.shape)
運行結(jié)果如下:
返回64行數(shù)據(jù),每一行數(shù)據(jù)有10個數(shù)據(jù),代表每一張圖片在10個類別中的概率
---------------------------------------------------------------------------------------------------------------------------------
train.py 文件代碼
(與 model.py 文件必須在同一個文件夾底下)
import torchvision.datasets
from model import *
from torch import nn
from torch.utils.data import DataLoader
# 準(zhǔn)備數(shù)據(jù)集,CIFAR10 數(shù)據(jù)集是PIL Image,要轉(zhuǎn)換為tensor數(shù)據(jù)類型
train_data = torchvision.datasets.CIFAR10(root="../data",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10(root="../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
# 看一下訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集都有多少張(如何獲得數(shù)據(jù)集的長度)
train_data_size = len(train_data) # length 長度
test_data_size = len(test_data)
# 如果train_data_size=10,那么打印出的字符串為:訓(xùn)練數(shù)據(jù)集的長度為:10
print("訓(xùn)練數(shù)據(jù)集的長度為:{}".format(train_data_size)) # 字符串格式化,把format中的變量替換{}
print("測試數(shù)據(jù)集的長度為:{}".format(test_data_size))
# 利用 DataLoader 來加載數(shù)據(jù)集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)
# 創(chuàng)建網(wǎng)絡(luò)模型
tudui = Tudui()
# 創(chuàng)建損失函數(shù)
loss_fn = nn.CrossEntropyLoss() # 分類問題可以用交叉熵
# 定義優(yōu)化器
learning_rate = 0.01 # 另一寫法:1e-2,即1x 10^(-2)=0.01
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate) # SGD 隨機(jī)梯度下降
# 設(shè)置訓(xùn)練網(wǎng)絡(luò)的一些參數(shù)
total_train_step = 0 # 記錄訓(xùn)練次數(shù)
total_test_step = 0 # 記錄測試次數(shù)
epoch = 10 # 訓(xùn)練輪數(shù)
for i in range(epoch):
print("----------第{}輪訓(xùn)練開始-----------".format(i+1)) # i從0-9
# 訓(xùn)練步驟開始
for data in train_dataloader: # 從訓(xùn)練的dataloader中取數(shù)據(jù)
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets)
# 優(yōu)化器優(yōu)化模型
optimizer.zero_grad() # 首先要梯度清零
loss.backward() # 反向傳播得到每一個參數(shù)節(jié)點的梯度
optimizer.step() # 對參數(shù)進(jìn)行優(yōu)化
total_train_step += 1
print("訓(xùn)練次數(shù):{},loss:{}".format(total_train_step,loss.item()))
運行結(jié)果如下:?
print(a) 和 print(a.item()) 的區(qū)別
import torch a = torch.tensor(5) print(a) print(a.item())
---------------------------------------------------------------------------------------------------------------------------------
如何知道模型是否訓(xùn)練好,或達(dá)到需求?
每次訓(xùn)練完一輪就進(jìn)行測試,以測試數(shù)據(jù)集上的損失或正確率來評估模型有沒有訓(xùn)練好
測試過程中不需要對模型進(jìn)行調(diào)優(yōu),利用現(xiàn)有模型進(jìn)行測試,所以有以下命令:
with torch.no_grad():
在上述 train.py 代碼后繼續(xù)寫:
# 測試步驟開始
total_test_loss = 0
with torch.no_grad(): # 無梯度,不進(jìn)行調(diào)優(yōu)
for data in test_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets) # 該loss為部分?jǐn)?shù)據(jù)在網(wǎng)絡(luò)模型上的損失,為tensor數(shù)據(jù)類型
# 求整體測試數(shù)據(jù)集上的誤差或正確率
total_test_loss = total_test_loss + loss.item() # loss為tensor數(shù)據(jù)類型,而total_test_loss為普通數(shù)字,所以要.item()一下
print("整體測試集上的Loss:{}".format(total_test_loss))
結(jié)果如下:
此處為了使測試的 loss 結(jié)果易找,在?train.py 中添加了一句 if 的代碼,使train每訓(xùn)練100輪才打印1次:
if total_train_step % 100 ==0: # 逢百才打印記錄
print("訓(xùn)練次數(shù):{},loss:{}".format(total_train_step,loss.item()))
完整代碼
import torchvision.datasets
from model import *
from torch import nn
from torch.utils.data import DataLoader
# 準(zhǔn)備數(shù)據(jù)集,CIFAR10 數(shù)據(jù)集是PIL Image,要轉(zhuǎn)換為tensor數(shù)據(jù)類型
train_data = torchvision.datasets.CIFAR10(root="../data",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10(root="../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
# 看一下訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集都有多少張(如何獲得數(shù)據(jù)集的長度)
train_data_size = len(train_data) # length 長度
test_data_size = len(test_data)
# 如果train_data_size=10,那么打印出的字符串為:訓(xùn)練數(shù)據(jù)集的長度為:10
print("訓(xùn)練數(shù)據(jù)集的長度為:{}".format(train_data_size)) # 字符串格式化,把format中的變量替換{}
print("測試數(shù)據(jù)集的長度為:{}".format(test_data_size))
# 利用 DataLoader 來加載數(shù)據(jù)集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)
# 創(chuàng)建網(wǎng)絡(luò)模型
tudui = Tudui()
# 創(chuàng)建損失函數(shù)
loss_fn = nn.CrossEntropyLoss() # 分類問題可以用交叉熵
# 定義優(yōu)化器
learning_rate = 0.01 # 另一寫法:1e-2,即1x 10^(-2)=0.01
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate) # SGD 隨機(jī)梯度下降
# 設(shè)置訓(xùn)練網(wǎng)絡(luò)的一些參數(shù)
total_train_step = 0 # 記錄訓(xùn)練次數(shù)
total_test_step = 0 # 記錄測試次數(shù)
epoch = 10 # 訓(xùn)練輪數(shù)
for i in range(epoch):
print("----------第{}輪訓(xùn)練開始-----------".format(i+1)) # i從0-9
# 訓(xùn)練步驟開始
for data in train_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets)
# 優(yōu)化器優(yōu)化模型
optimizer.zero_grad() # 首先要梯度清零
loss.backward() # 反向傳播得到每一個參數(shù)節(jié)點的梯度
optimizer.step() # 對參數(shù)進(jìn)行優(yōu)化
total_train_step += 1
if total_train_step % 100 ==0: # 逢百才打印記錄
print("訓(xùn)練次數(shù):{},loss:{}".format(total_train_step,loss.item()))
# 測試步驟開始
total_test_loss = 0
with torch.no_grad(): # 無梯度,不進(jìn)行調(diào)優(yōu)
for data in test_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets) # 該loss為部分?jǐn)?shù)據(jù)在網(wǎng)絡(luò)模型上的損失,為tensor數(shù)據(jù)類型
# 求整體測試數(shù)據(jù)集上的誤差或正確率
total_test_loss = total_test_loss + loss.item() # loss為tensor數(shù)據(jù)類型,而total_test_loss為普通數(shù)字
print("整體測試集上的Loss:{}".format(total_test_loss))
---------------------------------------------------------------------------------------------------------------------------------
與 TensorBoard 結(jié)合
import torchvision.datasets
from torch.utils.tensorboard import SummaryWriter
from model import *
from torch import nn
from torch.utils.data import DataLoader
# 準(zhǔn)備數(shù)據(jù)集,CIFAR10 數(shù)據(jù)集是PIL Image,要轉(zhuǎn)換為tensor數(shù)據(jù)類型
train_data = torchvision.datasets.CIFAR10(root="../data",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10(root="../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
# 看一下訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集都有多少張(如何獲得數(shù)據(jù)集的長度)
train_data_size = len(train_data) # length 長度
test_data_size = len(test_data)
# 如果train_data_size=10,那么打印出的字符串為:訓(xùn)練數(shù)據(jù)集的長度為:10
print("訓(xùn)練數(shù)據(jù)集的長度為:{}".format(train_data_size)) # 字符串格式化,把format中的變量替換{}
print("測試數(shù)據(jù)集的長度為:{}".format(test_data_size))
# 利用 DataLoader 來加載數(shù)據(jù)集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)
# 創(chuàng)建網(wǎng)絡(luò)模型
tudui = Tudui()
# 創(chuàng)建損失函數(shù)
loss_fn = nn.CrossEntropyLoss() # 分類問題可以用交叉熵
# 定義優(yōu)化器
learning_rate = 0.01 # 另一寫法:1e-2,即1x 10^(-2)=0.01
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate) # SGD 隨機(jī)梯度下降
# 設(shè)置訓(xùn)練網(wǎng)絡(luò)的一些參數(shù)
total_train_step = 0 # 記錄訓(xùn)練次數(shù)
total_test_step = 0 # 記錄測試次數(shù)
epoch = 10 # 訓(xùn)練輪數(shù)
# 添加tensorboard
writer = SummaryWriter("../logs_train")
for i in range(epoch):
print("----------第{}輪訓(xùn)練開始-----------".format(i+1)) # i從0-9
# 訓(xùn)練步驟開始
for data in train_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets)
# 優(yōu)化器優(yōu)化模型
optimizer.zero_grad() # 首先要梯度清零
loss.backward() # 反向傳播得到每一個參數(shù)節(jié)點的梯度
optimizer.step() # 對參數(shù)進(jìn)行優(yōu)化
total_train_step += 1
if total_train_step % 100 ==0: # 逢百才打印記錄
print("訓(xùn)練次數(shù):{},loss:{}".format(total_train_step,loss.item()))
writer.add_scalar("train_loss",loss.item(),total_train_step)
# 測試步驟開始
total_test_loss = 0
with torch.no_grad(): # 無梯度,不進(jìn)行調(diào)優(yōu)
for data in test_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets) # 該loss為部分?jǐn)?shù)據(jù)在網(wǎng)絡(luò)模型上的損失,為tensor數(shù)據(jù)類型
# 求整體測試數(shù)據(jù)集上的誤差或正確率
total_test_loss = total_test_loss + loss.item() # loss為tensor數(shù)據(jù)類型,而total_test_loss為普通數(shù)字
print("整體測試集上的Loss:{}".format(total_test_loss))
writer.add_scalar("test_loss",total_test_loss,total_test_step)
total_test_step += 1
writer.close()
運行結(jié)果:
在 Terminal 里輸入:
tensorboard --logdir=logs_train
---------------------------------------------------------------------------------------------------------------------------------
保存每一輪訓(xùn)練的模型?
添加兩句代碼:?
torch.save(tudui,"tudui_{}.pth".format(i)) # 每一輪保存一個結(jié)果
print("模型已保存")
writer.close()
運行后:
??
---------------------------------------------------------------------------------------------------------------------------------
正確率的實現(xiàn)(對分類問題)?
即便得到整體測試集上的 loss,也不能很好說明在測試集上的表現(xiàn)效果
- 在分類問題中可以用正確率表示(下述代碼改進(jìn))
- 在目標(biāo)檢測/語義分割中,可以把輸出放在tensorboard里顯示,看測試結(jié)果
例子:?
第一步:
import torch
outputs = torch.tensor([[0.1,0.2],
[0.3,0.4]])
print(outputs.argmax(1)) # 0或1表示方向,1為橫向比較大小. 運行結(jié)果:tensor([1, 1])
第二步:
preds = outputs.argmax(1)
targets = torch.tensor([0,1])
print(preds == targets) # tensor([False, True])
print(sum(preds == targets).sum()) # tensor(1),對應(yīng)位置相等的個數(shù)
上例說明了基本用法,現(xiàn)對原問題的代碼再進(jìn)一步優(yōu)化,計算整體正確率:
# 測試步驟開始
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): # 無梯度,不進(jìn)行調(diào)優(yōu)
for data in test_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets) # 該loss為部分?jǐn)?shù)據(jù)在網(wǎng)絡(luò)模型上的損失,為tensor數(shù)據(jù)類型
# 求整體測試數(shù)據(jù)集上的誤差或正確率
total_test_loss = total_test_loss + loss.item() # loss為tensor數(shù)據(jù)類型,而total_test_loss為普通數(shù)字
# 求整體測試數(shù)據(jù)集上的誤差或正確率
accuracy = (outputs.argmax(1) == targets).sum() # 1:橫向比較,==:True或False,sum:計算True或False個數(shù)
total_accuracy = total_accuracy + accuracy
print("整體測試集上的Loss:{}".format(total_test_loss))
print("整體測試集上的正確率:{}".format(total_accuracy/imgs.size(0)))
writer.add_scalar("test_loss",total_test_loss,total_test_step)
writer.add_scalar("test_accuracy",total_accuracy/imgs.size(0),total_test_step)
total_test_step += 1
torch.save(tudui,"tudui_{}.pth".format(i)) # 每一輪保存一個結(jié)果
print("模型已保存")
writer.close()
運行結(jié)果:
在 Terminal 里輸入:
tensorboard --logdir=logs_train
打開網(wǎng)址??http://localhost:6006/ :
---------------------------------------------------------------------------------------------------------------------------------
train.py 完整代碼
import torchvision.datasets
from torch.utils.tensorboard import SummaryWriter
from model import *
from torch import nn
from torch.utils.data import DataLoader
# 準(zhǔn)備數(shù)據(jù)集,CIFAR10 數(shù)據(jù)集是PIL Image,要轉(zhuǎn)換為tensor數(shù)據(jù)類型
train_data = torchvision.datasets.CIFAR10(root="../data",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10(root="../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
# 看一下訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集都有多少張(如何獲得數(shù)據(jù)集的長度)
train_data_size = len(train_data) # length 長度
test_data_size = len(test_data)
# 如果train_data_size=10,那么打印出的字符串為:訓(xùn)練數(shù)據(jù)集的長度為:10
print("訓(xùn)練數(shù)據(jù)集的長度為:{}".format(train_data_size)) # 字符串格式化,把format中的變量替換{}
print("測試數(shù)據(jù)集的長度為:{}".format(test_data_size))
# 利用 DataLoader 來加載數(shù)據(jù)集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)
# 創(chuàng)建網(wǎng)絡(luò)模型
tudui = Tudui()
# 創(chuàng)建損失函數(shù)
loss_fn = nn.CrossEntropyLoss() # 分類問題可以用交叉熵
# 定義優(yōu)化器
learning_rate = 0.01 # 另一寫法:1e-2,即1x 10^(-2)=0.01
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate) # SGD 隨機(jī)梯度下降
# 設(shè)置訓(xùn)練網(wǎng)絡(luò)的一些參數(shù)
total_train_step = 0 # 記錄訓(xùn)練次數(shù)
total_test_step = 0 # 記錄測試次數(shù)
epoch = 10 # 訓(xùn)練輪數(shù)
# 添加tensorboard
writer = SummaryWriter("../logs_train")
for i in range(epoch):
print("----------第{}輪訓(xùn)練開始-----------".format(i+1)) # i從0-9
# 訓(xùn)練步驟開始
for data in train_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets)
# 優(yōu)化器優(yōu)化模型
optimizer.zero_grad() # 首先要梯度清零
loss.backward() # 反向傳播得到每一個參數(shù)節(jié)點的梯度
optimizer.step() # 對參數(shù)進(jìn)行優(yōu)化
total_train_step += 1
if total_train_step % 100 ==0: # 逢百才打印記錄
print("訓(xùn)練次數(shù):{},loss:{}".format(total_train_step,loss.item()))
writer.add_scalar("train_loss",loss.item(),total_train_step)
# 測試步驟開始
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): # 無梯度,不進(jìn)行調(diào)優(yōu)
for data in test_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets) # 該loss為部分?jǐn)?shù)據(jù)在網(wǎng)絡(luò)模型上的損失,為tensor數(shù)據(jù)類型
# 求整體測試數(shù)據(jù)集上的誤差或正確率
total_test_loss = total_test_loss + loss.item() # loss為tensor數(shù)據(jù)類型,而total_test_loss為普通數(shù)字
# 求整體測試數(shù)據(jù)集上的誤差或正確率
accuracy = (outputs.argmax(1) == targets).sum() # 1:橫向比較,==:True或False,sum:計算True或False個數(shù)
total_accuracy = total_accuracy + accuracy
print("整體測試集上的Loss:{}".format(total_test_loss))
print("整體測試集上的正確率:{}".format(total_accuracy/test_data_size))
writer.add_scalar("test_loss",total_test_loss,total_test_step)
writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)
total_test_step += 1
torch.save(tudui,"tudui_{}.pth".format(i)) # 每一輪保存一個結(jié)果
print("模型已保存")
writer.close()
---------------------------------------------------------------------------------------------------------------------------------
model.train() 和 model.eval()
訓(xùn)練步驟開始之前會把網(wǎng)絡(luò)模型(我們這里的網(wǎng)絡(luò)模型叫 tudui)設(shè)置為train,并不是說把網(wǎng)絡(luò)設(shè)置為訓(xùn)練模型它才能夠開始訓(xùn)練
測試網(wǎng)絡(luò)前寫 網(wǎng)絡(luò).eval(),并不是說需要這一行才能把網(wǎng)絡(luò)設(shè)置成 eval 狀態(tài),才能進(jìn)行網(wǎng)絡(luò)測試
--------------------------------------------------------------------------------------------------------------------------------?
作用?
這兩句不寫網(wǎng)絡(luò)依然可以運行,它們的作用是:
本節(jié)寫的案例沒有 Dropout 層或 BatchNorm 層,所以有沒有這兩行無所謂
如果有這些特殊層,一定要調(diào)用
---------------------------------------------------------------------------------------------------------------------------------
回顧案例
首先,要準(zhǔn)備數(shù)據(jù)集,準(zhǔn)備對應(yīng)的 dataloader
import torchvision.datasets
from torch.utils.tensorboard import SummaryWriter
from model import *
from torch import nn
from torch.utils.data import DataLoader
# 準(zhǔn)備數(shù)據(jù)集,CIFAR10 數(shù)據(jù)集是PIL Image,要轉(zhuǎn)換為tensor數(shù)據(jù)類型
train_data = torchvision.datasets.CIFAR10(root="../data",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10(root="../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
# 看一下訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集都有多少張(如何獲得數(shù)據(jù)集的長度)
train_data_size = len(train_data) # length 長度
test_data_size = len(test_data)
# 如果train_data_size=10,那么打印出的字符串為:訓(xùn)練數(shù)據(jù)集的長度為:10
print("訓(xùn)練數(shù)據(jù)集的長度為:{}".format(train_data_size)) # 字符串格式化,把format中的變量替換{}
print("測試數(shù)據(jù)集的長度為:{}".format(test_data_size))
# 利用 DataLoader 來加載數(shù)據(jù)集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)
然后,創(chuàng)建網(wǎng)絡(luò)模型、損失函數(shù)、優(yōu)化器,設(shè)置訓(xùn)練中的一些參數(shù)、訓(xùn)練輪數(shù) epoch(為了能夠進(jìn)行多次訓(xùn)練)
# 創(chuàng)建網(wǎng)絡(luò)模型
tudui = Tudui()
# 創(chuàng)建損失函數(shù)
loss_fn = nn.CrossEntropyLoss() # 分類問題可以用交叉熵
# 定義優(yōu)化器
learning_rate = 0.01 # 另一寫法:1e-2,即1x 10^(-2)=0.01
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate) # SGD 隨機(jī)梯度下降
# 設(shè)置訓(xùn)練網(wǎng)絡(luò)的一些參數(shù)
total_train_step = 0 # 記錄訓(xùn)練次數(shù)
total_test_step = 0 # 記錄測試次數(shù)
epoch = 10 # 訓(xùn)練輪數(shù)
調(diào)用
tudui.train()
使網(wǎng)絡(luò)進(jìn)入訓(xùn)練狀態(tài),從訓(xùn)練的 dataloader 中不斷取數(shù)據(jù),算出誤差,放到優(yōu)化器中進(jìn)行優(yōu)化,采用某種特定的方式展示輸出,一輪結(jié)束后或特定步數(shù)后進(jìn)行測試
# 訓(xùn)練步驟開始
for data in train_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets)
# 優(yōu)化器優(yōu)化模型
optimizer.zero_grad() # 首先要梯度清零
loss.backward() # 反向傳播得到每一個參數(shù)節(jié)點的梯度
optimizer.step() # 對參數(shù)進(jìn)行優(yōu)化
total_train_step += 1
if total_train_step % 100 ==0: # 逢百才打印記錄
print("訓(xùn)練次數(shù):{},loss:{}".format(total_train_step,loss.item()))
writer.add_scalar("train_loss",loss.item(),total_train_step)
測試過程中可以設(shè)置
tudui.eval()
要設(shè)置
with torch.no_grad():
讓網(wǎng)絡(luò)模型中的參數(shù)都沒有,因為我們只需要進(jìn)行測試,不需要對網(wǎng)絡(luò)模型進(jìn)行調(diào)整,更不需要利用梯度來優(yōu)化
從測試數(shù)據(jù)集中取數(shù)據(jù),計算誤差,構(gòu)建特殊指標(biāo)顯示出來
for data in test_dataloader:
imgs,targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs,targets) # 該loss為部分?jǐn)?shù)據(jù)在網(wǎng)絡(luò)模型上的損失,為tensor數(shù)據(jù)類型
# 求整體測試數(shù)據(jù)集上的誤差或正確率
total_test_loss = total_test_loss + loss.item() # loss為tensor數(shù)據(jù)類型,而total_test_loss為普通數(shù)字
# 求整體測試數(shù)據(jù)集上的誤差或正確率
accuracy = (outputs.argmax(1) == targets).sum() # 1:橫向比較,==:True或False,sum:計算True或False個數(shù)
total_accuracy = total_accuracy + accuracy
最后可以通過一些方式來展示一下訓(xùn)練的網(wǎng)絡(luò)在測試集上的效果
print("整體測試集上的Loss:{}".format(total_test_loss))
print("整體測試集上的正確率:{}".format(total_accuracy/test_data_size))
writer.add_scalar("test_loss",total_test_loss,total_test_step)
writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)
total_test_step += 1
在特定步數(shù)或某一輪可以保存模型,保存模型的方式是之前講的方式1:
torch.save(tudui,"tudui_{}.pth".format(i))
回憶官方推薦的方式2:
torch.save(tudui.state_dict,"tudui_{}.pth".format(i))
(將網(wǎng)絡(luò)模型的狀態(tài)轉(zhuǎn)化為字典型,展示它的特定保存位置)
24. 利用GPU訓(xùn)練
兩種方式實現(xiàn)代碼在GPU上訓(xùn)練:
第一種使用GPU訓(xùn)練的方式
網(wǎng)絡(luò)模型:?
數(shù)據(jù)(包括輸入、標(biāo)注):
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-183.png)
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-184.png)
損失函數(shù):
更好的寫法:
其他地方同理加上?
這種寫法在CPU和GPU上都可以跑,優(yōu)先在GPU上跑
---------------------------------------------------------------------------------------------------------------------------------
比較CPU/GPU訓(xùn)練時間?
為了比較時間,引入 time 這個 package
對于CPU
?
運行 train.py,可以看到它運行了4.53s
對于GPU
時間竟然比CPU長(我不理解orz)
---------------------------------------------------------------------------------------------------------------------------------
查看GPU信息?
在 Terminal 里輸入
nvidia-smi
會出現(xiàn)一些GPU的信息
---------------------------------------------------------------------------------------------------------------------------------
Google Colab
Google 為我們提供了一個免費的GPU,默認(rèn)提供的環(huán)境當(dāng)中就有Pytorch
如何使用GPU?
修改 ——> 筆記本設(shè)置 ——> 硬件加速器選擇GPU(每周免費使用30h)
使用GPU后會重新啟動環(huán)境
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-194.png)
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-195.png)
將 train_gpu_1.py 代碼復(fù)制進(jìn)去運行,速度很快,結(jié)果如下:
查看GPU配置?
在 Google Colab 上運行 terminal 中運行的東西,在語句前加 !(感嘆號)
---------------------------------------------------------------------------------------------------------------------------------
第二種使用GPU訓(xùn)練的方式(更常用)
以下兩種寫法對于單顯卡來說等價:
device = torch.device("cuda")
device = torch.device("cuda:0")
語法糖(一種語法的簡寫),程序在 CPU 或 GPU/cuda 環(huán)境下都能運行:?
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-199.png)
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-200.png)
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-201.png)
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-202.png)
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-203.png)
網(wǎng)絡(luò)模型、損失函數(shù)都不需要另外賦值,直接 .to(device) 就可以
但是數(shù)據(jù)(圖片、標(biāo)注)需要另外轉(zhuǎn)移之后再重新賦值給變量
25. 完整的模型驗證(測試、demo)套路
核心:利用已經(jīng)訓(xùn)練好的模型,給它提供輸入進(jìn)行測試(類似之前案例中測試集的測試部分)
pytorch-CycleGAN-and-pix2pix
---------------------------------------------------------------------------------------------------------------------------------
示例?
Resize():?
隨便在網(wǎng)絡(luò)上找圖片,通過 Resize() 使圖片符合模型
# 如何從test.py文件去找到dog文件(相對路徑)
import torch
import torchvision.transforms
from PIL import Image
from torch import nn
image_path = "../imgs/dog.png" # 或右鍵-> Copy Path-> Absolute Path(絕對路徑)
# 讀取圖片(PIL Image),再用ToTensor進(jìn)行轉(zhuǎn)換
image = Image.open(image_path) # 現(xiàn)在的image是PIL類型
print(image) # <PIL.PngImagePlugin.PngImageFile image mode=RGB size=430x247 at 0x1DF29D33AF0>
# image = image.convert('RGB')
# 因為png格式是四通道,除了RGB三通道外,還有一個透明度通道,所以要調(diào)用上述語句保留其顏色通道
# 當(dāng)然,如果圖片本來就是三顏色通道,經(jīng)過此操作,不變
# 加上這一步后,可以適應(yīng) png jpg 各種格式的圖片
# 該image大小為430x247,網(wǎng)絡(luò)模型的輸入只能是32x32,進(jìn)行一個Resize()
# Compose():把transforms幾個變換聯(lián)立在一起
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)), # 32x32大小的PIL Image
torchvision.transforms.ToTensor()]) # 轉(zhuǎn)為Tensor數(shù)據(jù)類型
image = transform(image)
print(image.shape) # torch.Size([3, 32, 32])
# 加載網(wǎng)絡(luò)模型(之前采用的是第一種方式保存,故需要采用第一種方式加載)
# 首先搭建神經(jīng)網(wǎng)絡(luò)(10分類網(wǎng)絡(luò))
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
# 把網(wǎng)絡(luò)放到序列中
self.model = nn.Sequential(
nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入是32x32的,輸出還是32x32的(padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入輸出都是16x16的(同理padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
nn.MaxPool2d(kernel_size=2),
nn.Flatten(), # 展平
nn.Linear(in_features=64*4*4,out_features=64),
nn.Linear(in_features=64,out_features=10)
)
def forward(self,x):
x = self.model(x)
return x
# 然后加載網(wǎng)絡(luò)模型
model = torch.load("tudui_0.pth") # 因為test.py和tudui_0.pth在同一個層級下,所以地址可以直接寫
print(model)
output = model(image)
print(output)
運行會報錯,報錯提示如下:
RuntimeError: Expected 4-dimensional input for 4-dimensional weight [32, 3, 5, 5], but got 3-dimensional input of size [3, 32, 32] instead
原因:要求是四維的輸入 [batch_size,channel,length,width],但是獲得的圖片是三維的 —— 圖片沒有指定 batch_size(網(wǎng)絡(luò)訓(xùn)練過程中是需要 batch_size 的,而圖片輸入是三維的,需要reshape() 一下)
解決:torch.reshape() 方法
在上述代碼后面加上:
image = torch.reshape(image,(1,3,32,32))
model.eval() # 模型轉(zhuǎn)化為測試類型
with torch.no_grad(): # 節(jié)約內(nèi)存和性能
output = model(image)
print(output)
運行結(jié)果:
1.3220 概率最大,預(yù)測的是第六類
print(output.argmax(1))
--------------------------------------------------------------------------------------------------------------------------------?
![notimplementederror: module [vgg16] is missing the required](https://imgs.yssmx.com/Uploads/2024/01/810626-210.png)
怎么找到?
第六類對應(yīng)的是 frog(青蛙)
---------------------------------------------------------------------------------------------------------------------------------
預(yù)測錯誤的原因可能是訓(xùn)練次數(shù)不夠多,在 Google Colab 里,將訓(xùn)練輪數(shù) epoch 改為 30 次,完整代碼(train.py):
import torch
import torchvision.datasets
from torch.utils.tensorboard import SummaryWriter
# from model import *
from torch import nn
from torch.utils.data import DataLoader
import time # time這個package是用來計時的
# 準(zhǔn)備數(shù)據(jù)集,CIFAR10 數(shù)據(jù)集是PIL Image,要轉(zhuǎn)換為tensor數(shù)據(jù)類型
device = torch.device("cuda")
print(device)
train_data = torchvision.datasets.CIFAR10(root="../data",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10(root="../data",train=False,transform=torchvision.transforms.ToTensor(),download=True)
# 看一下訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集都有多少張(如何獲得數(shù)據(jù)集的長度)
train_data_size = len(train_data) # length 長度
test_data_size = len(test_data)
# 如果train_data_size=10,那么打印出的字符串為:訓(xùn)練數(shù)據(jù)集的長度為:10
print("訓(xùn)練數(shù)據(jù)集的長度為:{}".format(train_data_size)) # 字符串格式化,把format中的變量替換{}
print("測試數(shù)據(jù)集的長度為:{}".format(test_data_size))
# 利用 DataLoader 來加載數(shù)據(jù)集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)
# 搭建神經(jīng)網(wǎng)絡(luò)(10分類網(wǎng)絡(luò))
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
# 把網(wǎng)絡(luò)放到序列中
self.model = nn.Sequential(
nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入是32x32的,輸出還是32x32的(padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入輸出都是16x16的(同理padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
nn.MaxPool2d(kernel_size=2),
nn.Flatten(), # 展平
nn.Linear(in_features=64*4*4,out_features=64),
nn.Linear(in_features=64,out_features=10)
)
def forward(self,x):
x = self.model(x)
return x
# 創(chuàng)建網(wǎng)絡(luò)模型
tudui = Tudui()
tudui.to(device)
# 創(chuàng)建損失函數(shù)
loss_fn = nn.CrossEntropyLoss() # 分類問題可以用交叉熵
loss_fn.to(device)
# 定義優(yōu)化器
learning_rate = 0.01 # 另一寫法:1e-2,即1x 10^(-2)=0.01
optimizer = torch.optim.SGD(tudui.parameters(),lr=learning_rate) # SGD 隨機(jī)梯度下降
# 設(shè)置訓(xùn)練網(wǎng)絡(luò)的一些參數(shù)
total_train_step = 0 # 記錄訓(xùn)練次數(shù)
total_test_step = 0 # 記錄測試次數(shù)
epoch = 30 # 訓(xùn)練輪數(shù)
# 添加tensorboard
writer = SummaryWriter("../logs_train")
start_time = time.time() # 記錄下此時的時間,賦值給開始時間
for i in range(epoch):
print("----------第{}輪訓(xùn)練開始-----------".format(i+1)) # i從0-9
# 訓(xùn)練步驟開始
tudui.train()
for data in train_dataloader:
imgs,targets = data
imgs = imgs.to(device)
targets = targets.to(device)
outputs = tudui(imgs)
loss = loss_fn(outputs,targets)
# 優(yōu)化器優(yōu)化模型
optimizer.zero_grad() # 首先要梯度清零
loss.backward() # 反向傳播得到每一個參數(shù)節(jié)點的梯度
optimizer.step() # 對參數(shù)進(jìn)行優(yōu)化
total_train_step += 1
if total_train_step % 100 ==0: # 逢百才打印記錄
end_time = time.time()
print(end_time - start_time) # 第一次訓(xùn)練100次所花費的時間
print("訓(xùn)練次數(shù):{},loss:{}".format(total_train_step,loss.item()))
writer.add_scalar("train_loss",loss.item(),total_train_step)
# 測試步驟開始
tudui.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): # 無梯度,不進(jìn)行調(diào)優(yōu)
for data in test_dataloader:
imgs,targets = data
imgs = imgs.to(device)
targets = targets.to(device)
outputs = tudui(imgs)
loss = loss_fn(outputs,targets) # 該loss為部分?jǐn)?shù)據(jù)在網(wǎng)絡(luò)模型上的損失,為tensor數(shù)據(jù)類型
# 求整體測試數(shù)據(jù)集上的誤差或正確率
total_test_loss = total_test_loss + loss.item() # loss為tensor數(shù)據(jù)類型,而total_test_loss為普通數(shù)字
accuracy = (outputs.argmax(1) == targets).sum() # 1:橫向比較,==:True或False,sum:計算True或False個數(shù)
total_accuracy = total_accuracy + accuracy
print("整體測試集上的Loss:{}".format(total_test_loss))
print("整體測試集上的正確率:{}".format(total_accuracy/test_data_size)) # 正確率為預(yù)測對的個數(shù)除以測試集長度
writer.add_scalar("test_loss",total_test_loss,total_test_step)
writer.add_scalar("test_accuracy",total_test_loss,total_test_step,total_test_step)
total_test_step += 1
torch.save(tudui,"tudui_{}_gpu.pth".format(i)) # 每一輪保存一個結(jié)果
print("模型已保存")
writer.close()
運行結(jié)果:
下載后復(fù)制到 PyCharm 中 Learn_Torch 的 src 文件夾下,然后將之前 test.py 中的路徑修改為:
model = torch.load("tudui_29_gpu.pth")
運行后報錯,報錯提示:
RuntimeError: Input type (torch.FloatTensor) and weight type (torch.cuda.FloatTensor) should be the same or input should be a MKLDNN tensor and weight is a dense tensor
原因:采用GPU訓(xùn)練的模型,在CPU上加載,要從GPU上映射到CPU上(在不同環(huán)境中加載已經(jīng)訓(xùn)練好的模型,需要經(jīng)過映射)
解決:
model = torch.load("tudui_29_gpu.pth",map_location=torch.device('cpu'))
--------------------------------------------------------------------------------------------------------------------------------?
test.py(把訓(xùn)練模型運用到實際環(huán)境中)完整代碼
# 如何從test.py文件去找到dog文件(相對路徑)
import torch
import torchvision.transforms
from PIL import Image
from torch import nn
image_path = "../imgs/airplane.png" # 或右鍵-> Copy Path-> Absolute Path(絕對路徑)
# 讀取圖片(PIL Image),再用ToTensor進(jìn)行轉(zhuǎn)換
image = Image.open(image_path) # 現(xiàn)在的image是PIL類型
print(image) # <PIL.PngImagePlugin.PngImageFile image mode=RGB size=430x247 at 0x1DF29D33AF0>
# image = image.convert('RGB')
# 因為png格式是四通道,除了RGB三通道外,還有一個透明度通道,所以要調(diào)用上述語句保留其顏色通道
# 當(dāng)然,如果圖片本來就是三顏色通道,經(jīng)過此操作,不變
# 加上這一步后,可以適應(yīng) png jpg 各種格式的圖片
# 該image大小為430x247,網(wǎng)絡(luò)模型的輸入只能是32x32,進(jìn)行一個Resize()
# Compose():把transforms幾個變換聯(lián)立在一起
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)), # 32x32大小的PIL Image
torchvision.transforms.ToTensor()]) # 轉(zhuǎn)為Tensor數(shù)據(jù)類型
image = transform(image)
print(image.shape) # torch.Size([3, 32, 32])
# 加載網(wǎng)絡(luò)模型(之前采用的是第一種方式保存,故需要采用第一種方式加載)
# 首先搭建神經(jīng)網(wǎng)絡(luò)(10分類網(wǎng)絡(luò))
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
# 把網(wǎng)絡(luò)放到序列中
self.model = nn.Sequential(
nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入是32x32的,輸出還是32x32的(padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入輸出都是16x16的(同理padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
nn.MaxPool2d(kernel_size=2),
nn.Flatten(), # 展平
nn.Linear(in_features=64*4*4,out_features=64),
nn.Linear(in_features=64,out_features=10)
)
def forward(self,x):
x = self.model(x)
return x
# 然后加載網(wǎng)絡(luò)模型
model = torch.load("tudui_29_gpu.pth",map_location=torch.device('cpu')) # 因為test.py和tudui_0.pth在同一個層級下,所以地址可以直接寫
print(model)
image = torch.reshape(image,(1,3,32,32))
model.eval() # 模型轉(zhuǎn)化為測試類型
with torch.no_grad():
output = model(image)
print(output)
print(output.argmax(1))
--------------------------------------------------------------------------------------------------------------------------------?
示例二
再以飛機(jī)的圖片為例(airplane.png 保存在 imgs 文件夾里)
# 如何從test.py文件去找到dog文件(相對路徑)
import torch
import torchvision.transforms
from PIL import Image
from torch import nn
image_path = "../imgs/airplane.png" # 或右鍵-> Copy Path-> Absolute Path(絕對路徑)
# 讀取圖片(PIL Image),再用ToTensor進(jìn)行轉(zhuǎn)換
image = Image.open(image_path) # 現(xiàn)在的image是PIL類型
print(image) # <PIL.PngImagePlugin.PngImageFile image mode=RGB size=430x247 at 0x1DF29D33AF0>
# image = image.convert('RGB')
# 因為png格式是四通道,除了RGB三通道外,還有一個透明度通道,所以要調(diào)用上述語句保留其顏色通道
# 當(dāng)然,如果圖片本來就是三顏色通道,經(jīng)過此操作,不變
# 加上這一步后,可以適應(yīng) png jpg 各種格式的圖片
# 該image大小為430x247,網(wǎng)絡(luò)模型的輸入只能是32x32,進(jìn)行一個Resize()
# Compose():把transforms幾個變換聯(lián)立在一起
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)), # 32x32大小的PIL Image
torchvision.transforms.ToTensor()]) # 轉(zhuǎn)為Tensor數(shù)據(jù)類型
image = transform(image)
print(image.shape) # torch.Size([3, 32, 32])
# 加載網(wǎng)絡(luò)模型(之前采用的是第一種方式保存,故需要采用第一種方式加載)
# 首先搭建神經(jīng)網(wǎng)絡(luò)(10分類網(wǎng)絡(luò))
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
# 把網(wǎng)絡(luò)放到序列中
self.model = nn.Sequential(
nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入是32x32的,輸出還是32x32的(padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2), #輸入輸出都是16x16的(同理padding經(jīng)計算為2)
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
nn.MaxPool2d(kernel_size=2),
nn.Flatten(), # 展平
nn.Linear(in_features=64*4*4,out_features=64),
nn.Linear(in_features=64,out_features=10)
)
def forward(self,x):
x = self.model(x)
return x
# 然后加載網(wǎng)絡(luò)模型
model = torch.load("tudui_29_gpu.pth",map_location=torch.device('cpu')) # 因為test.py和tudui_0.pth在同一個層級下,所以地址可以直接寫
print(model)
image = torch.reshape(image,(1,3,32,32))
model.eval() # 模型轉(zhuǎn)化為測試類型
with torch.no_grad():
output = model(image)
print(output)
print(output.argmax(1)) # 把輸出轉(zhuǎn)換為一種利于解讀的方式
運行結(jié)果:
第 0 類就是 airplane,預(yù)測正確?
注意:
model.eval() # 模型轉(zhuǎn)化為測試類型
with torch.no_grad():
這兩行不寫也可以,但為了養(yǎng)成良好的代碼習(xí)慣,最好寫上。如果網(wǎng)絡(luò)模型中正好有 Dropout 或 BatchNorm 時,不寫的話預(yù)測就會有問題
26. 看看開源項目
GitHub - junyanz/pytorch-CycleGAN-and-pix2pix: Image-to-Image Translation in PyTorch
README.md?
先讀 README.md(怎么安裝、注意事項)
--------------------------------------------------------------------------------------------------------------------------------?
train.py
"""General-purpose training script for image-to-image translation.
This script works for various models (with option '--model': e.g., pix2pix, cyclegan, colorization) and
different datasets (with option '--dataset_mode': e.g., aligned, unaligned, single, colorization).
You need to specify the dataset ('--dataroot'), experiment name ('--name'), and model ('--model').
It first creates model, dataset, and visualizer given the option.
It then does standard network training. During the training, it also visualize/save the images, print/save the loss plot, and save models.
The script supports continue/resume training. Use '--continue_train' to resume your previous training.
Example:
Train a CycleGAN model:
python train.py --dataroot ./datasets/maps --name maps_cyclegan --model cycle_gan
Train a pix2pix model:
python train.py --dataroot ./datasets/facades --name facades_pix2pix --model pix2pix --direction BtoA
See options/base_options.py and options/train_options.py for more training options.
See training and test tips at: https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/blob/master/docs/tips.md
See frequently asked questions at: https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/blob/master/docs/qa.md
"""
import time
from options.train_options import TrainOptions
from data import create_dataset
from models import create_model
from util.visualizer import Visualizer
if __name__ == '__main__':
opt = TrainOptions().parse() # get training options
dataset = create_dataset(opt) # create a dataset given opt.dataset_mode and other options
dataset_size = len(dataset) # get the number of images in the dataset.
print('The number of training images = %d' % dataset_size)
model = create_model(opt) # create a model given opt.model and other options
model.setup(opt) # regular setup: load and print networks; create schedulers
visualizer = Visualizer(opt) # create a visualizer that display/save images and plots
total_iters = 0 # the total number of training iterations
for epoch in range(opt.epoch_count, opt.n_epochs + opt.n_epochs_decay + 1): # outer loop for different epochs; we save the model by <epoch_count>, <epoch_count>+<save_latest_freq>
epoch_start_time = time.time() # timer for entire epoch
iter_data_time = time.time() # timer for data loading per iteration
epoch_iter = 0 # the number of training iterations in current epoch, reset to 0 every epoch
visualizer.reset() # reset the visualizer: make sure it saves the results to HTML at least once every epoch
model.update_learning_rate() # update learning rates in the beginning of every epoch.
for i, data in enumerate(dataset): # inner loop within one epoch
iter_start_time = time.time() # timer for computation per iteration
if total_iters % opt.print_freq == 0:
t_data = iter_start_time - iter_data_time
total_iters += opt.batch_size
epoch_iter += opt.batch_size
model.set_input(data) # unpack data from dataset and apply preprocessing
model.optimize_parameters() # calculate loss functions, get gradients, update network weights
if total_iters % opt.display_freq == 0: # display images on visdom and save images to a HTML file
save_result = total_iters % opt.update_html_freq == 0
model.compute_visuals()
visualizer.display_current_results(model.get_current_visuals(), epoch, save_result)
if total_iters % opt.print_freq == 0: # print training losses and save logging information to the disk
losses = model.get_current_losses()
t_comp = (time.time() - iter_start_time) / opt.batch_size
visualizer.print_current_losses(epoch, epoch_iter, losses, t_comp, t_data)
if opt.display_id > 0:
visualizer.plot_current_losses(epoch, float(epoch_iter) / dataset_size, losses)
if total_iters % opt.save_latest_freq == 0: # cache our latest model every <save_latest_freq> iterations
print('saving the latest model (epoch %d, total_iters %d)' % (epoch, total_iters))
save_suffix = 'iter_%d' % total_iters if opt.save_by_iter else 'latest'
model.save_networks(save_suffix)
iter_data_time = time.time()
if epoch % opt.save_epoch_freq == 0: # cache our model every <save_epoch_freq> epochs
print('saving the model at the end of epoch %d, iters %d' % (epoch, total_iters))
model.save_networks('latest')
model.save_networks(epoch)
print('End of epoch %d / %d \t Time Taken: %d sec' % (epoch, opt.n_epochs + opt.n_epochs_decay, time.time() - epoch_start_time))
--------------------------------------------------------------------------------------------------------------------------------?
訓(xùn)練參數(shù)設(shè)置
from .base_options import BaseOptions
class TrainOptions(BaseOptions):
"""This class includes training options.
It also includes shared options defined in BaseOptions.
"""
def initialize(self, parser):
parser = BaseOptions.initialize(self, parser)
# visdom and HTML visualization parameters
parser.add_argument('--display_freq', type=int, default=400, help='frequency of showing training results on screen')
parser.add_argument('--display_ncols', type=int, default=4, help='if positive, display all images in a single visdom web panel with certain number of images per row.')
parser.add_argument('--display_id', type=int, default=1, help='window id of the web display')
parser.add_argument('--display_server', type=str, default="http://localhost", help='visdom server of the web display')
parser.add_argument('--display_env', type=str, default='main', help='visdom display environment name (default is "main")')
parser.add_argument('--display_port', type=int, default=8097, help='visdom port of the web display')
parser.add_argument('--update_html_freq', type=int, default=1000, help='frequency of saving training results to html')
parser.add_argument('--print_freq', type=int, default=100, help='frequency of showing training results on console')
parser.add_argument('--no_html', action='store_true', help='do not save intermediate training results to [opt.checkpoints_dir]/[opt.name]/web/')
# network saving and loading parameters
parser.add_argument('--save_latest_freq', type=int, default=5000, help='frequency of saving the latest results')
parser.add_argument('--save_epoch_freq', type=int, default=5, help='frequency of saving checkpoints at the end of epochs')
parser.add_argument('--save_by_iter', action='store_true', help='whether saves model by iteration')
parser.add_argument('--continue_train', action='store_true', help='continue training: load the latest model')
parser.add_argument('--epoch_count', type=int, default=1, help='the starting epoch count, we save the model by <epoch_count>, <epoch_count>+<save_latest_freq>, ...')
parser.add_argument('--phase', type=str, default='train', help='train, val, test, etc')
# training parameters
parser.add_argument('--n_epochs', type=int, default=100, help='number of epochs with the initial learning rate')
parser.add_argument('--n_epochs_decay', type=int, default=100, help='number of epochs to linearly decay learning rate to zero')
parser.add_argument('--beta1', type=float, default=0.5, help='momentum term of adam')
parser.add_argument('--lr', type=float, default=0.0002, help='initial learning rate for adam')
parser.add_argument('--gan_mode', type=str, default='lsgan', help='the type of GAN objective. [vanilla| lsgan | wgangp]. vanilla GAN loss is the cross-entropy objective used in the original GAN paper.')
parser.add_argument('--pool_size', type=int, default=50, help='the size of image buffer that stores previously generated images')
parser.add_argument('--lr_policy', type=str, default='linear', help='learning rate policy. [linear | step | plateau | cosine]')
parser.add_argument('--lr_decay_iters', type=int, default=50, help='multiply by a gamma every lr_decay_iters iterations')
self.isTrain = True
return parser
沒有dataroot?
點進(jìn)其繼承的父類查看,可以看到有dataroot?
下載代碼用 PyCharm 打開后,查看代碼里有沒有 required=True,若有,刪掉 required=True ,加一個默認(rèn)值 default="./dataset/maps" ,就可以在 PyCharm 里右鍵運行了文章來源:http://www.zghlxwxcb.cn/news/detail-810626.html
即找到所有 required=True 的參數(shù),將它刪去并添加上默認(rèn)值default?文章來源地址http://www.zghlxwxcb.cn/news/detail-810626.html
到了這里,關(guān)于【我是土堆 - PyTorch教程】學(xué)習(xí)隨手記(已更新 | 已完結(jié) | 10w字超詳細(xì)版)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!