BP网络的代码分析

去年在学习Stanford的ML课程的时候整理过一篇BP神经网络原理的解析,链接地址,不过没有对它的code实现作太多的解读,只是用MATLAB的工具箱做了实验。

Jeremy Lin

具体的原理性资料可以参考:

[1] BP神经网络解析 http://blog.csdn.net/linj_m/article/details/9897839

[2] Tom M.Mitchell 机器学习教程  地址

BP网络算法流程:

从上面的算法流程可以看出来,BP神经网络的步骤并不多,如果你之前就了解BP神经网络,那么上面的流程应该还是可以很顺利地看懂的,如果之前没学习过,那么我推荐你好好参看我上面推荐的两个Ref。

BPNN的结构体

要开始具体编写BP神经网络(以下简称BPNN)的步骤之前,你需要定义好BPNN的结构,这里面用结构体实现,自然你完全可以用类实现。

typedef struct
{
  int input_n;                  // 网络输入个数
  int hidden_n;                 // 隐藏层单元个数
  int output_n;                 // 网络输出个数

  double *input_units;          // 网络输入单元
  double *hidden_units;         // 隐藏层单元
  double *output_units;         // 网络输出单元

  double *hidden_delta;         // 隐藏层单元error
  double *output_delta;         // 网络输出单元error

  double *target;               // 目标输出单元

  double **input_weights;       // 从输入层到隐藏层的权值向量
  double **hidden_weights;      // 从隐藏层到输出层的权值向量

                                // 冲量项
  double **input_prev_weights;  // 从输入层到隐藏层的冲量项向量
  double **hidden_prev_weights; // 从隐藏层到输出层的冲量项向量
} BPNN;

创建BPNN

在上面的算法流程中的第一步是创建n_in个输入,n_hidden个隐藏层单元,n_out个输出单元的网络。然后是初始化网络的权值为较小的随机数。

在讲如何创建网络之前,我更愿意谈谈如何生成随机数。嘿,那不是直接用rand()吗?Yes,当然是用这个函数,但是,我们可以花点时间谈下rand()是如何生成随机数。在Meitu实习的末尾,我突然想了解随机数到底是如何生成的。那时候我第一步是自然是Google了,结果搜索关键词不够cool,得到的结果自然是用rand()生成了,哈,可是我想要的是随机数生成的原理啊。当然,最后知道了,那就是用线性同余法生成随机数。大家如果如果有兴趣的话,可以参考我提供的Refs[3],具体我不想在这里说了。

[3] 计算机程序设计艺术(卷2)  Chapter 3 Random Numbers

现在进入原题:

BPNN *bpnn_create(int n_in, int n_hidden, int n_out)
{
  // 创建BPNN
  BPNN *newnet;
  newnet = bpnn_internal_create(n_in, n_hidden, n_out);

  // 初始化BPNN的网络权值
  bpnn_randomize_weights(newnet->input_weights, n_in, n_hidden);
  bpnn_randomize_weights(newnet->hidden_weights, n_hidden, n_out);
  // 初始化BPNN的冲量
  bpnn_zero_weights(newnet->input_prev_weights, n_in, n_hidden);
  bpnn_zero_weights(newnet->hidden_prev_weights, n_hidden, n_out);

  return (newnet);
}

在这里,我们主要使用bpnn_internal_create()函数创建BPNN:

