您現在的位置是:首頁 > 籃球

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

  • 由 雷峰網leiphone 發表于 籃球
  • 2021-12-22
簡介DataFrame(bv_matrix, columns=vocab)使用二元詞袋模型的特徵向量在上面的例子中,每個二元特徵由兩個單片語成,其中的值表示這個二元片語在文件中出現的次數

正則表示式中如何查詢某字的片語

雷鋒網 AI 研習社按:本文是英特爾資料科學家 Dipanjan Sarkar 在 Medium 上釋出的「特徵工程」部落格續篇。在本系列的前兩部分中,作者介紹了連續資料的處理方法和離散資料的處理方法。本文則開始了一個新的主題,

非結構化文字資料的傳統處理方法

。雷鋒網 AI 研習社對原文進行了編譯。

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

文字資料通常是由表示單詞、句子,或者段落的文字流組成。由於文字資料非結構化(並不是整齊的格式化的資料表格)的特徵和充滿噪聲的本質,很難直接將機器學習方法應用在原始文字資料中。在本文中,我們將透過實踐的方法,探索從文字資料提取出有意義的特徵的一些普遍且有效的策略,提取出的特徵極易用來構建機器學習或深度學習模型。

研究動機

想要構建效能優良的機器學習模型,特徵工程必不可少。有時候,可能只需要一個優秀的特徵,你就能贏得 Kaggle 挑戰賽的勝利!對於非結構化的文字資料來說,特徵工程更加重要,因為我們需要將文字流轉化為機器學習演算法能理解的數字表示。即使現在有高階的自動化特徵工程,在把它們當作「黑盒子」應用之前,我們仍有必要去了解不同特徵工程策略背後的核心思想。永遠記住,「如果有人給了你一套修房子的工具,你應該知道什麼時候該用電鑽,什麼時候該用錘子!」

理解文字資料

我們雖然能夠獲得具有結構資料屬性的文字資料,但它們為結構化資料,並不在今天的討論範圍之內。

在本文中,我們討論以單詞、短語、句子和整個文件的形式展現的文字流。從本質上講,文字確實有一些句法結構,比如單片語成了短語,短語組成了句子,句子又組合成了段落。然而,與結構化資料集中固定的資料維度相比,文字文件沒有固定的結構,因為單詞有眾多的選擇,每個句子的長度也是可變的。本文就是一個很典型的案例。

特徵工程的策略

下面是一些流行且有效的處理文字資料的策略,這些方法也能應用在下游的機器學習系統中,用於提取有用的特徵。大家可以在 GitHub 中檢視本文使用的所有程式碼。

首先載入一些基本的依賴關係和設定:

import pandas as pd

import numpy as np

import re

import nltk

import matplotlib。pyplot as pltpd。options。display。max_colwidth = 200

%matplotlib inline

下面是文件中的語料庫,本文大部分內容都是基於該資料集的分析。語料庫通常是屬於一個或多個主題的文件的集合。

