0.調用實例
先看一個調用實例,順着調用流程探尋ncnn內部具體實現細節。
#include "net.h"
int main(int argc, char **argv)
{
ncnn::Mat in;
ncnn::Mat out;
ncnn::Net net;
net.load_param("model.param");
net.load_model("model.bin");
ncnn::Extractor ex = net.create_extractor();
ex.set_light_mode(true);
ex.set_num_threads(4);
ex.input("data", in);
ex.extract("output", out);
return 0;
}
1.blob結構
class Blob
{
public:
std::string name; //blob名字
int producer; //指明該blob來自哪個層的輸出,層索引
std::vector<int> consumers; //指明該blob作為哪個層的輸入,層索引
}
在blob的構造函數中初始化producer=-1
2.layer
class Layer
{
public:
int typeindex; //類型ID
std::string type; //類型名字
std::string name; //層的名字
std::vector<int> bottoms; //當前層所有輸入blob的索引
std::vector<int> tops; //當前層所有輸出blob索引
int load_param(const ParamDict &pd);
int load_model(const ModelBin &mb);
int forward(const std::vector<Mat> &bottom_blobs, std::vector<Mat> &top_blobs, const Option &opt = get_default_option());
}
- layer進行前向傳播時,根據bottoms索引值找到bottom數據,作為forward的輸入,計算結果存入tops對應的blob里,完成一層的inferecnce
- load_param和load_model有三種定義
- 第一個是在net里定義為
int load_param(FILE *fp);
,主要功能是從param文件中讀取數據,網絡層數,網絡blob個數,每一個輸入輸出blob的類型、名字等信息。涉及到網絡結構的參數(不是訓練參數),例如濾波器個數、padding、stride等信息由ParamDict里的load_param負責讀取。 - 第二個是在ParamDict里,定義為
int load_param(FILE *fp);
fp位置接net里的位置,該函數將參數讀取到ParamDict的一個類實例pd里,以pair對的形式存儲,不考慮具體參數含義,只需按照key,value存儲你即可。 - 第三個是在layer里,定義為
int load_param(const ParamDict &pd);
這個load_param負責從pair對里根據不同層對key的定義解析成和每一個層對應的參數,參數的不同決定了相同類型層的差異性,比如同樣是卷積層,但是濾波器個數不同。 - load_model和load_param類似,至此完成了整個網絡的解析工作。
- 第一個是在net里定義為
以上內容對應於我們平時使用ncnn的以下代碼形式:
ncnn::Net net;
net.load_param("model.param");
net.load_model("model.bin");
2.net解析
class Net
{
public:
int usewinograd_convolution;
int use_sgemm_convolution;
int use_int8_inference;
int use_vulkan_compute;
int load_param(FILE *fp);
int load_model(FILE *fp);
Extractor create_extractor();
protected:
std::vector<Blob> blobs;//網絡的所有blob
std::vector<Layer*> layers;//網絡的所有層指針
int forward_layer(int layer_index, std::vector<Mat> &blob_mats, Options &opt);
int find_blob_index_by_name(const char* name);
int find_layer_index_by_name(const char *name);
}
class Extractor
{
public:
int Extractor::input(const char *blob_name, const Mat &in);
int Extractor::input(int blob_index, VkMat &feat, VkCompute &cmd);
int Extractor::extract(const char *blob_name, Mat &feat);
int Extractor::extract(blob_index, const Mat &feat);//次函數直接forward_layer()
protected:
friend Extractor Net::create_extractor() const;
Extractor(const Net *net, int blob_count);
private:
const Net *net;
std::vector<Mat> blob_mats;
Option opt;
}
- Net類里的成員變量包含了另一個類create_extractor方法,該方法實際上就是調用Extractor類的構造函數,返回一個Extractor實例。
Extractor Net::create_extractor() const
{
return Extractor(this, blobs.size());
}
調用Extractor::input(const char *blob_name, const Mat &in)
設置輸入數據,這里比較簡單,通過輸入blob名字找到對應的索引,然后根據索引取到真實的blob數據。
- 整個網絡的核心是
Extractor::extract(const char *blob_name, Mat &feat)
- 該函數調用另一個重載函數
Extractor::extract(blob_index, const Mat &feat)
兩個輸入參數分別是要獲取數據blob索引和存放數據的輸出變量,通過blob_index在blobs(net的類成員變量,用於存放整個網絡的所有blob)找到對應的blob - 調用blob的類成員變量producer來找到該blob是哪個layer的輸出,也就是layer_index
- 接下來調用
forward_layer(layer_index, blob_mats, opt)
完成整個網絡的前向傳播,逐層前傳使用遞歸完成,blob_mats里存放真正的blob數據,是net的私有成員變量std::vector<Mat> blob_mats
,Mat是自定義類型 - 完成forward_layer后,
feat = blob_mats[blob_index]
,feat是調用例子中out的引用,將blob數據存放在feat變量中,整個流程結束
- 該函數調用另一個重載函數