自然語言處理時,通常的文本清理流程是什麼?

儘管文本清理受所做的任務影響很大,但是否有一些標準的清理流程,以及這些清理背後的邏輯。

比如,是否有必要替換URLs,時間,貨幣,姓名,地名,數字等


本文作者: @Aaron Yang

我們以英文文本處理為例。大致分為以下幾個步驟:

  • Normalization
  • Tokenization
  • Stop words
  • Part-of-Speech Tagging
  • Named Entity Recognition
  • Stemming and Lemmatization

Normalization

得到純文本文件後,第一步通常做的就是 Normalization。

在英語語言中,所有句子第一個詞的首字母一般是大寫。有時候全部大寫,用於表示強調和區分風格。這對人類讀者而言非常方便。但從機器學習演算法角度來說,無法區分Car/car/CAR。它們都是一個意思。因此我們一般把文本中的所有字母統一轉換成大寫或者小寫(通常情況是小寫),每一個詞用一個唯一的詞來表示。

上圖這是文本樣本,是電影《The Second Renaissance》的一段影評。這部電影是關於智能機器人對人類發動戰爭,爭取權利的故事。如果將影評存儲在名為「文本」的變數中,將其轉換成小寫,只需在 Python 中調用 lower() 方法即可。這是轉換後的樣子。請注意更改的所有字母。其他語言可能有,也可能沒有對應的大小寫。但是根據你的 NLP 任務的不同,類似原理可能也適用。

你可能還想清除文本中的句號、問號、感嘆號等。特殊字元,並且僅保留字母表中的字母和數字。文檔分類和聚類等應用中若要將所有文本文檔作為一個整體,那麼正則表達式這個方法特別有效。這裡,可以匹配小寫az大寫AZ或者數字09的範圍之外的所有字元並用空格代替。這個方法無需指定所有標點符號。 但是,也可以採用其它正則表達式。

小寫轉換和標點移除是兩個最常見的文本 Normalization 步驟。是否需要以及在哪個階段使用這兩個步驟取決於你的最終目標。


Tokenization

Token 是「符號」的高級表達。一般指具有某種意義,無法再分拆的符號。在英文自然語言處理中,Tokens 通常是單獨的詞。因此,Tokenization 就是將每個句子分拆成一系列詞。

通常情況下,最簡單的方法是使用 split() 方法返回詞列表。

這裡默認情況下是將一段話在空格字元處分拆,除了空格,也包其他標籤、新行等。這種方法還很智能,可以忽略一個序列中的兩個或多個空格字元。因此不會返回空字元串。 你同樣可以使用可選參數對它進行控制。目前為止,我們只使用了 Python 的內置功能。 當然,我們也可以使用工具箱,例如 NLTK,一種處理英文最常用的自然語言工具箱, 某些運算會簡單得很多。 在 NLTK 中分拆文本的最常見方法是 使用 nltk.tokenize 中的 word_tokenize() 函數。這與split() 執行的效果差不多,但更加聰明一些。在嘗試傳入未標準化的原始文本時,你會發現,根據標點符號位置的不同,對它們的處理也不同。

例如,頭銜Dr後面的句號.Dr保留在一起作為一個 Token。可想而知,NLTK 使用某種規則或模式決定如何處理每個標點符號。

有時,我們可能需要將一段話分解成句子而不是單詞。比如,如果你想翻譯文本,可能需要將文本分拆成句子。

這時,我們可以通過 NLTK 使用 sent_tokenize()實現這一點。然後可以根據需要將每個句子分拆成詞,NLTK 提供多種 Token 解析器。包括基於正則表達式的令牌解析器,可以用於一步清除標點符號並將其 Tokenize。


Stop Word

Stop Word 是無含義的詞,例如is/our/the/in/at等。它們不會給句子增加太多含義,單停止詞是頻率非常多的詞。 為了減少我們要處理的辭彙量,從而降低後續程序的複雜度,需要清除停止詞。