BPNN *bpnn_internal_create(int n_in, int n_hidden, int n_out)
{
  BPNN *newnet;

  newnet = (BPNN *) malloc (sizeof (BPNN));
  if (newnet == NULL) {
    printf("BPNN_CREATE: Couldn't allocate neural network\n");
    return (NULL);
  }

  newnet->input_n = n_in;
  newnet->hidden_n = n_hidden;
  newnet->output_n = n_out;
  newnet->input_units = alloc_1d_dbl(n_in + 1);
  newnet->hidden_units = alloc_1d_dbl(n_hidden + 1);
  newnet->output_units = alloc_1d_dbl(n_out + 1);

  newnet->hidden_delta = alloc_1d_dbl(n_hidden + 1);
  newnet->output_delta = alloc_1d_dbl(n_out + 1);
  newnet->target = alloc_1d_dbl(n_out + 1);

  newnet->input_weights = alloc_2d_dbl(n_in + 1, n_hidden + 1);
  newnet->hidden_weights = alloc_2d_dbl(n_hidden + 1, n_out + 1);

  newnet->input_prev_weights = alloc_2d_dbl(n_in + 1, n_hidden + 1);
  newnet->hidden_prev_weights = alloc_2d_dbl(n_hidden + 1, n_out + 1);

  return (newnet);
}

在上面各个单元的内存分配主要是通过如下两个函数实现的:

// 一维数组空间分配
double *alloc_1d_dbl(int n)
{
  double *newA;

  newA = (double *) malloc ((unsigned) (n * sizeof (double)));
  if (newA == NULL)
  {
    printf("ALLOC_1D_DBL: Couldn't allocate array of doubles\n");
    return (NULL);
  }
  return (newA);
}

// 二维数组空间分配
double **alloc_2d_dbl(int m, int n)
{
  int i;
  double **newA2;

  newA2 = (double **) malloc ((unsigned) (m * sizeof (double *)));
  if (newA2 == NULL)
  {
    printf("ALLOC_2D_DBL: Couldn't allocate array of dbl ptrs\n");
    return (NULL);
  }

  for (i = 0; i < m; i++)
  {
    newA2[i] = alloc_1d_dbl(n);
  }
  return (newA2);
}

那么接下来就是BPNN的网络权值初始化了:

#define BIGRND 0x7fffffff

double drnd()
{
  return ((double) rand() / (double) BIGRND);
}

double dpn1()
{
  return ((drnd() * 2.0) - 1.0);
}
void bpnn_randomize_weights(double **w, int m, int n)
{
  int i, j;

  for (i = 0; i <= m; i++)
  {
    for (j = 0; j <= n; j++)
  {
      w[i][j] = dpn1();
    }
  }
}

BPNN的训练

好了,前戏已做,现在开始训练:

void bpnn_train(BPNN *net, double eta, double momentum, double *eo, double *eh)
{
  int in, hid, out;
  double out_err, hid_err;

  in  = net->input_n;
  hid = net->hidden_n;
  out = net->output_n;

  // 前向传播
  bpnn_layerforward(net->input_units, net->hidden_units,
      net->input_weights, in, hid);
  bpnn_layerforward(net->hidden_units, net->output_units,
      net->hidden_weights, hid, out);

  // 计算误差
  bpnn_output_error(net->output_delta, net->target, net->output_units,
      out, &out_err);
  bpnn_hidden_error(net->hidden_delta, hid, net->output_delta, out,
      net->hidden_weights, net->hidden_units, &hid_err);
  *eo = out_err;
  *eh = hid_err;

  // 调整权值向量
  bpnn_adjust_weights(net->output_delta, out, net->hidden_units, hid,
      net->hidden_weights, net->hidden_prev_weights, eta, momentum);
  bpnn_adjust_weights(net->hidden_delta, hid, net->input_units, in,
      net->input_weights, net->input_prev_weights, eta, momentum);
}

下面是上面各个函数的具体实现:

// 激活函数
double squash(double x)
{
  return (1.0 / (1.0 + exp(-x)));
}

// 前向函数
void bpnn_layerforward(double *l1,double *l2, double **conn, int n1, int n2)
{
  double sum;
  int j, k;

  // 设置偏置
  l1[0] = 1.0;

  for (j = 1; j <= n2; j++)
  {
    sum = 0.0;
    for (k = 0; k <= n1; k++)
    {
      sum += conn[k][j] * l1[k];
    }
    l2[j] = squash(sum);
  }
}

