Julia 機器學習 ---- 特徵降維(特徵提取)

1、爲什麼要降維

以下是在數據集中應用降維的用處:

(1)隨着數據維度不斷降低,數據存儲所需的空間也會隨之減少。

(2)低維數據有助於減少計算/訓練用時。

(3)一些算法在高維度數據上容易表現不佳,降維可提高算法可用性。

(4)降維可以用刪除冗餘特徵解決多重共線性問題。比如我們有兩個變量:“一段時間內在跑步機上的耗時”和“卡路里消耗量”。這兩個變量高度相關,在跑步機上花的時間越長,燃燒的卡路里自然就越多。因此,同時存儲這兩個數據意義不大,只需一個就夠了。

(5)降維有助於數據可視化。如前所述,如果數據維度很高,可視化會變得相當困難,而繪製二維三維數據的圖表非常簡單。

其實通過特徵評價去掉一些不必要的特徵也屬於降維。

2、常用降維算法

降維方法分爲線性核非線性降維,非線性降維又分爲基於核函數和基於特徵值的方法。

線性降維方法:PCA  ICA LDA  LFA  LPP(LE的線性表示)

基於核函數的非線性降維方法:KPCA  KICA  KDA

基於特徵值的非線性降維方法(流型學習):ISOMAP  LLE  LE  LPP  LTSA  MVU
 

3、Julia中提供的代碼庫

MultivariateStats 是一個用於多元統計分析的Julia包。它提供了一套豐富有用的分析技術,如PCA、kernel PCA、CCA、LDA、PLS等。

ScikitLearn.jl 也提供了上面類似的降維函數,但是需要依賴Python的代碼包。

 

4、特徵降維函數使用說明

這裏重點介紹kernel PCA使用方式

4.1、kernel PCA

MultivariateStats 中的 kernel PCA函數接口參數如下:

參數名稱

參數描述

默認

kernel

核函數:它接收兩個矢量 x 和 y,並返回標量值。

其他常用核函數參考:https://github.com/theogf/KernelFunctions.jl

(x,y)->x'y

solver

解算器的選擇:

:eig: 實際使用 函數eigfact,是一個特徵值分解函數,函數主要是給出矩陣的特徵值和特徵向量

:eigs: 實際使用函數eigs (總是用於稀疏數據),函數主要是通過迭代法來求解矩陣特徵值和特徵向量

:eig

maxoutdim

最大輸出尺寸,也就是我們實際需要的維度

min(d, n)

inverse

是否對未預計算的內核執行反變換計算,如果是數據分析,可以不用的

false

β

學習反變換的嶺迴歸的超參數

(前提是:inverse 爲 true). 如果是數據分析,可以不用主要用於圖像分析

1.0

tol

eigs求解器的收斂性

0.0

maxiter

eigs解算器的最大迭代次數

300

 

常用的核函數爲

函數

描述

(x,y)->x'y

線性的

