前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在线手写识别的多卷积神经网络方法

在线手写识别的多卷积神经网络方法

作者头像
花落花飞去
发布2018-02-02 12:00:48
3.6K0
发布2018-02-02 12:00:48
举报
文章被收录于专栏:人工智能人工智能
  • 下载源码 - 2.2 MB
  • 下载源码 - 6.8 MB
  • 下载示例 - 159.2 KB
  • 下载数据lower_case_letter_v2.zip - 5.6 MB
  • 下载数据digit_v2.zip - 3.3 MB
  • 下载数据capital_letter_v2.zip - 5.6 MB

简介

本文所描述的研究主要关注在线手写体识别系统中的单词识别技术。该在线手写体识别系统使用多组件神经网络(multiple component neural networks, MCNN)作为分类器的可交换部分。作为一种新近的方法,该系统通过将手写文字分割成可单独识别的小片段(通常是字符)来进行识别。于是,识别结果便是每个已识别部分的组合。然后将这些组合词发送给单词识别模块作为输入,以便用一些字典搜索算法来从里面选择最好的一个。所提出的分类器克服了传统的分类器对大量字符类别进行分类时的障碍和困难。此外,所提出的分类器还具有可扩展的能力,可以通过添加或更改组件网络和内置字典的方法来动态地识别另外的字符类别。

引入

现在,触摸式用户接口(touch user interfaces, TUI)日益普及,将在人机交互中发挥重要作用。采用手指或笔进行输入的平板电脑、智能手机和TUI(触摸式用户接口)电脑正在成为许多人不可或缺的一部分。作为输入设备,手指或笔代替了传统的鼠标和键盘的许多功能。笔在鼠标上的一个主要优点是,笔是一种自然的书写工具,而鼠标在被当作书写工具时却是非常麻烦的。但是用笔作为输入时,需要将手写文本可靠地转换为可由计算机直接处理的编码,如ASCII(美国信息交换标准代码)。传统的转换模型通常包含一个从图像或输入屏幕中提取每个单词,并将其分成若干段的预处理步骤。神经网络分类器然后给出每段中每个可能字符的可能性。这些结果被后续的识别整个单词的特殊算法作为输入。近年来,手写字符识别的研究已经发展到可以商用的水平。然而,这种单个神经网络分类器的显著缺点是其在大型网络组织中以及在容量扩展中的复杂性。

在识别少数字符类别时,很容易建立一个可靠的、识别率高的神经网络。但是在识别大量字符类别时,就不那么容易了。较大的输入和输出增加了神经网络的层数以及神经元和连接的数目。于是,网络在训练过程中会遇到更多的困难,尤其是所训练网络的识别率会明显下降。此外,单个神经网络分类器只适用于特定的字符类别。在不重新构建或重新训练神经网络的情况下,这些单个神经网络分类器在识别附加的字符类别时,它们是不可交换且不可扩展的。

本文提出了一种基于多卷积神经网络(convolutional neural networks,CNNs)的在线手写字符识别系统。与传统的单神经网络分类器不同,新的神经网络分类器包含一系列识别率非常高的CNN部件。每个CNN部件只正确识别大量字符类别(数字,字母等)中的一部分。但是当用编程算法对这些网络进行组合时,通过简单地添加或移除CNN组件和语言词典,他们就可以创建一个灵活的、能够识别大量字符类别的分类器。<o:p>

卷积神经网络

卷积神经网络(CNN)是一种特殊的多层神经网络。像几乎所有其他的神经网络一样,它们是用反向传播算法的一种变体来进行训练的。他们之间不同的地方就是架构。卷积神经网络被设计成可以用最小的预处理直接从像素图像中识别视觉模式。他们可以识别极其易变的模式(例如手写字符),而且它们对失真以及简单的几何变换具有鲁棒性。

图1.  典型的卷积神经网络(LeNET 5)
图1. 典型的卷积神经网络(LeNET 5)

用于手写数字识别的卷积神经网络LeNET 5在MNIST数据集上获得了高达99%的可靠识别率。LeNET 5的输入层大小为32 x32,该层接收包含有要识别数字的灰度图像。像素强度被归一化到-1和+1之间。第一个隐藏层C1包含有六个特征图,每个特征图有25个权重,构成一个5x5的可训练核与偏差。将输入层与相应的核进行卷积,然后应用激活函数来计算特征图的值。将特征图的所有值限制为共享相同的可训练核或相同的权重值。由于边界效应,特征图的尺寸是28x28,比图像输入层的尺寸要小。<o:p>

每个卷积层之后是采样层,该层用因子2来降低各卷积层中特征图的尺寸。因此,隐藏层S2的二次采样图的尺寸为14×14。类似地,C3层具有16个尺寸为10×10的卷积图,S4层具有尺寸为5×5的16个采样图。这些层的功能与C1和S2层完全相同。S4层的特征图的尺寸是5x5,对于构建第三个卷积层而言太小了。这个神经网络的C1到S4层可以看作是一个可训练的特征提取器。然后,可训练的分类器以3个全连接层(通用分类器)的形式被添加到特征提取器之后。<o:p>

