愛因斯坦求和

Numpy庫、pytorch庫和tensorflow庫中,存在一個非常通用但鮮為人知的函數(shù),稱為einsum(),它根據(jù)愛因斯坦的求和約定執(zhí)行求和。PyTorch和TensorFlow像numpy支持einsum的好處之一是einsum可以用于神經(jīng)網(wǎng)絡(luò)架構(gòu)的任意計算圖,并且可以反向傳播。這是一個高效的符號計算,用于矩陣的各種求和操作, 在本教程文章中,我們揭開了einsum()的神秘面紗。

學(xué)習(xí)它的目的

愛因斯坦求和提供了一種緊湊而優(yōu)雅的方式來指定幾乎任何標(biāo)量/矢量/矩陣/張量的求和操作。 非常普遍,又減少計算機科學(xué)家所犯錯誤的數(shù)量,并減少他們花在推理線性代數(shù)上的時間。 通過同時更清晰,更明確,更自我表示,更具說明性和更少認(rèn)知負(fù)擔(dān)來實現(xiàn)。 它比矩陣乘法這樣的優(yōu)點在于它讓用戶不必考慮:

  • 提供參數(shù)張量的正確順序

  • 適用于參數(shù)張量的正確換位

  • 確保正確的張量尺寸相互排列

  • 正確的換位應(yīng)用于結(jié)果張量
    愛因斯坦求和確實以著名的物理學(xué)家和理論家阿爾伯特愛因斯坦的名字命名。 但是,愛因斯坦沒有參與其發(fā)展。 他只是通過表達(dá)式的來推廣它。 在給Tullio Levi-Civita的一封信中,共同開發(fā)人員與Ricci演算的Gregorio Ricci-Curbastro一起(其求和符號只是其中的一部分),愛因斯坦寫道:

    I admire the elegance of your method of computation; it must be nice to ride through these fields upon the horse of true mathematics while the like of us have to make our way laboriously on foot.

愛因斯坦本人也高度贊揚這個符號求值
NB: As a further aside, the most general formulation of Einstein summation involves topics such as covariance and contravariance, indicated by subscript and superscript indices respectively. For our purposes, we will ignore co-/contravariance, since we can and will choose the “basis” we operate in to make the complexities that they introduce disappear.

einsum工作原理

一旦掌握愛因斯坦求和 公式你會非常方便使用。

導(dǎo)入庫

import tensorflow as tf
import numpy as np
import torch

它使用格式字符串和任意數(shù)量的參數(shù)張量,并返回結(jié)果張量。


使用

格式化字符串語法:

  • 逗號表示分隔參數(shù),參數(shù)規(guī)范的數(shù)量和參數(shù)需要匹配
  • 結(jié)果和參數(shù)的分割使用箭頭,箭頭是必須有的
  • 參數(shù)和結(jié)果張量的規(guī)范是一系列(字母,ASCII)字符
  • 張量規(guī)格中的字符數(shù)正好等于此張量的維數(shù)。
語法
語法

示例如下:

v = np.arange(100)
M = np.arange(16).reshape(4,4)
A = np.arange(25).reshape(5,5)
B = np.arange(20).reshape(5,4)
s = np.einsum('a->', v)
T = np.einsum('ij->ji', M)
C = np.einsum('mn,np->mp', A,B)

assert v.ndim == len('a')
assert s.ndim == len('')
assert M.ndim == len('ij')
assert T.ndim == len('ji')
assert A.ndim == len('mn')
assert B.ndim == len('np')
assert C.ndim == len('mp')

工作機理

內(nèi)部工作
在愛因斯坦求和Numpy.einsum()中,用一個字母標(biāo)記每個張量的每個軸,該字母表示在該軸上迭代時將使用的索引。 然后,einsum()很容易表達(dá)為一組深層嵌套的for循環(huán)。 這些for循環(huán)的核心是參數(shù)乘積的總和。例子如下

矩陣轉(zhuǎn)置

import torch
a = torch.arange(24).reshape(4, 6)
torch.einsum('ij->ji', [a])
tensor([[ 0,  6, 12, 18],
        [ 1,  7, 13, 19],
        [ 2,  8, 14, 20],
        [ 3,  9, 15, 21],
        [ 4, 10, 16, 22],
        [ 5, 11, 17, 23]])

矩陣求和

a = torch.arange(6).reshape(2, 3)
torch.einsum('ij->', [a])
tensor(15)

矩陣列求和

a = torch.arange(6).reshape(2, 3)
torch.einsum('ij->j', [a])
tensor([3, 5, 7])

矩陣行求和

a = torch.arange(6).reshape(2, 3)
b = torch.arange(3)
torch.einsum('ik,k->i', [a, b])
tensor([ 5, 14])

點乘

點乘
a = torch.arange(6).reshape(2, 3)
b = torch.arange(15).reshape(3, 5)
torch.einsum('ik,kj->ij', [a, b])
tensor([[ 25,  28,  31,  34,  37],
        [ 70,  82,  94, 106, 118]])

2D矩陣抽取

2D矩陣跡抽取
a = torch.arange(9).reshape(3, 3)
torch.einsum('ii->i', a)
tensor([0, 4, 8])

2D矩陣跡

2D矩陣跡
a = torch.arange(9).reshape(3, 3)
torch.einsum('ii->', a)
tensor(12)

二次形式

二次形式

batch矩陣相乘

批外積

image.png
a = torch.randn(3,2,5)
b = torch.randn(3,5,3)
torch.einsum('ijk,ikl->ijl', [a, b])

點積

a = torch.arange(3)
b = torch.arange(3,6)  # [3, 4, 5]
torch.einsum('i,i->', [a, b])

