65.9K
CodeProject 正在变化。 阅读更多。
Home

大型模式识别系统使用多神经网络

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (71投票s)

2012年5月2日

CPOL

7分钟阅读

viewsIcon

230961

downloadIcon

253448

使用多神经网络进行大型模式识别系统、手写识别系统的教程

介绍 

如今,人工神经网络已广泛应用于人类生活的许多领域。然而,为像手写识别系统这样的大型分类器创建高效的网络仍然是科学家们面临的一大挑战。在我上一篇文章《使用 UNIPEN 数据库的在线手写识别系统库》中,我介绍了一个高效的手写识别系统库,可以轻松创建和修改神经网络。演示程序在数字集(97%)和字母集(93%)上取得了良好的识别结果。本文我将继续为大型模式分类,特别是手写识别提供解决方案。

 

使用附加的拼写检查模块可显著提高识别率 

识别系统的神经网络  

在传统的模式识别模型中,手工设计的特征提取器从输入中收集相关信息并消除不相关的变量。然后,训练分类器(通常可以使用标准的全连接多层神经网络作为分类器)将结果特征向量分类。然而,这可能存在一些影响识别结果的问题。卷积神经网络(CNN)解决了传统模型这一缺点,在模式识别任务上取得了最佳性能。

CNN 是一种特殊形式的多层神经网络。与其他网络一样,CNN 通过反向传播算法进行训练。区别在于其架构内部。卷积网络结合了三种架构思想,以确保一定程度的位移、缩放和畸变不变性:局部感受野、权值共享(或权值复制)以及空间或时间子采样。它们专门设计用于直接从数字图像中识别模式,并最大程度地减少预处理操作。CNN 的架构细节已在 Dr. Yahn LeCun 和 Dr. Patrice Simard 的文章(请参阅我之前的文章)中进行了全面描述。

 

图 1:LeNET 5 的架构

2:输入图像后面跟着一个执行 5 × 5 卷积的特征图和一个 2 x 2 子采样图 

上述网络对于数字、大写字母或小写字母等小型模式集合的识别结果确实很高。然而,当我们想要创建一个更大的神经网络来识别更大的集合(例如,数字和英文字母(62 个字符))时,问题就开始出现。找到一个足够大的优化网络变得更加困难,使用大型输入模式训练网络需要更长的时间。网络的收敛速度变慢,尤其是准确率显著下降,因为大的错误书写的字符,许多相似且易混淆的字符等等。此外,假设我们可以创建一个足够好的网络来准确识别英文字母,但它肯定无法正确识别其输出集之外的特殊字符(俄语或中文)因为没有扩展能力。因此,创建一个用于非常大的模式分类器的唯一网络非常困难,甚至可能不可能。

针对上述问题的解决方案是:与其使用一个大型的唯一网络,不如使用多个小型网络,这些网络对它们各自的输出集具有非常高的识别率。除了官方输出集(数字、字母等)之外,这些网络还有一个额外的未知输出(未知字符)。这意味着,如果输入模式未被识别为官方输出集中的字符,它将被理解为未知字符。然后,输入模式将被传递到下一个网络,直到系统能够正确识别它。 

 

图 3:具有未知输出的卷积神经网络

 

图 4:使用多神经网络的识别系统

该解决方案克服了传统模型的几乎所有限制。新系统包含多个小型网络,易于优化以获得最佳识别结果。训练这些小型网络比训练一个巨大的网络花费的时间更少。特别是,新模型非常灵活且可扩展。根据要求,我们可以加载一个或多个网络;我们也可以向系统中添加新网络来识别新模式,而无需更改或重建模型。所有这些小型网络都具有可重用性,可用于其他多神经网络系统。 

实验 

演示程序旨在展示识别系统的所有阶段,包括:创建组件网络、训练网络、在 UNIPEN 数据集上测试网络以及在鼠标绘图控件上测试网络。它是一个教程,可以帮助每个人理解识别系统。所有功能都可以在程序 GUI 上实现。因此,您可以在运行时创建、训练和测试您的网络,而无需更改任何代码或重新启动程序。

 

图 5:手写识别系统界面 

创建新神经网络 

6:创建新神经网络界面

创建新神经网络完全基于 GUI。创建网络取决于输入模式大小、层数、数据集等。在输出层,我们可以选择“未知输出”复选框为网络创建一个额外的未知输出,或者忽略它来创建一个普通网络。

当然,我们仍然可以通过代码创建网络

         void CreateNetwork()
<pre>        {
            network = new ConvolutionNetwork();
            //layer 0: inputlayer
            network.Layers = new Layer[6];
            network.LayerCount = 6;
            InputLayer inputlayer = new InputLayer("00-Layer Input", new Size(29, 29));
            network.InputDesignedPatternSize = new Size(29, 29);
            inputlayer.Initialize();
            network.Layers[0] = inputlayer;
            ConvolutionLayer convlayer = new ConvolutionLayer("01-Layer ConvolutionalSubsampling", inputlayer, new Size(13, 13), 10, 5);
            convlayer.Initialize();
            network.Layers[1] = convlayer;
            convlayer = new ConvolutionLayer("02-Layer ConvolutionalSubsampling", convlayer, new Size(5, 5), 60, 5);
            convlayer.Initialize();
            network.Layers[2] = convlayer;
            FullConnectedLayer fulllayer = new FullConnectedLayer("03-Layer FullConnected", convlayer, 200);
            fulllayer.Initialize();
            network.Layers[3] = fulllayer;
            fulllayer = new FullConnectedLayer("04-Layer FullConnected", fulllayer, 100);
            fulllayer.Initialize();
            network.Layers[4] = fulllayer;
            OutputLayer outputlayer = new OutputLayer("05-Layer Output", fulllayer, Letters3.Count, true);
            outputlayer.Initialize();
            network.Layers[5] = outputlayer;
            network.TagetOutputs = Letters3;
            network.UnknownOuput = '?';
        }  