图2.  基于Dr. Partrice Simard模型的卷积网络
图2. 基于Dr. Partrice Simard模型的卷积网络

手写数字识别的另一个CNN模型是将卷积和二次采样过程集成到一个单一层中,同样可以使识别率达到99%以上。该模型以更高的分辨率提取简单的特征图,然后通过对图层进行二次采样,以较低的分辨率将其转换为更复杂的特征图。可训练核的宽度通常选择成以一个单位为中心(即为奇数大小),以便既可以有足够的重叠而不会丢失信息(3将会过小,因为只有一个单位重叠),也不会产生冗余计算(7个单位太大,用5个单位或者采用超过70%的重叠)。填充输入(使输入更大,从而使边界上的特征单元居中)并不会显着提高性能。无填充的、具有两个采样层和一个5x5大小可训练核的每个卷积层可将特征图大小从n减小到(n-3)/2。由于在这个模型中使用的初始MNIST输入大小是28×28,所以在卷积2层后还能产生整数的最近大小是29×29。经过两层卷积之后,5x5大小的特征对于第三层卷积来说太小了。这个神经网络的前两层可以看作是一个可训练的特征提取器。然后,可训练的分类器以2个全连接层(通用分类器)的形式被添加到特征提取器之后。

多分量神经网络分类器

对于对诸如数字或英文字母表(26个字符)等的少量字符类别进行识别时,卷积神经网络的识别率确实很高。然而,创建一个可以可靠地识别更大的字符集合(62个字符)的大型神经网络,仍是具有挑战性的任务。因为利用大量输入模式来训练神经网络需要更长的时间,寻找一个优化的和足够大的网络变得更加困难。此类网络收敛速度较慢,尤其是在书写不良的字符、相似的字符和易混淆的字符集合比较大的情况下,准确率有明显的下降。<o:p>

针对上述问题,这里提出的解决方案是用多个在自己的输出集合里拥有高识别率的小网络来代替一个单独的复杂神经网络。每个网络组件在原始官方输出集(数字,字母...)之外都有一个额外的未知输出(未知字符)。这就意味着,如果输入模式不被该网络识别成官方的输出字符,那么它将被解释为一个未知的字符。

图3. MCNN在线手写识别系统
图3. MCNN在线手写识别系统

分类器的字符识别模块是与输入模式一起工作的多个神经网络部件的集合。一个手写的单词是通过分割成独立的字符视觉模式来进行预处理的。然后,这些模式传递给所有的神经网络组件作为输入,每个神经网络组件将识别自身所拥有字符类别的可能性。因为在不同的类中有几个相似的字符,因此一个视觉模式可能被单个、数个或者全部的网络组件识别到。如果一个网络无法识别出与它自身拥有字符类别相似的可能字符,它将返回一个未知的字符(空字符)。字符识别模块的输出结果是可能单词的列表,它由所有识别出的可能单词组成,如在上面的例子中,输出结果是“Exper1,Expert,ExperJ,EXper1,EXpert,EXperJ”。未知字符(空字符)不会被用于组合单词。之后,系统将这些单词依次输入下一个单词识别模块,以选择最正确的单词作为整个分类器的输出。在这个例子中,“Expert”这个单词将会被选中。

图4.  MCNNs分类器模块的输出
图4. MCNNs分类器模块的输出

在字符识别模块中使用的单词组成算法:

全局变量:

  • charMatrix = List<List<Char>> {{E},{x,X},{p},{e},{r},{1,t,J}}//字符表
  • words = List <string> //单词组合的列表。
  • startIndex:默认为0
  • baseWord:默认为空
代码语言:txt
复制
void GetWords(int startIndex, String baseWord)
      {
          String newWord = "";
          if (startIndex == charMatrix.Count - 1)
          {
              for (int i = 0; i < charMatrix[startIndex].Count; i++)
              {
                  newWord = String.Format("{0}{1}", baseWord, charMatrix[startIndex][i].ToString());
                  words.Add(newWord);
              }
          }
          else
          {
              for (int i = 0; i < charMatrix[startIndex].Count; i++)
              {
                  newWord = String.Format("{0}{1}", baseWord, charMatrix[startIndex][i].ToString());
                  int newIndex = startIndex + 1;
                  GetWords(newIndex, newWord);
              }
          }
      }

事实上,单词识别模块是一个使用了数个词典搜索算法和文字修正技术的拼写检查器。这些算法与技术可以帮助获得最好的、有意义的单词。所有来自字符识别模块的可能单词都被依次提供给字典搜索模块。如果在内置字典中找到其中一个单词,它将成为分类器的输出单词。另外,在自动模式下一些文字修正技术将被应用于选择最正确的单词,在手动模式下则向用户显示近似单词的列表。其中一些技巧是:

  • 逐个交换每个字符,并在其位置尝试所有字符,看是否可以产生一个正确的单词。
