機器學習筆記之(6)——數據降維(PCA與SVD)


數據降維(數據壓縮)是屬於非監督學習的一種,但是其實它也屬於一種數據處理的手段。也就是說,通過數據降維,對輸入的數據進行降維處理,由此剔除數據中的噪聲並通過機器學習算法的性能,用於數據預處理。主要有:主成分分析(PCA)和奇異值分解(SVD)。PCA——通過按照數據方差最大方向調整數據的逼近來達到降維的目的。SVD——是矩陣分解技術中的一種,通過對原始數據的逼近來達到降維的目的。(之前介紹過的監督學習Fisher分類器也可以實現數據的降維)

在低維下,數據更容易進行處理。另外,其相關特征可能在數據中明確地顯示出來。也可以起到去除噪聲的作用。

使用特征抽取來減少數據集中特征的數量。特征抽取算法會將數據轉換或映射到一個新的特征空間。特征抽取可以理解為:在盡可能多地保持相關信息的情況下,對數據進行壓縮的一種方法。特征抽取通常用於提高計算效率,同樣也可以幫助我們降低“維數災難”——尤其當模型不適用於正則化處理時。

所謂的降維就是指采用某種映射方法,將原高維空間中的數據點映射到低維度的空間中。在原始的高維空間中,包含冗余信息以及噪聲信息,在實際應用中例如圖像識別中會造成誤差,降低准確率;而通過降維,可以減少信息的冗余、提高識別等的精度,還可以尋求數據內部的本質結構特征。

如果特征維度成千上萬,則需要的訓練樣本的數量幾乎不可能滿足。而且高維空間距離計算也比較麻煩(大部分機器學習算法都是基於特征距離的計算)。在高維度情形下出現的數據樣本稀疏、距離計算困難等問題是所有機器學習方法共同面臨的難題,稱為“維度災難”。可以通過數據降維來解決。


PCA:

主成分分析(principal component analysis)。在PCA中,數據從原來的坐標系轉換到了新的坐標系,新坐標系的選擇是由數據本身決定的。第一個新坐標軸選擇的是原始數據中方差最大的方向,第二個新坐標軸的選擇和第一個坐標軸正交且具有最大方差的方向。該過程一直重復,重復的次數為原始數據中特征的數目。我們會發現,大部分方差都包含在最前面的幾個新坐標軸中。因此,可以忽略余下的坐標軸,即對數據進行了降維處理。

第一個主成分就是從數據差異性最大(即方差最大)的方向提取出來的,第二個主成分則來自於數據差異性次大的方向,並且該方向與第一個主成分方向正交。通過對數據集的協方差矩陣及其特征值分析,可以求得這些主成分的值。一旦得到了協方差矩陣的特征向量,我們就可以保留最大的N個值。這些特征向量也給出了N個最重要特征的真實結構。可以通過將數據乘上這N個特征向量而將它轉換到新的空間。

PCA的目標是在高維數據中找到最大方差的方向,並將數據映射到一個維度不大於原始數據的新的子空間上。新特征的坐標是相互正交的,且新的子空間上正交的坐標軸(主成分)可以被解釋為方差最大的方向。

對於d維原始特征空間,通過構建一個維度為d*k維的轉換矩陣,將樣本映射到新的k維特征空間上去(k<d)。



PCA算法步驟如下:

1、對原始d維數據做預處理。包括划分數據集與標准化處理。由於主成分的方向對數據值的范圍高度敏感,因此要先對特征值做標准化處理。

2、構造樣本的協方差矩陣。d*d維協方差矩陣是沿主對角線對稱的,其中d為數據集的維度,此矩陣成對地存儲了不同特征之間的協方差。如兩個特征,可通過如下公式計算它們之間的協方差:


其中,分別為特征的均值。若對數據集進行了標准化處理,則樣本的均值會為0.兩個特征之間的協方差如果為正,則它們會同時增減,而一個負的協方差則表示兩個特征會朝相反的方向變動。一個包含三個特征的協方差矩陣可以記為:


