风机桨叶故障诊断(三)
识别桨叶——初步构建BP神经网络
新的一天,希望有好的运气。今天开始着手系统的第一个模块,从一幅图像中寻找到桨叶所在的位置。第一直觉我们的识别任务属于难度比较大,干扰因素多的了,所以我没有考虑先试一下Logistic回归之类的相对简单的算法,我准备先简单抽选一些样本,直接用BP神经网络模型快速实现一下,然后再观察我们的算法问题所在,逐步进行完善,包括修改模型,增加样本数量等等。说干就干!
之前我们已经得到了可以用来提取样本的风机图像库,我手动的从里面截取了一些样本,数了一下,正样本49个,负样本28个。然后将这些大小不一的正负样本统一缩放成20×20像素的小图像,下图所示的就是正样本最后处理完的结果:
准备工作结束后,开始动手编写一个最简单的三层BP神经网络了。
我们的输入是我们归一化后的20×20的图像,所以输入层的神经元个数为400。隐含层的神经元个数我最初选取的是25,输出层我选择的节点个数为2。用[1 0]代表正样本的期望输出,[0 1]代表负样本的期望输出。
具体实现我是参考的coursera上吴恩达老师的机器学习课程上的代码,那个神经网络的示例代码是用来训练手写体数字识别的,是个多分类问题。由于我是在那份代码上简单修改来的,所以这也是为什么我的输出层有两个神经元而不是一个的原因,我把问题看成了一个多分类问题。
经过了一上午的coding后,成功运行了。由于每次训练的结果都会不同,我多次运行并观察,训练样本的预测准确率在90%以上,测试集上的预测准确率在80%左右。看起来初步的结果还可以,不过我只有17个测试样本,再加上都是我手动选取的,有一定的主观因素,80%的准确率水分还是很大的。我们的初步实现的目的就是发现我们这个识别任务的难点,问题所在,只停留在得到一个准确率的数字是不行的,所以我意识到应该想办法要做一些误差分析。
让我们来看一下我们的算法都有哪些问题,到底将测试样本中的哪些类型的图像预测错误了,也就是算法在哪类物体上表现不好。于是我着手写了下面的函数,需要提供测试样本集矩阵(每一行=一个20×20图像展开后形成的向量),以及表示每个样本是否被预测正确(用0和1表示)的数组,再提供一个可视化时每个样本图像边长显示多少的参数,matlab代码如下:
function [ h, display_array ] = DisplayErrorTestExample( X, example_width,errorIndex ) %显示所有测试集并标记出有错误的测试集 %errorIndex标识测试集每个样本的预测是否有误,有误用1,正确用0表示 % Set example_width automatically if not passed in if ~exist('example_width', 'var') || isempty(example_width) example_width = round(sqrt(size(X, 2))); end % Gray Image colormap(gray); % Compute rows, cols [m n] = size(X); example_height = (n / example_width); % Compute number of items to display display_rows = floor(sqrt(m)); display_cols = ceil(m / display_rows); % Between images padding pad = 1; % Setup blank display display_array = ones(pad + display_rows * (example_height + pad), ... pad + display_cols * (example_width + pad)); % Copy each example into a patch on the display array curr_ex = 1; for j = 1:display_rows for i = 1:display_cols if curr_ex > m, break; end % Copy the patch % Get the max value of the patch max_val = max(abs(X(curr_ex, :))); display_array(pad + (j - 1) * (example_height + pad) + (1:example_height), ... pad + (i - 1) * (example_width + pad) + (1:example_width)) = ... reshape(X(curr_ex, :), example_height, example_width) / max_val; %如果是错误的测试样本,标红框 if errorIndex(curr_ex)==1 display_array(pad + (j - 1) * (example_height + pad) , pad + (i - 1) * (example_width + pad) + (0:example_width+1))=zeros(1,example_width+2); display_array(pad + j * (example_height + pad) , pad + (i - 1) * (example_width + pad) + (0:example_width+1))=zeros(1,example_width+2); display_array(pad + (j - 1) * (example_height + pad) + (0:example_height+1), pad + (i - 1) * (example_width + pad) ) = zeros(example_height+2,1); display_array(pad + (j - 1) * (example_height + pad) + (0:example_height+1), pad + i * (example_width + pad) ) = zeros(example_height+2,1); end curr_ex = curr_ex + 1; end if curr_ex > m, break; end end % Display Image h = imagesc(display_array, [-1 1]); % Do not show axis axis image off drawnow; end
将上面的方法添加到写好的代码中,重新运行,其中两次的结果如下(图像显示的是仅有的17个测试样本,被黑框圈起的是预测错误的样本):
可以看到,算法在负样本的预测中错误较多,我感觉这一方面与本身负样本就较少,一方面与负样本间差异很大,造成算法学习起来困难。我们重新思考下我们的系统,我们第一步是要从图像中识别出桨叶,如果识别错了,就会将不是桨叶的物体认为成桨叶进行下一步的故障诊断,那结果将是不能接受的。相反,如果我们对于一幅风机图像中的三个桨叶没有完全识别出来,却是无关紧要的,因为我们能在视频短时间的几帧之内获取到数张同一风机的图片,对于同一桨叶的识别我们有很多次的机会,所以我们算法对于正样本的漏识率可以相对较高,因为最终的识别率还是很高。
所以可以看出,对于负样本的错误预测,也就是假阳性的概率,是必须要降低的。先从算法入手,或许我们可以通过修改神经网络输出时的判断阈值,也就是只有极有把握输入为正样本时,才判为正样本,否则均判为负样本。这样做也就是人为选择牺牲了识别率,降低了误识率。一切想法的好坏以实际说话,马上修改代码重新运行,其中两次结果如下:
可以看到,出现假阳性的概率明显下降,同时漏识率比较明显的上升了,不过这是我们愿意看到的现象。说明这点改进对于算法的提升显著,后面算法的不断完善中也应当保留这种思想。
今天就到这里了,忙了一天,今天的进展着实不小。总结一下,首先简单的选取了少量的样本并进行样本归一化,这样就得到了可供训练的训练集和测试集。然后训练了400×25×2的三层BP神经网络,最后对最初步的模型进行了误差分析并找到了一种效果显著的提升方法!