在上述句子中,即使沒有arethe,我們仍然能推斷出人對狗的正面感情。你可以自己思考一下 NLTK 將英語中的哪些詞作為停止詞。

這裡,NLTK 是基於特定的文本語料庫或文本集。不同的語料庫可能有不同的停止詞。在一個應用中, 某個詞可能是停止詞。而在另一個應用這個詞就是有意義的詞。要從文本中清除停止詞,可以使用帶過濾條件的 Python 列表理解。

這裡,我們將影評 Normalization 和 Tokenization 之後 清除其中的停止詞。結果有點難懂,但現在輸入量縮小了很多,並保留了比較重要的辭彙。


Part-of-Speech Tagging

還記得在學校學過的詞性嗎?名詞、代詞、動詞、副詞等等。識別詞在句子中的用途有助於我們更好理解句子內容。並且,標註詞性還可以明確詞之間的關係,並識別出交叉引用。同樣地,NLTK 給我們帶來了很多便利。你可以將詞傳入 PoS tag 函數。然後對每個詞返回一個標籤,並註明不同的詞性。

這裡函數正確地將出現的第一個lie標註為動詞,將第二個標註為名詞。關於標籤含義的更多詳細信息,請參閱 NLTK 文檔。詞性標註的一個典型應用是句子解析。

上面的示例是 NLTK 手冊中使用自定義語法解析歧義句的一個示例。實例中解析器返回了兩種有效解釋。我們也可以使用代碼畫出解析樹,以便可以輕易地看出兩者的區別。

