91精品国产综合久久四虎久久_国产成人午夜高潮毛片_99er视频精品免费观看_2020亚洲熟女在线观看_日本女优人体写真_国内黄色毛片_年轻的老师中文版在线_丰满女邻居做爰_久久久久久精品成人免费图片

比較兩個(gè)生產(chǎn)級(jí)NLP庫(kù):訓(xùn)練Spark-NLP和spaCy的管道
一篇詳細(xì)步驟的指導(dǎo),從初始化庫(kù)開始,到加載數(shù)據(jù)并使用Spark-NLP和spaCy來(lái)訓(xùn)練一個(gè)分詞器模型。

本系列博客的目地是通過(guò)使用兩個(gè)領(lǐng)先的生產(chǎn)級(jí)語(yǔ)言處理庫(kù)(John Snow Labs的Apache Spark NLPExplosion AI的spaCy)來(lái)處理真實(shí)的自然語(yǔ)言處理(NLP)場(chǎng)景,從而對(duì)他們做一個(gè)比較。這兩個(gè)庫(kù)都是開源的,有商業(yè)使用許可證(分別是Apache 2.0和MIT)。兩者的發(fā)展都很活躍,發(fā)布也很頻繁,且社區(qū)不斷增長(zhǎng)。

我希望能分析和識(shí)別這兩個(gè)庫(kù)的優(yōu)點(diǎn),發(fā)現(xiàn)對(duì)數(shù)據(jù)科學(xué)家和開發(fā)人員而言它們有什么區(qū)別,以及在哪些情況下使用其中一種或另一種更方便。本次分析希望進(jìn)行一次客觀的探索,并在幾個(gè)階段加入一定量的主觀決定(就像每個(gè)自然語(yǔ)言理解應(yīng)用中那樣)。

盡管聽起來(lái)很簡(jiǎn)單,但比較兩個(gè)不同的庫(kù),并做出可比較的基準(zhǔn)測(cè)試是非常具有挑戰(zhàn)性的。請(qǐng)記住,你的應(yīng)用程序會(huì)與這里所做的有著不同的場(chǎng)景、數(shù)據(jù)管道、文本特性、硬件設(shè)置和一些非功能性要求。

我會(huì)假定讀者已經(jīng)熟悉NLP的概念和編程。你們可能沒(méi)有這兩個(gè)工具的相關(guān)知識(shí),不過(guò)我的目標(biāo)是使代碼盡可能自我說(shuō)明,提高讀性,從而不會(huì)讓讀者陷入太多的細(xì)節(jié)里。這兩個(gè)庫(kù)都有公開的文檔,并且是完全開源的。所以我建議你先看一下spaCy 101Spark-NLP快速入門文檔。

關(guān)于這兩個(gè)庫(kù)

Spark-NLP于2017年10月被開源。作為一個(gè)Spark庫(kù),它是Apache Spark的原生擴(kuò)展。它以估計(jì)器(estimator)和轉(zhuǎn)換器(transformer)的形式引入了一套Spark ML Pipeline 階段(stage),用來(lái)處理分布式數(shù)據(jù)集。Spark NLP Annotators不僅包括如分詞、標(biāo)準(zhǔn)化和詞性標(biāo)注等基本功能,還有諸如高級(jí)情感分析、拼寫檢查、斷言狀態(tài)等其他高級(jí)功能。這些都在工作在Spark ML框架內(nèi)。Spark-NLP用Scala編寫,在JVM中運(yùn)行,并利用了Spark的優(yōu)化和執(zhí)行計(jì)劃。該庫(kù)目前提供Scala和Python的API。

spaCy是一個(gè)流行且易于使用的自然語(yǔ)言處理的Python庫(kù)。它最近發(fā)布了2.0版,其中包含了神經(jīng)網(wǎng)絡(luò)、實(shí)體識(shí)別等非常多的模型。它提供了目前業(yè)界領(lǐng)先的準(zhǔn)確性和速度,并且擁有一個(gè)活躍的開源社區(qū)。spaCy的出現(xiàn)至少有三年的時(shí)間了,它GitHub上的第一個(gè)版本可以追溯到2015年初。

Spark-NLP目前還沒(méi)有包括一套預(yù)訓(xùn)練的模型。而spaCy對(duì)七種(歐洲)語(yǔ)言提供了預(yù)先訓(xùn)練的模型,因此用戶可以快速注入目標(biāo)句子并在無(wú)需訓(xùn)練模型的情況下返回結(jié)果,包括分詞、詞條、詞類(POS)、相似性、實(shí)體識(shí)別等。

