PyTorch與向量化計(jì)算
向量化計(jì)算是一種特殊的并行計(jì)算方式。一般來(lái)說(shuō),程序在同一時(shí)間內(nèi)只執(zhí)行一個(gè)操作,而并行計(jì)算可以在同一時(shí)間內(nèi)執(zhí)行多個(gè)操作。向量化計(jì)算是指對(duì)不同的數(shù)據(jù)執(zhí)行同樣的一個(gè)或一批指令,或者把指令應(yīng)用到一個(gè)數(shù)組或向量上,從而將多次循環(huán)操作變成一次計(jì)算。
向量化操作可以極大地提高科學(xué)運(yùn)算的效率。盡管Python本身是一門(mén)高級(jí)語(yǔ)言,使用簡(jiǎn)便,但是其中存在著許多低效的操作,例如for循環(huán)等。因此,在科學(xué)計(jì)算中應(yīng)當(dāng)極力避免使用Python原生的for循環(huán),盡量使用向量化數(shù)值運(yùn)算,下面舉例說(shuō)明:
In: import torch as t# 定義for循環(huán)完成加法操作def for_loop_add(x, y):result = []for i, j in zip(x, y):result.append(i + j)return t.tensor(result)x = t.zeros(100)y = t.ones(100)%timeit -n 100 for_loop_add(x, y)%timeit -n 100 (x + y) # +是向量化計(jì)算Out:100 loops, best of 3: 786 μs per loop100 loops, best of 3: 2.57 μs per loop
從上面的例子中可以看出,for循環(huán)和向量化計(jì)算之間存在數(shù)百倍的速度差距,在實(shí)際使用中應(yīng)該盡量調(diào)用內(nèi)建函數(shù)(buildin-function)。這些函數(shù)底層由C/C++實(shí)現(xiàn),在實(shí)現(xiàn)中使用了向量化計(jì)算的思想,通過(guò)底層優(yōu)化實(shí)現(xiàn)了高效計(jì)算。在日常編程中應(yīng)該養(yǎng)成向量化的編程習(xí)慣,避免對(duì)較大的Tensor進(jìn)行逐元素的遍歷操作,從而提高程序的運(yùn)行效率。
本文將主要從廣播法則、高級(jí)索引兩個(gè)方面介紹PyTorch中的向量化計(jì)算。
01
廣播法則(broadcast)是科學(xué)計(jì)算中經(jīng)常使用的一個(gè)技巧,它在快速執(zhí)行向量化計(jì)算的同時(shí)不會(huì)占用額外的內(nèi)存/顯存。NumPy中的廣播法則定義如下。
所有輸入數(shù)組都與形狀(shape)最大的數(shù)組看齊,形狀不足的部分在前面加1補(bǔ)齊。
兩個(gè)數(shù)組要么在某一個(gè)維度的尺寸一致,要么其中一個(gè)數(shù)組在該維度的尺寸為1,否則不符合廣播法則的要求。
如果輸入數(shù)組的某個(gè)維度的尺寸為1,那么計(jì)算時(shí)沿此維度復(fù)制擴(kuò)充成目標(biāo)的形狀大小。
雖然PyTorch已經(jīng)支持了自動(dòng)廣播法則,但是建議通過(guò)以下兩種方式的組合手動(dòng)實(shí)現(xiàn)廣播法則,這樣更加直觀,也更不容易出錯(cuò)。
unsqueeze、view或者tensor[None] :為數(shù)據(jù)某一維度補(bǔ)1,實(shí)現(xiàn)第一個(gè)廣播法則。
expand或者expand_as,重復(fù)數(shù)組,實(shí)現(xiàn)第三個(gè)廣播法則;該操作不會(huì)復(fù)制整個(gè)數(shù)組,因此不會(huì)占用額外的空間。
注意:repeat可以實(shí)現(xiàn)與expand類(lèi)似的功能,expand是在已經(jīng)存在的Tensor上創(chuàng)建一個(gè)新的視圖(view),repeat會(huì)將相同的數(shù)據(jù)復(fù)制多份,因此會(huì)占用額外的空間。
首先來(lái)看自動(dòng)廣播法則:
In: # 自動(dòng)廣播法則# 第一步:a是2維的,b是3維的,所以先在較小的a前面補(bǔ)1個(gè)維度,# 即:a.unsqueeze(0),a的形狀變成(1,3,2),b的形狀是(2,3,1),# 第二步:a和b在第一和第三個(gè)維度的形狀不一樣,同時(shí)其中一個(gè)為1,# 利用廣播法則擴(kuò)展,兩個(gè)形狀都變成了(2,3,2)a = t.ones(3, 2)b = t.zeros(2, 3, 1)(a + b).shapeOut:torch.Size([2, 3, 2])
再來(lái)看如何手動(dòng)實(shí)現(xiàn)以上廣播過(guò)程:
In: # 手動(dòng)廣播法則,下面兩行操作是等效的,推薦使用None的方法# a.view(1, 3, 2).expand(2, 3, 2) + b.expand(2, 3, 2)a[None,:,:].expand(2, 3, 2) + b.expand(2, 3, 2)Out:tensor([[[1., 1.],[1., 1.],[1., 1.]],[[1., 1.],[1., 1.],[1., 1.]]])
02
2.1 基本索引
元組序列:在索引中直接使用一個(gè)元組序列對(duì)Tensor中數(shù)據(jù)的具體位置進(jìn)行定位,也可以直接使用多個(gè)整數(shù)(等價(jià)于元組序列省略括號(hào)的形式)代替。 切片對(duì)象(Slice Object):在索引中常見(jiàn)的切片對(duì)象形如start:stop:step,對(duì)一個(gè)維度進(jìn)行全選時(shí)可以直接使用:。 省略號(hào)(...):在索引中常用省略號(hào)來(lái)代表一個(gè)或多個(gè)維度的切片。 None:與NumPy中的newaxis相同,None在PyTorch索引中起到增加一個(gè)維度的作用。
2.1.1 元組序列
In: a = t.arange(1, 25).view(2, 3, 4)aOut:tensor([[[ 1, 2, 3, 4],[ 5, 6, 7, 8],[ 9, 10, 11, 12]],[[13, 14, 15, 16],[17, 18, 19, 20],[21, 22, 23, 24]]])In: # 提取位置[0, 1, 2]的元素# 等價(jià)于a[(0, 1, 2)](保留括號(hào)的元組形式)a[0, 1, 2]Out:tensor(7)
注意:a[0, 1, 2]與a[[0, 1, 2]]、a[(0, 1, 2),]并不等價(jià),后面兩個(gè)不滿(mǎn)足基本索引的條件,既不是一個(gè)元組序列又不是一個(gè)切片對(duì)象,它們屬于高級(jí)索引的范疇,這部分內(nèi)容將在后文進(jìn)行講解。
2.1.2 : 和 ...
在實(shí)際編程中,經(jīng)常會(huì)在Tensor的任意維度上進(jìn)行切片操作,PyTorch已經(jīng)封裝好了兩個(gè)運(yùn)算符:和...,它們的用法如下。
:常用于對(duì)一個(gè)維度進(jìn)行操作,基本的語(yǔ)法形式是:start:end:step。單獨(dú)使用:代表全選這個(gè)維度,start和end為空分別表示從頭開(kāi)始和一直到結(jié)束,step的默認(rèn)值是1。
...用于省略任意多個(gè)維度,可以用在切片的中間,也可以用在首尾。
下面舉例說(shuō)明這兩個(gè)運(yùn)算符的使用方法:
In: a = t.rand(64, 3, 224, 224)print(a[:,:,0:224:4,:].shape) # 第三個(gè)維度間隔切片# 省略start和end代表整個(gè)維度print(a[:,:,::4,:].shape)Out:torch.Size([64, 3, 56, 224])torch.Size([64, 3, 56, 224])In: # 使用...代替一個(gè)或多個(gè)維度,建議一個(gè)索引中只使用一次a[...,::4,:].shape# a[...,::4,...].shape # 如果將最后一個(gè)維度也改為...,那么在匹配維度時(shí)將混亂出錯(cuò)Out:torch.Size([64, 3, 56, 224])
2.1.3 None索引
在PyTorch的源碼中,None索引經(jīng)常被使用。None索引可以直觀地表示維度的擴(kuò)展,在廣播法則中充當(dāng)1的作用。使用None索引,本質(zhì)上與使用unsqueeze函數(shù)是等價(jià)的,都能起到擴(kuò)展維度的作用。在維度較多的情況下,或者需要對(duì)多個(gè)維度先進(jìn)行擴(kuò)展再進(jìn)行矩陣計(jì)算時(shí),使用None索引會(huì)更加清晰直觀。因此,推薦使用None索引進(jìn)行維度的擴(kuò)展,下面舉例說(shuō)明:
In: a = t.rand(2, 3, 4, 5)# 在最前面加一個(gè)維度,下面兩種寫(xiě)法等價(jià)print(a.unsqueeze(0).shape)print(a[None, ...].shape)Out:torch.Size([1, 2, 3, 4, 5])torch.Size([1, 2, 3, 4, 5])In: # 在原有的四個(gè)維度中均插入一個(gè)維度,成為(2,1,3,1,4,1,5)# unsqueeze方法,每成功增加一個(gè)維度,都需要重新計(jì)算下一個(gè)需要增加的維度位置b = a.unsqueeze(1)b = b.unsqueeze(3)b = b.unsqueeze(5)b.shapeOut:torch.Size([2, 1, 3, 1, 4, 1, 5])In: # None索引方法,直接在需要增加的維度上填寫(xiě)None即可a[:,None,:,None,:,None,:].shapeOut:torch.Size([2, 1, 3, 1, 4, 1, 5])
2.2 高級(jí)索引
與基本索引相比,高級(jí)索引的觸發(fā)條件有所不同,常見(jiàn)的高級(jí)索引遵循以下三個(gè)規(guī)律。
索引是一個(gè)非元組序列:例如tensor[(0, 1, 2),]。
索引是一個(gè)整數(shù)類(lèi)型或者布爾類(lèi)型的Tensor。
索引是元組序列,但是里面至少包含一個(gè)整數(shù)類(lèi)型或者布爾類(lèi)型的Tensor。
2.2.1 整數(shù)數(shù)組索引
對(duì)于整數(shù)數(shù)組索引(Integer Array Indexing),一般情況下需要先確定輸入輸出Tensor的形狀,這是因?yàn)樗械恼麛?shù)索引都有一個(gè)相對(duì)固定的模式:
其中

