機器學習一(學習筆記) 數據的特徵抽取及預處理

Scikit-learn

安裝Scikit-learn

pip3 install Scikit-learn
pip3 install scipy

一、數據的特徵抽取

現實世界中多數特徵都不是連續變量,比如分類、文字、圖像等,爲了對非連續變量做特徵表述,需要對這些特徵做數學化表述,因此就用到了特徵提取. sklearn.feature_extraction提供了特徵提取的很多方法

1、字典數據抽取

我們將城市和環境作爲字典數據,來進行特徵的提取。

sklearn.feature_extraction.DictVectorizer(sparse = True)

將映射列表轉換爲Numpy數組或scipy.sparse矩陣

  • sparse 是否轉換爲scipy.sparse矩陣表示,默認開啓

方法

fit_transform(X,y)

應用並轉化映射列表X,y爲目標類型

inverse_transform(X[, dict_type])

將Numpy數組或scipy.sparse矩陣轉換爲映射列表

from sklearn.feature_extraction import DictVectorizer

dict = DictVectorizer(sparse=False)

# 調用fit_transform
data = dict.fit_transform([{'city': '北京','temperature': 100}, {'city': '上海','temperature':60}, {' ': '深圳','temperature': 30}])

print(dict.get_feature_names())

print(dict.inverse_transform(data))

print(data)
'''
['city=上海', 'city=北京', 'city=深圳', 'temperature']
[{'city=北京': 1.0, 'temperature': 100.0}, {'city=上海': 1.0, 'temperature': 60.0}, {'city=深圳': 1.0, 'temperature': 30.0}]
[[  0.   1.   0. 100.]
 [  1.   0.   0.  60.]
 [  0.   0.   1.  30.]]
'''

2、文本特徵提取

只限於英文,中文需要先進性分詞處理

文本的特徵提取應用於很多方面,比如說文檔分類、垃圾郵件分類和新聞分類。那麼文本分類是通過詞是否存在、以及詞的概率(重要性)來表示。

(1)文檔的中詞的出現

數值爲1表示詞表中的這個詞出現,爲0表示未出現

sklearn.feature_extraction.text.CountVectorizer()

將文本文檔的集合轉換爲計數矩陣(scipy.sparse matrices)

方法

fit_transform(raw_documents,y)

學習詞彙詞典並返回詞彙文檔矩陣

from sklearn.feature_extraction.text import CountVectorizer
#抽取英文文本數據
content = ["life is short,i like python","life is too long,i dislike python"]

cv = CountVectorizer()
#進行抽取
data=  cv.fit_transform(content)
#打印數據,需要toarray()方法轉變爲numpy的數組形式
print(cv.get_feature_names())
print(data.toarray())

'''
['dislike', 'is', 'life', 'like', 'long', 'python', 'short', 'too']
[[0 1 1 1 0 1 1 0]
 [1 1 1 0 1 1 0 1]]
'''

中文案列

from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer()
data = cv.fit_transform(["人生 苦短,我 喜歡 python", "人生漫長,不用 python"])
#獲取特徵名稱
print(cv.get_feature_names())
#打印特徵化之後的數據,需要toarray()方法轉變爲numpy的數組形式
print(data.toarray())

'''
['python', '不用', '人生', '人生漫長', '喜歡', '苦短']
[[1 0 1 0 1 1]
 [1 1 0 1 0 0]]
'''

溫馨提示:每個文檔中的詞,只是整個語料庫中所有詞,的很小的一部分,這樣造成特徵向量的稀疏性(很多值爲0)爲了解決存儲和運算速度的問題,使用Python的scipy.sparse矩陣結構

3、TF-IDF表示詞的重要性

TF-IDF的主要思想是:如果某個詞或短語在一篇文章中出現的頻率TF高,並且在其他文章中很少出現,則認爲此詞或者短語具有很好的類別區分能力,適合用來分類。TF-IDF實際上是:TF * IDF。

