后向传播算法C语言实现

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#define PN 4
#define INPUT 2
#define HIDDEN 2
#define TARGET 1
#define OUTPUT 1


struct NN
{
  int ni;
  int nh;
  int no;
  double * ai;
  double * ah;
  double * ao;
  double * wi;
  double * wo;
  double * ci;
  double * co;
};


double rand(double a, double b)
{
  return (b - a) * rand() / (double)(RAND_MAX) + a;
}


double * makeMatrix(int i, int j, double fill)
{
  double * matrix = (double *) malloc (i * j * sizeof(double));
  int m, n;
  for (m = 0;m < i;++ m) {
    for (n = 0;n < j;++ n) {
      *(matrix + m * j + n) = fill;
    }
  }
  return matrix;
}


double sigmoid(double x) 
{
  return (exp(x) - exp(-x)) / (exp(x) + exp(-x));
}


double dsigmoid(double x)
{
  return 1.0 - x * x;
}


void init(NN * self, int ni, int nh, int no)
{
  srand(time(NULL));
  int i, j;
  self->ni = ni + 1;
  self->nh = nh;
  self->no = no;
  self->ai = (double *) malloc (sizeof(double) * self->ni);
  self->ah = (double *) malloc (sizeof(double) * self->nh);
  self->ao = (double *) malloc (sizeof(double) * self->no);
  for (i = 0;i < self->ni;++ i) {
    self->ai[i] = 1.0;
  }
  for (i = 0;i < self->nh;++ i) {
    self->ah[i] = 1.0;
  }
  for (i = 0;i < self->no;++ i) {
    self->ao[i] = 1.0;
  }
  self->wi = makeMatrix(self->ni, self->nh, 0);
  self->wo = makeMatrix(self->nh, self->no, 0);


  for (i = 0;i < self->ni;++ i) {
    for (j = 0;j < self->nh;++ j) {
      self->wi[i * self->nh + j] = rand(-0.2, 0.2);
    }
  }
  for (i = 0;i < self->nh;++ i) {
    for (j = 0;j < self->no;++ j) {
      self->wo[i * self->no + j] = rand(-2.0, 2.0);
    }
   }
  self->ci = makeMatrix(self->ni, self->nh, 0);
  self->co = makeMatrix(self->nh, self->no, 0);
}


double * update(NN * self, double *inputs)
{
  if (INPUT != self->ni - 1) {
    printf("wrong number of inputs\n");
  }
  
  int i, j, k;
  for (i = 0;i < self->ni - 1;++ i) {
    self->ai[i] = inputs[i];
  }


  for (j = 0;j < self->nh;++ j) {
    double sum = 0;
    for (i = 0;i < self->ni;++ i) {
      sum += self->ai[i] * self->wi[i * self->nh + j];
    }
    self->ah[j] = sigmoid(sum);
  }


  for (k = 0;k < self->no;++ k) {
    double sum = 0;
    for (j = 0;j < self->nh;++ j) {
      sum += self->ah[j] * self->wo[j * self->no + k];
    }
    self->ao[k] = sigmoid(sum);
  }
  double *output = (double *) malloc (sizeof(double) * self->no);
  for (i = 0;i < self->no;++ i) {
    *(output + i) = self->ao[i];
  }
  return output;
}


double backprobagation(NN * self, double *targets, double N, double M)
{
  if (TARGET != self->no) {
    printf("wrong number of target values\n");
  }
  
  int i, j, k;
  double *output_deltas = (double *) calloc (self->no, sizeof(double));
  for (k  = 0;k < self->no;++ k) {
    double error = targets[k] - self->ao[k];
    output_deltas[k] = dsigmoid(self->ao[k]) * error;
  }
  
  double *hidden_deltas = (double *) calloc (self->nh, sizeof(double));
  for (j = 0;j < self->nh;++ j) {
    double error = 0;
    for (k = 0;k < self->no;++ k) {
      error += output_deltas[k] * self->wo[j * self->no + k];
    }
    hidden_deltas[j] = dsigmoid(self->ah[j]) * error;
  }
  
  for (j = 0;j < self->nh;++ j) {
    for (k = 0;k < self->no;++ k) {
      double change = output_deltas[k] * self->ah[j];
      self->wo[j * self->no + k] += (N * change + M * self->co[j * self->no + k]);
      self->co[j * self->no + k] = change;
    }
  }


  for (i = 0;i < self->ni;++ i) {
    for (j = 0;j < self->nh;++ j) {
      double change = hidden_deltas[j] * self->ai[i];
      self->wi[i * self->nh + j] += (N * change + M * self->ci[i * self->nh + j]);
      self->ci[i * self->nh + j] = change;
    }
  }


  double error = 0;
  for (k = 0;k < TARGET;++ k) {
    error += 0.5 * (targets[k] - self->ao[k]) * (targets[k] - self->ao[k]);
  }
  return error;
}


void test(NN * self, double *inputs, double *targets)
{
  int i, j, k;
  for (i = 0;i <  PN;++ i) {
    double * input = (double *) malloc (sizeof(double) * INPUT);
    for (j = 0;j < INPUT;++ j) {
      input[j] = inputs[i * INPUT + j];
      printf("%lf ", input[j]);
    }
    update(self, input);
    for (k = 0;k < self->no;++ k) {
      printf("%lf ", self->ao[k]);
    }
    printf("\n");
  }
}


void train(NN * self, double *inputs, double *targets, int iteration, double N, double M)
{
  int i, j, k, p;
  for (i = 0;i < iteration;++ i) {
    double error = 0;
    for (p = 0;p < PN;++ p) {
      double *input = (double *) malloc (sizeof(double) * INPUT);
      double *target = (double *) malloc (sizeof(double) * TARGET);
      for (j = 0;j < INPUT;++ j) {
*(input + j) = inputs[p * INPUT + j];
      }
      for (k = 0;k < TARGET;++ k) {
*(target + k) = targets[p * TARGET + k];
      }
      update(self, input);
      error += backprobagation(self, target, N, M);
    }
    if (i % 100 == 0) {
      printf("error %-.5lf\n", error);
    }
  }
}


int main()
{
  double inputs[PN * INPUT] = {0, 0, 0, 1, 1, 0, 1, 1};
  double targets[PN * TARGET] = {0, 1, 1, 0};
  NN * self = (NN *) calloc (1, sizeof(NN));


  init(self, 2, 2, 1);
  train(self, inputs, targets, 5000, 0.5, 0.1);
  test(self, inputs, targets);


  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值