機器學習學習筆記之——演算法鏈與管道
演算法鏈與管道
對於許多機器學習演算法,你提供的特定資料表示非常重要。首先對資料進行縮放,然後手動合併特徵,再利用無監督機器學習來學習特徵。因此,大多數機器學習應用不僅需要應用單個演算法,而且還需要將許多不同的處理步驟和機器學習模型連結在一起。本章將介紹如何使用 Pipeline
類來簡化構建變換和模型鏈的過程。我們將重點介紹如何將 Pipeline
和 GridSearchCV
結合起來,從而同時搜尋所有處理步驟中的引數。
舉一個例子來說明模型鏈的重要性。我們知道,可以通過使用 MinMaxScaler
進行預處理來大大提高核 SVM
在 cancer
資料集上的效能。下面這些程式碼實現了劃分資料、計算最小值和最大值、縮放資料與訓練 SVM
:
from sklearn.svm import SVC
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
# 載入並劃分資料
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, random_state=0)
# 計算訓練資料的最小值和最大值
scaler = MinMaxScaler().fit(X_train)
# 對訓練資料進行縮放
X_train_scaled = scaler.transform(X_train)
svm = SVC()
# 在縮放後的訓練資料上學習SVM
svm.fit(X_train_scaled, y_train)
# 對測試資料進行縮放,並計算縮放後的資料的分數
X_test_scaled = scaler.transform(X_test)
print("Test score: {:.2f}".format(svm.score(X_test_scaled, y_test)))
# Test score: 0.97
1、用預處理進行引數選擇
現在,假設我們希望利用 GridSearchCV
找到更好的 SVC
引數。 我們應該怎麼做?一種簡單的方法可能如下所示:
from sklearn.model_selection import GridSearchCV
# 只是為了說明,不要在實踐中使用這些程式碼!
param_grid = {'C': [0.001, 0.01, 0.1, 1, 10, 100],
'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
grid = GridSearchCV(SVC(), param_grid=param_grid, cv=5)
grid.fit(X_train_scaled, y_train)
print("Best cross-validation accuracy: {:.2f}".format(grid.best_score_))
# Best cross-validation accuracy: 0.98
print("Best parameters: ", grid.best_params_)
# Best parameters: {'C': 1, 'gamma': 1}
print("Test set accuracy: {:.2f}".format(grid.score(X_test_scaled, y_test)))
# Test set accuracy: 0.97
這裡我們利用縮放後的資料對 SVC
引數進行網格搜尋。但是,上面的程式碼中有一個不易察覺的陷阱。在縮放資料時,我們使用了訓練集中的所有資料來找到訓練的方法。然後,我們使用縮放後的訓練資料來執行帶交叉驗證的網格搜尋。對於交叉驗證中的每次劃分,原始訓練集的一部分被劃分為訓練部分,另一部分被劃分為測試部分。測試部分用於度量在訓練部分上所訓練的模型在新資料上的表現。但是,我們在縮放資料時已經使用過測試部分中所包含的資訊。請記住,交叉驗證每次劃分的測試部分都是訓練集的一部分,我們使用整個訓練集的資訊來找到資料的正確縮放。
對於模型來說,這些資料與新資料看起來截然不同。如果我們觀察新資料(比如測試集中的資料),那麼這些資料並沒有用於對訓練資料進行縮放,其最大值和最小值也可能與訓練資料不同。下面這個例子顯示了交叉驗證與最終評估這兩個過程中資料處理的不同之處:
mglearn.plots.plot_improper_processing()
因此,對於建模過程,交叉驗證中的劃分無法正確地反映新資料的特徵。我們已經將這部分資料的資訊洩露(leak
)給建模過程。這將導致在交叉驗證過程中得到過於樂觀的結果, 並可能會導致選擇次優的引數。
為了解決這個問題,在交叉驗證的過程中,應該在進行任何預處理之前完成資料集的劃分。任何從資料集中提取資訊的處理過程都應該僅應用於資料集的訓練部分,因此,任何交叉驗證都應該位於處理過程的 “最外層迴圈”。
在 scikit-learn
中,要想使用 cross_val_score
函式和 GridSearchCV
函式實現這一點,可以使用 Pipeline
類。Pipeline
類可以將多個處理步驟合併(glue
)為單個 scikit-learn
估計器。Pipeline
類本身具有 fit
、predict
和 score
方法,其行為與 scikit-learn
中的其他模型相同。Pipeline
類最常見的用例是將預處理步驟(比如資料縮放)與一個監督模型 (比如分類器)連結在一起。
2、構建管道
我們來看一下如何使用 Pipeline
類來表示在使用 MinMaxScaler
縮放資料之後再訓練一個 SVM
的工作流程(暫時不用網格搜尋)。首先,我們構建一個由步驟列表組成的管道物件。 每個步驟都是一個元組,其中包含一個名稱(你選定的任意字串)和一個估計器的例項:
from sklearn.pipeline import Pipeline
pipe = Pipeline([("scaler", MinMaxScaler()), ("svm", SVC())])
這裡我們建立了兩個步驟:第一個叫作 "scaler"
,是 MinMaxScaler
的例項;第二個叫作 "svm"
,是 SVC
的例項。現在我們可以像任何其他 scikit-learn
估計器一樣來擬合這個管道:
pipe.fit(X_train, y_train)
這裡 pipe.fit
首先對第一個步驟(縮放器)呼叫 fit
,然後使用該縮放器對訓練資料進行變換,最後用縮放後的資料來擬合 SVM
。要想在測試資料上進行評估,我們只需呼叫 pipe.score
:
print("Test score: {:.2f}".format(pipe.score(X_test, y_test)))
# Test score: 0.97
如果對管道呼叫 score
方法,則首先使用縮放器對測試資料進行變換,然後利用縮放後的測試資料對 SVM
呼叫 score
方法。如你所見,這個結果與我們從開頭的程式碼得到的結果(手動進行資料變換)是相同的。利用管道,我們減少了 “預處理 + 分類” 過程 所需要的程式碼量。但是,使用管道的主要優點在於,現在我們可以在 cross_val_score
或 GridSearchCV
中使用這個估計器。
3、在網格搜尋中使用管道
在網格搜尋中使用管道的工作原理與使用任何其他估計器都相同。我們定義一個需要搜尋的引數網格,並利用管道和引數網格構建一個 GridSearchCV
。不過在指定引數網格時存在一處細微的變化。我們需要為每個引數指定它在管道中所屬的步驟。我們要調節的兩個引數 C 和 gamma
都是 SVC
的引數,屬於第二個步驟。我們給這個步驟的名稱是 "svm"
。為管道定義引數網格的語法是為每個引數指定步驟名稱,後面加上 __(雙下劃線),然後是引數名稱。因此,要想搜尋 SVC
的 C 引數,必須使用 "svm__C"
作為引數網格字典的鍵,對 gamma
引數也是同理:
param_grid = {'svm__C': [0.001, 0.01, 0.1, 1, 10, 100],
'svm__gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
有了這個引數網格,我們可以像平常一樣使用 GridSearchCV
:
grid = GridSearchCV(pipe, param_grid=param_grid, cv=5)
grid.fit(X_train, y_train)
print("Best cross-validation accuracy: {:.2f}".format(grid.best_score_))
# Best cross-validation accuracy: 0.98
print("Test set score: {:.2f}".format(grid.score(X_test, y_test)))
# Test set score: 0.97
print("Best parameters: {}".format(grid.best_params_))
# Best parameters: {'svm__C': 1, 'svm__gamma': 1}
與前面所做的網格搜尋不同,現在對於交叉驗證的每次劃分來說,僅使用訓練部分對 MinMaxScaler
進行擬合,測試部分的資訊沒有洩露到引數搜尋中。
mglearn.plots.plot_proper_processing()
在交叉驗證中,資訊洩露的影響大小取決於預處理步驟的性質。使用測試部分來估計資料的範圍,通常不會產生可怕的影響,但在特徵提取和特徵選擇中使用測試部分,則會導致結果的顯著差異。
4、通用的管道介面
Pipeline
類不但可用於預處理和分類,實際上還可以將任意數量的估計器連線在一起。例如,你可以構建一個包含特徵提取、特徵選擇、縮放和分類的管道,總共有 4 個步驟。同樣,最後一步可以用迴歸或聚類代替分類。
對於管道中估計器的唯一要求就是,除了最後一步之外的所有步驟都需要具有 transform
方法,這樣它們可以生成新的資料表示,以供下一個步驟使用。
在呼叫 Pipeline.fit
的過程中,管道內部依次對每個步驟呼叫 fit
和 transform
,其輸入是前一個步驟中 transform
方法的輸出。對於管道中的最後一步,則僅呼叫 fit
。
忽略某些細枝末節,其實現方法如下所示。請記住,pipeline.steps
是由元組組成的列表, 所以 pipeline.steps[0][1]
是第一個估計器,pipeline.steps[1][1]
是第二個估計器,以此類推:
def fit(self, X, y):
X_transformed = X
for name, estimator in self.steps[:-1]:
# 遍歷除最後一步之外的所有步驟
# 對資料進行擬合和變換
X_transformed = estimator.fit_transform(X_transformed, y)
# 對最後一步進行擬合
self.steps[-1][1].fit(X_transformed, y)
return self
使用 Pipeline
進行預測時,我們同樣利用除最後一步之外的所有步驟對資料進行變換 (transform
),然後對最後一步呼叫 predict
:
def predict(self, X):
X_transformed = X
for step in self.steps[:-1]:
# 遍歷除最後一步之外的所有步驟
# 對資料進行變換
X_transformed = step[1].transform(X_transformed)
# 利用最後一步進行預測
return self.steps[-1][1].predict(X_transformed)
整個過程如下圖所示,其中包含兩個變換器(transformer
)T1 和 T2,還有一個分類器 (叫作 Classifier
)。
管道實際上比上圖更加通用。管道的最後一步不需要具有 predict
函式,比如說,我們可以建立一個只包含一個縮放器和一個 PCA
的管道。由於最後一步(PCA
)具有 transform
方 法,所以我們可以對管道呼叫 transform
,以得到將 PCA.transform
應用於前一個步驟處理過的資料後得到的輸出。管道的最後一步只需要具有 fit
方法。
4.1、用 make_pipeline 方便地建立管道
利用上述語法建立管道有時有點麻煩,我們通常不需要為每一個步驟提供使用者指定的名稱。有一個很方便的函式 make_pipeline
,可以為我們建立管道並根據每個步驟所屬的類為其自動命名。make_pipeline
的語法如下所示:
from sklearn.pipeline import make_pipeline
# 標準語法
pipe_long = Pipeline([("scaler", MinMaxScaler()), ("svm", SVC(C=100))])
# 縮寫語法
pipe_short = make_pipeline(MinMaxScaler(), SVC(C=100))
管道物件 pipe_long
和 pipe_short
的作用完全相同,但 pipe_short
的步驟是自動命名的。 我們可以通過檢視 steps
屬性來檢視步驟的名稱:
print("Pipeline steps:\n{}".format(pipe_short.steps))
'''
Pipeline steps:
[('minmaxscaler', MinMaxScaler()), ('svc', SVC(C=100))]
'''
這兩個步驟被命名為 minmaxscaler
和 svc
。一般來說,步驟名稱只是類名稱的小寫版本。 如果多個步驟屬於同一個類,則會附加一個數字:
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
pipe = make_pipeline(StandardScaler(), PCA(n_components=2), StandardScaler())
print("Pipeline steps:\n{}".format(pipe.steps))
'''
Pipeline steps:
[('standardscaler-1', StandardScaler()), ('pca', PCA(n_components=2)), ('standardscaler-2', StandardScaler())]
'''
如你所見,第一個 StandardScaler
步驟被命名為 standardscaler-1
,而第二個被命名為 standardscaler-2
。但在這種情況下,使用具有明確名稱的 Pipeline
構建可能更好,以便 為每個步驟提供更具語義的名稱。
4.2、訪問步驟屬性
通常來說,你希望檢查管道中某一步驟的屬性——比如線性模型的係數或 PCA
提取的成分。要想訪問管道中的步驟,最簡單的方法是通過 named_steps
屬性,它是一個字典,將步驟名稱對映為估計器:
# 用前面定義的管道對cancer資料集進行擬合
pipe.fit(cancer.data)
# 從"pca"步驟中提取前兩個主成分
components = pipe.named_steps["pca"].components_
print("components.shape: {}".format(components.shape))
# components.shape: (2, 30)
4.3、訪問網格搜尋管道中的屬性
本章前面說過,使用管道的主要原因之一就是進行網格搜尋。一個常見的任務是在網格搜尋內訪問管道的某些步驟。我們對 cancer
資料集上的 LogisticRegression
分類器進行網格搜尋,在將資料傳入 LogisticRegression
分類器之前,先用 Pipeline
和 StandardScaler
對資料進行縮放。首先,我們用 make_pipeline
函式建立一個管道:
from sklearn.linear_model import LogisticRegression
pipe = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
接下來,我們建立一個引數網格。LogisticRegression
需要調節的正則化引數是引數 C。我們對這個引數使用對數網格,在 0.01 和 100 之間進行搜尋。由於我們使用了 make_pipeline
函式,所以管道中 LogisticRegression
步驟的名稱是小寫的類 名稱 logisticregression
。因此,為了調節引數 C,我們必須指定 logisticregression__C
的引數網格:
param_grid = {'logisticregression__C': [0.01, 0.1, 1, 10, 100]}
像往常一樣,我們將 cancer
資料集劃分為訓練集和測試集,並對網格搜尋進行擬合:
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=4)
grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)
那麼我們如何訪問 GridSearchCV
找到的最佳 LogisticRegression
模型的係數呢?我們知道,GridSearchCV
找到的最佳模型(在所有訓練資料上訓練得到的模型)儲存在 grid.best_estimator_
中:
print("Best estimator:\n{}".format(grid.best_estimator_))
'''
Best estimator:
Pipeline(steps=[('standardscaler', StandardScaler()),
('logisticregression', LogisticRegression(C=1, max_iter=1000))])
'''
在我們的例子中,best_estimator_
是一個管道, 它包含兩個步驟:standardscaler
和 logisticregression
。 如前所述, 我們可以使用管道的 named_steps
屬性來訪問 logisticregression
步驟:
print("Logistic regression step:\n{}".format(
grid.best_estimator_.named_steps["logisticregression"]))
'''
Logistic regression step:
LogisticRegression(C=1, max_iter=1000)
'''
現在我們得到了訓練過的 LogisticRegression
例項,下面我們可以訪問與每個輸入特徵相關的係數(權重):
print("Logistic regression coefficients:\n{}".format(
grid.best_estimator_.named_steps["logisticregression"].coef_))
'''
Logistic regression coefficients:
[[-0.43570655 -0.34266946 -0.40809443 -0.5344574 -0.14971847 0.61034122
-0.72634347 -0.78538827 0.03886087 0.27497198 -1.29780109 0.04926005
-0.67336941 -0.93447426 -0.13939555 0.45032641 -0.13009864 -0.10144273
0.43432027 0.71596578 -1.09068862 -1.09463976 -0.85183755 -1.06406198
-0.74316099 0.07252425 -0.82323903 -0.65321239 -0.64379499 -0.42026013]]
'''
這個係數列表可能有點長,但它通常有助於理解你的模型。
5、網格搜尋預處理步驟與模型引數
我們可以利用管道將機器學習工作流程中的所有處理步驟封裝成一個 scikit-learn
估計 器。這麼做的另一個好處在於,現在我們可以使用監督任務(比如迴歸或分類)的輸出來調節預處理引數。在前幾章裡,我們在應用嶺迴歸之前使用了 boston
資料集的多項式特徵。下面我們用一個管道來重複這個建模過程。管道包含 3 個步驟:縮放資料、計算多項式特徵與嶺迴歸:
from sklearn.datasets import load_boston
boston = load_boston()
X_train, X_test, y_train, y_test = train_test_split(boston.data, boston.target, random_state=0)
from sklearn.preprocessing import PolynomialFeatures
pipe = make_pipeline(
StandardScaler(),
PolynomialFeatures(),
Ridge())
我們怎麼知道選擇幾次多項式,或者是否選擇多項式或互動項呢?理想情況下,我們希望根據分類結果來選擇 degree
引數。我們可以利用管道搜尋 degree
引數以及 Ridge
的 alpha
引數。為了做到這一點,我們要定義一個包含這兩個引數的 param_grid
,並用步驟名稱作為字首:
param_grid = {'polynomialfeatures__degree': [1, 2, 3],
'ridge__alpha': [0.001, 0.01, 0.1, 1, 10, 100]}
現在我們可以再次執行網格搜尋:
grid = GridSearchCV(pipe, param_grid=param_grid, cv=5, n_jobs=-1)
grid.fit(X_train, y_train)
我們可以用熱圖將交叉驗證的結果視覺化:
mglearn.tools.heatmap(grid.cv_results_['mean_test_score'].reshape(3, -1),
xlabel="ridge__alpha", ylabel="polynomialfeatures__degree",
xticklabels=param_grid['ridge__alpha'],
yticklabels=param_grid['polynomialfeatures__degree'], vmin=0)
從交叉驗證的結果中可以看出,使用二次多項式很有用,但三次多項式的效果比一次或二次都要差很多。從找到的最佳引數中也可以看出這一點:
print("Best parameters: {}".format(grid.best_params_))
# Best parameters: {'polynomialfeatures__degree': 2, 'ridge__alpha': 10}
這個最佳引數對應的分數如下:
print("Test-set score: {:.2f}".format(grid.score(X_test, y_test)))
# Test-set score: 0.77
為了對比,我們執行一個沒有多項式特徵的網格搜尋:
param_grid = {'ridge__alpha': [0.001, 0.01, 0.1, 1, 10, 100]}
pipe = make_pipeline(StandardScaler(), Ridge())
grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)
print("Score without poly features: {:.2f}".format(grid.score(X_test, y_test)))
# Score without poly features: 0.63
正與我們觀察上圖中的網格搜尋結果所預料的那樣,不使用多項式特徵得到了明顯更差的結果。
同時搜尋預處理引數與模型引數是一個非常強大的策略。但是要記住,GridSearchCV
會嘗試指定引數的所有可能組合。因此,向網格中新增更多引數,需要構建的模型數量將呈指數增長。
6、網格搜尋選擇使用哪個模型
你甚至可以進一步將 GridSearchCV
和 Pipeline
結合起來:還可以搜尋管道中正在執行的實際步驟(比如用 StandardScaler
還是用 MinMaxScaler
)。這樣會導致更大的搜尋空間, 應該予以仔細考慮。嘗試所有可能的解決方案,通常並不是一種可行的機器學習策略。但下面是一個例子:在 iris
資料集上比較 RandomForestClassifier
和 SVC
。我們知道,SVC
可能需要對資料進行縮放,所以我們還需要搜尋是使用 StandardScaler
還是不使用預處理。我們知道,RandomForestClassifier
不需要預處理。我們先定義管道。這裡我們顯式地對步驟命名。我們需要兩個步驟,一個用於預處理,然後是一個分類器。我們可以用 SVC
和 StandardScaler
來將其例項化:
pipe = Pipeline([('preprocessing', StandardScaler()), ('classifier', SVC())])
現在我們可以定義需要搜尋的 parameter_grid
。我們希望 classifier
是 RandomForestClassifier
或 SVC
。由於這兩種分類器需要調節不同的引數,並且需要不同的預處理,所以我們可以使用 “在非網格的空間中搜尋” 中所講的搜尋網格列表。為了將一個估計器分配 給一個步驟,我們使用步驟名稱作為引數名稱。如果我們想跳過管道中的某個步驟(例如,RandomForest
不需要預處理),則可以將該步驟設定為 None
:
from sklearn.ensemble import RandomForestClassifier
param_grid = [
{'classifier': [SVC()], 'preprocessing': [StandardScaler(), None],
'classifier__gamma': [0.001, 0.01, 0.1, 1, 10, 100],
'classifier__C': [0.001, 0.01, 0.1, 1, 10, 100]},
{'classifier': [RandomForestClassifier(n_estimators=100)],
'preprocessing': [None], 'classifier__max_features': [1, 2, 3]}]
現在,我們可以像前面一樣將網格搜尋例項化並在 cancer
資料集上執行:
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, random_state=0)
grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)
print("Best params:\n{}\n".format(grid.best_params_))
'''
Best params:
{'classifier': SVC(C=10, gamma=0.01), 'classifier__C': 10, 'classifier__gamma': 0.01, 'preprocessing': StandardScaler()}
'''
print("Best cross-validation score: {:.2f}".format(grid.best_score_))
# Best cross-validation score: 0.99
print("Test-set score: {:.2f}".format(grid.score(X_test, y_test)))
# Test-set score: 0.98
網格搜尋的結果是 SVC
與 StandardScaler
預處理,在 C=10
和 gamma=0.01
時給出最佳結果。
7、小結與展望
本章介紹了 Pipeline
類,這是一種通用工具,可以將機器學習工作流程中的多個處理步驟連結在一起。現實世界中的機器學習應用很少僅涉及模型的單獨使用,而是需要一系列處理步驟。使用管道可以將多個步驟封裝為單個 Python
物件,這個物件具有我們熟悉的 scikit-learn
介面 fit
、predict
和 transform
。特別是使用交叉驗證進行模型評估與使用網格搜尋進行引數選擇時,使用 Pipeline
類來包括所有處理步驟對正確的評估至關重要。 利用 Pipeline
類還可以讓程式碼更加簡潔,並減少不用 pipeline
類構建處理鏈時可能會犯的錯誤(比如忘記將所有變換器應用於測試集,或者應用順序錯誤)的可能性。選擇特徵提取、預處理和模型的正確組合,這在某種程度上是一門藝術,通常需要一些試錯。但是有了管道,這種 “嘗試” 多個不同的處理步驟是非常簡單的。在進行試驗時,要小心不要將處理過程複雜化,並且一定要評估一下模型中的每個元件是否必要。
相關文章
- 機器學習演算法學習筆記機器學習演算法筆記
- 機器學習學習筆記機器學習筆記
- 機器學習演算法:Logistic迴歸學習筆記機器學習演算法筆記
- 機器學習筆記(KNN演算法)機器學習筆記KNN演算法
- 機器學習筆記機器學習筆記
- AI學習筆記之——如何理解機器學習(Machine Learning)AI筆記機器學習Mac
- 機器學習演算法筆記之5:支援向量機SVM機器學習演算法筆記
- 機器學習整合學習—Apple的學習筆記機器學習APP筆記
- 機器學習演算法筆記之7:模型評估與選擇機器學習演算法筆記模型
- 機器學習演算法筆記之3:線性模型機器學習演算法筆記模型
- 機器學習演算法筆記之8:聚類演算法機器學習演算法筆記聚類
- Redis學習之管道機制Redis
- python機器學習筆記:EM演算法Python機器學習筆記演算法
- 機器學習個人筆記(三)之無監督學習機器學習筆記
- 機器學習學習筆記——基本知識機器學習筆記
- 李巨集毅機器學習-學習筆記機器學習筆記
- 機器學習-學習筆記(二) --> 模型評估與選擇機器學習筆記模型
- 機器學習 | 吳恩達機器學習第九周學習筆記機器學習吳恩達筆記
- 《機器學習初步》筆記機器學習筆記
- 【區塊鏈學習】《區塊鏈學習指南》學習筆記區塊鏈筆記
- 機器學習筆記(1): 梯度下降演算法機器學習筆記梯度演算法
- Python機器學習筆記:sklearn庫的學習Python機器學習筆記
- Scikit-Learn 與 TensorFlow 機器學習實用指南學習筆記 5 —— 如何為機器學習演算法準備資料?機器學習筆記演算法
- 機器學習演算法筆記之4:貝葉斯分類器機器學習演算法筆記
- 機器學習中的聚類演算法演變及學習筆記機器學習聚類演算法筆記
- 機器學習演算法筆記之6:資料預處理機器學習演算法筆記
- 學習筆記-虛擬機器筆記虛擬機
- 機器學習課程筆記機器學習筆記
- Machine Learning 機器學習筆記Mac機器學習筆記
- Scikit-Learn 與 TensorFlow 機器學習實用指南學習筆記2 — 機器學習的主要挑戰機器學習筆記
- 機器學習筆記之效能評估指標機器學習筆記指標
- 機器學習框架ML.NET學習筆記【9】自動學習機器學習框架筆記
- 機器學習之學習速率機器學習
- 區塊鏈學習筆記與思考-4區塊鏈筆記
- 吳恩達機器學習筆記 —— 18 大規模機器學習吳恩達機器學習筆記
- Swoft 學習筆記之控制器筆記
- swoft 學習筆記之驗證器筆記
- Scikit-Learn 與 TensorFlow 機器學習實用指南學習筆記1 — 機器學習基礎知識簡介機器學習筆記