天天看點

中文自然語言處理stopword下載下傳_文本預處理的梳理與個人思考

中文自然語言處理stopword下載下傳_文本預處理的梳理與個人思考

文本預處理對于NLP領域的從業者或研究者來說并不陌生,也是很多人剛接觸NLP領域面臨的第一個問題。今天我想對文本預處理所涉及的知識進行一個整理,作為個人的筆記,同時分享一些個人思考與心得。文章以我對文本預處理認知順序展開,如果文中有錯誤,歡迎指正。

為什麼要有文本預處理?

1. 計算機無法直接了解文字

其實對于程式員來說,文本預處理不應該感到陌生。因為在我看來,程式編譯器就是一種“語言預處理”的過程。比如gcc把C語言的代碼轉化為機器語言,計算機才能執行程式。

是以将“自然語言”轉化為“符号語言”是文本預處理最本質最核心的一個目的。

2. 在下遊任務中,發現轉化的“符号語言”不夠好

無論是個人的一次項目還是人類曆史上的研究,文本預進行中所涉及的操作絕對不是一蹴而就的,而是在做下遊任務的時候,發現了一些問題,然後我們再回過頭來,通過預處理的方式解決。

我猜測,人類在第一次做文本預處理的時候,隻做了一件事,把所有“文字”轉成了“ASCII碼”(甚至那時候可能ASCII碼都沒有),丢進計算機去處理。然後,程式員們在下遊任務中發現了種種不滿意,而回過頭來優化。

比如我們覺得有些單詞對我的任務沒有作用,浪費計算力和空間,就定義了“stop word”。

比如我們覺得“one-hot”的表示方式太過于“龐大”而且沒有任何詞義表示,是以我們想辦法把龐大的“one-hot”轉化為稠密的“詞向量”。

是以,這一部分預處理的原因是“人工智能還不夠智能”,我們通過“人工”去彌補“智能”。和我們教兒童識字會将很多生僻詞拿走是一個道理。希望有朝一日,我們可以少一點人工幫助,把最原始的文字丢給計算機,讓他去處理。

根據上面的兩個原因,我得到了兩個結論。第一,文本預處理必不可少。第二,文本預處理沒有絕對的“必備操作”,我們應該根據自己下遊任務來思考需要哪些操作。而不是把文本預處理與下遊任務孤立開,不能所有情況都用一套流程來處理。

是以,我下面整理思路也是從“為什麼做”到“怎麼做”最後“反思”來梳理。反思部分有很多個人思考,僅供交流,抛磚引玉。

分詞 —— Tokenization

分詞這個稱呼其實是不準确,容易引起誤導。Tokenization這裡更加準确的翻譯應該是标記化。詞語和符号都是标記,标記化就是對句子中的詞語和标點進行合理的分割。

一般NLP處理都是以詞語為粒度的切割為前提。例如傳統的bow是對詞頻的統計形成文本向量。RNN是一個詞一個詞的輸入。

大概是由于:漢字或者字母的粒度,無法表達語義,例如給你一個‘s’,完全不知所雲。而以句子為粒度分析,句子千變萬化,兩篇同樣描述大熊貓的文章,甚至可能沒有任何兩個句子相同。不利于統計模型或者機器學習模型的分析。是以,詞語粒度的切割是最為合适的。

分詞在英語和中文的考慮是不同的,分開來說。

1. 英文分詞

英文單詞相對分詞會容易一些,但還是有一些細節需要注意。

英文單詞天生會用空格間隔開,是以很多人會采用以下方式分詞。

sentence.split() # split()預設以空格分詞
           

考慮分詞“It's your cat!”這句話。這樣分詞的最後一個詞會是“cat!”。就會把“cat”和“cat!”當作兩個詞來看待。于是有的人選擇先去掉标點符号再做分詞,那樣“it's”和“its”就都會成為“its”。

這就是為什麼即使是簡單的英文分詞,nltk還是出了word_tokenize的工具。

from nltk import word_tokenize
print(word_tokenize("it's your cat!"))
# 列印結果:['it', "'s", 'your', 'cat', '!']
           

可以看到nltk很好的兼顧了英文中标點符号的情況。值得讓人高興的是,GloVe等預訓練詞向量是有"'s"、"'re"等标記的向量的。

2. 中文分詞

先說一下結論:中文分詞可以當做是一個已解決的問題,也就是我們可以通過調用jieba等分詞庫來實作。

import jieba
seg_list = jieba.cut("我來到南京長江大橋")
print(list(seg_list)) # jieba.cut() 傳回的是一個生成器
# 列印結果:['我', '來到', '南京長江大橋']
           

深究一下分詞算法大抵有以下幾個方面,篇幅關系不做展開。

1. 基于規則分詞。簡單的有正向最大比對法和逆向最大比對法。據有外國學者1995年的研究表明,這兩種方法分詞完全一緻且正确的句子占90%左右,這兩種方法分詞不一樣但會至少有一種是正确的句子占9%。也就是隻有1%左右的句子,這兩種方法是分不出來的。