這兩個(gè)庫(kù)都提供了通過(guò)參數(shù)在某些級(jí)別的自定義,允許在磁盤中保存訓(xùn)練過(guò)的管道,并需要開發(fā)人員在特定使用案例中開發(fā)使用這些庫(kù)的程序。Spark NLP讓把一個(gè)NLP管道作為Spark ML機(jī)器學(xué)習(xí)管道(從數(shù)據(jù)加載、NLP、特征工程、模型訓(xùn)練、超參數(shù)調(diào)優(yōu)以及評(píng)估)的一部分來(lái)嵌入其中更容易。同時(shí)由于Spark可以優(yōu)化整個(gè)管道執(zhí)行過(guò)程,從而使Spark NLP的執(zhí)行速度更快。

基準(zhǔn)應(yīng)用

我在這里編寫的程序?qū)㈩A(yù)測(cè)原始.txt文件中的POS標(biāo)記。很多數(shù)據(jù)清理和準(zhǔn)備工作都是按順序進(jìn)行的。兩個(gè)應(yīng)用程序都將使用相同的數(shù)據(jù)進(jìn)行訓(xùn)練,并對(duì)同一數(shù)據(jù)進(jìn)行預(yù)測(cè),以實(shí)現(xiàn)最大可能的可比性。

我的目的是驗(yàn)證任何統(tǒng)計(jì)性程序的兩大支柱:

1.準(zhǔn)確性,衡量一個(gè)程序能夠正確預(yù)測(cè)語(yǔ)言特征的程度

2.性能,這意味著我需要等待多長(zhǎng)時(shí)間才能達(dá)到這樣的準(zhǔn)確度。以及在程序崩潰或我的孫子長(zhǎng)大之前,我可以向程序輸入多少輸入數(shù)據(jù)。

為了比較這些指標(biāo),我需要確保兩個(gè)庫(kù)有最大的可比性。我用了以下配置:

1.一臺(tái)臺(tái)式機(jī),操作系統(tǒng)是Linux Mint。帶有SSD硬盤和16GB內(nèi)存,以及4核3.5 GHz的英特爾i5-6600K處理器。

2.訓(xùn)練、測(cè)試和帶正確結(jié)果的數(shù)據(jù),這些數(shù)據(jù)遵循NLTK POS格式(見(jiàn)下文)。

3.安裝了spaCy 2.0.5的Jupyter Python 3 Notebook。

4.安裝了Spark-NLP 1.3.0和Apache Spark 2.1.1的Apache Zeppelin 0.7.3 Notebook。

數(shù)據(jù)

用于訓(xùn)練、測(cè)試和比對(duì)的數(shù)據(jù)來(lái)自?National American Corpus。我用了其中的報(bào)紙部分的MASC 3.0.2書面語(yǔ)料庫(kù)。

我用語(yǔ)料庫(kù)提供的工具(ANCtool)對(duì)數(shù)據(jù)進(jìn)行了整理。雖然我可以使用CoNLL數(shù)據(jù)格式,其中包含很多標(biāo)記信息,如詞條、索引和實(shí)體識(shí)別。但我更喜歡使用NLTK數(shù)據(jù)格式,其中包括Penn POS標(biāo)簽。它足以滿足我的目的。數(shù)據(jù)看起來(lái)像這樣:

Neither|DT?Davison|NNP?nor|CC?most|RBS?other|JJ?RxP|NNP?opponents|NNSdoubt|VBP?the|DT?efficacy|NN?of|IN?medications|NNS?.|.

如你所見(jiàn),訓(xùn)練數(shù)據(jù)中的內(nèi)容是:

  • 檢測(cè)到的句子的邊界(新一行,新的句子)
  • 分詞的結(jié)果(用空格分隔)
  • 檢測(cè)到的POS(用“|”分隔)

在原始文本文件中,所有內(nèi)容都混在一起、混亂并且沒(méi)有任何標(biāo)準(zhǔn)邊界。

以下是我們要運(yùn)行的基準(zhǔn)測(cè)試的關(guān)鍵指標(biāo)。

基準(zhǔn)測(cè)試數(shù)據(jù)集

在本文中,我們將使用兩個(gè)基準(zhǔn)數(shù)據(jù)集。 第一個(gè)非常小,用來(lái)進(jìn)行交互式調(diào)試和實(shí)驗(yàn):

  • 訓(xùn)練數(shù)據(jù):36個(gè).txt文件,總共77 KB
  • 測(cè)試數(shù)據(jù):14個(gè).txt文件,共114 KB
  • 需要預(yù)測(cè)21362個(gè)詞

