<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          13個(gè)你一定要知道的PyTorch特性

          共 7403字,需瀏覽 15分鐘

           ·

          2021-10-25 13:13

          點(diǎn)擊上方視學(xué)算法”,選擇加"星標(biāo)"或“置頂

          重磅干貨,第一時(shí)間送達(dá)

          編譯 | ronghuaiyang?
          來源 | 人工智能前沿講習(xí)?
          編輯 | 極市平臺(tái)

          導(dǎo)讀

          ?

          本文是作者使用PyTorch兩年后一些高效功能總結(jié),希望能幫助到需要的人。?

          PyTorch在學(xué)術(shù)界和工業(yè)界的應(yīng)用研究中都獲得了很多關(guān)注。它是一個(gè)具有很大靈活性的深度學(xué)習(xí)框架,使用了大量的實(shí)用工具和函數(shù)來加快工作速度。PyTorch的學(xué)習(xí)曲線并不是那么陡峭,但在其中實(shí)現(xiàn)高效和干凈的代碼可能會(huì)很棘手。在使用它超過2年之后,以下是我最喜歡的PyTorch功能,我希望我一開始學(xué)習(xí)它就知道。

          1 DatasetFolder

          當(dāng)學(xué)習(xí)PyTorch時(shí),人們首先要做的事情之一是實(shí)現(xiàn)自己的某種Dataset 。這是一個(gè)低級(jí)錯(cuò)誤,沒有必要浪費(fèi)時(shí)間寫這樣的東西。通常,數(shù)據(jù)集要么是數(shù)據(jù)列表(或者是numpy數(shù)組),要么磁盤上的文件。所以,把數(shù)據(jù)在磁盤上組織好,要比寫一個(gè)自定義的Dataset來加載某種奇怪的格式更好。

          分類器最常見的數(shù)據(jù)格式之一,是有一個(gè)帶有子文件夾的目錄,子文件夾表示類,子文件夾中的文件表示樣本,如下所示。

          folder/class_0/file1.txt
          folder/class_0/file2.txt
          folder/class_0/...

          folder/class_1/file3.txt
          folder/class_1/file4.txt

          folder/class_2/file5.txt
          folder/class_2/...

          有一個(gè)內(nèi)置的方式來加載這類數(shù)據(jù)集,不管你的數(shù)據(jù)是圖像,文本文件或其他什么,只要使用'DatasetFolder就可以了。令人驚訝的是,這個(gè)類是torchvision包的一部分,而不是核心PyTorch。這個(gè)類非常全面,你可以從文件夾中過濾文件,使用自定義代碼加載它們,并動(dòng)態(tài)轉(zhuǎn)換原始文件。例子:

          from?torchvision.datasets?import?DatasetFolder
          from?pathlib?import?Path
          #?I?have?text?files?in?this?folder
          ds?=?DatasetFolder("/Users/marcin/Dev/tmp/my_text_dataset",?
          ????loader=lambda?path:?Path(path).read_text(),
          ????extensions=(".txt",),?#only?load?.txt?files
          ????transform=lambda?text:?text[:100],?#?only?take?first?100?characters
          )

          #?Everything?you?need?is?already?there
          len(ds),?ds.classes,?ds.class_to_idx
          (20,?['novels',?'thrillers'],?{'novels':?0,?'thrillers':?1})

          如果你在處理圖像,還有一個(gè)torchvision.datasets.ImageFolder類,它基于DatasetLoader,它被預(yù)先配置為加載圖像。

          2 盡量少用.to\(device\),用zeros\_like/ones\_like之類的代替

          我讀過很多來自GitHub倉庫的PyTorch代碼。最讓我惱火的是,幾乎在每個(gè)repo中都有許多*.to(device)行,它們將數(shù)據(jù)從CPU或GPU轉(zhuǎn)移到其他地方。這樣的語句通常會(huì)出現(xiàn)在大量的repos或初學(xué)者教程中。我強(qiáng)烈建議盡可能少地實(shí)現(xiàn)這類操作,并依賴內(nèi)置的PyTorch功能自動(dòng)實(shí)現(xiàn)這類操作。到處使用.to(device)通常會(huì)導(dǎo)致性能下降,還會(huì)出現(xiàn)異常:

          Expected object of device type cuda but got device type cpu

          顯然,有些情況下你無法回避它,但大多數(shù)情況(如果不是全部)都在這里。其中一種情況是初始化一個(gè)全0或全1的張量,這在深度神經(jīng)網(wǎng)絡(luò)計(jì)算損失的的時(shí)候是經(jīng)常發(fā)生的,模型的輸出已經(jīng)在cuda上了,你需要另外的tensor也是在cuda上,這時(shí),你可以使用*_like操作符:

          my_output?#?on?any?device,?if?it's?cuda?then?my_zeros?will?also?be?on?cuda
          my_zeros?=?torch.zeros_like(my_output_from_model)

          在內(nèi)部,PyTorch所做的是調(diào)用以下操作:

          my_zeros?=?torch.zeros(my_output.size(),?dtype=my_output.dtype,?layout=my_output.layout,?device=my_output.device)

          所以所有的設(shè)置都是正確的,這樣就減少了代碼中出現(xiàn)錯(cuò)誤的概率。類似的操作包括:

          torch.zeros_like()
          torch.ones_like()
          torch.rand_like()
          torch.randn_like()
          torch.randint_like()
          torch.empty_like()
          torch.full_like()

          3 Register Buffer ( nn.Module.register_buffer)

          這將是我勸人們不要到處使用 .to(device) 的下一步。有時(shí),你的模型或損失函數(shù)需要有預(yù)先設(shè)置的參數(shù),并在調(diào)用forward時(shí)使用,例如,它可以是一個(gè)“權(quán)重”參數(shù),它可以縮放損失或一些固定張量,它不會(huì)改變,但每次都使用。對(duì)于這種情況,請(qǐng)使用nn.Module.register_buffer 方法,它告訴PyTorch將傳遞給它的值存儲(chǔ)在模塊中,并將這些值隨模塊一起移動(dòng)。如果你初始化你的模塊,然后將它移動(dòng)到GPU,這些值也會(huì)自動(dòng)移動(dòng)。此外,如果你保存模塊的狀態(tài),buffers也會(huì)被保存!

          一旦注冊(cè),這些值就可以在forward函數(shù)中訪問,就像其他模塊的屬性一樣。

          from?torch?import?nn
          import?torch

          class?ModuleWithCustomValues(nn.Module):
          ????def?__init__(self,?weights,?alpha):
          ????????super().__init__()
          ????????self.register_buffer("weights",?torch.tensor(weights))
          ????????self.register_buffer("alpha",?torch.tensor(alpha))
          ????
          ????def?forward(self,?x):
          ????????return?x?*?self.weights?+?self.alpha

          m?=?ModuleWithCustomValues(
          ????weights=[1.0,?2.0],?alpha=1e-4
          )
          m(torch.tensor([1.23,?4.56]))
          tensor([1.2301,?9.1201])

          4 Built-in Identity()

          有時(shí)候,當(dāng)你使用遷移學(xué)習(xí)時(shí),你需要用1:1的映射替換一些層,可以用nn.Module來實(shí)現(xiàn)這個(gè)目的,只返回輸入值。PyTorch內(nèi)置了這個(gè)類。

          例子,你想要在分類層之前從一個(gè)預(yù)訓(xùn)練過的ResNet50獲取圖像表示。以下是如何做到這一點(diǎn):

          from?torchvision.models?import?resnet50
          model?=?resnet50(pretrained=True)
          model.fc?=?nn.Identity()
          last_layer_output?=?model(torch.rand((1,?3,?224,?224)))
          last_layer_output.shape
          torch.Size([1,?2048])

          5 Pairwise distances: torch.cdist

          下次當(dāng)你遇到計(jì)算兩個(gè)張量之間的歐幾里得距離(或者一般來說:p范數(shù))的問題時(shí),請(qǐng)記住torch.cdist。它確實(shí)做到了這一點(diǎn),并且在使用歐幾里得距離時(shí)還自動(dòng)使用矩陣乘法,從而提高了性能。

          points1?=?torch.tensor([[0.0,?0.0],?[1.0,?1.0],?[2.0,?2.0]])
          points2?=?torch.tensor([[0.0,?0.0],?[-1.0,?-1.0],?[-2.0,?-2.0],?[-3.0,?-3.0]])?#?batches?don't?have?to?be?equal
          torch.cdist(points1,?points2,?p=2.0)
          tensor([[0.0000,?1.4142,?2.8284,?4.2426],
          ????????[1.4142,?2.8284,?4.2426,?5.6569],
          ????????[2.8284,?4.2426,?5.6569,?7.0711]])

          沒有矩陣乘法或有矩陣乘法的性能,在我的機(jī)器上使用mm時(shí),速度快了2倍以上。

          %%timeit
          points1?=?torch.rand((512,?2))
          points2?=?torch.rand((512,?2))
          torch.cdist(points1,?points2,?p=2.0,?compute_mode="donot_use_mm_for_euclid_dist")

          867μs±142μs per loop (mean±std. dev. of 7 run, 1000 loop each)

          %%timeit
          points1?=?torch.rand((512,?2))
          points2?=?torch.rand((512,?2))
          torch.cdist(points1,?points2,?p=2.0)

          417μs±52.9μs per loop (mean±std. dev. of 7 run, 1000 loop each)

          6 Cosine similarity: F.cosine_similarity

          與上一點(diǎn)相同,計(jì)算歐幾里得距離并不總是你需要的東西。當(dāng)處理向量時(shí),通常余弦相似度是選擇的度量。PyTorch也有一個(gè)內(nèi)置的余弦相似度實(shí)現(xiàn)。

          import?torch.nn.functional?as?F
          vector1?=?torch.tensor([0.0,?1.0])
          vector2?=?torch.tensor([0.05,?1.0])
          print(F.cosine_similarity(vector1,?vector2,?dim=0))
          vector3?=?torch.tensor([0.0,?-1.0])
          print(F.cosine_similarity(vector1,?vector3,?dim=0))
          tensor(0.9988)
          tensor(-1.)

          PyTorch中批量計(jì)算余弦距離

          import?torch.nn.functional?as?F
          batch_of_vectors?=?torch.rand((4,?64))
          similarity_matrix?=?F.cosine_similarity(batch_of_vectors.unsqueeze(1),?batch_of_vectors.unsqueeze(0),?dim=2)
          similarity_matrix
          tensor([[1.0000,?0.6922,?0.6480,?0.6789],
          ????????[0.6922,?1.0000,?0.7143,?0.7172],
          ????????[0.6480,?0.7143,?1.0000,?0.7312],
          ????????[0.6789,?0.7172,?0.7312,?1.0000]])

          7 歸一化向量: F.normalize

          最后一點(diǎn)仍然與向量和距離有松散的聯(lián)系,那就是歸一化:通常是通過改變向量的大小來提高計(jì)算的穩(wěn)定性。最常用的歸一化是L2,可以在PyTorch中按如下方式應(yīng)用:

          vector?=?torch.tensor([99.0,?-512.0,?123.0,?0.1,?6.66])
          normalized_vector?=?F.normalize(vector,?p=2.0,?dim=0)
          normalized_vector
          tensor([?1.8476e-01,?-9.5552e-01,??2.2955e-01,??1.8662e-04,??1.2429e-02])

          在PyTorch中執(zhí)行歸一化的舊方法是:

          vector?=?torch.tensor([99.0,?-512.0,?123.0,?0.1,?6.66])
          normalized_vector?=?vector?/?torch.norm(vector,?p=2.0)
          normalized_vector
          tensor([?1.8476e-01,?-9.5552e-01,??2.2955e-01,??1.8662e-04,??1.2429e-02])

          在PyTorch中批量進(jìn)行L2歸一化

          batch_of_vectors?=?torch.rand((4,?64))
          normalized_batch_of_vectors?=?F.normalize(batch_of_vectors,?p=2.0,?dim=1)
          normalized_batch_of_vectors.shape,?torch.norm(normalized_batch_of_vectors,?dim=1)?#?all?vectors?will?have?length?of?1.0
          (torch.Size([4,?64]),?tensor([1.0000,?1.0000,?1.0000,?1.0000]))

          8 線性層 + 分塊技巧 (torch.chunk)

          這是我最近發(fā)現(xiàn)的一個(gè)有創(chuàng)意的技巧。假設(shè)你想把你的輸入映射到N個(gè)不同的線性投影中。你可以通過創(chuàng)建N個(gè)nn.Linear來做到這一點(diǎn)。或者你也可以創(chuàng)建一個(gè)單一的線性層,做一個(gè)向前傳遞,然后將輸出分成N塊。這種方法通常會(huì)帶來更高的性能,所以這是一個(gè)值得記住的技巧。

          d?=?1024
          batch?=?torch.rand((8,?d))
          layers?=?nn.Linear(d,?128,?bias=False),?nn.Linear(d,?128,?bias=False),?nn.Linear(d,?128,?bias=False)
          one_layer?=?nn.Linear(d,?128?*?3,?bias=False)
          %%timeit
          o1?=?layers[0](batch)
          o2?=?layers[1](batch)
          o3?=?layers[2](batch)

          289 μs ± 30.8 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

          %%timeit
          o1,?o2,?o3?=?torch.chunk(one_layer(batch),?3,?dim=1)

          202 μs ± 8.09 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

          9 Masked select (torch.masked_select)

          有時(shí)你只需要對(duì)輸入張量的一部分進(jìn)行計(jì)算。給你一個(gè)例子:你想計(jì)算的損失只在滿足某些條件的張量上。為了做到這一點(diǎn),你可以使用torch.masked_select,注意,當(dāng)需要梯度時(shí)也可以使用這個(gè)操作。

          data?=?torch.rand((3,?3)).requires_grad_()
          print(data)
          mask?=?data?>?data.mean()
          print(mask)
          torch.masked_select(data,?mask)
          tensor([[0.0582,?0.7170,?0.7713],
          ????????[0.9458,?0.2597,?0.6711],
          ????????[0.2828,?0.2232,?0.1981]],?requires_grad=True)
          tensor([[False,??True,??True],
          ????????[?True,?False,??True],
          ????????[False,?False,?False]])
          tensor([0.7170,?0.7713,?0.9458,?0.6711],?grad_fn=)

          直接在tensor上應(yīng)用mask

          類似的行為可以通過使用mask作為輸入張量的 “indexer”來實(shí)現(xiàn)。

          data[mask]
          tensor([0.7170,?0.7713,?0.9458,?0.6711],?grad_fn=)

          有時(shí),一個(gè)理想的解決方案是用0填充mask中所有的False值,可以這樣做:

          data?*?mask
          tensor([[0.0000,?0.7170,?0.7713],
          ????????[0.9458,?0.0000,?0.6711],
          ????????[0.0000,?0.0000,?0.0000]],?grad_fn=)

          10 使用 torch.where來對(duì)tensors加條件

          當(dāng)你想把兩個(gè)張量結(jié)合在一個(gè)條件下這個(gè)函數(shù)很有用,如果條件是真,那么從第一個(gè)張量中取元素,如果條件是假,從第二個(gè)張量中取元素。

          x?=?torch.tensor([1.0,?2.0,?3.0,?4.0,?5.0],?requires_grad=True)
          y?=?-x
          condition_or_mask?=?x?<=?3.0
          torch.where(condition_or_mask,?x,?y)
          tensor([?1.,??2.,??3.,?-4.,?-5.],?grad_fn=)

          11 在給定的位置給張量填入值(Tensor.scatter)

          這個(gè)函數(shù)的用例如下,你想用給定位置下另一個(gè)張量的值填充一個(gè)張量。一維張量更容易理解,所以我將先展示它,然后繼續(xù)更高級(jí)的例子。

          data?=?torch.tensor([1,?2,?3,?4,?5])
          index?=?torch.tensor([0,?1])
          values?=?torch.tensor([-1,?-2,?-3,?-4,?-5])
          data.scatter(0,?index,?values)
          tensor([-1,?-2,??3,??4,??5])

          上面的例子很簡(jiǎn)單,但是現(xiàn)在看看如果將index改為index = torch.tensor([0, 1, 4])會(huì)發(fā)生什么:

          data?=?torch.tensor([1,?2,?3,?4,?5])
          index?=?torch.tensor([0,?1,?4])
          values?=?torch.tensor([-1,?-2,?-3,?-4,?-5])
          data.scatter(0,?index,?values)
          tensor([-1,?-2,??3,??4,?-3])

          為什么最后一個(gè)值是-3,這是反直覺的,對(duì)吧?這是PyTorch scatter函數(shù)的中心思想。index變量表示data張量的第i個(gè)值應(yīng)該放在values張量的哪個(gè)位置。我希望下面的簡(jiǎn)單python版的這個(gè)操作能讓你更明白:

          data_orig?=?torch.tensor([1,?2,?3,?4,?5])
          index?=?torch.tensor([0,?1,?4])
          values?=?torch.tensor([-1,?-2,?-3,?-4,?-5])
          scattered?=?data_orig.scatter(0,?index,?values)

          data?=?data_orig.clone()
          for?idx_in_values,?where_to_put_the_value?in?enumerate(index):
          ????what_value_to_put?=?values[idx_in_values]
          ????data[where_to_put_the_value]?=?what_value_to_put
          data,?scattered
          (tensor([-1,?-2,??3,??4,?-3]),?tensor([-1,?-2,??3,??4,?-3]))

          2D數(shù)據(jù)的PyTorch scatter例子

          始終記住,index的形狀與values的形狀相關(guān),而index中的值對(duì)應(yīng)于data中的位置。

          data?=?torch.zeros((4,?4)).float()
          index?=?torch.tensor([
          ????[0,?1],
          ????[2,?3],
          ????[0,?3],
          ????[1,?2]
          ])
          values?=?torch.arange(1,?9).float().view(4,?2)
          values,?data.scatter(1,?index,?values)
          (tensor([[1.,?2.],
          ????????[3.,?4.],
          ????????[5.,?6.],
          ????????[7.,?8.]]),
          tensor([[1.,?2.,?0.,?0.],
          ????????[0.,?0.,?3.,?4.],
          ????????[5.,?0.,?0.,?6.],
          ????????[0.,?7.,?8.,?0.]]))

          12 在網(wǎng)絡(luò)中進(jìn)行圖像插值 (F.interpolate)

          當(dāng)我學(xué)習(xí)PyTorch時(shí),我驚訝地發(fā)現(xiàn),實(shí)際上可以在前向傳遞中調(diào)整圖像(或任何中間張量),并保持梯度流。這種方法在使用CNN和GANs時(shí)特別有用。

          #?image?from?https://commons.wikimedia.org/wiki/File:A_female_British_Shorthair_at_the_age_of_20_months.jpg
          img?=?Image.open("./cat.jpg")
          img
          to_pil_image(
          ????F.interpolate(to_tensor(img).unsqueeze(0),??#?batch?of?size?1
          ??????????????????mode="bilinear",?
          ??????????????????scale_factor=2.0,?
          ??????????????????align_corners=False).squeeze(0)?#?remove?batch?dimension
          )

          看看梯度流是如何保存的:

          F.interpolate(to_tensor(img).unsqueeze(0).requires_grad_(),
          ??????????????????mode="bicubic",?
          ??????????????????scale_factor=2.0,?
          ??????????????????align_corners=False)
          tensor([[[[0.9216,?0.9216,?0.9216,??...,?0.8361,?0.8272,?0.8219],
          ????[0.9214,?0.9214,?0.9214,??...,?0.8361,?0.8272,?0.8219],
          ????[0.9212,?0.9212,?0.9212,??...,?0.8361,?0.8272,?0.8219],
          ????...,
          ????[0.9098,?0.9098,?0.9098,??...,?0.3592,?0.3486,?0.3421],
          ????[0.9098,?0.9098,?0.9098,??...,?0.3566,?0.3463,?0.3400],
          ????[0.9098,?0.9098,?0.9098,??...,?0.3550,?0.3449,?0.3387]],

          ????[[0.6627,?0.6627,?0.6627,??...,?0.5380,?0.5292,?0.5238],
          ????[0.6626,?0.6626,?0.6626,??...,?0.5380,?0.5292,?0.5238],
          ????[0.6623,?0.6623,?0.6623,??...,?0.5380,?0.5292,?0.5238],
          ????...,
          ????[0.6196,?0.6196,?0.6196,??...,?0.3631,?0.3525,?0.3461],
          ????[0.6196,?0.6196,?0.6196,??...,?0.3605,?0.3502,?0.3439],
          ????[0.6196,?0.6196,?0.6196,??...,?0.3589,?0.3488,?0.3426]],

          ????[[0.4353,?0.4353,?0.4353,??...,?0.1913,?0.1835,?0.1787],
          ????[0.4352,?0.4352,?0.4352,??...,?0.1913,?0.1835,?0.1787],
          ????[0.4349,?0.4349,?0.4349,??...,?0.1913,?0.1835,?0.1787],
          ????...,
          ????[0.3333,?0.3333,?0.3333,??...,?0.3827,?0.3721,?0.3657],
          ????[0.3333,?0.3333,?0.3333,??...,?0.3801,?0.3698,?0.3635],
          ????[0.3333,?0.3333,?0.3333,??...,?0.3785,?0.3684,?0.3622]]]],
          grad_fn=)

          13 將圖像做成網(wǎng)格 (torchvision.utils.make_grid)

          當(dāng)使用PyTorch和torchvision時(shí),不需要使用matplotlib或一些外部庫來復(fù)制粘貼代碼來顯示圖像網(wǎng)格。只要使用torchvision.utils.make_grid就行了。

          from?torchvision.utils?import?make_grid
          from?torchvision.transforms.functional?import?to_tensor,?to_pil_image
          from?PIL?import?Image
          img?=?Image.open("./cat.jpg")
          to_pil_image(
          ????make_grid(
          ????????[to_tensor(i)?for?i?in?[img,?img,?img]],
          ?????????nrow=2,?#?number?of?images?in?single?row
          ?????????padding=5?#?"frame"?size
          ?????)
          )

          原文鏈接:https://zablo.net/blog/post/pytorch-13-features-you-should-know/

          如果覺得有用,就請(qǐng)分享到朋友圈吧!


          點(diǎn)個(gè)在看 paper不斷!

          瀏覽 38
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲男人影院 | 黄色日本视频 | 丁香五月婷婷av影院 | 欧美日本在线 | 日韩在线网址 |