本文编译自towards data science,作者是Dave Currie,该作者用TensorFlow搭建了一个拼写检查器,以用于处理自然语言处理(NLP)的数据。各位圈友一起看看,他是怎么做到的?
机器学习的一个最重大的问题就是,我们需要干净的数据。自然语言处理项目存在着一个问题——使用人类书写的文本。而不幸的是,我们并不擅长写作。想象一下,如果在Reddit上的有关帖子和评论的数据聚焦存在着许多拼写错误,这对于机器来说,是不是有些不好处理呢?
因此,制作一个拼写检查器将会是一个超级有价值的项目,这将有助于缓解这些问题。
我们即将用于此项目的模型与我在文章“亚马逊评论中的文本汇总”(都是seq2seq模型)中写的是很类似的,但是我添加了一些额外的代码行,以便可以使用grid search来调整整个架构和超参数,并且可以使用TensorBoard来分析结果。如果你想要更详细地演示如何在你的代码中添加TensorBoard,请查看“使用TensorFlow和TensorBoard预测Movie Review Sentiment”。
本文的着重点将在于如何为模型准备数据,同时我还将讨论该模型的一些其他功能。我们将在此项目中使用Python 3和TensorFlow 1.1。数据是由古腾堡项目中的二十本流行书籍组成。如果你有兴趣扩大这个项目以使其更准确,那么你可以在古腾堡项目上下载数百本图书。此外,如果看到人们使用这种模式制作出的拼写检查器是多么的好用,那将是超级有趣的。
如果你想要查看完整的代码,可以在GitHub页面查看:
https://github.com/Currie32/Spell-Checker
为了让你预览这个模型所具有的能力,这里有一些策划的例子可以当做参考:
-
Spellinis difficult,whchiswyhyou need to study everyday.
-
Spellingis difficult,whichiswhyyou need to study everyday.
-
The first days of her existence inthcountry werevreyhard for Dolly.
-
The first days of her existence inthecountry wereveryhard for Dolly.
-
Thiis really somethingimpressivthaatwe should look into right away!
-
Thisis really somethingimpressivethatwe 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'
', ' ', 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页面上找到它。但是,我认为值得向你展示输入数据中包含的字符:
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上使用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模型中去(它可以获得最先进的翻译结果)。
感谢你的阅读,希望你可以从中学到新的知识!
来源:Towards Data Science
作者:Dave Currie

















暂无评论内容