void bpnn_output_error(double *delta, double *target, double *output, int nj, double *err)
{
  int j;
  double o, t, errsum;

  errsum = 0.0;
  for (j = 1; j <= nj; j++)
  {
    o = output[j];
    t = target[j];
    delta[j] = o * (1.0 - o) * (t - o);
    errsum += ABS(delta[j]);
  }
  *err = errsum;
}

void bpnn_hidden_error(double *delta_h, int nh, double *delta_o, int no, double **who, double *hidden, double *err)
{
  int j, k;
  double h, sum, errsum;

  errsum = 0.0;
  for (j = 1; j <= nh; j++)
  {
    h = hidden[j];
    sum = 0.0;
    for (k = 1; k <= no; k++)
    {
      sum += delta_o[k] * who[j][k];
    }
    delta_h[j] = h * (1.0 - h) * sum;
    errsum += ABS(delta_h[j]);
  }
  *err = errsum;
}

void bpnn_adjust_weights(double *delta, int ndelta, double *ly, int nly, double **w,
           double **oldw, double eta,double momentum)
{
  double new_dw;
  int k, j;

  ly[0] = 1.0;
  for (j = 1; j <= ndelta; j++)
  {
    for (k = 0; k <= nly; k++)
    {
      new_dw = ((eta * delta[j] * ly[k]) + (momentum * oldw[k][j]));
      w[k][j] += new_dw;
      oldw[k][j] = new_dw;
    }
  }
}

void bpnn_feedforward(BPNN *net)
{
  int in, hid, out;

  in = net->input_n;
  hid = net->hidden_n;
  out = net->output_n;

  bpnn_layerforward(net->input_units, net->hidden_units,
      net->input_weights, in, hid);
  bpnn_layerforward(net->hidden_units, net->output_units,
      net->hidden_weights, hid, out);
}

到现在为止,BPNN的主要函数算是介绍完毕了。

不过,还有一个东西还没说,那就是所谓的终止条件。从推导BP的过程来说,我们很容易想到的一种终止条件自然是:直到对训练样例的误差E降低到某个预定义的阈值之下。但是,这其实并不是一个好的想法,因为BP算法容易过度拟合训练样例,降低对于其他实例的泛化精度。而为解决这个问题一个最常用的方法是在训练数据外再为算法提供一个验证数据集(validation dataset)。算法在使用训练数据集合驱动梯度下降搜索的同时,监视对于这个验证数据集合的误差。

本文地址:http://blog.csdn.net/linj_m/article/details/40679085

更加资源请 关注 博客:LinJM-机器视觉微博:林建民-机器视觉

时间: 2024-10-27 09:11:04

BP网络的代码分析的相关文章

浅识——后向投影成像算法(BackProjection,BP) 【MATLAB代码分析】

一.什么是BP算法 由来:BP算法最初是McCorkle受计算机层析技术的启发推导而来,所谓的计算机层析:就是CT(Computer Tomograpy),这是在医院中再普遍不过的技术了. BP算法的原理:BP算法参考了"时延-叠加"的思想,在雷达应用中,其对雷达接收天线接收到的回波信号进行距离向匹配率,获取回波数据中包含的相幅信息,再通过IFFT进行逆傅里叶变换,获取收发天线组合的时延,最后累积信号相干相加得到目标函数. 1.1  BP算法 回波信号与参与电信号进行匹配滤波后,获得的

[深度学习]Python/Theano实现逻辑回归网络的代码分析

首先PO上主要Python代码(2.7), 这个代码在Deep Learning上可以找到. 1 # allocate symbolic variables for the data 2 index = T.lscalar() # index to a [mini]batch 3 x = T.matrix('x') # the data is presented as rasterized images 4 y = T.ivector('y') # the labels are presente

BP网络中的反向传播

