機器學習十講第八講

維度災難

什麼是維度災難

1624632470743

高維空間的歐式距離

1624632566863

由於距離在高維空間中不再有效,因此一些基於距離的機器學習模型就會收到影響

基於距離的機器學習模型:K近鄰(樣本間距離),支持向量機(樣本到決策面距離),K-Means(樣本到聚類中心距離),層次聚類(不同簇之間的距離),推薦系統(商品或用戶相似度),信息檢索(查詢和文檔之前的相似度)。

稀疏性與過度擬合

1624632699463

維度災難舉例

決策樹

1624632822332

樸素貝葉斯

1624632835614

應對維度災難

特徵選擇和降維

1624632918135

過度擬合與正則化

1624632961972

核技巧

1624633007695

使用Python認識維度問題

單位球體積隨着維度的變化

#公式實現
import numpy as np
import math
#引用伽馬函數
from scipy.special import gamma
def V(d,r):
    return math.pi**(d/2)*(r**d)/gamma(d/2 + 1)
#測試一下
#print(V(1,1))


#使用公式計算1-20維單位球的體積
import pandas as pd
df = pd.DataFrame()
df["d"] = np.arange(1,20)
#半徑爲1
df["V"] = V(df["d"],1)
df.round(9)


#將單位球體積隨着維度的變化進行可視化
##爲了方便觀察,設置維度爲1-70
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots(figsize=(12, 6)) #設置圖片大小
ds = np.arange(1,70)
plt.plot(ds,V(ds,1),color="#E4007F",marker="o")
plt.xlabel("維度$d$")
plt.ylabel("單位球體積$V_d$")
1624633156034
#看一下0.1半徑體積的比例是多少
##假設邊界爲0.1
def ratio(d):
    return (V(d,1) - V(d,0.9))/ V(d,1)
df["ratio90"] =  1 - 0.9**(df.d)
df.round(6).head(20)


#將0.1邊界體積比例可視化
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots(figsize=(12, 6)) #設置圖片大小
ds = np.arange(1,50)
plt.plot(ds,ratio(ds),color="#E4007F",marker="o")
plt.xlabel("維度$d$")
plt.ylabel("0.1邊界體積佔比")

1624633254405

#剛纔的方法是指定邊界爲0.1,爲了能查看任意邊界比例的情況,再寫一個方法
def volumn_fraction_in_border(d,r):
    return(V(d,1)-V(d,1-r))/V(d,1)
#可視化
import numpy as np
#設置維度
ds=[1,2,5,20,100]
#設置顏色
colors=["gray","#E4007F","","","",""]
rs=np.linspace(0,1,100)
result=[]
for d in ds:
    vs=[]
    for r in rs:
        vs.append(volumn_fraction_in_border(d,r))
    result.append(vs)
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots(figsize=(8, 8)) #設置圖片大小
plt.title("單位球邊界體積比例隨維度的變化")
for i in range(len(ds)):
    plt.plot(rs,result[i],color="#E4007F",label="d="+str(ds[i]))
    plt.text(rs[50]-i*0.12,0.5+i*0.1,"$d$="+str(ds[i]))
plt.xlim(0,1)
plt.ylim(0,1)
plt.xlabel("邊界半徑佔比$r$")
plt.ylabel("邊界體積比例")
1624633297994
  • 高維空間中的樣本距離
#定義方法計算歐式距離
def data_euclidean_dist(x):
    sum_x = np.sum(np.square(x), 1)
    dist = np.add(np.add(-2 * np.dot(x, x.T), sum_x).T, sum_x)
    return dist
#自定義一個矩陣,計算兩兩之間的歐式距離
x = np.array([[0, 1, 3], [3, 8, 9], [2, 3, 5]])
dist_matrix = data_euclidean_dist(x)
dist_matrix

#使用mask去掉對角線(0)
##計算該歐式距離中的最小值
mask = np.ones(dist_matrix.shape, dtype=bool)
np.fill_diagonal(mask, 0)
dist_matrix[mask].min()


#生成一個包含5000個樣本5000個維度的數據集。每一個維度都是從[-1,1]之間隨機生成。
X = np.random.uniform(-1,1,(5000,5000))
#顯示
X

