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

原文:Towards Data Science

作者:Dave Currie

来源:机器人圈

本文长度为2400字,建议阅读5分钟

本文教你用TensorFlow搭建拼写检查器,用于处理自然语言处理(NLP)的数据。

机器学习的一个最重要的问题就是,我们需要干净的数据。自然语言处理项目存在着一个问题——使用人类书写的文本。而不幸的是,我们并不擅长写作。想象一下,如果在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模型(https://code.facebook.com/posts/1978007565818999/a-novel-approach-to-neural-machine-translation/?utm_campaign=Artificial%2BIntelligence%2Band%2BDeep%2BLearning%2BWeekly&utm_medium=email&utm_source=Artificial_Intelligence_and_Deep_Learning_Weekly_13)中去(它可以获得最先进的翻译结果)。

感谢你的阅读,希望你可以从中学到新的知识!

原文发布于微信公众号 - 数据派THU(DatapiTHU)

原文发表时间:2017-05-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏恰童鞋骚年

Unity3D游戏开发初探—2.初步了解3D模型基础

  简而言之,3D模型就是三维的、立体的模型,D是英文Dimensions的缩写。

13730
来自专栏AI研习社

MIT Taco 项目:自动生成张量计算的优化代码,深度学习加速效果提高 100 倍

我们生活在大数据的时代,但在实际应用中,大多数数据是 “稀疏的”。例如,如果用一个庞大的表格表示亚马逊所有客户与其所有产品的对应映射关系,购买某个产品以 “1”...

378110
来自专栏生信技能树

如何通过Google来使用ggplot2可视化

今天是大年初二,这篇文章我只想传达一点: 没有什么菜鸟级别的生物信息学数据处理是不能通过Google得到解决方案的,如果有,请换个关键词继续Google! 第一...

34180
来自专栏机器人网

Python最有用的机器学习工具和库

Python是最好的编程语言之一,在科学计算中用途广泛:计算机视觉、人工智能、数学、天文等。它同样适用于机器学习也是意料之中的事。

15650
来自专栏人工智能头条

饿了么推荐系统:从0到1

31650
来自专栏携程技术中心

个性化推荐沙龙 | 饿了么推荐系统的从0到1(含视频)

本文来自陈一村在携程个性化推荐与人工智能Meetup上的分享。 陈一村 ,饿了么数据运营部资深算法工程师。2016年加入饿了么,现从事大数据挖掘和算法相关工作,...

46180
来自专栏PaddlePaddle

【AI核心技术】课程十九:神经图灵机—寻址

UAI与PaddlePaddle联合推出的【AI核心技术掌握】系列课程持续更新中!

9710
来自专栏PPV课数据科学社区

【学习】如何用SPSS和Clementine处理缺失值、离群值、极值?

一、什么是预处理、预分析? 高质量数据是数据分析的前提和分析结论可靠性的保障。尽管在获取数据源时数据分析师格外谨慎,耗费大量的时间,但数据质量仍然需持续关注。不...

1.1K50
来自专栏AI科技评论

开发 | MIT Taco项目:自动生成张量计算的优化代码,深度学习加速效果提高100倍

AI科技评论消息:我们生活在大数据的时代,但在实际应用中,大多数数据是“稀疏的”。例如,如果用一个庞大的表格表示亚马逊所有客户与其所有产品的对应映射关系,购买某...

388110
来自专栏大数据挖掘DT机器学习

Python 自然语言处理(NLP)工具库汇总

最近正在用nltk 对中文网络商品评论进行褒贬情感分类,计算评论的信息熵(entropy)、互信息(point mutual information)和困惑值(...

546120

扫码关注云+社区

领取腾讯云代金券