<output id="lfp77"><del id="lfp77"></del></output>

<meter id="lfp77"></meter>
<ol id="lfp77"><strike id="lfp77"></strike></ol>

    <delect id="lfp77"><strike id="lfp77"></strike></delect>

    <track id="lfp77"></track>
    <delect id="lfp77"><ruby id="lfp77"></ruby></delect>

      <sub id="lfp77"></sub><meter id="lfp77"></meter>

      <em id="lfp77"></em>
        13397158231   jevian_ma@worldflying.cn

      用C語言手寫一個神經網絡

      2024-04-07 03:04:27

      該程序是模擬tensflow游樂場寫的,實現了基本的神經網絡效果并驗證通過,不多廢話,上代碼。

      核心代碼在nn.c中,包含激活函數和損失函數,前向傳播,反向傳播以及更新權重與偏執的函數。

      #include <stdint.h>
      #include <stdlib.h>
      #include <math.h>
      #include "config.h"
      #include "dataset.h"
      #include "nn.h"
      
      int networkShape[] = {2, 8, 8, 8, 8, 8, 8, 1};
      NODE **network;
      
      double getOutPut()
      {
          return network[sizeof(networkShape) / sizeof(int) - 1][0].output;
      }
      
      double square(double output, double target)
      {
          double r = output - target;
          return r * r / 2;
      }
      
      double squareder(double output, double target)
      {
          return output - target;
      }
      
      double activation(double x)
      {
      #if ACTIVATIONFUNCTION == RELU
          if (x > 0)
          {
              return x;
          }
          else
          {
              return 0;
          }
      #elif ACTIVATIONFUNCTION == TANH
          return tanh(x);
      #endif
      }
      
      double activationder(double x)
      {
      #if ACTIVATIONFUNCTION == RELU
          if (x > 0)
          {
              return 1;
          }
          else
          {
              return 0;
          }
      #elif ACTIVATIONFUNCTION == TANH
          // tanh的倒數
          double y = tanh(x);
          return 1 - y * y;
      #endif
      }
      
      double outlayeractivation(double x)
      {
      #if OUTLAYERACTIVATIONFUNCTION == TANH
          return tanh(x);
      #endif
      }
      
      double outlayeractivationder(double x)
      {
      #if OUTLAYERACTIVATIONFUNCTION == TANH
          // tanh的倒數
          double y = tanh(x);
          return 1 - y * y;
      #endif
      }
      
      void buildNetwork()
      {
          network = (PPNODE)malloc((sizeof(networkShape) / sizeof(int)) * sizeof(PNODE));
          // 輸入層
          network[0] = (PNODE)malloc(networkShape[0] * sizeof(NODE));
          // 隱藏層與輸出層
          for (int i = 1, leni = sizeof(networkShape) / sizeof(int); i < leni; i++)
          {
              network[i] = (PNODE)malloc(networkShape[i] * sizeof(NODE));
              int prenodeNum = networkShape[i - 1];
              for (int j = 0, lenj = networkShape[i]; j < lenj; j++)
              {
                  network[i][j].link = (PLINK)malloc(prenodeNum * sizeof(LINK));
              }
          }
          // 輸入層
          for (int i = 0; i < networkShape[0]; i++)
          {
              network[0][i].bias = 0.1;
          }
          // 隱藏層與輸出層
          for (int i = 1, leni = sizeof(networkShape) / sizeof(int); i < leni; i++)
          {
              for (int j = 0, lenj = networkShape[i]; j < lenj; j++)
              {
                  network[i][j].bias = 0.1;
                  network[i][j].inputDer = 0;
                  network[i][j].outputDer = 0;
                  network[i][j].accInputDer = 0;
                  network[i][j].numAccumulatedDers = 0;
                  for (int k = 0, lenk = networkShape[i - 1]; k < lenk; k++)
                  {
                      network[i][j].link[k].weight = (double)rand() / RAND_MAX - 0.5;
                      network[i][j].link[k].errorDer = 0;
                      network[i][j].link[k].accErrorDer = 0;
                      network[i][j].link[k].numAccumulatedDers = 0;
                  }
              }
          }
      }
      
      void forwardProp(POINT point)
      {
          int outlayerNum = sizeof(networkShape) / sizeof(int) - 1; // 輸出層所在層
          // 輸入層
          network[0][0].output = point.x;
          network[0][1].output = point.y;
          // 隱藏層
          for (int i = 1, leni = outlayerNum; i < leni; i++)
          {
              for (int j = 0, lenj = networkShape[i]; j < lenj; j++)
              {
                  network[i][j].totalInput = network[i][j].bias;
                  for (int k = 0, lenk = networkShape[i - 1]; k < lenk; k++)
                  {
                      network[i][j].totalInput += network[i][j].link[k].weight * network[i - 1][k].output;
                  }
                  network[i][j].output = activation(network[i][j].totalInput);
              }
          }
          // 輸出層
          for (int i = 0, leni = networkShape[outlayerNum]; i < leni; i++)
          {
              network[outlayerNum][i].totalInput = network[outlayerNum][i].bias;
              for (int j = 0, lenj = networkShape[outlayerNum - 1]; j < lenj; j++)
              {
                  network[outlayerNum][i].totalInput += network[outlayerNum][i].link[j].weight * network[outlayerNum - 1][j].output;
              }
              network[outlayerNum][i].output = outlayeractivation(network[outlayerNum][i].totalInput);
          }
      }
      
      void backProp(POINT point)
      {
          // 清空所有節點的outputDer
          for (int i = 0, leni = sizeof(networkShape) / sizeof(int); i < leni; i++)
          {
              for (int j = 0; j < networkShape[i]; j++)
              {
                  network[i][j].outputDer = 0;
              }
          }
          int outlayerNum = sizeof(networkShape) / sizeof(int) - 1; // 輸出層所在層
          // 輸出層
          for (int i = 0, leni = networkShape[outlayerNum]; i < leni; i++)
          {
              network[outlayerNum][i].outputDer = squareder(network[outlayerNum][i].output, point.label); // 目標和結果的差距
              network[outlayerNum][i].inputDer = network[outlayerNum][i].outputDer * outlayeractivationder(network[outlayerNum][i].totalInput);
              network[outlayerNum][i].accInputDer += network[outlayerNum][i].inputDer;
              network[outlayerNum][i].numAccumulatedDers++;
              for (int j = 0, lenj = networkShape[outlayerNum]; j < lenj; j++)
              {
                  network[outlayerNum][i].link[i].errorDer = network[outlayerNum][i].inputDer * network[outlayerNum - 1][i].output;
                  network[outlayerNum][i].link[i].accErrorDer += network[outlayerNum][i].link[i].errorDer;
                  network[outlayerNum][i].link[i].numAccumulatedDers++;
                  network[outlayerNum - 1][i].outputDer += network[outlayerNum][i].link[i].weight * network[outlayerNum][i].inputDer;
              }
          }
          // 隱藏層
          for (int i = outlayerNum; i > 0; i--)
          {
              for (int j = 0; j < networkShape[i]; j++)
              {
                  network[i][j].inputDer = network[i][j].outputDer * activationder(network[i][j].totalInput);
                  network[i][j].accInputDer += network[i][j].inputDer;
                  network[i][j].numAccumulatedDers++;
                  for (int k = 0; k < networkShape[i - 1]; k++)
                  {
                      network[i][j].link[k].errorDer = network[i][j].inputDer * network[i - 1][k].output;
                      network[i][j].link[k].accErrorDer += network[i][j].link[k].errorDer;
                      network[i][j].link[k].numAccumulatedDers++;
                      network[i - 1][k].outputDer += network[i][j].link[k].weight * network[i][j].inputDer;
                  }
              }
          }
      }
      
      void updateWeights()
      {
          // 隱藏層與輸出層
          for (int i = 1; i < sizeof(networkShape) / sizeof(int); i++)
          {
              for (int j = 0; j < networkShape[i]; j++)
              {
                  if (network[i][j].numAccumulatedDers > 0)
                  {
                      network[i][j].bias -= LEARNINGRATE * network[i][j].accInputDer / network[i][j].numAccumulatedDers;
                      network[i][j].accInputDer = 0;
                      network[i][j].numAccumulatedDers = 0;
                  }
                  for (int k = 0; k < networkShape[i - 1]; k++)
                  {
                      if (network[i][j].link[k].numAccumulatedDers > 0)
                      {
                          network[i][j].link[k].weight -= LEARNINGRATE * network[i][j].link[k].accErrorDer / network[i][j].link[k].numAccumulatedDers;
                          network[i][j].link[k].accErrorDer = 0;
                          network[i][j].link[k].numAccumulatedDers = 0;
                      }
                  }
              }
          }
      }

      對應頭文件為nn.h

      #ifndef __NN_H__
      #define __NN_H__
      
      #include "config.h"
      
      typedef struct LINK
      {
          double weight;
          double errorDer;
          double accErrorDer;
          int numAccumulatedDers;
      } LINK;
      typedef LINK *PLINK;
      
      typedef struct NODE
      {
          double bias;
          PLINK link;
          double output;
          double inputDer;
          double outputDer;
          double accInputDer;
          int numAccumulatedDers;
          double totalInput;
      } NODE;
      typedef NODE *PNODE;
      typedef PNODE *PPNODE;
      
      double getOutPut();
      double square(double output, double target);
      double squareder(double output, double target);
      double tanhder(double x); // tanh的倒數
      double activation(double x);
      double activationder(double x);
      double outlayeractivation(double x);
      double outlayeractivationder(double x);
      void buildNetwork();
      void forwardProp(POINT point);
      void backProp(POINT point);
      void updateWeights();
      
      #endif

      自動創建與生成訓練集與測試集的程序,這里就創建了一個基于半徑為5的型,圓中間是一部分數據,圓外圍是一部分數據。

      #include <stdlib.h>
      #include <math.h>
      #include "config.h"
      #include "dataset.h"
      
      POINT points[NUMSAMPLES];
      
      void shuffle()
      {
          for (int i = 0; i < NUMSAMPLES; i++)
          {
              int index = i * ((double)rand() / RAND_MAX);
              POINT point = points[i];
              points[i] = points[index];
              points[index] = point;
          }
      }
      
      // 創建NUMSAMPLES個參數,按照原型來創建
      void classifyCircleData()
      {
          double radius = 5;
          // 創建內部圓上的點
          for (int i = 0; i < NUMSAMPLES / 2; i++)
          {
              double r = 0.5 * radius * rand() / RAND_MAX;   // 生成隨機的半徑
              double angle = 2.0 * M_PI * rand() / RAND_MAX; // 生成隨機的角度
              points[i].x = r * cos(angle);
              points[i].y = r * sin(angle);
              points[i].label = 1;
          }
          // 創建外部圓上的點
          for (int i = NUMSAMPLES / 2; i < NUMSAMPLES; i++)
          {
              double r = 0.7 * radius + 0.3 * radius * rand() / RAND_MAX; // 生成隨機的半徑
              double angle = 2.0 * M_PI * rand() / RAND_MAX;              // 生成隨機的角度
              points[i].x = r * cos(angle);
              points[i].y = r * sin(angle);
              points[i].label = -1;
          }
          shuffle();
      }

      對應頭文件為dataset.h

      #ifndef __DATASET_H__
      #define __DATASET_H__
      
      typedef struct
      {
          double x;
          double y;
          double label;
      } POINT;
      
      void classifyCircleData();
      
      #endif

      程序配置部分為config.h,定義了數據集大小,學習率以及batchsize大小,還有激活函數,損失函數等應該選什么。

      #ifndef __CONFIG_H__
      #define __CONFIG_H__
      
      #define NUMSAMPLES 500 // 創建測試點的數量,其中前一半作為訓練集,后一半作為測試集
      #define LEARNINGRATE 0.03
      #define BATCHSIZE 10
      #define ACTIVATIONFUNCTION RELU
      #define OUTLAYERACTIVATIONFUNCTION TANH
      
      #endif

      main.c主要是調用上述函數,初始化網絡以及數據集,以及訓練。

      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      #include "config.h"
      #include "dataset.h"
      #include "nn.h"
      
      extern POINT points[NUMSAMPLES];
      
      double getLoss(int mode) // 0代表訓練集,1代表測試集
      {
          double loss = 0;
          if (mode)
          {
              for (int i = NUMSAMPLES / 2; i < NUMSAMPLES; i++)
              {
                  forwardProp(points[i]);
                  loss += square(getOutPut(), points[i].label);
              }
          }
          else
          {
              for (int i = 0; i < NUMSAMPLES / 2; i++)
              {
                  forwardProp(points[i]);
                  loss += square(getOutPut(), points[i].label);
              }
          }
          return loss / (NUMSAMPLES / 2);
      }
      
      void training()
      {
          for (int i = 0; i < NUMSAMPLES / 2; i++)
          {
              forwardProp(points[i]);
              backProp(points[i]);
              if ((i + 1) % BATCHSIZE == 0)
              {
                  updateWeights();
              }
          }
          double lossTrain = getLoss(0);
          double lossTest = getLoss(1);
          printf("lossTrain:%f,lossTest:%f\n", lossTrain, lossTest);
      }
      
      int main(int argc, char **argv)
      {
          srand((unsigned)time(NULL));
          classifyCircleData();
          buildNetwork();
          double lossTrain = getLoss(0);
          double lossTest = getLoss(1);
          printf("lossTrain:%f,lossTest:%f\n", lossTrain, lossTest);
          for (int i = 0; i < 100; i++)
          {
              training();
          }
          return 0;
      }

      代碼完整地址為:傳送門

      后期可能會根據我學習的深入繼續更新這份代碼,就不另行通知了。


      文章作者:沃航科技

      優秀產品推薦:可編程網絡IO控制器

      上一篇:yolo v8安裝訓練與驗證方法概述

      聯系我們

      • 地址:武漢市東湖高新開發區光谷總部國際1棟2412室
      • QQ:932773931
      • 電話:027-59761089-806
      • 手機:13397158231
      • 郵箱:jevian_ma@worldflying.cn

      關注公眾號

      掃碼添加微信

      沃航(武漢)科技有限公司版權所有

      備案號:鄂ICP備16014230號-1

      本網站由提供CDN加速/云存儲服務

      国产精品综合日本欧美,日韩欧美国产精品第一页不卡,国产综合一区二区中文,久久久精品无码久久久久久
      <output id="lfp77"><del id="lfp77"></del></output>

      <meter id="lfp77"></meter>
      <ol id="lfp77"><strike id="lfp77"></strike></ol>

        <delect id="lfp77"><strike id="lfp77"></strike></delect>

        <track id="lfp77"></track>
        <delect id="lfp77"><ruby id="lfp77"></ruby></delect>

          <sub id="lfp77"></sub><meter id="lfp77"></meter>

          <em id="lfp77"></em>