3、計算協方差矩陣的特征值和相應的特征向量。協方差矩陣的特征向量代表主成分(最大方差方向),而對應的特征值大小就決定了特征向量的重要性。協方差矩陣的特征對計算如下:


特征值為一個標量。

4、選擇與前k個最大特征值對應的特征向量,其中k為新特征空間的維度(k<=d)。由於要將數據集壓縮到一個新的特征子空間上來實現數據降維,所以只選擇那些包含最多信息(方差最大)的特征向量(主成分)組成子集。由於特征值的大小決定了特征向量的重要性,因此需要將特征值按降序排列,感興趣的是排序在前k個的特征值所對應的特征向量。

特征值的方差貢獻率:特征值與所有特征值和的比值:


5、通過前k個特征向量構建映射矩陣W。

6、通過映射矩陣W將d維的輸入數據集X轉換到新的k維特征子空間。將數據集中的信息轉換到新的主成分軸上。



下面給出上述過程的python代碼:

##############################數據的讀入、划分、標准化##########################
import pandas as pd
#Python Data Analysis Library 或 pandas 是基於NumPy 的一種工具,該工具是為了解決數據分析任務而創建的。
#Pandas 納入了大量庫和一些標准的數據模型,提供了高效地操作大型數據集所需的工具。
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data',header=None)
#讀入csv文件,形成一個數據框
#使用葡萄酒數據集


from sklearn.cross_validation import train_test_split
x, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
x_train, x_test, y_train, y_test =train_test_split(x, y, test_size=0.3,stratify=y,random_state=0)
#葡萄酒數據集划分為訓練集和測試集,分別占據數據集的70%和30%

#使用單位方差對數據集進行標准化
from sklearn.preprocessing import StandardScaler
sc=StandardScaler()
#注意,當改為from sklearn.preprocessing import StandardScaler as sc時,會報錯
# fit_transform() missing 1 required positional argument: 'X'。這就是由於sc沒有StandardScaler()而僅僅是StandardScaler
x_train_std=sc.fit_transform(x_train)
x_test_std=sc.fit_transform(x_test)



##############################構造協方差矩陣、獲得特征值與特征向量###############
import numpy as np
cov_mat=np.cov(x_train_std.T)#求數據集的協方差矩陣
#進行特征分解
eigen_vals,eigen_vecs=np.linalg.eig(cov_mat)#用numpy中的linalg.eig函數來計算數據集協方差矩陣的特征對
print('\n特征值 \n%s' % eigen_vals)
#對於此處導入的葡萄酒數據集,可以獲得13*13的協方差矩陣和13個特征向量及其對應的特征值。
#eigen_vals為存放了13個特征值的向量。而特征向量以列的方式存儲於一個13*13維的矩陣中eigen_vecs。


#########################繪制特征值的方差貢獻率圖像##############################
tot=sum(eigen_vals)
var_exp=[(i/tot) for i in sorted(eigen_vals,reverse=True)]#用sorted函數進行排序,並用for循環求出各個特征值的方差貢獻率,組成一個列表
#使用numpy的cumsum函數計算累計方差,並通過matplotlib的step函數繪制
cum_var_exp=np.cumsum(var_exp)

#畫圖
import matplotlib.pyplot as plt
###########下面兩句使得圖片內可以出現中文
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
###########
plt.bar(range(1, 14), var_exp, alpha=0.5, align='center',label= "單個方差貢獻")
plt.step(range(1, 14), cum_var_exp, where='mid',label='累計方差貢獻')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal component index')
plt.legend(loc='best')
plt.tight_layout()
plt.show()