(x,y)->(x'y+c)^d

多項式的

(x,y)->exp(-γ*norm(x-y)^2.0)

徑向基函數 (RBF)。徑向基函數是一個取值僅僅依賴於離原點距離的實值函數,也就是Φ(x)=Φ(‖x‖),比如高斯函數

 

代碼示例

這裏提供一個較爲完整的數據分析流程

 

using MultivariateStats
using DataFrames,CSV
using Statistics
using LinearAlgebra
using Plots
using Clustering
using Query
using Parsers
import Query:@from


# download("http://samplecsvs.s3.amazonaws.com/Sacramentorealestatetransactions.csv","houses.csv")
houses = DataFrame(CSV.File(joinpath(dirname(pathof(DataFrames)),"D:/houses.csv")));

#####數據清洗-特徵相關性分析,這裏簡單過濾下。
#(1) 像street、city、zip 、latitude 和 longitude 都是地理位置信息相關的。
#  其中street、city、zip其實都太好量化,雖然zip是個數值,但是這個數值是沒有
#  規律無法使用歐式距離來衡量,所以我這裏選擇經緯度:latitude 和 longitude
#(2) 分類問題,type字段其實是一個分類,數據被分爲4類,Residential,Condo,Multi-Family,Unknown
# (3) sale_date 轉成毫秒,或者 可以擴展其他的特徵,比如月份 ,年份等,可能月份更好一點。可惜的是我們只有5月份的數據,索性不用了。
# unique(houses[!,:type])
# occursin("a","Condo,Residential,Multi-Family")

function newType(type::AbstractString)
    if type === "Residential"
        return 1.0
    elseif type === "Condo"
        return 2.0
    elseif type === "Multi-Family"
        return 3.0
    end
end

# function parseDate(date::String)
#     diffDate = DateTime(2000, 01, 01)
#     parseDate =Parsers.parse(DateTime, string("2008-05-",date[9:10]), Parsers.Options(dateformat="m/d/yyyy I:M:S.s p"))
#     return (parseDate-diffDate).value
# end

newHouses = @from i in houses begin
    @let newType = newType(i.type)
    # @let neSaleDate = parseDate(string(i.sale_date))
    @where i.sq__ft > 0 && (i.type ==="Residential" || i.type ==="Condo"|| i.type ==="Multi-Family" )
    @select {i.beds,i.baths,i.sq__ft,i.price,type=newType,i.latitude,i.longitude }
    @collect DataFrame
end

#### sq__ft,i.price,i.latitude,i.longitude這幾個的特徵值比較大,爲了消除各個特徵向量的差異性,對數據做標準化
#使用Z標準化,並把數據變成浮點數
mapcols(x -> x*1.0, newHouses)
function zscore(df::DataFrame)
    h_mean = map(mean,eachcol(df[!,1:end]))
    h_sdt  = map(std,eachcol(df[!,1:end]))
    h,c = size(df[!,1:end])
    for col in 1:c
        mean_v=h_mean[col]
        std_v = h_sdt[col]
        println(mean_v)
        for hex in 1:h
            @inbounds df[hex,col] = (df[hex,col]-mean_v)/std_v
        end
    end
end

zscore(newHouses)

#####現在也沒有特別好的算法來判斷矩陣是線性還是非線性,我這裏通過圖來分析是否是線性數據,
#直觀上看,如果特徵變量之間沒有線性關係,大概率是非線性的數據,
plot(size=(500,500),leg=false)
x = newHouses[:sq__ft]
y = newHouses[:price]
scatter(x,y)

x = newHouses[:latitude]
y = newHouses[:longitude]
scatter(x,y)


#### 未降維之前 kmean 算法分析下
features = collect(Float64,Matrix(newHouses[:, 1:end])');
features
result = kmeans(features, 2)
scatter([newHouses.sq__ft], [newHouses.price], marker_z=result.assignments,
                color=:lightrainbow, legend=false)

### 核PCA降維(如果是線性數據可以使用PCA,這兩種常用的降維算法,適用與不同的矩陣)
# fh = disallowmissing(newHouses)
# 核PCA適合處理非線性數據,首先需要判斷數據是屬於線性的還是非線性的
# 如果各個特徵向量之間的差異比較大,可以先做數據的標準化。

#線性函數降維 (x,y)->x'y 也是默認的核函數
thisMtrix = Matrix{Float64}(newHouses[:,:])'
x = thisMtrix
# train a kernel PCA model
M = fit(KernelPCA, x; maxoutdim=2, inverse=true)
# apply kernel PCA model to testing set
Yte = transform(M, x)
# reconstruct testing observations (approximately)
# Xr = reconstruct(M, Yte)

result = kmeans(Yte, 3)
scatter(Yte[1,:], Yte[2,:], marker_z=result.assignments,
                color=:lightrainbow, legend=false)

f_result = fuzzy_cmeans(Yte, 3, 2, maxiter=50, display=:iter)

這裏有兩張圖可以對降維前後數據分析效果做對比:

降維前對 sq__ft (面積),price(價格)作圖分析:

降維後作圖分析,明顯更加直觀

 

其他函數待續。。。。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章