corpus = [‘The sky is blue and beautiful。’,

‘Love this blue and beautiful sky!’,

‘The quick brown fox jumps over the lazy dog。’,

“A king‘s breakfast has sausages, ham, bacon, eggs, toast and beans”,

’I love green eggs, ham, sausages and bacon!‘,

’The brown fox is quick and the blue dog is lazy!‘,

’The sky is very blue and the sky is very beautiful today‘,

’The dog is lazy but the brown fox is quick!‘

labels = [’weather‘, ’weather‘, ’animals‘, ’food‘, ’food‘, ’animals‘, ’weather‘, ’animals‘]

corpus = np。array(corpus)

corpus_df = pd。DataFrame({’Document‘: corpus,

’Category‘: labels})

corpus_df = corpus_df[[’Document‘, ’Category‘]]

corpus_df

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

本文中應用的語料庫案例

可以看到,我們已經從語料庫中提取出幾個不同類別的文件。在討論特徵工程之前,一如往常,首先得做資料預處理,刪除一些不必要的字元、符號和標記。

文字預處理

有很多種對文字資料進行清洗和預處理的方法。下面我將重點介紹在自然語言處理(NLP)流程中大量使用的方法。

刪除標籤:文字中通常會包含一些不必要的內容,比如 HTML 標籤,這在分析文字時並沒有太多價值。BeautifulSoup 庫提供了清理標籤的函式。

清理重音字元:在許多文字語料庫中,特別是在處理英文時,通常會遇到重音字元/字母。因此我們要確保將這些字元轉換為標準的 ASCII 字元。一個簡單的例子就是將 é 轉換成 e。

拓展縮寫:在英文中,縮寫基本上是單詞或者音節的縮減版。縮減版通常是刪除某些單詞或者短語中特定的字母和聲音而來。舉例來說,do not 和 don’t , I would 和 I‘d。將縮寫單詞轉換為完整的原始形式有助於文字的標準化。

刪除特殊字元:特殊字元和非字母數字的符號通常會增加額外噪聲。通常,可以透過簡單的正則表示式來實現這一點。

詞幹提取和詞性還原:可以利用詞幹創造新的詞彙,例如透過附加字首和字尾等詞綴來創造新的單詞。這被稱為詞性變化。詞幹提取是將這個過程反過來。一個簡單的例子是單詞:WATCHES, WATCHING, 和 WATCHED,這些單詞都把 WATCH 作為詞根。詞性還原與詞幹提取很相似,透過移除詞綴以得到單詞的基本形式。然而在詞性還原裡,單詞的基本形式是詞根(root word),而不是詞幹(root stem)。其不同之處在於詞根(root word)總是字典上正確的詞(即出現在詞典中),但詞幹並不是這樣。

去除無用詞:在從文字中構建有意義的特徵時,沒有意義的詞被稱為無用詞。如果你在一個語料庫中做一個簡單的詞頻分析,這些無用詞通常會以最大的頻率出現。像 a , an 這樣的詞被認為是無用詞。但是實際上並沒有明確通用的無用詞表,我們通常使用 nltk 的標準英語無用詞表。大家也可以根據特定的需要新增無用詞。

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

除此之外,還可以使用其他的標準操作,比如標記化、刪除多餘的空格、文字大寫轉換為小寫,以及其他更高階的操作,例如拼寫更正、語法錯誤更正、刪除重複字元等。

由於本文的重點是特徵工程,我們將構建一個簡單的文字預處理程式,其重點是刪除特殊字元、多餘的空格、數字、無用詞以及語料庫的大寫轉小寫。

wpt = nltk。WordPunctTokenizer()

stop_words = nltk。corpus。stopwords。words(’english‘)

def normalize_document(doc):

# lower case and remove special characters\whitespaces

doc = re。sub(r’[^a-zA-Z\s]‘, ’‘, doc, re。I|re。A)

doc = doc。lower()

doc = doc。strip()

# tokenize document

tokens = wpt。tokenize(doc)

# filter stopwords out of document

filtered_tokens = [token for token in tokens if token not in stop_words]

# re-create document from filtered tokens

doc = ’ ‘。join(filtered_tokens)

return doc

normalize_corpus = np。vectorize(normalize_document)

一旦搭建好基礎的預處理流程,我們就可以將它應用在語料庫中了。

norm_corpus = normalize_corpus(corpus)

norm_corpus

Output

————

array([’sky blue beautiful‘, ’love blue beautiful sky‘,

’quick brown fox jumps lazy dog‘,

’kings breakfast sausages ham bacon eggs toast beans‘,

’love green eggs ham sausages bacon‘,

’brown fox quick blue dog lazy‘,

’sky blue sky beautiful today‘,

’dog lazy brown fox quick‘],

dtype=’

上面的輸出結果應該能讓大家清楚的瞭解樣本文件在預處理之後的樣子。現在我們來開始特徵工程吧!

詞袋模型(Bag of Word)

這也許是非結構化文字中最簡單的向量空間表示模型。向量空間是表示非結構化文字(或其他任何資料)的一種簡單數學模型,向量的每個維度都是特定的特徵/屬性。詞袋模型將每個文字文件表示為數值向量,其中維度是來自語料庫的一個特定的詞,而該維度的值可以用來表示這個詞在文件中的出現頻率、是否出現(由 0 和 1 表示),或者加權值。將這個模型叫做詞袋模型,是因為每個文件可以看作是裝著單詞的袋子,而無須考慮單詞的順序和語法。

from sklearn。feature_extraction。text import CountVectorizer

cv = CountVectorizer(min_df=0。, max_df=1。)

cv_matrix = cv。fit_transform(norm_corpus)

cv_matrix = cv_matrix。toarray()

cv_matrix

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

可以看到,文件已經被轉換為數字向量,這樣每個文件都由上述特徵矩陣中的一個向量(行)表示。下面的程式碼有助於以一種更易理解的格式來表示這一點。

# get all unique words in the corpus

vocab = cv。get_feature_names()

# show document feature vectors

pd。DataFrame(cv_matrix, columns=vocab)

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

詞袋模型的文件特徵向量

上面的表格應該更能助於理解!可以清楚地看到,特徵向量中每個列(維度)都代表一個來自語料庫的單詞,每一行代表一個文件。單元格中的值表示單詞(由列表示)出現在特定文件(由行表示)中的次數。因此,如果一個文件語料庫是由 N 個單片語成,那麼這個文件可以由一個

N 維向量

表示。

N 元詞袋模型(Bag of N-Gram Model)

一個單詞只是一個標記,通常被稱為單元(unigram)或者一元(1-gram)。我們已經知道,詞袋模型不考慮單詞的順序。但是如果我們也想要考慮序列中出現的短語或者詞彙集合呢?N 元模型能夠幫我們實現這一點。N-Gram 是來自文字文件的單詞記號的集合,這些記號是連續的,並以序列的形式出現。二元表示階數為二的 N-Gram,也就是兩個單詞。同理三元表示三個單詞。N 元詞袋模型是普通詞袋模型的一種拓展,使得我們可以利用基於 N 元的特徵。下面的示例展示了文件中二元的特徵向量。

# you can set the n-gram range to 1,2 to get unigrams as well as bigrams

bv = CountVectorizer(ngram_range=(2,2))

bv_matrix = bv。fit_transform(norm_corpus)

bv_matrix = bv_matrix。toarray()

vocab = bv。get_feature_names()

pd。DataFrame(bv_matrix, columns=vocab)

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

使用二元詞袋模型的特徵向量

在上面的例子中,每個二元特徵由兩個單片語成,其中的值表示這個二元片語在文件中出現的次數。

TF-IDF 模型

在大型語料庫中使用詞袋模型可能會出現一些潛在的問題。由於特徵向量是基於詞的頻率,某些單詞可能會在文件中頻繁出現,這可能會在特徵集上掩蓋掉其他單詞。TF-IDF 模型試圖透過縮放或者在計算中使用歸一化因子來解決這個問題。TF-IDF 即 Term Frequency-Inverse Document Frequency,在計算中結合了兩種度量:

詞頻(Term Frequency)

逆文件頻率(Inverse Document Frequency)

。這種技術是為搜尋引擎中查詢排序而開發的,現在它是資訊檢索和 NLP 領域中不可或缺的模型。

在數學上,TF-IDF 可以定義為:

tfidf = tf x idf

,也可以進一步拓展為下面的表示:

在這裡,

tfidf(w, D)

表示單詞

w

在文件

D

中的 TF-IDF 分數。

Tf(w,D)

項表示單詞

w

在文件

D

中的詞頻,這個值可以從詞袋模型中獲得。

idf(w,D)

項是單詞

w

的逆文件頻率,可以由語料庫中所有文件的總數量

C

除以單詞

w

的文件頻率

df(w)

的 log 值得到,其中文件頻率是指語料庫中文件出現單詞

w

的頻率。這種模型有多種變種,但是給出的最終結果都很相似。下面在語料庫中使用這個模型吧!

from sklearn。feature_extraction。text import TfidfVectorizer

tv = TfidfVectorizer(min_df=0。, max_df=1。, use_idf=True)

tv_matrix = tv。fit_transform(norm_corpus)

tv_matrix = tv_matrix。toarray()

vocab = tv。get_feature_names()

pd。DataFrame(np。round(tv_matrix, 2), columns=vocab)

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

基於TF-IDF模型的文件特徵向量

基於 TF-IDF 的特徵向量與原始的詞袋模型相比,展示出了縮放和歸一化的特性。想要進一步深入瞭解該模型的讀者可以參考 Text Analytics with Python 的 181 頁。

文件相似性

文件相似性是使用從詞袋模型或者 tf-idf 模型中提取出的特徵,基於距離或者相似度度量判斷兩個文件相似程度的過程。

因此,可以使用在上一部分中提到的 tf-idf 模型提取出的特徵,用其來生成新的特徵。這些特徵在搜尋引擎、文件聚類以及資訊檢索等領域發揮著重要作用。

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

語料庫中的配對文件相似性需要計算語料庫中每兩個文件對的文件相似性。因此,如果一個語料庫中有 C 個文件,那麼最終會得到一個 C*C 的矩陣,矩陣中每個值代表了該行和該列的文件對的相似度分數。可以用幾種相似度和距離度量計算文件相似度。其中包括餘弦距離/相似度、歐式距離、曼哈頓距離、BM25相似度、jaccard 距離等。在我們的分析中,我們將使用最流行和最廣泛使用的相似度度量:餘弦相似度,並根據 TF-IDF 特徵向量比較文件對的相似度。

from sklearn。metrics。pairwise import cosine_similarity

similarity_matrix = cosine_similarity(tv_matrix)

similarity_df = pd。DataFrame(similarity_matrix)

similarity_df

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

文件對的相似性矩陣(餘弦相似度)

餘弦相似度給出了表示兩個文件特徵向量之間角度的餘弦值的度量。兩個文件特徵向量之間的角度越低,兩個文件的相似度就越高,如下圖所示:

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

仔細觀察相似度矩陣可以清楚地看出,文件(0,1 和 6),(2,5 和 7)之間非常相似,文件 3 和 4 略微相似。這表明了這些相似的文件一定具有一些相似特徵。這是分組或聚類的一個很好的案例,可以透過無監督的學習方法來解決,特別是當需要處理數百萬文字文件的龐大語料庫時。

具有相似特徵的文件聚類

聚類是利用無監督學習的方法,將資料點(本場景中即文件)分類到組或者 cluster 中。我們將在這裡利用一個無監督的層次聚類演算法,透過利用我們之前生成的文件相似性特徵,將我們的玩具語料庫中的類似文件聚合到一起。有兩種型別的層次聚類方法,分別是凝聚方法(agglomerative)和分裂方法(divisive)。這裡將會使用凝聚聚類演算法,這是一種自下而上(bottom up)的層次聚類演算法,最開始每個文件的單詞都在自己的類中,根據測量資料點之間的距離度量和連線準則(linkage criterion),將相似的類連續地合併在一起。下圖展示了一個簡單的描述。

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

連線準則決定了合併策略。常用的連線準則有 Ward, Complete linkage, Average linkage 等等。這些標準在將一對 cluster 合併在一起(文件中低層次的類聚類成高層次的)時是非常有用的,這是透過最最佳化目標函式實現的。我們選擇 Ward 最小方差作為連線準則,以最小化總的內部聚類方差。由於已經有了相似特徵,我們可以直接在樣本文件上構建連線矩陣。

from scipy。cluster。hierarchy import dendrogram, linkage

Z = linkage(similarity_matrix, ’ward‘)

pd。DataFrame(Z, columns=[’Document\Cluster 1‘, ’Document\Cluster 2‘,

’Distance‘, ’Cluster Size‘], dtype=’object‘)

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

我們語料庫的連線矩陣

如果仔細檢視連線矩陣,可以看到連線矩陣的每個步驟(行)都告訴了我們哪些資料點(或者 cluster)被合併在一起。如果有 n 個數據點,那麼連線矩陣 Z 將是(n-1)*4 的形狀,其中 Z[i] 表示在步驟 i 合併了哪些 cluster。每行有四個元素,前兩個元素是資料點或 cluster 的名稱,第三個元素是前兩個元素(資料點或 cluster)之間的距離,最後一個元素是合併完成後 cluster 中元素/資料點的總數。大家可以參考 scipy 文件,其中有詳細解釋。

下面,把這個矩陣看作一個樹狀圖,以更好地理解元素!

plt。figure(figsize=(8, 3))

plt。title(’Hierarchical Clustering Dendrogram‘)

plt。xlabel(’Data point‘)

plt。ylabel(’Distance‘)

dendrogram(Z)

plt。axhline(y=1。0, c=’k‘, ls=’——‘, lw=0。5)

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

可以看到每個資料點是如何從一個單獨的簇開始,慢慢與其他資料點合併形成叢集的。從顏色和樹狀圖的更高層次來看,如果考慮距離度量為 1。0(由虛線表示)或者更小,可以看出模型已經正確識別了三個主要的聚類。利用這個距離,我們可以得到叢集的標籤。

from scipy。cluster。hierarchy import fcluster

max_dist = 1。0

cluster_labels = fcluster(Z, max_dist, criterion=’distance‘)

cluster_labels = pd。DataFrame(cluster_labels, columns=[’ClusterLabel‘])

pd。concat([corpus_df, cluster_labels], axis=1)

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

可以清楚地看到,我們的演算法已經根據分配給它們的標籤,正確識別了文件中的三個不同類別。這應該能夠給大家一個關於如何使用 TF-IDF 特徵來建立相似度特徵的思路。大家可以用這種處理流程來進行聚類。

主題模型

也可以使用一些摘要技術從文字文件中提取主題或者基於概念的特徵。主題模型圍繞提取關鍵主題或者概念。每個主題可以表示為文件語料庫中的一個詞袋或者一組詞。總之,這些術語表示特定的話題、主題或概念,憑藉這些單詞所表達的語義含義,可以輕鬆將每個主題與其他主題區分開來。這些概念可以從簡單的事實、陳述到意見、前景。主題模型在總結大量文字來提取和描繪關鍵概念時非常有用。它們也可用於從文字資料中捕捉潛在的特徵。

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

主題建模有很多種方法,其中大多涉及到某種形式的矩陣分解。比如隱含語義索引(Latent Semantic Indexing, LSI)就使用了奇異值分解。這裡將使用另一種技術:隱含狄利克雷分佈(Latent Dirichlet Allocation, LDA),它使用了生成機率模型,其中每個文件由幾個主題組合而成,每個術語或單詞可以分配給某個主題。這與基於 pLSI(probabilistic LSI)的模型很類似。在 LDA 的情況下,每個隱含主題都包含一個狄利克雷先驗。

這項技術背後的數學原理相當複雜,所以我會試著總結一下,而不是羅列很多讓人厭倦的細節。我建議讀者可以看看 Christine Doig 的一個優秀的演講,深入瞭解一下。

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

上圖中的黑色框表示利用前面提到的引數,從 M 個文件中提取 K 個主題的核心演算法。下面的步驟是對演算法的解釋。

初始化必要的引數。

隨機初始化文件,將每個單詞分配到 K 個主題中去。

按照如下方法迭代

對於每個文件 D:

a) 對於文件中的單詞 W:

i。對於主題 T:

計算 P(T|D), 表示文件 D 中單詞分配給 T 主題的比例。

計算 P(W|T),表示在所有文件中,主題 T 包含單詞 W 的比例。

ii。 透過計算機率 P(T|D)*P(W|T) 重新分配單詞 W 的主題 T。

執行幾個迭代之後,就能獲得混合了每個文件的主題,然後就可以根據指向某個主題的單詞生成文件的主題。像 gensim 或者 scikit-learn 這樣的框架,使得我們能夠利用 LDA 模型來生成主題。

大家應該記住,當 LDA 應用於文件-單詞矩陣(TF-IDF 或者詞袋特徵矩陣)時,它會被分解為兩個主要部分:

文件-主題矩陣,也就是我們要找的特徵矩陣

主題-單詞矩陣,能夠幫助我們檢視語料庫中潛在的主題

使用 scikit-learn 可以得到如下的文件-主題矩陣。

from sklearn。decomposition import LatentDirichletAllocation

lda = LatentDirichletAllocation(n_topics=3, max_iter=10000, random_state=0)

dt_matrix = lda。fit_transform(cv_matrix)

features = pd。DataFrame(dt_matrix, columns=[’T1‘, ’T2‘, ’T3‘])

features

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

可以清楚地看到哪些文件對上述輸出中的三個主題貢獻最大,可以透過如下的方式檢視主題及其組成部分。

tt_matrix = lda。components_

for topic_weights in tt_matrix:

topic = [(token, weight) for token, weight in zip(vocab, topic_weights)]

topic = sorted(topic, key=lambda x: -x[1])

topic = [item for item in topic if item[1] > 0。6]

print(topic)

print()

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

可以看到,由於組成術語不同,很容易區分這三個主題。第一個在討論天氣,第二個關於食物,最後一個關於動物。主題建模的主題數量選擇是一門完整的課題,既是一門藝術,也是一門科學。獲得最優主題數量的方法有很多,這些技術既複雜又繁瑣,這裡就不展開討論了。

使用主題模型特徵的文件聚類

這裡使用 LDA 法從詞袋模型特徵構建主題模型特徵。現在,我們可以利用獲得的文件單詞矩陣,使用無監督的聚類演算法,對文件進行聚類,這與我們之前使用的相似度特徵進行聚類類似。

這次我們使用非常流行的基於分割槽的聚類方法——K-means 聚類,根據文件主題模型特徵表示,進行聚類或分組。在 K-means 聚類法中,有一個輸入引數 K,它制定了使用文件特徵輸出的聚類數量。這種聚類方法是一種基於中心的聚類方法,試圖將這些文件聚類為等方差的類。這種方法透過最小化類內平方和來建立聚類。選擇出最優的 K 的方法有很多,比如誤差平方和度量,輪廓係數(Silhouette Coefficients)和 Elbow method。

from sklearn。cluster import KMeans

km = KMeans(n_clusters=3, random_state=0)

km。fit_transform(features)

cluster_labels = km。labels_

cluster_labels = pd。DataFrame(cluster_labels, columns=[’ClusterLabel‘])

pd。concat([corpus_df, cluster_labels], axis=1)

如何對非結構化文字資料進行特徵工程操作?這裡有妙招!

從上面的輸出中可以看到,文件的聚類分配完全正確。

未來會涉及到的高階策略

在這篇文章沒有涉及近期出現的一些關於文字資料特徵工程的高階方法,包括利用深度學習模型來提取單詞特徵的方法。我們將在本系列的下一部分中深入探討這些模型,並詳細介紹 Word2Vec 和 GloVe 等流行的單詞嵌入模型,敬請期待!

總結

這些例子應該能有助於大家理解文字資料特徵工程的一些通用策略。本文中介紹的是基於數學概念、資訊檢索和自然語言處理的傳統策略,這些久經考驗的方法在各種資料集和問題上都表現優異。在下一篇文章中,我將詳細介紹如何利用深度學習模型進行文字資料特徵工程。

對連續資料特徵工程感興趣的讀者,請檢視本系列第一部分!

對離散資料特徵工程感興趣的讀者,請檢視本系列第二部分!

本文中所使用的所有程式碼和資料集都可以從 GitHub 中訪問。程式碼也可以作為 Jupyter 筆記本使用。

Via towardsdatascience。com 雷鋒網 AI 研習社編譯整理。

Top