第二組數(shù)據(jù)仍然不是“大數(shù)據(jù)”,而是一個(gè)相對(duì)大的數(shù)據(jù)集,用于評(píng)估典型的單機(jī)應(yīng)用場(chǎng)景:

  • 訓(xùn)練數(shù)據(jù):72個(gè).txt文件,總共150 KB
  • 兩個(gè)測(cè)試數(shù)據(jù)集:9225個(gè).txt文件,75 MB; 1125個(gè)文件,15 MB
  • 需要預(yù)測(cè)1千3百萬(wàn)個(gè)詞

需要注意的是,我們這里并沒(méi)有評(píng)估“大數(shù)據(jù)”數(shù)據(jù)集。這是因?yàn)殡m然spaCy可以利用多核CPU,但它不能像Spark NLP那樣原生就可以使用集群。 因此,Spark NLP在使用集群的TB級(jí)數(shù)據(jù)集上的速度要比spaCy快幾個(gè)數(shù)量級(jí)。同樣,大型機(jī)上的數(shù)據(jù)庫(kù)的性能也會(huì)超過(guò)我這里本地安裝的MySQL數(shù)據(jù)庫(kù)。我的目標(biāo)是在單機(jī)上評(píng)估這兩個(gè)庫(kù),并使用這兩個(gè)庫(kù)的多核功能。這是開發(fā)時(shí)常見(jiàn)的情況,也適用于不需要處理大型數(shù)據(jù)集的應(yīng)用程序。

開始吧

那么讓我們動(dòng)手吧。 首先,我們必須要導(dǎo)入相關(guān)的庫(kù)并啟動(dòng)。?

spaCy

import os

import io

import time

import re

import random

import pandas as pd

import spacy

nlp_model = spacy.load(‘en’, disable=[‘parser’, ‘ner’])

nlp_blank = spacy.blank(‘en’, disable=[‘parser’, ‘ner’])

我禁用了spaCy中的一些管道,以避免不必要的解析器使它過(guò)于臃腫。我還使用了一個(gè)nlp_model作為參考,這是spaCy提供的一個(gè)預(yù)先訓(xùn)練好的NLP模型。但我將使用nlp_blank,這將更具代表性,它將是我自行訓(xùn)練的模型。

Spark-NLP

import org.apache.spark.sql.expressions.Window

import org.apache.spark.ml.Pipeline

import com.johnsnowlabs.nlp._

import com.johnsnowlabs.nlp.annotators._

import com.johnsnowlabs.nlp.annotators.pos.perceptron._

import com.johnsnowlabs.nlp.annotators.sbd.pragmatic._

import com.johnsnowlabs.nlp.util.io.ResourceHelper

import com.johnsnowlabs.util.Benchmark

我面臨的第一個(gè)挑戰(zhàn)是我要處理三種完全不同的分詞結(jié)果,這會(huì)導(dǎo)致難以確定一個(gè)詞是否與分詞和POS標(biāo)記相匹配:

1.spaCy的分詞器采用基于規(guī)則的方法,并且已經(jīng)包含了一個(gè)詞匯表,其中保存了許多常見(jiàn)的縮略語(yǔ)用于分詞。

2.SparkNLP的分詞器有自己的分詞規(guī)則。

3.我的訓(xùn)練和測(cè)試數(shù)據(jù)。這些數(shù)據(jù)按照ANC的標(biāo)準(zhǔn)進(jìn)行分詞。在很多情況下,它分割詞的方式與這兩個(gè)庫(kù)的分詞器完全不同。

所以,為了克服這個(gè)問(wèn)題,我需要決定如何比較一組完全不同的標(biāo)簽的POS標(biāo)簽。對(duì)于Spark-NLP,我將保持原樣。它的默認(rèn)規(guī)則與ANC開放標(biāo)準(zhǔn)分詞格式基本匹配。對(duì)于spaCy,我需要放松中綴規(guī)則,以便通過(guò)不使用“ – ”分割詞來(lái)增加分詞的匹配準(zhǔn)確度。

spaCy

class DummyTokenMatch:

? ? def __init__(self, content):

? ? ? ? self.start = lambda : 0

? ? ? ? self.end = lambda : len(content)

def do_nothing(content):

? ? return [DummyTokenMatch(content)]

model_tokenizer = nlp_model.tokenizer

nlp_blank.tokenizer = spacy.tokenizer.Tokenizer(nlp_blank.vocab, prefix_search=model_tokenizer.prefix_search,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? suffix_search=model_tokenizer.suffix_search,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? infix_finditer=do_nothing,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? token_match=model_tokenizer.token_match)