如果index的形狀不完全相同,但是滿(mǎn)足廣播法則,那么它們將自動(dòng)對(duì)齊成一樣的形狀,從而完成整數(shù)數(shù)組索引操作。對(duì)于不能夠廣播或者不能夠得到相同形狀的索引,無(wú)法進(jìn)行整數(shù)數(shù)組索引操作。下面舉例說(shuō)明:
In: a = t.arange(12).view(3, 4)# 相同形狀的index索引# 獲取索引為[1,0]、[2,2]的元素a[t.tensor([1, 2]), t.tensor([0, 2])]Out:tensor([ 4, 10])In: # 不相同形狀的index索引,滿(mǎn)足廣播法則# 獲取索引為[1,0]、[2,0]、[1,2]、[2,2]的元素a[t.tensor([1,2])[None,:], t.tensor([0, 2])[:,None]]Out:tensor([[ 4, 8],[ 6, 10]])
有時(shí)高級(jí)索引與基本索引需要混合使用,這時(shí)候基本索引(如切片對(duì)象、省略號(hào)、None等)會(huì)將高級(jí)索引切分成多個(gè)區(qū)域。假設(shè)高級(jí)索引idx1,idx2,idx3的形狀都是
所有的高級(jí)索引都處于相鄰的維度:例如tensor[idx1, :, :]或者tensor[:, idx2, idx3],那么直接將所有高級(jí)索引所在區(qū)域的維度轉(zhuǎn)換成高級(jí)索引的維度,Tensor的其他維度按照基本索引正常計(jì)算。
基本索引將多個(gè)高級(jí)索引劃分到不同區(qū)域:例如tensor[idx1, :, idx3],那么統(tǒng)一將高級(jí)索引的維度放在輸出Tensor維度的開(kāi)頭,剩下部分補(bǔ)齊基本索引的維度。這時(shí)所有的高級(jí)索引并不相鄰,無(wú)法確定高級(jí)索引的維度應(yīng)該替換Tensor的哪些維度,因此統(tǒng)一放到開(kāi)頭位置。
下面舉例說(shuō)明:
In: a = t.arange(24).view(2, 3, 4)idx1 = t.tensor([[1, 0]]) # shape 1×2idx2 = t.tensor([[0, 2]]) # shape 1×2# 所有的高級(jí)索引相鄰a[:, idx1, idx2].shapeOut:torch.Size([2, 1, 2])In: # 手動(dòng)計(jì)算輸出形狀# a的第一個(gè)維度保留,后兩個(gè)維度是索引維度a.shape[0], idx1.shapeOut:(2, torch.Size([1, 2]))In: a = t.arange(120).reshape(2, 3, 4, 5)# 中間兩個(gè)維度替換成高級(jí)索引的維度a[:, idx1, idx2, :].shapeOut:torch.Size([2, 1, 2, 5])In: # 高級(jí)索引被劃分到不同區(qū)域# 高級(jí)索引的維度放在輸出維度的最前面,剩下的維度依次補(bǔ)齊a[idx1, :, idx2].shapeOut:torch.Size([1, 2, 3, 5])In: a[:,idx1,:,idx2].shapeOut:torch.Size([1, 2, 2, 4])
整數(shù)數(shù)組索引是根據(jù)索引數(shù)組(
當(dāng)索引數(shù)組的個(gè)數(shù)等于Tensor的維度數(shù)時(shí),索引輸出的形狀等價(jià)于
的形狀,輸出的每一個(gè)元素等價(jià)于 。 當(dāng)索引數(shù)組的個(gè)數(shù)小于Tensor的維度數(shù)時(shí),類(lèi)似于切片操作,將這個(gè)切片當(dāng)做索引操作的結(jié)果。
下面來(lái)看幾個(gè)示例:
In: a = t.arange(12).view(3, 4)print(a)print(a[[2,0]]) # 索引數(shù)組個(gè)數(shù)小于a的維度數(shù)# 索引數(shù)組個(gè)數(shù)等于a的維度數(shù)# 獲取索引為[1,3]、[2,2]、[0,1]的元素print(a[[1, 2, 0], [3, 2, 1]]) Out:tensor([[ 0, 1, 2, 3],[ 4, 5, 6, 7],[ 8, 9, 10, 11]])tensor([[ 8, 9, 10, 11],[ 0, 1, 2, 3]])tensor([ 7, 10, 1])In: # 輸出形狀取決于索引數(shù)組的形狀# 獲取索引為[0,1]、[2,3]、[1,3]、[0,1]的元素idx1 = t.tensor([[0, 2], [1, 0]])idx2 = t.tensor([[1, 3], [3, 1]])a[idx1, idx2]Out:tensor([[ 1, 11],[ 7, 1]])
In: # 錯(cuò)誤示范idx1 = [[0, 2], [1, 0]]idx2 = [[1, 3], [3, 1]]idx = t.tensor([idx1, idx2]) # 提前將索引數(shù)組進(jìn)行組合# a[idx]# 如果報(bào)錯(cuò),表示超出了索引范圍# 沒(méi)報(bào)錯(cuò),但是結(jié)果不是想要的結(jié)果。這是因?yàn)橹凰饕说谝粋€(gè)維度,后面的維度直接進(jìn)行切片
2.2.2 布爾數(shù)組索引
在高級(jí)索引中,如果索引數(shù)組的類(lèi)型是布爾型,那么就會(huì)使用布爾數(shù)組索引(Boolean Array Indexing)。布爾類(lèi)型的數(shù)組對(duì)象可以通過(guò)比較運(yùn)算符產(chǎn)生,下面舉例說(shuō)明:
In: a = t.arange(12).view(3, 4)idx_bool = t.rand(3, 4) > 0.5idx_boolOut:tensor([[ True, True, True, True],[False, False, False, False],[False, False, False, False]])In: a[idx_bool] # 返回idx_bool中為T(mén)rue的部分Out:tensor([0, 1, 2, 3])
布爾數(shù)組索引常用于對(duì)特定條件下的數(shù)值進(jìn)行修改。例如,對(duì)一個(gè)Tensor中的所有正數(shù)進(jìn)行乘2操作,最直觀的方法是寫(xiě)一個(gè)for循環(huán),遍歷整個(gè)Tensor,對(duì)滿(mǎn)足條件的數(shù)進(jìn)行計(jì)算。
In: # 利用for循環(huán)a = t.tensor([[1, -3, 2], [2, 9, -1], [-8, 4, 1]])for i in range(a.shape[0]):for j in range(a.shape[1]):if a[i, j] > 0:a[i, j] *= 2aOut:tensor([[ 2, -3, 4],[ 4, 18, -1],[-8, 8, 2]])
此時(shí),可以使用布爾數(shù)組索引來(lái)簡(jiǎn)化運(yùn)算:
In: # 利用布爾數(shù)組索引a = t.tensor([[1, -3, 2], [2, 9, -1], [-8, 4, 1]])a[a > 0] *= 2aOut:tensor([[ 2, -3, 4],[ 4, 18, -1],[-8, 8, 2]])
2.3 einsum / einops
在高級(jí)索引中還有一類(lèi)特殊方法:愛(ài)因斯坦操作。下面介紹兩種常用的愛(ài)因斯坦操作:einsum和einops,它們被廣泛地用于向量、矩陣和張量的運(yùn)算。靈活運(yùn)用愛(ài)因斯坦操作可以用非常簡(jiǎn)單的方式表示較為復(fù)雜的多維Tensor之間的運(yùn)算。
2.3.1 einsum
In: # 轉(zhuǎn)置操作import torch as ta = t.arange(9).view(3, 3)b = t.einsum('ij->ji', a) # 直接交換兩個(gè)維度print(a)print(b)Out:tensor([[0, 1, 2],[3, 4, 5],[6, 7, 8]])tensor([[0, 3, 6],[1, 4, 7],[2, 5, 8]])In: # 求和操作a = t.arange(36).view(3, 4, 3)b = t.einsum('ijk->', a) # 所有元素求和bOut:tensor(630)In: # 多個(gè)張量之間的混合運(yùn)算a = t.arange(6).view(2, 3)b = t.arange(3)# 矩陣對(duì)應(yīng)維度相乘,b進(jìn)行了廣播t.einsum('ij,j->ij', a, b)Out:tensor([[ 0, 1, 4],[ 0, 4, 10]])In: # 直觀表達(dá)矩陣的內(nèi)積和外積a = t.arange(6).view(2, 3)b = t.arange(6).view(3, 2)c_in = t.einsum('ij,ij->', a, a) # 內(nèi)積,結(jié)果是一個(gè)數(shù)c_out = t.einsum('ik,kj->ij', a, b) # 外積,矩陣乘法的結(jié)果print(c_in)print(c_out)Out:tensor(55)tensor([[10, 13],[28, 40]])
2.3.2 einops
除了上面介紹的愛(ài)因斯坦求和,其他的愛(ài)因斯坦操作都封裝在einops中,它支持NumPy、PyTorch、Chainer、TensorFlow等多種框架的數(shù)據(jù)格式。在愛(ài)因斯坦操作中,多次轉(zhuǎn)置操作不再使用tensor_x.transpose(1, 2).transpose(2, 3),而是用更直觀的方式:rearrange(tensor_x, 'b c h w -> b h w c')代替。
einops有很多復(fù)雜的操作,這里僅講解最常見(jiàn)、最直觀的用法,并分析如何在深度學(xué)習(xí)框架中高效使用einops操作。有關(guān)einops更詳細(xì)的內(nèi)容示例和底層實(shí)現(xiàn)可以參考einops的說(shuō)明文檔。
In: from einops import rearrange, reducea = t.rand(16, 3, 64, 64) # batch × channel × height × weight# 轉(zhuǎn)置操作rearrange(a, 'b c h w -> b h w c').shapeOut:torch.Size([16, 64, 64, 3])In: # 融合部分維度y = rearrange(a, 'b c h w -> b (h w c)') # flatteny.shapeOut:torch.Size([16, 12288])
愛(ài)因斯坦操作憑借其便捷、直觀的特點(diǎn),在視覺(jué)Transformer中得到了廣泛的應(yīng)用。假設(shè)輸入是256×256×3的彩色圖像,根據(jù)Transformer的要求,現(xiàn)在需要將其劃分成8×8=64個(gè)塊,每個(gè)塊有32×32×3=3072個(gè)像素,使用愛(ài)因斯坦操作實(shí)現(xiàn)如下:
In: img = t.randn(1, 3, 256, 256)x = rearrange(img, 'b c (h p1) (w p2) -> b (p1 p2) (h w c)', p1=8, p2=8)x.shapeOut:torch.Size([1, 64, 3072])
在很多網(wǎng)絡(luò)結(jié)構(gòu)中,需要提取通道間或者空間像素之間的信息,從而完成通道的部分維度和空間的部分維度之間的轉(zhuǎn)化。直接使用索引等操作會(huì)比較煩瑣,einops操作可以直觀地完成這個(gè)過(guò)程:
In: # Space to Depthb = t.rand(16, 32, 64, 64)s2d = rearrange(b, 'b c (h h0) (w w0) -> b (h0 w0 c) h w', h0=2, w0=2)# Depth to Spaced2s = rearrange(b, 'b (c h0 w0) h w -> b c (h h0) (w w0)', h0=2, w0=2)print("Space to Depth: ", s2d.shape)print("Depth to Space: ", d2s.shape)Out:Space to Depth: torch.Size([16, 128, 32, 32])Depth to Space: torch.Size([16, 8, 128, 128])
除了rearrange,常見(jiàn)的einops操作還有reduce,它常用于求和、求均值等操作,同時(shí)也用于搭建卷積神經(jīng)網(wǎng)絡(luò)中的池化層,下面舉例說(shuō)明:
In: # 對(duì)空間像素求和y = reduce(a, 'b c h w -> b c', reduction='sum')y.shape # 對(duì)h和w維度求和Out:torch.Size([16, 3])In: # 全局平局池化global_avg_pooling = reduce(a, 'b c h w -> b c', reduction='mean')global_avg_pooling.shape Out:torch.Size([16, 3])
einops的所有操作都支持反向傳播,可以有效地嵌入到深度學(xué)習(xí)模型框架中,示例如下:
In: x0 = t.rand(16, 3, 64, 64)x0.requires_grad = Truex1 = reduce(x0, 'b c h w -> b c', reduction='max')x2 = rearrange(x1, 'b c -> c b')x3 = reduce(x2, 'c b -> ', reduction='sum')x3.backward()x0.grad.shapeOut:torch.Size([16, 3, 64, 64])
03
向量化思想可以解決深度學(xué)習(xí)中的很多經(jīng)典問(wèn)題,例如實(shí)現(xiàn)img2col快速卷積算法、在目標(biāo)檢測(cè)中計(jì)算檢測(cè)結(jié)果框與ground truth的交并比(IoU)以及實(shí)現(xiàn)RCNN網(wǎng)絡(luò)中的RoI Align算法等。這里我們以反向Unique函數(shù)為例進(jìn)行說(shuō)明。
在PyTorch中有一個(gè)unique函數(shù),它的功能是返回輸入Tensor中不同的元素組成的unique list,同時(shí)返回輸入Tensor對(duì)應(yīng)于這個(gè)unique list的索引。當(dāng)拿到了這個(gè)unique list和對(duì)應(yīng)的索引,能否還原出輸入的Tensor呢?
答案是肯定的。最簡(jiǎn)單的思路是遍歷這個(gè)索引,逐個(gè)生成輸入Tensor對(duì)應(yīng)位置的元素,最后進(jìn)行組合即可。這個(gè)過(guò)程比較繁瑣,可以考慮使用高級(jí)索引解決這個(gè)問(wèn)題。根據(jù)上文中整數(shù)數(shù)組索引的思路,這個(gè)索引的size和目標(biāo)Tensor的size是一致的,因此可以直接使用整數(shù)數(shù)組索引對(duì)原始Tensor進(jìn)行構(gòu)建,具體實(shí)現(xiàn)如下:
In: # 隨機(jī)生成一組形狀為(10, 15, 10, 5)、0~9數(shù)字組成的張量a = t.randint(1, 10, (10, 15, 10, 5))# 獲取輸出的unique list和索引output, inverse_indices = t.unique(a, return_inverse=True)# 通過(guò)整數(shù)數(shù)組索引 還原原始tensora_generate = output[inverse_indices]a_generate.equal(a)Out:True
上述結(jié)果可以看出,還原的Tensor值與原始值一致,這意味著使用高級(jí)索引方法可以便捷地完成反向unique操作,從而避免了耗時(shí)較長(zhǎng)的循環(huán)遍歷操作。
04
本文對(duì)PyTorch中的向量化計(jì)算與高級(jí)索引進(jìn)行了詳細(xì)介紹。向量化思想在高維數(shù)據(jù)處理時(shí)能夠有效提升計(jì)算效率,高級(jí)索引操作可以幫助用戶(hù)靈活地對(duì)Tensor進(jìn)行取值、切片等操作,以便進(jìn)行更加復(fù)雜的計(jì)算。讀者可以仔細(xì)體會(huì)其中的向量化思想,并在解決實(shí)際問(wèn)題時(shí)嘗試使用向量化思想進(jìn)行編程,從而提高程序的運(yùn)行效率。
本文內(nèi)容節(jié)選自 @KEDE @陳云 的書(shū)籍《深度學(xué)習(xí)框架PyTorch入門(mén)與實(shí)踐(第2版)》
猜您喜歡:
戳我,查看GAN的系列專(zhuān)輯~!附下載 | 《可解釋的機(jī)器學(xué)習(xí)》中文版
附下載 |《TensorFlow 2.0 深度學(xué)習(xí)算法實(shí)戰(zhàn)》
附下載 |《計(jì)算機(jī)視覺(jué)中的數(shù)學(xué)方法》分享
《基于深度學(xué)習(xí)的表面缺陷檢測(cè)方法綜述》
《基于深度神經(jīng)網(wǎng)絡(luò)的少樣本學(xué)習(xí)綜述》
