径向基神经网络-算法原理与自实现
径向基
径向基-自实现代码
作者 : 老饼 日期 : 2022-06-09 04:48:41 更新 : 2022-06-29 01:27:30
本站原创文章,转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com


本文是笔者细扒matlab神经网络工具箱newrb的源码后,

去除冗余代码,重现的简版newrb代码,代码与newrb的结果完全一致。

通过本代码的学习,可以完全细节的了解径向基神经网络的实现逻辑。




  声 明  


本文不再讲述代码的算法原理,
对于算法的不理解,可以边看代码边参考《径向基-思想与原理》《径向基-详细算法流程》《OLS正交最小二乘算法》三张文章,
以上三张文章与代码不完全100%一致,但通过
借鉴文章中讲述的原理与思想,一定能理解代码的每一个细节的来由






  01. 代码结构说明  


本代码由3个主要函数和4个辅助构成。


testRbNet: 测试用例主函数,直接运行时就是执行该函数。


1、数据生成:生成一组训练数据和测试数据,
2、用自写的函数构建一个径向基神经网络,与用构建好的网络预测结果。
3、使用工具箱构建一个径向基神经网络。比较自写函数与工具箱预测结果是否一致


buildRbNet:构建一个径向基神经网络


使用OLS正交最小二乘法选择隐节点,直到误差满足要求。


predictRbNet:径向基神经网络的预测函数


通过传入网络权重、阈值和需要预测的X,返回网络的预测结果


4个辅助函数:用于辅助计算的小函数。


rbFcn:径向基函数
calDist:距离计算函数
findLargeColumn:找出最大的一列
solvelin2:最小二乘线性求解函数






  02. 代码运行结果解说  


运行后得到检验数据在自写函数、工具箱的预测结果,及两者是否一致的对比结果:




从运行结果可以看到,自写代码与工具箱的结果一样,说明扒出的逻辑与工具箱的一致。








  03. 具体代码  


matlab2014b亲测已跑通:


function testRbNet()
%本代码来自bp.bbbdata.com
%本代码模仿matlab神经网络工具箱的newrb神经网络,
%代码主旨用于教学,供大家学习理解newrb神经网络原理

%--------生成用于训练的输入输出数据-------------
%训练数据
x1 = 1:0.2:10;
x2 = -5:0.2:4;
X  = [x1;x2];     
Y  = [sin(X(1,:))+X(2,:);sin(X(1,:))+3*X(2,:)];
%测试数据
test_x=[2 3 ;5 6 ];

%-------------参数设置--------------
goal = 0.1;         % 目标误差
sp   = 2;           % 扩展系数spread
mn   = length(Y);   % 最大隐节点个数

%---------通过自写函数建立模型训练网络-------------
[w1,b1,w2,b2] = buildRbNet(X,Y,goal,sp,mn);
py      = predictRbNet( w1, b1, w2, b2, test_x);% 预测测试数据

%----------直接调用工具箱训练网络------------
net = newrb(X,Y,goal,sp,mn);
py_tool     = sim( net,test_x);                   % 预测测试数据

%------------结果对比-------------------------
testResult = isequal( py, py_tool);               % 比较预测结果是否一致
disp(['testResult = ',num2str(testResult)]);

% web('bp.bbbdata.com')
end

% 径向基神经网络预测函数
function y =predictRbNet(w1,b1,w2,b2,x)
hb = rbFcn( calDist( w1, x) .* repmat(b1,1,size(x,2))); % 计算隐节点激活值
y  = w2 * hb + repmat(b2,1,size(x,2));                  % 计算最终输出

end