2. 基于統計分詞。這種分詞方法需要建立語言模型,然後通過Viterbi算法進行規劃尋找機率最大的分詞方法。

3. 基于深度學習分詞。

對于這3種分詞方法,前2種我進行過程式設計實作都不算複雜,但第3種還沒有接觸過,這裡存疑,找時間再研究下。

停用詞 —— stop word

其實為什麼要有停用詞,我覺得主要是因為傳統的NLP是基于統計的。我們統計到了大量“is”、“a”、“what”這樣的單詞,但是這樣的詞又不能幫助我辨識文本與文本的差別。可能TF-IDF可以一定程度上解決高頻詞的重要度問題。但是既然沒用,為什麼不去掉。

操作思路就是:生成停用詞表,然後去掉資料裡所有的停用詞。

1. 生成停用詞表

可以自己對資料集做詞頻統計選出高頻詞,也可以網上下載下傳,友善一點就直接使用nltk提供的停用詞。

from nltk.corpus import stopwords

# 需要提前調用 nltk.download(‘stopwords’) 下載下傳
my_stopwords = set(stopwords.words('english'))
           

但無論如何生成這個停用詞表,都最好加一步篩選過程。比如“what”被nltk當做了停用詞,但假如你的任務恰好就是問答系統,那是不是“what”裡包含了很多重要的資訊呢。

my_stopwords .remove('what')
           
2. 去掉停用詞

從序列裡去掉特定的單詞,是一個簡單的程式設計問題,但仍然有一些細節可以注意。

(1)步驟1中生成的停用詞表變量使用set類型,就是因為這裡要判斷 in 的操作。set的in操作平均時間複雜度是O(1),list的in操作平均時間複雜度是O(n)。

(2)清單生成式是一種更加Pythonic的寫法,對代碼規範有點輕微強迫症是一種自然而然的好習慣。

# words 是已經分詞好的一句話
words = [ w for w in words if w not in my_stopwords ]
           

那麼反思一下停用詞這件事,既然是由詞的頻率進行判斷的。那和詞頻無關的表示方式,是否還有必要去掉停用詞呢?比如skip gram是由一個神經網絡訓練出來的詞向量。

kaggle上一個專家分享了他的經驗,推薦去看一下全文。

引用自:How to: Preprocessing when using embeddings Don't use standard preprocessing steps like stemming or stopword removal when you have pre-trained embeddings

他指出很多人在Embedding之前使用預處理,是想當然的覺得提出(他們認為的)重要資訊,可以幫助神經網絡更好的工作。但可能往往結果并不是想象的那樣。(感謝零顧醬的指正!原稿我根據上下文閱讀”過拟合了“)

You loose valuable information, which would help your NN to figure things out.

在看到這篇文章前,我是任何任務無腦去停用詞的。但在這之後,會每次任務,分别對“去停用詞”版本和“不去停用詞”版本作比較。不過一般基于預處理詞向量的詞表示方式的任務,确實是不去停用詞版本會更好。

是以這也印證了我文章開頭提到的第二個原因。我們有的預處理步驟,是因為我們處理任務的方法處理不了某些資訊,而并不是這些資訊真的毫無作用。比如我們用統計的思路去表達詞資訊,确實無法處理高頻詞的作用。

文本标準化 —— Text Normalization

需要做文本的标準化,主要是由于英語中同一個單詞可能有不同的形态。

名詞以apple為例,有apple,apples的形态。

動詞以take為例,有take,took,token的形态。

文本标準化通常的做法有兩種,Stemming和Lemmatization。

1. Stemming —— 詞幹分析

Stemming是一種基于規則的标準化方式。是早期語言學家總結了英語中的單詞變形方式,如詞尾增加字尾等方式。然後根據這些變形規則嘗試去反向還原單詞詞幹。

from nltk.stem import LancasterStemmer
lancaster=LancasterStemmer()
lancaster.stem('prestudy')
# 輸出:prestudi
# study:study studies:studi
           

根據上圖的例子,我們可以看到Stemming的幾個問題:

  • 還原出來的詞幹,未必是一個單詞。例子中prestudy已經是單詞原型了,但是還原成了prestudi。假如使用了預訓練的詞向量,比如prestudy在GloVe中是存在的,但是prestudi卻是沒有的,這樣就有點弄巧成拙的感覺。
  • 有的單詞是一樣的,但是規則卻不能将他們還原成一樣。
  • 有的單詞是不一樣的,但是規則會把這兩個單詞還原成一個。比如fli和flying變換過都是fli。

2. Lemmatization —— 詞元分析

詞元分析是基于詞典進行的标準化,是以好處就是還原出來的單詞都是現實存在的單詞,但對于詞典中沒有的單詞,就無法還原了。

from nltk.stem import WordNetLemmatizer
wordnet_lemmatizer = WordNetLemmatizer()
wordnet_lemmatizer.lemmatize('studies')
# 輸出是study
           

感謝:

沒有魚丸木有粗面 同學,零顧醬同學的指正!