PyTorch中Linear層的原理 | PyTorch系列(十六)

點擊上方AI算法與圖像處理”,選擇加"星標"或“置頂”

重磅乾貨,第一時間送達

文 |AI_study

原標題:PyTorch Callable Neural Networks - Deep earning In Python

Linear 層是如何實現的

在上一篇文章中,我們學習了 Linear 層如何使用矩陣乘法將它們的輸入特徵轉換爲輸出特徵。

當輸入特徵被一個Linear 層接收時,它們以一個 展平成一維張量的形式接收,然後乘以權重矩陣。這個矩陣乘法產生輸出特徵。

讓我們看看代碼中的一個例子。

一、使用矩陣進行變換

in_features = torch.tensor([1,2,3,4], dtype=torch.float32)


weight_matrix = torch.tensor([
    [1,2,3,4],
    [2,3,4,5],
    [3,4,5,6]
], dtype=torch.float32)


> weight_matrix.matmul(in_features)
tensor([30., 40., 50.])

在這裏,我們創建了一個一維張量,叫做in_features。我們還創建了一個權重矩陣當然是一個二維張量。然後,我們使用matmul()函數來執行生成一維張量的矩陣乘法運算。

一般來說,權重矩陣定義了一個線性函數,它把一個有四個元素的一維張量映射成一個有三個元素的一維張量。

這也是Linear 層的工作原理。它們使用權重矩陣將一個in_feature空間映射到一個out_feature空間。

二、使用PyTorch線性層進行轉換

讓我們看看如何創建一個PyTorch的 Linear 層來完成相同的操作。

fc = nn.Linear(in_features=4, out_features=3, bias=False)

這裏,我們有了。我們已經定義了一個線性層,它接受4個輸入特徵並把它們轉換成3個輸出特徵,所以我們從4維空間轉換到3維空間。我們知道需要一個權重矩陣被用執行這個操作,但是在這個例子中權重矩陣在哪裏呢?

我們將權重矩陣放在PyTorch LinearLayer類中,是由PyTorch創建。PyTorch LinearLayer類使用傳遞給構造函數的數字4和3來創建一個3 x 4的權重矩陣。讓我們通過查看PyTorch源代碼來驗證這一點。

# torch/nn/modules/linear.py (version 1.0.1)


def __init__(self, in_features, out_features, bias=True):
    super(Linear, self).__init__()
    self.in_features = in_features
    self.out_features = out_features
    self.weight = Parameter(torch.Tensor(out_features, in_features))
    if bias:
        self.bias = Parameter(torch.Tensor(out_features))
    else:
        self.register_parameter('bias', None)
    self.reset_parameters()

正如我們所看到的,當我們用一個3×4矩陣乘以一個4×1矩陣時,結果是一個3×1矩陣。這就是PyTorch以這種方式構建權重矩陣的原因。這些是矩陣乘法的線性代數規則。

我們來看看如何通過傳遞in_features張量來調用我們的層。

> fc(in_features)
tensor([-0.8877,  1.4250,  0.8370], grad_fn=<SqueezeBackward3>)

我們可以這樣調用對象實例,因爲PyTorch神經網絡模塊是可調用的Python對象。稍後我們將更詳細地研究這個重要的細節,但是首先,檢查這個輸出。我們確實得到了一個包含三個元素的一維張量。然而,產生了不同的值。

 callable Python objects. :https://en.wikipedia.org/wiki/Callable_object

這是因爲PyTorch創建了一個權值矩陣,並使用隨機值初始化它。這意味着這兩個例子中的線性函數是不同的,所以我們使用不同的函數來產生這些輸出。

記住權矩陣中的值定義了線性函數。這演示了在訓練過程中,隨着權重的更新,網絡的映射是如何變化的。

讓我們顯式地將線性層的權值矩陣設置爲與我們在另一個示例中使用的權值矩陣相同。

fc.weight = nn.Parameter(weight_matrix)

