都说《机器学习》是学计算机的人必须要看的一本书,确实不是浪得虚名。看了一章人工神经网络(ANN)中关于反向传播算法的内容,相比单个感知器而言,采用多层网络的反向传播算法能表示出更多种类的非线性曲面,下面总结下它基本的处理框架。
ANN核心数据结构:
{
int input_n; /* number of input units */
int hidden_n; /* number of hidden units */
int output_n; /* number of output units */
double * input_units; /* the input units */
double * hidden_units; /* the hidden units */
double * output_units; /* the output units */
double * hidden_delta; /* storage for hidden unit error */
double * output_delta; /* storage for output unit error */
double * target; /* storage for target vector */
double ** input_weights; /* weights from input to hidden layer */
double ** hidden_weights; /* weights from hidden to output layer */
/* ** The next two are for momentum ** */
double ** input_prev_weights; /* previous change on input to hidden wgt */
double ** hidden_prev_weights; /* previous change on hidden to output wgt */
} BPNN;
整个神经网络可以分成三层:输入层,隐藏层,输出层,通过加权线性变换,层与层之间的传递,最终得到输入层的实数值。
{ // 创建人工网络,参数分别指定输入层,隐藏层和输出层大小
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);
}
下面代码段是ANN运行的核心部分:
{ // 提供了训练集
printf( " Creating new network '%s'/n " , netname);
iimg = trainlist -> list[ 0 ]; // 指向训练集第一张图片
imgsize = ROWS(iimg) * COLS(iimg);
/* bthom ===========================
make a net with:
imgsize inputs, 4 hiden units, and 1 output unit
*/
// 输入层为图片大小,隐藏层为,输出层为
net = bpnn_create(imgsize, 4 , 1 );
}
// 训练
/* ************* Train it **************************** */
for (epoch = 1 ; epoch <= epochs; epoch ++ )
{
printf( " %d " , epoch); fflush(stdout);
sumerr = 0.0 ;
for (i = 0 ; i < train_n; i ++ )
{
/* * Set up input units on net with image i * */
// 为图像i在网络上建立输入单元
load_input_with_image(trainlist -> list[i], net);
/* * Set up target vector for image i * */
// 为图像i建立目标向量
load_target(trainlist -> list[i], net);
/* * Run backprop, learning rate 0.3, momentum 0.3 * */
// 学习速率.3,冲量.3
bpnn_train(net, 0.3 , 0.3 , & out_err, & hid_err);
sumerr += (out_err + hid_err);
}
进行性能评估:
for (i = 0 ; i < n; i ++ )
{
/* ** Load the image into the input layer. * */
load_input_with_image(il -> list[i], net); // 加载图片到输入层中
/* ** Run the net on this input. * */
bpnn_feedforward(net); // 在当前输入上运行神经网络
/* ** Set up the target vector for this image. * */
load_target(il -> list[i], net); // 为此图片建立目标向量
/* ** See if it got it right. ** */
if (evaluate_performance(net, & val, 0 ))
{ // 判断是否正确识别,
correct ++ ;
}
else if (list_errors)
{
printf( " %s - outputs " , NAME(il -> list[i]));
for (j = 1 ; j <= net -> output_n; j ++ )
{
printf( " %.3f " , net -> output_units[j]);
}
putchar( ' /n ' );
}
err += val;
}
err = err / ( double ) n;
if ( ! list_errors)
/* bthom==================================
this line prints part of the ouput line
discussed in section 3.1.2 of homework
*/
printf( " %g %g " , (( double ) correct / ( double ) n) * 100.0 , err);
用到的性能评估函数:
evaluate_performance(BPNN *net, double *err)
{//性能评估
double delta;
delta = net->target[1] - net->output_units[1];
*err = (0.5 * delta * delta);
/*** If the target unit is on... ***/
if (net->target[1] > 0.5)
{
/*** If the output unit is on, then we correctly recognized me! ***/
if (net->output_units[1] > 0.5)
{
return (1);
}
else
{
return (0);
}
/*** Else, the target unit is off... ***/
}
else
{
/*** If the output unit is on, then we mistakenly thought it was me ***/
if (net->output_units[1] > 0.5)
{
return (0);
/*** else, we correctly realized that it wasn't me ***/
}
else
{
return (1);
}
}
}
辅助处理函数区:
{ // 输入图像
double * units;
int nr, nc, imgsize, i, j, k;
nr = ROWS(img); // 行大小
nc = COLS(img); // 列大小
imgsize = nr * nc;;
if (imgsize != net -> input_n)
{ // 确保输入单元数目设置为图片大小
printf( " LOAD_INPUT_WITH_IMAGE: This image has %d pixels,/n " , imgsize);
printf( " but your net has %d input units. I give up./n " , net -> input_n);
exit ( - 1 );
}
// 取图片的每个像素为输入单元
units = net -> input_units;
k = 1 ;
for (i = 0 ; i < nr; i ++ )
{
for (j = 0 ; j < nc; j ++ )
{
units[k] = (( double ) img_getpixel(img, i, j)) / 255.0 ;
k ++ ;
}
}
}
load_target(IMAGE * img, BPNN * net)
{ // 加载目标值
int scale;
char userid[ 40 ], head[ 40 ], expression[ 40 ], eyes[ 40 ], photo[ 40 ];
userid[ 0 ] = head[ 0 ] = expression[ 0 ] = eyes[ 0 ] = photo[ 0 ] = ' /0 ' ;
/* ** scan in the image features ** */
sscanf(NAME(img), " %[^_]_%[^_]_%[^_]_%[^_]_%d.%[^_] " ,
userid, head, expression, eyes, & scale, photo);
if ( ! strcmp(userid, " glickman " ))
{
net -> target[ 1 ] = TARGET_HIGH; /* it's me, set target to HIGH */
}
else
{
net -> target[ 1 ] = TARGET_LOW; /* not me, set it to LOW */
}
}
void bpnn_train(BPNN * net, double eta, momentum * eo, momentum * eh)
{ // 人工神经网络训练
int in , hid, out ;
double out_err, hid_err;
in = net -> input_n;
hid = net -> hidden_n;
out = net -> output_n;
/* ** Feed forward input activations. ** */
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 );
/* ** Compute error on output and hidden units. ** */
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;
/* ** Adjust input and hidden weights. ** */
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);
}
void bpnn_feedforward(BPNN * net)
{ // 前向反馈
int in , hid, out ;
in = net -> input_n; // 输入层大小
hid = net -> hidden_n; // 隐藏层大小
out = net -> output_n; // 输出层大小
/* ** Feed forward input activations. ** */
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 );
}
void bpnn_adjust_weights( double * delta, double * ly, 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_layerforward( double * l1, double * l2, double ** conn, int n1, int n2)
{ // 层次前向输入
double sum;
int j, k;
/* ** Set up thresholding unit ** */
l1[ 0 ] = 1.0 ;
// 加权线性变换
/* ** For each unit in second layer ** */
for (j = 1 ; j <= n2; j ++ )
{
/* ** Compute weighted sum of its inputs ** */
sum = 0.0 ;
for (k = 0 ; k <= n1; k ++ )
{
sum += conn[k][j] * l1[k];
}
l2[j] = squash(sum);
}
}