#求隨着維度增大,樣本間歐式距離最大值與最小值的差值,需要用mask去掉對角0
min_max_list = []
#設置間隔爲50
for d in range(50,5000,50):
    dist_matrix = data_euclidean_dist(X[:,:d])
    mask = np.ones(dist_matrix.shape, dtype=bool)
    np.fill_diagonal(mask, 0)
    min_max = (dist_matrix[mask].max() - dist_matrix[mask].min())/dist_matrix[mask].max()
    if d%50 == 0:
        print(d,min_max.round(3))
    min_max_list.append(min_max)
 

#可視化
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots(figsize=(16, 4)) #設置圖片大小
ds = np.arange(0,len(min_max_list))
plt.plot(ds,min_max_list,color="#E4007F")
plt.xlabel("維度$d$")
plt.ylabel("最大-最小距離差距")

1624633470712

  • 維度對分類性能的影響
#生成Trunk數據集
import pandas as pd
import numpy as np
max_features,num_samples = 1000,500 #最大特徵數量和樣本數量
X_pos = pd.DataFrame()
X_neg = pd.DataFrame()

for i in range(max_features):
    X_pos["f"+str(i+1)] = np.random.randn(num_samples)+ np.sqrt(1/(i+1)) #生成當前特徵的正類樣本
    X_neg["f"+str(i+1)] = np.random.randn(num_samples)- np.sqrt(1/(i+1)) #生成當前特徵的負類樣本
    
X_pos["label"],X_neg["label"] = 0,1 #添加標籤
trunk_data = pd.concat([X_pos,X_neg],axis=0) #合併正類和負類樣本
trunk_data.head()


#隨機選擇幾個特徵,用散點圖可視化正負類樣本的分佈情況。
import matplotlib.pyplot as plt
%matplotlib inline
features = [1,5,10,100]
fig, ax = plt.subplots(figsize=(16, 4)) #設置圖片大小

for i in range(len(features)):
    plt.subplot(1,4,i+1)
    plt.scatter(trunk_data[trunk_data.label == 0]["f" + str(features[i])], trunk_data[trunk_data.label == 0]["f" + str(features[i]+1)], color="#007979")
    plt.scatter(trunk_data[trunk_data.label == 1]["f" + str(features[i])], trunk_data[trunk_data.label == 1]["f" + str(features[i]+1)],color="#E4007F")
    plt.xlabel("feature " + str(features[i]))
    plt.ylabel("feature " + str(features[i]+1))
plt.tight_layout()

1624633546995

#不斷增加特徵數量,觀察分類性能隨着維數的變化。
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(trunk_data.iloc[:,:-1],trunk_data["label"],test_size=0.5)#訓練集和測試集劃分
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
num_features = np.arange(1,max_features,10)
exp_times = 10 #試驗次數
test_result = np.zeros(len(num_features))
train_result = np.zeros(len(num_features))
#實驗用到不同維度,所以使用兩層for循環控制
for t in range(exp_times): #運行多次試驗
    scores_train = [] #記錄訓練集正確率
    scores_test = [] #記錄測試集正確率
    for num_feature in num_features:  #使用不同特徵數量
        clf = LinearDiscriminantAnalysis()#新建分類模型
        clf.fit(X_train.iloc[:,:num_feature],y_train)#用訓練集對應特徵數去訓練
        score_train = clf.score(X_train.iloc[:,:num_feature],y_train)#記錄訓練集正確率
        score_test = clf.score(X_test.iloc[:,:num_feature],y_test)#記錄測試集正確率
        scores_train.append(score_train)
        scores_test.append(score_test)
    train_result += np.array(scores_train)
    test_result += np.array(scores_test)
    print(t)
#求平均值
test_result /= exp_times
train_result /= exp_times

#訓練好後進行可視化
fig, ax = plt.subplots(figsize=(12, 6)) 
ax.plot(np.log10(num_features),test_result,color="#E4007F",marker=".")
plt.ylim(0.5,1)
plt.xlabel("number of features")
plt.ylabel("accuracy")

1624633594182

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