##########################################特征轉換#############################
#按特征值的降序排列特征對:
eigen_pairs=[(np.abs(eigen_vals[i]),eigen_vecs[:,i]) for i in range(len(eigen_vals))]
eigen_pairs.sort(reverse=True)
#接下來選取兩個對應的特征值最大的特征向量,這兩個值之和占據數據集總體方差的60%(可見特征值的方差貢獻率圖像)
w=np.hstack((eigen_pairs[0][1][:, np.newaxis],eigen_pairs[1][1][:, np.newaxis]))
#np.hstack用於把幾個小數組合並成一個大數組,hstack表示軸1合並。
#hstack的字母h來自於horizontal,表示兩個數組是水平的,hstack((a,b))將把b排在a的右邊的意思。
#np.newaxis 在使用和功能上等價於 None,其實就是 None 的一個別名。
print('\nW矩陣 \n%s' % w)

print('\n將一個樣本轉換的PCA的子空間上,得到: \n%s' % x_train_std[0].dot(w))
print('\n樣本轉換前為: \n%s' % x_train_std[0])

##將數據集進行特征轉換
x_train_pca=x_train_std.dot(w)
#畫圖
X_train_pca = x_train_std.dot(w)
colors = ['r', 'b', 'g']
markers = ['s', 'x', 'o']

for l, c, m in zip(np.unique(y_train), colors, markers):
    plt.scatter(x_train_pca[y_train == l, 0],X_train_pca[y_train == l, 1],c=c, label=l, marker=m)

plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()

結果如下圖所示:




上面是給出PCA過程的Python代碼,接下來看一下scikit-learn中的PCA類(建議與上面程序結合着理解)

#####################################定義一個畫圖函數###########################
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

def plot_decision_regions(X, y, classifier, resolution=0.02):

    # setup marker generator and color map
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    # plot class samples
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], 
                    y=X[y == cl, 1],
                    alpha=0.6, 
                    c=cmap(idx),
                    edgecolor='black',
                    marker=markers[idx], 
                    label=cl)


##############################數據的讀入、划分、標准化###########################
import pandas as pd
#Python Data Analysis Library 或 pandas 是基於NumPy 的一種工具,該工具是為了解決數據分析任務而創建的。
#Pandas 納入了大量庫和一些標准的數據模型,提供了高效地操作大型數據集所需的工具。
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data',header=None)
#讀入csv文件,形成一個數據框
#使用葡萄酒數據集


from sklearn.cross_validation import train_test_split
x, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
x_train, x_test, y_train, y_test =train_test_split(x, y, test_size=0.3,stratify=y,random_state=0)
#葡萄酒數據集划分為訓練集和測試集,分別占據數據集的70%和30%

#使用單位方差對數據集進行標准化
from sklearn.preprocessing import StandardScaler
sc=StandardScaler()
#注意,當改為from sklearn.preprocessing import StandardScaler as sc時,會報錯
# fit_transform() missing 1 required positional argument: 'X'。這就是由於sc沒有StandardScaler()而僅僅是StandardScaler
x_train_std=sc.fit_transform(x_train)
x_test_std=sc.fit_transform(x_test)


#####################################使用sklearn中的PCA類#######################
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA
pca=PCA(n_components=2)#類似於上面的程序,選取兩個最大的特征值作為主成分
#做降維處理
x_train_pca=pca.fit_transform(x_train_std)
x_test_pca=pca.transform(x_test_std)

#用邏輯斯蒂回歸進行分類(對訓練數據進行處理)
lr=LogisticRegression()
lr.fit(x_train_pca,y_train)
plot_decision_regions(x_train_pca,y_train,classifier=lr)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()

#用邏輯斯蒂回歸進行分類(對測試數據進行處理)
lr=LogisticRegression()
lr.fit(x_train_pca,y_train)
plot_decision_regions(x_test_pca,y_test,classifier=lr)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()

結果如下圖所示:


比較PCA類與自行實現的PCA分析結果,可以發現,左圖為右圖沿Y軸對稱反轉獲得。這僅僅是由於特征分析方法不一樣而導致,本質上沒有區別。


