本篇大綱:
1、文本數據的處理
2、離散型數據的處理
簡單目錄結構
文本數據的處理:
1、分詞
2、去停用詞
3、高低頻詞處理
4、文本數值化:詞袋法,詞集法,TF-IDF
5、詞性分析
離散型數據的處理:
1、獨熱編碼OneHot Encoding
2、啞編碼get_dummies
3、字典特徵提取編碼DictVectorizer
4、map映射
5、順序編碼OrdinalEncoder :一般特徵專用
6、類別編碼Label Encoding :一般標籤專用
按照慣例,開篇之前重複一下數據、特徵以及特徵工程三者的定義。
數據:
數據指的是能獲取到的原始數據信息。
數據處理中的首先要處理的數據通常包括錯誤數據、冗餘數據和缺失數據
特徵:
特徵是原始數據的數值表示。
有很多方法可以將原始數據轉換成數值類型,所以特徵有很多形式。
特徵工程:
特徵工程就是在給定數據、模型和任務的情況下設計出最合適,最恰當的特徵的一個過程。
文本數據的處理
無論是機器學習還是深度學習,進模型前的數據最終都是數值化後的數據。
但數據本身可能是文本,可能是音頻,甚至可能是圖片。
所以就需要有一個數值化的過程。
通常文本數據數值化有以下這些方式:
1、詞袋法,2、詞集法,3,TF-IDF
4、Word Embedding(如word2Vec,doc2vec等)
但是數值化之前,我們還需要進行的一步操作叫做分詞。
分詞
因為文本涉及到中文文本和英文文本,
英文文本可以簡單地以空格來進行單詞的拆解。
而中文文本則不能單獨地以標點符號或者空格來進行拆解了。
這個時候,可以使用到一些現成的中文分詞工具,比如jieba,SnowNLP,HanLP等。
目的即將句子拆解成合適的詞語。
jieba分詞示例:
分詞往往還會配套使用去停用詞。
停用詞可以理解為比較沒有意義的詞,對文章而言貢獻不大的詞,比如的,呢,了等等。
之所以去停用詞,則是為了提前進行噪音詞的消除。
去停用詞示例
當然,這裡只是一個示例,網上可以找到現成的一個比較完整的停用詞表,收集了將近兩千個停用詞。
所以去完停用詞後的分詞結果,則是屬於一個一個詞語構成的。
那麼如果是中文文本的話,分詞到這裡基本就ok了。
而對於英文文本來說。
首先是分詞,若是單獨以空格進行直接分隔,那麼很多時候其實會有較大誤差,比如英文也有自己的短語。
比如New York是一個地名,單獨拆開顯然又是不合適的。
所以這裡首先涉及到N-Gram。
以2-Gram舉例,也就是每兩個單詞作為一個短語。
但是這樣的話問題又來了。
1、The New York會變成兩個詞語,即The New 和New York。
2、句子結尾的單詞會和標點符號或者下句的句首(取決於會不會去標點操作)綁定在一起。
對於第一個問題,不論是以一元語言模型拆解也好,二元語言模型拆解也好,當N大於1時,拆解後的詞往往可以表達更豐富的信息,也更多地保留文本的初始序列結構。但這裡也會有一些代價,比如都會存在一些詞被拆解的不太合適的情況。同時,N越大時,得到的特徵矩陣也往往會越稀疏。因為短語中單詞越多,其可能重複的概率就越低。
對於第二個問題,當英文文檔是長篇幅多語句,甚至是整篇文章時,我們可能會先進行句子和段落的拆解,然後才做進一步的分詞操作。
在得到文本的分詞結果後,往往我們需要做一下最簡單的數據清洗。
首先,同樣的,我們獲取到現成的停用詞表,做一些去停用詞的操作。
但英文停用詞表相比之下作用會小很多,一來數量較小,二來絕大部分都是針對單詞來的。
其次,我們可以對分詞後的結果做一個簡單去高頻詞和低頻詞的操作。
當然,這裡的頻率閾值需要我們自己人為設定,本質行為上有點特徵選擇的意味在裡面。
所以我們在這裡往往閾值會設置得比較偏極端一些,以減少那些被我們不經意刪除的有效信息。
那麼到這裡,分詞基本上就算簡單做完了。
文本數值化
分詞結束後,就回到了我們開始的問題。
此時,我們需要對分詞後的內容進行數值化。
這裡我們列舉常用的四種:
1、詞袋法,2、詞集法,3,TF-IDF
4、Word Embedding(如word2Vec,doc2vec等)
詞袋法即對每個詞語進行排列,然後根據詞語出現次數為其賦值。
詞集法則是對詞袋法做一個閾值為1的二值化操作,也就是有則為1,沒有則為0。
TF-IDF即TF與IDF的乘積,TF為詞頻,IDF為逆向文件頻率。其目的類似於去高頻噪音。
TF=該詞出現次數/所有文本總詞數
IDF=log(總文本數/該詞出現的文本數)
其中,詞袋法和TF-IDF在sklearn中都有現成的庫,並且都自帶有ngram_range參數,也就是自帶有N元語言模型,可以嵌套使用。
代碼示例:
統計出分詞後每個詞對應的頻數,然後進行賦值。
對於英文文本,配合二元語言模型使用,通常效果可能會更好一些。
對詞出現的頻數進行二值化處理,大於0次則為1,否則則為0。
這裡使用了兩種方式。
一種是使用CountVectorizer和TfidfTransformer配合使用。
因為TfidfTransformer無法直接處理單詞或短語,只會對數值進行進一步的處理。
所以要想直接對單詞或短語處理的話,可以直接使用TfidfVectorizer來處理。
另外,CountVectorizer和TfidfVectorizer都有ngram_range的參數,但是ngram_range需要傳入一個區間範圍,比如示例中(2,2)即代表以2元語言模型為基準,(1,1,)則代表使用一元語言模型,(3,3)即代表使用三元語言模型,當然,也可以同時使用多個,比如(2,4)等等,只是短語構成的單詞越多,越容易得到稀疏矩陣,效果可能會相對越差。
而wordEmbedding相關內容,這裡暫且不提,後續有機會的話咱們在RNN相關內容裡再進一步闡述,有興趣可以先自行了解。
很多時候,如果我們分析文本來進行提取特徵的話,使用所有的句子和短語似乎會有一些多餘。或者說會有很多噪音信息。
雖然我們可能會做高頻詞和低頻詞的處理,然後我們會進一步使用TF-IDF進行高頻噪音詞的處理。
但比如說我們要對一個文本做分類的話,可能我們更關心文本中可能出現的一些名詞,比如我們看到籃球,看到了科比,那麼我們大概率會認為這是和體育雜誌相關。
因此,我們還可以對文本進行進一步的詞性提取。
將單詞映射到詞性的模型通常和特定的語言有關。
這裡我們選擇的依然是jieba。
當然,除了jieba,還有很多其他選擇,比如snownlp,THULAC,pyhanlp等等。
這裡做了簡單的分詞後進行進一步的詞性分析。
flag對應為當前單詞的詞性。
但凡以n打頭的詞性,都可以理解為名詞,比如nr代表人名,ns代表地名等等。
但凡以v打頭的詞性,都可以理解為動詞。
以a打頭的詞,都是形容詞。以r打頭的詞,都是代詞。
這裡只是簡單舉了幾種示例,具體可以自行查詢一下詞性標註表。
那麼有了詞性分析後,假如我們想要做文本分類,那麼我們可以對所有文本的名詞,動詞分別進行抽取,然後各自單獨作為一列特徵,看起來,相比完整的文本而言,對我們來說,可能會更有效果。
另外,對於英文文本,也同樣可以有相應的詞性分析。這裡我們就不進一步展開了。
所以,簡單對語句進行按詞性的拆解過濾,就可以按如下方式進行。
這裡對語句中的所有動詞和名詞都進行了簡單篩選,同時對重複的詞語做了一下去重。
關於這裡的去重操作,可以理解為二值化的思想,我們只想知道有哪些詞,但是並不關心其出現的頻數。比如對於文本而言,出現一個葫蘆娃和出現七個葫蘆娃,對我們本身分類可能並沒有太大區別。當然,具體的業務場景需求,再進行具體的進一步修正即可。
此時對文本進行有效信息提取之後,我們再進一步按前面對應的方式進行數值化。
整體而言,數據也會顯得更加高效乾淨一些。
那麼到這裡,關於文本轉數值就基本講完了。
離散型數據的處理
一般來說,對於離散型數據,我們有以下一些數值化處理的方式。
1、獨熱編碼OneHot Encoding
2、啞編碼get_dummies
3、字典特徵提取編碼DictVectorizer
4、map映射
5、順序編碼OrdinalEncoder :一般特徵專用
6、類別編碼Label Encoding :一般標籤專用
我們以實踐為主,因此,我們先展示這六種方式對應的代碼實現,然後再簡單說說各自的優缺點及不同場景下的選擇參考指標。
代碼示例:
如果遇到類似於Onehot could not convert string to float: 'F'
請通過conda prompt升級sklearn版本: conda update scikit-learn
這裡提供三種常見的操作方式,即分別對於一列操作,多列操作以及全數據集操作。
看起來,在表現形式上啞編碼和OneHot沒啥區別。
但實際上因為get_dummies是屬於pandas的功能,而OneHot則是屬於sklearn的功能。
sklearn的API基本上都有一個共性,以OneHot為例,可以在訓練集上進行fit,或者進行fit_transform進行轉換,並且,可以直接對測試集進行transform,這就解決了,測試集可能存在數據量很少或者說數據特徵存在取值缺失的情況。因為此時的transform,是根據訓練集得到的。
而get_dummies則是pandas的功能,這就導致了,get_dummies不具備數據集的遷移的特性。
因此,嚴格意義上來說,get_dummies只能用於一個數據集上數據的轉換。
所以通常情況下,能用get_dummies的地方,我們都可以用OneHot來轉換。
這裡我們同樣給出了兩種形式,一種是單列或多列,另外一種是全數據集的情況。
這裡提供了兩種map的映射方式。
第一種是通過enumerate自動生成數字索引,比較方便,但是會失去類型變量之間的含義。
第二種需要自定義各個對應類別的數值,相對繁瑣,但是能保留部分數值含義。
比如使用樹模型時,大於2,就意味著學歷大於高中。
這裡也分成三種常見方式進行分別處理,即針對單列,多列以及全部數據集的情況。
另外,這裡每種分類對應的取值可以通過ord_encoder.categories_進行查看。
這裡和上面其他比較大的區別在於,labelEncoding本身只能針對於一列處理,這也符合其設計初衷,是針對標籤列處理的,即便存在多標籤,那麼我們也可以如代碼示例的方式進行多列分別處理。
那麼,常見的幾種離散類型數據的處理我們已經給出了基本的代碼使用方式。
離散變量數值化小結
那麼問題來了,什麼情況下該怎麼選擇使用哪種數值化方式呢?
首先,當某個離散變量中其對應的特徵都比較重要,或者說對結果都會產生比較大的影響時,我們更適合使用OneHot,因為OneHot會對每個特徵取值作為一個單獨的特徵維度進行提取。
正因為OneHot會將特徵的每一個取值都變成一個特徵維度,所以當使用OneHot對多列離散數據進行數值化時,尤其是對全數據集都做OneHot操作時,我們可能會得到一個特徵數量特別多並且整體取值特別稀疏的矩陣。
特徵數量多,就意味著模型複雜,模型複雜,就意味著時間代價更高,並且更容易發生過擬合現象。
當然,也並不是單純的說特徵數量越多就越容易發生過擬合,由欠擬合到擬合再到過擬合需要一個過程,但是無效特徵的增加,對於模型而言,只會增加負面的負擔。
另一方面,當使用樹模型來進行模型訓練時,特徵過多,會導致樹深過深,也就會增加過擬合的概率。當然,樹模型中,比如LightGBM,CatBoost等也能自己處理類別型數據,也就不需要使用OneHot來提前額外做離散數據數值化處理。
啞編碼本身屬於pandas的庫,默認不顯示地寫目標轉換列的話,會對所有非數字列進行轉換。
相比較OneHot而言,啞編碼有一個參數drop_first,是否去除第一個類別,默認參數為False,當為False的時候,和OneHot表現形式一致,當為True的時候,會少一個特徵列,而使用全零來表示該特徵。
DictVectorizer對應的參數需要為字典形式,其獲得的結果,也對應為二維數組。
對於數據本身無大小意義的離散型數據而言,使用OneHot、啞編碼或者DictVectorizer,都是ok的。
個人感覺對於數字本身無大小意義的離散變量,使用OneHot來數值化相對好一些。
而對於數據本身有大小意義的離散型數據而言,而優先考慮使用OrdinalEncoder,LabelEncoder和map映射。
其中OrdinalEncoder主要是用於特徵,並且能保留大小意義,比如說,某列的特徵值為low,mid,high,那麼使用該方式會比較好地得到對應的0,1,2。即在離散化的同時,保留其該有的順序,但是對於複雜數據,比如中文,則無法很好識別其該有的正確順序。
此時使用map映射的方式,則不失為一種較好的選擇。
而對於標籤中離散數據的數值化,則主要使用LabelEncoding。
那麼到這裡,離散數據數值化也就基本清楚了。
更多文章請關注