"I / shot an elephant / in my pajamas" ("我穿著睡衣殺了一頭象")以及 "I / shot / an elephant in my pajamas";("我殺了一頭穿著我睡衣的象"

另外,還有其他很多方法可以進行 PoS,比如 Hidden Markov Models (HMM) 以及 Recurrent Neural Networks (RNNs)


Named Entity

Named Entity 一般是名詞短語,又來指代某些特定對象、人、或地點 可以使用 ne_chunk()方法標註文本中的命名實體。在進行這一步前,必須先進行 Tokenization 並進行 PoS Tagging。

如圖,這是一個非常簡單的示例。NLTK 還可以識別出不同的實體類型,分辨出人、組織和 GPE(地緣政治實體)。 另外,它還將UdaticyInc這兩個詞識別成一個實體,效果不錯。Named Entity 並不是所有的情況都識別的很好,但如果是對大型語料庫進行訓練,卻非常有效。命名實體識別通常用於對新聞文章建立索引和進行搜索。我們可以搜索自己感興趣的公司的相關新聞。


Stemming and Lemmatization

為了進一步簡化文本數據,我們可以將詞的不同變化和變形標準化。Stemming 提取是將詞還原成詞幹或詞根的過程。

例如brancing/branched/branches等,都可以還原成branch。總而言之,它們都表達了分成多個路線或分支的含義。這有助於降低複雜度,並同時保留詞所含的基本含義。Stemming 是利用非常簡單的搜索和替換樣式規則進行的。

例如,後綴inged可以丟棄;ies可以用y替換等等。這樣可能會變成不是完整詞的詞幹,但是只要這個詞的所有形式都還原成同一個詞幹即可。因此 它們都含有共同的根本含義。

NLTK 有幾個不同的詞幹提取器可供選擇,例如PorterStemmer()方法。上圖例子中我們已經清除了 Stop Words,所以部分轉換效果非常好。例如,started還原成了start。但是像其它詞,例如people末尾的e被刪除,出現這樣的原因是因為規則過於簡單。

Lemmatization 是將詞還原成標準化形式的另一種技術。在這種情況下,轉換過程實際上是利用詞典,將一個詞的不同變形映射到它的詞根。通過這種方法,我們能將較大的詞形變化,如 is/was/were 還原成詞根beNLTK 中的默認詞形還原器使用 nltk.stem.wordnet 資料庫將詞還原成詞根。

這裡我們試一下像詞幹提取一樣,將 WordNetLemmatizer() 的實例初始化,並將各個詞傳入 lemmatize()方法。結果中只有詞ones被還原成了one,其它詞並無任何變化。仔細讀各個詞,你會發現ones是這裡唯一的複數名詞。實際上,這就是它被轉換的原因。

Lemmatization 需要知道每個詞的詞性。在這個例子中WordNetLemmatizer()默認詞性是名詞。但是我們可以指定 PoS 參數,修改這個默認設置。我們傳入 v 代表動詞。現在,兩個動詞形式boringstarted都被轉換了。

小結一下,在前面的示例中,可以看出 Stemming 有時會生成不是完整英語詞的詞幹。Lemmatization 與 Stemming 類似,差別在於最終形式也是有含義的詞。這就是說,Lemmatization 需要字典,而 Stemming 不需要字典。因此,根據你施加約束的不同,Stemming 是對內存要求較低的方案。


總結

上文內容其實來自Udacity「自然語言處理工程師」的課程。總結一下典型英文自然語言處理工作流程是什麼樣 對於純文本句子。首先將其轉換成小寫,並清除標點符號,將其 Normalization。然後用 Tokenization 將其分拆成詞,接下來可以清除 Stop Words,以減少要處理的辭彙量。根據應用的不同,可以選擇同時進行 Stemming 和 Lemmatization,將詞還原成詞根或詞幹。常見的方法是先進行 Lemmatization,再進行Stemming。這個程序將自然語言句子轉換成標準化記號序列,這樣可以用於進一步分析。


看任務而定,對於某些任務是無用信息可以刪除或者統一替換,某些任務卻是特徵不能刪掉。

先參考該任務相關文獻的預處理方法和評測源碼方法等。

比如我做微博twitter情感分析就會將url,at,表情符號等統一替換。做文本匹配就會去停用詞,標點符號,數字轉換等等。做地域用戶畫像就不能把地名清洗了,不同的任務有不同的套路,實在不行就試試多通道輸入看哪種清洗方法好。


把數字都換成一個特殊字元NUM有時候挺有用的。

很多task會考慮扔掉stopwords。

把單詞都變成小寫字母。

把長文本分成句子和單詞這些fine granularity會比較有用。

可以在句首加上BOS,在句末加上EOS。

一般會有一個dictionary,不在dictionary以內的單詞就用UNK取代。

單詞會被轉成數字(它對應的index,從0開始,一般0就是UNK)。

做機器翻譯的時候會把單詞轉成subword units

當然以上處理都不是固定的,可能會根據情況採用。


如果是Word-Based Deep Learning的話,一個非常標準的流程如下:

  1. Tokenization: 將句子變成Token(不完全是詞)。
  2. Normalization: 為了降低Token的稀疏性(Sparsity)。全部轉小寫。小的NLU Dataset考慮統一詞性。再小的直接變詞根(Stem)。如果是NLG要考慮可逆性,一般不能做太多的歸一化。
  3. Rare word replacement: 將詞頻小於5的替換成一個特殊的Token &。Rare Word如同雜訊。故此法降噪並減少字典的大小。
  4. Add &, &:在句前,句後加入&和&。這個是個依賴具體模型(Model-Based)的處理策略。因為現在都用RNN,RNN的結構對於結尾不敏感(Sensitive),所以要加。
  5. Long Sentence Cut-Off:將句長超過L的變成L。這個也是Model-Based,因為RNN需要Unrolling。

Misc:

-1. 很多操作不應考慮Test Set。絕大多數人都是加Test set的,這是全然錯誤的。比如說做Rare Word replacement的時候,不應該統計Test Set的詞頻。

0.一個好的預處理對結果影響不小。

  1. BPE: bpe技術非常有效
  2. Long Tail: 為防止&過多影響詞頻,可將&拆成&,&, ....,然後random-projection。&對結果影響很大。
  3. Misspelled:對小的,質量不高的Dataset,考慮糾正錯詞。(將詞替換成字典中最接近的詞)
  4. Char-Level: Rare Word還有Char-Level的處理方法,不需要&
  5. ELMo: 今年開始直接ELMo就好了
  6. Special Tokens: 要根據具體Task處理。比如,人名統一成&, &,從句子中抽取Structured信息等。這個一般都是Dict + 正則表達式直接算。少數用PoS, Parser等,效果普遍不如暴力規則。如果你用NN來提就是一篇Paper了。
  7. Stop Words: 有的人喜歡刪Stop Words(標點)。非常不推薦。Stop Words是句子很重要的信息,起到天然的分割(segmentation)效果,而不用Model自己去學句子的分隔。刪了stop word基本上就相當於選擇了Bag-Of-Words的方法,或者是類BOG的方法,像CNN Filter這種。更不用說NLG中刪了punctuations。比如萬惡的MSCOCO caption evaluation api,竟然刪PUNCTUATIONS,導致只在Caption這種短句中適用。eval長句子就是坑。


在自然語言處理中不管你做什麼任務,首先都是要把文本轉為向量,然後輸入到各種演算法里。本文就以中文文本處理為例,來講述一下如何對文本進行預處理。

一. 本文布局

  • 去除指定無用的符號
  • 對文本進行jieba分詞
  • 去除停用詞
  • 將文本轉為tfidf向量並輸入到演算法中

二. 操作流程

1.去除指定無用的符號

python里的replace非常的好用

m1 = map(lambda s:s.replace(nnnnn,""),data)

這裡就是把文本里的nnnnn替換為空值,然後你可以利用這個去除任何你指定的符號,我一般先去掉文本中大量的重複符號

2.對文本進行jieba分詞

這個比較簡單,導入jieba包(沒有的話下一個),然後

jieba.cut(s)

3.去除停用詞

什麼是停用詞呢?——就是一些沒有用的詞,比如「的」「了」「么」等等,還有一些標點符號什麼的

網上有很多停用詞表,隨便的下一個來用就行。用法也很簡單,導入停用詞表,將其轉為list格式,然後遍歷你剛才jieba分詞好的文本,判斷每個詞是否在停用詞表中,不在的話,就添加到另外一個列表裡


4.將文本轉為tfidf向量並輸入到演算法中

什麼是tfidf向量呢?看這篇文章即可,講解的通俗易懂

TF-IDF與餘弦相似性的應用(一):自動提取關鍵詞 - 阮一峰的網路日誌

那麼如何把我們處理好的文本轉為tfidf向量呢?大家可以看這篇博客,上面記錄了用三種方法將文本轉為tfidf向量

使用不同的方法計算TF-IDF值

我在這裡大致的演示一下如何操作,詳細的可以看上面的博客

剛才我們得到的列表是個list of list格式,意思就是列表裡還套著一個列表。列表裡面,每個小列表相當於一句話,這句話是經過我們前面預處理過的。

這裡我先用sklearn的包來將文本轉為tfidf向量,但是把sklearn包對輸入的文本的格式有要求

大概長這個樣子

[我 愛 中國 , 我 喜歡 自然語言處理...]

words = []
for line_index in range(len(x_train)):
words.append( .join(x_train[line_index]))

x_train指的就是我們預處理的文本,也就是上面所說的list of list格式的文本。用上面的代碼,就可以把list of list 格式的文本轉為sklearn所需要的格式

然後導入sklearn里的tfidf,並將其輸入到演算法中

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
vectorizer.fit(words)

from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(vectorizer.transform(words),y_train)

三. 總結

以上便是中文文本比較常見的預處理過程,比較粗糙的預處理

至於題主所說是否有必要替換URLs,時間,貨幣,姓名,地名,數字等詞,我個人覺得看你的需求,你要做的方向是什麼,這些詞是否很重要。比如命名實體識別,你要是想要識別出時間,地點,人物,組織名。那有關這些詞的肯定就不能去除了,如果你只是做一個簡單的二分類,而這些詞又不是很重要,那去除也無妨


推薦閱讀:

TAG:Python | 機器學習 | 自然語言處理 | 正則表達式 | 深度學習DeepLearning |