日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tensor判断是否相等_PyTorch的Tensor(中)

發布時間:2023/12/10 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tensor判断是否相等_PyTorch的Tensor(中) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景

在PyTorch的Tensor系列上一篇文章中:

Gemfield:PyTorch的Tensor(上)?zhuanlan.zhihu.com

Gemfield介紹了一個Tensor的創建過程,特別是在創建一個Tensor的時候,調用棧從Python到C++再回到Python的過程。與此同時,在內存中對應的是一個Variable實例的創建(嚴格來說,Variable實例的某個field也是Variable實例)。

在本文,Gemfield將介紹PyTorch的Tensor中autograd相關的部分。autograd是PyTorch之所以是神經網絡框架的一個重要原因。autograd機制提供了對Tensor上所有操作自動求微分的功能。我們知道,對于一個Variable來說,它的唯一數據成員就是impl_,這個impl_成員是TensorImpl 類型,在初始化階段impl_會被實例化為一個Variable::Impl的實例(TensorImpl的子類):

Variable --> impl_ = Variable::Impl實例

對于一個Variable的autograd來說,autograd的部分就體現在impl_的autograd_meta_成員上。在初始化階段,autograd_meta_會被初始化為一個Variable::AutogradMeta的實例(AutogradMetaInterface的子類),或者會被初始化為一個Variable::DifferentiableViewMeta的實例(Variable::AutogradMeta的子類),然后通過Variable的 get_autograd_meta()來訪問。實際上,autograd_meta_正是一個Variable是普通tensor還是帶autograd功能的tensor的唯一標識:

#1 Variable是個Tensor,沒有requires_grad Variable --> impl_ --> autograd_meta_ = None#2 Variable --> impl_ --> autograd_meta_ = Variable::AutogradMeta實例#3 Variable --> impl_ --> autograd_meta_ = Variable::DifferentiableViewMeta實例

而一個Variable::AutogradMeta實例有如下成員,這些成員正是PyTorch autograd系統的中堅:

# Variable::AutogradMeta 和 Variable::DifferentiableViewMeta Variable grad_; std::shared_ptr<Function> grad_fn_; std::weak_ptr<Function> grad_accumulator_; VariableVersion version_counter_; std::vector<std::shared_ptr<FunctionPreHook>> hooks_; bool requires_grad_; bool is_view_; uint32_t output_nr_;# 僅 Variable::DifferentiableViewMeta Variable base_; uint32_t attr_version;
  • 1,grad_是另外一個Variable,存儲當前Variable實例的梯度;
  • 2,grad_fn是個Function的實例,非leaf variables才有。通過Variable的grad_fn()來訪問,實際上,PyTorch中就是通過是否grad_fn_ == nullptr來判斷一個Variable是否是leaf variable的;
  • 3,grad_accumulator_是個Function的實例,只有leaf variables才有。通過Variable的grad_accumulator()來訪問;
  • 4,version_counter_里有個version number;
  • 5,hooks_可以是一組;
  • 6,requires_grad_ 是個flag,表明此Variable實例是否需要grad;
  • 7,is_view_是個flag,表明此Variable實例是否是個view(沒有實際存儲,基于base的variable);
  • 8,output_nr_是個數字;
  • 9,base_是view的base variable;
  • 10,attr_version是個數字。

我們通過下面這一小段代碼來演示下這個能力:

gemfield = torch.ones(2, 2, requires_grad=True) syszux = gemfield + 2 civilnet = syszux * syszux * 3 gemfieldout = civilnet.mean() gemfieldout.backward()

特別的,對于在python會話中的每一步操作,gemfield都將映射到內存上類實例中的成員/結構體的變化。

Tensor創建:gemfield = torch.ones(2, 2, requires_grad=True)

我們使用gemfield = torch.ones(2, 2, requires_grad=True) 語句來創建了一個tensor。在https://zhuanlan.zhihu.com/p/54896021一文中已經介紹過了,這個調用會在內存中產生如下一個Variable實例:

#gemfieldVariable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [[1., 1.],[1., 1.]]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= None--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = True--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist

這個gemfield變量就是圖中的leaf,為什么呢?因為這是用戶直接創建的(不是經過計算得到的),位于圖中最“底端/外側”的位置,沒有子節點。這個時候,Tensor gemfield的grad是None,grad_fn是None。output_nr_為0,表明這個Variable是function的第1個輸出。