請(qǐng)注意:我向nlp_blank傳遞了vocab對(duì)象,因此nlp_blank并不是真的空。 這個(gè)vocab詞匯對(duì)象包含有英語(yǔ)語(yǔ)言規(guī)則和策略,可幫助我們的空白模型標(biāo)記POS,并對(duì)英語(yǔ)單詞進(jìn)行分詞。因此,spaCy開始是有一點(diǎn)小優(yōu)勢(shì)的,而Spark-NLP事先并“不懂”英語(yǔ)。

訓(xùn)練管道

現(xiàn)在來(lái)到訓(xùn)練這一步。在spaCy里,我需要提供一個(gè)指定的訓(xùn)練數(shù)據(jù)格式,它的格式如下所示:

TRAIN_DATA = [

(“I like green eggs”, {‘tags’: [‘N’, ‘V’, ‘J’, ‘N’]}),

(“Eat blue ham”, {‘tags’: [‘V’, ‘J’, ‘N’]})

]

而在Spark-NLP中,我必須提供一個(gè)文件夾,其中包含帶“分隔詞|標(biāo)記”格式的.txt數(shù)據(jù)文件,它看起來(lái)就像ANC訓(xùn)練數(shù)據(jù)。所以,我只需將文件夾路徑傳遞給POS標(biāo)記器(即PerceptronApproach)。

讓我們加載spaCy的訓(xùn)練數(shù)據(jù)。在下面代碼里,我必須添加一些手工生成的例外、規(guī)則與一些字符,因?yàn)閟paCy的訓(xùn)練集需要干凈的數(shù)據(jù)。

spaCy

start = time.time()

train_path = “./target/training/”

train_files = sorted([train_path + f for f in os.listdir(train_path) if os.path.isfile(os.path.join(train_path, f))])

TRAIN_DATA = []

for file in train_files:

? ? fo = io.open(file, mode=’r’, encoding=’utf-8′)

? ? for line in fo.readlines():

? ? ? ? line = line.strip()

? ? ? ? if line == ”:

? ? ? ? ? ? continue

? ? ? ? line_words = []

? ? ? ? line_tags = []

? ? ? ? for pair in re.split(“\\s+”, line):

? ? ? ? ? ? tag = pair.strip().split(“|”)

? ? ? ? ? ? line_words.append(re.sub(‘(\w+)\.’, ‘\1’, tag[0].replace(‘$’, ”).replace(‘-‘, ”).replace(‘\”, ”)))

? ? ? ? ? ? line_tags.append(tag[-1])

? ? ? ? TRAIN_DATA.append((‘ ‘.join(line_words), {‘tags’: line_tags}))

? ? fo.close()

TRAIN_DATA[240] = (‘The company said the one? time provision would substantially eliminate all future losses at the unit .’, {‘tags’: [‘DT’, ‘NN’, ‘VBD’, ‘DT’, ‘JJ’, ‘-‘, ‘NN’, ‘NN’, ‘MD’, ‘RB’, ‘VB’, ‘DT’, ‘JJ’, ‘NNS’, ‘IN’, ‘DT’, ‘NN’, ‘.’]})

n_iter=5

tagger = nlp_blank.create_pipe(‘tagger’)

tagger.add_label(‘-‘)

tagger.add_label(‘(‘)

tagger.add_label(‘)’)

tagger.add_label(‘#’)

tagger.add_label(‘…’)

tagger.add_label(“one-time”)

nlp_blank.add_pipe(tagger)

optimizer = nlp_blank.begin_training()

for i in range(n_iter):

? ? random.shuffle(TRAIN_DATA)

? ? losses = {}

? ? for text, annotations in TRAIN_DATA:

? ? ? ? nlp_blank.update(, [annotations], sgd=optimizer, losses=losses)

? ? print(losses)

print (time.time() – start)

運(yùn)行時(shí)間

{‘tagger’: 5.773235303101046}

{‘tagger’: 1.138113870966123}

{‘tagger’: 0.46656132966405683}

{‘tagger’: 0.5513760568314119}

{‘tagger’: 0.2541630900934435}

Time to run: 122.11359786987305 seconds

為了繞過(guò)一些坑,我不得不做了一些額外工作。spaCy不讓我使用我的分詞器的詞匯,因?yàn)樗锩姘艘恍┏舐淖址@纾谴嬖谟趘ocab標(biāo)簽中,否則spaCy不會(huì)訓(xùn)練帶有“l(fā)arge-screen”或“No”標(biāo)記的句子。我必須將這些字符添加到vocab列表中,以便在訓(xùn)練期間spaCy可以找到它們。

現(xiàn)在,讓我們看看在Spark-NLP中如何構(gòu)建管道的。

Spark-NLP

val documentAssembler = new DocumentAssembler()