% 构建径向基神经网络
function [w1,b1,w2,b2] = buildRbNet(X,Y,goal,spread,MN)
[~, Q]  = size(X);                             % 样本个数
b1      = sqrt(-log(.5))/spread;               % 生成b1
A       = rbFcn(calDist(X',X)*b1);             % 隐节点激活值

left = 1:Q;                                    % 初始化待选池
used = [];                                     % 初始化已用池
P    = A *diag(1./sqrt(sum(A.*A)));            % 初始化单位正交待选池:将隐节点激活值单位化

% 找出本次误差下降最快的列
Er   = diag(1./(sum(Y.*Y,2)))*((Y * P) .^ 2) ; % 误差下降占比
pick = findLargeColumn(Er);                    % 找出误差下降占比最大的列

% 将选出的列添加到已选池
newCol      = P(:,pick);                        % 备份本次选出的列
P(:,pick)   = [];                               % 删除本次选出的列
used        = [used left(pick)];                % 更新已用池
left(pick)  = [];                               % 更新待选池

% 更新网络
w1      = X(:,used)';                                      % 更新当前w1
a1      = rbFcn(calDist(w1,X)*b1);                         % 计算网络隐层激活值
[w2,b2] = solvelin2(a1,Y);                                 % 求解最小二乘下的w2,b2
a2      = w2*a1 + b2*ones(1,Q);                            % 计算网络最终输出
MSE     = sum(sum((Y-a2).*(Y-a2)))/(size(Y,1)*size(Y,2));  % 当前网络的误差

for k = 2:MN   
  %单位正交待选池与新加入的列进行正交,并单位化     
  P = P - newCol * newCol' * P / (newCol'*newCol);   % 正交
  P = P *diag(1./sqrt(sum(P.*P)));                   % 单位化
  
  Er   =  diag(1./(sum(Y.*Y,2)))*((Y * P) .^ 2) ;    % 计算误差下降占比
  pick = findLargeColumn(Er);                        % 找出误差下降占比最大的列
 
  % 将选出的列添加到已选池
  newCol     = P(:,pick);           % 备份本次选出的列
  P(:,pick)  = [];                  % 删除本次选出的列
  used       = [used left(pick)];   % 更新已用池
  left(pick) = [];                  % 更新待选池

  % 更新网络
  w1      = X(:,used)';                 % 更新当前w1
  a1      = rbFcn(calDist(w1,X)*b1);    % 计算网络隐层激活值
  [w2,b2] = solvelin2(a1,Y);            % 求解最小二乘下的w2,b2
  a2      = w2*a1 + b2*ones(1,Q);       % 计算网络最终输出
  MSE     = sum(sum((Y-a2).*(Y-a2)))/(size(Y,1)*size(Y,2));  % 当前网络的误差
 
  if (MSE < goal), break, end       %若误差满足要求,则退出循环
end

S1 = size(w1,1);
b1 = ones(S1,1)*b1;
end

% 找出误差下降占比最大的列
function i = findLargeColumn(m)
%找出m平方和最大的一列.
m(isnan(m)) = 0;
m           = sum(m .^ 2,1);    % 计算每列平方和
[~,i]       = max(m);           % 返回最大值的索引
end

% 求解最小二乘解
function [w,b] = solvelin2(p,t)
if nargout <= 1
  w= t/p;
else
  [pr,pc] = size(p);
  x = t/[p; ones(1,pc)];
  w = x(:,1:pr);
  b = x(:,pr+1);
end
end

% 计算a和b的欧氏距离
function dist = calDist(a,b)
%计算欧氏距离
ALen = size(a,1);
BLen = size(b,2);
dist = zeros(ALen,BLen);
for i = 1 : ALen
    for j = 1 :BLen
        dist(i,j) = sum((a(i,:)-b(:,j)').^2);
    end
end
dist = sqrt( dist);
end

%径向基函数
function y = rbFcn(x)
y = exp( -( x.*x));
end

代码其实可以写得更清晰,更简化,但为了跟matlab源码尽量保持一致,只能牺牲代码质量





  结束语  


阅读代码,调试代码,才能真正的理解算法的原理,希望本文能够帮助大家掌握径向基神经网络的Base算法原理.


对于径向基,老饼也没办法把它讲得非常清爽干脆,它的实现依赖了较多基础知识,

对于这些基础知识讲解的“度”在哪里,一百个读者有一百个度,老饼举棋不定。

老饼当前能做的,只有尽量把它讲好,但自认无法做到真的讲好了。









 End 







联系小饼