TfidfVectorizer會根據指定的公式將文檔中的詞轉換爲概率表示。(樸素貝葉斯介紹詳細的用法)

class sklearn.feature_extraction.text.TfidfVectorizer()

方法

fit_transform(raw_documents,y)

學習詞彙和idf,返回術語文檔矩陣。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

from sklearn.feature_extraction.text import CountVectorizer
#抽取英文文本數據
cv = CountVectorizer()

from sklearn.feature_extraction.text import TfidfVectorizer
content = ["life is short,i like python","life is too long,i dislike python"]
tf = TfidfVectorizer(stop_words='english')
#處理文本數據
data = tf.fit_transform(content)
#進行抽取
data=  cv.fit_transform(content)
#打印數據
print(cv.get_feature_names())
print(data.toarray())
print(tf.vocabulary_)

'''
['dislike', 'is', 'life', 'like', 'long', 'python', 'short', 'too']
[[0.         0.40993715 0.57615236 0.         0.40993715 0.57615236]
 [0.57615236 0.40993715 0.         0.57615236 0.40993715 0.        ]]
{'life': 1, 'short': 5, 'like': 2, 'python': 4, 'long': 3, 'dislike': 0}
'''

二、數據的特徵預處理

1、單個特徵數據

1、特徵歸一化

歸一化首先在特徵(維度)非常多的時候,可以防止某一維或某幾維對數據影響過大,也是爲了把不同來源的數據統一到一個參考區間下,這樣比較起來纔有意義,其次可以程序可以運行更快。 例如:一個人的身高和體重兩個特徵,假如體重50kg,身高175cm,由於兩個單位不一樣,數值大小不一樣。如果比較兩個人的體型差距時,那麼身高的影響結果會比較大,k-臨近算法會有這個距離公式。

min-max方法

常用的方法是通過對原始數據進行線性變換把數據映射到[0,1]之間,變換的函數爲:

Min-Max Scaling又稱爲Min-Max normalization, 特徵量化的公式爲:
 特徵量化的公式

量化後的特徵將分佈在區間[0,1]之間。