? ? .setInputCol(“text”)

? ? .setOutputCol(“document”)

val tokenizer = new Tokenizer()

? ? .setInputCols(“document”)

? ? .setOutputCol(“token”)

? ? .setPrefixPattern(“\\A([^\\s\\p{L}\\d\\$\\.#]*)”)

? ? .addInfixPattern(“(\\$?\\d+(?:[^\\s\\d]{1}\\d+)*)”)

?? ?

val posTagger = new PerceptronApproach()

? ? .setInputCols(“document”, “token”)

? ? .setOutputCol(“pos”)

? ? .setCorpusPath(“/home/saif/nlp/comparison/target/training”)

? ? .setNIterations(5)

?? ?

val finisher = new Finisher()

? ? .setInputCols(“token”, “pos”)

? ? .setOutputAsArray(true)

val pipeline = new Pipeline()

? ? .setStages(Array(

? ? ? ? documentAssembler,

? ? ? ? tokenizer,

? ? ? ? posTagger,

? ? ? ? finisher

? ? ))

val model = Benchmark.time(“Time to train model”) {

? ? pipeline.fit(data)

}

正如你所看到的,構(gòu)建一個(gè)管道是一個(gè)非常線性的過(guò)程:設(shè)置文檔組裝器,這使得目標(biāo)文本列成為后續(xù)注釋器(即分詞器)的目標(biāo);接著PerceptronApproach就是POS模型,它將同時(shí)接收文檔文本和符號(hào)化表單作為輸入。

我必須更新前綴模式,并添加一個(gè)新的中綴模式,以便使用和ANC相同的方式匹配日期和數(shù)字(這可能會(huì)在Spark NLP的下一版本中成為默認(rèn)模式)。正如你所看到的,管道的每個(gè)組件都在用戶的控制之下; 沒(méi)有隱含的vocab或英語(yǔ)知識(shí),這和spaCy不同。

來(lái)自PerceptronApproach的corpusPath被傳遞到包含管道分隔文本文件的文件夾。finisher注釋器包裝POS和分詞的結(jié)果,以便下一步使用它。正如SetOutputAsArray()的名字所表示的,它會(huì)返回一個(gè)數(shù)組而不是一個(gè)拼接起來(lái)的字符串,不過(guò)這在處理時(shí)會(huì)有一定的計(jì)算代價(jià)。

傳遞給fit()的數(shù)據(jù)并不重要,因?yàn)槲ㄒ槐挥?xùn)練的NLP標(biāo)注器是PerceptronApproach。而且這個(gè)標(biāo)注器是用外部POS Corpora進(jìn)行訓(xùn)練的。

運(yùn)行時(shí)間

Time to train model: 3.167619593sec

作為一個(gè)附注,可以在管道中注入SentenceDetector或SpellChecker。這樣在某些情況下,可以通過(guò)讓模型知道句子結(jié)束的位置來(lái)幫助提升POS的準(zhǔn)確性。

接下來(lái)做什么?

到目前為止,我們已經(jīng)初始化了庫(kù),加載了數(shù)據(jù),并且用兩個(gè)庫(kù)都訓(xùn)練了一個(gè)分詞器模型。需要注意的是,spaCy帶有一個(gè)預(yù)先訓(xùn)練好的分詞器,因此如果你的文本數(shù)據(jù)來(lái)自于spaCy訓(xùn)練過(guò)的語(yǔ)言(例如英語(yǔ))和領(lǐng)域(例如新聞報(bào)道),這一步可能不是必需的。但為了讓生成的符號(hào)與我們的ANC語(yǔ)料庫(kù)更匹配,對(duì)分詞中綴的修改是非常重要的。在迭代5次的情況下,Spark-NLP的訓(xùn)練速度比spaCy快了38倍多。

在此系列的下一篇中我們將通過(guò)使用剛剛訓(xùn)練出的模型來(lái)運(yùn)行NLP管道,介紹代碼、準(zhǔn)確性和性能。

相關(guān)資料:

Saif Addin Ellafi

Saif Addin Ellafi是一名軟件開發(fā)者、分析師、數(shù)據(jù)科學(xué)家,并永遠(yuǎn)是一名學(xué)生。他同時(shí)還是一名極限運(yùn)動(dòng)和游戲愛(ài)好者。他在銀行和金融行業(yè)的數(shù)據(jù)領(lǐng)域擁有豐富的解決問(wèn)題和測(cè)試的經(jīng)驗(yàn)。現(xiàn)在他在John Snow Labs,并是Spark-NLP的主要貢獻(xiàn)者。

Golden pipes (source: PublicDomainPictures.net)