PyTorch模塊的權值需要是參數。這就是爲什麼我們把權矩陣張量放在一個參數類實例中。現在讓我們看看這一層如何使用新的權重矩陣轉換輸入。我們希望看到與前面示例相同的結果。

> fc(in_features)
tensor([30.0261, 40.1404, 49.7643], grad_fn=<AddBackward0>)

這一次我們更接近於30、40和50的值。然而,我們確切。這是爲什麼呢?這是不精確的因爲線性層在輸出中加入了一個偏置張量。觀察當我們關閉偏差時會發生什麼。我們通過向構造函數傳遞一個假標記來做到這一點。

fc = nn.Linear(in_features=4, out_features=3, bias=False)
fc.weight = nn.Parameter(weight_matrix)
> fc(in_features)
tensor([30., 40., 50.], grad_fn=<SqueezeBackward3>)

現在,我們有一個精確的匹配。這就是Linear 層的工作原理。

三、線性變換的數學符號

有時我們會看到 Linear 層操作被稱爲

y=Ax+b.

在這個方程中,我們有:

我們會注意到這與直線方程相似

y=mx+b.

可調用的層和神經網絡

我們之前指出過,我們把層對象實例當作一個函數來調用是多麼奇怪。

> fc(in_features)
tensor([30.0261, 40.1404, 49.7643], grad_fn=<AddBackward0>)

使這成爲可能的是PyTorch模塊類實現了另一個特殊的Python函數,稱爲__call__()。如果一個類實現了__call__()方法,那麼只要對象實例被調用,這個特殊的調用方法就會被調用。

這個事實是一個重要的PyTorch概念,因爲在我們的層和網絡中,__call __()與forward()方法交互的方式是用的。

我們不直接調用forward()方法,而是調用對象實例。在對象實例被調用之後,在底層調用了__ call __方法,然後調用了forward()方法。這適用於所有的PyTorch神經網絡模塊,即網絡和層。

讓我們在PyTorch源代碼中看看這一點。

# torch/nn/modules/module.py (version 1.0.1)


def __call__(self, *input, **kwargs):
    for hook in self._forward_pre_hooks.values():
        hook(self, input)
    if torch._C._get_tracing_state():
        result = self._slow_forward(*input, **kwargs)
    else:
        result = self.forward(*input, **kwargs)
    for hook in self._forward_hooks.values():
        hook_result = hook(self, input, result)
        if hook_result is not None:
            raise RuntimeError(
                "forward hooks should never return any values, but '{}'"
                "didn't return None".format(hook))
    if len(self._backward_hooks) > 0:
        var = result
        while not isinstance(var, torch.Tensor):
            if isinstance(var, dict):
                var = next((v for v in var.values() if isinstance(v, torch.Tensor)))
            else:
                var = var[0]
        grad_fn = var.grad_fn
        if grad_fn is not None:
            for hook in self._backward_hooks.values():
                wrapper = functools.partial(hook, self)
                functools.update_wrapper(wrapper, hook)
                grad_fn.register_hook(wrapper)
    return result

PyTorch在__ call __()方法中運行的額外代碼就是我們從不直接調用forward()方法的原因。如果我們這樣做,額外的PyTorch代碼將不會被執行。因此,每當我們想要調用forward()方法時,我們都會調用對象實例。這既適用於層,也適用於網絡,因爲它們都是PyTorch神經網絡模塊。

現在可以實現網絡的forward()方法了。

文章中內容都是經過仔細研究的,本人水平有限,翻譯無法做到完美,但是真的是費了很大功夫,希望小夥伴能動動你性感的小手,分享朋友圈或點個“在看”,支持一下我 ^_^

英文原文鏈接是:

https://deeplizard.com/learn/video/rcc86nXKwkw

加羣交流

歡迎小夥伴加羣交流,目前已有交流羣的方向包括:AI學習交流羣,目標檢測,秋招互助,資料下載等等;加羣可掃描並回復感興趣方向即可(註明:地區+學校/企業+研究方向+暱稱)

 謝謝你看到這裏! ????

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章