Tensor的簡單加法:syszux = gemfield + 2

我們使用 syszux = gemfield + 2 來得到一個新的Tensor,名字為syszux。這個加法嘛,在初始化的時候已經和C++中的THPVariable_add函數綁定上,并注冊到Python的torch._C._TensorBase符號上了:

PyMethodDef variable_methods[] = {{"__add__", (PyCFunction)THPVariable_add, METH_VARARGS | METH_KEYWORDS, NULL},......

而THPVariable_add的定義如下:

static PyObject * THPVariable_add(PyObject* self_, PyObject* args, PyObject* kwargs) {......return wrap(dispatch_add(self, r.tensor(0), r.scalar(1))); }

1,scalar to tensor

在這個函數中,首先要將syszux = gemfield + 2 中的2從標量轉換為tensor,這個轉換邏輯如下:

auto tensor = scalar_to_tensor(scalar); tensor.unsafeGetTensorImpl()->set_wrapped_number(true); return autograd::make_variable(tensor);

現在scalar 2已經變成了內存中一個Variable的實例,在add真正執行之前,在內存中已經有2個Variable實例了,分別是gemfield和2:

#gemfield Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [[1., 1.],[1., 1.]]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= None--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = True--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist#scalar 2 Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [2.]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= None--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = False--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist

不過要注意了,由于gemfield是一個leaf variable,因此在后文的加法運算中,gemfield會被觸發Variable::grad_accumulator()調用,這會初始化gemfield的grad_accumulator_成員,因此在那之后,gemfield在內存中的樣子就會變為:

#gemfield Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [[1., 1.],[1., 1.]]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= None--> grad_accumulator_ (Function實例)= AccumulateGrad實例0x55ca7f304500--> ......

2,dispatch to add

既然是分發,肯定是為了分發到對應種類的Type上。分發的核心邏輯是:

dispatch_type().add(*this, other, alpha)

dispatch_type是at::Tensor類上的一個方法,根據其impl(TensorImpl類)的id、dtype等信息,推導出Type類型(VariableType)。在這個例子上,根據輸入參數的類型,最終分發到torch/csrc/autograd/generated/VariableType.cpp(參考autograd的代碼生成)中的VariableType的add方法上:

Tensor VariableType::add(const Tensor & self, const Tensor & other, Scalar alpha) const {std::shared_ptr<AddBackward0> grad_fn;grad_fn = std::shared_ptr<AddBackward0>(new AddBackward0(), deleteFunction);grad_fn->set_next_edges(collect_next_edges( self, other ));grad_fn->alpha = alpha;auto tmp = ([&]() {at::AutoNonVariableTypeMode non_var_type_mode(true);return baseType->add(self_, other_, alpha);})();auto result = as_variable(tmp);set_history(flatten_tensor_args( result ), grad_fn);return result; }

匿名函數中的baseType->add最終又調用了如下的調用棧,只能說一個簡單的加法在graph設計中也會變得比較復雜:

VariableType::add | V baseType->add(self_, other_, alpha) | V #ATen/TypeDefault.cpp TypeDefault::add | V #aten/src/ATen/native/BinaryOps.cpp add(const Tensor& self, const Tensor& other, Scalar alpha) | V #此處依賴初始化階段的REGISTER_DISPATCH的工作 add_stub | V #aten/src/ATen/native/cpu/BinaryOpsKernel.cpp.AVX2.cpp add_kernel | V #aten/src/ATen/native/cpu/BinaryOpsKernel.cpp.AVX2.cpp binary_kernel_vec | V binary_loop

3,構建autograd

3.1 構建grad_fn (AddBackward0)

AddBackward0是加法運算的反向傳播算法,構建這個grad_fn實例是通過如下一行代碼完成的:

std::shared_ptr<AddBackward0> grad_fn = std::shared_ptr<AddBackward0>(new AddBackward0(), deleteFunction);

可以看到,對于加法,對應的grad_fn是AddBackward0。在聊AddBackward0之前,你有必要先了解下PyTorch中的Function繼承體系(可以參考 https://zhuanlan.zhihu.com/p/61765561 ),還記得吧:

class AddBackward0 : public TraceableFunction class TraceableFunction : public Function

gemfield + 2 加法運算完成后,就會創建出來這個AddBackward0(一個Function)實例,并且使用collect_next_edges()搜集gemfield和2的grad_fn或者grad_accumulator。gemfield是leaf variable,因此搜集的就是grad_accumulator(2啥都沒有),類型是AccumulateGrad(1個Function的實例),然后再向這個AddBackward0實例上注冊:

grad_fn->set_next_edges(collect_next_edges( self, other ));

next edges就是self和other的gradient_edge():gradient_edge()函數返回的是Edge實例(通過Variable的grad_fn_構建)。當set_next_edges調用完成后,一個Function的next_edges_成員(類型為std::vector<Edge>)就會被初始化。

1,如果一個Variable是內部創建的(通過運算得到,比如syszux變量),那么grad_fn_就是這個Variable的gradient function;

2,如果一個Variable是用戶創建的(比如gemfield變量),則grad_fn_就是這個Variable的gradient accumulator,也就是一個AccumulateGrad類(Function子類)的實例。

但不管怎樣,Variable的grad_fn_成員在這里終歸是要構建成一個Edge實例并以此作為gradient_edge()函數的返回:

Edge gradient_edge() const {if (const auto& gradient = grad_fn()) {return Edge(gradient, output_nr());} else {return Edge(grad_accumulator(), 0);} }

對于leaf Variable來說,grad_fn是null,在這種情況下Variable將使用gradient accumulator, 用來累加輸出給這個Variable的梯度。注意只有當Variable的requires_grad = True時才有gradient accumulators。經過構建后的AddBackward0的內存布局如下所示:

#grad_fn AddBackward0, requires_grad == True Function實例 --> sequence_nr_ (uint64_t) = 0--> next_edges_ (edge_list) --> std::vector<Edge> = [(AccumulateGrad實例0x55ca7f304500, 0),(0, 0)]--> input_metadata_ --> [(type, shape, device)...] = None--> alpha (Scalar) = 1--> pre_hooks_ = None--> post_hooks_ = None--> anomaly_metadata_ = None--> apply() --> 使用 AddBackward0 的apply#grad_fn,requires_grad == False Function實例 --> None

3.2 構建grad_accumulator_ (AccumulateGrad)

前文已經提到過,在grad_fn調用collect_next_edges去搜集輸入參數(Variable實例)的edges時,對于leaf gemfield來說,會觸發Variable::grad_accumulator()調用,在一個Variable第一次調用這個API的時候,會去初始化它的grad_accumulator_成員:

result = std::make_shared<AccumulateGrad>(Variable(std::move(intrusive_from_this))); autograd_meta->grad_accumulator_ = result;

這會new一個AccumulateGrad對象,使用UINT64_MAX(也就是18446744073709551615)來初始化Function的sequence_nr_成員。構建完成后,grad_accumulator_(0x55ca7f304500)在內存中看起來是這個樣子的:

# 類AccumulateGrad,繼承自Function,是一個Function實例 Function實例 --> sequence_nr_ (uint64_t) = UINT64_MAX--> next_edges_ (edge_list) --> None--> input_metadata_ --> [(type, shape, device)...] = [(CPUFloatType, [2, 2], cpu)]--> pre_hooks_ = None--> post_hooks_ = None--> anomaly_metadata_ = None--> apply() --> 使用 AccumulateGrad 的apply--> variable = gemfield

初始化完畢后,此時gemfield這個Variable的grad_accumulator_已經被賦值為AccumulateGrad實例(0x55ca7f304500)。

4 構建Variable(syszux)

在加法表達式完成之后,內存中也就產生了syszux。剛產生的syszux實例在內存中看起來是這樣的:

//syszux Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [[3., 3.],[3., 3.]]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= None--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = False--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist

之后會使用set_history(flatten_tensor_args( result ), grad_fn)來設置syszux的gradient_edge,這個函數做了2件事情:

1,AddBackward0實例中的input_metadata_ 追加了Variable syszux的(type, shape, device),追加完成后,syszux信息在input_metadata_中的index就是下文中會用到的output_nr_,此處為0(第一個元素嘛);這樣AddBackward0實例在內存中看起來是這樣:

//grad_fn->add_input_metadata(variable); grad_fn_ --> input_metadata_ += (variable.type, variable.shape, variable.device) = []#AddBackward0實例, requires_grad == True Function實例 --> sequence_nr_ (uint64_t) = 0--> next_edges_ (edge_list) --> std::vector<Edge> = [(AccumulateGrad實例, 0),(0, 0)]--> input_metadata_ --> [(type, shape, device)...] = [(CPUFloatType, [2, 2],cpu])]--> alpha (Scalar) = 1--> pre_hooks_ = None--> post_hooks_ = None--> anomaly_metadata_ = None--> apply() --> 使用 AddBackward0 的apply

2,syszux的autograd_meta中的grad_fn_ 被賦值成了上述AddBackward0實例,而autograd_meta中的output_nr_被賦值成了上文中的“當前Variable信息在input_metadata_中的index”。這樣syszux實例在內存中看起來就是這樣:

//as_variable_ref(variable).set_gradient_edge({grad_fn, output_nr}); Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [[3., 3.],[3., 3.]]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= AddBackward0實例0x55ca7f872e90(參考上面)--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = False--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist

完成這一步后,如果你在python會話中打印syszux的grad_fn屬性,就會調用THPVariable_get_grad_fn函數,接著調用Variable的grad_fn()函數拿到grad_fn_,然后使用 functionToPyObject()函數從cpp_function_types表中查找對應的Python表示:

>>> syszux.grad_fn <AddBackward object at 0x7f1550f2e0d0>

civilnet = syszux * syszux * 3

現在進入civilnet = syszux * syszux * 3這個表達式的解析了,這個表達式執行期間,就會產生兩次python object的__mul__(乘法)調用,并且會new一個新的Variable civilnet出來。

1,Python到C++

在初始化階段,Variable的python綁定已經完成了下面的初始化:

PyMethodDef variable_methods[] = {{"__add__", (PyCFunction)THPVariable_add, METH_VARARGS | METH_KEYWORDS, NULL},{"__mul__", (PyCFunction)THPVariable_mul, METH_VARARGS | METH_KEYWORDS, NULL},...... }

因此這個表達式會導致對C++函數THPVariable_mul的調用,并且是2次:兩次乘法。

2,dispatch to mul

和加法類似,這個乘法的調用棧如下所示(其中的一次):

#torch/csrc/autograd/generated/python_variable_methods.cpp THPVariable_mul | V #torch/csrc/autograd/generated/python_variable_methods_dispatch.h dispatch_mul | V #aten/src/ATen/core/TensorMethods.h Tensor::mul | V #需要dispatch type 了 #torch/csrc/autograd/generated/VariableType_4.cpp Tensor VariableType::mul(const Tensor & self, const Tensor & other) | V #運算符太簡單了,扔回到base的default實現 #build/aten/src/ATen/TypeDefault.cpp Tensor TypeDefault::mul(const Tensor & self, const Tensor & other) | V #aten/src/ATen/native/BinaryOps.cpp Tensor mul(const Tensor& self, const Tensor& other) | V #aten/src/ATen/native/TensorIterator.cpp TensorIterator::binary_op | V #此處依賴初始化階段的REGISTER_DISPATCH的工作 #build/aten/src/ATen/native/cpu/BinaryOpsKernel.cpp.AVX2.cpp(如果使用的是CPU版的pytorch,并且cpu支持AVX2) void mul_kernel(TensorIterator& iter) | V #aten/src/ATen/native/cpu/Loops.h void binary_kernel_vec(TensorIterator& iter, func_t op, vec_func_t vop)

如果使用的是CPU版的PyTorch,那么這里的乘法運算最終分發到cpu的native實現上了。

3,構建Autograd信息

當乘法運算分發到VariableType::mul的時候,PyTorch將會在這個函數中構建autograd的信息。

Tensor VariableType::mul(const Tensor & self, const Tensor & other) const {std::shared_ptr<MulBackward0> grad_fn;grad_fn = std::shared_ptr<MulBackward0>(new MulBackward0(), deleteFunction);grad_fn->set_next_edges(collect_next_edges( self, other ));if (grad_fn->should_compute_output(1)) {grad_fn->self_ = SavedVariable(self, false);}if (grad_fn->should_compute_output(0)) {grad_fn->other_ = SavedVariable(other, false);}auto tmp = ([&]() {at::AutoNonVariableTypeMode non_var_type_mode(true);return baseType->mul(self_, other_);})();auto result = as_variable(tmp);set_history(flatten_tensor_args( result ), grad_fn);return result; }

3.1 構建grad_fn (MulBackward0)

經過表達式 grad_fn = std::shared_ptr<MulBackward0>(new MulBackward0(), deleteFunction)后,一個Function的實例就產生了,類型為MulBackward0(0x55ca7ebba2a0)。

struct MulBackward0 : public TraceableFunction {variable_list apply(variable_list&& grads) override;SavedVariable self_;SavedVariable other_; };

和AddBackward0不同的是,MulBackward0還有兩個SavedVariable成員。比如使用syszux初始化SavedVariable self_的時候,進行了以下的拷貝:

#從syszux 拷貝到 SavedVariable self_ variable.output_nr() --> output_nr_ variable.requires_grad() --> requires_grad_ variable.data() --> data_ variable.grad_fn() --> grad_fn_ variable.version_counter() --> version_counter_ version_counter_.current_version() --> saved_version_

在內存中,這個MulBackward0實例的布局如下所示:

#grad_fn MulBackward0, requires_grad == True Function實例 --> sequence_nr_ (uint64_t) = 1 (每個線程內自增)--> next_edges_ (edge_list) --> std::vector<Edge> = None--> input_metadata_ --> [(type, shape, device)...] = None--> self_ (SavedVariable) = syszux的淺拷貝--> other_ (SavedVariable) = syszux的另一個淺拷貝--> pre_hooks_ = None--> post_hooks_ = None--> anomaly_metadata_ = None--> apply() --> 使用 MulBackward0 的apply

3.2,初始化MulBackward0的next_edges_

前文已經提到過,在grad_fn調用collect_next_edges去搜集輸入參數(Variable實例,此處為syszux)的edges時,對于“非leaf”的syszux來說,會觸發Variable::grad_fn()調用,這會得到syszux的grad_fn,也就是AddBackward0實例。使用兩個syszux的grad_fn組成的edges初始化完MulBackward0實例后,MulBackward0在內存中看起來是這個樣子:

#grad_fn MulBackward0, requires_grad == True,0x55ca7ebba2a0 Function實例 --> sequence_nr_ (uint64_t) = 1(每個線程內自增)--> next_edges_ (edge_list) = [(AddBackward0實例0x55ca7f872e90,0),(AddBackward0實例0x55ca7f872e90,0)]--> input_metadata_ --> [(type, shape, device)...] = None--> pre_hooks_ = None--> post_hooks_ = None--> anomaly_metadata_ = None--> apply() --> 使用 AccumulateGrad 的apply

初始化完畢后,此時MulBackward0這個Function(0x55ca7ebba2a0)的next_edges_已經被賦值為syszux中的grad_fn組成的edges。

4 構建Variable(tmp)

在第一次乘法表達式完成之后,內存中也就產生了臨時Variable tmp。剛產生的tmp實例在內存中看起來是這樣的:

//tmp Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [[9., 9.],[9., 9.]]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= None--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = False--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist

之后會使用set_history(flatten_tensor_args( result ), grad_fn)來設置tmp的gradient_edge,這個函數做了2件事情:

1,MulBackward0實例中的input_metadata_ 追加了Variable syszux的(type, shape, device),追加完成后,syszux信息在input_metadata_中的index就是下文中會用到的output_nr_,此處為0(第一個元素嘛);這樣MulBackward0實例在內存中看起來是這樣:

//grad_fn->add_input_metadata(variable); grad_fn_ --> input_metadata_ += (variable.type, variable.shape, variable.device) = []#MulBackward0實例, requires_grad == True Function實例 --> sequence_nr_ (uint64_t) = 1--> next_edges_ (edge_list) = [(AddBackward0實例0x55ca7f872e90,0),(AddBackward0實例0x55ca7f872e90,0)]--> input_metadata_ --> [(type, shape, device)...] = [(CPUFloatType, [2, 2],cpu])]--> alpha (Scalar) = 1--> pre_hooks_ = None--> post_hooks_ = None--> anomaly_metadata_ = None--> apply() --> 使用 MulBackward0 的apply

2,Variable tmp的autograd_meta中的grad_fn_ 被賦值成了上述MulBackward0實例,而autograd_meta中的output_nr_被賦值成了上文中的“當前Variable信息在input_metadata_中的index”。這樣tmp實例在內存中看起來就是這樣:

//as_variable_ref(variable).set_gradient_edge({grad_fn, output_nr}); Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [[9., 9.],[9., 9.]]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= MulBackward0實例0x55ca7ebba2a0(參考上面)--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = False--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist

5,第二次乘法

syszux * syszux后得到的tmp還要繼續進行tmp * 3的運算,經過前面的一次加法(gemfield + 2)和一次乘法(syszux * syszux),我想我們目前已經能總結出規律了。每一次這樣的運算,會經歷以下的步驟:

  • 1,加法/乘法的調用棧,最終派發到某種device的實現上,如果運算輸入是個scalar,進行scalar到Variable的構建;
  • 2,派發到VariableType上時,會順便進行autograd信息的構建;
  • 2.1,構建一個加法/乘法的反向計算函數實例(比如AddBackward0,MulBackward0);
  • 2.2,初始化反向計算函數實例的next_edges_和其它相關成員,next_edges_成員的值來自前向時候的輸入參數,如果輸入Variable是leaf的話,則next_edges_來自輸入Variable的grad_accumulator_;如果是非leaf的話,則來自Variable的grad_fn_;
  • 2.3,使用步驟3中的Variable實例來初始化反向計算函數實例的input_metadata_,
  • 3,運算后得到新的Variable,使用Variable::Impl進行構建,使用步驟2中的反向計算函數實例初始化該Variable實例的grad_fn_成員。

對于civilnet = tmp * 3的運算(civilnet = syszux * syszux * 3的第二步),上述步驟就是:

1,THPVariable_mul的分發,不再贅述;其間要使用scalar 3構建一個Variable;

2,在調用棧到達VariableType::mul的時候,構建又一個MulBackward0實例(0x55ca7fada2f0),并初始化其next_edges_成員:

#grad_fn MulBackward0, requires_grad == True,0x55ca7fada2f0 Function實例 --> sequence_nr_ (uint64_t) = 2 (每個線程內自增)--> next_edges_ (edge_list) = [(MulBackward0實例0x55ca7ebba2a0,0),(0,0)]--> input_metadata_ --> [(type, shape, device)...] = [(CPUFloatType, [2, 2],cpu])]--> self_ (SavedVariable) = tmp的淺拷貝--> other_ (SavedVariable) = 3的淺拷貝--> pre_hooks_ = None--> post_hooks_ = None--> anomaly_metadata_ = None--> apply() --> 使用 MulBackward0 的apply

注意sequence_nr_又自增了1。

3,構建Variable civilnet:

//as_variable_ref(variable).set_gradient_edge({grad_fn, output_nr}); Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = [[27., 27.],[27., 27.]]--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= MulBackward0實例0x55ca7fada2f0(參考上面)--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = False--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist

gemfieldout = civilnet.mean()

步驟其實類似了:

1,python調用到C++的THPVariable_mean的調用:

static PyObject * THPVariable_mean(PyObject* self_, PyObject* args, PyObject* kwargs) {......return wrap(dispatch_mean(self)); }

調用棧如下:

#torch/csrc/autograd/generated/python_variable_methods.cpp static PyObject * THPVariable_mean(PyObject* self_, PyObject* args, PyObject* kwargs) | V #aten/src/ATen/core/TensorMethods.h Tensor Tensor::mean() | V #torch/csrc/autograd/generated/VariableType_2.cpp Tensor VariableType::mean(const Tensor & self) | V #build/aten/src/ATen/TypeDefault.cpp Tensor TypeDefault::mean(const Tensor & self) | V #aten/src/ATen/native/ReduceOps.cpp Tensor mean(const Tensor &self) | V #aten/src/ATen/native/ReduceOps.cpp static inline Tensor mean(const Tensor &self, optional<ScalarType> dtype) | V #aten/src/ATen/native/ReduceOps.cpp static inline Tensor mean(const Tensor &self, IntArrayRef dim, bool keepdim, optional<ScalarType> dtype) | V #aten/src/ATen/native/ReduceOps.cpp Tensor &mean_out(Tensor &result, const Tensor &self, IntArrayRef dim,bool keepdim, optional<ScalarType> opt_dtype) | V at::sum_out(result, self, dim, keepdim, dtype).div_(dim_prod); | V #build/aten/src/ATen/TypeDefault.cpp Tensor & TypeDefault::sum_out(Tensor & out, const Tensor & self, IntArrayRef dim, bool keepdim, ScalarType dtype) | V #aten/src/ATen/native/ReduceOps.cpp Tensor& sum_out(Tensor& result, const Tensor& self, IntArrayRef dim, bool keepdim, ScalarType dtype) | V #aten/src/ATen/native/ReduceOps.cpp Tensor& sum_out(Tensor& result, const Tensor& self, IntArrayRef dim,bool keepdim, optional<ScalarType> opt_dtype) | V sum_stub ,依賴初始化階段的REGISTER_DISPATCH的工作

2,構建autograd

在調用棧到達 VariableType::mean時,開始順便構建autograd的信息。主要是構建一個op的反向計算函數實例:MeanBackward0實例(0x55ca7eb358b0)。MeanBackward0類型定義如下:

struct MeanBackward0 : public TraceableFunction {variable_list apply(variable_list&& grads) override;std::vector<int64_t> self_sizes;int64_t self_numel = 0; };

多了self_sizes和self_numel成員。構建完成后,MeanBackward0實例在內存中看起來如下所示:

#grad_fn MeanBackward0, requires_grad == True,0x55ca7eb358b0 Function實例 --> sequence_nr_ (uint64_t) = 3 (每個線程內自增)--> next_edges_ (edge_list) = [(MulBackward0實例0x55ca7fada2f0,0)]--> input_metadata_ --> [(type, shape, device)...] = [(CPUFloatType|[]|cpu])]--> self_sizes (std::vector<int64_t>) = (2, 2)--> self_numel = 4--> pre_hooks_ = None--> post_hooks_ = None--> anomaly_metadata_ = None--> apply() --> 使用 MulBackward0 的apply

注意sequence_nr_的值又自增了1,另外就是input_metadata_中的shape為空。

3,構建Variable gemfieldout

gemfieldout在內存中的布局如下所示:

//as_variable_ref(variable).set_gradient_edge({grad_fn, output_nr}); Variable實例 --> Variable::Imple實例 --> tensor data --> TensorImpl實例 --> Storage實例 = (27,)--> autograd_meta --> grad_ (又一個Variable實例) = None--> grad_fn_ (Function實例)= MeanBackward0實例0x55ca7eb358b0(參考上面)--> grad_accumulator_ (Function實例)= None--> version_counter_ = 0--> hooks_ len = 0--> requires_grad_ = False--> is_view_ = false--> output_nr_ = 0--> base_ = Not exist

總結

本文《PyTorch的Tensor(中)》主要介紹了Tensor的autograd部分,具體來說,就是在前向運算中,autograd的信息是如何構建和聯系在一起的:

  • 1,op的調用棧,最終派發到某種device的實現上,如果運算輸入是個scalar,進行scalar到Variable的構建;
  • 2,派發到VariableType上時,會順便進行autograd信息的構建;
  • 2.1,構建一個op的反向計算函數實例(比如AddBackward0,MulBackward0);
  • 2.2,初始化反向計算函數實例的next_edges_和其它相關成員,next_edges_成員的值來自前向時候的輸入參數,如果輸入Variable是leaf的話,則next_edges_來自輸入Variable的grad_accumulator_;如果是非leaf的話,則來自Variable的grad_fn_;
  • 2.3,使用步驟3中的Variable實例來初始化反向計算函數實例的input_metadata_,
  • 3,運算后得到新的Variable,使用Variable::Impl進行構建,使用步驟2中的反向計算函數實例初始化該Variable實例的grad_fn_成員。

而在下一篇文章《PyTorch的Tensor(下)》中,將主要介紹在backward的時候,Tensor中的autograd部分是怎么運行的。不像這篇文章中的Variable實例,那個時候其grad_成員將不再是None了。

總結

以上是生活随笔為你收集整理的tensor判断是否相等_PyTorch的Tensor(中)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。