训练网络

 使用“创建网络”功能创建神经网络后,将使用 UNIPEN 数据库训练该网络。

 

图 7:训练网络界面

根据网络大小,我们可以选择 UNIPENdata 文件夹中的训练集 1a、1b 或 1c。训练过程的统计信息可以显示许多有用的信息,例如:训练轮数、均方误差、每轮训练时间、成功率……

UNIPEN 数据浏览器和识别测试

演示程序中的 UNIPEN 数据浏览器控件可以显示所有 UNIPEN 数据文件。我们还可以通过加载训练好的网络参数文件来测试这些文件上训练好的神经网络。 

图 8:UNIPEN 数据浏览器和识别界面

 鼠标绘图测试

 

图 9:鼠标绘图识别界面

鼠标绘图控件基于 Alex Fr 精彩的文章《DrawTools》。我只修改了一些代码以适应我的需求。图像中的连写文本通过以下相同算法分为行、词和独立字符:

    private void btRecognition_Click(object sender, EventArgs e)
<pre>        {
            //recognition all characters in the drawArea
            if (bitmap != null)
            {
                bitmap.Dispose();
                bitmap = null;
            }
            bitmap = new Bitmap(drawArea.Width, drawArea.Height);
            drawArea.DrawToBitmap(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
            drawBitmap =(Bitmap) bitmap.Clone();
            if (bitmap != null)
            {
                lbRecognizedText.Items.Clear();
                List<InputPattern> lineList=null;
                List<InputPattern> wordList=null;  
                InputPattern parentPt=new InputPattern(bitmap,255,new Rectangle(0,0,bitmap.Width,bitmap.Height));
                lineList = GetPatternsFromBitmap(parentPt,500,1,true,10,10);
                if (lineList.Count > 0)
                {
                        
                    if (characterList != null)
                    {
                        characterList.Clear();
                        characterList = null;
                    }
                    characterList = new List<InputPattern>();
                    foreach (var line in lineList)
                    {
                        String text = "";
                        wordList = GetPatternsFromBitmap(line, 50, 10,false, 10, 10);
                        if (wordList != null)
                        {
                            if (wordList.Count > 0)
                            {
                                foreach (var word in wordList)
                                {
                                    List<InputPattern> charList = GetPatternsFromBitmap(word, 5, 5, false, 10, 10);
                                    //check if have part bitmaps
                                    if (charList != null)
                                    {
                                        if (charList.Count > 0)
                                        {
                                            panelNavigation.Visible = true;
                                            foreach (var c in charList)
                                            {
                                                characterList.Add(c);
                                                c.GetPatternBoundaries(5,5,false,10,10);
                                                Char accChar = new Char();
                                                PatternRecognition(c.OriginalBmp,out accChar);
                                                if (accChar != '\0')
                                                {
                                                    text = String.Format("{0}{1}", text, accChar.ToString());
                                                    drawBitmap = c.DrawChildPatternBoundaries(drawBitmap);
                                                }
                                            }
                                        }
                                    }
                                    text = String.Format("{0} ", text);
                                }
                                  
                            }
                        }
                        lbRecognizedText.Items.Add(text);
                    }
                }
                pbPreview.Image = drawBitmap;
                lblNavigation.Text = characterList.Count.ToString();
                index = 0;
            }
            
        }  

 

图 10:加载训练好的网络参数文件 

为了激活识别功能,我只需加载训练好的网络参数文件。根据我的识别需求,我可以加载一个、两个或所有文件。当只加载一个网络来识别其输出字符时,识别结果非常好(高于 90%)。然而,当我加载多个网络时,系统的准确率会降低。主要原因是连写文本中有许多易混淆的字符;训练集不够大等。

对于手写字符等大型模式集合,存在许多相似的字符,这些字符有时会让人类感到困惑,例如:O、0 和 o;9、4、g、q 等。这些字符可能导致网络识别错误。因此,该解决方案正在升级,通过在系统输出端使用附加的拼写检查器/投票模块来显著提高识别率。输入模式将由所有组件网络识别。然后,这些输出(未知输出除外)将作为拼写检查器/投票模块的输入。该模块将基于先前识别的字符、内部字典和其他因素来决定哪个是识别最准确的字符。

图 11:使用拼写检查器/投票模块的新识别系统

使用拼写检查器/投票模块(内部字典)的新识别系统

拼写检查器模块使系统识别效果大大提高  

结论 

提出的识别模型解决了大型识别系统的大部分问题:能够识别大型模式集合、灵活的设计和部署、可扩展性和可重用性……等等。还可以通过提高组件网络的识别率、使用拼写检查器/投票模块等来轻松提高系统的准确率。演示程序也证明了该库的能力,该库可用于许多其他应用,如预测应用、人脸识别……

未来工作和升级 

一些功能将被更新到库中

- LeNET 模型的卷积和采样层。

- 拼写检查器/投票模块 

- 字符分割。

目前,该项目占用了我太多的空闲时间。它应该放缓或暂时停止,直到我可以重新安排一切和/或找到新的良好赞助。但是,文章的投票/评论将决定项目是否继续。我将非常感谢收到有关该文章的评论和建议,特别是关于模型、拼写检查器模块和字符分割算法的建议……

 历史

版本 1.0:初始代码

版本 1.1 将拼写检查器/投票模块添加到系统中,显著提高了识别率。这让我感到非常惊喜和高兴。我将在完成代码重排后发布它。

© . All rights reserved.