自然語言處理時,通常的文本清理流程是什麼?
儘管文本清理受所做的任務影響很大,但是否有一些標準的清理流程,以及這些清理背後的邏輯。
比如,是否有必要替換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 任務的不同,類似原理可能也適用。
你可能還想清除文本中的句號、問號、感嘆號等。特殊字元,並且僅保留字母表中的字母和數字。文檔分類和聚類等應用中若要將所有文本文檔作為一個整體,那麼正則表達式這個方法特別有效。這裡,可以匹配小寫a
至z
大寫A
至Z
或者數字0
到9
的範圍之外的所有字元並用空格代替。這個方法無需指定所有標點符號。 但是,也可以採用其它正則表達式。
小寫轉換和標點移除是兩個最常見的文本 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
等。它們不會給句子增加太多含義,單停止詞是頻率非常多的詞。 為了減少我們要處理的辭彙量,從而降低後續程序的複雜度,需要清除停止詞。
在上述句子中,即使沒有are
和the
,我們仍然能推斷出人對狗的正面感情。你可以自己思考一下 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(地緣政治實體)。 另外,它還將Udaticy
和Inc
這兩個詞識別成一個實體,效果不錯。Named Entity 並不是所有的情況都識別的很好,但如果是對大型語料庫進行訓練,卻非常有效。命名實體識別通常用於對新聞文章建立索引和進行搜索。我們可以搜索自己感興趣的公司的相關新聞。
Stemming and Lemmatization
為了進一步簡化文本數據,我們可以將詞的不同變化和變形標準化。Stemming 提取是將詞還原成詞幹或詞根的過程。
例如brancing
/branched
/branches
等,都可以還原成branch
。總而言之,它們都表達了分成多個路線或分支的含義。這有助於降低複雜度,並同時保留詞所含的基本含義。Stemming 是利用非常簡單的搜索和替換樣式規則進行的。
例如,後綴ing
和ed
可以丟棄;ies
可以用y
替換等等。這樣可能會變成不是完整詞的詞幹,但是只要這個詞的所有形式都還原成同一個詞幹即可。因此 它們都含有共同的根本含義。
NLTK 有幾個不同的詞幹提取器可供選擇,例如PorterStemmer()
方法。上圖例子中我們已經清除了 Stop Words,所以部分轉換效果非常好。例如,started
還原成了start
。但是像其它詞,例如people
末尾的e
被刪除,出現這樣的原因是因為規則過於簡單。
Lemmatization 是將詞還原成標準化形式的另一種技術。在這種情況下,轉換過程實際上是利用詞典,將一個詞的不同變形映射到它的詞根。通過這種方法,我們能將較大的詞形變化,如 is
/was
/were
還原成詞根be
。NLTK 中的默認詞形還原器使用 nltk.stem.wordnet
資料庫將詞還原成詞根。
這裡我們試一下像詞幹提取一樣,將 WordNetLemmatizer()
的實例初始化,並將各個詞傳入 lemmatize()
方法。結果中只有詞ones
被還原成了one
,其它詞並無任何變化。仔細讀各個詞,你會發現ones
是這裡唯一的複數名詞。實際上,這就是它被轉換的原因。
Lemmatization 需要知道每個詞的詞性。在這個例子中WordNetLemmatizer()
默認詞性是名詞。但是我們可以指定 PoS 參數,修改這個默認設置。我們傳入 v
代表動詞。現在,兩個動詞形式boring
和started
都被轉換了。
小結一下,在前面的示例中,可以看出 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的話,一個非常標準的流程如下:
- Tokenization: 將句子變成Token(不完全是詞)。
- Normalization: 為了降低Token的稀疏性(Sparsity)。全部轉小寫。小的NLU Dataset考慮統一詞性。再小的直接變詞根(Stem)。如果是NLG要考慮可逆性,一般不能做太多的歸一化。
- Rare word replacement: 將詞頻小於5的替換成一個特殊的Token &
。Rare Word如同雜訊。故此法降噪並減少字典的大小。 - Add &
, & :在句前,句後加入&和& 。這個是個依賴具體模型(Model-Based)的處理策略。因為現在都用RNN,RNN的結構對於結尾不敏感(Sensitive),所以要加。 - Long Sentence Cut-Off:將句長超過L的變成L。這個也是Model-Based,因為RNN需要Unrolling。
Misc:
-1. 很多操作不應考慮Test Set。絕大多數人都是加Test set的,這是全然錯誤的。比如說做Rare Word replacement的時候,不應該統計Test Set的詞頻。
0.一個好的預處理對結果影響不小。
- BPE: bpe技術非常有效
- Long Tail: 為防止&
過多影響詞頻,可將& 拆成& ,& , ....,然後random-projection。& 對結果影響很大。 - Misspelled:對小的,質量不高的Dataset,考慮糾正錯詞。(將詞替換成字典中最接近的詞)
- Char-Level: Rare Word還有Char-Level的處理方法,不需要&
- ELMo: 今年開始直接ELMo就好了
- Special Tokens: 要根據具體Task處理。比如,人名統一成&
, & ,從句子中抽取Structured信息等。這個一般都是Dict + 正則表達式直接算。少數用PoS, Parser等,效果普遍不如暴力規則。如果你用NN來提就是一篇Paper了。 - 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 |