當將主成分由2改為None時,可得到:


當將n_components設置為None時,將按照方差貢獻率遞減的順序返回所有主成分。


核主成分分析:

許多機器學習算法都是假定輸入數據是線性可分的。降維處理也一樣,PCA和LDA都是針對線性可分數據降維。而對於非線性可分的數據要進行降維處理可以使用核技巧的PCA,或稱為核PCA。通過使用核PCA可以將非線性可分的數據轉換線性可分的低維子空間上(在機器學習筆記5中的核SVM介紹過類似的概念)

線性與非線性可分問題圖示如下:


由於本人對於核函數與核技巧理解還不是很深入,下面直接給出書本上的照片(后面有新的體會會及時更新本博文):




下面給出Python代碼:

################################基於RBF核的PCA##################################
from scipy.spatial.distance import pdist, squareform
from scipy import exp
from scipy.linalg import eigh
import numpy as np

def rbf_kernel_pca(X, gamma, n_components):#RBF核函數結合着理論部分來看
    """
    RBF kernel PCA implementation.

    Parameters
    ------------
    X: {NumPy ndarray}, shape = [n_samples, n_features]
        
    gamma: float
      Tuning parameter of the RBF kernel
        
    n_components: int
      Number of principal components to return

    Returns
    ------------
     X_pc: {NumPy ndarray}, shape = [n_samples, k_features]
       Projected dataset   

    """
    # Calculate pairwise squared Euclidean distances
    # in the MxN dimensional dataset.
    sq_dists = pdist(X, 'sqeuclidean')

    # Convert pairwise distances into a square matrix.
    mat_sq_dists = squareform(sq_dists)

    # Compute the symmetric kernel matrix.
    K = exp(-gamma * mat_sq_dists)

    # Center the kernel matrix.
    N = K.shape[0]
    one_n = np.ones((N, N)) / N
    K = K - one_n.dot(K) - K.dot(one_n) + one_n.dot(K).dot(one_n)

    # Obtaining eigenpairs from the centered kernel matrix
    # scipy.linalg.eigh returns them in ascending order
    eigvals, eigvecs = eigh(K)
    eigvals, eigvecs = eigvals[::-1], eigvecs[:, ::-1]

    # Collect the top k eigenvectors (projected samples)
    X_pc = np.column_stack((eigvecs[:, i]
                            for i in range(n_components)))

    return X_pc


################################分離半月形數據##################################
from sklearn.datasets import make_moons
x, y = make_moons(n_samples=100, random_state=123)

#畫出半月形數據
import matplotlib.pyplot as plt
plt.scatter(x[y == 0, 0], x[y == 0, 1], color='red', marker='^', alpha=0.5)
plt.scatter(x[y == 1, 0], x[y == 1, 1], color='blue', marker='o', alpha=0.5)
plt.tight_layout()
plt.show()   


#########################先用標准的PCA來進行降維處理#############################
from sklearn.decomposition import PCA
scikit_pca = PCA(n_components=2)
x_spca = scikit_pca.fit_transform(x)

fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(7, 3))

ax[0].scatter(x_spca[y == 0, 0], x_spca[y == 0, 1],color='red', marker='^', alpha=0.5)
ax[0].scatter(x_spca[y == 1, 0], x_spca[y == 1, 1],color='blue', marker='o', alpha=0.5)

ax[1].scatter(x_spca[y == 0, 0], np.zeros((50, 1)) + 0.02, color='red', marker='^', alpha=0.5)
ax[1].scatter(x_spca[y == 1, 0], np.zeros((50, 1)) - 0.02,color='blue', marker='o', alpha=0.5)

ax[0].set_xlabel('PC1')
ax[0].set_ylabel('PC2')
ax[1].set_ylim([-1, 1])
ax[1].set_yticks([])
ax[1].set_xlabel('PC1')
plt.tight_layout()
plt.show()


