自然语言处理如何检查拼写错误?(Tensorflow实例教程&源代码)

机器学习的一个最重要的问题就是,我们需要干净的数据。自然语言处理项目存在着一个问题——使用人类书写的文本。而不幸的是,我们并不擅长写作。想象一下,如果在Reddit上的有关帖子和评论的数据集中存在着许多拼写错误,这对于机器来说,是不是有些不好处理呢?

因此,制作一个拼写检查器将会是一个非常有价值的项目,这将有助于缓解这些问题。

我们即将用于此项目的模型与我在文章“亚马逊评论中的文本汇总”(https://medium.com/towards-data-science/text-summarization-with-amazon-reviews-41801c2210b)(都是seq2seq模型)中写的是很相似的,但是我添加了一些额外的代码行,以便可以使用grid search来调整整个架构和超参数,并且可以使用TensorBoard来分析结果。如果你想要更详细地演示如何在你的代码中添加TensorBoard,请查看“使用TensorFlow和TensorBoard预测Movie Review Sentiment”(https://medium.com/@Currie32/predicting-movie-review-sentiment-with-tensorflow-and-tensorboard-53bf16af0acf)。

本文的着重点将在于如何为模型准备数据,同时我还将讨论该模型的一些其他功能。我们将在此项目中使用Python 3和TensorFlow 1.1。数据是由古腾堡项目中的二十本流行书籍组成。如果你有兴趣扩大这个项目以使其更准确,那么你可以在古腾堡项目上下载数百本图书。此外,如果看到人们使用这种模式制作出的拼写检查器是多么的好用,那将是非常有趣的。

如果你想要查看完整的代码,可以在GitHub页面查看:https://github.com/Currie32/Spell-Checker

为了让你预览这个模型所具有的能力,这里有一些策划的例子可以当做参考:

Spellin is difficult, whch is wyh you need to study everyday.

Spelling is difficult, which is why you need to study everyday.

The first days of her existence in th country were vrey hard for Dolly.

The first days of her existence in the country were very hard for Dolly.

Thi is really something impressiv thaat we should look into right away!

This is really something impressive that we should look into right away!

为了使事情更有条理,我把我们将使用的所有书籍放在他们自己的文件夹中,名称定为“books”。这是我们将用来加载所有书籍的函数:


def load_book(path):
   input_file = os.path.join(path)
   with open(input_file) as f:
       book = f.read()
   return book

同时,我们还需要为每本书定下一个唯一的文件名:


path = './books/'
book_files = [f for f in listdir(path) if isfile(join(path, f))]
book_files = book_files[1:]

当我们将这两个代码块放在一起时,我们将能够将所有书籍中的文本加载到列表中。


books = []
for book in book_files:
   books.append(load_book(path+book))

  

如果你有兴趣了解每本书中有多少单词,你可以使用以下代码行:


for i in range(len(books)):
   print("There are {} words in {}.".format(len(books[i].split()), book_files[i]))

注意:如果你的代码中不包括.split(),那么它将返回的是每本书中的字符数。

 

清理这些书的文本是相当简单的。由于我们将使用的是字符,而不是单词作为我们模型的输入,所以我们不需要担心去除停用词,或者将单词缩短到只留下主干。我们只需要删除我们不想要的字符和多余的空格。


def clean_text(text):
   '''Remove unwanted characters and extra spaces from the text'''
   text = re.sub(r'\n', ' ', text)
   text = re.sub(r'[{}@_*>()\\#%+=\[\]]','', text)
   text = re.sub('a0','', text)
   text = re.sub('\'92t','\'t', text)
   text = re.sub('\'92s','\'s', text)
   text = re.sub('\'92m','\'m', text)
   text = re.sub('\'92ll','\'ll', text)
   text = re.sub('\'91','', text)
   text = re.sub('\'92','', text)
   text = re.sub('\'93','', text)
   text = re.sub('\'94','', text)
   text = re.sub('\.','. ', text)
   text = re.sub('\!','! ', text)
   text = re.sub('\?','? ', text)
   text = re.sub(' +',' ', text) # Removes extra spaces
   return text

我将跳过如何来制作vocab_to_int和int_to_vocab字典,因为这是非常标准的东西,你可以在这个项目的GitHub页面(https://github.com/Currie32/Spell-Checker)上找到它。但是,我认为值得向你展示输入数据中包含的字符:


The vocabulary contains 78 characters.
[' ', '!', '"', '$', '&', "'", ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<EOS>', '<GO>', '<PAD>', '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

我们可以删除更多的特殊字符,或者使文本全部小写,但是我想让这个拼写检查器尽可能有用。

数据在被输入到模型之前被组织成句子。我们将在每个时间段后跟一个空格(“.”)来拆分数据。一个问题是,一些句子是以问号或感叹号结尾的,但我们说话的时候不是这样的。幸运的是,我们的模型仍然能够理解使用问号和感叹号,只要与以下句子相结合,不超过最大句子长度。

举个例子来说明这个问题:

Today is a lovely day. I want to go to the beach. (这将被拆分为两个输入句子)

Is today a lovely day? I want to go to the beach. (这将是一个长的输入句子)


sentences = []
for book in clean_books:
   for sentence in book.split('. '):
       sentences.append(sentence + '.')

我在floydhub.com(https://www.floydhub.com/)上使用GPU来训练我的模型(我强烈推荐他们的服务),这节省了我几个小时的训练时间。尽管如此,为了正确调整这个模型,运行迭代仍然需要30-60分钟的时间,这就是为什么我要限制数据,从而不需要花费更长的时间来做这件事情。这当然会降低我们的模型的准确性,但由于这只是一个个人项目,所以,我不是很在乎。


max_length = 92
min_length = 10


good_sentences = []


for sentence in int_sentences:
   if len(sentence) <= max_length and len(sentence) >= min_length:
       good_sentences.append(sentence)

为了跟踪这个模型的性能,我将把数据拆分成一个训练集和一个测试集。测试集将由数据15%的组成。


training, testing = train_test_split(good_sentences,
                                    test_size = 0.15,
                                    random_state = 2)

就像我最近的一些项目一样,我将按照长度来给数据进行排序。这导致一批量的句子具有相似的长度,因此只需要使用较少的填充,并且模型训练的速度将更快。


training_sorted = []
testing_sorted = []


for i in range(min_length, max_length+1):
   for sentence in training:
       if len(sentence) == i:
           training_sorted.append(sentence)
   for sentence in testing:
       if len(sentence) == i:
           testing_sorted.append(sentence)

也许这个项目最有趣/最重要的部分就是将句子转换为含有错误的句子的函数,这些函数将被用作输入数据。在这个函数中创建的错误的方式将以下面三种之一的一种进行:

两个字符的顺序将被交换(hlelo〜hello)

将添加一个额外的字母(heljlo〜hello)

其中一个字符没有被打印出来(helo〜hello)

这三个错误发生的可能性是相等的,任一个错误发生的可能性为5%。因此,平均而言,每20个字符中就会有一个包含一个错误。


letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m',
          'n','o','p','q','r','s','t','u','v','w','x','y','z',]


def noise_maker(sentence, threshold):
   
   noisy_sentence = []
   i = 0
   while i < len(sentence):
       random = np.random.uniform(0,1,1)
       if random < threshold:
           noisy_sentence.append(sentence[i])
       else:
           new_random = np.random.uniform(0,1,1)
           if new_random > 0.67:
               if i == (len(sentence) - 1):
                   continue
               else:
                   noisy_sentence.append(sentence[i+1])
                   noisy_sentence.append(sentence[i])
                   i += 1
           elif new_random < 0.33:
               random_letter = np.random.choice(letters, 1)[0]
               noisy_sentence.append(vocab_to_int[random_letter])
               noisy_sentence.append(sentence[i])
           else:
               pass    
       i += 1
   return noisy_sentence

在本文中,我想向你展示的最后一件事是如何创建批次。通常,在训练他们的模型之前,会先创建他们的输入数据,这意味着他们具有固定数量的训练数据。然而,当我们训练我们的模型时,通过将noise_maker应用于每个批次,我们将要创建新的输入数据。这意味着对于每个时期,目标(正确的)句子将通过noise_maker进行反馈,并应该接收一个新的输入句子。使用这种方法的话,我们略微夸张地说,将会有无数量的训练数据。


def get_batches(sentences, batch_size, threshold):
   
   for batch_i in range(0, len(sentences)//batch_size):
       start_i = batch_i * batch_size
       sentences_batch = sentences[start_i:start_i + batch_size]
       
       sentences_batch_noisy = []
       for sentence in sentences_batch:
           sentences_batch_noisy.append(
               noise_maker(sentence, threshold))
           
       sentences_batch_eos = []
       for sentence in sentences_batch:
           sentence.append(vocab_to_int['<EOS>'])
           sentences_batch_eos.append(sentence)
           
       pad_sentences_batch = np.array(
           pad_sentence_batch(sentences_batch_eos))
       pad_sentences_noisy_batch = np.array(
           pad_sentence_batch(sentences_batch_noisy))
       
       pad_sentences_lengths = []
       for sentence in pad_sentences_batch:
           pad_sentences_lengths.append(len(sentence))
       
       pad_sentences_noisy_lengths = []
       for sentence in pad_sentences_noisy_batch:
           pad_sentences_noisy_lengths.append(len(sentence))
       
       yield (pad_sentences_noisy_batch,
              pad_sentences_batch,
              pad_sentences_noisy_lengths,
              pad_sentences_lengths)

这就是整个这个项目!虽然结果是令人鼓舞的,但这种模式仍然存在着一定的局限性。我真的会很感激,如果有人可以扩大这个模型或改进其设计!如果你可以这样做,请在评论中发表一下。新设计的想法将会应用到Facebook AI实验室最新的CNN模型


原文发布时间为:2017-05-26 

本文作者:Dave Currie

本文来自合作伙伴“数据派THU”,了解相关信息可以关注“数据派THU”微信公众号

时间: 2024-10-31 07:32:55

自然语言处理如何检查拼写错误?(Tensorflow实例教程&源代码)的相关文章

(转)干货|这篇TensorFlow实例教程文章告诉你GANs为何引爆机器学习?(附源码)

干货|这篇TensorFlow实例教程文章告诉你GANs为何引爆机器学习?(附源码)   该博客来源自:https://mp.weixin.qq.com/s?__biz=MzA4NzE1NzYyMw==&mid=2247492203&idx=5&sn=3020c3a43bd4dd678782d8aa24996745&chksm=903f1c73a74895652ee688d070fd807771e3fe6a8947f77f3a15a44a65557da0313ac5ad59

Javascript实例教程(1) 目录

javascript|教程 javascript是一种基于对象和事件驱动并具有安全性能的脚本语言.使用它的目的是与HTML超文本标记语言.Java 脚本语言(Java小程序)一起实现在一个Web页面中链接多个对象,与Web客户交互作用,从而可以开发客户端的应用程序等.它是通过嵌入或调入在标准的HTML语言中实现的.它的出现弥补了HTML语言的缺陷,它是Java与HTML折衷的选择.本javascript实例教程旨在通过介绍一些实例来加深对Javascrip的理解.本系列教程包括:1. 利用jav

ASP实例教程:Drive对象

取得指定驱动器的可用空间数:本例演示如何首先创建一个FileSystemObject对象,然后使用AvailableSpace属性来获得指定驱动器的可用空间. 取得指定驱动器的可用空间数:本例演示如何首先创建一个FileSystemObject对象,然后使用AvailableSpace属性来获得指定驱动器的可用空间.取得指定驱动器的剩余空间容量:本例演示如何使用FreeSpace空间属性来取得指定驱动器的剩余空间.取得指定驱动器的总容量:本例演 Drive 对象 注意:本实例教程因为和具体空间的

ThinkPHP验证码和分页实例教程_php实例

本文实例讲述了ThinkPHP常用的两个功能:验证码与分页.在ThinkPHP的项目开发中非常常见,具有很高的实用价值.完整实例分享给大家,供大家参考.具体如下: 一.验证码: 导入验证码类,在aoli\ThinkPHP\Lib\ORG\Util\Image.class.php里有验证码方法    1.英文验证码: buildImageVerify($length,$mode,$type,$width,$height,$verifyName) 参数如下: length :验证码的长度,默认为 4

ThinkPHP中ajax使用实例教程_php实例

本文实例讲述了ThinkPHP中使用ajax的方法,提交表单如下图所示: 点击提交,不需要刷新本页,将内容提交到数据库当中,并在本页显示提交的内容.如下图所示: 一.jquery实现方法: MessageAction.class.php页面代码如下: <?php class MessageAction extends Action{ function index(){ $this->display(); } function add(){ //ajaxReturn(数据,'提示信息',状态)

Flash Switch Case实例教程

Flash Switch  Case实例教程 厌倦了无休止的,如果...别的吗? Flash MX中增添了一个新的方式处理这个问题:开关.这一指示中已经存在的一些语言(脚本语言, PHP的角.. ) ,并允许您表达和评价作为一种替代,如果/人. 该教程 事实上,交换机( )不会带来什么新的脚本.它只是一种方式,如果写/别人有所不同.其工作原理如下: 您确定您有什么评价之间的( )的开关. 之间的( )的开关,您将采取行动,履行在特定情况下,也就是说当表达的情况下被评价为严格平等的表达,你"交换&

Silverlight实例教程 - Validation数据验证DataAnnotation机制和调试技巧

Silverlight 4 Validation验证实例系列 Silverlight实例教程 - Validation数据验证开篇 Silverlight实例教程 - Validation数据验证基础属性和事件 Silverlight实例教程 - Validation数据验证DataAnnotation机制和调试技巧 Silverlight实例教程 - Validation客户端同步数据验证 Silverlight实例教程 - Validation服务器端异步数据验证 Silverlight实例

Silverlight实例教程 - Out of Browser与COM互操作实例

Silverlight 实例教程索引 Silverlight 实例教程 - Out of Browser开篇 Silverlight 实例教程 - Out of Browser配置,安装和卸载 Silverlight 实例教程 - Out of Browser的自定义应用 Silverlight 实例教程 - Out of Browser存取本地文件系统 Silverlight 实例教程 - Out of Browser与COM的交互基础 Silverlight 实例教程 - Out of Br

mysql Replace INTO 实例教程

mysql Replace INTO 实例教程 Replace INTO和INSERT INTO的区别,我和他说晚上上我的blog看吧,那时候还在忙,现在从MYSQL手册里找了点东西,MYSQL手册里说REPLACE INTO说的还是比较详细的. REPLACE的运行与INSERT很相像.只有一点除外,如果表中的一个旧记录与一个用于PRIMARY KEY或一个UNIQUE索引的新记录具有相同的值,则在新记录被插入之前,旧记录被删除.请参见13.2.4节,"INSERT语法". 注意,除