一個MPL示例

# 15: MLP Backprop done easily (stochastic version).
#     h = sigmoid(Wx + b)
#     y = softmax(Vh + c)
Ni = 784
Nh = 500
No =  10
 
W  = np.random.normal(size = (Nh,Ni))  # Nh x Ni
b  = np.random.normal(size = (Nh,))    # Nh
V  = np.random.normal(size = (No,Nh))  # No x Nh
c  = np.random.normal(size = (No,))    # No
 
# Load x and t...
x, t  = train_set[k]
 
# With a judicious, consistent choice of index labels, we can
# express fprop() and bprop() extremely tersely; No thought
# needs to be given about the details of shoehorning matrices
# into np.dot(), such as the exact argument order and the
# required transpositions.
#
# Let
#
#     'i' be the input  dimension label.
#     'h' be the hidden dimension label.
#     'o' be the output dimension label.
#
# Then
 
# Fprop
ha    = np.einsum("hi, i -> h", W, x) + b
h     = sigmoid(ha)
ya    = np.einsum("oh, h -> o", V, h) + c
y     = softmax(ya)
 
# Bprop
dLdya = y - t
dLdV  = np.einsum("h , o -> oh", h, dLdya)
dLdc  = dLdya
dLdh  = np.einsum("oh, o -> h ", V, dLdya)
dLdha = dLdh * sigmoidgrad(ha)
dLdW  = np.einsum("i,  h -> hi", x, dLdha)
dLdb  = dLdha

TreeQN

我曾經(jīng)在實現(xiàn)TreeQN( arXiv:1710.11417)的等式6時使用了einsum:給定網(wǎng)絡(luò)層l上的低維狀態(tài)表示zl,和激活a上的轉(zhuǎn)換函數(shù)Wa,我們想要計算殘差鏈接的下一層狀態(tài)表示。

TreeQN

在實踐中,我們想要高效地計算大小為B的batch中的K維狀態(tài)表示Z ∈ ?B × K,并同時計算所有轉(zhuǎn)換函數(shù)(即,所有激活A(yù))。我們可以將這些轉(zhuǎn)換函數(shù)安排為一個張量W ∈ ?A × K × K,并使用einsum高效地計算下一層狀態(tài)表示。

import torch.nn.functional as F

def random_tensors(shape, num=1, requires_grad=False):
  tensors = [torch.randn(shape, requires_grad=requires_grad) for i in range(0, num)]
  return tensors[0] if num == 1 else tensors

# 參數(shù)
# -- [激活數(shù) x 隱藏層維度]
b = random_tensors([5, 3], requires_grad=True)
# -- [激活數(shù) x 隱藏層維度 x 隱藏層維度]
W = random_tensors([5, 3, 3], requires_grad=True)

def transition(zl):
    # -- [batch大小 x 激活數(shù) x 隱藏層維度]
    return zl.unsqueeze(1) + F.tanh(torch.einsum("bk,aki->bai", [zl, W]) + b)

# 隨機取樣仿造輸入
# -- [batch大小 x 隱藏層維度]
zl = random_tensors([2, 3])

transition(zl)

注意力

注意力
# 參數(shù)
# -- [隱藏層維度]
bM, br, w = random_tensors([7], num=3, requires_grad=True)
# -- [隱藏層維度 x 隱藏層維度]
WY, Wh, Wr, Wt = random_tensors([7, 7], num=4, requires_grad=True)

# 注意力機制的單次應(yīng)用
def attention(Y, ht, rt1):
    # -- [batch大小 x 隱藏層維度]
    tmp = torch.einsum("ik,kl->il", [ht, Wh]) + torch.einsum("ik,kl->il", [rt1, Wr])
    Mt = F.tanh(torch.einsum("ijk,kl->ijl", [Y, WY]) + tmp.unsqueeze(1).expand_as(Y) + bM)
    # -- [batch大小 x 序列長度]
    at = F.softmax(torch.einsum("ijk,k->ij", [Mt, w]))
    # -- [batch大小 x 隱藏層維度]
    rt = torch.einsum("ijk,ij->ik", [Y, at]) + F.tanh(torch.einsum("ij,jk->ik", [rt1, Wt]) + br)
    # -- [batch大小 x 隱藏層維度], [batch大小 x 序列維度]
    return rt, at

# 取樣仿造輸入
# -- [batch大小 x 序列長度 x 隱藏層維度]
Y = random_tensors([3, 5, 7])
# -- [batch大小 x 隱藏層維度]
ht, rt1 = random_tensors([3, 7], num=2)

rt, at = attention(Y, ht, rt1)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 撰文 | Michel Janssen & Jürgen Renn 翻譯 | 張林峰 校對 | 王玫珺 陳曉雪 張...
    知識分子閱讀 2,363評論 4 8
  • 引言 The problem of clustering has been studied widely in t...
    matrices閱讀 773評論 0 0
  • 今天在讀者上閱讀到一篇文章,很是感慨,作者寫到一句話,尤是感悟,這句話是“人生的窮,是人生窮短”,如今很多人都可能...
    低溫奶閱讀 268評論 0 1
  • 步行就是為享受步行而行的, 全不在乎是否能到達(dá)目的地, 不呈現(xiàn)些微的緊張和不耐煩, 或希望盡快到達(dá)目的地。 步伐是...
    靈兒Queenie閱讀 356評論 0 1
  • 2018-6-1 親愛的兒子,今天是你小學(xué)生涯的最后一個六一兒童節(jié),看著蹭蹭上長的你,希望你長大,又不希望你那么快...
    美麗心情_a8bf閱讀 116評論 0 2

友情鏈接更多精彩內(nèi)容