女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

PyTorch構建自己一種易用的計算圖結構

jf_pmFSk4VX ? 來源:GiantPandaCV ? 2023-02-01 14:26 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

PNNX

PNNX項目 PyTorch Neural Network eXchange(PNNX)是PyTorch模型互操作性的開放標準.

PNNX為PyTorch提供了一種開源的模型格式, 它定義了與PyTorch相匹配的數據流圖和運算操作, 我們的框架在PNNX之上封裝了一層更加易用和簡單的計算圖格式. PyTorch訓練好一個模型之后, 然后模型需要轉換到PNNX格式, 然后PNNX格式我們再去讀取, 形成計算圖.

PyTorch到我們計算圖?

PNNX幫我做了很多的圖優化、算子融合的工作, 所以底層的用它PNNX的話, 我們可以吸收圖優化的結果, 后面推理更快.

但是我們不直接在項目中用PNNX, 因為別人的工作和自己推理框架開發思路總是有不同的. 所以在這上面封裝, 又快速又好用方便, 符合自己的使用習慣. PNNX的使用方法, 我們只是去讀取PNNX導出的模型, 然后構建自己一種易用的計算圖結構.

PNNX的格式定義

PNNX由操作數operand(運算數)和operator(運算符號), PNNX::Graph用來管理和操作這兩者.

操作數(operand), 也可以通過操作數來方向訪問到這個數字的產生者和使用者Customer

代碼鏈接

Operand

定義鏈接

Operand有以下幾個部分組成:

Producer: 類型是operator, 表示產生了這個操作數(operand)的運算符(operator). 也就是說這個操作數(operand)是Producer的輸出.

比如Producer是有個Add, Operand就是對應的Add結果.

Customer:類型是operator, 表示需要這個操作數是下一個操作的運算符(operator)的輸入. 值得注意的是生產者Producer作為產生這個操作數的operator只能有一個, 而消費者Customer可以有多個, 消費者將當前的操作數Operand作為輸入.

Name: 類型是std::string, 表示這個操作數的名稱.

Shape: 類型是std::vector , 用來表示操作數的大小.

Operator

定義鏈接

operator有以下幾個部分組成:

Inputs: 類型為std::vector, 表示這個運算符計算過程中所需要的輸入操作數(operand)

Outputs: 類型為std::vector, 表示這個運算符計算過程中得到的輸出操作數(operand)

Type, Name 類型均為std::string, 分別表示運算符號的類型和名稱

Params, 類型為std::map,用于存放該運算符的所有參數(例如對應Convolution operator的params中將存放stride, padding, kernel size等信息)

Attrs, 類型為std::map, 用于存放運算符號所需要的具體權重屬性(例如對應Convolution operator的attrs中就存放著卷積的權重和偏移量)

我們對PNNX的封裝

對Operands(運算數)的封裝

structRuntimeOperand{
std::stringname;///操作數的名稱
std::vectorshapes;///操作數的形狀
std::vector>>datas;///存儲操作數
RuntimeDataTypetype=RuntimeDataType::kTypeUnknown;///操作數的類型,一般是float
};

對Operator(運算符)的封裝

對PNNX::operator的封裝是RuntimeOperator, 下面會講具體的PNNX到KuiperInfer計算圖的轉換過程.

///計算圖中的計算節點
structRuntimeOperator{
~RuntimeOperator();
std::stringname;///運算符號節點的名稱
std::stringtype;///運算符號節點的類型
std::shared_ptrlayer;///節點對應的計算Layer

std::vectoroutput_names;///運算符號的輸出節點名稱
std::shared_ptroutput_operands;///運算符號的輸出操作數

std::map>input_operands;///運算符的輸入操作數
std::vector>input_operands_seq;///運算符的輸入操作數,順序排列

std::mapparams;///算子的參數信息
std::map>attribute;///算子的屬性信息,內含權重信息
};

從PNNX計算圖到KuiperInfer計算圖的過程

本節代碼鏈接

1. 加載PNNX的計算圖

