パイナップルの背後のパイソンマジック


ピトルッチは近年の深い学習枠組みへの試みの一つとして浮上した.この人気は、その使いやすいAPIに起因することができます、そして、それはより多くの「パイソン」です.

Pytorchは私たちに一貫性とクリーンAPIを与えるためにPythonの多くのネイティブ機能を活用しています.本稿では、それらの固有の特徴を詳細に説明します.これらを学ぶことは、より良い理由をあなたがピーチで特定の方法を行い、それが提供しているもののより良い使用を理解するのに役立ちます.

マジックメソッド


層のような

 are some of the basic constructs in PyTorch that we use to build our models. You import the layer and apply them to tensors.



```python
import torch
import torch.nn as nn

x = torch.rand(1, 784)
layer = nn.Linear(784, 10)
output = layer(x)
ここではいくつかのテンソルの層を呼び出すことができますx , それで、それは機能権利でなければなりませんか?Is nn.Linear() 関数を返す?タイプをチェックして確認しましょう.
>>> type(layer)
<class 'torch.nn.modules.linear.Linear'>
驚き!nn.Linear は実際にクラスとそのクラスのオブジェクトである.

"What! How could we call it then? Aren't only functions supposed to be callable?"


Nope、callableオブジェクトを作成することもできます.Pythonは、マジック関数を使用してクラスコール可能なオブジェクトを作成するネイティブな方法を提供します.
数を2倍にするクラスの簡単な例を見ましょう.
class Double(object):
    def __call__(self, x):
        return 2*x
ここでは、マジックメソッドを追加__call__ クラス内の任意の数を2倍にするクラスです.さて、このクラスのオブジェクトを作成し、いくつかの数で呼び出します.
>>> d = Double()
>>> d(2)
4
あるいは、上記のコードを1行で結合することもできる.
>>> Double()(2)
4
Pythonのすべてがオブジェクトであるので、これは動きます.数を2倍にする機能の例を見てください.
def double(x):
    return 2*x

>>> double(2)
4
__call__ 舞台裏の方法.
>>> double.__call__(2)
4

フォワードパスにおけるマジック法


つの完全に接続された層をMNISTイメージに適用するモデルの例を見てみましょう.
import torch.nn as nn

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784, 10)        

    def forward(self, x):
        return self.fc1(x)
以下のコードはあなたによく知らなければなりません.テンソルX上のこのモデルの出力を計算している.
x = torch.rand(10, 784)
model = Model()
output = model(x)
テンソルに直接モデルを呼び出すことは.forward() 関数.どうやって動くの?
それは前の例で同じ理由です.我々はクラスを継承しているnn.Module . 内部的にはnn.Module__call__() を呼び出すマジックメソッド.forward() . だから、私たちがオーバーライド.forward() 後で実行する.
# nn.Module
class Module(object):
    def __call__(self, x):
        # Simplified
        # Actual implementation has validation and gradient tracking.
        return self.forward(x)
したがって、テンソルに直接モデルを呼び出すことができた.
output = model(x)
# model.__call__(x) 
#   -> model.forward(x)

DataSetにおけるマジックメソッド


PyTorchでは、Dataset 我々のトレーニングとテストデータセットを準備するクラス.あなたはなぜ我々のようなあいまいな名前でメソッドを定義疑問に思ったことがありますか__len__ and __getitem__ それで?
from torch.utils.data import Dataset

class Numbers(Dataset):
    def __init__(self, x, y):
        self.data = x
        self.labels = y

    def __len__(self):
        return len(self.data)

    def __getitem__(self, i):
        return (self.data[i], self.labels[i])

>>> dataset = Numbers([1, 2, 3], [0, 1, 0])
>>> print(len(dataset))
3
>>> print(dataset[0])
(1, 0)
これらのメソッドはPythonのマジックメソッドです.リストとタプルのようなiterableの長さをどのように使うかを知っていますlen 関数.
>>> x = [10, 20, 30]
>>> len(x)
3
Pythonでは__len__ カスタムクラスでlen() それに取り組む.例えば、
class Data(object):
    def __len__(self):
        return 10

>>> d = Data()
>>> len(d)
10
同様に、インデックス表記を使用してリストとタプルの要素にアクセスする方法を知っています.
>>> x = [10, 20, 30]
>>> x[0]
10
Pythonでは__getitem__ カスタムクラスの機能を許可するMagicメソッド.例えば、
class Data(object):
    def __init__(self):
        self.x = [10, 20, 30]

    def __getitem__(self, i):
        return x[i]

>>> d = Data()
>>> d[0]
10
上記の概念を使用すると、現在MNISTのような組み込みデータセットを理解することができます.
from torchvision.datasets import MNIST

>>> trainset = MNIST(root='mnist', download=True, train=True)
>>> print(len(trainset))
60000
>>> print(trainset[0])
(<PIL.Image.Image image mode=L size=28x28 at 0x7F06DC654128>, 0)

DataLoaderのマジックメソッド


MNIST数字のトレーニングデータセットのDataLoaderを作成しましょう.
from torchvision.datasets import MNIST
from torch.utils.data import DataLoader
import torchvision.transforms as transforms

trainset = MNIST(root='mnist', 
                 download=True, 
                 train=True, 
                 transform=transforms.ToTensor())
trainloader = DataLoader(trainset, batch_size=32, shuffle=True)
さて、データローダから直接最初のバッチにアクセスしようとしましょう.indexを使ってアクセスしようとすると、例外を取得します.
>>> trainloader[0]
TypeError: 'DataLoader' object does not support indexing
こんなふうにしてやったかもしれない.
images, labels =  next(iter(trainloader))
あなたはなぜ私たちはiter() それから呼び出しnext() ? これを説明しましょう.
リストを考えるx 3要素で.Pythonでは、x 使用iter 関数.
x = [1, 2, 3]
y = iter(x)
イテレータは、1つの要素だけが一度にメモリにロードされるように怠惰な読み込みを許可するために使用されます.
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
StopIteration:
我々は各要素を取得し、我々はリストの最後に到達すると、我々は得るStopIteration 例外.
このパターンは、通常の機械学習のワークフローにマッチし、メモリ内の一度にデータの小さなバッチを取り、順方向と逆方向のパスを行います.それでDataLoader また、このパターンをpytorchに組み込む.
Pythonでクラスからイテレータを作成するには、magicメソッドを定義する必要があります__iter__ and __next__
class ExampleLoader(object):
    def __init__(self, data):
        self.data = iter(data)

    def __iter__(self):
        return self

    def __next__(self):
        return next(self.data)        

>>> l = ExampleLoader([1, 2, 3])
>>> next(iter(l))
1
ここでiter() 関数は__iter__ 同じオブジェクトを返すクラスのマジックメソッド.そしてnext() 関数は__next__ 次の要素を返すクラスのマジックメソッド.
PyTorchでは、DataRowerの実装でこのパターンを次のように実装します.
class DataLoader(object):
    def __iter__(self):
        if self.num_workers == 0:
            return _SingleProcessDataLoaderIter(self)
        else:
            return _MultiProcessingDataLoaderIter(self)

class _SingleProcessDataLoaderIter(_BaseDataLoaderIter):
    def __next__(self):
        # logic to return batch from whole data
        ...
したがって、イテレータ作成部と実際のデータローディング部を分離します.
  • あなたが電話するときiter() データローダでは、複数の労働者を使用しているかどうかチェックします
  • それに基づいて、それは単一または複数の労働者のために別のイテレータクラスを返します
  • >>> type(iter(trainloader))
    torch.utils.data.dataloader._SingleProcessDataLoaderIter
    
  • このイテレータクラスは__next__ setの実際のデータを返すメソッドbatch_size 我々が呼ぶときnext() それに
  • images, labels =  next(iter(trainloader))
    
    # equivalent to:
    images, labels = trainloader.__iter__().__next__()
    
    したがって、1つのバッチの画像とラベルを取得します.

    結論


    このように、我々はPytorchがAPI設計の中でパイソン自身からいくつかの先進概念を借りる方法を見ました.私は、記事がこれらの概念が場面の後でどのように働くかをdeystifyするのに役立つと思っていて、より良いPytorchユーザーになるのを援助します.

    接続


    あなたがこのブログ記事を楽しんだならば、私が毎週新しいブログ記事を共有するところで、私と連絡をとってください.