#########################使用核PCA來進行降維處理#################################
x_kpca = rbf_kernel_pca(x, gamma=15, n_components=2)

fig, ax = plt.subplots(nrows=1,ncols=2, figsize=(7,3))
ax[0].scatter(x_kpca[y==0, 0], x_kpca[y==0, 1],color='red', marker='^', alpha=0.5)
ax[0].scatter(x_kpca[y==1, 0], x_kpca[y==1, 1],color='blue', marker='o', alpha=0.5)

ax[1].scatter(x_kpca[y==0, 0], np.zeros((50,1))+0.02,color='red', marker='^', alpha=0.5)
ax[1].scatter(x_kpca[y==1, 0], np.zeros((50,1))-0.02,color='blue', marker='o', alpha=0.5)

ax[0].set_xlabel('PC1')
ax[0].set_ylabel('PC2')
ax[1].set_ylim([-1, 1])
ax[1].set_yticks([])
ax[1].set_xlabel('PC1')
plt.tight_layout()
plt.show()


################################分離同心圓數據##################################
from sklearn.datasets import make_circles
X, y = make_circles(n_samples=1000, random_state=123, noise=0.1, factor=0.2)

plt.scatter(X[y == 0, 0], X[y == 0, 1], color='red', marker='^', alpha=0.5)
plt.scatter(X[y == 1, 0], X[y == 1, 1], color='blue', marker='o', alpha=0.5)

plt.tight_layout()
plt.show()


#########################先用標准的PCA來進行降維處理#############################
scikit_pca = PCA(n_components=2)
X_spca = scikit_pca.fit_transform(X)

fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(7, 3))

ax[0].scatter(X_spca[y == 0, 0], X_spca[y == 0, 1],color='red', marker='^', alpha=0.5)
ax[0].scatter(X_spca[y == 1, 0], X_spca[y == 1, 1],color='blue', marker='o', alpha=0.5)

ax[1].scatter(X_spca[y == 0, 0], np.zeros((500, 1)) + 0.02,color='red', marker='^', alpha=0.5)
ax[1].scatter(X_spca[y == 1, 0], np.zeros((500, 1)) - 0.02,color='blue', marker='o', alpha=0.5)

ax[0].set_xlabel('PC1')
ax[0].set_ylabel('PC2')
ax[1].set_ylim([-1, 1])
ax[1].set_yticks([])
ax[1].set_xlabel('PC1')

plt.tight_layout()
plt.show()


#########################使用核PCA來進行降維處理#################################
X_kpca = rbf_kernel_pca(X, gamma=15, n_components=2)

fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(7, 3))
ax[0].scatter(X_kpca[y == 0, 0], X_kpca[y == 0, 1],color='red', marker='^', alpha=0.5)
ax[0].scatter(X_kpca[y == 1, 0], X_kpca[y == 1, 1],color='blue', marker='o', alpha=0.5)

ax[1].scatter(X_kpca[y == 0, 0], np.zeros((500, 1)) + 0.02,color='red', marker='^', alpha=0.5)
ax[1].scatter(X_kpca[y == 1, 0], np.zeros((500, 1)) - 0.02,color='blue', marker='o', alpha=0.5)

ax[0].set_xlabel('PC1')
ax[0].set_ylabel('PC2')
ax[1].set_ylim([-1, 1])
ax[1].set_yticks([])
ax[1].set_xlabel('PC1')

plt.tight_layout()
plt.show()

結果如下圖所示:






下面給出使用sklearn中核PCA類來進行處理的Python代碼:

################################分離半月形數據##################################
from sklearn.datasets import make_moons
x, y = make_moons(n_samples=100, random_state=123)

#畫出半月形數據
import matplotlib.pyplot as plt
plt.scatter(x[y == 0, 0], x[y == 0, 1], color='red', marker='^', alpha=0.5)
plt.scatter(x[y == 1, 0], x[y == 1, 1], color='blue', marker='o', alpha=0.5)
plt.tight_layout()
plt.show()   