intload_result=this->graph_->load(param_path_,bin_path_);

2. 獲取PNNX計算圖中的運算符(operators)

std::vectoroperators=this->graph_->ops;
if(operators.empty()){
LOG(ERROR)<

3. 遍歷PNNX計算圖中的運算符, 構建KuiperInfer計算圖

for(constpnnx::Operator*op:operators){
...
}

4. 初始化RuntimeOperator的輸入

初始化RuntimeOperator中的RuntimeOperator.input_operands和RuntimeOperator.input_operands_seq兩個屬性.

通過解析pnnx的計算圖來初始化KuiperInfer RuntimeOperator中的輸入部分. 簡單來說就是從pnnx::inputs轉換得到KuiperInfer::inputs

structRuntimeOperator{
///本過程要初始化的兩個屬性
std::map>input_operands;///運算符的輸入操作數
std::vector>input_operands_seq;///運算符的輸入操作數,順序排列
...
}

從PNNX::Input到KuiperInfer::Input的轉換過程, 代碼鏈接

constpnnx::Operator*op=...
conststd::vector&inputs=op->inputs;
if(!inputs.empty()){
InitInputOperators(inputs,runtime_operator);
}
....
voidRuntimeGraph::InitInputOperators(conststd::vector&inputs,
conststd::shared_ptr&runtime_operator){
//遍歷輸入pnnx的操作數類型(operands),去初始化KuiperInfer中的操作符(RuntimeOperator)的輸入.
for(constpnnx::Operand*input:inputs){
if(!input){
continue;
}
//得到pnnx操作數對應的生產者(類型是pnnx::operator)
constpnnx::Operator*producer=input->producer;
//初始化RuntimeOperator的輸入runtime_operand
std::shared_ptrruntime_operand=std::make_shared();
//賦值runtime_operand的名稱和形狀
runtime_operand->name=producer->name;
runtime_operand->shapes=input->shape;

switch(input->type){
case1:{
runtime_operand->type=RuntimeDataType::kTypeFloat32;
break;
}
case0:{
runtime_operand->type=RuntimeDataType::kTypeUnknown;
break;
}
default:{
LOG(FATAL)<type;
}
}
//runtime_operand放入到KuiperInfer的運算符中
runtime_operator->input_operands.insert({producer->name,runtime_operand});
runtime_operator->input_operands_seq.push_back(runtime_operand);
}
}

5. 初始化RuntimeOperator中的輸出

初始化RuntimeOperator.output_names屬性. 通過解析PNNX的計算圖來初始化KuiperInfer Operator中的輸出部分.代碼鏈接

簡單來說就是從PNNX::outputs到KuiperInfer::output

voidRuntimeGraph::InitOutputOperators(conststd::vector&outputs,
conststd::shared_ptr&runtime_operator){
for(constpnnx::Operand*output:outputs){
if(!output){
continue;
}
constauto&consumers=output->consumers;
for(constauto&c:consumers){
runtime_operator->output_names.push_back(c->name);
}
}
}

6. 初始化RuntimeOperator的權重(Attr)屬性

KuiperInfer::RuntimeAttributes. Attributes中存放的是operator計算時需要的權重屬性, 例如Convolution Operator中的weights和bias.

//初始化算子中的attribute(權重)
constpnnx::Operator*op=...
conststd::map&attrs=op->attrs;
if(!attrs.empty()){
InitGraphAttrs(attrs,runtime_operator);
}

代碼鏈接

voidRuntimeGraph::InitGraphAttrs(conststd::map&attrs,
conststd::shared_ptr&runtime_operator){
for(constauto&pair:attrs){
conststd::string&name=pair.first;
//1.得到pnnx中的Attribute
constpnnx::Attribute&attr=pair.second;
switch(attr.type){
case1:{
//2.根據Pnnx的Attribute初始化KuiperInferOperator中的Attribute
std::shared_ptrruntime_attribute=std::make_shared();
runtime_attribute->type=RuntimeDataType::kTypeFloat32;
//2.1賦值權重weight(此處的data是std::vector類型)
runtime_attribute->weight_data=attr.data;
runtime_attribute->shape=attr.shape;
runtime_operator->attribute.insert({name,runtime_attribute});
break;
}
default:{
LOG(FATAL)<

7. 初始化RuntimeOperator的參數(Param)屬性

簡單來說就是從pnnx::Params去初始化KuiperInfer::Params

conststd::map¶ms=op->params;
if(!params.empty()){
InitGraphParams(params,runtime_operator);
}

KuiperInfer::RuntimeParameter有多個派生類構成, 以此來對應中多種多樣的參數, 例如ConvOperator中有std::string類型的參數, padding_mode, 也有像uint32_t類型的kernel_size和padding_size參數, 所以我們需要以多種參數類型去支持他.

換句話說, 一個KuiperInfer::Params, param可以是其中的任意一個派生類, 這里我們利用了多態的特性. KuiperInfer::RuntimeParameter具有多種派生類, 如下分別表示為Int參數和Float參數, 他們都是RuntimeParameter的派生類.

std::mapparams;///算子的參數信息
//用指針來實現多態

structRuntimeParameter{///計算節點中的參數信息
virtual~RuntimeParameter()=default;

explicitRuntimeParameter(RuntimeParameterTypetype=RuntimeParameterType::kParameterUnknown):type(type){

}
RuntimeParameterTypetype=RuntimeParameterType::kParameterUnknown;
};
///int類型的參數
structRuntimeParameterInt:publicRuntimeParameter{
RuntimeParameterInt():RuntimeParameter(RuntimeParameterType::kParameterInt){

}
intvalue=0;
};
///float類型的參數
structRuntimeParameterFloat:publicRuntimeParameter{
RuntimeParameterFloat():RuntimeParameter(RuntimeParameterType::kParameterFloat){

}
floatvalue=0.f;
};

從PNNX::param到RuntimeOperator::param的轉換過程.代碼鏈接

voidRuntimeGraph::InitGraphParams(conststd::map¶ms,
conststd::shared_ptr&runtime_operator){
for(constauto&pair:params){
conststd::string&name=pair.first;
constpnnx::Parameter¶meter=pair.second;
constinttype=parameter.type;
//根據PNNX的Parameter去初始化KuiperInfer::RuntimeOperator中的Parameter
switch(type){
caseint(RuntimeParameterType::kParameterUnknown):{
RuntimeParameter*runtime_parameter=newRuntimeParameter;
runtime_operator->params.insert({name,runtime_parameter});
break;
}
//在這應該使用派生類RuntimeParameterBool
caseint(RuntimeParameterType::kParameterBool):{
RuntimeParameterBool*runtime_parameter=newRuntimeParameterBool;
runtime_parameter->value=parameter.b;
runtime_operator->params.insert({name,runtime_parameter});
break;
}
//在這應該使用派生類RuntimeParameterInt
caseint(RuntimeParameterType::kParameterInt):{
RuntimeParameterInt*runtime_parameter=newRuntimeParameterInt;
runtime_parameter->value=parameter.i;
runtime_operator->params.insert({name,runtime_parameter});
break;
}

caseint(RuntimeParameterType::kParameterFloat):{
RuntimeParameterFloat*runtime_parameter=newRuntimeParameterFloat;
runtime_parameter->value=parameter.f;
runtime_operator->params.insert({name,runtime_parameter});
break;
}

caseint(RuntimeParameterType::kParameterString):{
RuntimeParameterString*runtime_parameter=newRuntimeParameterString;
runtime_parameter->value=parameter.s;
runtime_operator->params.insert({name,runtime_parameter});
break;
}

caseint(RuntimeParameterType::kParameterIntArray):{
RuntimeParameterIntArray*runtime_parameter=newRuntimeParameterIntArray;
runtime_parameter->value=parameter.ai;
runtime_operator->params.insert({name,runtime_parameter});
break;
}

caseint(RuntimeParameterType::kParameterFloatArray):{
RuntimeParameterFloatArray*runtime_parameter=newRuntimeParameterFloatArray;
runtime_parameter->value=parameter.af;
runtime_operator->params.insert({name,runtime_parameter});
break;
}
caseint(RuntimeParameterType::kParameterStringArray):{
RuntimeParameterStringArray*runtime_parameter=newRuntimeParameterStringArray;
runtime_parameter->value=parameter.as;
runtime_operator->params.insert({name,runtime_parameter});
break;
}
default:{
LOG(FATAL)<

8. 初始化成功

將通過如上步驟初始化好的KuiperInfer::RuntimeOperator存放到一個vector中

this->operators_.push_back(runtime_operator);

驗證我們的計算圖

我們先準備好了如下的一個計算圖(準備過程不是本節的重點, 讀者直接使用即可), 存放在tmp目錄中, 它由兩個卷積, 一個Add(expression)以及一個最大池化層組成.

3685b1f6-98fa-11ed-bfe3-dac502259ad0.png

TEST(test_runtime,runtime1){
usingnamespacekuiper_infer;
conststd::string¶m_path="./tmp/test.pnnx.param";
conststd::string&bin_path="./tmp/test.pnnx.bin";
RuntimeGraphgraph(param_path,bin_path);
graph.Init();
constautooperators=graph.operators();
for(constauto&operator_:operators){
LOG(INFO)<type<name;
}
}

如上為一個測試函數, Init就是我們剛才分析過的一個函數, 它定義了從PNNX計算圖到KuiperInfer計算圖的過程.

最后的輸出

I202301071133.03383856358test_main.cpp:13]Starttest...
I202301071133.03441156358test_runtime1.cpp:17]type:pnnx.Inputname:pnnx_input_0
I202301071133.03442156358test_runtime1.cpp:17]type:nn.Conv2dname:conv1
I202301071133.03442556358test_runtime1.cpp:17]type:nn.Conv2dname:conv2
I202301071133.03443056358test_runtime1.cpp:17]type:pnnx.Expressionname:pnnx_expr_0
I202301071133.03443556358test_runtime1.cpp:17]type:nn.MaxPool2dname:max
I202301071133.03444056358test_runtime1.cpp:17]type:pnnx.Outputname:pnnx_output_0

可以看出, Init函數最后得到的結果和圖1中定義的是一致的. 含有兩個Conv層, conv1和conv2, 一個add層Expression以及一個最大池化MaxPool2d層.








審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 運算符
    +關注

    關注

    0

    文章

    173

    瀏覽量

    11475
  • float
    +關注

    關注

    0

    文章

    9

    瀏覽量

    7898
  • pytorch
    +關注

    關注

    2

    文章

    809

    瀏覽量

    13943

原文標題:自制深度學習推理框架-第六課-構建自己的計算圖

文章出處:【微信號:GiantPandaCV,微信公眾號:GiantPandaCV】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    如何利用PyTorch API構建CNN?

      很多人對于卷積神經網絡(CNN)并不了解,卷積神經網絡是一種前饋神經網絡,它包括卷積計算并具有很深的結構,卷積神經網絡是深度學習的代表性算法之。那么如何利用
    發表于 07-16 18:13

    TVM整體結構,TVM代碼的基本構成

    TIR是更接近硬件的表示結構。Relay中IR通過relay::function來描述,function描述了整個結構,是結構的另外
    發表于 01-07 17:21

    一種基于MapReduce的結構聚類算法

    (tril5)(m為圖中邊的條數),因此很難處理大規模的數據。為了解決SCAN算法的可擴展性問題,提出了一種新穎的基于MapReduce的海量結構聚類算法MRSCAN。具體地,提出
    發表于 12-19 11:05 ?0次下載
    <b class='flag-5'>一種</b>基于MapReduce的<b class='flag-5'>圖</b><b class='flag-5'>結構</b>聚類算法

    教你用PyTorch快速準確地建立神經網絡

    動態計算PyTorch被稱為“由運行定義的”框架,這意味著計算結構(神經網絡體系
    的頭像 發表于 02-11 14:33 ?3497次閱讀

    基于PyTorch的深度學習入門教程之PyTorch的安裝和配置

    神經網絡結構,并且運用各種深度學習算法訓練網絡參數,進而解決各種任務。 本文從PyTorch環境配置開始。PyTorch一種Python接口的深度學習框架,使用靈活,學習方便。還有其
    的頭像 發表于 02-16 15:15 ?2865次閱讀

    基于PyTorch的深度學習入門教程之PyTorch的自動梯度計算

    計算 Part3:使用PyTorch構建個神經網絡 Part4:訓練個神經網絡分類器 Part5:數據并行化 本文是關于Part2的內容
    的頭像 發表于 02-16 15:26 ?2275次閱讀

    基于PyTorch的深度學習入門教程之使用PyTorch構建個神經網絡

    PyTorch的自動梯度計算 Part3:使用PyTorch構建個神經網絡 Part4:訓練
    的頭像 發表于 02-15 09:40 ?2324次閱讀

    PyTorch教程5.3之前向傳播、反向傳播和計算

    電子發燒友網站提供《PyTorch教程5.3之前向傳播、反向傳播和計算.pdf》資料免費下載
    發表于 06-05 15:36 ?0次下載
    <b class='flag-5'>PyTorch</b>教程5.3之前向傳播、反向傳播和<b class='flag-5'>計算</b><b class='flag-5'>圖</b>

    pytorch如何構建網絡模型

      利用 pytorch構建網絡模型有很多種方法,以下簡單列出其中的四?! 〖僭O構建個網絡模型如下:  卷積層--》Relu 層--
    發表于 07-20 11:51 ?0次下載

    中科曙光打造一種全新的計算體系構建與運營模式—“立體計算

    4月2日,中科曙光“立體計算湖南行”啟動儀式在長沙成功舉辦。面對“加快發展新質生產力”的新要求,中科曙光提出“立體計算”新思路,旨在打造一種全新的計算體系
    的頭像 發表于 04-03 09:52 ?729次閱讀
    中科曙光打造<b class='flag-5'>一種</b>全新的<b class='flag-5'>計算</b>體系<b class='flag-5'>構建</b>與運營模式—“立體<b class='flag-5'>計算</b>”

    使用PyTorch構建神經網絡

    PyTorch個流行的深度學習框架,它以其簡潔的API和強大的靈活性在學術界和工業界得到了廣泛應用。在本文中,我們將深入探討如何使用PyTorch構建神經網絡,包括從基礎概念到高級
    的頭像 發表于 07-02 11:31 ?1084次閱讀

    如何使用PyTorch建立網絡模型

    PyTorch個基于Python的開源機器學習庫,因其易用性、靈活性和強大的動態特性,在深度學習領域得到了廣泛應用。本文將從PyTorch
    的頭像 發表于 07-02 14:08 ?834次閱讀

    PyTorch如何訓練自己的數據集

    PyTorch個廣泛使用的深度學習框架,它以其靈活性、易用性和強大的動態特性而聞名。在訓練深度學習模型時,數據集是不可或缺的組成部分。然而,很多時候,我們可能需要使用
    的頭像 發表于 07-02 14:09 ?3553次閱讀

    PyTorch的特性和使用方法

    使用Python重新寫了很多內容,使其更加靈活易用。它不僅是個擁有自動求導功能的深度神經網絡框架,還可以看作是個加入了GPU支持的NumPy。PyTorch支持動態
    的頭像 發表于 07-02 14:27 ?1195次閱讀

    pytorch如何訓練自己的數據

    本文將詳細介紹如何使用PyTorch框架來訓練自己的數據。我們將從數據準備、模型構建、訓練過程、評估和測試等方面進行講解。 環境搭建 首先,我們需要安裝PyTorch。可以通過訪問
    的頭像 發表于 07-11 10:04 ?1021次閱讀