【動手學深度學習】#4 深度學習計算

主要參考學習資料:

《動手學深度學習》阿斯頓·張 等 著

【動手學深度學習 PyTorch版】嗶哩嗶哩@跟李牧學AI

概述

  • 為了實現更復雜的網絡,我們需要研究比層更高一級的單元,在編程中由表示。通過自定義層和塊,我們能更靈活地搭建網絡。
  • 我們有多種方法可以訪問、初始化和綁定模型參數。
  • 為了加載和保存權重向量和整個模型,我們需要進行文件讀寫
  • 將計算設備指定為GPU而不是CPU,可以更高效地完成深度學習的計算任務。

目錄

  • 4.1 層和塊
    • 4.1.1 自定義塊
    • 4.1.2 順序塊
  • 4.2 參數管理
    • 4.2.1 參數訪問
      • 1.目標參數
      • 2.一次性訪問所有參數
      • 3.從嵌套塊收集參數
    • 4.2.2 參數初始化
      • 1.內置初始化
      • 2.自定義初始化
    • 4.2.3 參數綁定
  • 4.3 自定義層
    • 4.3.1 不帶參數的層
    • 4.3.2 帶參數的層
  • 4.4 讀寫文件
    • 4.4.1 加載和保存張量
    • 4.4.2 加載和保存模型參數
  • 4.5 GPU
    • 4.5.1 計算設備
    • 4.5.2 張量與GPU
    • 4.5.3 神經網絡與GPU

4.1 層和塊

事實證明,研究討論“比單個層大”但“比整個模型小”的組件更有價值。計算機視覺中廣泛流行的ResNet-152架構有數百層,這些層是由層組的重復模式組成。為了實現更復雜的網絡,我們引入神經網絡的概念。

塊可以描述單個層、由多個層組成的組件或整個模型本身。使用塊的好處是可以將一些塊組成更大的組件,這一過程通常是遞歸的。

在編程中,塊由表示,類的任何子類都必須定義一個將其輸入轉換為輸出的前向傳播函數,并且必須存儲任何必需的參數。為了計算梯度,塊必須具有反向傳播函數。在自定義塊時,由于自動微分提供了一些后端實現,我們只需要考慮前向傳播函數和必須的參數。

4.1.1 自定義塊

塊必須提供的基本功能:

  • 將輸入數據作為其前向傳播函數的參數。
  • 通過前向傳播函數來生成輸出。
  • 計算其輸出關于輸入的梯度,可通過其反向傳播函數進行訪問,通常是自動完成的。
  • 存儲和訪問前向傳播計算所需的參數。
  • 根據需要初始化模型參數。
import torch  
from torch import nn  
#functional模塊提供一些函數化的神經網絡操作,可直接應用于張量
from torch.nn import functional as F  #以Module為父類定義MLP類
class MLP(nn.Module):  def __init__(self):  #初始化繼承自Module的數據屬性super().__init__()  #實例化兩個全連接層self.hidden = nn.Linear(20, 256)  self.out = nn.Linear(256, 10)  #定義模型的前向傳播,根據輸入X返回模型輸出def forward(self, X):  return self.out(F.relu(self.hidden(X)))

下面這個例子展示了自定義塊的靈活性:

class FixedHiddenMLP(nn.Module)def __init__(self):super().__init__()#設置不隨梯度更新的固定的權重,即常量參數self.rand_weight = torch.rand((20, 20), requires_grad=False)self.linear = nn.Linear(20, 20)#在前向傳播函數中執行自定義代碼def forward(self, X):X = self.linear(X)X = F.relu(torch.mm(X, self.rand_weight) + 1)#復用全連接層相當于這兩個全連接層共享參數X = self.linear(X)#控制流while X.abs().sum() > 1:X /= 2return X.sum()

4.1.2 順序塊