代码语言:txt
复制
private bool ReplaceChars(String word, out String result)
      {
          result = "";
          bool isFoundWord = false;
          foreach (WordDictionary dictionary in Dictionaries)
          {
              ArrayList replacementChars = dictionary.ReplaceCharacters;
              for (int i = 0; i < replacementChars.Count; i++)
              {
                  int split = ((string)replacementChars[i]).IndexOf(' ');
                  string key = ((string)replacementChars[i]).Substring(0, split);
                  string replacement = ((string)replacementChars[i]).Substring(split + 1);
                  int pos = word.IndexOf(key);
                  while (pos > -1)
                  {
                      string tempWord = word.Substring(0, pos);
                      tempWord += replacement;
                      tempWord += word.Substring(pos + key.Length);
                      if (this.TestWord(tempWord))
                      {
                          result = tempWord.ToString();
                          isFoundWord = true;
                          return isFoundWord;
                      }
                      pos = word.IndexOf(key, pos + 1);
                  }
              }
          }
          return isFoundWord;
      }
  • 尝试逐个地交换相邻的字符。
代码语言:txt
复制
private bool SwapChar(String word, out String result)
     {
         result = "";
         bool isFoundWord = false;
         foreach (WordDictionary dictionary in Dictionaries)
         {
             for (int i = 0; i < word.Length - 1; i++)
             {
                 StringBuilder tempWord = new StringBuilder(word);
                 char swap = tempWord[i];
                 tempWord[i] = tempWord[i + 1];
                 tempWord[i + 1] = swap;
                 if (this.TestWord(tempWord.ToString()))
                 {
                     result = tempWord.ToString();
                     isFoundWord = true;
                     return isFoundWord;
                 }
             }
         }
         return isFoundWord;
     }
  • 尝试一次删除单词的一个字符。
  • 尝试在每个字母前插入一个新的字符。
代码语言:txt
复制
private bool ForgotChar(String word, out String result)
       {
           result = "";
           bool isFoundWord = false;
           foreach (WordDictionary dictionary in Dictionaries)
           {
               char[] tryme = dictionary.TryCharacters.ToCharArray();
               for (int i = 0; i <= word.Length; i++)
               {
                   for (int x = 0; x < tryme.Length; x++)
                   {
                       StringBuilder tempWord = new StringBuilder(word);
                       tempWord.Insert(i, tryme[x]);
                       if (this.TestWord(tempWord.ToString()))
                       {
                           result = tempWord.ToString();
                           isFoundWord = true;
                           return isFoundWord;
                       }
                   }
               }
           }
           return isFoundWord;
       }
  • 在每个字符之后都将字符串分为两部分。如果这两个部分都是正确的单词,那就把这两个单词作为一个建议项。
代码语言:txt
复制
private bool TwoWords(String word, out String result)
      {
          result = "";
          bool isFoundWord = false;
          for (int i = 1; i < word.Length - 1; i++)
          {
              string firstWord = word.Substring(0, i);
              string secondWord = word.Substring(i);
              if (this.TestWord(firstWord) && this.TestWord(secondWord))
              {
                  string tempWord = firstWord + " " + secondWord;
                  result = tempWord;
                  isFoundWord = true;
                  return isFoundWord;
              }
          }
          return isFoundWord;
      }

通过在拼写检查器中同时使用多个不同语言的字典,如果存在能够识别这些语言字符类别的神经网络部件,则所提出的分类器可以正确识别这些不同的语言。

代码语言:txt
复制
public NNTestingControl()
       {
           InitializeComponent();
           bitmap = null;
           networks = null;
           textSpellControl1.SpellChecker = this.multipleSpelling;
           //英语字典
           multipleSpelling.Dictionaries.Add(this.wordDictionary1);
           //法语字典
           //multipleSpelling.Dictionaries.Add(this.wordDictionary2);
           //意大利语字典
           //multipleSpelling.Dictionaries.Add(this.wordDictionary3);

       }

实验和结果

该演示使用三个CNN部件识别62个英文字符类别。它在我自己绘制的单词样本中可以得到较高的识别率。我希望这个项目可以帮助任何想要研究手写字符识别的人。目前我没有时间去继续该项目,但是我希望有人能把它开发成一个好的开源项目。这是我之前所有文章的完整源代码。这个项目的所有信息都可以在这里找到。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 引入
  • 卷积神经网络
  • 多分量神经网络分类器
  • 实验和结果
相关产品与服务
腾讯云小微
腾讯云小微,是一套腾讯云的智能服务系统,也是一个智能服务开放平台,接入小微的硬件可以快速具备听觉和视觉感知能力,帮助智能硬件厂商实现语音人机互动和音视频服务能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档