本文的主要参考:How the backpropagation algorithm works 下面是BP网络的参数结构示意图 首先定义第l层网络第j个神经元的输出(activation) 为了表示简便,令 则有alj=σ(zlj),其中σ是激活函数 定义网络的cost function,其中的n是训练样本的个数. 下面主要介绍使用反向传播来求取cost function相对于权重wij和偏置项bij的导数. 显然,当输入已知时,cost function只是权值w和偏置项b的函数.这里为了方便

20145234黄斐《网络对抗技术》实验四,恶意代码分析

恶意代码 概述 恶意代码是指故意编制或设置的.对网络或系统会产生威胁或潜在威胁的计算机代码.最常见的恶意代码有计算机病毒(简称病毒).特洛伊木马(简称木马).计算机蠕虫(简称蠕虫).后门.逻辑炸弹等. 特征: 恶意的目的,获取靶机权限.用户隐私等 本身是计算机程序,可以执行,并作用于靶机 通过执行发生作用,一般来说不运行是没问题的 恶意代码分析 在大多数情况下,进行恶意代码分析时,我们将只有恶意代码的可执行文件本身,而这些文件并不是我们人类可读的.为了了解这些文件的意义,你需要使用各种工具和技巧

20145326蔡馨熠《网络对抗》——恶意代码分析

20145326蔡馨熠<网络对抗>--恶意代码分析 1.实验后回答问题 (1)如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所以想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用什么方法来监控.. 需要监控什么? 系统中各种程序.文件的行为. 还需要注意是否会出现权限更改的行为. 注册表. 是否有可疑进程. 如果有网络连接的情况,需要注意这个过程中的IP地址与端口. 用什么来监控? 最先想到的肯定是使用wireshark抓包了,再进行进一步分析. Sysinternals

20145239杜文超《网络对抗技术》- 恶意代码分析

20145239杜文超<网络对抗技术>- 恶意代码分析 实验后回答问题 1.如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用什么方法来监控. (1)使用计划任务schtasks指令动态监控系统运行 (2)使用sysmon工具动态监控系统运行 2.如果已经确定是某个程序或进程有问题,你有什么工具可以进一步得到它的哪些信息. (1)使用systracer工具进行快照分析注册表信息.文件行为等信息的变化 (2)把程序放在vir

20145225唐振远《网络对抗》Exp4 恶意代码分析

20145225唐振远<网络对抗>Exp4 恶意代码分析 基础问题回答 如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用什么方法来监控. 使用Windows自带的schtasks指令设置一个计划任务,指定每隔一定时间记录主机的联网记录或者是端口开放.注册表信息等等: 通过sysmon工具,配置好想记录事件的文件,之后在事件查看器里找到相关日志文件便可以查看: 使用Process Explorer工具,监视进程执行情况,查

20145306张文锦《网络对抗》恶意代码分析

20145306张文锦<网络对抗>恶意代码分析 1.schtasks监控系统运行 用图形界面配置计划任务.首先为新建任务设置名字:设置触发器为两分钟执行一次:设置操作. 过程中遇到了问题,需要用跟高的权限来运行. 以最高权限运行计划任务.结果: 检测到的应用有微信,IE浏览器,UC浏览器,Apple服务等. 2.使用sysmon工具监控系统运行 sysmon使用的配置文件是老师给的配置文件. 安装sysmon. 在事件查看器中查看日志 查看详细信息: qq,微信,浏览器,等应用. 3.使用vi

2015306 白皎 《网络攻防》Exp4 恶意代码分析

2015306 白皎 <网络攻防>Exp4 恶意代码分析 netstat [Mac.Linux.Win] sysinteral [MS]:1 2 3 一.系统监控--Windows计划任务schtasks 1.创建计划任务,使系统每5分钟自动检测到哪些有哪些程序在连接我们的网络. 注:任务将创建于当前登录的用户名文件夹下. C:\schtasks /create /TN netstat /sc MINUTE /MO 5 /TR "cmd /c netstat -bn > c:\