#########################核PCA來進行降維處理#############################
from sklearn.decomposition import KernelPCA
KPCA=KernelPCA(n_components=2,kernel='rbf',gamma=15)
x_KPCA=KPCA.fit_transform(x)


#畫出結果
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(7, 3))

ax[0].scatter(x_KPCA[y == 0, 0], x_KPCA[y == 0, 1],color='red', marker='^', alpha=0.5)
ax[0].scatter(x_KPCA[y == 1, 0], x_KPCA[y == 1, 1],color='blue', marker='o', alpha=0.5)
ax[1].scatter(x_KPCA[y == 0, 0], np.zeros((50, 1)) + 0.02, color='red', marker='^', alpha=0.5)
ax[1].scatter(x_KPCA[y == 1, 0], np.zeros((50, 1)) - 0.02,color='blue', marker='o', alpha=0.5)

ax[0].set_xlabel('PC1')
ax[0].set_ylabel('PC2')
ax[1].set_ylim([-1, 1])
ax[1].set_yticks([])
ax[1].set_xlabel('PC1')
plt.tight_layout()
plt.show()

結果如下圖所示:



SVD:

奇異值分解(singular value decomposition,SVD)是一種矩陣分解的技術,是強大的信息提取的工具。SVD的應用場景:隱性語義索引、推薦系統。SVD的數學推導見下圖片:


SVD將原始的數據集矩陣Data分解成為如下式所示的形式:


上述分解會構建出一個矩陣,該矩陣只有對角元素,其他元素均為0。另一個慣例就是,該矩陣的對角元素是從大到小排列的。這些對角元素稱為奇異值。對應的就是原始數據集矩陣的奇異值。

奇異值就是矩陣特征值的平方根。

由於奇異值分解后的矩陣只有從大到小排列的對角元素。在科學和工程中,一直認為:在某個奇異值的數目(r個)之后,其他的奇異值都置為0。這就意味着數據集中僅有r個重要的特征,而其余特征則都是噪聲或冗余特征。

在奇異值跳變的地方選取奇異值(或者,保留矩陣中90%的能量信息。為了計算總能量,可以將所有奇異值求其平方和。於是可以將奇異值的平方和累加到總值的90%為止),那么原始的矩陣就可以用以下結果來近似:


Python中實現奇異值分解的函數如下圖所示



此處的Sigma以行向量的形式返回,但實際上它是一個矩陣,只是為了節省空間而已~

給出Python代碼如下:

#######################################定義一個數據矩陣#########################
def loadData():
    return[[0, 0, 0, 2, 2],
           [0, 0, 0, 3, 3],
           [0, 0, 0, 1, 1],
           [1, 1, 1, 0, 0],
           [2, 2, 2, 0, 0],
           [5, 5, 5, 0, 0],
           [1, 1, 1, 0, 0]]


##########################################對上面矩陣進行奇異值分解###############
Data=loadData()
import numpy as np
U,Sigma,VT=np.linalg.svd(Data)
print("奇異值")
print(Sigma)#通過打印的結果可以看出,前兩個奇異值比后三個奇異值大很多,故此把后三個去掉(在奇異值跳變的地方選)

######################################重構原始矩陣##############################
Sig=np.mat([[Sigma[0],0],[0,Sigma[1]]])
Data1=U[:,:2]*Sig*VT[:2,:]
print("奇異值分解降維后矩陣")
print(Data1)

結果如下圖所示:


感覺對於奇異值分解降維理解還不夠深入,接下來我更深入的體會會繼續更新本博文


當然~除了以上降維方法以外,還有流形學習降維、多維縮放降維、等度量映射降維、局部線性嵌入等。

主要參考資料如下:

  • 《機器學習實戰》
  • 《Python機器學習》
  • 《Python大戰機器學習》





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: