<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          自制深度學(xué)習(xí)推理框架-實(shí)現(xiàn)我們的第一個(gè)算子Relu-第三課

          共 6636字,需瀏覽 14分鐘

           ·

          2022-12-31 10:21

          我們的課程主頁

          https://github.com/zjhellofss/KuiperInfer 歡迎pr和點(diǎn)贊

          手把手教大家去寫一個(gè)深度學(xué)習(xí)推理框架 B站視頻課程

          Relu算子的介紹

          Relu是一種非線性激活函數(shù),它的特點(diǎn)有運(yùn)算簡(jiǎn)單,不會(huì)在梯度處出現(xiàn)梯度消失的情況,而且它在一定程度上能夠防止深度學(xué)習(xí)模型在訓(xùn)練中發(fā)生的過擬合現(xiàn)象。Relu的公式表達(dá)如下所示,「如果對(duì)于深度學(xué)習(xí)基本概念不了解的同學(xué),可以將Relu當(dāng)作一個(gè)公式進(jìn)行對(duì)待,可以不用深究其背后的含義?!?/strong>

          我們今天的任務(wù)就是來完成這個(gè)公式中的操作,「值得注意的是,在我們的項(xiàng)目中,x和y可以理解為我們?cè)诘诙⒌谌?jié)中實(shí)現(xiàn)的張量類(tensor).」

          Operator類

          Operator類就是我們?cè)诘谝还?jié)中說過的計(jì)算圖中「節(jié)點(diǎn)」的概念,計(jì)算圖的另外一個(gè)概念是數(shù)據(jù)流圖,如果同學(xué)們忘記了這個(gè)概念,可以重新重新翻看第一節(jié)課程。

          在我們的代碼中我們先定義一個(gè)「Operator」類,它是一個(gè)父類,其余的Operator,包括我們本節(jié)要實(shí)現(xiàn)的ReluOperator都是其派生類,「Operator中會(huì)存放節(jié)點(diǎn)相關(guān)的參數(shù)?!?/strong>例如在「ConvOperator」中就會(huì)存放初始化卷積算子所需要的stride,  padding,  kernel_size等信息,本節(jié)的「ReluOperator」就會(huì)帶有「thresh」值信息。

          我們從下方的代碼中來了解Operator類和ReluOperator類,它們是父子關(guān)系,Operator是基類,OpType記錄Operator的類型。

          enum class OpType {
            kOperatorUnknown = -1,
            kOperatorRelu = 0,
          };

          class Operator {
           public:
            OpType kOpType = OpType::kOperatorUnknown;

            virtual ~Operator() = default;

            explicit Operator(OpType op_type);
          };

          ReluOperator實(shí)現(xiàn):

          class ReluOperator : public Operator {
           public:
            ~ReluOperator() override = default;

            explicit ReluOperator(float thresh);

            void set_thresh(float thresh);

            float get_thresh() const;

           private:
            float thresh_ = 0.f;
          };

          Layer類

          我們會(huì)在operator類中存放從「計(jì)算圖結(jié)構(gòu)文件」得到的信息,例如在ReluOperator中存放的thresh值作為一個(gè)參數(shù)就是我們從計(jì)算圖結(jié)構(gòu)文件中得到的,計(jì)算圖相關(guān)的概念我們已經(jīng)在第一節(jié)中講過。

          下一步我們需要根據(jù)ReLuOperator類去完成ReluLayer的初始化,「他們的區(qū)別在于ReluOperator負(fù)責(zé)存放從計(jì)算圖中得到的節(jié)點(diǎn)信息,不負(fù)責(zé)計(jì)算」,而ReluLayer則「負(fù)責(zé)具體的計(jì)算操作」,同樣,所有的Layer類有一個(gè)公共父類Layer. 我們可以從下方的代碼中來了解兩者的關(guān)系。

          class Layer {
           public:
            explicit Layer(const std::string &layer_name);

            virtual void Forwards(const std::vector<std::shared_ptr<Tensor<float>>> &inputs,
                                  std::vector<std::shared_ptr<Tensor<float>>> &outputs)
          ;

            virtual ~Layer() = default;
           private:
            std::string layer_name_;
          };

          其中Layer的Forwards方法是具體的執(zhí)行函數(shù),負(fù)責(zé)將輸入的inputs中的數(shù)據(jù),進(jìn)行relu運(yùn)算并存放到對(duì)應(yīng)的outputs中。

          class ReluLayer : public Layer {
           public:
            ~ReluLayer() override = default;

            explicit ReluLayer(const std::shared_ptr<Operator> &op);

            void Forwards(const std::vector<std::shared_ptr<Tensor<float>>> &inputs,
                          std::vector<std::shared_ptr<Tensor<float>>> &outputs)
           override
          ;

           private:
            std::shared_ptr<ReluOperator> op_;
          };

          這是集成于Layer的ReluLayer類,我們可以看到其中有一個(gè)op成員,是一個(gè)ReluOperator指針,「這個(gè)指針中負(fù)責(zé)存放ReluLayer計(jì)算時(shí)所需要用到的一些參數(shù)」。此處op_存放的參數(shù)比較簡(jiǎn)單,只有ReluOperator中的thresh參數(shù)。

          我們?cè)倏纯词窃趺词褂肦eluOperator去初始化ReluLayer的,先通過統(tǒng)一接口傳入Operator類,再轉(zhuǎn)換為對(duì)應(yīng)的ReluOperator指針,最后再通過指針中存放的信息去初始化「op_」.

          ReluLayer::ReluLayer(const std::shared_ptr<Operator> &op) : Layer("Relu") {
            CHECK(op->kOpType == OpType::kOperatorRelu);
            ReluOperator *relu_op = dynamic_cast<ReluOperator *>(op.get());
            CHECK(relu_op != nullptr);
            this->op_ = std::make_shared<ReluOperator>(relu_op->get_thresh());
          }

          我們來看一下具體ReluLayer的Forwards過程,它在執(zhí)行具體的計(jì)算,完成Relu函數(shù)描述的功能。

          void ReluLayer::Forwards(const std::vector<std::shared_ptr<Tensor<float>>> &inputs,
                                   std::vector<std::shared_ptr<Tensor<float>>> &outputs)
           
          {
            CHECK(this->op_ != nullptr);
            CHECK(this->op_->kOpType == OpType::kOperatorRelu);

            const uint32_t batch_size = inputs.size();
            for (int i = 0; i < batch_size; ++i) {
              CHECK(!inputs.at(i)->empty());
              const std::shared_ptr<Tensor<float>>& input_data = inputs.at(i);

              input_data->data().transform([&](float value) {
                float thresh = op_->get_thresh();
                if (value >= thresh) {
                  return value;
                } else {
                  return 0.f;
                }
              });
              outputs.push_back(input_data);
            }
          }

          在for循環(huán)中,首先讀取輸入input_data, 再對(duì)input_data使用armadillo自帶的transform按照我們給定的thresh過濾其中的元素,如果「value」的值大于thresh則不變,如果小于thresh就返回0.

          最后,我們寫一個(gè)測(cè)試函數(shù)來驗(yàn)證我們以上的兩個(gè)類,節(jié)點(diǎn)op類,計(jì)算層layer類的正確性。先判斷Forwards返回的outputs是否已經(jīng)保存了relu層的輸出,輸出大小應(yīng)該assert為1.  隨后再進(jìn)行比對(duì),我們應(yīng)該知道在thresh等于0的情況下,第一個(gè)輸出index(0)和第二個(gè)輸出index(1)應(yīng)該是0,第三個(gè)輸出應(yīng)該是3.f.

          TEST(test_layer, forward_relu) {
            using namespace kuiper_infer;
            float thresh = 0.f;
            std::shared_ptr<Operator> relu_op = std::make_shared<ReluOperator>(thresh);
            std::shared_ptr<Tensor<float>> input = std::make_shared<Tensor<float>>(113);
            input->index(0) = -1.f;
            input->index(1) = -2.f;
            input->index(2) = 3.f;
            std::vector<std::shared_ptr<Tensor<float>>> inputs;
            std::vector<std::shared_ptr<Tensor<float>>> outputs;
            inputs.push_back(input);
            ReluLayer layer(relu_op);
            layer.Forwards(inputs, outputs);
            ASSERT_EQ(outputs.size(), 1);
            for (int i = 0; i < outputs.size(); ++i) {
              ASSERT_EQ(outputs.at(i)->index(0), 0.f);
              ASSERT_EQ(outputs.at(i)->index(1), 0.f);
              ASSERT_EQ(outputs.at(i)->index(2), 3.f);
            }
          }

          本期代碼倉(cāng)庫(kù)位置

          git clone https://gitee.com/fssssss/KuiperCourse.git
          git checkout fouth

          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    国产高清无码中文字幕 | 丁香五月天视频 | 亚洲免费观看A∨中文 | 黄色综合网站 | 亚洲AV无码久久精品色无码蜜桃 |