其中min是樣本中最小值,max是樣本中最大值,注意在數據流場景下最大值最小值是變化的,另外,最大值與最小值非常容易受異常點影響,所以這種方法魯棒性較差,只適合傳統精確小數據場景。(魯棒是Robust的音譯,也就是健壯和強壯的意思。它也是在異常和危險情況下系統生存的能力

  • min-max自定義處理

這裏我們使用相親約會對象數據在MatchData.txt,這個樣本時男士的數據,三個特徵:1、玩遊戲所消耗時間的百分比;2、每年獲得的飛行常客里程數;3、每週消費的冰淇淋公升數。然後有一個所屬類別,被女士評價的三個類別,不喜歡、魅力一般、極具魅力。 首先導入數據進行矩陣轉換處理

import numpy as np
from sklearn.preprocessing import MinMaxScaler, StandardScaler, Imputer

mm = MinMaxScaler(feature_range=(2, 3))

data = mm.fit_transform([[90,2,10,40],[60,4,15,45],[75,3,13,46]])

print(data)

輸出結果爲

[[3.         2.         2.         2.        ]
 [2.         3.         3.         2.83333333]
 [2.5        2.5        2.6        3.        ]]

我們查看數據集會發現,有的數值大到幾萬,有的才個位數,同樣如果計算兩個樣本之間的距離時,其中一個影響會特別大。也就是說飛行里程數對於結算結果或者說相親結果影響較大,但是統計的人覺得這三個特徵同等重要,所以需要將數據進行這樣的處理。

這樣每個特徵任意的範圍將變成[0,1]的區間內的值,或者也可以根據需求處理到[-1,1]之間,我們再定義一個函數,進行這樣的轉換。

2、標準化

標準化數據通過減去均值然後除以方差(或標準差),這種數據標準化方法經過處理後數據符合標準正態分佈,即均值爲0,標準差爲1,轉化函數爲:

x =(x - 𝜇)/𝜎

適用於:如果數據的分佈本身就服從正態分佈,就可以用這個方法。
通常這種方法基本可用於有outlier(異常值;極端值;離羣值)的情況,但是,在計算方差和均值的時候outliers仍然會影響計算。所以,在出現outliers的情況下可能會出現轉換後的數的不同feature分佈完全不同的情況。
其中μ是樣本的均值,σ是樣本的標準差,它們可以通過現有的樣本進行估計,在已有的樣本足夠多的情況下比較穩定,適合嘈雜的數據場景
參考文章:歸一化和標準化的一些理解

sklearn中提供了StandardScalar類實現列標準化:

from sklearn.preprocessing import MinMaxScaler, StandardScaler, Imputer
std = StandardScaler()
data = std.fit_transform([[ 1., -1., 3.],[ 2., 4., 2.],[ 4., 6., -1.]])
print(data)
‘’‘
[[-1.06904497 -1.35873244  0.98058068]
 [-0.26726124  0.33968311  0.39223227]
 [ 1.33630621  1.01904933 -1.37281295]]
‘’’

3、缺失值處理

由於各種原因,許多現實世界的數據集包含缺少的值,通常編碼爲空白,NaN或其他佔位符。然而,這樣的數據集與scikit的分類器不兼容,它們假設數組中的所有值都是數字,並且都具有和保持含義。使用不完整數據集的基本策略是丟棄包含缺失值的整個行和/或列。然而,這是以丟失可能是有價值的數據(即使不完整)的代價。更好的策略是估算缺失值,即從已知部分的數據中推斷它們。

(1)填充缺失值 使用sklearn.preprocessing中的Imputer類進行數據的填充

#!/usr/bin/python
# -*- coding: UTF-8 -*-
from sklearn.preprocessing import Imputer
import numpy as np

im = Imputer(missing_values='NaN', strategy='mean', axis=0)
data = im.fit_transform([[1, 5], [np.nan, 3], [7, 6]])
print(data)
'''
[[1. 5.]
 [4. 3.]
 [7. 6.]]
'''
'''
class Imputer(sklearn.base.BaseEstimator, sklearn.base.TransformerMixin)
    """
    用於完成缺失值的補充

    :param param missing_values: integer or "NaN", optional (default="NaN")
        丟失值的佔位符,對於編碼爲np.nan的缺失值,使用字符串值“NaN”

    :param strategy: string, optional (default="mean")
        插補策略
        如果是“平均值”,則使用沿軸的平均值替換缺失值
        如果爲“中位數”,則使用沿軸的中位數替換缺失值
        如果“most_frequent”,則使用沿軸最頻繁的值替換缺失

    :param axis: integer, optional (default=0)
        插補的軸
        如果axis = 0,則沿列排列
        如果axis = 1,則沿行排列
    """

3、異常值處理

異常值,即在數據集中存在不合理的值,又稱離羣點。
其他:待補充

2、多個特徵

1、線性降維

1、刪除低方差的特徵

#!/usr/bin/python
# -*- coding: UTF-8 -*-
from sklearn.feature_selection import VarianceThreshold
var = VarianceThreshold(threshold=1.0)
data = var.fit_transform([
    [0, 2, 0, 3, 2],
    [0, 1, 4, 3, 2],
    [0, 1, 1, 3, 6]])
print(data)
'''
[[0 2]
 [4 2]
 [1 6]]
'''

2、主成分分析算法(PCA)

參考:如何通俗易懂地講解什麼是 PCA 主成分分析?
PCA算法理解及代碼實現
Principal Component Analysis(PCA)是最常用的線性降維方法,它的目標是通過某種線性投影,將高維的數據映射到低維的空間中表示,並期望在所投影的維度上數據的方差最大,以此使用較少的數據維度,同時保留住較多的原數據點的特性。

通俗的理解,如果把所有的點都映射到一起,那麼幾乎所有的信息(如點和點之間的距離關係)都丟失了,而如果映射後方差儘可能的大,那麼數據點則會 分散開來,以此來保留更多的信息。可以證明,PCA是丟失原始數據信息最少的一種線性降維方式。(實際上就是最接近原始數據,但是PCA並不試圖去探索數 據內在結構)

設 n 維向量w爲目標子空間的一個座標軸方向(稱爲映射向量),最大化數據映射後的方差,

四大機器學習降維算法:PCA、LDA、LLE、Laplacian Eigenmaps

其中 m 是數據實例的個數, xi是數據實例 i 的向量表達, x拔是所有數據實例的平均向量。定義W爲包含所有映射向量爲列向量的矩陣,經過線性代數變換,可以得到如下優化目標函數:

四大機器學習降維算法:PCA、LDA、LLE、Laplacian Eigenmaps

其中tr表示矩陣的跡, 四大機器學習降維算法:PCA、LDA、LLE、Laplacian Eigenmaps A是數據協方差矩陣。

容易得到最優的W是由數據協方差矩陣前 k 個最大的特徵值對應的特徵向量作爲列向量構成的。這些特徵向量形成一組正交基並且最好地保留了數據中的信息。

PCA的輸出就是Y = W‘X,由X的原始維度降低到了k維。

PCA追求的是在降維之後能夠最大化保持數據的內在信息,並通過衡量在投影方向上的數據方差的大小來衡量該方向的重要性。但是這樣投影以後對數據 的區分作用並不大,反而可能使得數據點揉雜在一起無法區分。這也是PCA存在的最大一個問題,這導致使用PCA在很多情況下的分類效果並不好。具體可以看 下圖所示,若使用PCA將數據點投影至一維空間上時,PCA會選擇2軸,這使得原本很容易區分的兩簇點被揉雜在一起變得無法區分;而這時若選擇1軸將會得 到很好的區分結果。

四大機器學習降維算法:PCA、LDA、LLE、Laplacian Eigenmaps

PCA(Principal component analysis),主成分分析。特點是保存數據集中對方差影響最大的那些特徵,PCA極其容易受到數據中特徵範圍影響,所以在運用PCA前一定要做特徵標準化,這樣才能保證每維度特徵的重要性等同。

sklearn.decomposition.PCA

.PCA常用方法

  • fit(X): 用數據X來訓練PCA模型。
  • fit_transform(X):用X來訓練PCA模型,同時返回降維後的數據。
#參數:n_components:  
#意義:PCA算法中所要保留的主成分個數n,也即保留下來的特徵個數n
#類型:int 或者 string,缺省時默認爲None,所有成分被保留。
          賦值爲int,比如n_components=1,將把原始數據降到一個維度。
          賦值爲string,比如n_components='mle',將自動選取特徵個數n,使得滿足所要求的方差百分比。

通過一個例子來看

    #主成分分析進行特徵降維
    :return: None
    """
    pca = PCA(n_components=0.9)

    data = pca.fit_transform([[2,8,4,5],[6,3,0,8],[5,4,9,1]])

    print(data)

    return None

2、LDA

Linear Discriminant Analysis(也有叫做Fisher Linear Discriminant)是一種有監督的(supervised)線性降維算法。與PCA保持數據信息不同,LDA是爲了使得降維後的數據點儘可能地容易被區分!

假設原始數據表示爲X,(m*n矩陣,m是維度,n是sample的數量)

既然是線性的,那麼就是希望找到映射向量a, 使得 a‘X後的數據點能夠保持以下兩種性質:

1、同類的數據點儘可能的接近(within class)

2、不同類的數據點儘可能的分開(between class)

所以呢還是上次PCA用的這張圖,如果圖中兩堆點是兩類的話,那麼我們就希望他們能夠投影到軸1去(PCA結果爲軸2),這樣在一維空間中也是很容易區分的。

四大機器學習降維算法:PCA、LDA、LLE、Laplacian Eigenmaps

接下來是推導,因爲這裏寫公式很不方便,我就引用Deng Cai老師的一個ppt中的一小段圖片了:

四大機器學習降維算法:PCA、LDA、LLE、Laplacian Eigenmaps

思路還是非常清楚的,目標函數就是最後一行J(a),μ(一飄)就是映射後的中心用來評估類間距,s(一瓢)就是映射後的點與中心的距離之和用來評估類內距。J(a)正好就是從上述兩個性質演化出來的。

因此兩類情況下:

加上a’a=1的條件(類似於PCA)

四大機器學習降維算法:PCA、LDA、LLE、Laplacian Eigenmaps

可以拓展成多類:

四大機器學習降維算法:PCA、LDA、LLE、Laplacian Eigenmaps

以上公式推導可以具體參考pattern classification書中的相應章節,講fisher discirminant的

OK,計算映射向量a就是求最大特徵向量,也可以是前幾個最大特徵向量組成矩陣A=[a1,a2,….ak]之後,就可以對新來的點進行降維了: y = A’X (線性的一個好處就是計算方便!)

可以發現,LDA最後也是轉化成爲一個求矩陣特徵向量的問題,和PCA很像,事實上很多其他的算法也是歸結於這一類,一般稱之爲譜(spectral)方法。

LinearDiscriminantAnalysis

#接口
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

data = LinearDiscriminantAnalysis(n_components=2)
data = pca.fit_transform([[2,8,4,5],[6,3,0,8],[5,4,9,1]])


三、jieba分詞(附錄)

jieba分詞是python寫成的一個算是工業界的分詞開源庫,其github地址爲:https://github.com/fxsjy/jieba,在Python裏的安裝方式:

pip install jieba

簡單示例:

import jieba as jb

seg_list = jb.cut("我來到北京清華大學", cut_all=True)
print("全模式: " + "/ ".join(seg_list))  # 全模式

seg_list = jb.cut("我來到北京清華大學", cut_all=False)
print("精確模式: " + "/ ".join(seg_list))  # 精確模式

seg_list = jb.cut("他來到了網易杭研大廈")  
print("默認模式: " + "/ ".join(seg_list)) # 默認是精確模式

seg_list = jb.cut_for_search("小明碩士畢業於中國科學院計算所,後在日本京都大學深造")  
print("搜索引擎模式: " + "/ ".join(seg_list)) # 搜索引擎模式

執行結果:

全模式:/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學
精確模式:/ 來到/ 北京/ 清華大學
默認模式:/ 來到// 網易/ 杭研/ 大廈
搜索引擎模式: 小明/ 碩士/ 畢業// 中國/ 科學/ 學院/ 科學院/ 中國科學院/ 計算/ 計算所//// 日本/ 京都/ 大學/ 日本京都大學/ 深造

jieba分詞的基本思路

jieba分詞對已收錄詞和未收錄詞都有相應的算法進行處理,其處理的思路很簡單,主要的處理思路如下:

  • 加載詞典dict.txt
  • 從內存的詞典中構建該句子的DAG(有向無環圖)
  • 對於詞典中未收錄詞,使用HMM模型的viterbi算法嘗試分詞處理
  • 已收錄詞和未收錄詞全部分詞完畢後,使用dp尋找DAG的最大概率路徑 輸出分詞結果

import jieba 
con1 = jieba.cut("今天很殘酷,明天更殘酷,後天很美好,但絕對大部分是死在明天晚上,所以每個人不要放棄今天。")

con2 = jieba.cut("我們看到的從很遠星系來的光是在幾百萬年之前發出的,這樣當我們看到宇宙時,我們是在看它的過去。")

con3 = jieba.cut("如果只用一種方式瞭解某樣事物,你就不會真正瞭解它。瞭解事物真正含義的祕密取決於如何將其與我們所瞭解的事物相聯繫。")

# 轉換成列表
content1 = list(con1)
content2 = list(con2)
content3 = list(con3)

# 吧列表轉換成字符串
c1 = ' '.join(content1)
c2 = ' '.join(content2)
c3 = ' '.join(content3)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章