Sequential類的設計目的是將其他模塊串起來。構建自己的簡化的MySequential只需定義如下函數:

  • 將塊逐個追加到列表中的函數。
  • 前向傳播函數,用于將輸入按追加塊的順序傳遞給塊組成的“鏈條”。
class MySequential(nn.Module):  #args傳入任意數量的模塊def __init__(self, *args):  super().__init__()  #遍歷模塊并為每個模塊分配一個字符串鍵存入_modules#_modules的變量類型是OrderedDictfor idx, module in enumerate(args):  self._modules[str(idx)] = module  def forward(self, X):  #OrderedDict保證按照成員添加的順序遍歷它們for block in self._modules.values():  X = block(X)  return X

4.2 參數管理

4.2.1 參數訪問

import torch
from torch import nnnet = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))
X = torch.rand(size=(2, 4))

通過索引訪問模型的任意層,state_dict返回所訪問層的參數:

print(net[2].state_dict())
OrderedDict([('weight', tensor([[ 0.2889, -0.2738, -0.3096, -0.3151,  0.2594,  0.1743, -0.2288, -0.1173]])), ('bias', tensor([0.0921]))])

1.目標參數

每個參數都表示為參數類的一個實例:

print(type(net[2].bias))
<class 'torch.nn.parameter.Parameter'>

參數是復合的對象,如下訪問參數實例,并進一步訪問該參數的值(張量類型)和梯度:

print(net[2].bias)
print(net[2].bias.data)
net[2].weight.grad == True
Parameter containing:
tensor([0.0921], requires_grad=True)
tensor([0.0921])
False

另一種訪問網絡參數的方式:

net.state_dict()['2.bias'].data
tensor([0.0921])

2.一次性訪問所有參數

訪問一個全連接層的參數:

print(*[(name, param.shape) for name, param in net[0].named_parameters()])
('weight', torch.Size([8, 4])) ('bias', torch.Size([8]))

訪問所有層的參數:

print(*[(name, param.shape) for name, param in net.named_parameters()])
('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1]))

3.從嵌套塊收集參數

定義生成塊的函數:

def block1():return nn.Sequential(nn.Linear(4, 8), nn.ReLU(),nn.Linear(8, 4), nn.ReLU())def block2():net = nn.Sequential()for i in range(4):net.add_module(f'block {i}', block1())return net

設計一個嵌套塊的網絡,并觀察其架構:

rgnet = nn.Sequential(block2(), nn.Linear(4, 1))
print(rgnet)
Sequential((0): Sequential((block 0): Sequential((0): Linear(in_features=4, out_features=8, bias=True)(1): ReLU()(2): Linear(in_features=8, out_features=4, bias=True)(3): ReLU())(block 1): Sequential((0): Linear(in_features=4, out_features=8, bias=True)(1): ReLU()(2): Linear(in_features=8, out_features=4, bias=True)(3): ReLU())(block 2): Sequential((0): Linear(in_features=4, out_features=8, bias=True)(1): ReLU()(2): Linear(in_features=8, out_features=4, bias=True)(3): ReLU())(block 3): Sequential((0): Linear(in_features=4, out_features=8, bias=True)(1): ReLU()(2): Linear(in_features=8, out_features=4, bias=True)(3): ReLU()))(1): Linear(in_features=4, out_features=1, bias=True)
)

因為層是分層嵌套的,因此可以像通過嵌套列表索引一樣訪問層。下面訪問第一個主要的塊中第二個子塊的第一層的偏置:

rgnet[0][1][0].bias.data
tensor([ 0.1969, -0.0747, -0.1520,  0.0474, -0.2245, -0.2320, -0.2794,  0.3429])

4.2.2 參數初始化

1.內置初始化

normal_將權重參數初始化為標準差為0.01的高斯隨機變量,zeros_將偏置參數設置為0:

def init_normal(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, mean=0, std=0.01)nn.init.zeros_(m.bias)

constant_將所有參數初始化為給定的常量:

def init_constant(m):if type(m) == nn.Linear:nn.init.constant_(m.weight, 1)nn.init.zeros_(m.bias)

xavier_uniform_為上一章中介紹的Xavier均勻分布初始化:

def init_xavier(m):if type(m) == nn.Linear:nn.init.xavier_uniform_(m.weight)

2.自定義初始化

以如下分布為例為任意權重參數 w w w定義初始化方法:

w ~ { U ( 5 , 10 ) , 可能性為 1 4 0 , 可能性為 1 2 U ( ? 10 , ? 5 ) , 可能性為 1 4 w\sim\left\{\begin{matrix}U(5,10),&\displaystyle可能性為\frac14\\0,&\displaystyle可能性為\frac12\\U(-10,-5),&\displaystyle可能性為\frac14\end{matrix}\right. w? ? ??U(5,10),0,U(?10,?5),?可能性為41?可能性為21?可能性為41??

def my_init(m):if type(m) == nn.Linear:#以(-10, 10)為邊界的均勻分布nn.init.uniform_(m.weight, -10, 10)#將絕對值小于5的權重清零m.weight.data *= m.weight.data.abs() >= 5

4.2.3 參數綁定

定義一個層,并使用這個層的參數來設置另一個層的參數可以實現參數共享:

shared = nn.Linear(8, 8)
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(),shared, nn.ReLU,shared, nn.ReLU,nn.Linear(8, 1))

改變shared的參數也會同步改變net中兩個對應層的參數。

4.3 自定義層

4.3.1 不帶參數的層

構建一個從其輸入中減去均值的層:

import torch
import torch.nn.functional as F
from torch import nnclass CenteredLayer(nn.Module):def __init__(self):super().__init__()def forward(self, X):return X - X.mean()

將自定義層作為組件合并到更復雜的模型中:

net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())

該自定義層沒有指定輸入維度,但框架會在數據第一次通過模型傳遞時動態地推斷出每個層的大小,即延遲初始化

4.3.2 帶參數的層

下面是自定義版本的全連接層:

class MyLinear(nn.Module):#傳入輸入數和輸出數作為參數def __init__(self, in_units, units):super().__init__()#通過Parameter實例創建參數self.weight = nn.Parameter(torch.randn(in_units, units))self.bias = nn.Parameter(torch.randn(units,))def forward(self, X):linear = torch.matmul(X, self.weight.data) + self.bias.datareturn F.relu(linear)

4.4 讀寫文件

4.4.1 加載和保存張量

張量通過load和save函數保存到內存并分別讀寫它們。

import torch
from torch import nn
from torch.nn import functional as F#單個張量
x = torch.arange(4)
torch.save(x, 'x-file')
x2 = torch.load('x-file')#張量列表
y = torch.zeros(4)
torch.save([x, y], 'x-files')
x2, y2 = torch.load('x-files')#從字符串映射到張量的字典
mydict = {'x': x, 'y': y}  
torch.save(mydict, 'mydict')
mydict2 = torch.load('mydict')

4.4.2 加載和保存模型參數

深度學習框架提供了內置函數來保存和加載整個網絡,但這只保存模型的參數而不是模型,因此架構需要自己生成。先定義一個多層感知機模型:

class MLP(nn.Module):def __init__(self):super().__init__()self.hidden = nn.Linear(20, 256)self.output = nn.Linear(256, 10)def forward(self, X):return self.output(F.relu(self.hidden(X)))net = MLP()

將模型參數存儲在文件mlp.params中:

torch.save(net.state_dict(), 'mlp.params')

實例化原多層感知機模型的一個備份,并通過直接讀取文件中存儲的參數來恢復模型:

clone = MLP()
clone.load_state_dict(torch.load('mlp.params'))

4.5 GPU

GPU具有強大的并行處理能力、浮點運算速度和內存帶寬,在處理人工智能這樣的復雜計算任務上相較于CPU有無可比擬的優勢。首先確保至少安裝了一個NVIDIA GPU,然后下載NVIDIA驅動和CUDA,最后使用nvidia-smi命令查看顯卡信息:

!nvidia-smi

4

在pytorch中,每個數組都有一個設備,我們通常稱其為環境。默認情況下,所有變量和相關的計算都分配給CPU,有時環境可能是GPU。

4.5.1 計算設備

我們可以指定用于存儲和計算的設備。在pytorch中,CPU和GPU可以使用torch.device(‘cpu’)和torch.device(‘cuda’))表示。如果有多個GPU,我們使用torch.device(f’cuda:{i}')來表示第i塊GPU(i從0開始),以及cuda:0和cuda等價。

import torch
from torch import nntorch.device('cpu'), torch.device('cuda'), torch.device('cuda:0')
(device(type='cpu'), device(type='cuda'), device(type='cuda', index=0))

查詢可用GPU的數量:

torch.cuda.device_count()
1

定義兩個函數以允許在不存在所需GPU的情況下執行代碼:

def try_gpu(i=0):"""如果存在,返回第i個GPU,否則返回CPU"""if torch.cuda.device_count() >= i + 1:return torch.device(f'cuda:{i}')return torch.device('cpu')def try_all_gpus():"""返回所有可用的GPU,如果沒有GPU則返回CPU"""devices = [torch.device(f'cuda:{i}') for i in range(torch.cuda.device_count())]return devices if devices else [torch.device('cpu')]try_gpu(), try_gpu(10), try_all_gpus()
(device(type='cuda', index=0),device(type='cpu'),[device(type='cuda', index=0)])

4.5.2 張量與GPU

默認情況下,張量是在內存中創建并使用CPU進行計算的。

x = torch.tensor([1, 2, 3])
x.device
device(type='cpu')

在創建張量時指定存儲設備來在GPU上存儲張量:

X = torch.ones(2, 3, device=try_gpu())
X
tensor([[1., 1., 1.],[1., 1., 1.]], device='cuda:0')

假設我們有兩個GPU(博主沒有所以移用示例代碼),如下在第二個GPU上創建一個隨機張量:

Y = torch.rand(2, 3, device=try_gpu(1))
Y
tensor([[0.2506, 0.4556, 0.9745],[0.2359, 0.6094, 0.0410]] device='cuda:1')

如果要計算X+Y,我們需要決定在哪里執行這個操作。張量的計算只能在同一設備上運行,因此要將X復制到第二個GPU才能執行:

Z = X.cuda(1)
print(X)
print(Z)
tensor([[1., 1., 1.],[1., 1., 1.]], device='cuda:0')
tensor([[1., 1., 1.],[1., 1., 1.]], device='cuda:1')

現在可以在同一個GPU上將其相加:

Y + Z
tensor([[1.2506, 1.4556, 1.9745],[1.2359, 1.6094, 1.0410]] device='cuda:1')

4.5.3 神經網絡與GPU

類似地,神經網絡模型可以指定設備,將模型參數放在GPU上:

net = nn.Sequential(nn.Linear(3, 1))
net = net.to(device=try_gpu())

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899074.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899074.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899074.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何在 Windows 上安裝并使用 Postman?

Postman 是一個功能強大的API測試工具&#xff0c;它可以幫助程序員更輕松地測試和調試 API。在本文中&#xff0c;我們將討論如何在 Windows 上安裝和使用 Postman。 Windows 如何安裝和使用 Postman 教程&#xff1f;

php寫入\查詢influxdb數據

namespace app\index\controller;use InfluxDB2\Client; use InfluxDB2\Model\WritePrecision; use InfluxDB2\Point;class Demo {/*** 顯示資源列表** return \think\Response*/public function index(){$token 你的TOKEN;$org zzlichi;$bucket initdb;$client new Client…

26考研——圖_圖的基本概念(6)

408答疑 文章目錄 一、圖的基本概念圖的定義非空性非線性結構 頂點和邊的表示頂點邊 有向圖 & 無向圖有向圖有向圖 G 1 G_1 G1? 的表示 無向圖無向圖 G 2 G_2 G2? 的表示 簡單圖 & 多重圖簡單圖多重圖 頂點的度、入度和出度頂點的度有向圖的度 路徑、路徑長度和回路…

面向對象軟件工程實踐軟件案例

智力運動-數字化思維訓練課程介紹 數字化思維訓練是科技賦能素質教育創新實踐項目&#xff0c;通過數字化信息化手段&#xff0c;深度融合優質原創智力運動教育課程資源&#xff0c;服務幼兒園與小學&#xff0c;提供信息時代校園素質教育教學解決方案。在《面向對象軟件工程》…

Linux學習筆記(應用篇一)

基于I.MX6ULL.MINI開發板 標準I/O庫鏈接目錄刪除文件正則表達式系統標識時間堆內存信號標準信號 進程進程組進程間通信線程互斥鎖線程安全 本文章是入門篇的概念&#xff0c;有點零散&#xff0c;后續需要補充復習 **inode&#xff08;索引節點&#xff09;**是 Linux 和 Unix …

Modbus RTU ---> Modbus TCP透傳技術實現(Modbus透傳、RS485透傳、RTU透傳)分站代碼實現、協議轉換器

文章目錄 Modbus RTU到Modbus TCP透傳技術實現1. 透傳技術概述1.1 透傳基本原理- 協議幀格式轉換- 地址映射與管理- 通信時序適配- 錯誤檢測與處理 2. 透傳網關硬件架構2.1 典型硬件結構- 微控制器/處理器(ARM、STM32等)- RS-485/RS-232收發器- 以太網控制器(如W5500)- 電源管理…

MySQL數據庫中常用的命令

登錄&#xff1a; mysql -u username -h ip地址 -P 端口 -p 密碼 mysql -u username -S /path/mysql.sock -P -p 用戶管理&#xff1a; select user,host from mysql.user;//查看數據庫中所用用戶信息 create user username%;//創建用戶 create user username% identifie…

醫學交互作用分析步驟和目的(R語言)

醫學交互作用分析的目的和用途&#xff08;R語言&#xff09; 醫學交互作用分析一直是醫學數據分析的組成部分&#xff0c;總結最近的一些認識。 目的&#xff1a; 在獨立危險因素鑒定的研究中&#xff0c;&#xff08;獨立危險因素的&#xff09;交互作用可以作為獨立危險因…

Javaweb后端登錄會話技術jwt令牌

jwt生成與校驗 是base4補位的 最后面是簽名&#xff0c;簽名不是base64&#xff0c;是通過簽名算法加密后來的 令牌長度不是固定的&#xff0c;長度取決于原始內容&#xff0c;載荷&#xff0c;大小 頭有&#xff0c;類型&#xff0c;簽名算法 base64可以對任意的二進制數據進…

Mybatis操作數據庫(注解+xml兩個方式)

文章目錄 1.個人回顧2.關于mybatis注解的說明3.字段和屬性不匹配的解決方案3.1第一個方案3.2第二個方案3.3第三個方案 4.xml路徑配置5.xml里面的字段映射 1.個人回顧 剛剛翻看了一下自己的這個之前寫的博客&#xff0c;上一次和這個javaee相關的博客還是去年寫的&#xff0c;也…

SysVinit和Systemd的系統運行級別

Linux運行級別 SysVinit系統(init守護進程)Linux系統運行級別SysVinit系統(init守護進程)查看Linux運行級別SysVinit系統(init守護進程)修改運行級別&#xff1a; Systemd守護進程Linux系統運行級別systemd查看運行級別Systemd查看系統當前運行級別 systemd修改運行級別multi-u…

Mysql-經典實戰案例(11):深度解析Sysbench壓測(從入門到MySQL服務器性能驗證)

引言 如何用Sysbench壓測滿足mysql生產運行的服務器&#xff1f; Sysbench返回的壓測結果如何解讀&#xff1f; 別急&#xff0c;本文會教大家如何使用并且如何解讀壓測的結果信息&#xff0c;如何對mysql服務器進行壓測&#xff01; 一、Sysbench核心功能全景解析 1.1 工…

vscode終端不識別npm 無法解析npm

vscode 用以管理員打開識別npm vscode 用普通用戶打開不識別npm 剛換了一臺新電腦&#xff0c;尋思安裝各種環境&#xff0c;一頓操作猛如虎&#xff0c;當最后一個打開vscode后&#xff0c;運行項目發現&#xff0c;新建終端>npm run dev 無法識別。 在cmd 中 打node -…

springboot body 轉對象強驗證屬性多余屬性拋錯誤

在Spring Boot中&#xff0c;當使用RequestBody注解來接收HTTP請求中的JSON數據并將其轉換為Java對象時&#xff0c;Spring默認會忽略額外的屬性。這意味著如果發送的JSON包含一些目標對象中沒有定義的屬性&#xff0c;Spring不會報錯&#xff0c;這些額外的屬性會被簡單地忽略…

01. Linux嵌入式系統學習筆記(一)(linux基礎指令)

一. linux基礎操作指令 1. 新建文件和目錄 (1) 新建文件 touch 命令&#xff1a;用于創建空文件。 touch filename.txt 如果文件已存在&#xff0c;touch 會更新文件的訪問時間和修改時間。 (2) 新建目錄 mkdir 命令&#xff1a;用于創建目錄。 mkdir directoryname 使…

Java 列表復制與對象引用

Java 列表復制與對象引用 一、知識點 1. 對象引用的基本概念 在 Java 中&#xff0c;List<School> 這樣的集合存儲的并不是真正的對象&#xff0c;而是對象的“地址”&#xff08;引用&#xff09;。就好比你有一個文件柜&#xff0c;文件柜里放的不是文件本身&#x…

如何理解 Apache Iceberg 與湖倉一體(Lakehouse)?

一、什么是湖倉一體&#xff08;Lakehouse&#xff09;&#xff1f; 湖倉一體是一種融合了數據湖的靈活存儲能力與數據倉庫的高效分析功能的現代數據架構。它通過整合兩者的優勢&#xff0c;解決了傳統架構的局限性&#xff0c;為企業數據處理提供了更全面的解決方案。 數據湖…

Android面試總結之Android RecyclerView:從基礎機制到緩存優化

引言 在 Android 開發中&#xff0c;RecyclerView是高效展示列表數據的核心組件。其強大的性能源于獨特的視圖復用機制和四級緩存體系。本文將結合源碼與示例&#xff0c;帶你深入理解RecyclerView的工作原理與優化策略。 核心組件 RecyclerView&#xff1a;作為容器視圖&am…

【鴻蒙開發】Hi3861學習筆記- TCP客戶端

00. 目錄 文章目錄 00. 目錄01. TCP概述02. TCP應用場景03. TCP和UDP比較04. TCP相關API05. TCP編程流程06. 硬件設計07. 軟件設計08. 實驗現象09. 附錄 01. TCP概述 TCP&#xff08;Transmission Control Protocol&#xff09;是一種面向連接、可靠的傳輸層協議&#xff0c;旨…

【負載均衡系列】Keepalive

一、Keepalived 的核心功能 Keepalived 是一款用于實現 ?高可用(HA)? 和 ?負載均衡 的開源工具,核心基于 ?VRRP(Virtual Router Redundancy Protocol)? 協議,工作在網絡四層(傳輸層)和七層(應用層)。 主要用途: 通過虛擬IP(VIP)實現服務高可用(主備切換)。…