1、特征工程
1.1 概念
特征工程(Feature Engineering)是機器學習項目中非常關鍵的一步,它是指通過領域知識來選擇、創建或修改能夠使機器學習模型更好地工作的特征(即輸入變量)。特征工程的目標是提高模型的性能,通過提取數據中有用的信息并將其轉換為算法可以使用的格式。良好的特征工程可以使模型更加高效,有時候甚至比選擇更復雜的模型更加重要
一般使用 pandas 來進行數據清洗和數據處理、然后使用 sklearn 庫來進行特征工程處理
特征工程是將任意數據(文本、圖像等)轉換為可用于機器學習的數字特征,比如:字典特征提取、文本特征提取、圖像特征提取
特征工程步驟:
數據理解與預處理:首先需要理解數據集的特性,包括數據的分布、缺失值情況、異常值,去除重復記錄、處理缺失值、糾正錯誤數據等
特征生成:根據業務理解或領域知識創造新的特征、將非數值特征轉換為數值形式
特征選擇:利用統計檢驗、相關性分析或其他特征選擇技術來確定哪些特征對模型最為重要。這有助于減少維度,同時保持模型的有效性
特征變換:對原始特征進行轉換,比如使用對數變換來處理偏斜的數據分布,或者使用多項式特征擴展來增加模型的非線性能力
特征編碼:對于類別特征,需要進行編碼處理,比如獨熱編碼(one-hot encoding)或標簽編碼(label encoding)
標簽編碼是一種簡單的編碼方式,它將每個類別標簽用唯一的整數表示。例如,如果有三個類別:'apple', 'banana', 'cherry',標簽編碼可能會將它們分別編碼為 0, 1, 2。標簽編碼適用于有序分類變量(ordinal variables),即這些類別的順序是有意義的,例如教育水平或大小等級。然而,對于沒有自然順序的名義變量(nominal variables),直接使用標簽編碼可能會引入人為的順序關系,這可能會影響模型的性能,因為模型可能會假定數值上較大的類別比數值上較小的類別更重要
獨熱編碼是一種將名義變量轉換為數值型數據的方式,它創建了一個新的二進制列(即只有 0 或 1 的列)用于每一個可能的類別值。例如,對于上述的類別'apple', 'banana', 'cherry',獨熱編碼會創建三列,每列對應一個類別,每一行中只有一個類別被標記為 1,其余為 0。這種方式避免了引入任何順序上的意義,因此適用于沒有內在順序關系的類別變量
特征縮放:確保所有特征都在相似的尺度上,即歸一化、標準化
降維:當特征數量過多時,可以采用降維技術如PCA(主成分分析)來減少特征數量,同時盡可能保留原始數據的信息
1.2 特征工程 API
特征工程API(Feature Engineering API)是指提供自動化或半自動化特征工程功能的服務接口,這些API允許開發者或數據科學家通過編程方式來處理數據集,從而簡化特征工程的工作流程
常用的 API 如下:
名稱 | 作用 |
---|---|
DictVectorizer | 字典特征提取 |
CountVectorizer | 文本特征提取 |
TfidfVectorizer | TF-IDF文本特征詞的重要程度特征提取 |
MinMaxScaler | 歸一化 |
StandardScaler | 標準化 |
VarianceThreshold | 底方差過濾降維 |
PCA | 主成分分析降維 |
常用方法和功能介紹如下:
函數名 | 參數 | 作用 |
---|---|---|
fit_transform(list_of_dicts) | 一個包含字典的列表 | fit 和transform 兩個方法的合成 |
fit(list_of_dicts) | 一個包含字典的列表 | fit 方法用來計算數據的統計信息,比如訓練集的均值,方差,最大值,最小值等,只要fit 之后,如果存在多個數據(比如 train、test)需要transform ,可以直接transform |
transform(list_of_dicts) | 一個包含字典的列表 | transform 方法根據fit 方法所學習到的參數來轉換數據。這可能意味著標準化特征,填充缺失值,編碼類別變量等 |
get_feature_names_out() | 返回一個包含所有特征名稱的列表,這些特征是在 fit 或 fit_transform 方法中學習到的 | |
inverse_transform(X) | fit_transform生成 | 將特征矩陣轉換回原來的字典形式 |
1.2.1 DictVectorizer-字典特征提取
在矩陣中,若數值為0的元素數目遠遠多于非0元素的數目,并且非0元素分布沒有規律時,則稱該矩陣為稀疏矩陣,與之相反,若非0元素數目占大多數時,則稱該矩陣為稠密矩陣。定義非零元素的總數比上矩陣所有元素的總數為矩陣的稠密度
在 Python 里,稀疏矩陣和稠密矩陣的“定義”不是按數學上零元素比例來自動判斷的,而是由你用的存儲數據類型/類來決定的。
稀疏矩陣通常由三部分組成:
數據 (data): 存儲非零元素的值
索引 (indices): 指出每個非零元素所在的列
指針 (indptr): 指出每行第一個非零元素的位置
非稀疏矩陣:非稀疏矩陣實際上就是普通的矩陣或稠密矩陣,它們是由一系列元素按照一定的行和列排列組成的數學結構。當我們談論非稀疏矩陣的組成部分時,主要指的是它的行、列以及矩陣中的元素
稀疏矩陣調用以下函數可以轉為稠密矩陣
todense()
返回的是一個numpy.matrix
對象toarray()
返回的是一個numpy.ndarray
對象
DictVectorizer
是一個用于將特征集合(通常是以字典形式表示的)轉換為向量空間模型的工具。它能夠將一系列字典轉換成一個特征矩陣,其中每一行代表一個樣本,每一列表示一個特征DictVectorizer
實例化參數:sparse(bool)
: 布爾值,默認值為True
。如果為True
,輸出是三元組的稀疏矩陣;如果為False
,輸出是Numpy數組dtype(numpy.dtype)
: 指定輸出矩陣的數據類型,默認為numpy.float64
separator(str)
: 當特征名包含嵌套鍵時,指定嵌套鍵之間的分隔符,默認為'='
提取為稀疏矩陣案例:
from sklearn.feature_extraction import DictVectorizer import pandas as pd ? def dict_dictVectorizer():data = [{'username': '小明', 'password': '111', 'age': 10},{'username': '小紅', 'password': '222', 'age': 11},{'username': '小明', 'password': '333', 'age': 12},]vec = DictVectorizer(sparse=True)vec_new = vec.fit_transform(data)print('稀疏矩陣類型:\n', type(vec_new))print('稀疏矩陣:\n', vec_new)# 特征名print('特征名:\n', vec.get_feature_names_out())csr_matrix = vec_new.todense()print('轉為稠密矩陣:\n', csr_matrix)
提取為二維數組案例:
from sklearn.feature_extraction import DictVectorizer ? def dict_dictVectorizer2():data = [{'username': '小明', 'password': '111', 'age': 10},{'username': '小紅', 'password': '222', 'age': 11},{'username': '小明', 'password': '333', 'age': 12},]vec = DictVectorizer(sparse=False)vec_new = vec.fit_transform(data)print('非稀疏矩陣類型:\n', type(vec_new))print('非稀疏矩陣:\n', vec_new)print('特征名:\n', vec.get_feature_names_out())
1.2.2 CountVectorizer-文本特征提取
CountVectorizer
是一個在自然語言處理(NLP)任務中常用的工具,用于將文本數據轉換為向量形式。它屬于sklearn.feature_extraction.text
模塊的一部分,是Scikit-learn庫提供的功能之一。CountVectorizer
主要通過以下方式工作:詞匯表構建:首先根據訓練集中的文檔創建一個固定的詞匯表(即所有唯一詞的集合)。每個詞在詞匯表中都有一個對應的索引位置
詞頻統計:對于每個文檔,
CountVectorizer
會計算出詞匯表中每個詞出現的次數,并忽略那些未出現在文檔中的詞輸出格式:最終的輸出是一個稀疏矩陣(sparse matrix),每一行對應一個文檔,每一列對應詞匯表中的一個詞。矩陣中的每個元素表示某個詞在某個文檔中出現的次數
CountVectorizer
默認會忽略一些英文中的停用詞(如“is”、“the”等),并且會對單詞進行小寫化處理。這些行為可以通過傳遞參數來調整,例如設置stop_words='english'
可以顯式地過濾英語停用詞,而lowercase=False
則可以禁用自動小寫化處理CountVectorizer
實例化參數:stop_words
(string or list, default=None):如果為english
,則會移除英語停用詞。也可以傳入一個包含停用詞的自定義列表,如果是單個英文字母,會忽略,比如i
lowercase
(bool, default=True):是否將文本轉換為小寫
英文文本案例:
from sklearn.feature_extraction.text import CountVectorizer import pandas as pd ? def english_countVectorizer():data = ["Life is is short", "I am learning Python"]# 創建轉換器對象transfer = CountVectorizer(stop_words=["is"])# 進行提取,得到非稀疏矩陣data_new = transfer.fit_transform(data)print(data_new)df = pd.DataFrame(data_new.toarray(),index=["第一個句子", "第二個句子"],columns=transfer.get_feature_names_out())print(df)
中文文本案例:
安裝中文分詞庫
jieba
,命令:·pip install jieba
from sklearn.feature_extraction.text import CountVectorizer import pandas as pd import jieba ? def cut(text):return " ".join(list(jieba.cut(text))) def chinese_countVectorizer():data = ["我愛北京天安門", "天安門上太陽升"]data_new = [cut(v) for v in data]transfer = CountVectorizer()data_final = transfer.fit_transform(data_new)print(transfer.get_feature_names_out())df = pd.DataFrame(data_final.toarray(),index=["第一個句子", "第二個句子"],columns=transfer.get_feature_names_out())print(df)
1.2.3 TfidfVectorizer-重要文本特征提取
TF-IDF(Term Frequency-Inverse Document Frequency,詞頻-逆文檔頻率)是一種常用的文本特征表示方法,用于評估一個詞對一個文檔或語料庫中的重要程度,比如如果某個詞或短語在一篇文章中出現的概率高,并且在其他文章中很少出現,則認為此詞或者短語具有很好的類別區分能力,適合用來分類
詞頻:詞語在文檔中出現的頻率
逆文檔頻率:詞語在整個文檔集合中的重要程度
TF-IDF 稀有文本特征提取將 CountVectorizer和 TfidfTransformer 的所有功能組合在一個模型中,TfidfTransformer 將詞頻/字符頻數矩陣轉換為標準化的 tf 或 tf-idf 矩陣
TF-IDF是兩個指標的乘積:詞頻(TF)和逆文檔頻率(IDF),兩個部分的計算公式如下:
詞頻(TF):詞頻越高,說明該詞在該文檔中越重要
$$
TF(t,d)= \frac{詞 t 在文檔 d 中出現的次數}{文檔 d 中詞的總數}
$$逆文檔頻率(IDF):出現頻率較低的詞語會有較高的 IDF 值,從而提高其在文檔中的權重,這里加1是為了防止除數為零的情況,并且確保 IDF 不會為負數
$$
IDF(t)=log(\frac{文檔總數+1}{詞 t 在文檔 d 中出現的次數+1})+1
$$TF-IDF:通過 TF-IDF 評分,重要的詞語(即在該文檔中頻繁出現而在其他文檔中不常見的詞語)會得到較高的權重
$$
TF-IDF(t,d)=TF(t,d)×IDF(t)
$$TfidfVectorizer 是 scikit-learn 提供的一個工具,用于將文本數據轉換為 TF-IDF 特征矩陣,函數介紹如下:
TfidfVectorizer()
:用來初始化 TfidfVectorizer 對象,它屬于sklearn.feature_extraction.text
模塊的一部分,常用參數如下:stop_words
(string or list, default=None):同CountVectorizer
use_idf
:布爾值,是否啟用逆文檔頻率(IDF)加權,默認值為True
smooth_idf
:布爾值,是否加1平滑IDF,默認值為True
norm
:字符串,表示歸一化方法,'l1'
或'l2'
,默認值為'l2'
案例:
from sklearn.feature_extraction.text import TfidfVectorizer ? def english_tfidfVectorizer():# 創建一個TfidfVectorizer對象tfidf = TfidfVectorizer()# 創建一個DataFrame對象data = ['This is the first document','This is the second document','And the third one','Is this the first document',]# 使用fit_transform方法對文本進行向量化X = tfidf.fit_transform(data)# 打印結果print(X.toarray())print(tfidf.get_feature_names_out())
注意事項:
實踐證明,TfidfVectorizer使用的計算TF-IDF公式如下:
1.2.4 無量綱化
將不同規格的數據轉換到同一規格,或將不同分布的數據轉換到某個特定分布的需求,這種需求統稱為將數據無量綱化
在以梯度和矩陣為核心的算法中,譬如邏輯回歸,支持向量機,神經網絡,無量綱化可以加快求解速度
在距離類模型,譬如K近鄰,KMeans聚類中,無量綱化可以幫我們提升模型精度,避免某一個取值范圍特別大的特征對距離計算造成影響
決策樹和樹的集成算法,對決策樹不需要無量綱化,決策樹可以把任意數據都處理得很好
數據的無量綱化可以是線性的,也可以是非線性的。線性的無量綱化包括中心化(Zero-centered或者Mean-subtraction)處理和縮放處理(Scale)。中心化的本質是讓所有記錄減去一個固定值,即讓數據樣本數據平移到某個位置(數據歸一化)。縮放的本質是通過除以一個固定值,將數據固定在某個范圍內之中,取對數也算是一種縮放處理(數據標準化)
比如,以下是一個人的基本數據信息:
編號 | 身高 | 收入 | 體重 |
---|---|---|---|
1 | 1.75 | 15000 | 120 |
2 | 1.5 | 16000 | 140 |
3 | 1.6 | 20000 | 100 |
如果通過歐式距離公式來判斷兩個人的差距,公式如下:
$$
L = \sqrt{(1.75-1.5)^2+(15000-16000)^2+(120-140)^2}
$$
上述公式可以看出,基本上都是靠收入來決定了,而身高和體重基本上沒有影響,那么對于實際生活中,身高和體重也要納入到判斷兩個人的差距計算,所以我們應該采取把數據無量綱化
1.2.4.1 歸一化
歸一化是一種線性變換過程,使得數據按照一定的規則重新映射到一個特定的范圍,通常是 [0, 1],通過下面的方式實現:
$$
Xnorm= \frac{X?Xmin}{Xmax?Xmin}
$$
上述公式中,Xmin 和 Xmax 分別是原始數據集中的最小值和最大值,X 表示當前需要歸一化的值
歸一化的目的是為了消除不同特征值之間量級上的差異,從而避免因不同特征值的范圍差異對模型訓練產生不良影響
缺點就是對異常值非常敏感
MinMaxScaler
:是一種用于歸一化數據的技術,通常用于機器學習和數據分析中。它將每個特征的值縮放到一個指定的范圍內,通常是 [0, 1]。MinMaxScaler
是sklearn.preprocessing
模塊中的一個類,可以方便地應用于數據集,MinMaxScaler()
用來實例化對象,參數如下:feature_range
:把數據壓縮到的范圍,默認是(0,1)
數據為列表歸一化案例:最大值和最小值是在對應的列中找
from sklearn.preprocessing import MinMaxScaler ? def min_max_scaler_list():data = [[1, 2], [2, 3], [3, 5], [4, 6], [5, 8]]print('原始數據:\n', data)min_max_scaler = MinMaxScaler()X_minmax = min_max_scaler.fit_transform(data)print('歸一化后數據:\n', X_minmax)
數據為ndarray歸一化案例:
from sklearn.preprocessing import MinMaxScaler from sklearn.feature_extraction import DictVectorizer import numpy as np ? def min_max_scaler_ndarray():data = [{'city': '成都', 'age': 30, 'temperature': 200}, {'city': '重慶', 'age': 33, 'temperature': 60},{'city': '北京', 'age': 42, 'temperature': 80}]transfer = DictVectorizer(sparse=False)data = transfer.fit_transform(data)print('原始數據:\n', data)min_max_scaler = MinMaxScaler()X_minmax = e(data)print('歸一化后數據:\n', X_minmax)
數據為DataFrame歸一化案例:
from sklearn.preprocessing import MinMaxScaler import pandas as pd ? def min_max_scaler_dataframe():data = [[1, 2], [2, 3]]data = pd.DataFrame(data=data, index=["第一行", "第二行"], columns=["第一列", "第二列"])print('原始數據:\n', data)transfer = MinMaxScaler(feature_range=(0, 1))X_minmax = transfer.fit_transform(data)print('歸一化后數據:\n', X_minmax)
1.2.4.2 標準化
在機器學習中,標準化是一種數據預處理技術,也稱為數據歸一化或特征縮放。它的目的是將不同特征的數值范圍縮放到統一的標準范圍,以便更好地適應一些機器學習算法,特別是那些對輸入數據的尺度敏感的算法
最常見的標準化方法是 Z-score 標準化,也稱為零均值標準化。它通過對每個特征的值減去其均值,再除以其標準差,將數據轉換為均值為0,標準差為1的分布。這可以通過以下公式計算:z 是轉換后的數值,x 是原始數據的值,μ 是該特征的均值,σ 是該特征的標準差
$$
z=\frac{x?μ}{σ}
$$
上述公式參數求解公式如下:
$$
z=(x-μ)/σ \\μ=E(X) \\\sigma=\sqrt{\frac{\sum_{i=1}^{n}\left(x_{i}-\mu\right)^{2}}{n}}
$$
StandardScaler
:是sklearn.preprocessing
模塊中的一個類,用于執行標準化操作,即將數據轉換為具有零均值和單位方差的形式,StandardScaler()
用來實例化對象,與 MinMaxScaler 一樣,原始數據類型可以是 list、DataFrame 和 ndarray
案例:
from sklearn.preprocessing import StandardScaler import numpy as np ? # 標準化 def standard_scaler():# 示例數據集data = np.array([[1, 2],[2, 3],[3, 5],[4, 6],[5, 8]])print("原始數據:\n", data)# 實例化StandardScaler對象scaler = StandardScaler()data_scaled = scaler.fit_transform(data)print("標準化后數據:\n", data_scaled)# 查看標準化后的均值和標準差print("均值:", scaler.mean_)print("標準差:", scaler.scale_)
1.2.5 特征降維
特征降維是在機器學習和數據挖掘中常用的一種技術,用于減少數據集中的特征數量。這通常是為了簡化模型,提高計算效率,或者為了去除噪音和冗余信息
特征降維方式很有,掌握以下兩種:
特征選擇方法:直接選擇最具代表性的特征,而不是創建新的特征,特征選擇有以下方式可以實現:
過濾法:根據一些評分標準對特征進行評分,然后選擇得分最高的特征,可以分為低方差過濾法和相關系數法
主成分分析(PCA):通過正交變換將數據轉換為一系列線性無關的主成分,其中每個成分都是原始變量的線性組合,且按方差遞減順序排列,形成新的特征,新的特征數量會底于之前特征數量
1.2.5.1 特征選擇-VarianceThreshold 低方差過濾
VarianceThreshold 低方差過濾是指如果一個特征的方差很小,說明這個特征的值在樣本中幾乎相同,模型很難通過這個特征去區分不同的對象,比如區分英短和美短兩種貓,有一個特征是有兩個眼睛,那么這個特征就可以去掉
實現步驟:
準備數據:準備需要過濾特征的數據集
計算方差:計算每個特征的方差,可以使用 Pandas 的
var()
方法設定閾值:設定一個閾值來確定哪些特征的方差過低。這個閾值通常是根據數據的具體情況和業務需求來設定的。如果方差低于這個閾值,則認為該特征的變異程度太低,可以考慮移除
過濾特征:使用 scikit-learn 中的
VarianceThreshold
類來移除方差低于指定閾值的特征查看結果:查看經過低方差過濾后的特征數量,并確認哪些特征被移除了
VarianceThreshold()
:用于實例化對象,屬于sklearn.feature_selection
的子類,用來實現底方差過濾特征選擇,參數如下:threshold
:設置方差閾值,低于閾值的特征刪除
案例:
from sklearn.datasets import load_iris from sklearn.feature_selection import VarianceThreshold ? ? def variance_threshold():data = load_iris()X = data.data# 創建VarianceThreshold對象,閾值設置為0.5selector = VarianceThreshold(threshold=0.5)X_transformed = selector.fit_transform(X)# 輸出結果print('原始數據:\n', X[0:5])print('過濾之后:\n', X_transformed[0:5])print("原始特征數量:\n", X.shape[1])print("減少特征數量:\n", X_transformed.shape[1])# 獲取一個布爾掩碼,指示哪些特征被保留下來mask = selector.get_support()new_features = np.arange(X.shape[1])[mask]print('保留下來的特征索引:\n',new_features)# 獲取被移除的特征索引removed_features = np.arange(X.shape[1])[~mask]print("被移除的特征索引:\n", removed_features)
1.2.5.2 特征選擇-根據相關系數
根據特征與目標變量之間的相關系數進行特征選擇,這種方法的基本思想是選擇那些與目標變量(在監督學習任務中)相關性較高的特征。相關系數可以用來衡量兩個變量之間的線性關系強度
開發中一般不使用求相關系數的方法,一般使用主成分分析,因為主成分分樣過程中就包括了求相關系數
常見的相關系數計算方法包括皮爾遜相關系數(Pearson correlation coefficient)、斯皮爾曼相關系數(Spearman correlation coefficient)
皮爾遜相關系數:皮爾遜相關系數衡量的是兩個變量之間的線性關系強度和方向。它是通過計算兩個變量的協方差除以它們的標準差的乘積來得到的,其值介于-1與1之間,計算公式如下:
$$
\rho=\frac{\operatorname{Cov}(x, y)}{\sqrt{D x} \cdot \sqrt{D y}}=\frac{E[(x_i-E x)(y_i-E y)]}{\sqrt{D x} \cdot \sqrt{D y}}=\frac{\sum_{i=1}^{n}(x_i-\tilde{x})(y_i-\bar{y}) /(n-1)}{\sqrt{\sum_{i=1}^{n}(x_i-\bar{x})^{2} /(n-1)} \cdot \sqrt{\sum_{i=1}^{n}(y_i-\bar{y})^{2} /(n-1)}}
$$
對于上述公式:
$\bar{x}$ 表示數據一的平均值,$\bar{y}$ 表示數據二的平均值,n 表示數據總個數
|ρ|<0.4 為低度相關; 0.4<=|ρ|<0.7 為顯著相關; 0.7<=|ρ|<1 為高度相關
ρ = 1,正相關表示隨著一個變量增加,另一個變量也傾向于增加(系數為正值)
ρ = -1, 負相關表示隨著一個變量增加,另一個變量傾向于減少(系數為負值)
ρ = 0,兩個變量不存在線性關系
scipy.stats.pearsonr(x, y)
是 Scipy 庫中用于計算兩個變量之間 Pearson 相關系數的函數。Pearson 相關系數是衡量兩個變量之間線性關系強度和方向的指標。相關系數的范圍是 -1 到 1,參數和返回值如下:x: 第一個輸入數組,可以是一維的序列
y: 第二個輸入數組,必須與 x 具有相同的長度
pearsonr
函數返回一個元組(correlation, pvalue)
:correlation: Pearson 相關系數。
pvalue: 假設檢驗的 p 值,用于判斷相關系數是否顯著不同于 0
案例:
from scipy.stats import pearsonr import numpy as np ? def pearsonr_demo():# 完全正相關x = np.array([1, 2, 3, 4, 5])y = np.array([2, 4, 6, 8, 10])# 計算 Pearson 相關系數及其 p 值correlation, pvalue = pearsonr(x, y)print('Pearson 相關系數:', correlation)print('p 值:', correlation)# 完全負相關y2 = np.array([5, 4, 3, 2, 1])correlation2, pvalue2 = pearsonr(x, y2)print('Pearson 相關系數:', correlation2)print('p 值:', correlation2)# 隨機數據y3 = np.array([np.random.rand() for _ in range(len(x))])correlation3, pvalue3 = pearsonr(x, y3)print('Pearson 相關系數:', correlation3)print('p 值:', correlation3)